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 <edward.welbourne@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This commit is contained in:
Thiago Macieira 2025-05-19 10:20:32 -07:00
parent 9a66c7c1b9
commit 09edc851c5
3 changed files with 62 additions and 81 deletions

View File

@ -28,49 +28,9 @@
#include <unistd.h>
#endif
#ifdef Q_OS_WIN
#include <iostream>
#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 <typename... Args> static ssize_t writeToStderr(Args &&... args)
{
struct iovec vec[] = { IoVec(std::forward<Args>(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<int>::digits10 + 3;
std::array<char, Digits10> 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 <typename... Args> 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 <typename T> static
std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().si_uid) >= 1>
printSentSignalInfo(T *info)
{
writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid),
" UID ", asyncSafeToString(info->si_uid));
}
static void printSentSignalInfo(...) {}
template <typename T> static
std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1> printCrashingSignalInfo(T *info)
{
using HexString = std::array<char, sizeof(quintptr) * 2>;
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)

View File

@ -68,21 +68,36 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
namespace QTest {
namespace CrashHandler {
struct iovec IoVec(struct iovec vec)
template <typename... Args> static ssize_t writeToStderr(Args &&... args)
{
return vec;
}
struct iovec IoVec(const char *str)
{
struct iovec r = {};
r.iov_base = const_cast<char *>(str);
r.iov_len = strlen(str);
return r;
auto makeIovec = [](auto &&arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, iovec>) {
return arg;
} else {
struct iovec r = {};
r.iov_base = const_cast<char *>(arg);
r.iov_len = strlen(arg);
return r;
}
};
struct iovec vec[] = { makeIovec(std::forward<Args>(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<int>::digits10 + 3;
std::array<char, Digits10> 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 <typename T> static
std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().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 <typename T> static std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1>
printCrashingSignalInfo(T *info)
{
using HexString = std::array<char, sizeof(quintptr) * 2>;
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()

View File

@ -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()