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
35# if !defined(Q_OS_INTEGRITY)
36# include <sys/resource.h>
39# include <sys/ucontext.h>
40# elif __has_include(<ucontext.h>)
43using ucontext_t =
void;
46#if defined(Q_OS_MACOS)
47#include <QtCore/private/qcore_mac_p.h>
48#include <QtTest/private/qtestutil_macos_p.h>
50#include <IOKit/pwr_mgt/IOPMLib.h>
52#include <mach/mach_init.h>
53#include <CoreFoundation/CFPreferences.h>
55#define CSR_ALLOW_UNRESTRICTED_FS (1
<< 1
)
58#if defined(Q_OS_LINUX)
65#include <emscripten.h>
69# define _PATH_DEFPATH "/usr/bin:/bin"
75# define SA_RESETHAND 0
80using namespace Qt::StringLiterals;
82# define OUR_SIGNALS(F)
94# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
95# define ENUMERATE_SIGNALS(S) SIG ## S,
102# if defined(__GLIBC_MINOR__
) && (__GLIBC_MINOR__
>= 32
|| __GLIBC__
> 2
)
105 if (
const char *p = sigabbrev_np(signum))
114# undef ENUMERATE_SIGNALS
123 SIGILL, SIGBUS, SIGFPE, SIGSEGV
125using OldActionsArray = std::array<
struct sigaction, fatalSignals.size()>;
129 auto makeIovec = [](std::string_view arg) {
131 r.iov_base =
const_cast<
char *>(arg.data());
132 r.iov_len = arg.size();
135 struct iovec vec[] = { makeIovec(
std::forward<Args>(args))... };
136 return ::writev(STDERR_FILENO, vec,
std::size(vec));
141struct AsyncSafeIntBuffer
146 static constexpr int Digits10 = std::numeric_limits<
int>::digits10 + 3;
147 std::array<
char, Digits10> array;
148 constexpr AsyncSafeIntBuffer() : array{} {}
149 AsyncSafeIntBuffer(Qt::Initialization) {}
152std::string_view asyncSafeToString(
int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized)
154 char *ptr = result.array.data();
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{}) {
168 static constexpr int StartingDivider = ([]() {
170 for (
int i = 0; i < std::numeric_limits<
int>::digits10; ++i)
174 int divider = StartingDivider;
175 while (divider && n < divider)
179 while (divider > 1) {
180 int quot = n / divider;
192 return std::string_view(result.array.data(), ptr - result.array.data());
195std::string_view asyncSafeToHexString(quintptr u,
char *ptr)
200 int shift =
sizeof(quintptr) * 8 - 4;
203 for (size_t i = 0; i <
sizeof(quintptr) * 2; ++i, shift -= 4)
204 ptr[i + 2] = QtMiscUtils::toHexLower(u >> shift);
206 return std::string_view(ptr,
sizeof(quintptr) * 2 + 2);
215static void actionHandler(
int signum, siginfo_t *info,
void * );
219#if defined(Q_OS_LINUX)
220 int fd = open(
"/proc/self/status", O_RDONLY);
224 ssize_t size = read(fd, buffer,
sizeof(buffer) - 1);
230 const char tracerPidToken[] =
"\nTracerPid:";
231 char *tracerPid = strstr(buffer, tracerPidToken);
236 tracerPid +=
sizeof(tracerPidToken);
237 long int pid = strtol(tracerPid, &tracerPid, 10);
240#elif defined(Q_OS_MACOS)
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])) {
266#if defined(Q_OS_MACOS)
267 return QTestPrivate::macCrashReporterWillShowDialog();
277 const int disableCoreDump = qEnvironmentVariableIntValue(
"QTEST_DISABLE_CORE_DUMP", &ok);
278 if (ok && disableCoreDump) {
282 if (setrlimit(RLIMIT_CORE, &limit) != 0)
283 qWarning(
"Failed to disable core dumps: %d", errno);
293 const int disableStackDump = qEnvironmentVariableIntValue(
"QTEST_DISABLE_STACK_DUMP", &ok);
294 if (ok && disableStackDump)
300#if defined(Q_OS_MACOS)
303# if defined(Q_PROCESSOR_ARM)
306 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
307 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
313 auto hasExecutable = [](
const char *execname) {
314 std::string candidate;
316 if (
const char *p = getenv(
"PATH"); p && *p)
320 for (
const char *p = std::strtok(&path[0],
":'"); p; p = std::strtok(
nullptr,
":")) {
323 candidate += execname;
324 if (QT_ACCESS(candidate.data(), X_OK) == 0)
330 static constexpr DebuggerProgram debuggerSearchOrder[] = {
331# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
337 for (DebuggerProgram candidate : debuggerSearchOrder) {
343 if (hasExecutable(
"gdb")) {
349 if (hasExecutable(
"lldb")) {
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");
371 if ([[maybe_unused]]
auto ctx =
static_cast<ucontext_t *>(ucontext)) {
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;
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;
384#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_32)
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)
390#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_LOONGARCH)
392#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_32)
394#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_64)
396#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_RISCV)
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];
409 if (debugger == None || alreadyDebugging())
412# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER)
414 (
void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
417# if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
418 writeToStderr(
"\n=== Stack trace ===\n");
421 AsyncSafeIntBuffer pidbuffer;
422 asyncSafeToString(getpid(),
std::move(pidbuffer));
430 (
void) dup2(STDERR_FILENO, STDOUT_FILENO);
434 char disasmInstr[
sizeof(
"x/i 0x") +
sizeof(ip) * 2] = {};
436 strcpy(disasmInstr,
"x/i ");
437 asyncSafeToHexString(ip, disasmInstr + strlen(disasmInstr));
441 std::array<
const char *, 16> argv;
443 Args &operator<<(
const char *arg)
445 Q_ASSERT(count <
int(argv.size()));
449 operator
char **()
const {
return const_cast<
char **>(argv.data()); }
457 argv <<
"gdb" <<
"--nx" <<
"--batch";
459 argv <<
"-ex" << disasmInstr;
460 argv <<
"-ex" <<
"thread apply all bt"
461 <<
"-ex" <<
"printf \"\\n\""
462 <<
"-ex" <<
"info proc mappings"
466 argv <<
"lldb" <<
"--no-lldbinit" <<
"--batch";
468 argv <<
"-o" << disasmInstr;
469 argv <<
"-o" <<
"bt all"
474 argv << pidbuffer.array.data() <<
nullptr;
475 execvp(argv.argv[0], argv);
478 }
else if (pid < 0) {
479 writeToStderr(
"Failed to start debugger.\n");
482 QT_EINTR_LOOP(ret, waitpid(pid,
nullptr, 0));
485 writeToStderr(
"=== End of stack trace ===\n");
500 for (
int signo : fatalSignals)
501 sigdelset(&set, signo);
503 pthread_sigmask(SIG_BLOCK, &set,
nullptr);
512 case FPE_INTDIV:
return "FPE_INTDIV";
515 case FPE_INTOVF:
return "FPE_INTOVF";
518 case FPE_FLTDIV:
return "FPE_FLTDIV";
521 case FPE_FLTOVF:
return "FPE_FLTOVF";
524 case FPE_FLTUND:
return "FPE_FLTUND";
527 case FPE_FLTRES:
return "FPE_FLTRES";
530 case FPE_FLTINV:
return "FPE_FLTINV";
533 case FPE_FLTSUB:
return "FPE_FLTSUB";
536 case FPE_FLTUNK:
return "FPE_FLTUNK";
539 case FPE_CONDTRAP:
return "FPE_CONDTRAP";
547 case ILL_ILLOPC:
return "ILL_ILLOPC";
550 case ILL_ILLOPN:
return "ILL_ILLOPN";
553 case ILL_ILLADR:
return "ILL_ILLADR";
556 case ILL_ILLTRP:
return "ILL_ILLTRP";
559 case ILL_PRVOPC:
return "ILL_PRVOPC";
562 case ILL_PRVREG:
return "ILL_PRVREG";
565 case ILL_COPROC:
return "ILL_COPROC";
568 case ILL_BADSTK:
return "ILL_BADSTK";
571 case ILL_BADIADDR:
return "ILL_BADIADDR";
579 case SEGV_MAPERR:
return "SEGV_MAPERR";
582 case SEGV_ACCERR:
return "SEGV_ACCERR";
586 case SEGV_BNDERR:
return "SEGV_BNDERR";
590 case SEGV_PKUERR:
return "SEGV_PKUERR";
592#ifdef Q_PROCESSOR_SPARC
595 case SEGV_ACCADI:
return "SEGV_ACCADI";
598 case SEGV_ADIDERR:
return "SEGV_ADIDERR";
601 case SEGV_ADIPERR:
return "SEGV_ADIPERR";
604#ifdef Q_PROCESSOR_ARM
606 case SEGV_MTEAERR:
return "SEGV_MTEAERR";
609 case SEGV_MTESERR:
return "SEGV_MTESERR";
614 case SEGV_CPERR:
return "SEGV_CPERR";
622 case BUS_ADRALN:
return "BUS_ADRALN";
625 case BUS_ADRERR:
return "BUS_ADRERR";
628 case BUS_OBJERR:
return "BUS_OBJERR";
631 case BUS_MCEERR_AR:
return "BUS_MCEERR_AR";
634 case BUS_MCEERR_AO:
return "BUS_MCEERR_AO";
643template <
typename T>
static
647 writeToStderr(
" sent by PID ", asyncSafeToString(info->si_pid),
648 " UID ", asyncSafeToString(info->si_uid));
655 using HexString = std::array<
char,
sizeof(quintptr) * 2 + 2>;
656 auto toHexString = [](quintptr u, HexString &&r = {}) {
657 return asyncSafeToHexString(u, r.data());
660 std::string_view name = unixSignalCodeToName(info->si_signo, info->si_code);
661 writeToStderr(
", code ", name.size() ? name : asyncSafeToString(info->si_code));
663 writeToStderr(
", at instruction address ", toHexString(pc));
664 writeToStderr(
", accessing address ", toHexString(quintptr(info->si_addr)));
678 act.sa_handler = SIG_DFL;
729 struct R { size_t size, pageSize; };
730 static constexpr size_t MinStackSize = 32 * 1024;
731 size_t pageSize = sysconf(_SC_PAGESIZE);
733 if (size < MinStackSize) {
737 size = (size + pageSize - 1) & -pageSize;
740 return R{ size + pageSize, pageSize };
747# if defined(SA_ONSTACK
) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
751 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
775# if defined(SA_ONSTACK
) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
787 writeToStderr(
"Received signal ", asyncSafeToString(signum),
788 " (SIG", signalName(signum),
")");
791 bool isCrashingSignal =
792 std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
793 if (isCrashingSignal && (!info || info->si_code <= 0))
794 isCrashingSignal =
false;
795 if (isCrashingSignal)
796 printCrashingSignalInfo(info, (pc = getProgramCounter(ucontext)));
797 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
801 if (signum != SIGINT) {
802 generateStackTrace(pc);
804 writeToStderr(
"Pausing process ", asyncSafeToString(getpid()),
811 for (size_t i = 0; i < fatalSignals.size(); ++i) {
812 struct sigaction &act = oldActions[i];
813 if (signum != fatalSignals[i])
817 if (
SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
818 (
void) sigaction(signum, &act,
nullptr);
820 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