|
2 | 2 | # Run a test binary under debug CI. On fatal signals, print post-mortem |
3 | 3 | # backtraces from core dumps when available. Linux also runs under catchsegv |
4 | 4 | # so a partial backtrace appears in the log even without a core file. |
| 5 | +# |
| 6 | +# When LIVEKIT_TEST_STALL_SECONDS is set to a positive integer, a watchdog |
| 7 | +# monitors test output and dumps live thread backtraces if the log goes silent |
| 8 | +# for that many seconds (integration-test hang diagnostics on linux-x64). |
5 | 9 | set -uo pipefail |
6 | 10 |
|
7 | 11 | usage() { |
@@ -57,6 +61,36 @@ dump_macos_crash_reports() { |
57 | 61 | fi |
58 | 62 | } |
59 | 63 |
|
| 64 | +dump_live_backtraces() { |
| 65 | + local test_pid=$1 |
| 66 | + local reason=$2 |
| 67 | + |
| 68 | + echo "=== live backtrace diagnostics (${reason}, pid ${test_pid}) ===" |
| 69 | + |
| 70 | + if [[ "$(uname -s)" == "Linux" ]]; then |
| 71 | + if command -v gdb >/dev/null 2>&1; then |
| 72 | + gdb -batch \ |
| 73 | + -ex 'set pagination off' \ |
| 74 | + -ex 'thread apply all bt full' \ |
| 75 | + -p "${test_pid}" || true |
| 76 | + else |
| 77 | + echo "gdb not available; install gdb for live backtraces" |
| 78 | + fi |
| 79 | + return 0 |
| 80 | + fi |
| 81 | + |
| 82 | + if [[ "$(uname -s)" == "Darwin" ]]; then |
| 83 | + if command -v sample >/dev/null 2>&1; then |
| 84 | + sample "${test_pid}" 5 -mayDie 2>&1 || true |
| 85 | + fi |
| 86 | + if command -v lldb >/dev/null 2>&1; then |
| 87 | + lldb -p "${test_pid}" --batch -o 'thread backtrace all' -o 'detach' -o 'quit' 2>&1 || true |
| 88 | + else |
| 89 | + echo "lldb not available" |
| 90 | + fi |
| 91 | + fi |
| 92 | +} |
| 93 | + |
60 | 94 | dump_backtraces() { |
61 | 95 | local test_pid=$1 |
62 | 96 | local status=$2 |
@@ -122,11 +156,57 @@ run_test() { |
122 | 156 | fi |
123 | 157 | } |
124 | 158 |
|
| 159 | +start_stall_watchdog() { |
| 160 | + local test_pid=$1 |
| 161 | + local log_file=$2 |
| 162 | + local stall_limit=$3 |
| 163 | + |
| 164 | + ( |
| 165 | + local last_size=-1 |
| 166 | + local stall=0 |
| 167 | + while kill -0 "${test_pid}" 2>/dev/null; do |
| 168 | + local size |
| 169 | + size=$(wc -c <"${log_file}" 2>/dev/null || echo 0) |
| 170 | + if [[ "${size}" == "${last_size}" ]]; then |
| 171 | + stall=$((stall + 5)) |
| 172 | + else |
| 173 | + stall=0 |
| 174 | + last_size=${size} |
| 175 | + fi |
| 176 | + if ((stall >= stall_limit)); then |
| 177 | + echo "=== TEST HANG DETECTED: no output for ${stall}s (pid ${test_pid}) ===" |
| 178 | + echo "--- last log lines ---" |
| 179 | + tail -n 40 "${log_file}" || true |
| 180 | + dump_live_backtraces "${test_pid}" "stall ${stall}s" |
| 181 | + kill -ABRT "${test_pid}" 2>/dev/null || kill -TERM "${test_pid}" 2>/dev/null || true |
| 182 | + break |
| 183 | + fi |
| 184 | + sleep 5 |
| 185 | + done |
| 186 | + ) & |
| 187 | + echo $! |
| 188 | +} |
| 189 | + |
| 190 | +stall_limit=${LIVEKIT_TEST_STALL_SECONDS:-0} |
| 191 | +log_file="${RUNNER_TEMP:-/tmp}/livekit-test-output.log" |
| 192 | + |
125 | 193 | set +e |
126 | | -run_test "$@" & |
127 | | -test_pid=$! |
128 | | -wait "${test_pid}" |
129 | | -status=$? |
| 194 | +if ((stall_limit > 0)); then |
| 195 | + : >"${log_file}" |
| 196 | + run_test "$@" >"${log_file}" 2>&1 & |
| 197 | + test_pid=$! |
| 198 | + watchdog_pid=$(start_stall_watchdog "${test_pid}" "${log_file}" "${stall_limit}") |
| 199 | + wait "${test_pid}" |
| 200 | + status=$? |
| 201 | + kill "${watchdog_pid}" 2>/dev/null || true |
| 202 | + wait "${watchdog_pid}" 2>/dev/null || true |
| 203 | + cat "${log_file}" |
| 204 | +else |
| 205 | + run_test "$@" & |
| 206 | + test_pid=$! |
| 207 | + wait "${test_pid}" |
| 208 | + status=$? |
| 209 | +fi |
130 | 210 | set -e |
131 | 211 |
|
132 | 212 | if ((status > 128)); then |
|
0 commit comments