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 <edward.welbourne@qt.io>
This commit is contained in:
parent
4fcd854657
commit
33435ce567
@ -41,7 +41,7 @@ namespace CrashHandler {
|
|||||||
|
|
||||||
#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
|
#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
|
||||||
void printTestRunTime();
|
void printTestRunTime();
|
||||||
void generateStackTrace();
|
void generateStackTrace(quintptr ip = 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void maybeDisableCoreDump();
|
void maybeDisableCoreDump();
|
||||||
|
@ -358,7 +358,14 @@ void printTestRunTime()
|
|||||||
"ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n");
|
"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())
|
if (debugger == None || alreadyDebugging())
|
||||||
return;
|
return;
|
||||||
@ -383,6 +390,14 @@ void generateStackTrace()
|
|||||||
// child process
|
// child process
|
||||||
(void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
|
(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 {
|
struct Args {
|
||||||
std::array<const char *, 16> argv;
|
std::array<const char *, 16> argv;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -400,15 +415,19 @@ void generateStackTrace()
|
|||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
break;
|
break;
|
||||||
case Gdb:
|
case Gdb:
|
||||||
argv << "gdb" << "--nx" << "--batch"
|
argv << "gdb" << "--nx" << "--batch";
|
||||||
<< "-ex" << "thread apply all bt"
|
if (ip)
|
||||||
|
argv << "-ex" << disasmInstr;
|
||||||
|
argv << "-ex" << "thread apply all bt"
|
||||||
<< "-ex" << "printf \"\\n\""
|
<< "-ex" << "printf \"\\n\""
|
||||||
<< "-ex" << "info proc mappings"
|
<< "-ex" << "info proc mappings"
|
||||||
<< "--pid";
|
<< "--pid";
|
||||||
break;
|
break;
|
||||||
case Lldb:
|
case Lldb:
|
||||||
argv << "lldb" << "--no-lldbinit" << "--batch"
|
argv << "lldb" << "--no-lldbinit" << "--batch";
|
||||||
<< "-o" << "bt all"
|
if (ip)
|
||||||
|
argv << "-o" << disasmInstr;
|
||||||
|
argv << "-o" << "bt all"
|
||||||
<< "--attach-pid";
|
<< "--attach-pid";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -425,6 +444,8 @@ void generateStackTrace()
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeToStderr("=== End of stack trace ===\n");
|
writeToStderr("=== End of stack trace ===\n");
|
||||||
|
# else
|
||||||
|
Q_UNUSED(ip);
|
||||||
# endif // !Q_OS_INTEGRITY && !Q_OS_VXWORKS
|
# endif // !Q_OS_INTEGRITY && !Q_OS_VXWORKS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,14 +474,16 @@ printSentSignalInfo(T *info)
|
|||||||
[[maybe_unused]] static void printSentSignalInfo(...) {}
|
[[maybe_unused]] static void printSentSignalInfo(...) {}
|
||||||
|
|
||||||
template <typename T> static std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1>
|
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>;
|
using HexString = std::array<char, sizeof(quintptr) * 2 + 2>;
|
||||||
auto toHexString = [](quintptr u, HexString &&r = {}) {
|
auto toHexString = [](quintptr u, HexString &&r = {}) {
|
||||||
return asyncSafeToHexString(u, r.data());
|
return asyncSafeToHexString(u, r.data());
|
||||||
};
|
};
|
||||||
writeToStderr(", code ", asyncSafeToString(info->si_code),
|
writeToStderr(", code ", asyncSafeToString(info->si_code));
|
||||||
", for address ", toHexString(quintptr(info->si_addr)));
|
if (pc)
|
||||||
|
writeToStderr(", at instruction address ", toHexString(pc));
|
||||||
|
writeToStderr(", accessing address ", toHexString(quintptr(info->si_addr)));
|
||||||
}
|
}
|
||||||
[[maybe_unused]] static void printCrashingSignalInfo(...) {}
|
[[maybe_unused]] static void printCrashingSignalInfo(...) {}
|
||||||
|
|
||||||
@ -581,23 +604,24 @@ void FatalSignalHandler::freeAlternateStack()
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void actionHandler(int signum, siginfo_t *info, void *)
|
void actionHandler(int signum, siginfo_t *info, void *ucontext)
|
||||||
{
|
{
|
||||||
writeToStderr("Received signal ", asyncSafeToString(signum),
|
writeToStderr("Received signal ", asyncSafeToString(signum),
|
||||||
" (SIG", signalName(signum), ")");
|
" (SIG", signalName(signum), ")");
|
||||||
|
|
||||||
|
quintptr pc = 0;
|
||||||
bool isCrashingSignal =
|
bool isCrashingSignal =
|
||||||
std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
|
std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
|
||||||
if (isCrashingSignal && (!info || info->si_code <= 0))
|
if (isCrashingSignal && (!info || info->si_code <= 0))
|
||||||
isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
|
isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
|
||||||
if (isCrashingSignal)
|
if (isCrashingSignal)
|
||||||
printCrashingSignalInfo(info);
|
printCrashingSignalInfo(info, (pc = getProgramCounter(ucontext)));
|
||||||
else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
|
else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
|
||||||
printSentSignalInfo(info);
|
printSentSignalInfo(info);
|
||||||
|
|
||||||
printTestRunTime();
|
printTestRunTime();
|
||||||
if (signum != SIGINT) {
|
if (signum != SIGINT) {
|
||||||
generateStackTrace();
|
generateStackTrace(pc);
|
||||||
if (pauseOnCrash) {
|
if (pauseOnCrash) {
|
||||||
writeToStderr("Pausing process ", asyncSafeToString(getpid()),
|
writeToStderr("Pausing process ", asyncSafeToString(getpid()),
|
||||||
" for debugging\n");
|
" for debugging\n");
|
||||||
|
@ -57,12 +57,13 @@ void printTestRunTime()
|
|||||||
name ? name : "[Non-test]", msecsFunctionTime, msecsTotalTime);
|
name ? name : "[Non-test]", msecsFunctionTime, msecsTotalTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateStackTrace()
|
void generateStackTrace(quintptr ip)
|
||||||
{
|
{
|
||||||
if (debugger == None || alreadyDebugging())
|
if (debugger == None || alreadyDebugging())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// ### Implement starting a debugger on Windows
|
// ### Implement starting a debugger on Windows
|
||||||
|
Q_UNUSED(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockUnixSignals()
|
void blockUnixSignals()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user