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 --- cmd/ioworkload/scenario_sleep.go | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'cmd') diff --git a/cmd/ioworkload/scenario_sleep.go b/cmd/ioworkload/scenario_sleep.go index bb6c5a4..c004c4b 100644 --- a/cmd/ioworkload/scenario_sleep.go +++ b/cmd/ioworkload/scenario_sleep.go @@ -21,17 +21,47 @@ func sleepSyscalls() error { if err := callClockNanosleep(3_000_000); err != nil { return err } + // Also exercise the TIMER_ABSTIME (absolute) path so the tracer's + // absolute-sleep handling is covered: the request timespec here is an + // absolute CLOCK_MONOTONIC wakeup time, not a relative duration, so the + // tracer must NOT report it as a sleep duration (see task a20). + if err := callClockNanosleepAbs(2_000_000); err != nil { + return err + } } return nil } +// callClockNanosleep issues a relative clock_nanosleep (flags = 0), sleeping for +// requestedNs nanoseconds. func callClockNanosleep(requestedNs int64) error { req := unix.Timespec{Sec: requestedNs / 1_000_000_000, Nsec: requestedNs % 1_000_000_000} + return invokeClockNanosleep(0, &req) +} + +// callClockNanosleepAbs issues an absolute clock_nanosleep (flags = +// TIMER_ABSTIME). The request timespec is an absolute CLOCK_MONOTONIC wakeup +// time computed as "now + aheadNs", so the call blocks for roughly aheadNs but +// the request value itself is a multi-decade absolute timestamp, not a +// duration. +func callClockNanosleepAbs(aheadNs int64) error { + var now unix.Timespec + if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &now); err != nil { + return fmt.Errorf("clock_gettime: %w", err) + } + target := now.Nano() + aheadNs + req := unix.Timespec{Sec: target / 1_000_000_000, Nsec: target % 1_000_000_000} + return invokeClockNanosleep(unix.TIMER_ABSTIME, &req) +} + +// invokeClockNanosleep is the shared raw clock_nanosleep syscall wrapper used by +// both the relative and absolute callers. +func invokeClockNanosleep(flags uintptr, req *unix.Timespec) error { _, _, errno := syscall.RawSyscall6( unix.SYS_CLOCK_NANOSLEEP, uintptr(unix.CLOCK_MONOTONIC), - 0, - uintptr(unsafe.Pointer(&req)), + flags, + uintptr(unsafe.Pointer(req)), 0, 0, 0, -- cgit v1.2.3