1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4//
5// W A R N I N G
6// -------------
7//
8// This file is not part of the Qt API. It exists purely as an
9// implementation detail. This header file may change from version to
10// version without notice, or even be removed.
11//
12// We mean it.
13//
14
15#ifndef QTESTCRASHHANDLER_H
16#define QTESTCRASHHANDLER_H
17
18#include <QtCore/qnamespace.h>
19#include <QtTest/qttestglobal.h>
20
21#include <QtCore/private/qtools_p.h>
22
23#ifdef Q_OS_UNIX
24#include <signal.h>
25#include <sys/mman.h>
26#include <sys/uio.h>
27#include <string.h>
28#include <unistd.h>
29#endif
30
31#ifdef Q_OS_WIN
32#include <iostream>
33# if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
34# include <crtdbg.h>
35# endif
36#include <qt_windows.h> // for Sleep
37#endif
38
39QT_BEGIN_NAMESPACE
40namespace QTest {
41namespace CrashHandler {
42#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
43 struct iovec IoVec(struct iovec vec);
44 struct iovec IoVec(const char *str);
45
46 template <typename... Args> static ssize_t writeToStderr(Args &&... args)
47 {
48 struct iovec vec[] = { IoVec(std::forward<Args>(args))... };
49 return ::writev(STDERR_FILENO, iovec: vec, count: std::size(vec));
50 }
51
52 // async-signal-safe conversion from int to string
53 struct AsyncSafeIntBuffer
54 {
55 // digits10 + 1 for all possible digits
56 // +1 for the sign
57 // +1 for the terminating null
58 static constexpr int Digits10 = std::numeric_limits<int>::digits10 + 3;
59 std::array<char, Digits10> array;
60 constexpr AsyncSafeIntBuffer() : array{} {} // initializes array
61 AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized
62 };
63
64 struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized);
65#elif defined(Q_OS_WIN)
66 // Windows doesn't need to be async-safe
67 template <typename... Args> static void writeToStderr(Args &&... args)
68 {
69 (std::cerr << ... << args);
70 }
71
72 inline std::string asyncSafeToString(int n)
73 {
74 return std::to_string(n);
75 }
76#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
77
78 bool alreadyDebugging();
79 void blockUnixSignals();
80
81#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
82 void printTestRunTime();
83 void generateStackTrace();
84#endif
85
86 void maybeDisableCoreDump();
87 Q_TESTLIB_EXPORT void prepareStackTrace();
88
89#if defined(Q_OS_WIN)
90 // Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
91 class DebugSymbolResolver
92 {
93 Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
94 public:
95 struct Symbol
96 {
97 Symbol() : name(nullptr), address(0) {}
98
99 const char *name; // Must be freed by caller.
100 DWORD64 address;
101 };
102
103 explicit DebugSymbolResolver(HANDLE process);
104 ~DebugSymbolResolver() { cleanup(); }
105
106 bool isValid() const { return m_symFromAddr; }
107
108 Symbol resolveSymbol(DWORD64 address) const;
109
110 private:
111 // typedefs from DbgHelp.h/.dll
112 struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
113 ULONG SizeOfStruct;
114 ULONG TypeIndex; // Type Index of symbol
115 ULONG64 Reserved[2];
116 ULONG Index;
117 ULONG Size;
118 ULONG64 ModBase; // Base Address of module comtaining this symbol
119 ULONG Flags;
120 ULONG64 Value; // Value of symbol, ValuePresent should be 1
121 ULONG64 Address; // Address of symbol including base address of module
122 ULONG Register; // register holding value or pointer to value
123 ULONG Scope; // scope of the symbol
124 ULONG Tag; // pdb classification
125 ULONG NameLen; // Actual length of name
126 ULONG MaxNameLen;
127 CHAR Name[1]; // Name of symbol
128 };
129
130 typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
131 typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
132
133 void cleanup();
134
135 const HANDLE m_process;
136 HMODULE m_dbgHelpLib;
137 SymFromAddrType m_symFromAddr;
138 };
139
140 class Q_TESTLIB_EXPORT WindowsFaultHandler
141 {
142 public:
143 WindowsFaultHandler();
144
145 private:
146 static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo);
147 };
148 using FatalSignalHandler = WindowsFaultHandler;
149#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
150 class Q_TESTLIB_EXPORT FatalSignalHandler
151 {
152 public:
153 # define OUR_SIGNALS(F) \
154 F(HUP) \
155 F(INT) \
156 F(QUIT) \
157 F(ABRT) \
158 F(ILL) \
159 F(BUS) \
160 F(FPE) \
161 F(SEGV) \
162 F(PIPE) \
163 F(TERM) \
164 /**/
165 # define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
166 # define ENUMERATE_SIGNALS(S) SIG ## S,
167 static const char *signalName(int signum) noexcept
168 {
169 switch (signum) {
170 OUR_SIGNALS(CASE_LABEL)
171 }
172
173 # if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
174 // get the other signal names from glibc 2.32
175 // (accessing the sys_sigabbrev variable causes linker warnings)
176 if (const char *p = sigabbrev_np(sig: signum))
177 return p;
178 # endif
179 return "???";
180 }
181 static constexpr std::array fatalSignals = {
182 OUR_SIGNALS(ENUMERATE_SIGNALS)
183 };
184 # undef CASE_LABEL
185 # undef ENUMERATE_SIGNALS
186
187 static constexpr std::array crashingSignals = {
188 // Crash signals are special, because if we return from the handler
189 // without adjusting the machine state, the same instruction that
190 // originally caused the crash will get re-executed and will thus cause
191 // the same crash again. This is useful if our parent process logs the
192 // exit result or if core dumps are enabled: the core file will point
193 // to the actual instruction that crashed.
194 SIGILL, SIGBUS, SIGFPE, SIGSEGV
195 };
196 using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
197
198 FatalSignalHandler();
199 ~FatalSignalHandler();
200
201 private:
202 Q_DISABLE_COPY_MOVE(FatalSignalHandler)
203
204 static OldActionsArray &oldActions();
205 auto alternateStackSize();
206 int setupAlternateStack();
207 void freeAlternateStack();
208
209 template <typename T> static
210 std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().si_uid) >= 1>
211 printSentSignalInfo(T *info)
212 {
213 writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid),
214 " UID ", asyncSafeToString(info->si_uid));
215 }
216 static void printSentSignalInfo(...) {}
217
218 template <typename T> static
219 std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1> printCrashingSignalInfo(T *info)
220 {
221 using HexString = std::array<char, sizeof(quintptr) * 2>;
222 auto toHexString = [](quintptr u, HexString &&r = {}) {
223 int shift = sizeof(quintptr) * 8 - 4;
224 for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4)
225 r[i] = QtMiscUtils::toHexLower(value: u >> shift);
226 struct iovec vec;
227 vec.iov_base = r.data();
228 vec.iov_len = r.size();
229 return vec;
230 };
231 writeToStderr(", code ", asyncSafeToString(info->si_code),
232 ", for address 0x", toHexString(quintptr(info->si_addr)));
233 }
234 static void printCrashingSignalInfo(...) {}
235 static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */);
236
237 [[maybe_unused]] static void regularHandler(int signum)
238 {
239 actionHandler(signum, info: nullptr, nullptr);
240 }
241
242 void *alternateStackBase = MAP_FAILED;
243 static bool pauseOnCrash;
244 };
245#else // Q_OS_WASM or weird systems
246class Q_TESTLIB_EXPORT FatalSignalHandler {};
247inline void blockUnixSignals() {}
248#endif // Q_OS_* choice
249} // namespace CrashHandler
250} // namespace QTest
251QT_END_NAMESPACE
252
253#endif // QTESTCRASHHANDLER_H
254

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/testlib/qtestcrashhandler_p.h