diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-01 11:14:53 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-01 11:14:53 +0300 |
| commit | 96065ab5c13295f0c2ec810cf540c229c41e2647 (patch) | |
| tree | a8b14025b63d9e2bc2bf59c8100666b10b59cc68 /integrationtests/signals_test.go | |
| parent | 9ff67f7743b039f39e829c062b9f40c148a8e5fe (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/signals_test.go')
| -rw-r--r-- | integrationtests/signals_test.go | 42 |
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"}) +} |
