]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tools: add a test wrapper that replays crashing tests under gdb
authorDaan De Meyer <daan@amutable.com>
Sat, 16 May 2026 19:09:12 +0000 (19:09 +0000)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 19 May 2026 21:14:32 +0000 (23:14 +0200)
meson test --wrapper hook to print a gdb backtrace inline in the test
log when a test exits with an actual crash signal (SIGSEGV, SIGABRT,
SIGBUS, SIGFPE, SIGILL). Wired into the default add_test_setup() so it
runs automatically on every `meson test`.

Environmental terminations (SIGTERM/SIGKILL/SIGPIPE/SIGALRM) are passed
through without replay, and the original signal is re-raised so the
parent's wait() observes WIFSIGNALED rather than a plain exit code.

.packit.yml
meson.build
tools/test-crash-trace.sh [new file with mode: 0755]

index 97e1e588550482565faf1c9ebf9c97e7f8cbc102..e0aa839d925c20703c6772377f9b559cf5beebfc 100644 (file)
@@ -28,12 +28,24 @@ actions:
 jobs:
 - job: copr_build
   trigger: pull_request
+  # Install gdb into the buildroot so post-failure hooks can pull backtraces from any cores the
+  # %check phase leaves behind.
   targets:
-  - fedora-rawhide-aarch64
-  - fedora-rawhide-i386
-  - fedora-rawhide-ppc64le
-  - fedora-rawhide-s390x
-  - fedora-rawhide-x86_64
+    fedora-rawhide-aarch64:
+      additional_packages:
+        - gdb
+    fedora-rawhide-i386:
+      additional_packages:
+        - gdb
+    fedora-rawhide-ppc64le:
+      additional_packages:
+        - gdb
+    fedora-rawhide-s390x:
+      additional_packages:
+        - gdb
+    fedora-rawhide-x86_64:
+      additional_packages:
+        - gdb
 
 - job: tests
   trigger: pull_request
index c76471e1fe4ff0be5f1e933555bd495fe9382873..d9c8b84e4707b5914e0c0e7efbd4544322859616 100644 (file)
@@ -16,6 +16,7 @@ project('systemd', 'c',
 add_test_setup(
         'default',
         exclude_suites : ['clang-tidy', 'unused-symbols', 'integration-tests'],
+        exe_wrapper : find_program('tools/test-crash-trace.sh'),
         is_default : true,
 )
 
diff --git a/tools/test-crash-trace.sh b/tools/test-crash-trace.sh
new file mode 100755 (executable)
index 0000000..d4a8a60
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# meson test wrapper that replays crashing tests under gdb and prints a
+# backtrace, so SIGSEGV/SIGABRT/etc. failures in CI show a stack trace inline
+# in the test log instead of just an exit code.
+#
+# Usage: meson test --wrapper=$PWD/tools/test-crash-trace.sh
+
+set -euo pipefail
+
+if [[ $# -eq 0 ]]; then
+    echo "usage: $0 <command> [args...]" >&2
+    exit 2
+fi
+
+rc=0
+"$@" || rc=$?
+
+# Replay only on actual crash signals (128 + signal): SIGILL=132, SIGABRT=134,
+# SIGBUS=135, SIGFPE=136, SIGSEGV=139. SIGTERM/SIGKILL/SIGPIPE/SIGALRM mean the
+# test was killed by the environment (timeout, etc.), not that it crashed —
+# replaying those just makes gdb hit the same kill.
+case "$rc" in
+    132|134|135|136|139)
+        if command -v gdb >/dev/null 2>&1; then
+            echo "===== exit $rc — replaying under gdb =====" >&2
+            style_args=()
+            if [[ -t 2 ]]; then
+                style_args=(--ex 'set style enabled on')
+            fi
+            gdb --batch \
+                --ex 'set pagination off' \
+                ${style_args[@]+"${style_args[@]}"} \
+                --ex 'run' \
+                --ex 'thread apply all bt full' \
+                --args "$@" >&2 || true
+        else
+            echo "===== exit $rc — install gdb for a backtrace =====" >&2
+        fi
+        ;;
+esac
+
+# If the child died by signal, re-raise it so the parent's wait() observes
+# WIFSIGNALED instead of a plain exit code. Best-effort: SIGKILL can't be
+# delivered to ourselves and would have killed us already.
+if [[ $rc -gt 128 ]]; then
+    sig=$((rc - 128))
+    trap - "$sig" 2>/dev/null || true
+    # Suppress the wrapper bash's own core dump from the re-raised crash signal;
+    # the original test binary already had its chance to dump core on line 18.
+    ulimit -c 0
+    kill -s "$sig" $$ 2>/dev/null || true
+fi
+
+exit "$rc"