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/qtestcase.h>
6#include <QtTest/private/qtestcrashhandler_p.h>
7#include <QtTest/qtestassert.h>
8
9#include <QtCore/qbytearray.h>
10#include <QtCore/qcoreapplication.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qdir.h>
13#include <QtCore/qdiriterator.h>
14#include <QtCore/qfile.h>
15#include <QtCore/qfileinfo.h>
16#include <QtCore/qfloat16.h>
17#include <QtCore/qlibraryinfo.h>
18#include <QtCore/qlist.h>
19#include <QtCore/qmetaobject.h>
20#include <QtCore/qobject.h>
21#include <QtCore/qstringlist.h>
22#include <QtCore/qtemporarydir.h>
23#include <QtCore/qthread.h>
24#include <QtCore/qvarlengtharray.h>
25#include <QtCore/private/qlocking_p.h>
26#include <QtCore/private/qtools_p.h>
27#include <QtCore/private/qwaitcondition_p.h>
28
29#include <QtCore/qtestsupport_core.h>
30
31#include <QtTest/private/qtestlog_p.h>
32#include <QtTest/private/qtesttable_p.h>
33#include <QtTest/qtestdata.h>
34#include <QtTest/private/qtestresult_p.h>
35#include <QtTest/private/qsignaldumper_p.h>
36#include <QtTest/private/qbenchmark_p.h>
37#if QT_CONFIG(batch_test_support)
38#include <QtTest/private/qtestregistry_p.h>
39#endif // QT_CONFIG(batch_test_support)
40#include <QtTest/private/cycle_p.h>
41#include <QtTest/private/qtestblacklist_p.h>
42#if defined(HAVE_XCTEST)
43#include <QtTest/private/qxctestlogger_p.h>
44#endif
45#if defined Q_OS_MACOS
46#include <QtTest/private/qtestutil_macos_p.h>
47#endif
48
49#if defined(Q_OS_DARWIN)
50#include <QtTest/private/qappletestlogger_p.h>
51#endif
52
53#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
54# include <charconv>
55#else
56// Broken implementation, causes link failures just by #include'ing!
57# undef __cpp_lib_to_chars // in case <version> was included
58#endif
59
60#include <stdio.h>
61#include <stdlib.h>
62
63#if defined(Q_OS_LINUX)
64#include <sys/prctl.h>
65#include <sys/types.h>
66#include <fcntl.h>
67#endif
68
69#ifdef Q_OS_UNIX
70#include <QtCore/private/qcore_unix_p.h>
71
72#include <errno.h>
73#if __has_include(<paths.h>)
74# include <paths.h>
75#endif
76#include <signal.h>
77#include <time.h>
78#include <sys/mman.h>
79#include <sys/wait.h>
80#include <unistd.h>
81# if !defined(Q_OS_INTEGRITY)
82# include <sys/resource.h>
83# endif
84# ifndef _PATH_DEFPATH
85# define _PATH_DEFPATH "/usr/bin:/bin"
86# endif
87# ifndef SIGSTKSZ
88# define SIGSTKSZ 0 /* we have code to set the minimum */
89# endif
90# ifndef SA_RESETHAND
91# define SA_RESETHAND 0
92# endif
93#endif
94
95#if defined(Q_OS_MACOS)
96#include <IOKit/pwr_mgt/IOPMLib.h>
97#include <mach/task.h>
98#include <mach/mach_init.h>
99#include <CoreFoundation/CFPreferences.h>
100#endif
101
102#if defined(Q_OS_WASM)
103#include <emscripten.h>
104#endif
105
106QT_BEGIN_NAMESPACE
107
108using namespace Qt::StringLiterals;
109
110namespace QTest {
111namespace CrashHandler {
112#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
113struct iovec IoVec(struct iovec vec)
114{
115 return vec;
116}
117struct iovec IoVec(const char *str)
118{
119 struct iovec r = {};
120 r.iov_base = const_cast<char *>(str);
121 r.iov_len = strlen(s: str);
122 return r;
123}
124
125struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result)
126{
127 char *ptr = result.array.data();
128 if (false) {
129#ifdef __cpp_lib_to_chars
130 } else if (auto r = std::to_chars(first: ptr, last: ptr + result.array.size(), value: n, base: 10); r.ec == std::errc{}) {
131 ptr = r.ptr;
132#endif
133 } else {
134 // handle the sign
135 if (n < 0) {
136 *ptr++ = '-';
137 n = -n;
138 }
139
140 // find the highest power of the base that is less than this number
141 static constexpr int StartingDivider = ([]() {
142 int divider = 1;
143 for (int i = 0; i < std::numeric_limits<int>::digits10; ++i)
144 divider *= 10;
145 return divider;
146 }());
147 int divider = StartingDivider;
148 while (divider && n < divider)
149 divider /= 10;
150
151 // now convert to string
152 while (divider > 1) {
153 int quot = n / divider;
154 n = n % divider;
155 divider /= 10;
156 *ptr++ = quot + '0';
157 }
158 *ptr++ = n + '0';
159 }
160
161#ifndef QT_NO_DEBUG
162 // this isn't necessary, it just helps in the debugger
163 *ptr = '\0';
164#endif
165 struct iovec r;
166 r.iov_base = result.array.data();
167 r.iov_len = ptr - result.array.data();
168 return r;
169};
170#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
171
172bool alreadyDebugging()
173{
174#if defined(Q_OS_LINUX)
175 int fd = open(file: "/proc/self/status", O_RDONLY);
176 if (fd == -1)
177 return false;
178 char buffer[2048];
179 ssize_t size = read(fd: fd, buf: buffer, nbytes: sizeof(buffer) - 1);
180 if (size == -1) {
181 close(fd: fd);
182 return false;
183 }
184 buffer[size] = 0;
185 const char tracerPidToken[] = "\nTracerPid:";
186 char *tracerPid = strstr(haystack: buffer, needle: tracerPidToken);
187 if (!tracerPid) {
188 close(fd: fd);
189 return false;
190 }
191 tracerPid += sizeof(tracerPidToken);
192 long int pid = strtol(nptr: tracerPid, endptr: &tracerPid, base: 10);
193 close(fd: fd);
194 return pid != 0;
195#elif defined(Q_OS_WIN)
196 return IsDebuggerPresent();
197#elif defined(Q_OS_MACOS)
198 // Check if there is an exception handler for the process:
199 mach_msg_type_number_t portCount = 0;
200 exception_mask_t masks[EXC_TYPES_COUNT];
201 mach_port_t ports[EXC_TYPES_COUNT];
202 exception_behavior_t behaviors[EXC_TYPES_COUNT];
203 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
204 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
205 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
206 ports, behaviors, flavors);
207 if (result == KERN_SUCCESS) {
208 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
209 if (MACH_PORT_VALID(ports[portIndex])) {
210 return true;
211 }
212 }
213 }
214 return false;
215#else
216 // TODO
217 return false;
218#endif
219}
220
221namespace {
222enum DebuggerProgram { None, Gdb, Lldb };
223static bool hasSystemCrashReporter()
224{
225#if defined(Q_OS_MACOS)
226 return QTestPrivate::macCrashReporterWillShowDialog();
227#else
228 return false;
229#endif
230}
231} // unnamed namespaced
232
233void maybeDisableCoreDump()
234{
235#ifdef RLIMIT_CORE
236 bool ok = false;
237 const int disableCoreDump = qEnvironmentVariableIntValue(varName: "QTEST_DISABLE_CORE_DUMP", ok: &ok);
238 if (ok && disableCoreDump) {
239 struct rlimit limit;
240 limit.rlim_cur = 0;
241 limit.rlim_max = 0;
242 if (setrlimit(RLIMIT_CORE, rlimits: &limit) != 0)
243 qWarning(msg: "Failed to disable core dumps: %d", errno);
244 }
245#endif
246}
247
248static DebuggerProgram debugger = None;
249void prepareStackTrace()
250{
251
252 bool ok = false;
253 const int disableStackDump = qEnvironmentVariableIntValue(varName: "QTEST_DISABLE_STACK_DUMP", ok: &ok);
254 if (ok && disableStackDump)
255 return;
256
257 if (hasSystemCrashReporter())
258 return;
259
260#if defined(Q_OS_MACOS)
261 #define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
262 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
263 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
264 return; // LLDB will fail to provide a valid stack trace
265#endif
266
267#ifdef Q_OS_UNIX
268 // like QStandardPaths::findExecutable(), but simpler
269 auto hasExecutable = [](const char *execname) {
270 std::string candidate;
271 std::string path;
272 if (const char *p = getenv(name: "PATH"); p && *p)
273 path = p;
274 else
275 path = _PATH_DEFPATH;
276 for (const char *p = std::strtok(s: &path[0], delim: ":'"); p; p = std::strtok(s: nullptr, delim: ":")) {
277 candidate = p;
278 candidate += '/';
279 candidate += execname;
280 if (QT_ACCESS(name: candidate.data(), X_OK) == 0)
281 return true;
282 }
283 return false;
284 };
285
286 static constexpr DebuggerProgram debuggerSearchOrder[] = {
287# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
288 Gdb, Lldb
289# else
290 Lldb, Gdb
291# endif
292 };
293 for (DebuggerProgram candidate : debuggerSearchOrder) {
294 switch (candidate) {
295 case None:
296 Q_UNREACHABLE();
297 break;
298 case Gdb:
299 if (hasExecutable("gdb")) {
300 debugger = Gdb;
301 return;
302 }
303 break;
304 case Lldb:
305 if (hasExecutable("lldb")) {
306 debugger = Lldb;
307 return;
308 }
309 break;
310 }
311 }
312#endif // Q_OS_UNIX
313}
314
315#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
316void printTestRunTime()
317{
318 const int msecsFunctionTime = qRound(d: QTestLog::msecsFunctionTime());
319 const int msecsTotalTime = qRound(d: QTestLog::msecsTotalTime());
320 const char *const name = QTest::currentTestFunction();
321 writeToStderr(args: "\n ", args: name ? name : "[Non-test]",
322 args: " function time: ", args: asyncSafeToString(n: msecsFunctionTime),
323 args: "ms, total time: ", args: asyncSafeToString(n: msecsTotalTime), args: "ms\n");
324}
325
326void generateStackTrace()
327{
328 if (debugger == None || alreadyDebugging())
329 return;
330
331# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER)
332 // allow ourselves to be debugged
333 (void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
334# endif
335
336# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
337 writeToStderr(args: "\n=== Stack trace ===\n");
338
339 // execlp() requires null-termination, so call the default constructor
340 AsyncSafeIntBuffer pidbuffer;
341 asyncSafeToString(n: getpid(), result: std::move(pidbuffer));
342
343 // Note: POSIX.1-2001 still has fork() in the list of async-safe functions,
344 // but in a future edition, it might be removed. It would be safer to wake
345 // up a babysitter thread to launch the debugger.
346 pid_t pid = fork();
347 if (pid == 0) {
348 // child process
349 (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
350
351 switch (debugger) {
352 case None:
353 Q_UNREACHABLE();
354 break;
355 case Gdb:
356 execlp(file: "gdb", arg: "gdb", "--nx", "--batch", "-ex", "thread apply all bt",
357 "--pid", pidbuffer.array.data(), nullptr);
358 break;
359 case Lldb:
360 execlp(file: "lldb", arg: "lldb", "--no-lldbinit", "--batch", "-o", "bt all",
361 "--attach-pid", pidbuffer.array.data(), nullptr);
362 break;
363 }
364 _exit(status: 1);
365 } else if (pid < 0) {
366 writeToStderr(args: "Failed to start debugger.\n");
367 } else {
368 int ret;
369 QT_EINTR_LOOP(ret, waitpid(pid, nullptr, 0));
370 }
371
372 writeToStderr(args: "=== End of stack trace ===\n");
373# endif // Q_OS_UNIX && !Q_OS_WASM && !Q_OS_INTEGRITY && !Q_OS_VXWORKS
374}
375#endif // !defined(Q_OS_WASM) || QT_CONFIG(thread)
376
377#if defined(Q_OS_WIN)
378void blockUnixSignals()
379{
380 // Windows does have C signals, but doesn't use them for the purposes we're
381 // talking about here
382}
383#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
384void blockUnixSignals()
385{
386 // Block most Unix signals so the WatchDog thread won't be called when
387 // external signals are delivered, thus avoiding interfering with the test
388 sigset_t set;
389 sigfillset(set: &set);
390
391 // we allow the crashing signals, in case we have bugs
392 for (int signo : FatalSignalHandler::fatalSignals)
393 sigdelset(set: &set, signo: signo);
394
395 pthread_sigmask(SIG_BLOCK, newmask: &set, oldmask: nullptr);
396}
397#endif // Q_OS_* choice
398
399#if defined(Q_OS_WIN)
400void DebugSymbolResolver::cleanup()
401{
402 if (m_dbgHelpLib)
403 FreeLibrary(m_dbgHelpLib);
404 m_dbgHelpLib = 0;
405 m_symFromAddr = nullptr;
406}
407
408DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
409 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
410{
411 bool success = false;
412 m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
413 if (m_dbgHelpLib) {
414 SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
415 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
416 m_symFromAddr = reinterpret_cast<SymFromAddrType>(
417 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
418 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
419 }
420 if (!success)
421 cleanup();
422}
423
424DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
425{
426 // reserve additional buffer where SymFromAddr() will store the name
427 struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
428 enum { symbolNameLength = 255 };
429
430 char name[symbolNameLength + 1];
431 };
432
433 Symbol result;
434 if (!isValid())
435 return result;
436 NamedSymbolInfo symbolBuffer;
437 memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
438 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
439 symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
440 if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
441 return result;
442 result.name = qstrdup(symbolBuffer.Name);
443 result.address = symbolBuffer.Address;
444 return result;
445}
446
447WindowsFaultHandler::WindowsFaultHandler()
448{
449# if !defined(Q_CC_MINGW)
450 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
451# endif
452 SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
453 SetUnhandledExceptionFilter(windowsFaultHandler);
454}
455
456LONG WINAPI WindowsFaultHandler::windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
457{
458 enum { maxStackFrames = 100 };
459 char appName[MAX_PATH];
460 if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
461 appName[0] = 0;
462 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
463 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
464 const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
465 fprintf(stderr, "A crash occurred in %s.\n", appName);
466 if (const char *name = QTest::currentTestFunction())
467 fprintf(stderr, "While testing %s\n", name);
468 fprintf(stderr, "Function time: %dms Total time: %dms\n\n"
469 "Exception address: 0x%p\n"
470 "Exception code : 0x%lx\n",
471 msecsFunctionTime, msecsTotalTime, exceptionAddress,
472 exInfo->ExceptionRecord->ExceptionCode);
473
474 DebugSymbolResolver resolver(GetCurrentProcess());
475 if (resolver.isValid()) {
476 DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
477 if (exceptionSymbol.name) {
478 fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name);
479 delete [] exceptionSymbol.name;
480 }
481 void *stack[maxStackFrames];
482 fputs("\nStack:\n", stderr);
483 const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
484 for (unsigned f = 0; f < frameCount; ++f) {
485 DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
486 if (symbol.name) {
487 fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
488 delete [] symbol.name;
489 } else {
490 fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1);
491 }
492 }
493 }
494
495 fputc('\n', stderr);
496
497 return EXCEPTION_EXECUTE_HANDLER;
498}
499#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
500bool FatalSignalHandler::pauseOnCrash = false;
501
502FatalSignalHandler::FatalSignalHandler()
503{
504 pauseOnCrash = qEnvironmentVariableIsSet(varName: "QTEST_PAUSE_ON_CRASH");
505 struct sigaction act;
506 memset(s: &act, c: 0, n: sizeof(act));
507 act.sa_handler = SIG_DFL;
508 oldActions().fill(u: act);
509
510 // Remove the handler after it is invoked.
511 act.sa_flags = SA_RESETHAND | setupAlternateStack();
512
513# ifdef SA_SIGINFO
514 act.sa_flags |= SA_SIGINFO;
515 act.sa_sigaction = FatalSignalHandler::actionHandler;
516# else
517 act.sa_handler = FatalSignalHandler::regularHandler;
518# endif
519
520 // Block all fatal signals in our signal handler so we don't try to close
521 // the testlog twice.
522 sigemptyset(set: &act.sa_mask);
523 for (int signal : fatalSignals)
524 sigaddset(set: &act.sa_mask, signo: signal);
525
526 for (size_t i = 0; i < fatalSignals.size(); ++i)
527 sigaction(sig: fatalSignals[i], act: &act, oact: &oldActions()[i]);
528}
529
530FatalSignalHandler::~FatalSignalHandler()
531{
532 // Restore the default signal handlers in place of ours.
533 // If ours has been replaced, leave the replacement alone.
534 auto isOurs = [](const struct sigaction &old) {
535# ifdef SA_SIGINFO
536 return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler;
537# else
538 return old.sa_handler == FatalSignalHandler::regularHandler;
539# endif
540 };
541 struct sigaction action;
542
543 for (size_t i = 0; i < fatalSignals.size(); ++i) {
544 struct sigaction &act = oldActions()[i];
545 if (act.sa_flags == 0 && act.sa_handler == SIG_DFL)
546 continue; // Already the default
547 if (sigaction(sig: fatalSignals[i], act: nullptr, oact: &action))
548 continue; // Failed to query present handler
549 if (isOurs(action))
550 sigaction(sig: fatalSignals[i], act: &act, oact: nullptr);
551 }
552
553 freeAlternateStack();
554}
555
556FatalSignalHandler::OldActionsArray &FatalSignalHandler::oldActions()
557{
558 Q_CONSTINIT static OldActionsArray oldActions {};
559 return oldActions;
560}
561
562auto FatalSignalHandler::alternateStackSize()
563{
564 struct R { size_t size, pageSize; };
565 static constexpr size_t MinStackSize = 32 * 1024;
566 size_t pageSize = sysconf(_SC_PAGESIZE);
567 size_t size = SIGSTKSZ;
568 if (size < MinStackSize) {
569 size = MinStackSize;
570 } else {
571 // round up to a page
572 size = (size + pageSize - 1) & -pageSize;
573 }
574
575 return R{ .size: size + pageSize, .pageSize: pageSize };
576}
577
578int FatalSignalHandler::setupAlternateStack()
579{
580 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
581 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
582# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
583 // Let the signal handlers use an alternate stack
584 // This is necessary if SIGSEGV is to catch a stack overflow
585 auto r = alternateStackSize();
586 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
587# ifdef MAP_STACK
588 flags |= MAP_STACK;
589# endif
590 alternateStackBase = mmap(addr: nullptr, len: r.size, PROT_READ | PROT_WRITE, flags: flags, fd: -1, offset: 0);
591 if (alternateStackBase == MAP_FAILED)
592 return 0;
593
594 // mark the bottom page inaccessible, to catch a handler stack overflow
595 (void) mprotect(addr: alternateStackBase, len: r.pageSize, PROT_NONE);
596
597 stack_t stack;
598 stack.ss_flags = 0;
599 stack.ss_size = r.size - r.pageSize;
600 stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
601 sigaltstack(ss: &stack, oss: nullptr);
602 return SA_ONSTACK;
603# else
604 return 0;
605# endif
606}
607
608void FatalSignalHandler::freeAlternateStack()
609{
610# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
611 if (alternateStackBase != MAP_FAILED) {
612 stack_t stack = {};
613 stack.ss_flags = SS_DISABLE;
614 sigaltstack(ss: &stack, oss: nullptr);
615 munmap(addr: alternateStackBase, len: alternateStackSize().size);
616 }
617# endif
618}
619
620void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *)
621{
622 writeToStderr(args: "Received signal ", args: asyncSafeToString(n: signum),
623 args: " (SIG", args: signalName(signum), args: ")");
624
625 bool isCrashingSignal =
626 std::find(first: crashingSignals.begin(), last: crashingSignals.end(), val: signum) != crashingSignals.end();
627 if (isCrashingSignal && (!info || info->si_code <= 0))
628 isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
629 if (isCrashingSignal)
630 printCrashingSignalInfo(info);
631 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
632 printSentSignalInfo(info);
633
634 printTestRunTime();
635 if (signum != SIGINT) {
636 generateStackTrace();
637 if (pauseOnCrash) {
638 writeToStderr(args: "Pausing process ", args: asyncSafeToString(n: getpid()),
639 args: " for debugging\n");
640 raise(SIGSTOP);
641 }
642 }
643
644 // chain back to the previous handler, if any
645 for (size_t i = 0; i < fatalSignals.size(); ++i) {
646 struct sigaction &act = oldActions()[i];
647 if (signum != fatalSignals[i])
648 continue;
649
650 // restore the handler (if SA_RESETHAND hasn't done the job for us)
651 if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
652 (void) sigaction(sig: signum, act: &act, oact: nullptr);
653
654 if (!isCrashingSignal)
655 raise(sig: signum);
656
657 // signal is blocked, so it'll be delivered when we return
658 return;
659 }
660
661 // we shouldn't reach here!
662 std::abort();
663}
664#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
665
666} // namespace CrashHandler
667} // namespace QTest
668
669QT_END_NAMESPACE
670

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/testlib/qtestcrashhandler.cpp