syscall.go
Go Source File · 118 lines
package collector
import ( "fmt" "time"
"github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit"
"github.com/os2026/ebpf-rca/internal/syscalls")
// scKey 与 syscall.bpf.c 中的 struct sc_key 布局一致。type scKey struct { Pid uint32 Nr uint32}
// scStat 与 syscall.bpf.c 中的 struct sc_stat 布局一致。type scStat struct { Count uint64 TotalNs uint64 MaxNs uint64 Comm [16]byte}
// SyscallSample 是单个 (进程, syscall) 在窗口内的派生指标。type SyscallSample struct { Pid uint32 Comm string Nr uint32 Syscall string CallsPerSec float64 AvgLatUs float64 TotalMsPerSec float64 // 窗口内该 syscall 累计耗时(ms)/秒 MaxLatUs float64}
// SyscallCollector 加载 syscall 热点场景的 eBPF 程序。type SyscallCollector struct { objs syscallObjects links []link.Link prev map[scKey]scStat}
// NewSyscallCollector 加载字节码、挂载 raw_syscalls tracepoint。func NewSyscallCollector() (*SyscallCollector, error) { if err := rlimit.RemoveMemlock(); err != nil { return nil, fmt.Errorf("remove memlock: %w", err) } c := &SyscallCollector{prev: make(map[scKey]scStat)} if err := loadSyscallObjects(&c.objs, nil); err != nil { return nil, fmt.Errorf("load bpf objects: %w", err) } en, err := link.Tracepoint("raw_syscalls", "sys_enter", c.objs.HandleEnter, nil) if err != nil { c.Close() return nil, fmt.Errorf("attach sys_enter: %w", err) } c.links = append(c.links, en)
ex, err := link.Tracepoint("raw_syscalls", "sys_exit", c.objs.HandleExit, nil) if err != nil { c.Close() return nil, fmt.Errorf("attach sys_exit: %w", err) } c.links = append(c.links, ex) return c, nil}
// Close 卸载探针并释放资源。func (c *SyscallCollector) Close() { for _, l := range c.links { _ = l.Close() } c.objs.Close()}
// Poll 读取 syscall_stats,计算自上次调用以来的差分。func (c *SyscallCollector) Poll(interval time.Duration) ([]SyscallSample, error) { cur := make(map[scKey]scStat) var key scKey var val scStat it := c.objs.SyscallStats.Iterate() for it.Next(&key, &val) { cur[key] = val } if err := it.Err(); err != nil { return nil, fmt.Errorf("iterate syscall_stats: %w", err) }
secs := interval.Seconds() var samples []SyscallSample for k, v := range cur { p := c.prev[k] dCount := v.Count - p.Count dTotal := v.TotalNs - p.TotalNs if dCount == 0 { continue } s := SyscallSample{ Pid: k.Pid, Comm: commToString(v.Comm), Nr: k.Nr, Syscall: syscalls.Name(k.Nr), MaxLatUs: float64(v.MaxNs) / 1000.0, } if secs > 0 { s.CallsPerSec = float64(dCount) / secs s.TotalMsPerSec = float64(dTotal) / secs / 1e6 } s.AvgLatUs = float64(dTotal) / float64(dCount) / 1000.0 samples = append(samples, s) } c.prev = cur return samples, nil}





