From ee3a7106a724dc193589ab652ef21503ba291562 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 30 May 2026 10:49:46 +0300 Subject: fix(sleep): record sentinel for TIMER_ABSTIME clock_nanosleep (a20) clock_nanosleep with the TIMER_ABSTIME flag passes an ABSOLUTE wakeup time in the request timespec, not a relative duration. The generated BPF sleep handler computed requested_ns = tv_sec*1e9 + tv_nsec unconditionally, so absolute sleeps exported a bogus multi-decade "sleep duration" in CSV/parquet/stream. generateExtraSleep now carries an optional flags-argument expression per sleep syscall. For clock_nanosleep the generated handler checks args[1] & TIMER_ABSTIME (value 1) and only computes the relative duration when the flag is clear; absolute sleeps keep the existing -1 sentinel (same value used for null/unreadable timespec pointers). nanosleep is always relative and stays unconditional (no flags arg). - Regenerated internal/c/generated_tracepoints.c (mage generate idempotent). - Added codegen tests asserting the TIMER_ABSTIME guard for clock_nanosleep and its absence for nanosleep. - Extended the ioworkload sleep scenario to issue an absolute clock_nanosleep and the sleep parquet integration test to assert it is reported as -1. Co-Authored-By: Claude Opus 4.8 --- internal/generate/codegen_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'internal/generate/codegen_test.go') diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 9bd391e..880d67d 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -1371,6 +1371,10 @@ func TestGenerateSleepHandlerCapturesRequestedTimespec(t *testing.T) { requireContains(t, output, "ev->requested_ns = -1;") requireContains(t, output, "if (ctx->args[0] != 0) {") requireContains(t, output, "ev->requested_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;") + // nanosleep is ALWAYS a relative sleep (no flags argument), so its handler + // must compute the duration unconditionally — no TIMER_ABSTIME flags check. + requireNotContains(t, output, "TIMER_ABSTIME") + requireNotContains(t, output, "& 1 /* TIMER_ABSTIME */") } func TestGenerateClockNanosleepHandlerCapturesRequestedTimespec(t *testing.T) { @@ -1386,6 +1390,27 @@ func TestGenerateClockNanosleepHandlerCapturesRequestedTimespec(t *testing.T) { requireContains(t, output, "ev->requested_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;") } +// TestGenerateClockNanosleepHandlerSkipsAbsoluteSleeps locks in the +// TIMER_ABSTIME fix: when flags (args[1]) has TIMER_ABSTIME set, the request +// timespec is an ABSOLUTE wakeup time, not a relative duration, so the handler +// must keep the -1 sentinel instead of exporting a bogus multi-decade +// "sleep duration". The relative-duration computation is therefore guarded by a +// flags check, and only runs when the flag is clear. +func TestGenerateClockNanosleepHandlerSkipsAbsoluteSleeps(t *testing.T) { + output := generateFromPair(t, FormatClockNanosleep, FormatExitClockNanosleep) + + // The flags check on args[1] against TIMER_ABSTIME (value 1) must be present, + // guarding the relative-duration assignment. + requireContains(t, output, "if ((ctx->args[1] & 1 /* TIMER_ABSTIME */) == 0) {") + // The duration is computed inside the guard (relative branch only); the abs + // branch leaves the -1 sentinel set above. + requireContains(t, output, + " if (bpf_probe_read_user(&ts, sizeof(ts), (void *)ctx->args[2]) == 0) {\n"+ + " if ((ctx->args[1] & 1 /* TIMER_ABSTIME */) == 0) {\n"+ + " ev->requested_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;\n"+ + " }\n }") +} + // TestClockNanosleepExitHandlerIsUnclassifiedRet locks in that the exit side of // clock_nanosleep records a plain ret_event with ret_type UNCLASSIFIED. The // syscall returns 0 on success or a positive errno (and -1 only when invoked -- cgit v1.2.3