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 // Try to handle https://github.com/llvm/llvm-project/issues/53254,
262 // where LLDB will hang and fail to provide a valid stack trace.
263# if defined(Q_PROCESSOR_ARM)
264 return;
265 #else
266 #define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
267 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
268 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
269 return;
270# endif
271#endif
272
273#ifdef Q_OS_UNIX
274 // like QStandardPaths::findExecutable(), but simpler
275 auto hasExecutable = [](const char *execname) {
276 std::string candidate;
277 std::string path;
278 if (const char *p = getenv(name: "PATH"); p && *p)
279 path = p;
280 else
281 path = _PATH_DEFPATH;
282 for (const char *p = std::strtok(s: &path[0], delim: ":'"); p; p = std::strtok(s: nullptr, delim: ":")) {
283 candidate = p;
284 candidate += '/';
285 candidate += execname;
286 if (QT_ACCESS(name: candidate.data(), X_OK) == 0)
287 return true;
288 }
289 return false;
290 };
291
292 static constexpr DebuggerProgram debuggerSearchOrder[] = {
293# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
294 Gdb, Lldb
295# else
296 Lldb, Gdb
297# endif
298 };
299 for (DebuggerProgram candidate : debuggerSearchOrder) {
300 switch (candidate) {
301 case None:
302 Q_UNREACHABLE();
303 break;
304 case Gdb:
305 if (hasExecutable("gdb")) {
306 debugger = Gdb;
307 return;
308 }
309 break;
310 case Lldb:
311 if (hasExecutable("lldb")) {
312 debugger = Lldb;
313 return;
314 }
315 break;
316 }
317 }
318#endif // Q_OS_UNIX
319}
320
321#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
322void printTestRunTime()
323{
324 const int msecsFunctionTime = qRound(d: QTestLog::msecsFunctionTime());
325 const int msecsTotalTime = qRound(d: QTestLog::msecsTotalTime());
326 const char *const name = QTest::currentTestFunction();
327 writeToStderr(args: "\n ", args: name ? name : "[Non-test]",
328 args: " function time: ", args: asyncSafeToString(n: msecsFunctionTime),
329 args: "ms, total time: ", args: asyncSafeToString(n: msecsTotalTime), args: "ms\n");
330}
331
332void generateStackTrace()
333{
334 if (debugger == None || alreadyDebugging())
335 return;
336
337# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER)
338 // allow ourselves to be debugged
339 (void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
340# endif
341
342# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
343 writeToStderr(args: "\n=== Stack trace ===\n");
344
345 // execlp() requires null-termination, so call the default constructor
346 AsyncSafeIntBuffer pidbuffer;
347 asyncSafeToString(n: getpid(), result: std::move(pidbuffer));
348
349 // Note: POSIX.1-2001 still has fork() in the list of async-safe functions,
350 // but in a future edition, it might be removed. It would be safer to wake
351 // up a babysitter thread to launch the debugger.
352 pid_t pid = fork();
353 if (pid == 0) {
354 // child process
355 (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
356
357 switch (debugger) {
358 case None:
359 Q_UNREACHABLE();
360 break;
361 case Gdb:
362 execlp(file: "gdb", arg: "gdb", "--nx", "--batch", "-ex", "thread apply all bt",
363 "--pid", pidbuffer.array.data(), nullptr);
364 break;
365 case Lldb:
366 execlp(file: "lldb", arg: "lldb", "--no-lldbinit", "--batch", "-o", "bt all",
367 "--attach-pid", pidbuffer.array.data(), nullptr);
368 break;
369 }
370 _exit(status: 1);
371 } else if (pid < 0) {
372 writeToStderr(args: "Failed to start debugger.\n");
373 } else {
374 int ret;
375 QT_EINTR_LOOP(ret, waitpid(pid, nullptr, 0));
376 }
377
378 writeToStderr(args: "=== End of stack trace ===\n");
379# endif // Q_OS_UNIX && !Q_OS_WASM && !Q_OS_INTEGRITY && !Q_OS_VXWORKS
380}
381#endif // !defined(Q_OS_WASM) || QT_CONFIG(thread)
382
383#if defined(Q_OS_WIN)
384void blockUnixSignals()
385{
386 // Windows does have C signals, but doesn't use them for the purposes we're
387 // talking about here
388}
389#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
390void blockUnixSignals()
391{
392 // Block most Unix signals so the WatchDog thread won't be called when
393 // external signals are delivered, thus avoiding interfering with the test
394 sigset_t set;
395 sigfillset(set: &set);
396
397 // we allow the crashing signals, in case we have bugs
398 for (int signo : FatalSignalHandler::fatalSignals)
399 sigdelset(set: &set, signo: signo);
400
401 pthread_sigmask(SIG_BLOCK, newmask: &set, oldmask: nullptr);
402}
403#endif // Q_OS_* choice
404
405#if defined(Q_OS_WIN)
406void DebugSymbolResolver::cleanup()
407{
408 if (m_dbgHelpLib)
409 FreeLibrary(m_dbgHelpLib);
410 m_dbgHelpLib = 0;
411 m_symFromAddr = nullptr;
412}
413
414DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
415 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
416{
417 bool success = false;
418 m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
419 if (m_dbgHelpLib) {
420 SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
421 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
422 m_symFromAddr = reinterpret_cast<SymFromAddrType>(
423 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
424 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
425 }
426 if (!success)
427 cleanup();
428}
429
430DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
431{
432 // reserve additional buffer where SymFromAddr() will store the name
433 struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
434 enum { symbolNameLength = 255 };
435
436 char name[symbolNameLength + 1];
437 };
438
439 Symbol result;
440 if (!isValid())
441 return result;
442 NamedSymbolInfo symbolBuffer;
443 memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
444 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
445 symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
446 if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
447 return result;
448 result.name = qstrdup(symbolBuffer.Name);
449 result.address = symbolBuffer.Address;
450 return result;
451}
452
453WindowsFaultHandler::WindowsFaultHandler()
454{
455# if !defined(Q_CC_MINGW)
456 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
457# endif
458 SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
459 SetUnhandledExceptionFilter(windowsFaultHandler);
460}
461
462LONG WINAPI WindowsFaultHandler::windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
463{
464 enum { maxStackFrames = 100 };
465 char appName[MAX_PATH];
466 if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
467 appName[0] = 0;
468 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
469 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
470 const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
471 fprintf(stderr, "A crash occurred in %s.\n", appName);
472 if (const char *name = QTest::currentTestFunction())
473 fprintf(stderr, "While testing %s\n", name);
474 fprintf(stderr, "Function time: %dms Total time: %dms\n\n"
475 "Exception address: 0x%p\n"
476 "Exception code : 0x%lx\n",
477 msecsFunctionTime, msecsTotalTime, exceptionAddress,
478 exInfo->ExceptionRecord->ExceptionCode);
479
480 DebugSymbolResolver resolver(GetCurrentProcess());
481 if (resolver.isValid()) {
482 DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
483 if (exceptionSymbol.name) {
484 fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name);
485 delete [] exceptionSymbol.name;
486 }
487 Q_DECL_UNINITIALIZED void *stack[maxStackFrames];
488 fputs("\nStack:\n", stderr);
489 const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
490 for (unsigned f = 0; f < frameCount; ++f) {
491 DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
492 if (symbol.name) {
493 fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
494 delete [] symbol.name;
495 } else {
496 fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1);
497 }
498 }
499 }
500
501 fputc('\n', stderr);
502
503 return EXCEPTION_EXECUTE_HANDLER;
504}
505#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
506bool FatalSignalHandler::pauseOnCrash = false;
507
508FatalSignalHandler::FatalSignalHandler()
509{
510 pauseOnCrash = qEnvironmentVariableIsSet(varName: "QTEST_PAUSE_ON_CRASH");
511 struct sigaction act;
512 memset(s: &act, c: 0, n: sizeof(act));
513 act.sa_handler = SIG_DFL;
514 oldActions().fill(u: act);
515
516 // Remove the handler after it is invoked.
517 act.sa_flags = SA_RESETHAND | setupAlternateStack();
518
519# ifdef SA_SIGINFO
520 act.sa_flags |= SA_SIGINFO;
521 act.sa_sigaction = FatalSignalHandler::actionHandler;
522# else
523 act.sa_handler = FatalSignalHandler::regularHandler;
524# endif
525
526 // Block all fatal signals in our signal handler so we don't try to close
527 // the testlog twice.
528 sigemptyset(set: &act.sa_mask);
529 for (int signal : fatalSignals)
530 sigaddset(set: &act.sa_mask, signo: signal);
531
532 for (size_t i = 0; i < fatalSignals.size(); ++i)
533 sigaction(sig: fatalSignals[i], act: &act, oact: &oldActions()[i]);
534}
535
536FatalSignalHandler::~FatalSignalHandler()
537{
538 // Restore the default signal handlers in place of ours.
539 // If ours has been replaced, leave the replacement alone.
540 auto isOurs = [](const struct sigaction &old) {
541# ifdef SA_SIGINFO
542 return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler;
543# else
544 return old.sa_handler == FatalSignalHandler::regularHandler;
545# endif
546 };
547 struct sigaction action;
548
549 for (size_t i = 0; i < fatalSignals.size(); ++i) {
550 struct sigaction &act = oldActions()[i];
551 if (act.sa_flags == 0 && act.sa_handler == SIG_DFL)
552 continue; // Already the default
553 if (sigaction(sig: fatalSignals[i], act: nullptr, oact: &action))
554 continue; // Failed to query present handler
555 if (isOurs(action))
556 sigaction(sig: fatalSignals[i], act: &act, oact: nullptr);
557 }
558
559 freeAlternateStack();
560}
561
562FatalSignalHandler::OldActionsArray &FatalSignalHandler::oldActions()
563{
564 Q_CONSTINIT static OldActionsArray oldActions {};
565 return oldActions;
566}
567
568auto FatalSignalHandler::alternateStackSize()
569{
570 struct R { size_t size, pageSize; };
571 static constexpr size_t MinStackSize = 32 * 1024;
572 size_t pageSize = sysconf(_SC_PAGESIZE);
573 size_t size = SIGSTKSZ;
574 if (size < MinStackSize) {
575 size = MinStackSize;
576 } else {
577 // round up to a page
578 size = (size + pageSize - 1) & -pageSize;
579 }
580
581 return R{ .size: size + pageSize, .pageSize: pageSize };
582}
583
584int FatalSignalHandler::setupAlternateStack()
585{
586 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
587 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
588# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
589 // Let the signal handlers use an alternate stack
590 // This is necessary if SIGSEGV is to catch a stack overflow
591 auto r = alternateStackSize();
592 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
593# ifdef MAP_STACK
594 flags |= MAP_STACK;
595# endif
596 alternateStackBase = mmap(addr: nullptr, len: r.size, PROT_READ | PROT_WRITE, flags: flags, fd: -1, offset: 0);
597 if (alternateStackBase == MAP_FAILED)
598 return 0;
599
600 // mark the bottom page inaccessible, to catch a handler stack overflow
601 (void) mprotect(addr: alternateStackBase, len: r.pageSize, PROT_NONE);
602
603 stack_t stack;
604 stack.ss_flags = 0;
605 stack.ss_size = r.size - r.pageSize;
606 stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
607 sigaltstack(ss: &stack, oss: nullptr);
608 return SA_ONSTACK;
609# else
610 return 0;
611# endif
612}
613
614void FatalSignalHandler::freeAlternateStack()
615{
616# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
617 if (alternateStackBase != MAP_FAILED) {
618 stack_t stack = {};
619 stack.ss_flags = SS_DISABLE;
620 sigaltstack(ss: &stack, oss: nullptr);
621 munmap(addr: alternateStackBase, len: alternateStackSize().size);
622 }
623# endif
624}
625
626void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *)
627{
628 writeToStderr(args: "Received signal ", args: asyncSafeToString(n: signum),
629 args: " (SIG", args: signalName(signum), args: ")");
630
631 bool isCrashingSignal =
632 std::find(first: crashingSignals.begin(), last: crashingSignals.end(), val: signum) != crashingSignals.end();
633 if (isCrashingSignal && (!info || info->si_code <= 0))
634 isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
635 if (isCrashingSignal)
636 printCrashingSignalInfo(info);
637 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
638 printSentSignalInfo(info);
639
640 printTestRunTime();
641 if (signum != SIGINT) {
642 generateStackTrace();
643 if (pauseOnCrash) {
644 writeToStderr(args: "Pausing process ", args: asyncSafeToString(n: getpid()),
645 args: " for debugging\n");
646 raise(SIGSTOP);
647 }
648 }
649
650 // chain back to the previous handler, if any
651 for (size_t i = 0; i < fatalSignals.size(); ++i) {
652 struct sigaction &act = oldActions()[i];
653 if (signum != fatalSignals[i])
654 continue;
655
656 // restore the handler (if SA_RESETHAND hasn't done the job for us)
657 if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
658 (void) sigaction(sig: signum, act: &act, oact: nullptr);
659
660 if (!isCrashingSignal)
661 raise(sig: signum);
662
663 // signal is blocked, so it'll be delivered when we return
664 return;
665 }
666
667 // we shouldn't reach here!
668 std::abort();
669}
670#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
671
672} // namespace CrashHandler
673} // namespace QTest
674
675QT_END_NAMESPACE
676

Provided by KDAB

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

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