Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtestcrashhandler_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// Copyright (C) 2024 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <QtTest/private/qtestcrashhandler_p.h>
6
7#include <QtCore/qbytearray.h>
8#include <QtCore/qstring.h>
9#include <QtCore/private/qcore_unix_p.h>
10
11#include <QtTest/qtestcase.h>
12#include <QtTest/private/qtestlog_p.h>
13
14#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
15# include <charconv>
16#else
17// Broken implementation, causes link failures just by #include'ing!
18# undef __cpp_lib_to_chars // in case <version> was included
19#endif
20#include <string_view>
21
22#include <stdio.h>
23#include <stdlib.h>
24
25#include <errno.h>
26#if __has_include(<paths.h>)
27# include <paths.h>
28#endif
29#include <signal.h>
30#include <time.h>
31#include <sys/mman.h>
32#include <sys/uio.h>
33#include <sys/wait.h>
34#include <unistd.h>
35# if !defined(Q_OS_INTEGRITY)
36# include <sys/resource.h>
37# endif
38# if __has_include(<sys/ucontext.h>)
39# include <sys/ucontext.h>
40# elif __has_include(<ucontext.h>)
41# include <ucontext.h>
42# else
43using ucontext_t = void;
44# endif
45
46#if defined(Q_OS_MACOS)
47#include <QtCore/private/qcore_mac_p.h>
48#include <QtTest/private/qtestutil_macos_p.h>
49
50#include <IOKit/pwr_mgt/IOPMLib.h>
51#include <mach/task.h>
52#include <mach/mach_init.h>
53#include <CoreFoundation/CFPreferences.h>
54
55#define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
56#endif
57
58#if defined(Q_OS_LINUX)
59#include <sys/prctl.h>
60#include <sys/types.h>
61#include <fcntl.h>
62#endif
63
64#if defined(Q_OS_WASM)
65#include <emscripten.h>
66#endif
67
68#ifndef _PATH_DEFPATH
69# define _PATH_DEFPATH "/usr/bin:/bin"
70#endif
71#ifndef SIGSTKSZ
72# define SIGSTKSZ 0 /* we have code to set the minimum */
73#endif
74#ifndef SA_RESETHAND
75# define SA_RESETHAND 0
76#endif
77
78QT_BEGIN_NAMESPACE
79
80using namespace Qt::StringLiterals;
81
82# define OUR_SIGNALS(F)
83 F(HUP)
84 F(INT)
85 F(QUIT)
86 F(ABRT)
87 F(ILL)
88 F(BUS)
89 F(FPE)
90 F(SEGV)
91 F(PIPE)
92 F(TERM)
93 /**/
94# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
95# define ENUMERATE_SIGNALS(S) SIG ## S,
96static const char *signalName(int signum) noexcept
97{
98 switch (signum) {
99 OUR_SIGNALS(CASE_LABEL)
100 }
101
102# if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
103 // get the other signal names from glibc 2.32
104 // (accessing the sys_sigabbrev variable causes linker warnings)
105 if (const char *p = sigabbrev_np(signum))
106 return p;
107# endif
108 return "???";
109}
110static constexpr std::array fatalSignals = {
111 OUR_SIGNALS(ENUMERATE_SIGNALS)
112};
113# undef CASE_LABEL
114# undef ENUMERATE_SIGNALS
115
116static constexpr std::array crashingSignals = {
117 // Crash signals are special, because if we return from the handler
118 // without adjusting the machine state, the same instruction that
119 // originally caused the crash will get re-executed and will thus cause
120 // the same crash again. This is useful if our parent process logs the
121 // exit result or if core dumps are enabled: the core file will point
122 // to the actual instruction that crashed.
123 SIGILL, SIGBUS, SIGFPE, SIGSEGV
124};
125using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
126
127template <typename... Args> static ssize_t writeToStderr(Args &&... args)
128{
129 auto makeIovec = [](std::string_view arg) {
130 struct iovec r = {};
131 r.iov_base = const_cast<char *>(arg.data());
132 r.iov_len = arg.size();
133 return r;
134 };
135 struct iovec vec[] = { makeIovec(std::forward<Args>(args))... };
136 return ::writev(STDERR_FILENO, vec, std::size(vec));
137}
138
139namespace {
140// async-signal-safe conversion from int to string
141struct AsyncSafeIntBuffer
142{
143 // digits10 + 1 for all possible digits
144 // +1 for the sign
145 // +1 for the terminating null
146 static constexpr int Digits10 = std::numeric_limits<int>::digits10 + 3;
147 std::array<char, Digits10> array;
148 constexpr AsyncSafeIntBuffer() : array{} {} // initializes array
149 AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized
150};
151
152std::string_view asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized)
153{
154 char *ptr = result.array.data();
155 if (false) {
156#ifdef __cpp_lib_to_chars
157 } else if (auto r = std::to_chars(ptr, ptr + result.array.size(), n, 10); r.ec == std::errc{}) {
158 ptr = r.ptr;
159#endif
160 } else {
161 // handle the sign
162 if (n < 0) {
163 *ptr++ = '-';
164 n = -n;
165 }
166
167 // find the highest power of the base that is less than this number
168 static constexpr int StartingDivider = ([]() {
169 int divider = 1;
170 for (int i = 0; i < std::numeric_limits<int>::digits10; ++i)
171 divider *= 10;
172 return divider;
173 }());
174 int divider = StartingDivider;
175 while (divider && n < divider)
176 divider /= 10;
177
178 // now convert to string
179 while (divider > 1) {
180 int quot = n / divider;
181 n = n % divider;
182 divider /= 10;
183 *ptr++ = quot + '0';
184 }
185 *ptr++ = n + '0';
186 }
187
188#ifndef QT_NO_DEBUG
189 // this isn't necessary, it just helps in the debugger
190 *ptr = '\0';
191#endif
192 return std::string_view(result.array.data(), ptr - result.array.data());
193};
194
195std::string_view asyncSafeToHexString(quintptr u, char *ptr)
196{
197 // We format with leading zeroes so the output is of fixed length.
198 // Formatting to shorter is more complex and unnecessary here (unlike
199 // decimals above).
200 int shift = sizeof(quintptr) * 8 - 4;
201 ptr[0] = '0';
202 ptr[1] = 'x';
203 for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4)
204 ptr[i + 2] = QtMiscUtils::toHexLower(u >> shift);
205
206 return std::string_view(ptr, sizeof(quintptr) * 2 + 2);
207}
208} // unnamed namespace
209
210namespace QTest {
211namespace CrashHandler {
212Q_CONSTINIT static OldActionsArray oldActions {};
213static bool pauseOnCrash = false;
214
215static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */);
216
218{
219#if defined(Q_OS_LINUX)
220 int fd = open("/proc/self/status", O_RDONLY);
221 if (fd == -1)
222 return false;
223 char buffer[2048];
224 ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
225 if (size == -1) {
226 close(fd);
227 return false;
228 }
229 buffer[size] = 0;
230 const char tracerPidToken[] = "\nTracerPid:";
231 char *tracerPid = strstr(buffer, tracerPidToken);
232 if (!tracerPid) {
233 close(fd);
234 return false;
235 }
236 tracerPid += sizeof(tracerPidToken);
237 long int pid = strtol(tracerPid, &tracerPid, 10);
238 close(fd);
239 return pid != 0;
240#elif defined(Q_OS_MACOS)
241 // Check if there is an exception handler for the process:
242 mach_msg_type_number_t portCount = 0;
243 exception_mask_t masks[EXC_TYPES_COUNT];
244 mach_port_t ports[EXC_TYPES_COUNT];
245 exception_behavior_t behaviors[EXC_TYPES_COUNT];
246 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
247 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
248 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
249 ports, behaviors, flavors);
250 if (result == KERN_SUCCESS) {
251 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
252 if (MACH_PORT_VALID(ports[portIndex])) {
253 return true;
254 }
255 }
256 }
257 return false;
258#else
259 // TODO
260 return false;
261#endif
262}
263
265{
266#if defined(Q_OS_MACOS)
267 return QTestPrivate::macCrashReporterWillShowDialog();
268#else
269 return false;
270#endif
271}
272
274{
275#ifdef RLIMIT_CORE
276 bool ok = false;
277 const int disableCoreDump = qEnvironmentVariableIntValue("QTEST_DISABLE_CORE_DUMP", &ok);
278 if (ok && disableCoreDump) {
279 struct rlimit limit;
280 limit.rlim_cur = 0;
281 limit.rlim_max = 0;
282 if (setrlimit(RLIMIT_CORE, &limit) != 0)
283 qWarning("Failed to disable core dumps: %d", errno);
284 }
285#endif
286}
287
290{
291
292 bool ok = false;
293 const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
294 if (ok && disableStackDump)
295 return;
296
298 return;
299
300#if defined(Q_OS_MACOS)
301 // Try to handle https://github.com/llvm/llvm-project/issues/53254,
302 // where LLDB will hang and fail to provide a valid stack trace.
303# if defined(Q_PROCESSOR_ARM)
304 return;
305# else
306 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
307 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
308 return;
309# endif
310#endif
311
312 // like QStandardPaths::findExecutable(), but simpler
313 auto hasExecutable = [](const char *execname) {
314 std::string candidate;
315 std::string path;
316 if (const char *p = getenv("PATH"); p && *p)
317 path = p;
318 else
319 path = _PATH_DEFPATH;
320 for (const char *p = std::strtok(&path[0], ":'"); p; p = std::strtok(nullptr, ":")) {
321 candidate = p;
322 candidate += '/';
323 candidate += execname;
324 if (QT_ACCESS(candidate.data(), X_OK) == 0)
325 return true;
326 }
327 return false;
328 };
329
330 static constexpr DebuggerProgram debuggerSearchOrder[] = {
331# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
332 Gdb, Lldb
333# else
334 Lldb, Gdb
335# endif
336 };
337 for (DebuggerProgram candidate : debuggerSearchOrder) {
338 switch (candidate) {
339 case None:
340 Q_UNREACHABLE();
341 break;
342 case Gdb:
343 if (hasExecutable("gdb")) {
344 debugger = Gdb;
345 return;
346 }
347 break;
348 case Lldb:
349 if (hasExecutable("lldb")) {
350 debugger = Lldb;
351 return;
352 }
353 break;
354 }
355 }
356}
357
359{
360 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
361 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
362 const char *const name = QTest::currentTestFunction();
363 writeToStderr("\n ", name ? name : "[Non-test]",
364 " function time: ", asyncSafeToString(msecsFunctionTime),
365 "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n");
366}
367
368static quintptr getProgramCounter(void *ucontext)
369{
370 quintptr pc = 0;
371 if ([[maybe_unused]] auto ctx = static_cast<ucontext_t *>(ucontext)) {
372#if 0 // keep the list below alphabetical
373
374#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM_64)
375 pc = ctx->uc_mcontext->__ss.__pc;
376#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_X86_64)
377 pc = ctx->uc_mcontext->__ss.__rip;
378
379#elif defined(Q_OS_FREEBSD) && defined(Q_PROCESSOR_X86_32)
380 pc = ctx->uc_mcontext.mc_eip;
381#elif defined(Q_OS_FREEBSD) && defined(Q_PROCESSOR_X86_64)
382 pc = ctx->uc_mcontext.mc_rip;
383
384#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_32)
385 // pc = ctx->uc_mcontext.arm_pc; // untested
386#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
387 pc = ctx->uc_mcontext.pc;
388#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_MIPS)
389 // pc = ctx->uc_mcontext.pc; // untested
390#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_LOONGARCH)
391 // pc = ctx->uc_mcontext.__pc; // untested
392#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_32)
393 // pc = ctx->uc_mcontext.uc_regs->gregs[PT_NIP]; // untested
394#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_64)
395 // pc = ctx->uc_mcontext.gregs[PT_NIP]; // untested
396#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_RISCV)
397 // pc = ctx->uc_mcontext.__gregs[REG_PC]; // untested
398#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_32)
399 pc = ctx->uc_mcontext.gregs[REG_EIP];
400#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)
401 pc = ctx->uc_mcontext.gregs[REG_RIP];
402#endif
403 }
404 return pc;
405}
406
407void generateStackTrace(quintptr ip)
408{
409 if (debugger == None || alreadyDebugging())
410 return;
411
412# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER)
413 // allow ourselves to be debugged
414 (void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
415# endif
416
417# if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
418 writeToStderr("\n=== Stack trace ===\n");
419
420 // execlp() requires null-termination, so call the default constructor
421 AsyncSafeIntBuffer pidbuffer;
422 asyncSafeToString(getpid(), std::move(pidbuffer));
423
424 // Note: POSIX.1-2001 still has fork() in the list of async-safe functions,
425 // but in a future edition, it might be removed. It would be safer to wake
426 // up a babysitter thread to launch the debugger.
427 pid_t pid = fork();
428 if (pid == 0) {
429 // child process
430 (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
431
432 // disassemble the crashing instruction, if known
433 // (syntax is the same for gdb and lldb)
434 char disasmInstr[sizeof("x/i 0x") + sizeof(ip) * 2] = {}; // zero-init for terminator
435 if (ip) {
436 strcpy(disasmInstr, "x/i ");
437 asyncSafeToHexString(ip, disasmInstr + strlen(disasmInstr));
438 }
439
440 struct Args {
441 std::array<const char *, 16> argv;
442 int count = 0;
443 Args &operator<<(const char *arg)
444 {
445 Q_ASSERT(count < int(argv.size()));
446 argv[count++] = arg;
447 return *this;
448 }
449 operator char **() const { return const_cast<char **>(argv.data()); }
450 } argv;
451
452 switch (debugger) {
453 case None:
454 Q_UNREACHABLE();
455 break;
456 case Gdb:
457 argv << "gdb" << "--nx" << "--batch";
458 if (ip)
459 argv << "-ex" << disasmInstr;
460 argv << "-ex" << "thread apply all bt"
461 << "-ex" << "printf \"\\n\""
462 << "-ex" << "info proc mappings"
463 << "--pid";
464 break;
465 case Lldb:
466 argv << "lldb" << "--no-lldbinit" << "--batch";
467 if (ip)
468 argv << "-o" << disasmInstr;
469 argv << "-o" << "bt all"
470 << "--attach-pid";
471 break;
472 }
473 if (argv.count) {
474 argv << pidbuffer.array.data() << nullptr;
475 execvp(argv.argv[0], argv);
476 }
477 _exit(1);
478 } else if (pid < 0) {
479 writeToStderr("Failed to start debugger.\n");
480 } else {
481 int ret;
482 QT_EINTR_LOOP(ret, waitpid(pid, nullptr, 0));
483 }
484
485 writeToStderr("=== End of stack trace ===\n");
486# else
487 Q_UNUSED(ip);
488# endif // !Q_OS_INTEGRITY && !Q_OS_VXWORKS
489}
490
491#ifndef Q_OS_WASM // no signal handling for WASM
493{
494 // Block most Unix signals so the WatchDog thread won't be called when
495 // external signals are delivered, thus avoiding interfering with the test
496 sigset_t set;
497 sigfillset(&set);
498
499 // we allow the crashing signals, in case we have bugs
500 for (int signo : fatalSignals)
501 sigdelset(&set, signo);
502
503 pthread_sigmask(SIG_BLOCK, &set, nullptr);
504}
505
506static std::string_view unixSignalCodeToName(int signo, int code) noexcept
507{
508 switch (signo) {
509 case SIGFPE:
510 switch (code) {
511#ifdef FPE_INTDIV
512 case FPE_INTDIV: return "FPE_INTDIV"; // Integer divide by zero.
513#endif
514#ifdef FPE_INTOVF
515 case FPE_INTOVF: return "FPE_INTOVF"; // Integer overflow.
516#endif
517#ifdef FPE_FLTDIV
518 case FPE_FLTDIV: return "FPE_FLTDIV"; // Floating point divide by zero.
519#endif
520#ifdef FPE_FLTOVF
521 case FPE_FLTOVF: return "FPE_FLTOVF"; // Floating point overflow.
522#endif
523#ifdef FPE_FLTUND
524 case FPE_FLTUND: return "FPE_FLTUND"; // Floating point underflow.
525#endif
526#ifdef FPE_FLTRES
527 case FPE_FLTRES: return "FPE_FLTRES"; // Floating point inexact result.
528#endif
529#ifdef FPE_FLTINV
530 case FPE_FLTINV: return "FPE_FLTINV"; // Floating point invalid operation.
531#endif
532#ifdef FPE_FLTSUB
533 case FPE_FLTSUB: return "FPE_FLTSUB"; // Subscript out of range.
534#endif
535#ifdef FPE_FLTUNK
536 case FPE_FLTUNK: return "FPE_FLTUNK"; // Undiagnosed floating-point exception.
537#endif
538#ifdef FPE_CONDTRAP
539 case FPE_CONDTRAP: return "FPE_CONDTRAP"; // Trap on condition.
540#endif
541 }
542 break;
543
544 case SIGILL:
545 switch (code) {
546#ifdef ILL_ILLOPC
547 case ILL_ILLOPC: return "ILL_ILLOPC"; // Illegal opcode.
548#endif
549#ifdef ILL_ILLOPN
550 case ILL_ILLOPN: return "ILL_ILLOPN"; // Illegal operand.
551#endif
552#ifdef ILL_ILLADR
553 case ILL_ILLADR: return "ILL_ILLADR"; // Illegal addressing mode.
554#endif
555#ifdef ILL_ILLTRP
556 case ILL_ILLTRP: return "ILL_ILLTRP"; // Illegal trap.
557#endif
558#ifdef ILL_PRVOPC
559 case ILL_PRVOPC: return "ILL_PRVOPC"; // Privileged opcode.
560#endif
561#ifdef ILL_PRVREG
562 case ILL_PRVREG: return "ILL_PRVREG"; // Privileged register.
563#endif
564#ifdef ILL_COPROC
565 case ILL_COPROC: return "ILL_COPROC"; // Coprocessor error.
566#endif
567#ifdef ILL_BADSTK
568 case ILL_BADSTK: return "ILL_BADSTK"; // Internal stack error.
569#endif
570#ifdef ILL_BADIADDR
571 case ILL_BADIADDR: return "ILL_BADIADDR"; // Unimplemented instruction address.
572#endif
573 }
574 break;
575
576 case SIGSEGV:
577 switch (code) {
578#ifdef SEGV_MAPERR
579 case SEGV_MAPERR: return "SEGV_MAPERR"; // Address not mapped to object.
580#endif
581#ifdef SEGV_ACCERR
582 case SEGV_ACCERR: return "SEGV_ACCERR"; // Invalid permissions for mapped object.
583#endif
584#ifdef SEGV_BNDERR
585 // Intel MPX - deprecated
586 case SEGV_BNDERR: return "SEGV_BNDERR"; // Bounds checking failure.
587#endif
588#ifdef SEGV_PKUERR
589 // Intel PKRU
590 case SEGV_PKUERR: return "SEGV_PKUERR"; // Protection key checking failure.
591#endif
592#ifdef Q_PROCESSOR_SPARC
593 // these seem to be Sparc-specific on Linux
594# ifdef SEGV_ACCADI
595 case SEGV_ACCADI: return "SEGV_ACCADI"; // ADI not enabled for mapped object.
596# endif
597# ifdef SEGV_ADIDERR
598 case SEGV_ADIDERR: return "SEGV_ADIDERR"; // Disrupting MCD error.
599# endif
600# ifdef SEGV_ADIPERR
601 case SEGV_ADIPERR: return "SEGV_ADIPERR"; // Precise MCD exception.
602# endif
603#endif
604#ifdef Q_PROCESSOR_ARM
605# ifdef SEGV_MTEAERR
606 case SEGV_MTEAERR: return "SEGV_MTEAERR"; // Asynchronous ARM MTE error.
607# endif
608# ifdef SEGV_MTESERR
609 case SEGV_MTESERR: return "SEGV_MTESERR"; // Synchronous ARM MTE exception.
610# endif
611#endif
612#ifdef SEGV_CPERR
613 // seen on both AArch64 and x86 Linux
614 case SEGV_CPERR: return "SEGV_CPERR"; // Control protection fault
615#endif
616 }
617 break;
618
619 case SIGBUS:
620 switch (code) {
621#ifdef BUS_ADRALN
622 case BUS_ADRALN: return "BUS_ADRALN"; // Invalid address alignment.
623#endif
624#ifdef BUS_ADRERR
625 case BUS_ADRERR: return "BUS_ADRERR"; // Non-existant physical address.
626#endif
627#ifdef BUS_OBJERR
628 case BUS_OBJERR: return "BUS_OBJERR"; // Object specific hardware error.
629#endif
630#ifdef BUS_MCEERR_AR
631 case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; // Hardware memory error: action required.
632#endif
633#ifdef BUS_MCEERR_AO
634 case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; // Hardware memory error: action optional.
635#endif
636 }
637 break;
638 }
639
640 return {};
641}
642
643template <typename T> static
644 std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().si_uid) >= 1>
646{
647 writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid),
648 " UID ", asyncSafeToString(info->si_uid));
649}
650[[maybe_unused]] static void printSentSignalInfo(...) {}
651
652template <typename T> static std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1>
653printCrashingSignalInfo(T *info, quintptr pc)
654{
655 using HexString = std::array<char, sizeof(quintptr) * 2 + 2>;
656 auto toHexString = [](quintptr u, HexString &&r = {}) {
657 return asyncSafeToHexString(u, r.data());
658 };
659
660 std::string_view name = unixSignalCodeToName(info->si_signo, info->si_code);
661 writeToStderr(", code ", name.size() ? name : asyncSafeToString(info->si_code));
662 if (pc)
663 writeToStderr(", at instruction address ", toHexString(pc));
664 writeToStderr(", accessing address ", toHexString(quintptr(info->si_addr)));
665}
667
668[[maybe_unused]] static void regularHandler(int signum)
669{
670 actionHandler(signum, nullptr, nullptr);
671}
672
674{
675 pauseOnCrash = qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH");
676 struct sigaction act;
677 memset(&act, 0, sizeof(act));
678 act.sa_handler = SIG_DFL;
680
681 // Remove the handler after it is invoked.
683
684# ifdef SA_SIGINFO
685 act.sa_flags |= SA_SIGINFO;
686 act.sa_sigaction = actionHandler;
687# else
689# endif
690
691 // Block all fatal signals in our signal handler so we don't try to close
692 // the testlog twice.
694 for (int signal : fatalSignals)
696
697 for (size_t i = 0; i < fatalSignals.size(); ++i)
699}
700
702{
703 // Restore the default signal handlers in place of ours.
704 // If ours has been replaced, leave the replacement alone.
705 auto isOurs = [](const struct sigaction &old) {
706# ifdef SA_SIGINFO
707 return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == actionHandler;
708# else
709 return old.sa_handler == regularHandler;
710# endif
711 };
712 struct sigaction action;
713
714 for (size_t i = 0; i < fatalSignals.size(); ++i) {
715 struct sigaction &act = oldActions[i];
716 if (sigaction(fatalSignals[i], nullptr, &action))
717 continue; // Failed to query present handler
718 if (action.sa_flags == 0 && action.sa_handler == SIG_DFL)
719 continue; // Already the default
720 if (isOurs(action))
721 sigaction(fatalSignals[i], &act, nullptr);
722 }
723
725}
726
727static auto alternateStackSize() noexcept
728{
729 struct R { size_t size, pageSize; };
730 static constexpr size_t MinStackSize = 32 * 1024;
731 size_t pageSize = sysconf(_SC_PAGESIZE);
732 size_t size = SIGSTKSZ;
733 if (size < MinStackSize) {
734 size = MinStackSize;
735 } else {
736 // round up to a page
737 size = (size + pageSize - 1) & -pageSize;
738 }
739
740 return R{ size + pageSize, pageSize };
741}
742
744{
745 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
746 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
747# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
748 // Let the signal handlers use an alternate stack
749 // This is necessary if SIGSEGV is to catch a stack overflow
750 auto r = alternateStackSize();
751 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
752# ifdef MAP_STACK
753 flags |= MAP_STACK;
754# endif
755 alternateStackBase = mmap(nullptr, r.size, PROT_READ | PROT_WRITE, flags, -1, 0);
757 return 0;
758
759 // mark the bottom page inaccessible, to catch a handler stack overflow
760 (void) mprotect(alternateStackBase, r.pageSize, PROT_NONE);
761
763 stack.ss_flags = 0;
765 stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
766 sigaltstack(&stack, nullptr);
767 return SA_ONSTACK;
768# else
769 return 0;
770# endif
771}
772
774{
775# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
777 stack_t stack = {};
778 stack.ss_flags = SS_DISABLE;
779 sigaltstack(&stack, nullptr);
781 }
782# endif
783}
784
785void actionHandler(int signum, siginfo_t *info, void *ucontext)
786{
787 writeToStderr("Received signal ", asyncSafeToString(signum),
788 " (SIG", signalName(signum), ")");
789
790 quintptr pc = 0;
791 bool isCrashingSignal =
792 std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
793 if (isCrashingSignal && (!info || info->si_code <= 0))
794 isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
795 if (isCrashingSignal)
796 printCrashingSignalInfo(info, (pc = getProgramCounter(ucontext)));
797 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
799
801 if (signum != SIGINT) {
802 generateStackTrace(pc);
803 if (pauseOnCrash) {
804 writeToStderr("Pausing process ", asyncSafeToString(getpid()),
805 " for debugging\n");
806 raise(SIGSTOP);
807 }
808 }
809
810 // chain back to the previous handler, if any
811 for (size_t i = 0; i < fatalSignals.size(); ++i) {
812 struct sigaction &act = oldActions[i];
813 if (signum != fatalSignals[i])
814 continue;
815
816 // restore the handler (if SA_RESETHAND hasn't done the job for us)
817 if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
818 (void) sigaction(signum, &act, nullptr);
819
820 if (!isCrashingSignal)
821 raise(signum);
822
823 // signal is blocked, so it'll be delivered when we return
824 return;
825 }
826
827 // we shouldn't reach here!
828 std::abort();
829}
830#endif // !defined(Q_OS_WASM)
831
832} // namespace CrashHandler
833} // namespace QTest
834
835QT_END_NAMESPACE
static std::enable_if_t< sizeof(std::declval< T >().si_addr) >=1 > printCrashingSignalInfo(T *info, quintptr pc)
static quintptr getProgramCounter(void *ucontext)
static void regularHandler(int signum)
static std::string_view unixSignalCodeToName(int signo, int code) noexcept
static auto alternateStackSize() noexcept
static void printCrashingSignalInfo(...)
static std::enable_if_t< sizeof(std::declval< T >().si_pid)+sizeof(std::declval< T >().si_uid) >=1 > printSentSignalInfo(T *info)
static void actionHandler(int signum, siginfo_t *info, void *)
static DebuggerProgram debugger
void generateStackTrace(quintptr ip)
static void printSentSignalInfo(...)
#define __has_include(x)
#define MAP_FAILED
#define _PATH_DEFPATH
static ssize_t writeToStderr(Args &&... args)
static const char * signalName(int signum) noexcept
#define SIGSTKSZ
#define SA_RESETHAND
static constexpr std::array fatalSignals
static constexpr std::array crashingSignals
#define OUR_SIGNALS(F)