github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/ebpftracer/tracer_syscall_stats.go (about)

     1  package ebpftracer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/castai/kvisor/pkg/kernel"
     8  	"github.com/castai/kvisor/pkg/systable"
     9  	"github.com/cilium/ebpf"
    10  )
    11  
    12  type SyscallStatsKeyCgroupID uint64
    13  
    14  type SyscallStats struct {
    15  	ID    SyscallID
    16  	Count uint64
    17  }
    18  
    19  type SyscallID uint32
    20  
    21  func (s SyscallID) String() string {
    22  	return systable.List[s]
    23  }
    24  
    25  type rawSyscallStatsKey struct {
    26  	CgroupID uint64
    27  	ID       uint64
    28  }
    29  
    30  func (t *Tracer) ReadSyscallStats() (map[SyscallStatsKeyCgroupID][]SyscallStats, error) {
    31  	out := make(map[SyscallStatsKeyCgroupID][]SyscallStats)
    32  	statsMap := t.module.objects.SyscallStatsMap
    33  	iterator := statsMap.Iterate()
    34  
    35  	var (
    36  		key   rawSyscallStatsKey
    37  		value uint64
    38  	)
    39  	for iterator.Next(&key, &value) {
    40  		if key.ID > 340 {
    41  			//t.log.Warnf("syscall id=%d not mapped", key.ID)
    42  			// TODO: Add metrics and fix this hardcoded value.
    43  			continue
    44  		}
    45  		out[SyscallStatsKeyCgroupID(key.CgroupID)] = append(out[SyscallStatsKeyCgroupID(key.CgroupID)], SyscallStats{
    46  			ID:    SyscallID(key.ID),
    47  			Count: value,
    48  		})
    49  	}
    50  
    51  	if err := iterator.Err(); err != nil {
    52  		return nil, fmt.Errorf("syscall stats iterator: %w", err)
    53  	}
    54  
    55  	t.cleanupSyscallStats(out)
    56  
    57  	return out, nil
    58  }
    59  
    60  func (t *Tracer) cleanupSyscallStats(stats map[SyscallStatsKeyCgroupID][]SyscallStats) {
    61  	var obsoleteStatsKeys []rawSyscallStatsKey
    62  
    63  	t.removedCgroupsMu.Lock()
    64  	for removedCgroupID := range t.removedCgroups {
    65  		if st, found := stats[SyscallStatsKeyCgroupID(removedCgroupID)]; found {
    66  			for _, syscallStats := range st {
    67  				obsoleteStatsKeys = append(obsoleteStatsKeys, rawSyscallStatsKey{
    68  					CgroupID: removedCgroupID,
    69  					ID:       uint64(syscallStats.ID),
    70  				})
    71  			}
    72  
    73  		}
    74  	}
    75  	t.removedCgroupsMu.Unlock()
    76  
    77  	if len(obsoleteStatsKeys) > 0 {
    78  		if err := t.cleanupSyscallStatsKernel(obsoleteStatsKeys); err != nil {
    79  			t.log.Errorf("cleanup obsolete syscall stats: %v", err)
    80  		} else {
    81  			t.removedCgroupsMu.Lock()
    82  			t.removedCgroups = map[uint64]struct{}{}
    83  			t.removedCgroupsMu.Unlock()
    84  		}
    85  	}
    86  }
    87  
    88  func (t *Tracer) cleanupSyscallStatsKernel(obsoleteStatsKeys []rawSyscallStatsKey) error {
    89  	kernelVersion, err := kernel.CurrentKernelVersion()
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	// The ebpf batch helpers are available since kernel version 5.6.
    95  	if kernelVersion.Major > 5 || (kernelVersion.Major == 5 && kernelVersion.Minor >= 6) {
    96  		_, err = t.module.objects.SyscallStatsMap.BatchDelete(obsoleteStatsKeys, &ebpf.BatchOptions{})
    97  		if err != nil {
    98  			if !errors.Is(err, ebpf.ErrKeyNotExist) {
    99  				return fmt.Errorf("got error while trying to delete syscall stats: %w", err)
   100  			}
   101  		}
   102  	} else {
   103  		for _, key := range obsoleteStatsKeys {
   104  			err = t.module.objects.SyscallStatsMap.Delete(key)
   105  			if !errors.Is(err, ebpf.ErrKeyNotExist) {
   106  				t.log.Warnf("deleting syscall stats: %v", err)
   107  			}
   108  		}
   109  	}
   110  	return nil
   111  }