summaryrefslogtreecommitdiff
path: root/integrationtests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-01 11:14:53 +0300
committerPaul Buetow <paul@buetow.org>2026-06-01 11:14:53 +0300
commit96065ab5c13295f0c2ec810cf540c229c41e2647 (patch)
treea8b14025b63d9e2bc2bf59c8100666b10b59cc68 /integrationtests
parent9ff67f7743b039f39e829c062b9f40c148a8e5fe (diff)
test(integration): add Signals family tracing coverage
The SIGNALS syscall family previously had zero end-to-end coverage (signalfd/signalfd4 are IPC-family fd creators, not Signals). Add a self-targeting ioworkload scenario and an integration test that assert the family's tracepoints fire. scenario_signals.go (signals-basic) issues, all self-directed so it mutates no other process: - rt_sigaction : install SIG_IGN disposition for SIGUSR1 - rt_sigprocmask: BLOCK SIGUSR1 before sending, so self-delivery only marks it pending (never runs a handler / kills us) - sigaltstack : set then disable an alternate signal stack - kill/tgkill/tkill/rt_sigqueueinfo: send SIGUSR1 to self four ways - rt_sigpending : query the pending mask - rt_sigtimedwait: reap the pending signal with a SHORT 100ms timeout (hang guard); EAGAIN tolerated Safety: signal is blocked before any send; rt_sigtimedwait uses a 100ms timeout so it cannot hang; the original signal mask and SIGUSR1 disposition are restored and the alt stack disabled on exit. The goroutine is pinned with LockOSThread so gettid() matches the tgkill/tkill target and the per-thread mask applies to the waiting thread. Raw syscalls are issued directly so the tracepoints fire regardless of the Go runtime's own signal handling. pause (noreturn) and rt_sigreturn (handler-return only) are deliberately excluded. signals_test.go asserts enter_ tracepoints with MinCount>=1 for rt_sigaction, rt_sigprocmask, rt_sigpending, sigaltstack, kill, tgkill, and rt_sigtimedwait, plus a positive duration for the rt_sigtimedwait enter/exit pair. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'integrationtests')
-rw-r--r--integrationtests/signals_test.go42
1 files changed, 42 insertions, 0 deletions
diff --git a/integrationtests/signals_test.go b/integrationtests/signals_test.go
new file mode 100644
index 0000000..98840aa
--- /dev/null
+++ b/integrationtests/signals_test.go
@@ -0,0 +1,42 @@
+package integrationtests
+
+import "testing"
+
+// signalsTraceArgs restricts tracing to the SIGNALS-family syscalls the
+// signals-basic workload issues, so the output is dominated by those calls.
+// pause (noreturn-blocking) and rt_sigreturn (only reachable via handler
+// return) are intentionally absent: the scenario never invokes them.
+var signalsTraceArgs = []string{
+ "-trace-syscalls",
+ "rt_sigaction,rt_sigprocmask,rt_sigpending,sigaltstack,kill,tgkill,tkill,rt_sigqueueinfo,rt_sigtimedwait",
+}
+
+// TestSignalsBasic verifies the SIGNALS syscall family is traced end-to-end.
+// The signals-basic workload self-directs every call (installs a handler,
+// blocks SIGUSR1, sets an alternate stack, sends SIGUSR1 to itself four ways,
+// queries pending signals, and reaps one with a short timeout), so it never
+// touches another process. Each required syscall must appear as an enter event.
+func TestSignalsBasic(t *testing.T) {
+ h := newTestHarness(t)
+ result, pid, err := h.RunWithIorArgs("signals-basic", defaultDuration, signalsTraceArgs)
+ if err != nil {
+ t.Fatalf("run scenario signals-basic: %v", err)
+ }
+
+ AssertNoUnexpectedPID(t, result, pid)
+ AssertNoUnexpectedComm(t, result, "ioworkload")
+ AssertEventsPresent(t, result, []ExpectedEvent{
+ {Tracepoint: "enter_rt_sigaction", Comm: "ioworkload", MinCount: 1},
+ {Tracepoint: "enter_rt_sigprocmask", Comm: "ioworkload", MinCount: 1},
+ {Tracepoint: "enter_rt_sigpending", Comm: "ioworkload", MinCount: 1},
+ {Tracepoint: "enter_sigaltstack", Comm: "ioworkload", MinCount: 1},
+ {Tracepoint: "enter_kill", Comm: "ioworkload", MinCount: 1},
+ {Tracepoint: "enter_tgkill", Comm: "ioworkload", MinCount: 1},
+ {Tracepoint: "enter_rt_sigtimedwait", Comm: "ioworkload", MinCount: 1},
+ })
+
+ // rt_sigtimedwait blocks (briefly) on its timeout, so the paired enter/exit
+ // must carry a positive duration — proof the exit tracepoint was correlated.
+ assertEventDurationPositive(t, result,
+ ExpectedEvent{Tracepoint: "enter_rt_sigtimedwait", Comm: "ioworkload"})
+}