diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-30 10:49:46 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-30 10:49:46 +0300 |
| commit | ee3a7106a724dc193589ab652ef21503ba291562 (patch) | |
| tree | 4d5d0f2b75e085880f7f560de1546337aa2c14ce /integrationtests | |
| parent | 5e334d33b3ddc3e308455262577c461835939bc7 (diff) | |
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 <noreply@anthropic.com>
Diffstat (limited to 'integrationtests')
| -rw-r--r-- | integrationtests/sleep_test.go | 29 |
1 files changed, 24 insertions, 5 deletions
diff --git a/integrationtests/sleep_test.go b/integrationtests/sleep_test.go index fbd93a7..d7dbdd5 100644 --- a/integrationtests/sleep_test.go +++ b/integrationtests/sleep_test.go @@ -38,8 +38,14 @@ func TestSleepRequestedTimespecInParquet(t *testing.T) { t.Fatalf("expected parquet rows for workload PID %d", pid) } + // The workload issues, per loop iteration: a relative nanosleep (2ms), a + // relative clock_nanosleep (3ms), and an ABSOLUTE clock_nanosleep + // (TIMER_ABSTIME) whose request is an absolute CLOCK_MONOTONIC timestamp. + // The absolute one must be reported as the -1 sentinel, never as a bogus + // multi-decade "sleep duration" (task a20). var sawNanosleep bool - var sawClockNanosleep bool + var sawClockNanosleepRel bool + var sawClockNanosleepAbs bool for _, row := range rows { switch row.Syscall { case "nanosleep": @@ -50,8 +56,18 @@ func TestSleepRequestedTimespecInParquet(t *testing.T) { t.Fatalf("nanosleep bytes = %d, want 0", row.Bytes) } case "clock_nanosleep": - if row.RequestedSleepNS == 3_000_000 { - sawClockNanosleep = true + switch row.RequestedSleepNS { + case 3_000_000: + sawClockNanosleepRel = true + case -1: + // Absolute (TIMER_ABSTIME) sleep: sentinel, not a duration. + sawClockNanosleepAbs = true + default: + // Any large positive value here means the absolute wakeup + // timestamp leaked through as a duration — the bug in a20. + if row.RequestedSleepNS > 1_000_000_000 { + t.Fatalf("clock_nanosleep RequestedSleepNS = %d looks like an absolute timestamp; TIMER_ABSTIME must record -1", row.RequestedSleepNS) + } } if row.Bytes != 0 { t.Fatalf("clock_nanosleep bytes = %d, want 0", row.Bytes) @@ -62,7 +78,10 @@ func TestSleepRequestedTimespecInParquet(t *testing.T) { if !sawNanosleep { t.Fatal("expected nanosleep row with RequestedSleepNS=2000000") } - if !sawClockNanosleep { - t.Fatal("expected clock_nanosleep row with RequestedSleepNS=3000000") + if !sawClockNanosleepRel { + t.Fatal("expected relative clock_nanosleep row with RequestedSleepNS=3000000") + } + if !sawClockNanosleepAbs { + t.Fatal("expected absolute (TIMER_ABSTIME) clock_nanosleep row with RequestedSleepNS=-1") } } |
