github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/fsslower/tracer/tracer.go (about) 1 // Copyright 2022-2023 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 //go:build !withoutebpf 16 17 package tracer 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "unsafe" 24 25 "github.com/cilium/ebpf" 26 "github.com/cilium/ebpf/link" 27 "github.com/cilium/ebpf/perf" 28 29 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 30 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/fsslower/types" 32 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 33 ) 34 35 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -no-global-types -target $TARGET -cc clang -cflags ${CFLAGS} -type event fsslower ./bpf/fsslower.bpf.c -- -I./bpf/ 36 type Config struct { 37 MountnsMap *ebpf.Map 38 39 Filesystem string 40 MinLatency uint 41 } 42 43 type Tracer struct { 44 config *Config 45 enricher gadgets.DataEnricherByMntNs 46 eventCallback func(*types.Event) 47 48 objs fsslowerObjects 49 readEnterLink link.Link 50 readExitLink link.Link 51 writeEnterLink link.Link 52 writeExitLink link.Link 53 openEnterLink link.Link 54 openExitLink link.Link 55 syncEnterLink link.Link 56 syncExitLink link.Link 57 statfsEnterLink link.Link 58 statfsExitLink link.Link 59 reader *perf.Reader 60 } 61 62 type fsConf struct { 63 read string 64 write string 65 open string 66 fsync string 67 statfs string 68 } 69 70 var fsConfMap = map[string]fsConf{ 71 "btrfs": { 72 read: "btrfs_file_read_iter", 73 write: "btrfs_file_write_iter", 74 open: "btrfs_file_open", 75 fsync: "btrfs_sync_file", 76 statfs: "btrfs_statfs", 77 }, 78 "ext4": { 79 read: "ext4_file_read_iter", 80 write: "ext4_file_write_iter", 81 open: "ext4_file_open", 82 fsync: "ext4_sync_file", 83 statfs: "ext4_statfs", 84 }, 85 "fuse": { 86 read: "fuse_file_read_iter", 87 write: "fuse_file_write_iter", 88 open: "fuse_open", 89 fsync: "fuse_fsync", 90 }, 91 "nfs": { 92 read: "nfs_file_read", 93 write: "nfs_file_write", 94 open: "nfs_file_open", 95 fsync: "nfs_file_fsync", 96 statfs: "nfs_statfs", 97 }, 98 "ntfs3": { 99 read: "ntfs_file_read_iter", 100 write: "ntfs_file_write_iter", 101 open: "ntfs_file_open", 102 fsync: "generic_file_fsync", 103 }, 104 "xfs": { 105 read: "xfs_file_read_iter", 106 write: "xfs_file_write_iter", 107 open: "xfs_file_open", 108 fsync: "xfs_file_fsync", 109 statfs: "xfs_fs_statfs", 110 }, 111 } 112 113 func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs, 114 eventCallback func(*types.Event), 115 ) (*Tracer, error) { 116 t := &Tracer{ 117 config: config, 118 enricher: enricher, 119 eventCallback: eventCallback, 120 } 121 122 if err := t.install(); err != nil { 123 t.close() 124 return nil, err 125 } 126 127 go t.run() 128 129 return t, nil 130 } 131 132 // Stop stops the tracer 133 // TODO: Remove after refactoring 134 func (t *Tracer) Stop() { 135 t.close() 136 } 137 138 func (t *Tracer) close() { 139 // read 140 t.readEnterLink = gadgets.CloseLink(t.readEnterLink) 141 t.readExitLink = gadgets.CloseLink(t.readExitLink) 142 143 // write 144 t.writeEnterLink = gadgets.CloseLink(t.writeEnterLink) 145 t.writeExitLink = gadgets.CloseLink(t.writeExitLink) 146 147 // open 148 t.openEnterLink = gadgets.CloseLink(t.openEnterLink) 149 t.openExitLink = gadgets.CloseLink(t.openExitLink) 150 151 // sync 152 t.syncEnterLink = gadgets.CloseLink(t.syncEnterLink) 153 t.syncExitLink = gadgets.CloseLink(t.syncExitLink) 154 155 // statfs 156 t.statfsEnterLink = gadgets.CloseLink(t.statfsEnterLink) 157 t.statfsExitLink = gadgets.CloseLink(t.statfsExitLink) 158 159 if t.reader != nil { 160 t.reader.Close() 161 } 162 163 t.objs.Close() 164 } 165 166 func (t *Tracer) install() error { 167 var err error 168 169 spec, err := loadFsslower() 170 if err != nil { 171 return fmt.Errorf("loading ebpf program: %w", err) 172 } 173 174 consts := map[string]interface{}{ 175 "min_lat_ns": uint64(t.config.MinLatency * 1000 * 1000), 176 } 177 178 if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil { 179 return fmt.Errorf("loading ebpf spec: %w", err) 180 } 181 182 // choose a configuration based on the filesystem type passed 183 fsConf, ok := fsConfMap[t.config.Filesystem] 184 if !ok { 185 return fmt.Errorf("%q is not a supported filesystem", t.config.Filesystem) 186 } 187 188 // read 189 t.readEnterLink, err = link.Kprobe(fsConf.read, t.objs.IgFsslReadE, nil) 190 if err != nil { 191 return fmt.Errorf("attaching kprobe: %w", err) 192 } 193 t.readExitLink, err = link.Kretprobe(fsConf.read, t.objs.IgFsslReadX, nil) 194 if err != nil { 195 return fmt.Errorf("attaching kretprobe: %w", err) 196 } 197 198 // write 199 t.writeEnterLink, err = link.Kprobe(fsConf.write, t.objs.IgFsslWrE, nil) 200 if err != nil { 201 return fmt.Errorf("attaching kprobe: %w", err) 202 } 203 t.writeExitLink, err = link.Kretprobe(fsConf.write, t.objs.IgFsslWrX, nil) 204 if err != nil { 205 return fmt.Errorf("attaching kretprobe: %w", err) 206 } 207 208 // open 209 t.openEnterLink, err = link.Kprobe(fsConf.open, t.objs.IgFsslOpenE, nil) 210 if err != nil { 211 return fmt.Errorf("attaching kprobe: %w", err) 212 } 213 t.openExitLink, err = link.Kretprobe(fsConf.open, t.objs.IgFsslOpenX, nil) 214 if err != nil { 215 return fmt.Errorf("attaching kretprobe: %w", err) 216 } 217 218 // sync 219 t.syncEnterLink, err = link.Kprobe(fsConf.fsync, t.objs.IgFsslSyncE, nil) 220 if err != nil { 221 return fmt.Errorf("attaching kprobe: %w", err) 222 } 223 t.syncExitLink, err = link.Kretprobe(fsConf.fsync, t.objs.IgFsslSyncX, nil) 224 if err != nil { 225 return fmt.Errorf("attaching kretprobe: %w", err) 226 } 227 228 // statfs 229 t.statfsEnterLink, err = link.Kprobe(fsConf.statfs, t.objs.IgFsslStatfsE, nil) 230 if err != nil { 231 return fmt.Errorf("attaching kprobe: %w", err) 232 } 233 t.statfsExitLink, err = link.Kretprobe(fsConf.statfs, t.objs.IgFsslStatfsX, nil) 234 if err != nil { 235 return fmt.Errorf("attaching kretprobe: %w", err) 236 } 237 238 t.reader, err = perf.NewReader(t.objs.fsslowerMaps.Events, gadgets.PerfBufferPages*os.Getpagesize()) 239 if err != nil { 240 return fmt.Errorf("creating perf ring buffer: %w", err) 241 } 242 return nil 243 } 244 245 var ops = []string{"R", "W", "O", "F", "S"} 246 247 func (t *Tracer) run() { 248 for { 249 record, err := t.reader.Read() 250 if err != nil { 251 if errors.Is(err, perf.ErrClosed) { 252 // nothing to do, we're done 253 return 254 } 255 256 msg := fmt.Sprintf("Error reading perf ring buffer: %s", err) 257 t.eventCallback(types.Base(eventtypes.Err(msg))) 258 return 259 } 260 261 if record.LostSamples > 0 { 262 msg := fmt.Sprintf("lost %d samples", record.LostSamples) 263 t.eventCallback(types.Base(eventtypes.Warn(msg))) 264 continue 265 } 266 267 bpfEvent := (*fsslowerEvent)(unsafe.Pointer(&record.RawSample[0])) 268 269 event := types.Event{ 270 Event: eventtypes.Event{ 271 Type: eventtypes.NORMAL, 272 Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp), 273 }, 274 WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId}, 275 Comm: gadgets.FromCString(bpfEvent.Task[:]), 276 Pid: bpfEvent.Pid, 277 Op: ops[int(bpfEvent.Op)], 278 Bytes: bpfEvent.Size, 279 Offset: bpfEvent.Offset, 280 Latency: bpfEvent.DeltaUs, 281 File: gadgets.FromCString(bpfEvent.File[:]), 282 } 283 284 if t.enricher != nil { 285 t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 286 } 287 288 t.eventCallback(&event) 289 } 290 } 291 292 // --- Registry changes 293 294 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 295 params := gadgetCtx.GadgetParams() 296 t.config.Filesystem = params.Get(ParamFilesystem).AsString() 297 t.config.MinLatency = params.Get(ParamMinLatency).AsUint() 298 299 defer t.close() 300 if err := t.install(); err != nil { 301 return fmt.Errorf("installing tracer: %w", err) 302 } 303 304 go t.run() 305 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 306 307 return nil 308 } 309 310 func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) { 311 t.config.MountnsMap = mountnsMap 312 } 313 314 func (t *Tracer) SetEventHandler(handler any) { 315 nh, ok := handler.(func(ev *types.Event)) 316 if !ok { 317 panic("event handler invalid") 318 } 319 t.eventCallback = nh 320 } 321 322 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 323 tracer := &Tracer{ 324 config: &Config{}, 325 } 326 return tracer, nil 327 }