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 }