From 19da341d0822b6638fd6e7d43332987e11399643 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 25 Feb 2026 21:38:06 +0200 Subject: Add tracepoint grouping helper for probe manager --- internal/probemanager/grouping.go | 53 ++++++++++++++++++++++++++++++++ internal/probemanager/grouping_test.go | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 internal/probemanager/grouping.go create mode 100644 internal/probemanager/grouping_test.go (limited to 'internal') diff --git a/internal/probemanager/grouping.go b/internal/probemanager/grouping.go new file mode 100644 index 0000000..aa4f133 --- /dev/null +++ b/internal/probemanager/grouping.go @@ -0,0 +1,53 @@ +package probemanager + +import "strings" + +const ( + sysEnterPrefix = "sys_enter_" + sysExitPrefix = "sys_exit_" +) + +// TracepointPair holds enter/exit tracepoint names for one syscall. +type TracepointPair struct { + Enter string + Exit string +} + +// GroupTracepoints groups syscall tracepoint names by base syscall name. +// Input names must be in sys_enter_ / sys_exit_ format. +func GroupTracepoints(names []string) map[string]TracepointPair { + out := make(map[string]TracepointPair, len(names)/2) + for _, name := range names { + base, isEnter, ok := parseSyscallTracepoint(name) + if !ok { + continue + } + + pair := out[base] + if isEnter { + pair.Enter = name + } else { + pair.Exit = name + } + out[base] = pair + } + return out +} + +func parseSyscallTracepoint(name string) (base string, isEnter bool, ok bool) { + if strings.HasPrefix(name, sysEnterPrefix) { + base = strings.TrimPrefix(name, sysEnterPrefix) + if base == "" { + return "", false, false + } + return base, true, true + } + if strings.HasPrefix(name, sysExitPrefix) { + base = strings.TrimPrefix(name, sysExitPrefix) + if base == "" { + return "", false, false + } + return base, false, true + } + return "", false, false +} diff --git a/internal/probemanager/grouping_test.go b/internal/probemanager/grouping_test.go new file mode 100644 index 0000000..3e838d5 --- /dev/null +++ b/internal/probemanager/grouping_test.go @@ -0,0 +1,55 @@ +package probemanager + +import "testing" + +func TestGroupTracepointsPairsEnterAndExit(t *testing.T) { + got := GroupTracepoints([]string{ + "sys_enter_read", + "sys_exit_read", + "sys_enter_openat", + "sys_exit_openat", + }) + + if len(got) != 2 { + t.Fatalf("expected 2 grouped syscalls, got %d", len(got)) + } + + read := got["read"] + if read.Enter != "sys_enter_read" || read.Exit != "sys_exit_read" { + t.Fatalf("unexpected read pair: %+v", read) + } + + openat := got["openat"] + if openat.Enter != "sys_enter_openat" || openat.Exit != "sys_exit_openat" { + t.Fatalf("unexpected openat pair: %+v", openat) + } +} + +func TestGroupTracepointsAllowsPartialPairs(t *testing.T) { + got := GroupTracepoints([]string{ + "sys_enter_ioctl", + "sys_exit_fcntl", + }) + + ioctl := got["ioctl"] + if ioctl.Enter != "sys_enter_ioctl" || ioctl.Exit != "" { + t.Fatalf("unexpected ioctl pair: %+v", ioctl) + } + + fcntl := got["fcntl"] + if fcntl.Enter != "" || fcntl.Exit != "sys_exit_fcntl" { + t.Fatalf("unexpected fcntl pair: %+v", fcntl) + } +} + +func TestGroupTracepointsIgnoresInvalidNames(t *testing.T) { + got := GroupTracepoints([]string{ + "sys_enter_", + "sys_exit_", + "random_name", + "syscalls:sys_enter_read", + }) + if len(got) != 0 { + t.Fatalf("expected no grouped entries, got %+v", got) + } +} -- cgit v1.2.3