diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2020-01-31 20:34:27 +0100 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2020-02-01 10:43:52 +0100 |
commit | 0c8ff269dcda7de68c475535d575c0bb34af788b (patch) | |
tree | 63f10f629dc39d4f523172b99b52a6c506aebc73 /src/testlib | |
parent | d005d743dea1b072fdd18507f7bf6170f5e3d7ab (diff) |
testlib: Unite Windows and Unix signal handler code
Change-Id: I7b9d480008167c071bf925d655eb97ef437bc206
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/testlib')
-rw-r--r-- | src/testlib/qtestcase.cpp | 330 |
1 files changed, 165 insertions, 165 deletions
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index fde20ada33..372e208919 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1533,121 +1533,6 @@ void TestMethods::invokeTests(QObject *testObject) const QTestResult::setCurrentTestFunction(nullptr); } -#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) - -class FatalSignalHandler -{ -public: - FatalSignalHandler() - { - sigemptyset(&handledSignals); - - const int fatalSignals[] = { - SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 }; - - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = FatalSignalHandler::signal; - - // Remove the handler after it is invoked. -#if !defined(Q_OS_INTEGRITY) - act.sa_flags = SA_RESETHAND; -#endif - - // 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 -# if defined(Q_CC_GNU) && defined(Q_OF_ELF) - // Put the alternate stack in the .lbss (large BSS) section so that it doesn't - // interfere with normal .bss symbols - __attribute__((section(".lbss.altstack"), aligned(4096))) -# endif - static char alternate_stack[16 * 1024]; - stack_t stack; - stack.ss_flags = 0; - stack.ss_size = sizeof alternate_stack; - stack.ss_sp = alternate_stack; - sigaltstack(&stack, nullptr); - act.sa_flags |= SA_ONSTACK; -#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 i = 0; fatalSignals[i]; ++i) - sigaddset(&act.sa_mask, fatalSignals[i]); - - struct sigaction oldact; - - for (int i = 0; fatalSignals[i]; ++i) { - sigaction(fatalSignals[i], &act, &oldact); - if ( -#ifdef SA_SIGINFO - oldact.sa_flags & SA_SIGINFO || -#endif - oldact.sa_handler != SIG_DFL) { - sigaction(fatalSignals[i], &oldact, nullptr); - } else - { - sigaddset(&handledSignals, fatalSignals[i]); - } - } - } - - ~FatalSignalHandler() - { - // Unregister any of our remaining signal handlers - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_DFL; - - struct sigaction oldact; - - for (int i = 1; i < 32; ++i) { - if (!sigismember(&handledSignals, i)) - continue; - sigaction(i, &act, &oldact); - - // If someone overwrote it in the mean time, put it back - if (oldact.sa_handler != FatalSignalHandler::signal) - sigaction(i, &oldact, nullptr); - } - } - -private: - static void signal(int signum) - { - const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); - const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); - if (signum != SIGINT) { - stackTrace(); - if (qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH")) { - fprintf(stderr, "Pausing process %d for debugging\n", getpid()); - raise(SIGSTOP); - } - } - qFatal("Received signal %d\n" - " Function time: %dms Total time: %dms", - signum, msecsFunctionTime, msecsTotalTime); -#if defined(Q_OS_INTEGRITY) - { - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_DFL; - sigaction(signum, &act, NULL); - } -#endif - } - - sigset_t handledSignals; -}; - -#endif - -} // namespace - #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // Helper class for resolving symbol names by dynamically loading "dbghelp.dll". @@ -1746,49 +1631,177 @@ DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) return result; } -static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo) +#endif // Q_OS_WIN && !Q_OS_WINRT + +class FatalSignalHandler { - 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; - printf("A crash occurred in %s.\n" - "Function time: %dms Total time: %dms\n\n" - "Exception address: 0x%p\n" - "Exception code : 0x%lx\n", - appName, msecsFunctionTime, msecsTotalTime, - exceptionAddress, exInfo->ExceptionRecord->ExceptionCode); - - DebugSymbolResolver resolver(GetCurrentProcess()); - if (resolver.isValid()) { - DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress)); - if (exceptionSymbol.name) { - printf("Nearby symbol : %s\n", exceptionSymbol.name); - delete [] exceptionSymbol.name; +public: + FatalSignalHandler() + { +#if defined(Q_OS_WIN) +# if !defined(Q_CC_MINGW) + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); +# endif +# if !defined(Q_OS_WINRT) + SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); + SetUnhandledExceptionFilter(windowsFaultHandler); +# endif +#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + sigemptyset(&handledSignals); + + const int fatalSignals[] = { + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 }; + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = FatalSignalHandler::signal; + + // Remove the handler after it is invoked. +# if !defined(Q_OS_INTEGRITY) + act.sa_flags = SA_RESETHAND; +# endif + + // 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 +# if defined(Q_CC_GNU) && defined(Q_OF_ELF) + // Put the alternate stack in the .lbss (large BSS) section so that it doesn't + // interfere with normal .bss symbols + __attribute__((section(".lbss.altstack"), aligned(4096))) +# endif + static char alternate_stack[16 * 1024]; + stack_t stack; + stack.ss_flags = 0; + stack.ss_size = sizeof alternate_stack; + stack.ss_sp = alternate_stack; + sigaltstack(&stack, nullptr); + act.sa_flags |= SA_ONSTACK; +# 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 i = 0; fatalSignals[i]; ++i) + sigaddset(&act.sa_mask, fatalSignals[i]); + + struct sigaction oldact; + + for (int i = 0; fatalSignals[i]; ++i) { + sigaction(fatalSignals[i], &act, &oldact); + if ( +# ifdef SA_SIGINFO + oldact.sa_flags & SA_SIGINFO || +# endif + oldact.sa_handler != SIG_DFL) { + sigaction(fatalSignals[i], &oldact, nullptr); + } else + { + sigaddset(&handledSignals, fatalSignals[i]); + } } - void *stack[maxStackFrames]; - fputs("\nStack:\n", stdout); - 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) { - printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address); - delete [] symbol.name; - } else { - printf("#%3u: Unable to obtain symbol\n", f + 1); +#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + } + + ~FatalSignalHandler() + { +#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + // Unregister any of our remaining signal handlers + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; + + struct sigaction oldact; + + for (int i = 1; i < 32; ++i) { + if (!sigismember(&handledSignals, i)) + continue; + sigaction(i, &act, &oldact); + + // If someone overwrote it in the mean time, put it back + if (oldact.sa_handler != FatalSignalHandler::signal) + sigaction(i, &oldact, nullptr); + } +#endif + } + +private: +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + static LONG WINAPI 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; + printf("A crash occurred in %s.\n" + "Function time: %dms Total time: %dms\n\n" + "Exception address: 0x%p\n" + "Exception code : 0x%lx\n", + appName, msecsFunctionTime, msecsTotalTime, + exceptionAddress, exInfo->ExceptionRecord->ExceptionCode); + + DebugSymbolResolver resolver(GetCurrentProcess()); + if (resolver.isValid()) { + DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress)); + if (exceptionSymbol.name) { + printf("Nearby symbol : %s\n", exceptionSymbol.name); + delete [] exceptionSymbol.name; + } + void *stack[maxStackFrames]; + fputs("\nStack:\n", stdout); + 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) { + printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address); + delete [] symbol.name; + } else { + printf("#%3u: Unable to obtain symbol\n", f + 1); + } } } + + fputc('\n', stdout); + fflush(stdout); + + return EXCEPTION_EXECUTE_HANDLER; } +#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - fputc('\n', stdout); - fflush(stdout); +#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + static void signal(int signum) + { + const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); + const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); + if (signum != SIGINT) { + stackTrace(); + if (qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH")) { + fprintf(stderr, "Pausing process %d for debugging\n", getpid()); + raise(SIGSTOP); + } + } + qFatal("Received signal %d\n" + " Function time: %dms Total time: %dms", + signum, msecsFunctionTime, msecsTotalTime); +# if defined(Q_OS_INTEGRITY) + { + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_DFL; + sigaction(signum, &act, NULL); + } +# endif + } - return EXCEPTION_EXECUTE_HANDLER; -} -#endif // Q_OS_WIN) && !Q_OS_WINRT + sigset_t handledSignals; +#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) +}; + +} // namespace static void initEnvironment() { @@ -1896,18 +1909,6 @@ int QTest::qRun() try { #endif -#if defined(Q_OS_WIN) - if (!noCrashHandler) { -# ifndef Q_CC_MINGW - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); -# endif -# ifndef Q_OS_WINRT - SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); - SetUnhandledExceptionFilter(windowsFaultHandler); -# endif - } // !noCrashHandler -#endif // Q_OS_WIN - #if QT_CONFIG(valgrind) if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) { if (Q_UNLIKELY(!qApp)) @@ -1922,11 +1923,10 @@ int QTest::qRun() } else #endif { -#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) QScopedPointer<FatalSignalHandler> handler; if (!noCrashHandler) handler.reset(new FatalSignalHandler); -#endif + TestMethods::MetaMethods commandLineMethods; for (const QString &tf : qAsConst(QTest::testFunctions)) { const QByteArray tfB = tf.toLatin1(); |