diff options
author | Thiago Macieira <[email protected]> | 2025-05-14 18:51:11 -0700 |
---|---|---|
committer | Thiago Macieira <[email protected]> | 2025-05-25 13:24:27 -0700 |
commit | 33435ce5678c795ac099708bc5079f8fd5ff01eb (patch) | |
tree | 8ad466d4173db4be8b0551ca4ceccab735e98ccb | |
parent | 4fcd8546573ddc0b933378f7aa8530103cf76910 (diff) |
QTest::CrashHandler: prepare to disassemble the crashing instruction
This is the infrastructure code, without actually getting the
instruction pointer from the machine context.
Change-Id: Iadd2c78913b2d0177949fffdeafa12e9fc3daf87
Reviewed-by: Edward Welbourne <[email protected]>
-rw-r--r-- | src/testlib/qtestcrashhandler_p.h | 2 | ||||
-rw-r--r-- | src/testlib/qtestcrashhandler_unix.cpp | 46 | ||||
-rw-r--r-- | src/testlib/qtestcrashhandler_win.cpp | 3 |
3 files changed, 38 insertions, 13 deletions
diff --git a/src/testlib/qtestcrashhandler_p.h b/src/testlib/qtestcrashhandler_p.h index 80ec18a3efe..33323e48584 100644 --- a/src/testlib/qtestcrashhandler_p.h +++ b/src/testlib/qtestcrashhandler_p.h @@ -41,7 +41,7 @@ namespace CrashHandler { #if !defined(Q_OS_WASM) || QT_CONFIG(thread) void printTestRunTime(); - void generateStackTrace(); + void generateStackTrace(quintptr ip = 0); #endif void maybeDisableCoreDump(); diff --git a/src/testlib/qtestcrashhandler_unix.cpp b/src/testlib/qtestcrashhandler_unix.cpp index 15ba4bb1c7f..ca10efa8e2f 100644 --- a/src/testlib/qtestcrashhandler_unix.cpp +++ b/src/testlib/qtestcrashhandler_unix.cpp @@ -358,7 +358,14 @@ void printTestRunTime() "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n"); } -void generateStackTrace() +static quintptr getProgramCounter(void *ucontext) +{ + quintptr pc = 0; + Q_UNUSED(ucontext); + return pc; +} + +void generateStackTrace(quintptr ip) { if (debugger == None || alreadyDebugging()) return; @@ -383,6 +390,14 @@ void generateStackTrace() // child process (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr + // disassemble the crashing instruction, if known + // (syntax is the same for gdb and lldb) + char disasmInstr[sizeof("x/i 0x") + sizeof(ip) * 2] = {}; // zero-init for terminator + if (ip) { + strcpy(disasmInstr, "x/i "); + asyncSafeToHexString(ip, disasmInstr + strlen(disasmInstr)); + } + struct Args { std::array<const char *, 16> argv; int count = 0; @@ -400,15 +415,19 @@ void generateStackTrace() Q_UNREACHABLE(); break; case Gdb: - argv << "gdb" << "--nx" << "--batch" - << "-ex" << "thread apply all bt" + argv << "gdb" << "--nx" << "--batch"; + if (ip) + argv << "-ex" << disasmInstr; + argv << "-ex" << "thread apply all bt" << "-ex" << "printf \"\\n\"" << "-ex" << "info proc mappings" << "--pid"; break; case Lldb: - argv << "lldb" << "--no-lldbinit" << "--batch" - << "-o" << "bt all" + argv << "lldb" << "--no-lldbinit" << "--batch"; + if (ip) + argv << "-o" << disasmInstr; + argv << "-o" << "bt all" << "--attach-pid"; break; } @@ -425,6 +444,8 @@ void generateStackTrace() } writeToStderr("=== End of stack trace ===\n"); +# else + Q_UNUSED(ip); # endif // !Q_OS_INTEGRITY && !Q_OS_VXWORKS } @@ -453,14 +474,16 @@ printSentSignalInfo(T *info) [[maybe_unused]] static void printSentSignalInfo(...) {} template <typename T> static std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1> -printCrashingSignalInfo(T *info) +printCrashingSignalInfo(T *info, quintptr pc) { using HexString = std::array<char, sizeof(quintptr) * 2 + 2>; auto toHexString = [](quintptr u, HexString &&r = {}) { return asyncSafeToHexString(u, r.data()); }; - writeToStderr(", code ", asyncSafeToString(info->si_code), - ", for address ", toHexString(quintptr(info->si_addr))); + writeToStderr(", code ", asyncSafeToString(info->si_code)); + if (pc) + writeToStderr(", at instruction address ", toHexString(pc)); + writeToStderr(", accessing address ", toHexString(quintptr(info->si_addr))); } [[maybe_unused]] static void printCrashingSignalInfo(...) {} @@ -581,23 +604,24 @@ void FatalSignalHandler::freeAlternateStack() # endif } -void actionHandler(int signum, siginfo_t *info, void *) +void actionHandler(int signum, siginfo_t *info, void *ucontext) { writeToStderr("Received signal ", asyncSafeToString(signum), " (SIG", signalName(signum), ")"); + quintptr pc = 0; bool isCrashingSignal = std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end(); if (isCrashingSignal && (!info || info->si_code <= 0)) isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash if (isCrashingSignal) - printCrashingSignalInfo(info); + printCrashingSignalInfo(info, (pc = getProgramCounter(ucontext))); else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE)) printSentSignalInfo(info); printTestRunTime(); if (signum != SIGINT) { - generateStackTrace(); + generateStackTrace(pc); if (pauseOnCrash) { writeToStderr("Pausing process ", asyncSafeToString(getpid()), " for debugging\n"); diff --git a/src/testlib/qtestcrashhandler_win.cpp b/src/testlib/qtestcrashhandler_win.cpp index 62c6909f9c7..8b02bd39662 100644 --- a/src/testlib/qtestcrashhandler_win.cpp +++ b/src/testlib/qtestcrashhandler_win.cpp @@ -57,12 +57,13 @@ void printTestRunTime() name ? name : "[Non-test]", msecsFunctionTime, msecsTotalTime); } -void generateStackTrace() +void generateStackTrace(quintptr ip) { if (debugger == None || alreadyDebugging()) return; // ### Implement starting a debugger on Windows + Q_UNUSED(ip); } void blockUnixSignals() |