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 }