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  }