From 3cd5e655ee1768b4118815d1ea887acdd57eb498 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 9 Jun 2026 22:09:13 +0300 Subject: test: add coverage for pidfd_send_signal and fadvise64 pidfd_send_signal (FamilyIPC, KindFd@arg0) and fadvise64 (KindFd, UNCLASSIFIED fd-based hint) previously had no end-to-end integration coverage despite correct classification/tracing. pidfd_send_signal: add a pidfd-send-signal ioworkload scenario that opens a pidfd for the current process and issues a sig-0 liveness probe (delivers nothing, safe to target self) via syscall.Syscall6 with the per-arch nr 424. TestPidfdSendSignal asserts enter_pidfd_send_signal is captured; pidfd_send_signal added to the pidfd -trace-syscalls list. fadvise64: add readwrite-fadvise64 and readwrite-fadvise64-ebadf scenarios using unix.Fadvise(fd, 0, 0, FADV_NORMAL), mirroring the readahead tests. TestReadwriteFadvise64 asserts enter_fadvise64 with Bytes==0 (UNCLASSIFIED: offset/len are hints, not bytes transferred) and positive duration; the ebadf variant asserts enter capture with Bytes==0 on the failing call. Co-Authored-By: Claude Opus 4.8 --- integrationtests/pidfd_test.go | 17 ++++++++++++++- integrationtests/readwrite_test.go | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) (limited to 'integrationtests') diff --git a/integrationtests/pidfd_test.go b/integrationtests/pidfd_test.go index 8df2e13..cbe671d 100644 --- a/integrationtests/pidfd_test.go +++ b/integrationtests/pidfd_test.go @@ -2,7 +2,7 @@ package integrationtests import "testing" -var pidfdTraceArgs = []string{"-trace-syscalls", "pidfd_open,pidfd_getfd,openat,write,close"} +var pidfdTraceArgs = []string{"-trace-syscalls", "pidfd_open,pidfd_getfd,pidfd_send_signal,openat,write,close"} // TestPidfdGetfdSuccess asserts the resolved path of the pidfd_getfd event is // the duplicated source file, NOT the pidfd's anon_inode. @@ -36,3 +36,18 @@ func TestPidfdGetfdFailure(t *testing.T) { }, }, pidfdTraceArgs) } + +// TestPidfdSendSignal asserts ior captures the enter_pidfd_send_signal +// tracepoint when ioworkload issues a pidfd_send_signal liveness probe (sig 0) +// against its own pidfd. The BPF enter handler captures args[0] = the pidfd +// (FamilyIPC, KindFd); the exit is UNCLASSIFIED. Signal 0 delivers nothing, so +// the probe is safe to target self. +func TestPidfdSendSignal(t *testing.T) { + runScenarioResultWithIorArgs(t, "pidfd-send-signal", []ExpectedEvent{ + { + Tracepoint: "enter_pidfd_send_signal", + Comm: "ioworkload", + MinCount: 1, + }, + }, pidfdTraceArgs) +} diff --git a/integrationtests/readwrite_test.go b/integrationtests/readwrite_test.go index 8bfa539..636cc5c 100644 --- a/integrationtests/readwrite_test.go +++ b/integrationtests/readwrite_test.go @@ -302,6 +302,49 @@ func TestReadwriteReadaheadEbadf(t *testing.T) { }, 0) } +func TestReadwriteFadvise64(t *testing.T) { + // fadvise64(2) is KindFd / UNCLASSIFIED: it declares a page-cache access hint + // and returns 0/-1 (no byte count, transfers no bytes to userspace), so the + // tracer must attribute zero bytes (not misread the 0/-1 return or the + // offset/len hint parameters as a byte count) while still capturing the fd + // (args[0]) on enter and timing the syscall. + result, _ := runScenarioResult(t, "readwrite-fadvise64", []ExpectedEvent{ + { + PathContains: "fadvise64file.txt", + Tracepoint: "enter_fadvise64", + Comm: "ioworkload", + MinCount: 1, + }, + }) + exp := ExpectedEvent{ + PathContains: "fadvise64file.txt", + Tracepoint: "enter_fadvise64", + Comm: "ioworkload", + } + // UNCLASSIFIED: no byte count is attributed for a successful fadvise64. + assertEventBytesEqual(t, result, exp, 0) + // Timing is captured end-to-end (enter/exit paired into a duration). + assertEventDurationPositive(t, result, exp) +} + +func TestReadwriteFadvise64Ebadf(t *testing.T) { + // fadvise64 on an invalid fd fails with EBADF, but ior still captures the + // enter_fadvise64 tracepoint because arguments are read on syscall entry + // before the kernel returns the error. The UNCLASSIFIED -1 return must not + // be attributed as bytes. + result, _ := runScenarioResult(t, "readwrite-fadvise64-ebadf", []ExpectedEvent{ + { + Tracepoint: "enter_fadvise64", + Comm: "ioworkload", + MinCount: 1, + }, + }) + assertEventBytesEqual(t, result, ExpectedEvent{ + Tracepoint: "enter_fadvise64", + Comm: "ioworkload", + }, 0) +} + func TestReadwriteCachestat(t *testing.T) { // cachestat(2) is KindFd / UNCLASSIFIED: it queries page-cache residency for // a file and returns 0/-1 (no byte count, no I/O bytes to userspace), so the -- cgit v1.2.3