5#include <QtTest/private/qtestcrashhandler_p.h>
7#include <QtCore/qbytearray.h>
8#include <QtCore/qstring.h>
9#include <QtCore/private/qcore_unix_p.h>
11#include <QtTest/qtestcase.h>
12#include <QtTest/private/qtestlog_p.h>
14#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
18# undef __cpp_lib_to_chars
36# if !defined(Q_OS_INTEGRITY)
37# include <sys/resource.h>
40# include <sys/ucontext.h>
41# elif __has_include(<ucontext.h>)
44using ucontext_t =
void;
47#if defined(Q_OS_MACOS)
48#include <QtCore/private/qcore_mac_p.h>
49#include <QtTest/private/qtestutil_macos_p.h>
51#include <IOKit/pwr_mgt/IOPMLib.h>
53#include <mach/mach_init.h>
54#include <CoreFoundation/CFPreferences.h>
56#define CSR_ALLOW_UNRESTRICTED_FS (1
<< 1
)
59#if defined(Q_OS_LINUX)
66#include <emscripten.h>
70# define _PATH_DEFPATH "/usr/bin:/bin"
76# define SA_RESETHAND 0
81using namespace Qt::StringLiterals;
83# define OUR_SIGNALS(F)
95# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
96# define ENUMERATE_SIGNALS(S) SIG ## S,
103# if defined(__GLIBC_MINOR__
) && (__GLIBC_MINOR__
>= 32
|| __GLIBC__
> 2
)
106 if (
const char *p = sigabbrev_np(signum))
115# undef ENUMERATE_SIGNALS
124 SIGILL, SIGBUS, SIGFPE, SIGSEGV
126using OldActionsArray = std::array<
struct sigaction, fatalSignals.size()>;
130 auto makeIovec = [](std::string_view arg) {
132 r.iov_base =
const_cast<
char *>(arg.data());
133 r.iov_len = arg.size();
136 struct iovec vec[] = { makeIovec(
std::forward<Args>(args))... };
137 return ::writev(STDERR_FILENO, vec,
std::size(vec));
142struct AsyncSafeIntBuffer
147 static constexpr int Digits10 = std::numeric_limits<
int>::digits10 + 3;
148 std::array<
char, Digits10> array;
149 constexpr AsyncSafeIntBuffer() : array{} {}
150 AsyncSafeIntBuffer(Qt::Initialization) {}
153std::string_view asyncSafeToString(
int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized)
155 char *ptr = result.array.data();
157#ifdef __cpp_lib_to_chars
158 }
else if (
auto r =
std::to_chars(ptr, ptr + result.array.size(), n, 10); r.ec ==
std::errc{}) {
169 static constexpr int StartingDivider = ([]() {
171 for (
int i = 0; i < std::numeric_limits<
int>::digits10; ++i)
175 int divider = StartingDivider;
176 while (divider && n < divider)
180 while (divider > 1) {
181 int quot = n / divider;
193 return std::string_view(result.array.data(), ptr - result.array.data());
196std::string_view asyncSafeToHexString(quintptr u,
char *ptr)
201 int shift =
sizeof(quintptr) * 8 - 4;
204 for (size_t i = 0; i <
sizeof(quintptr) * 2; ++i, shift -= 4)
205 ptr[i + 2] = QtMiscUtils::toHexLower(u >> shift);
207 return std::string_view(ptr,
sizeof(quintptr) * 2 + 2);
216static void actionHandler(
int signum, siginfo_t *info,
void * );
220#if defined(Q_OS_LINUX)
221 int fd = open(
"/proc/self/status", O_RDONLY);
225 ssize_t size = read(fd, buffer,
sizeof(buffer) - 1);
231 const char tracerPidToken[] =
"\nTracerPid:";
232 char *tracerPid = strstr(buffer, tracerPidToken);
237 tracerPid +=
sizeof(tracerPidToken);
238 long int pid = strtol(tracerPid, &tracerPid, 10);
241#elif defined(Q_OS_MACOS)
243 mach_msg_type_number_t portCount = 0;
244 exception_mask_t masks[EXC_TYPES_COUNT];
245 mach_port_t ports[EXC_TYPES_COUNT];
246 exception_behavior_t behaviors[EXC_TYPES_COUNT];
247 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
248 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
249 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
250 ports, behaviors, flavors);
251 if (result == KERN_SUCCESS) {
252 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
253 if (MACH_PORT_VALID(ports[portIndex])) {
267#if defined(Q_OS_MACOS)
268 return QTestPrivate::macCrashReporterWillShowDialog();
278 const int disableCoreDump = qEnvironmentVariableIntValue(
"QTEST_DISABLE_CORE_DUMP", &ok);
279 if (ok && disableCoreDump) {
283 if (setrlimit(RLIMIT_CORE, &limit) != 0)
284 qWarning(
"Failed to disable core dumps: %d", errno);
294 const int disableStackDump = qEnvironmentVariableIntValue(
"QTEST_DISABLE_STACK_DUMP", &ok);
295 if (ok && disableStackDump)
301#if defined(Q_OS_MACOS)
304# if defined(Q_PROCESSOR_ARM)
307 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
308 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
314 auto hasExecutable = [](
const char *execname) {
315 std::string candidate;
317 if (
const char *p = getenv(
"PATH"); p && *p)
321 char *saveptr =
nullptr;
322 for (
const char *p = strtok_r(&path[0],
":", &saveptr); p;
323 p = strtok_r(
nullptr,
":", &saveptr)) {
326 candidate += execname;
327 if (QT_ACCESS(candidate.data(), X_OK) == 0)
333 static constexpr DebuggerProgram debuggerSearchOrder[] = {
334# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
340 for (DebuggerProgram candidate : debuggerSearchOrder) {
346 if (hasExecutable(
"gdb")) {
352 if (hasExecutable(
"lldb")) {
363 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
364 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
365 const char *
const name = QTest::currentTestFunction();
366 writeToStderr(
"\n ", name ? name :
"[Non-test]",
367 " function time: ", asyncSafeToString(msecsFunctionTime),
368 "ms, total time: ", asyncSafeToString(msecsTotalTime),
"ms\n");
374 if ([[maybe_unused]]
auto ctx =
static_cast<ucontext_t *>(ucontext)) {
377#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM_64)
378 pc = arm_thread_state64_get_pc(ctx->uc_mcontext->__ss);
379#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_X86_64)
380 pc = ctx->uc_mcontext->__ss.__rip;
382#elif defined(Q_OS_FREEBSD) && defined(Q_PROCESSOR_X86_32)
383 pc = ctx->uc_mcontext.mc_eip;
384#elif defined(Q_OS_FREEBSD) && defined(Q_PROCESSOR_X86_64)
385 pc = ctx->uc_mcontext.mc_rip;
387#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_32)
389#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
390 pc = ctx->uc_mcontext.pc;
391#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_MIPS)
393#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_LOONGARCH)
395#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_32)
397#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_64)
399#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_RISCV)
401#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_32)
402 pc = ctx->uc_mcontext.gregs[REG_EIP];
403#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)
404 pc = ctx->uc_mcontext.gregs[REG_RIP];
412 if (debugger == None || alreadyDebugging())
415# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER)
417 (
void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
420# if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
421 writeToStderr(
"\n=== Stack trace ===\n");
424 AsyncSafeIntBuffer pidbuffer;
425 asyncSafeToString(getpid(),
std::move(pidbuffer));
433 (
void) dup2(STDERR_FILENO, STDOUT_FILENO);
437 char disasmInstr[
sizeof(
"x/i 0x") +
sizeof(ip) * 2] = {};
439 strcpy(disasmInstr,
"x/i ");
440 asyncSafeToHexString(ip, disasmInstr + strlen(disasmInstr));
444 std::array<
const char *, 16> argv;
446 Args &operator<<(
const char *arg)
448 Q_ASSERT(count <
int(argv.size()));
452 operator
char **()
const {
return const_cast<
char **>(argv.data()); }
460 argv <<
"gdb" <<
"--nx" <<
"--batch";
462 argv <<
"-ex" << disasmInstr;
463 argv <<
"-ex" <<
"thread apply all bt"
464 <<
"-ex" <<
"printf \"\\n\""
465 <<
"-ex" <<
"info proc mappings"
469 argv <<
"lldb" <<
"--no-lldbinit" <<
"--batch";
471 argv <<
"-o" << disasmInstr;
472 argv <<
"-o" <<
"bt all"
477 argv << pidbuffer.array.data() <<
nullptr;
478 execvp(argv.argv[0], argv);
481 }
else if (pid < 0) {
482 writeToStderr(
"Failed to start debugger.\n");
485 QT_EINTR_LOOP(ret, waitpid(pid,
nullptr, 0));
488 writeToStderr(
"=== End of stack trace ===\n");
503 for (
int signo : fatalSignals)
504 sigdelset(&set, signo);
506 pthread_sigmask(SIG_BLOCK, &set,
nullptr);
515 case FPE_INTDIV:
return "FPE_INTDIV";
518 case FPE_INTOVF:
return "FPE_INTOVF";
521 case FPE_FLTDIV:
return "FPE_FLTDIV";
524 case FPE_FLTOVF:
return "FPE_FLTOVF";
527 case FPE_FLTUND:
return "FPE_FLTUND";
530 case FPE_FLTRES:
return "FPE_FLTRES";
533 case FPE_FLTINV:
return "FPE_FLTINV";
536 case FPE_FLTSUB:
return "FPE_FLTSUB";
539 case FPE_FLTUNK:
return "FPE_FLTUNK";
542 case FPE_CONDTRAP:
return "FPE_CONDTRAP";
550 case ILL_ILLOPC:
return "ILL_ILLOPC";
553 case ILL_ILLOPN:
return "ILL_ILLOPN";
556 case ILL_ILLADR:
return "ILL_ILLADR";
559 case ILL_ILLTRP:
return "ILL_ILLTRP";
562 case ILL_PRVOPC:
return "ILL_PRVOPC";
565 case ILL_PRVREG:
return "ILL_PRVREG";
568 case ILL_COPROC:
return "ILL_COPROC";
571 case ILL_BADSTK:
return "ILL_BADSTK";
574 case ILL_BADIADDR:
return "ILL_BADIADDR";
582 case SEGV_MAPERR:
return "SEGV_MAPERR";
585 case SEGV_ACCERR:
return "SEGV_ACCERR";
589 case SEGV_BNDERR:
return "SEGV_BNDERR";
593 case SEGV_PKUERR:
return "SEGV_PKUERR";
595#ifdef Q_PROCESSOR_SPARC
598 case SEGV_ACCADI:
return "SEGV_ACCADI";
601 case SEGV_ADIDERR:
return "SEGV_ADIDERR";
604 case SEGV_ADIPERR:
return "SEGV_ADIPERR";
607#ifdef Q_PROCESSOR_ARM
609 case SEGV_MTEAERR:
return "SEGV_MTEAERR";
612 case SEGV_MTESERR:
return "SEGV_MTESERR";
617 case SEGV_CPERR:
return "SEGV_CPERR";
625 case BUS_ADRALN:
return "BUS_ADRALN";
628 case BUS_ADRERR:
return "BUS_ADRERR";
631 case BUS_OBJERR:
return "BUS_OBJERR";
634 case BUS_MCEERR_AR:
return "BUS_MCEERR_AR";
637 case BUS_MCEERR_AO:
return "BUS_MCEERR_AO";
646template <
typename T>
static
650 writeToStderr(
" sent by PID ", asyncSafeToString(info->si_pid),
651 " UID ", asyncSafeToString(info->si_uid));
658 using HexString = std::array<
char,
sizeof(quintptr) * 2 + 2>;
659 auto toHexString = [](quintptr u, HexString &&r = {}) {
660 return asyncSafeToHexString(u, r.data());
663 std::string_view name = unixSignalCodeToName(info->si_signo, info->si_code);
664 writeToStderr(
", code ", name.size() ? name : asyncSafeToString(info->si_code));
666 writeToStderr(
", at instruction address ", toHexString(pc));
667 writeToStderr(
", accessing address ", toHexString(quintptr(info->si_addr)));
681 act.sa_handler = SIG_DFL;
732 struct R { size_t size, pageSize; };
733 static constexpr size_t MinStackSize = 32 * 1024;
734 size_t pageSize = sysconf(_SC_PAGESIZE);
736 if (size < MinStackSize) {
740 size = (size + pageSize - 1) & -pageSize;
743 return R{ size + pageSize, pageSize };
750# if defined(SA_ONSTACK
) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
754 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
778# if defined(SA_ONSTACK
) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
790 writeToStderr(
"Received signal ", asyncSafeToString(signum),
794 bool isCrashingSignal =
795 std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
796 if (isCrashingSignal && (!info || info->si_code <= 0))
797 isCrashingSignal =
false;
798 if (isCrashingSignal)
799 printCrashingSignalInfo(info, (pc = getProgramCounter(ucontext)));
800 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
804 if (signum != SIGINT) {
805 generateStackTrace(pc);
807 writeToStderr(
"Pausing process ", asyncSafeToString(getpid()),
814 for (size_t i = 0; i < fatalSignals.size(); ++i) {
815 struct sigaction &act = oldActions[i];
816 if (signum != fatalSignals[i])
820 if (
SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
821 (
void) sigaction(signum, &act,
nullptr);
823 if (!isCrashingSignal)
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
void maybeDisableCoreDump()
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 bool hasSystemCrashReporter()
static DebuggerProgram debugger
void generateStackTrace(quintptr ip)
static void printSentSignalInfo(...)
static ssize_t writeToStderr(Args &&... args)
static const char * signalName(int signum) noexcept
static constexpr std::array fatalSignals
static constexpr std::array crashingSignals