summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/c/filter.c8
-rw-r--r--internal/generate/bpfhandler.go23
-rw-r--r--internal/generate/codegen_test.go59
3 files changed, 84 insertions, 6 deletions
diff --git a/internal/c/filter.c b/internal/c/filter.c
index 88c4fa0..5585c12 100644
--- a/internal/c/filter.c
+++ b/internal/c/filter.c
@@ -78,7 +78,7 @@ static __always_inline int ior_on_syscall_enter(__u32 tid, __u32 enter_trace_id)
return state.emit_event != 0;
}
-static __always_inline int ior_on_syscall_exit(__u32 tid, __u32 exit_trace_id, __s64 ret) {
+static __always_inline int ior_on_syscall_exit(__u32 tid, __u32 enter_trace_id, __s64 ret) {
__u64 now;
__u64 duration = 0;
__u8 emit_event = 1;
@@ -92,8 +92,10 @@ static __always_inline int ior_on_syscall_exit(__u32 tid, __u32 exit_trace_id, _
if (now >= state->start_ns)
duration = now - state->start_ns;
- // A tracepoint pair uses enter_id == exit_id + 1 in this codebase.
- if (state->enter_trace_id == exit_trace_id + 1)
+ // Pair aggregate stats using the explicit enter_trace_id passed by the
+ // generated exit handler, avoiding any numeric adjacency assumption
+ // between kernel-assigned enter and exit tracepoint IDs.
+ if (state->enter_trace_id == enter_trace_id)
ior_update_syscall_aggregate(state->enter_trace_id, duration, ret);
emit_event = state->emit_event;
diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go
index ed672c8..95a14b0 100644
--- a/internal/generate/bpfhandler.go
+++ b/internal/generate/bpfhandler.go
@@ -31,10 +31,27 @@ func generateBPFHandler(tp GeneratedTracepoint) string {
eventTypeConst := eventTypeConstant(tp.Classification.Kind, isEnter)
extra := generateExtra(tp, isEnter)
- return renderHandler(f.Name, ctxStruct, eventStruct, comment, eventTypeConst, extra, isEnter)
+ // Derive the explicit enter trace ID constant for exit handlers so the
+ // generated ior_on_syscall_exit call does not rely on numeric adjacency
+ // between kernel-assigned enter/exit IDs.
+ enterName := enterConstForHandler(f.Name, isEnter)
+
+ return renderHandler(f.Name, ctxStruct, eventStruct, comment, eventTypeConst, extra, isEnter, enterName)
+}
+
+// enterConstForHandler returns the C #define constant name for the
+// corresponding enter tracepoint. For enter handlers it returns
+// strings.ToUpper(name) directly; for exit handlers it replaces "EXIT"
+// with "ENTER" so the generated code passes the explicit enter ID.
+func enterConstForHandler(name string, isEnter bool) string {
+ upper := strings.ToUpper(name)
+ if isEnter {
+ return upper
+ }
+ return strings.Replace(upper, "SYS_EXIT_", "SYS_ENTER_", 1)
}
-func renderHandler(name, ctxStruct, eventStruct, comment, eventTypeConst, extra string, isEnter bool) string {
+func renderHandler(name, ctxStruct, eventStruct, comment, eventTypeConst, extra string, isEnter bool, enterName string) string {
var b strings.Builder
fmt.Fprintf(&b, "/// %s is a struct %s\n", name, comment)
fmt.Fprintf(&b, "SEC(\"tracepoint/syscalls/%s\")\n", name)
@@ -47,7 +64,7 @@ func renderHandler(name, ctxStruct, eventStruct, comment, eventTypeConst, extra
fmt.Fprintf(&b, " if (!ior_on_syscall_enter(tid, %s))\n", strings.ToUpper(name))
b.WriteString(" return 0;\n")
} else {
- fmt.Fprintf(&b, " if (!ior_on_syscall_exit(tid, %s, ctx->ret))\n", strings.ToUpper(name))
+ fmt.Fprintf(&b, " if (!ior_on_syscall_exit(tid, %s, ctx->ret))\n", enterName)
b.WriteString(" return 0;\n")
}
b.WriteString("\n")
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go
index f44e456..276a832 100644
--- a/internal/generate/codegen_test.go
+++ b/internal/generate/codegen_test.go
@@ -759,6 +759,65 @@ func TestClassifySyscallNoExit(t *testing.T) {
}
}
+func TestEnterConstForHandler(t *testing.T) {
+ tests := []struct {
+ name string
+ isEnter bool
+ want string
+ }{
+ {"sys_enter_read", true, "SYS_ENTER_READ"},
+ {"sys_exit_read", false, "SYS_ENTER_READ"},
+ {"sys_enter_openat", true, "SYS_ENTER_OPENAT"},
+ {"sys_exit_openat", false, "SYS_ENTER_OPENAT"},
+ {"sys_exit_io_uring_enter", false, "SYS_ENTER_IO_URING_ENTER"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := enterConstForHandler(tt.name, tt.isEnter)
+ if got != tt.want {
+ t.Errorf("enterConstForHandler(%q, %v) = %q, want %q", tt.name, tt.isEnter, got, tt.want)
+ }
+ })
+ }
+}
+
+func TestExitHandlerPassesEnterTraceID(t *testing.T) {
+ output := generateFromPair(t, FormatRead, FormatExitRead)
+
+ requireContains(t, output, "ior_on_syscall_exit(tid, SYS_ENTER_READ, ctx->ret)")
+ if strings.Contains(output, "ior_on_syscall_exit(tid, SYS_EXIT_READ") {
+ t.Error("exit handler must pass the enter trace ID, not the exit trace ID")
+ }
+}
+
+func TestExitHandlerDoesNotRelyOnIDAdjacency(t *testing.T) {
+ input := FormatRead + "\n" + FormatExitRead
+ formats := mustParseAll(t, input)
+ enterID := -1
+ exitID := -1
+ for _, f := range formats {
+ if strings.HasPrefix(f.Name, "sys_enter_") {
+ enterID = f.ID
+ }
+ if strings.HasPrefix(f.Name, "sys_exit_") {
+ exitID = f.ID
+ }
+ }
+ if enterID < 0 || exitID < 0 {
+ t.Fatal("missing enter or exit format")
+ }
+ if enterID != exitID+1 {
+ t.Skipf("IDs are not adjacent (enter=%d, exit=%d), adjacency test not applicable", enterID, exitID)
+ }
+
+ output := GenerateTracepointsC(formats)
+ if strings.Contains(output, "ior_on_syscall_exit(tid, SYS_EXIT_") {
+ t.Error("generated exit handler passes exit trace ID; should pass enter trace ID to avoid adjacency dependency")
+ }
+ requireContains(t, output, "ior_on_syscall_exit(tid, SYS_ENTER_READ, ctx->ret)")
+}
+
func syntheticPair(syscall string) string {
enter := strings.Replace(FormatKill, "sys_enter_kill", "sys_enter_"+syscall, 1)
enter = strings.Replace(enter, "ID: 183", "ID: 1001", 1)