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