From 09edc851c52d97bcea6a4d0b4e94c6760439d84b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 19 May 2025 10:20:32 -0700 Subject: [PATCH] QTest::CrashHandler: move the Unix async-safe I/O code to the .cpp This declutters the header from code that doesn't need to be there and is only used in the .cpp anyway. This code used to be in qtestcase.cpp before commit c0014becca2cd376eadd5c8a0265e5cf47c9aa01, which we now amend. Additionally, since Windows code doesn't need to be async-safe anyway, we can simplify it. I'm also going to need the toHexString() lambda in another place. Change-Id: Ic9571bac864dfb31564cfffd785e8ab15cab3ae5 Reviewed-by: Edward Welbourne Reviewed-by: Ahmad Samir --- src/testlib/qtestcrashhandler_p.h | 66 ------------------------ src/testlib/qtestcrashhandler_unix.cpp | 69 +++++++++++++++++++++----- src/testlib/qtestcrashhandler_win.cpp | 8 +-- 3 files changed, 62 insertions(+), 81 deletions(-) diff --git a/src/testlib/qtestcrashhandler_p.h b/src/testlib/qtestcrashhandler_p.h index d11ea964b58..16209eaae36 100644 --- a/src/testlib/qtestcrashhandler_p.h +++ b/src/testlib/qtestcrashhandler_p.h @@ -28,49 +28,9 @@ #include #endif -#ifdef Q_OS_WIN -#include -#endif - QT_BEGIN_NAMESPACE namespace QTest { namespace CrashHandler { -#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread)) - struct iovec IoVec(struct iovec vec); - struct iovec IoVec(const char *str); - - template static ssize_t writeToStderr(Args &&... args) - { - struct iovec vec[] = { IoVec(std::forward(args))... }; - return ::writev(STDERR_FILENO, vec, std::size(vec)); - } - - // async-signal-safe conversion from int to string - struct AsyncSafeIntBuffer - { - // digits10 + 1 for all possible digits - // +1 for the sign - // +1 for the terminating null - static constexpr int Digits10 = std::numeric_limits::digits10 + 3; - std::array array; - constexpr AsyncSafeIntBuffer() : array{} {} // initializes array - AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized - }; - - struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized); -#elif defined(Q_OS_WIN) - // Windows doesn't need to be async-safe - template static void writeToStderr(Args &&... args) - { - (std::cerr << ... << args); - } - - inline std::string asyncSafeToString(int n) - { - return std::to_string(n); - } -#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread)) - bool alreadyDebugging(); void blockUnixSignals(); @@ -149,32 +109,6 @@ namespace CrashHandler { int setupAlternateStack(); void freeAlternateStack(); - template static - std::enable_if_t().si_pid) + sizeof(std::declval().si_uid) >= 1> - printSentSignalInfo(T *info) - { - writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid), - " UID ", asyncSafeToString(info->si_uid)); - } - static void printSentSignalInfo(...) {} - - template static - std::enable_if_t().si_addr) >= 1> printCrashingSignalInfo(T *info) - { - using HexString = std::array; - auto toHexString = [](quintptr u, HexString &&r = {}) { - int shift = sizeof(quintptr) * 8 - 4; - for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4) - r[i] = QtMiscUtils::toHexLower(u >> shift); - struct iovec vec; - vec.iov_base = r.data(); - vec.iov_len = r.size(); - return vec; - }; - writeToStderr(", code ", asyncSafeToString(info->si_code), - ", for address 0x", toHexString(quintptr(info->si_addr))); - } - static void printCrashingSignalInfo(...) {} static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */); [[maybe_unused]] static void regularHandler(int signum) diff --git a/src/testlib/qtestcrashhandler_unix.cpp b/src/testlib/qtestcrashhandler_unix.cpp index f0ffe36dd66..86a77312ba4 100644 --- a/src/testlib/qtestcrashhandler_unix.cpp +++ b/src/testlib/qtestcrashhandler_unix.cpp @@ -68,21 +68,36 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -namespace QTest { -namespace CrashHandler { -struct iovec IoVec(struct iovec vec) +template static ssize_t writeToStderr(Args &&... args) { - return vec; -} -struct iovec IoVec(const char *str) -{ - struct iovec r = {}; - r.iov_base = const_cast(str); - r.iov_len = strlen(str); - return r; + auto makeIovec = [](auto &&arg) { + if constexpr (std::is_same_v, iovec>) { + return arg; + } else { + struct iovec r = {}; + r.iov_base = const_cast(arg); + r.iov_len = strlen(arg); + return r; + } + }; + struct iovec vec[] = { makeIovec(std::forward(args))... }; + return ::writev(STDERR_FILENO, vec, std::size(vec)); } -struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result) +namespace { +// async-signal-safe conversion from int to string +struct AsyncSafeIntBuffer +{ + // digits10 + 1 for all possible digits + // +1 for the sign + // +1 for the terminating null + static constexpr int Digits10 = std::numeric_limits::digits10 + 3; + std::array array; + constexpr AsyncSafeIntBuffer() : array{} {} // initializes array + AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized +}; + +struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized) { char *ptr = result.array.data(); if (false) { @@ -127,7 +142,10 @@ struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result) r.iov_len = ptr - result.array.data(); return r; }; +} // unnamed namespace +namespace QTest { +namespace CrashHandler { bool alreadyDebugging() { #if defined(Q_OS_LINUX) @@ -349,6 +367,33 @@ void blockUnixSignals() pthread_sigmask(SIG_BLOCK, &set, nullptr); } +template static + std::enable_if_t().si_pid) + sizeof(std::declval().si_uid) >= 1> +printSentSignalInfo(T *info) +{ + writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid), + " UID ", asyncSafeToString(info->si_uid)); +} +[[maybe_unused]] static void printSentSignalInfo(...) {} + +template static std::enable_if_t().si_addr) >= 1> +printCrashingSignalInfo(T *info) +{ + using HexString = std::array; + auto toHexString = [](quintptr u, HexString &&r = {}) { + int shift = sizeof(quintptr) * 8 - 4; + for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4) + r[i] = QtMiscUtils::toHexLower(u >> shift); + struct iovec vec; + vec.iov_base = r.data(); + vec.iov_len = r.size(); + return vec; + }; + writeToStderr(", code ", asyncSafeToString(info->si_code), + ", for address 0x", toHexString(quintptr(info->si_addr))); +} +[[maybe_unused]] static void printCrashingSignalInfo(...) {} + bool FatalSignalHandler::pauseOnCrash = false; FatalSignalHandler::FatalSignalHandler() diff --git a/src/testlib/qtestcrashhandler_win.cpp b/src/testlib/qtestcrashhandler_win.cpp index f814a6bc68e..6a9f610eabd 100644 --- a/src/testlib/qtestcrashhandler_win.cpp +++ b/src/testlib/qtestcrashhandler_win.cpp @@ -51,9 +51,11 @@ void printTestRunTime() const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); const char *const name = QTest::currentTestFunction(); - writeToStderr("\n ", name ? name : "[Non-test]", - " function time: ", asyncSafeToString(msecsFunctionTime), - "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n"); + + // Windows doesn't have the concept of async-safety, so fprintf() is + // probably as good as WriteFile() and WriteConsole(). + fprintf(stderr, "\n %s function time: %dms, total time: %dms\n", + name ? name : "[Non-test]", msecsFunctionTime, msecsTotalTime); } void generateStackTrace()