123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 | // Copyright (C) 2024 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only//// W A R N I N G// -------------//// This file is not part of the Qt API. It exists purely as an// implementation detail. This header file may change from version to// version without notice, or even be removed.//// We mean it.//#ifndef QTESTCRASHHANDLER_H#define QTESTCRASHHANDLER_H#include <QtCore/qnamespace.h>#include <QtTest/qttestglobal.h>#include <QtCore/private/qtools_p.h>#ifdef Q_OS_UNIX#include <signal.h>#include <sys/mman.h>#include <sys/uio.h>#include <string.h>#include <unistd.h>#endif#ifdef Q_OS_WIN#include <iostream># if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))# include <crtdbg.h># endif#include <qt_windows.h>// for Sleep#endif QT_BEGIN_NAMESPACE namespace QTest {namespace CrashHandler {#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))struct iovec IoVec(struct iovec vec);struct iovec IoVec(const char*str);template<typename... Args>static ssize_t writeToStderr(Args &&... args){struct iovec vec[] = {IoVec(std::forward<Args>(args))... };return::writev(STDERR_FILENO, vec,std::size(vec));}// async-signal-safe conversion from int to stringstruct AsyncSafeIntBuffer {// digits10 + 1 for all possible digits// +1 for the sign// +1 for the terminating nullstaticconstexprint Digits10 =std::numeric_limits<int>::digits10 +3;std::array<char, Digits10> array;constexprAsyncSafeIntBuffer() : array{} {}// initializes arrayAsyncSafeIntBuffer(Qt::Initialization) {}// leaves array uninitialized};struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result =Qt::Uninitialized);#elif defined(Q_OS_WIN)// Windows doesn't need to be async-safetemplate<typename... Args>static voidwriteToStderr(Args &&... args){(std::cerr << ... << args);}inline std::string asyncSafeToString(int n){returnstd::to_string(n);}#endif// defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))boolalreadyDebugging();voidblockUnixSignals();#if !defined(Q_OS_WASM) || QT_CONFIG(thread)voidprintTestRunTime();voidgenerateStackTrace();#endifvoidmaybeDisableCoreDump(); Q_TESTLIB_EXPORT voidprepareStackTrace();#if defined(Q_OS_WIN)// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".class DebugSymbolResolver {Q_DISABLE_COPY_MOVE(DebugSymbolResolver)public:struct Symbol {Symbol() :name(nullptr),address(0) {}const char*name;// Must be freed by caller. DWORD64 address;};explicitDebugSymbolResolver(HANDLE process);~DebugSymbolResolver() {cleanup(); }boolisValid()const{return m_symFromAddr; } Symbol resolveSymbol(DWORD64 address)const;private:// typedefs from DbgHelp.h/.dllstruct DBGHELP_SYMBOL_INFO {// SYMBOL_INFO ULONG SizeOfStruct; ULONG TypeIndex;// Type Index of symbol ULONG64 Reserved[2]; ULONG Index; ULONG Size; ULONG64 ModBase;// Base Address of module comtaining this symbol ULONG Flags; ULONG64 Value;// Value of symbol, ValuePresent should be 1 ULONG64 Address;// Address of symbol including base address of module ULONG Register;// register holding value or pointer to value ULONG Scope;// scope of the symbol ULONG Tag;// pdb classification ULONG NameLen;// Actual length of name ULONG MaxNameLen; CHAR Name[1];// Name of symbol};typedefBOOL(__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);typedefBOOL(__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);voidcleanup();const HANDLE m_process; HMODULE m_dbgHelpLib; SymFromAddrType m_symFromAddr;};class Q_TESTLIB_EXPORT WindowsFaultHandler {public:WindowsFaultHandler();private:static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo);};using FatalSignalHandler = WindowsFaultHandler;#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)class Q_TESTLIB_EXPORT FatalSignalHandler {public:# define OUR_SIGNALS(F) \ F(HUP) \ F(INT) \ F(QUIT) \ F(ABRT) \ F(ILL) \ F(BUS) \ F(FPE) \ F(SEGV) \ F(PIPE) \ F(TERM) \/**/# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);# define ENUMERATE_SIGNALS(S) SIG ## S,static const char*signalName(int signum) noexcept {switch(signum) {OUR_SIGNALS(CASE_LABEL)}# if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)// get the other signal names from glibc 2.32// (accessing the sys_sigabbrev variable causes linker warnings)if(const char*p =sigabbrev_np(signum))return p;# endifreturn"???";}staticconstexpr std::array fatalSignals = {OUR_SIGNALS(ENUMERATE_SIGNALS)};# undef CASE_LABEL# undef ENUMERATE_SIGNALSstaticconstexpr std::array crashingSignals = {// Crash signals are special, because if we return from the handler// without adjusting the machine state, the same instruction that// originally caused the crash will get re-executed and will thus cause// the same crash again. This is useful if our parent process logs the// exit result or if core dumps are enabled: the core file will point// to the actual instruction that crashed. SIGILL, SIGBUS, SIGFPE, SIGSEGV };using OldActionsArray =std::array<struct sigaction, fatalSignals.size()>;FatalSignalHandler();~FatalSignalHandler();private:Q_DISABLE_COPY_MOVE(FatalSignalHandler)static OldActionsArray &oldActions();autoalternateStackSize();intsetupAlternateStack();voidfreeAlternateStack();template<typename T>staticstd::enable_if_t<sizeof(std::declval<T>().si_pid) +sizeof(std::declval<T>().si_uid) >=1>printSentSignalInfo(T *info){writeToStderr(" sent by PID ",asyncSafeToString(info->si_pid)," UID ",asyncSafeToString(info->si_uid));}static voidprintSentSignalInfo(...) {}template<typename T>staticstd::enable_if_t<sizeof(std::declval<T>().si_addr) >=1>printCrashingSignalInfo(T *info){using HexString =std::array<char,sizeof(quintptr) *2>;auto toHexString = [](quintptr u, HexString &&r = {}) {int shift =sizeof(quintptr) *8-4;for(size_t i =0; i <sizeof(quintptr) *2; ++i, shift -=4) r[i] =QtMiscUtils::toHexLower(u >> shift);struct iovec vec; vec.iov_base = r.data(); vec.iov_len = r.size();return vec;};writeToStderr(", code ",asyncSafeToString(info->si_code),", for address 0x",toHexString(quintptr(info->si_addr)));}static voidprintCrashingSignalInfo(...) {}static voidactionHandler(int signum, siginfo_t *info,void*/* ucontext */);[[maybe_unused]]static voidregularHandler(int signum){actionHandler(signum,nullptr,nullptr);}void*alternateStackBase = MAP_FAILED;static bool pauseOnCrash;};#else// Q_OS_WASM or weird systemsclass Q_TESTLIB_EXPORT FatalSignalHandler {};inlinevoidblockUnixSignals() {}#endif// Q_OS_* choice}// namespace CrashHandler}// namespace QTest QT_END_NAMESPACE #endif// QTESTCRASHHANDLER_H
|