github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/snapshot/process/tracer/tracer.go (about) 1 // Copyright 2019-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 "path/filepath" 24 "strconv" 25 "syscall" 26 "unsafe" 27 28 "github.com/cilium/ebpf" 29 "github.com/cilium/ebpf/link" 30 31 containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils" 32 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 33 processcollectortypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/snapshot/process/types" 34 eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 35 bpfiterns "github.com/inspektor-gadget/inspektor-gadget/pkg/utils/bpf-iter-ns" 36 "github.com/inspektor-gadget/inspektor-gadget/pkg/utils/host" 37 ) 38 39 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type process_entry processCollector ./bpf/process-collector.bpf.c -- -Werror -O2 -g -c -x c 40 41 type Config struct { 42 MountnsMap *ebpf.Map 43 ShowThreads bool 44 } 45 46 func RunCollector(config *Config, enricher gadgets.DataEnricherByMntNs) ([]*processcollectortypes.Event, error) { 47 events, err := runeBPFCollector(config, enricher) 48 if err == nil { 49 return events, nil 50 } 51 52 if !errors.Is(err, ebpf.ErrNotSupported) { 53 return nil, fmt.Errorf("running ebpf iterator: %w", err) 54 } 55 56 events, err = runProcfsCollector(config, enricher) 57 if err != nil { 58 return nil, fmt.Errorf("running procfs collector: %w", err) 59 } 60 61 return events, err 62 } 63 64 func runeBPFCollector(config *Config, enricher gadgets.DataEnricherByMntNs) ([]*processcollectortypes.Event, error) { 65 spec, err := loadProcessCollector() 66 if err != nil { 67 return nil, fmt.Errorf("loading ebpf program: %w", err) 68 } 69 70 consts := map[string]interface{}{ 71 "show_threads": config.ShowThreads, 72 } 73 objs := processCollectorObjects{} 74 75 if err := gadgets.LoadeBPFSpec(config.MountnsMap, spec, consts, &objs); err != nil { 76 return nil, fmt.Errorf("loading ebpf spec: %w", err) 77 } 78 79 defer objs.Close() 80 81 dumpTaskIter, err := link.AttachIter(link.IterOptions{ 82 Program: objs.IgSnapProc, 83 }) 84 if err != nil { 85 return nil, fmt.Errorf("attaching BPF iterator: %w", err) 86 } 87 defer dumpTaskIter.Close() 88 89 buf, err := bpfiterns.Read(dumpTaskIter) 90 if err != nil { 91 return nil, fmt.Errorf("reading iterator: %w", err) 92 } 93 94 events := []*processcollectortypes.Event{} 95 96 entrySize := int(unsafe.Sizeof(processCollectorProcessEntry{})) 97 98 for i := 0; i < len(buf)/entrySize; i++ { 99 entry := (*processCollectorProcessEntry)(unsafe.Pointer(&buf[i*entrySize])) 100 101 event := processcollectortypes.Event{ 102 Event: eventtypes.Event{ 103 Type: eventtypes.NORMAL, 104 }, 105 Pid: int(entry.Tgid), 106 Tid: int(entry.Pid), 107 Uid: entry.Uid, 108 Gid: entry.Gid, 109 Command: gadgets.FromCString(entry.Comm[:]), 110 ParentPid: int(entry.ParentPid), 111 WithMountNsID: eventtypes.WithMountNsID{MountNsID: entry.MntnsId}, 112 } 113 114 if enricher != nil { 115 enricher.EnrichByMntNs(&event.CommonData, event.MountNsID) 116 } 117 118 events = append(events, &event) 119 } 120 121 return events, nil 122 } 123 124 func getTidEvent(config *Config, enricher gadgets.DataEnricherByMntNs, pid, tid int) (*processcollectortypes.Event, error) { 125 var val uint32 126 127 comm := host.GetProcComm(tid) 128 mntnsid, err := containerutils.GetMntNs(tid) 129 if err != nil { 130 return nil, err 131 } 132 133 if config.MountnsMap != nil { 134 // TODO: This would be more efficient to store these elements in user space to avoid 135 // performing systemcalls to lookup in the eBPF map 136 err := config.MountnsMap.Lookup(&mntnsid, &val) 137 if err != nil { 138 return nil, err 139 } 140 } 141 142 taskPath := filepath.Join(host.HostProcFs, fmt.Sprint(pid), "task", fmt.Sprint(tid)) 143 info, err := os.Lstat(taskPath) 144 if err != nil { 145 return nil, fmt.Errorf("getting user of process: %w", err) 146 } 147 148 stat := info.Sys().(*syscall.Stat_t) 149 150 event := &processcollectortypes.Event{ 151 Event: eventtypes.Event{ 152 Type: eventtypes.NORMAL, 153 }, 154 Tid: tid, 155 Pid: pid, 156 Uid: stat.Uid, 157 Gid: stat.Gid, 158 Command: comm, 159 WithMountNsID: eventtypes.WithMountNsID{MountNsID: mntnsid}, 160 } 161 162 if enricher != nil { 163 enricher.EnrichByMntNs(&event.CommonData, event.WithMountNsID.MountNsID) 164 } 165 166 return event, nil 167 } 168 169 func getPidEvents(config *Config, enricher gadgets.DataEnricherByMntNs, pid int) ([]*processcollectortypes.Event, error) { 170 var events []*processcollectortypes.Event 171 172 taskPath := filepath.Join(host.HostProcFs, fmt.Sprint(pid), "task") 173 items, err := os.ReadDir(taskPath) 174 if err != nil { 175 return nil, err 176 } 177 178 for _, item := range items { 179 if !item.IsDir() { 180 continue 181 } 182 183 tid, err := strconv.ParseInt(item.Name(), 10, strconv.IntSize) 184 if err != nil { 185 continue 186 } 187 188 event, err := getTidEvent(config, enricher, pid, int(tid)) 189 if err != nil { 190 continue 191 } 192 193 events = append(events, event) 194 } 195 196 return events, nil 197 } 198 199 func runProcfsCollector(config *Config, enricher gadgets.DataEnricherByMntNs) ([]*processcollectortypes.Event, error) { 200 items, err := os.ReadDir(host.HostProcFs) 201 if err != nil { 202 return nil, err 203 } 204 205 events := []*processcollectortypes.Event{} 206 207 for _, item := range items { 208 if !item.IsDir() { 209 continue 210 } 211 212 pid, err := strconv.ParseInt(item.Name(), 10, strconv.IntSize) 213 if err != nil { 214 continue 215 } 216 217 if config.ShowThreads { 218 pidEvents, err := getPidEvents(config, enricher, int(pid)) 219 if err != nil { 220 continue 221 } 222 events = append(events, pidEvents...) 223 } else { 224 event, err := getTidEvent(config, enricher, int(pid), int(pid)) 225 if err != nil { 226 continue 227 } 228 events = append(events, event) 229 } 230 } 231 232 return events, nil 233 } 234 235 // --- 236 237 type Tracer struct { 238 config *Config 239 eventHandler func(ev []*processcollectortypes.Event) 240 } 241 242 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 243 tracer := &Tracer{ 244 config: &Config{}, 245 } 246 return tracer, nil 247 } 248 249 func (t *Tracer) SetEventHandlerArray(handler any) { 250 nh, ok := handler.(func(ev []*processcollectortypes.Event)) 251 if !ok { 252 panic("event handler invalid") 253 } 254 t.eventHandler = nh 255 } 256 257 func (t *Tracer) SetMountNsMap(mntnsMap *ebpf.Map) { 258 t.config.MountnsMap = mntnsMap 259 } 260 261 func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error { 262 t.config.ShowThreads = gadgetCtx.GadgetParams().Get(ParamThreads).AsBool() 263 264 processes, err := RunCollector(t.config, nil) 265 if err != nil { 266 return fmt.Errorf("running snapshotter: %w", err) 267 } 268 t.eventHandler(processes) 269 return nil 270 }