Skip to content

Commit 1f6bf1e

Browse files
committed
ui-smoke: capture a native backtrace on GUI crash
PYTHONFAULTHANDLER stops at the Python frame; a Qt/dbus/GL segfault needs the C stack. Arm a core dump in the launchers and, if the GUI leaves a core, gdb its backtrace into the log. Failure-path only, so green runs pay nothing.
1 parent e92110f commit 1f6bf1e

3 files changed

Lines changed: 55 additions & 0 deletions

File tree

tests/ui-smoke/_lib/crashdump.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
# Native crash capture for the UI smoke launchers. A GUI segfault is the
3+
# failure these tests most need to explain, and it lands in C/C++ (Qt,
4+
# dbus, GL) where PYTHONFAULTHANDLER stops at the event-loop frame. Arm a
5+
# core dump before launch; after the run, if the GUI left a core, print a
6+
# native backtrace into the log so CI shows the faulting frame directly.
7+
# Source with LIB_DIR set; runs only on the failure path, so green runs
8+
# pay nothing.
9+
10+
crashdump_arm() {
11+
CORE_DIR="$(mktemp -d -t ui-smoke-cores.XXXXXX)"
12+
export CORE_DIR
13+
ulimit -c unlimited 2>/dev/null || true
14+
# core_pattern is global and needs root; best-effort (CI has sudo).
15+
# If it does not take, crashdump_report still finds a cwd "core".
16+
sudo sysctl -w "kernel.core_pattern=$CORE_DIR/core.%e.%p" >/dev/null 2>&1 || true
17+
}
18+
19+
crashdump_report() {
20+
[ -n "${CORE_DIR:-}" ] || return 0
21+
local core
22+
# shellcheck disable=SC2012 # mktemp dir, no odd filenames
23+
core=$(ls -t "$CORE_DIR"/core* ./core* /tmp/core* 2>/dev/null | head -1)
24+
if [ -n "$core" ]; then
25+
echo "=== crash: native backtrace ($core) ==="
26+
# gdb is not on the CI runner by default; pull it in to read the core.
27+
command -v gdb >/dev/null 2>&1 || sudo apt-get install -y -q gdb >/dev/null 2>&1 || true
28+
if command -v gdb >/dev/null 2>&1; then
29+
# "bt" first: gdb auto-selects the faulting thread on a SIGSEGV
30+
# core. "thread apply all bt" after gives the rest.
31+
gdb -batch -nx \
32+
-ex "bt" \
33+
-ex "echo \n=== all threads ===\n" \
34+
-ex "thread apply all bt" \
35+
"$(command -v python3)" "$core" 2>&1 | head -400
36+
else
37+
echo "(gdb unavailable; core left at $core)"
38+
fi
39+
fi
40+
rm -rf "$CORE_DIR"
41+
}

tests/ui-smoke/_lib/launch.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ DRIVER_TIMEOUT=180
3939
# launch-env.sh so launch.sh and quit-launch.sh cannot drift apart.
4040
. "$LIB_DIR/launch-env.sh"
4141

42+
# Arm a core dump so a GUI segfault can be backtraced after the run.
43+
. "$LIB_DIR/crashdump.sh"
44+
crashdump_arm
45+
4246
# Export the per-invocation values so the inner bash -c receives them
4347
# as proper env vars (avoids embedding paths into the inner script
4448
# via quoting, which breaks on apostrophes / spaces).
@@ -102,4 +106,7 @@ echo "=== ui-smoke.out ==="
102106
echo "=== ui-smoke.err ==="
103107
[ -f ui-smoke.err ] && cat ui-smoke.err
104108

109+
# If the GUI dumped a core, print its native backtrace.
110+
crashdump_report
111+
105112
exit "$RC"

tests/ui-smoke/_lib/quit-launch.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ QUIT_GRACE=15
3939
# launch-env.sh so launch.sh and quit-launch.sh cannot drift apart.
4040
. "$LIB_DIR/launch-env.sh"
4141

42+
# Arm a core dump so a GUI segfault can be backtraced after the run.
43+
. "$LIB_DIR/crashdump.sh"
44+
crashdump_arm
45+
4246
export CONFIG_INI LIB_DIR DRIVER_TIMEOUT GUI_MATCH QUIT_GRACE
4347

4448
# shellcheck disable=SC2016
@@ -117,4 +121,7 @@ echo "=== linuxcnc.err ==="
117121
echo "=== ui-smoke.out ==="
118122
[ -f ui-smoke.out ] && cat ui-smoke.out
119123

124+
# If the GUI dumped a core, print its native backtrace.
125+
crashdump_report
126+
120127
exit "$RC"

0 commit comments

Comments
 (0)