110namespace CrashHandler {
111#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
112struct iovec IoVec(struct iovec vec)
116struct iovec IoVec(const char *
str)
119 r.iov_base =
const_cast<char *
>(
str);
120 r.iov_len = strlen(
str);
124struct iovec asyncSafeToString(int
n, AsyncSafeIntBuffer &&
result)
128#ifdef __cpp_lib_to_chars
129 }
else if (
auto r = std::to_chars(
ptr,
ptr +
result.array.size(),
n, 10);
r.ec == std::errc{}) {
140 static constexpr int StartingDivider = ([]() {
142 for (
int i = 0; i < std::numeric_limits<int>::digits10; ++
i)
146 int divider = StartingDivider;
147 while (divider &&
n < divider)
151 while (divider > 1) {
152 int quot =
n / divider;
165 r.iov_base =
result.array.data();
173#if defined(Q_OS_LINUX)
174 int fd =
open(
"/proc/self/status", O_RDONLY);
184 const char tracerPidToken[] =
"\nTracerPid:";
185 char *tracerPid = strstr(
buffer, tracerPidToken);
190 tracerPid +=
sizeof(tracerPidToken);
191 long int pid = strtol(tracerPid, &tracerPid, 10);
194#elif defined(Q_OS_WIN)
195 return IsDebuggerPresent();
196#elif defined(Q_OS_MACOS)
198 mach_msg_type_number_t portCount = 0;
199 exception_mask_t masks[EXC_TYPES_COUNT];
200 mach_port_t
ports[EXC_TYPES_COUNT];
201 exception_behavior_t behaviors[EXC_TYPES_COUNT];
202 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
203 exception_mask_t
mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
204 kern_return_t
result = task_get_exception_ports(mach_task_self(),
mask, masks, &portCount,
205 ports, behaviors, flavors);
206 if (
result == KERN_SUCCESS) {
207 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
208 if (MACH_PORT_VALID(
ports[portIndex])) {
221enum DebuggerProgram {
None, Gdb, Lldb };
222static bool hasSystemCrashReporter()
224#if defined(Q_OS_MACOS)
237 if (
ok && disableCoreDump) {
241 if (setrlimit(RLIMIT_CORE, &
limit) != 0)
242 qWarning(
"Failed to disable core dumps: %d", errno);
253 if (
ok && disableStackDump)
256 if (hasSystemCrashReporter())
259#if defined(Q_OS_MACOS)
260 #define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
261 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
262 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
268 auto hasExecutable = [](
const char *execname) {
269 std::string candidate;
271 if (
const char *
p = getenv(
"PATH");
p && *
p)
274 path = _PATH_DEFPATH;
275 for (
const char *
p = std::strtok(&
path[0],
":'");
p;
p = std::strtok(
nullptr,
":")) {
278 candidate += execname;
279 if (QT_ACCESS(candidate.data(), X_OK) == 0)
285 static constexpr DebuggerProgram debuggerSearchOrder[] = {
286# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
292 for (DebuggerProgram candidate : debuggerSearchOrder) {
298 if (hasExecutable(
"gdb")) {
304 if (hasExecutable(
"lldb")) {
314#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
320 writeToStderr(
"\n ",
name ?
name :
"[Non-test]",
321 " function time: ", asyncSafeToString(msecsFunctionTime),
322 "ms, total time: ", asyncSafeToString(msecsTotalTime),
"ms\n");
330# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
331 writeToStderr(
"\n=== Stack trace ===\n");
334 AsyncSafeIntBuffer pidbuffer;
335 asyncSafeToString(getpid(), std::move(pidbuffer));
343 (
void) dup2(STDERR_FILENO, STDOUT_FILENO);
350 execlp(
"gdb",
"gdb",
"--nx",
"--batch",
"-ex",
"thread apply all bt",
351 "--pid", pidbuffer.array.data(),
nullptr);
354 execlp(
"lldb",
"lldb",
"--no-lldbinit",
"--batch",
"-o",
"bt all",
355 "--attach-pid", pidbuffer.array.data(),
nullptr);
359 }
else if (pid < 0) {
360 writeToStderr(
"Failed to start debugger.\n");
366 writeToStderr(
"=== End of stack trace ===\n");
377#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
386 for (
int signo : FatalSignalHandler::fatalSignals)
387 sigdelset(&
set, signo);
389 pthread_sigmask(SIG_BLOCK, &
set,
nullptr);
394void DebugSymbolResolver::cleanup()
397 FreeLibrary(m_dbgHelpLib);
399 m_symFromAddr =
nullptr;
402DebugSymbolResolver::DebugSymbolResolver(
HANDLE process)
403 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(
nullptr)
405 bool success =
false;
406 m_dbgHelpLib = LoadLibraryW(L
"dbghelp.dll");
408 SymInitializeType symInitialize =
reinterpret_cast<SymInitializeType
>(
409 reinterpret_cast<QFunctionPointer
>(GetProcAddress(m_dbgHelpLib,
"SymInitialize")));
410 m_symFromAddr =
reinterpret_cast<SymFromAddrType
>(
411 reinterpret_cast<QFunctionPointer
>(GetProcAddress(m_dbgHelpLib,
"SymFromAddr")));
412 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
418DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64
address)
const
421 struct NamedSymbolInfo :
public DBGHELP_SYMBOL_INFO {
422 enum { symbolNameLength = 255 };
424 char name[symbolNameLength + 1];
430 NamedSymbolInfo symbolBuffer;
431 memset(&symbolBuffer, 0,
sizeof(NamedSymbolInfo));
432 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
433 symbolBuffer.SizeOfStruct =
sizeof(DBGHELP_SYMBOL_INFO);
434 if (!m_symFromAddr(m_process,
address, 0, &symbolBuffer))
437 result.address = symbolBuffer.Address;
441WindowsFaultHandler::WindowsFaultHandler()
443# if !defined(Q_CC_MINGW)
444 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
446 SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
447 SetUnhandledExceptionFilter(windowsFaultHandler);
450LONG WINAPI WindowsFaultHandler::windowsFaultHandler(
struct _EXCEPTION_POINTERS *exInfo)
452 enum { maxStackFrames = 100 };
454 if (!GetModuleFileNameA(NULL, appName,
MAX_PATH))
458 const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
459 fprintf(stderr,
"A crash occurred in %s.\n", appName);
461 fprintf(stderr,
"While testing %s\n",
name);
462 fprintf(stderr,
"Function time: %dms Total time: %dms\n\n"
463 "Exception address: 0x%p\n"
464 "Exception code : 0x%lx\n",
465 msecsFunctionTime, msecsTotalTime, exceptionAddress,
466 exInfo->ExceptionRecord->ExceptionCode);
468 DebugSymbolResolver resolver(GetCurrentProcess());
469 if (resolver.isValid()) {
470 DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
471 if (exceptionSymbol.name) {
472 fprintf(stderr,
"Nearby symbol : %s\n", exceptionSymbol.name);
473 delete [] exceptionSymbol.name;
475 void *stack[maxStackFrames];
476 fputs(
"\nStack:\n", stderr);
477 const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
478 for (
unsigned f = 0;
f < frameCount; ++
f) {
479 DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[
f]));
481 fprintf(stderr,
"#%3u: %s() - 0x%p\n",
f + 1, symbol.name, (
const void *)symbol.address);
482 delete [] symbol.name;
484 fprintf(stderr,
"#%3u: Unable to obtain symbol\n",
f + 1);
491 return EXCEPTION_EXECUTE_HANDLER;
493#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
494bool FatalSignalHandler::pauseOnCrash =
false;
496FatalSignalHandler::FatalSignalHandler()
499 struct sigaction act;
500 memset(&act, 0,
sizeof(act));
501 act.sa_handler = SIG_DFL;
502 oldActions().fill(act);
505 act.sa_flags = SA_RESETHAND | setupAlternateStack();
508 act.sa_flags |= SA_SIGINFO;
509 act.sa_sigaction = FatalSignalHandler::actionHandler;
511 act.sa_handler = FatalSignalHandler::regularHandler;
516 sigemptyset(&act.sa_mask);
517 for (
int signal : fatalSignals)
518 sigaddset(&act.sa_mask,
signal);
520 for (
size_t i = 0;
i < fatalSignals.size(); ++
i)
521 sigaction(fatalSignals[
i], &act, &oldActions()[
i]);
524FatalSignalHandler::~FatalSignalHandler()
528 auto isOurs = [](
const struct sigaction &old) {
530 return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler;
532 return old.sa_handler == FatalSignalHandler::regularHandler;
535 struct sigaction action;
537 for (
size_t i = 0;
i < fatalSignals.size(); ++
i) {
538 struct sigaction &act = oldActions()[
i];
539 if (act.sa_flags == 0 && act.sa_handler == SIG_DFL)
541 if (sigaction(fatalSignals[
i],
nullptr, &action))
544 sigaction(fatalSignals[
i], &act,
nullptr);
547 freeAlternateStack();
550FatalSignalHandler::OldActionsArray &FatalSignalHandler::oldActions()
552 Q_CONSTINIT
static OldActionsArray oldActions {};
556auto FatalSignalHandler::alternateStackSize()
558 struct R {
size_t size, pageSize; };
559 static constexpr size_t MinStackSize = 32 * 1024;
560 size_t pageSize = sysconf(_SC_PAGESIZE);
561 size_t size = SIGSTKSZ;
562 if (
size < MinStackSize) {
566 size = (
size + pageSize - 1) & -pageSize;
569 return R{
size + pageSize, pageSize };
572int FatalSignalHandler::setupAlternateStack()
576# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
579 auto r = alternateStackSize();
580 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
584 alternateStackBase = mmap(
nullptr,
r.size, PROT_READ | PROT_WRITE,
flags, -1, 0);
589 (
void) mprotect(alternateStackBase,
r.pageSize, PROT_NONE);
593 stack.ss_size =
r.size -
r.pageSize;
594 stack.ss_sp =
static_cast<char *
>(alternateStackBase) +
r.pageSize;
595 sigaltstack(&stack,
nullptr);
602void FatalSignalHandler::freeAlternateStack()
604# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
607 stack.ss_flags = SS_DISABLE;
608 sigaltstack(&stack,
nullptr);
609 munmap(alternateStackBase, alternateStackSize().
size);
614void FatalSignalHandler::actionHandler(
int signum, siginfo_t *
info,
void *)
616 writeToStderr(
"Received signal ", asyncSafeToString(signum),
617 " (SIG", signalName(signum),
")");
619 bool isCrashingSignal =
620 std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
621 if (isCrashingSignal && (!
info ||
info->si_code <= 0))
622 isCrashingSignal =
false;
623 if (isCrashingSignal)
624 printCrashingSignalInfo(
info);
625 else if (
info && (
info->si_code == SI_USER ||
info->si_code == SI_QUEUE))
626 printSentSignalInfo(
info);
629 if (signum != SIGINT) {
632 writeToStderr(
"Pausing process ", asyncSafeToString(getpid()),
639 for (
size_t i = 0;
i < fatalSignals.size(); ++
i) {
640 struct sigaction &act = oldActions()[
i];
641 if (signum != fatalSignals[
i])
645 if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
646 (
void) sigaction(signum, &act,
nullptr);
648 if (!isCrashingSignal)