summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-25 21:38:06 +0200
committerPaul Buetow <paul@buetow.org>2026-02-25 21:38:06 +0200
commit19da341d0822b6638fd6e7d43332987e11399643 (patch)
tree0f334e7cb8f8d228ce14b853916472633a6f07b1
parent02a664aac2d41ca8e88d5744bc4668429236cc2b (diff)
Add tracepoint grouping helper for probe manager
-rw-r--r--internal/probemanager/grouping.go53
-rw-r--r--internal/probemanager/grouping_test.go55
2 files changed, 108 insertions, 0 deletions
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_<name> / sys_exit_<name> 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)
+ }
+}