github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/ebpf/tracer.go (about)

     1  // Copyright 2024 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ebpfoperator
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"strings"
    21  
    22  	"github.com/cilium/ebpf"
    23  	"github.com/cilium/ebpf/btf"
    24  	"github.com/cilium/ebpf/perf"
    25  	"github.com/cilium/ebpf/ringbuf"
    26  
    27  	"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
    28  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
    29  	metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1"
    30  	"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
    31  )
    32  
    33  type Tracer struct {
    34  	metadatav1.Tracer
    35  
    36  	ds       datasource.DataSource
    37  	accessor datasource.FieldAccessor
    38  
    39  	mapType       ebpf.MapType
    40  	eventSize     uint32 // needed to trim trailing bytes when reading for perf event array
    41  	ringbufReader *ringbuf.Reader
    42  	perfReader    *perf.Reader
    43  }
    44  
    45  func validateTracerMap(traceMap *ebpf.MapSpec) error {
    46  	if traceMap.Type != ebpf.RingBuf && traceMap.Type != ebpf.PerfEventArray {
    47  		return fmt.Errorf("map %q has a wrong type, expected: ringbuf or perf event array, got: %s",
    48  			traceMap.Name, traceMap.Type.String())
    49  	}
    50  	return nil
    51  }
    52  
    53  func (i *ebpfInstance) populateTracer(t btf.Type, varName string) error {
    54  	i.logger.Debugf("populating tracer %q", varName)
    55  
    56  	parts := strings.Split(varName, typeSplitter)
    57  	if len(parts) != 3 {
    58  		return fmt.Errorf("invalid tracer info: %q", varName)
    59  	}
    60  
    61  	name := parts[0]
    62  	mapName := parts[1]
    63  	structName := parts[2]
    64  
    65  	i.logger.Debugf("> name       : %q", name)
    66  	i.logger.Debugf("> map name   : %q", mapName)
    67  	i.logger.Debugf("> struct name: %q", structName)
    68  
    69  	tracerConfig := i.config.Sub("tracers." + name)
    70  	if tracerConfig != nil {
    71  		if configMapName := tracerConfig.GetString("mapName"); configMapName != "" && configMapName != mapName {
    72  			return fmt.Errorf("validating tracer %q: mapName %q in eBPF program does not match %q from metadata file",
    73  				name, configMapName, mapName)
    74  		}
    75  		if configStructName := tracerConfig.GetString("structName"); configStructName != "" && configStructName != structName {
    76  			return fmt.Errorf("validating tracer %q: structName %q in eBPF program does not match %q from metadata file",
    77  				name, configStructName, structName)
    78  		}
    79  		i.logger.Debugf("> successfully validated with metadata")
    80  	}
    81  
    82  	if _, ok := i.tracers[name]; ok {
    83  		i.logger.Debugf("tracer %q already defined, skipping", name)
    84  		return nil
    85  	}
    86  
    87  	tracerMap, ok := i.collectionSpec.Maps[mapName]
    88  	if !ok {
    89  		return fmt.Errorf("map %q not found in eBPF object", mapName)
    90  	}
    91  
    92  	if err := validateTracerMap(tracerMap); err != nil {
    93  		return fmt.Errorf("trace map is invalid: %w", err)
    94  	}
    95  
    96  	var btfStruct *btf.Struct
    97  	if err := i.collectionSpec.Types.TypeByName(structName, &btfStruct); err != nil {
    98  		return fmt.Errorf("finding struct %q in eBPF object: %w", structName, err)
    99  	}
   100  
   101  	i.logger.Debugf("adding tracer %q", name)
   102  	i.tracers[name] = &Tracer{
   103  		Tracer: metadatav1.Tracer{
   104  			MapName:    mapName,
   105  			StructName: btfStruct.Name,
   106  		},
   107  		eventSize: btfStruct.Size,
   108  	}
   109  
   110  	err := i.populateStructDirect(btfStruct)
   111  	if err != nil {
   112  		return fmt.Errorf("populating struct %q for tracer %q: %w", btfStruct.Name, name, err)
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  func (t *Tracer) receiveEvents(gadgetCtx operators.GadgetContext) error {
   119  	switch t.mapType {
   120  	case ebpf.RingBuf:
   121  		return t.receiveEventsFromRingReader(gadgetCtx)
   122  	case ebpf.PerfEventArray:
   123  		return t.receiveEventsFromPerfReader(gadgetCtx)
   124  	default:
   125  		return fmt.Errorf("invalid map type")
   126  	}
   127  }
   128  
   129  func (t *Tracer) receiveEventsFromRingReader(gadgetCtx operators.GadgetContext) error {
   130  	slowBuf := make([]byte, t.eventSize)
   131  	lastSlowLen := 0
   132  	for {
   133  		rec, err := t.ringbufReader.Read()
   134  		if err != nil {
   135  			return err
   136  		}
   137  		data := t.ds.NewData()
   138  		sample := rec.RawSample
   139  		if uint32(len(rec.RawSample)) < t.eventSize {
   140  			// event is truncated; we need to copy
   141  			copy(slowBuf, rec.RawSample)
   142  
   143  			// zero difference; TODO: improve
   144  			if len(rec.RawSample) < lastSlowLen {
   145  				for i := len(rec.RawSample); i < lastSlowLen; i++ {
   146  					slowBuf[i] = 0
   147  				}
   148  			}
   149  			lastSlowLen = len(rec.RawSample)
   150  			sample = slowBuf
   151  		}
   152  		err = t.accessor.Set(data, sample)
   153  		if err != nil {
   154  			gadgetCtx.Logger().Warnf("error setting buffer: %v", err)
   155  			t.ds.Release(data)
   156  			continue
   157  		}
   158  		err = t.ds.EmitAndRelease(data)
   159  		if err != nil {
   160  			gadgetCtx.Logger().Warnf("error emitting data: %v", err)
   161  		}
   162  	}
   163  }
   164  
   165  func (t *Tracer) receiveEventsFromPerfReader(gadgetCtx operators.GadgetContext) error {
   166  	slowBuf := make([]byte, t.eventSize)
   167  	lastSlowLen := 0
   168  	for {
   169  		rec, err := t.perfReader.Read()
   170  		if err != nil {
   171  			return err
   172  		}
   173  		data := t.ds.NewData()
   174  		sample := rec.RawSample
   175  		sampleLen := len(rec.RawSample)
   176  		if uint32(sampleLen) < t.eventSize {
   177  			// event is truncated; we need to copy
   178  			copy(slowBuf, rec.RawSample)
   179  
   180  			// zero difference; TODO: improve
   181  			if sampleLen < lastSlowLen {
   182  				for i := sampleLen; i < lastSlowLen; i++ {
   183  					slowBuf[i] = 0
   184  				}
   185  			}
   186  			lastSlowLen = sampleLen
   187  			sample = slowBuf
   188  		} else if uint32(sampleLen) > t.eventSize {
   189  			// event has trailing garbage, remove it
   190  			sample = sample[:t.eventSize]
   191  		}
   192  		err = t.accessor.Set(data, sample)
   193  		if err != nil {
   194  			gadgetCtx.Logger().Warnf("error setting buffer: %v", err)
   195  			t.ds.Release(data)
   196  			continue
   197  		}
   198  		err = t.ds.EmitAndRelease(data)
   199  		if err != nil {
   200  			gadgetCtx.Logger().Warnf("error emitting data: %v", err)
   201  		}
   202  		if rec.LostSamples > 0 {
   203  			t.ds.ReportLostData(rec.LostSamples)
   204  		}
   205  	}
   206  }
   207  
   208  func (i *ebpfInstance) runTracer(gadgetCtx operators.GadgetContext, tracer *Tracer) error {
   209  	if tracer.MapName == "" {
   210  		return fmt.Errorf("tracer map name empty")
   211  	}
   212  
   213  	m, ok := i.collection.Maps[tracer.MapName]
   214  	if !ok {
   215  		return fmt.Errorf("looking up tracer map %q: not found", tracer.MapName)
   216  	}
   217  
   218  	tracer.mapType = m.Type()
   219  
   220  	var err error
   221  	switch m.Type() {
   222  	case ebpf.RingBuf:
   223  		i.logger.Debugf("creating ringbuf reader for map %q", tracer.MapName)
   224  		tracer.ringbufReader, err = ringbuf.NewReader(m)
   225  	case ebpf.PerfEventArray:
   226  		i.logger.Debugf("creating perf reader for map %q", tracer.MapName)
   227  		tracer.perfReader, err = perf.NewReader(m, gadgets.PerfBufferPages*os.Getpagesize())
   228  	default:
   229  		return fmt.Errorf("unknown type for tracer map %q", tracer.MapName)
   230  	}
   231  	if err != nil {
   232  		return fmt.Errorf("creating BPF map reader: %w", err)
   233  	}
   234  
   235  	go tracer.receiveEvents(gadgetCtx)
   236  
   237  	<-gadgetCtx.Context().Done()
   238  
   239  	if tracer.ringbufReader != nil {
   240  		tracer.ringbufReader.Close()
   241  	}
   242  	if tracer.perfReader != nil {
   243  		tracer.perfReader.Close()
   244  	}
   245  	return nil
   246  }