diff --git a/src/testlib/qtestcrashhandler_unix.cpp b/src/testlib/qtestcrashhandler_unix.cpp index a89103028a2..f0ffe36dd66 100644 --- a/src/testlib/qtestcrashhandler_unix.cpp +++ b/src/testlib/qtestcrashhandler_unix.cpp @@ -2,53 +2,14 @@ // Copyright (C) 2024 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#if QT_CONFIG(batch_test_support) -#include -#endif // QT_CONFIG(batch_test_support) -#include -#include -#if defined(HAVE_XCTEST) -#include -#endif -#if defined Q_OS_MACOS -#include -#endif - -#if defined(Q_OS_DARWIN) -#include -#endif #if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014 # include @@ -66,9 +27,6 @@ #include #endif -#ifdef Q_OS_UNIX -#include - #include #if __has_include() # include @@ -81,6 +39,7 @@ # if !defined(Q_OS_INTEGRITY) # include # endif + # ifndef _PATH_DEFPATH # define _PATH_DEFPATH "/usr/bin:/bin" # endif @@ -90,9 +49,11 @@ # ifndef SA_RESETHAND # define SA_RESETHAND 0 # endif -#endif #if defined(Q_OS_MACOS) +#include +#include + #include #include #include @@ -109,7 +70,6 @@ using namespace Qt::StringLiterals; namespace QTest { namespace CrashHandler { -#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread)) struct iovec IoVec(struct iovec vec) { return vec; @@ -167,7 +127,6 @@ struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result) r.iov_len = ptr - result.array.data(); return r; }; -#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread)) bool alreadyDebugging() { @@ -192,8 +151,6 @@ bool alreadyDebugging() long int pid = strtol(tracerPid, &tracerPid, 10); close(fd); return pid != 0; -#elif defined(Q_OS_WIN) - return IsDebuggerPresent(); #elif defined(Q_OS_MACOS) // Check if there is an exception handler for the process: mach_msg_type_number_t portCount = 0; @@ -270,7 +227,6 @@ void prepareStackTrace() # endif #endif -#ifdef Q_OS_UNIX // like QStandardPaths::findExecutable(), but simpler auto hasExecutable = [](const char *execname) { std::string candidate; @@ -315,10 +271,8 @@ void prepareStackTrace() break; } } -#endif // Q_OS_UNIX } -#if !defined(Q_OS_WASM) || QT_CONFIG(thread) void printTestRunTime() { const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); @@ -339,7 +293,7 @@ void generateStackTrace() (void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); # endif -# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS) +# if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS) writeToStderr("\n=== Stack trace ===\n"); // execlp() requires null-termination, so call the default constructor @@ -377,17 +331,10 @@ void generateStackTrace() } writeToStderr("=== End of stack trace ===\n"); -# endif // Q_OS_UNIX && !Q_OS_WASM && !Q_OS_INTEGRITY && !Q_OS_VXWORKS +# endif // !Q_OS_INTEGRITY && !Q_OS_VXWORKS } -#endif // !defined(Q_OS_WASM) || QT_CONFIG(thread) -#if defined(Q_OS_WIN) -void blockUnixSignals() -{ - // Windows does have C signals, but doesn't use them for the purposes we're - // talking about here -} -#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM) +#ifndef Q_OS_WASM // no signal handling for WASM void blockUnixSignals() { // Block most Unix signals so the WatchDog thread won't be called when @@ -401,109 +348,7 @@ void blockUnixSignals() pthread_sigmask(SIG_BLOCK, &set, nullptr); } -#endif // Q_OS_* choice -#if defined(Q_OS_WIN) -void DebugSymbolResolver::cleanup() -{ - if (m_dbgHelpLib) - FreeLibrary(m_dbgHelpLib); - m_dbgHelpLib = 0; - m_symFromAddr = nullptr; -} - -DebugSymbolResolver::DebugSymbolResolver(HANDLE process) - : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr) -{ - bool success = false; - m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll"); - if (m_dbgHelpLib) { - SymInitializeType symInitialize = reinterpret_cast( - reinterpret_cast(GetProcAddress(m_dbgHelpLib, "SymInitialize"))); - m_symFromAddr = reinterpret_cast( - reinterpret_cast(GetProcAddress(m_dbgHelpLib, "SymFromAddr"))); - success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE); - } - if (!success) - cleanup(); -} - -DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const -{ - // reserve additional buffer where SymFromAddr() will store the name - struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO { - enum { symbolNameLength = 255 }; - - char name[symbolNameLength + 1]; - }; - - Symbol result; - if (!isValid()) - return result; - NamedSymbolInfo symbolBuffer; - memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo)); - symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength; - symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO); - if (!m_symFromAddr(m_process, address, 0, &symbolBuffer)) - return result; - result.name = qstrdup(symbolBuffer.Name); - result.address = symbolBuffer.Address; - return result; -} - -WindowsFaultHandler::WindowsFaultHandler() -{ -# if !defined(Q_CC_MINGW) - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); -# endif - SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); - SetUnhandledExceptionFilter(windowsFaultHandler); -} - -LONG WINAPI WindowsFaultHandler::windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo) -{ - enum { maxStackFrames = 100 }; - char appName[MAX_PATH]; - if (!GetModuleFileNameA(NULL, appName, MAX_PATH)) - appName[0] = 0; - const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); - const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); - const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress; - fprintf(stderr, "A crash occurred in %s.\n", appName); - if (const char *name = QTest::currentTestFunction()) - fprintf(stderr, "While testing %s\n", name); - fprintf(stderr, "Function time: %dms Total time: %dms\n\n" - "Exception address: 0x%p\n" - "Exception code : 0x%lx\n", - msecsFunctionTime, msecsTotalTime, exceptionAddress, - exInfo->ExceptionRecord->ExceptionCode); - - DebugSymbolResolver resolver(GetCurrentProcess()); - if (resolver.isValid()) { - DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress)); - if (exceptionSymbol.name) { - fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name); - delete [] exceptionSymbol.name; - } - Q_DECL_UNINITIALIZED void *stack[maxStackFrames]; - fputs("\nStack:\n", stderr); - const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL); - for (unsigned f = 0; f < frameCount; ++f) { - DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f])); - if (symbol.name) { - fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address); - delete [] symbol.name; - } else { - fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1); - } - } - } - - fputc('\n', stderr); - - return EXCEPTION_EXECUTE_HANDLER; -} -#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM) bool FatalSignalHandler::pauseOnCrash = false; FatalSignalHandler::FatalSignalHandler() @@ -668,7 +513,7 @@ void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *) // we shouldn't reach here! std::abort(); } -#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) +#endif // !defined(Q_OS_WASM) } // namespace CrashHandler } // namespace QTest diff --git a/src/testlib/qtestcrashhandler_win.cpp b/src/testlib/qtestcrashhandler_win.cpp index a89103028a2..67e46ab221c 100644 --- a/src/testlib/qtestcrashhandler_win.cpp +++ b/src/testlib/qtestcrashhandler_win.cpp @@ -2,249 +2,33 @@ // Copyright (C) 2024 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include +#include #include -#include -#include -#include -#include -#include -#if QT_CONFIG(batch_test_support) -#include -#endif // QT_CONFIG(batch_test_support) -#include -#include -#if defined(HAVE_XCTEST) -#include -#endif -#if defined Q_OS_MACOS -#include -#endif - -#if defined(Q_OS_DARWIN) -#include -#endif - -#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014 -# include -#else -// Broken implementation, causes link failures just by #include'ing! -# undef __cpp_lib_to_chars // in case was included -#endif #include #include -#if defined(Q_OS_LINUX) -#include -#include -#include -#endif - -#ifdef Q_OS_UNIX -#include - -#include -#if __has_include() -# include -#endif -#include -#include -#include -#include -#include -# if !defined(Q_OS_INTEGRITY) -# include -# endif -# ifndef _PATH_DEFPATH -# define _PATH_DEFPATH "/usr/bin:/bin" -# endif -# ifndef SIGSTKSZ -# define SIGSTKSZ 0 /* we have code to set the minimum */ -# endif -# ifndef SA_RESETHAND -# define SA_RESETHAND 0 -# endif -#endif - -#if defined(Q_OS_MACOS) -#include -#include -#include -#include -#endif - -#if defined(Q_OS_WASM) -#include -#endif - QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; namespace QTest { namespace CrashHandler { -#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread)) -struct iovec IoVec(struct iovec vec) -{ - return vec; -} -struct iovec IoVec(const char *str) -{ - struct iovec r = {}; - r.iov_base = const_cast(str); - r.iov_len = strlen(str); - return r; -} - -struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result) -{ - char *ptr = result.array.data(); - if (false) { -#ifdef __cpp_lib_to_chars - } else if (auto r = std::to_chars(ptr, ptr + result.array.size(), n, 10); r.ec == std::errc{}) { - ptr = r.ptr; -#endif - } else { - // handle the sign - if (n < 0) { - *ptr++ = '-'; - n = -n; - } - - // find the highest power of the base that is less than this number - static constexpr int StartingDivider = ([]() { - int divider = 1; - for (int i = 0; i < std::numeric_limits::digits10; ++i) - divider *= 10; - return divider; - }()); - int divider = StartingDivider; - while (divider && n < divider) - divider /= 10; - - // now convert to string - while (divider > 1) { - int quot = n / divider; - n = n % divider; - divider /= 10; - *ptr++ = quot + '0'; - } - *ptr++ = n + '0'; - } - -#ifndef QT_NO_DEBUG - // this isn't necessary, it just helps in the debugger - *ptr = '\0'; -#endif - struct iovec r; - r.iov_base = result.array.data(); - r.iov_len = ptr - result.array.data(); - return r; -}; -#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread)) - bool alreadyDebugging() { -#if defined(Q_OS_LINUX) - int fd = open("/proc/self/status", O_RDONLY); - if (fd == -1) - return false; - char buffer[2048]; - ssize_t size = read(fd, buffer, sizeof(buffer) - 1); - if (size == -1) { - close(fd); - return false; - } - buffer[size] = 0; - const char tracerPidToken[] = "\nTracerPid:"; - char *tracerPid = strstr(buffer, tracerPidToken); - if (!tracerPid) { - close(fd); - return false; - } - tracerPid += sizeof(tracerPidToken); - long int pid = strtol(tracerPid, &tracerPid, 10); - close(fd); - return pid != 0; -#elif defined(Q_OS_WIN) return IsDebuggerPresent(); -#elif defined(Q_OS_MACOS) - // Check if there is an exception handler for the process: - mach_msg_type_number_t portCount = 0; - exception_mask_t masks[EXC_TYPES_COUNT]; - mach_port_t ports[EXC_TYPES_COUNT]; - exception_behavior_t behaviors[EXC_TYPES_COUNT]; - thread_state_flavor_t flavors[EXC_TYPES_COUNT]; - exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD); - kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount, - ports, behaviors, flavors); - if (result == KERN_SUCCESS) { - for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) { - if (MACH_PORT_VALID(ports[portIndex])) { - return true; - } - } - } - return false; -#else - // TODO - return false; -#endif } -namespace { -enum DebuggerProgram { None, Gdb, Lldb }; -static bool hasSystemCrashReporter() -{ -#if defined(Q_OS_MACOS) - return QTestPrivate::macCrashReporterWillShowDialog(); -#else - return false; -#endif -} -} // unnamed namespaced - void maybeDisableCoreDump() { -#ifdef RLIMIT_CORE - bool ok = false; - const int disableCoreDump = qEnvironmentVariableIntValue("QTEST_DISABLE_CORE_DUMP", &ok); - if (ok && disableCoreDump) { - struct rlimit limit; - limit.rlim_cur = 0; - limit.rlim_max = 0; - if (setrlimit(RLIMIT_CORE, &limit) != 0) - qWarning("Failed to disable core dumps: %d", errno); - } -#endif } +enum DebuggerProgram { None, Gdb, Lldb, Cdb }; static DebuggerProgram debugger = None; void prepareStackTrace() { @@ -254,71 +38,9 @@ void prepareStackTrace() if (ok && disableStackDump) return; - if (hasSystemCrashReporter()) - return; - -#if defined(Q_OS_MACOS) - // Try to handle https://github.com/llvm/llvm-project/issues/53254, - // where LLDB will hang and fail to provide a valid stack trace. -# if defined(Q_PROCESSOR_ARM) - return; - #else - #define CSR_ALLOW_UNRESTRICTED_FS (1 << 1) - std::optional sipConfiguration = qt_mac_sipConfiguration(); - if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS)) - return; -# endif -#endif - -#ifdef Q_OS_UNIX - // like QStandardPaths::findExecutable(), but simpler - auto hasExecutable = [](const char *execname) { - std::string candidate; - std::string path; - if (const char *p = getenv("PATH"); p && *p) - path = p; - else - path = _PATH_DEFPATH; - for (const char *p = std::strtok(&path[0], ":'"); p; p = std::strtok(nullptr, ":")) { - candidate = p; - candidate += '/'; - candidate += execname; - if (QT_ACCESS(candidate.data(), X_OK) == 0) - return true; - } - return false; - }; - - static constexpr DebuggerProgram debuggerSearchOrder[] = { -# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) - Gdb, Lldb -# else - Lldb, Gdb -# endif - }; - for (DebuggerProgram candidate : debuggerSearchOrder) { - switch (candidate) { - case None: - Q_UNREACHABLE(); - break; - case Gdb: - if (hasExecutable("gdb")) { - debugger = Gdb; - return; - } - break; - case Lldb: - if (hasExecutable("lldb")) { - debugger = Lldb; - return; - } - break; - } - } -#endif // Q_OS_UNIX + // ### Implement finding a debugger on Windows } -#if !defined(Q_OS_WASM) || QT_CONFIG(thread) void printTestRunTime() { const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); @@ -334,76 +56,15 @@ void generateStackTrace() if (debugger == None || alreadyDebugging()) return; -# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER) - // allow ourselves to be debugged - (void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); -# endif - -# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS) - writeToStderr("\n=== Stack trace ===\n"); - - // execlp() requires null-termination, so call the default constructor - AsyncSafeIntBuffer pidbuffer; - asyncSafeToString(getpid(), std::move(pidbuffer)); - - // Note: POSIX.1-2001 still has fork() in the list of async-safe functions, - // but in a future edition, it might be removed. It would be safer to wake - // up a babysitter thread to launch the debugger. - pid_t pid = fork(); - if (pid == 0) { - // child process - (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr - - switch (debugger) { - case None: - Q_UNREACHABLE(); - break; - case Gdb: - execlp("gdb", "gdb", "--nx", "--batch", "-ex", "thread apply all bt", - "-ex", "info proc mappings", - "--pid", pidbuffer.array.data(), nullptr); - break; - case Lldb: - execlp("lldb", "lldb", "--no-lldbinit", "--batch", "-o", "bt all", - "--attach-pid", pidbuffer.array.data(), nullptr); - break; - } - _exit(1); - } else if (pid < 0) { - writeToStderr("Failed to start debugger.\n"); - } else { - int ret; - QT_EINTR_LOOP(ret, waitpid(pid, nullptr, 0)); - } - - writeToStderr("=== End of stack trace ===\n"); -# endif // Q_OS_UNIX && !Q_OS_WASM && !Q_OS_INTEGRITY && !Q_OS_VXWORKS + // ### Implement starting a debugger on Windows } -#endif // !defined(Q_OS_WASM) || QT_CONFIG(thread) -#if defined(Q_OS_WIN) void blockUnixSignals() { // Windows does have C signals, but doesn't use them for the purposes we're // talking about here } -#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM) -void blockUnixSignals() -{ - // Block most Unix signals so the WatchDog thread won't be called when - // external signals are delivered, thus avoiding interfering with the test - sigset_t set; - sigfillset(&set); - // we allow the crashing signals, in case we have bugs - for (int signo : FatalSignalHandler::fatalSignals) - sigdelset(&set, signo); - - pthread_sigmask(SIG_BLOCK, &set, nullptr); -} -#endif // Q_OS_* choice - -#if defined(Q_OS_WIN) void DebugSymbolResolver::cleanup() { if (m_dbgHelpLib) @@ -503,173 +164,6 @@ LONG WINAPI WindowsFaultHandler::windowsFaultHandler(struct _EXCEPTION_POINTERS return EXCEPTION_EXECUTE_HANDLER; } -#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM) -bool FatalSignalHandler::pauseOnCrash = false; - -FatalSignalHandler::FatalSignalHandler() -{ - pauseOnCrash = qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH"); - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_DFL; - oldActions().fill(act); - - // Remove the handler after it is invoked. - act.sa_flags = SA_RESETHAND | setupAlternateStack(); - -# ifdef SA_SIGINFO - act.sa_flags |= SA_SIGINFO; - act.sa_sigaction = FatalSignalHandler::actionHandler; -# else - act.sa_handler = FatalSignalHandler::regularHandler; -# endif - - // Block all fatal signals in our signal handler so we don't try to close - // the testlog twice. - sigemptyset(&act.sa_mask); - for (int signal : fatalSignals) - sigaddset(&act.sa_mask, signal); - - for (size_t i = 0; i < fatalSignals.size(); ++i) - sigaction(fatalSignals[i], &act, &oldActions()[i]); -} - -FatalSignalHandler::~FatalSignalHandler() -{ - // Restore the default signal handlers in place of ours. - // If ours has been replaced, leave the replacement alone. - auto isOurs = [](const struct sigaction &old) { -# ifdef SA_SIGINFO - return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler; -# else - return old.sa_handler == FatalSignalHandler::regularHandler; -# endif - }; - struct sigaction action; - - for (size_t i = 0; i < fatalSignals.size(); ++i) { - struct sigaction &act = oldActions()[i]; - if (sigaction(fatalSignals[i], nullptr, &action)) - continue; // Failed to query present handler - if (action.sa_flags == 0 && action.sa_handler == SIG_DFL) - continue; // Already the default - if (isOurs(action)) - sigaction(fatalSignals[i], &act, nullptr); - } - - freeAlternateStack(); -} - -FatalSignalHandler::OldActionsArray &FatalSignalHandler::oldActions() -{ - Q_CONSTINIT static OldActionsArray oldActions {}; - return oldActions; -} - -auto FatalSignalHandler::alternateStackSize() -{ - struct R { size_t size, pageSize; }; - static constexpr size_t MinStackSize = 32 * 1024; - size_t pageSize = sysconf(_SC_PAGESIZE); - size_t size = SIGSTKSZ; - if (size < MinStackSize) { - size = MinStackSize; - } else { - // round up to a page - size = (size + pageSize - 1) & -pageSize; - } - - return R{ size + pageSize, pageSize }; -} - -int FatalSignalHandler::setupAlternateStack() -{ - // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as - // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h) -# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS) - // Let the signal handlers use an alternate stack - // This is necessary if SIGSEGV is to catch a stack overflow - auto r = alternateStackSize(); - int flags = MAP_PRIVATE | MAP_ANONYMOUS; -# ifdef MAP_STACK - flags |= MAP_STACK; -# endif - alternateStackBase = mmap(nullptr, r.size, PROT_READ | PROT_WRITE, flags, -1, 0); - if (alternateStackBase == MAP_FAILED) - return 0; - - // mark the bottom page inaccessible, to catch a handler stack overflow - (void) mprotect(alternateStackBase, r.pageSize, PROT_NONE); - - stack_t stack; - stack.ss_flags = 0; - stack.ss_size = r.size - r.pageSize; - stack.ss_sp = static_cast(alternateStackBase) + r.pageSize; - sigaltstack(&stack, nullptr); - return SA_ONSTACK; -# else - return 0; -# endif -} - -void FatalSignalHandler::freeAlternateStack() -{ -# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS) - if (alternateStackBase != MAP_FAILED) { - stack_t stack = {}; - stack.ss_flags = SS_DISABLE; - sigaltstack(&stack, nullptr); - munmap(alternateStackBase, alternateStackSize().size); - } -# endif -} - -void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *) -{ - writeToStderr("Received signal ", asyncSafeToString(signum), - " (SIG", signalName(signum), ")"); - - 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); - else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE)) - printSentSignalInfo(info); - - printTestRunTime(); - if (signum != SIGINT) { - generateStackTrace(); - if (pauseOnCrash) { - writeToStderr("Pausing process ", asyncSafeToString(getpid()), - " for debugging\n"); - raise(SIGSTOP); - } - } - - // chain back to the previous handler, if any - for (size_t i = 0; i < fatalSignals.size(); ++i) { - struct sigaction &act = oldActions()[i]; - if (signum != fatalSignals[i]) - continue; - - // restore the handler (if SA_RESETHAND hasn't done the job for us) - if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags) - (void) sigaction(signum, &act, nullptr); - - if (!isCrashingSignal) - raise(signum); - - // signal is blocked, so it'll be delivered when we return - return; - } - - // we shouldn't reach here! - std::abort(); -} -#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) - } // namespace CrashHandler } // namespace QTest