summaryrefslogtreecommitdiffstats
path: root/src/testlib/qtestcrashhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/testlib/qtestcrashhandler.cpp')
-rw-r--r--src/testlib/qtestcrashhandler.cpp676
1 files changed, 0 insertions, 676 deletions
diff --git a/src/testlib/qtestcrashhandler.cpp b/src/testlib/qtestcrashhandler.cpp
deleted file mode 100644
index a89103028a2..00000000000
--- a/src/testlib/qtestcrashhandler.cpp
+++ /dev/null
@@ -1,676 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// 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 <QtTest/qtestcase.h>
-#include <QtTest/private/qtestcrashhandler_p.h>
-#include <QtTest/qtestassert.h>
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qdiriterator.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qfloat16.h>
-#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qtemporarydir.h>
-#include <QtCore/qthread.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/private/qlocking_p.h>
-#include <QtCore/private/qtools_p.h>
-#include <QtCore/private/qwaitcondition_p.h>
-
-#include <QtCore/qtestsupport_core.h>
-
-#include <QtTest/private/qtestlog_p.h>
-#include <QtTest/private/qtesttable_p.h>
-#include <QtTest/qtestdata.h>
-#include <QtTest/private/qtestresult_p.h>
-#include <QtTest/private/qsignaldumper_p.h>
-#include <QtTest/private/qbenchmark_p.h>
-#if QT_CONFIG(batch_test_support)
-#include <QtTest/private/qtestregistry_p.h>
-#endif // QT_CONFIG(batch_test_support)
-#include <QtTest/private/cycle_include_p.h>
-#include <QtTest/private/qtestblacklist_p.h>
-#if defined(HAVE_XCTEST)
-#include <QtTest/private/qxctestlogger_p.h>
-#endif
-#if defined Q_OS_MACOS
-#include <QtTest/private/qtestutil_macos_p.h>
-#endif
-
-#if defined(Q_OS_DARWIN)
-#include <QtTest/private/qappletestlogger_p.h>
-#endif
-
-#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
-# include <charconv>
-#else
-// Broken implementation, causes link failures just by #include'ing!
-# undef __cpp_lib_to_chars // in case <version> was included
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#if defined(Q_OS_LINUX)
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#endif
-
-#ifdef Q_OS_UNIX
-#include <QtCore/private/qcore_unix_p.h>
-
-#include <errno.h>
-#if __has_include(<paths.h>)
-# include <paths.h>
-#endif
-#include <signal.h>
-#include <time.h>
-#include <sys/mman.h>
-#include <sys/wait.h>
-#include <unistd.h>
-# if !defined(Q_OS_INTEGRITY)
-# include <sys/resource.h>
-# 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 <IOKit/pwr_mgt/IOPMLib.h>
-#include <mach/task.h>
-#include <mach/mach_init.h>
-#include <CoreFoundation/CFPreferences.h>
-#endif
-
-#if defined(Q_OS_WASM)
-#include <emscripten.h>
-#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<char *>(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<int>::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
-}
-
-static DebuggerProgram debugger = None;
-void prepareStackTrace()
-{
-
- bool ok = false;
- const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
- 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<uint32_t> 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
-}
-
-#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
-void printTestRunTime()
-{
- const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
- const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
- const char *const name = QTest::currentTestFunction();
- writeToStderr("\n ", name ? name : "[Non-test]",
- " function time: ", asyncSafeToString(msecsFunctionTime),
- "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n");
-}
-
-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
-}
-#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)
- 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<SymInitializeType>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
- m_symFromAddr = reinterpret_cast<SymFromAddrType>(
- reinterpret_cast<QFunctionPointer>(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()
-{
- 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<char *>(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
-
-QT_END_NAMESPACE