QTest::CrashHandler: move the Unix signal-handling code into the .cpp
This further reduces the size of the header. Change-Id: I24a16daec8aed5a38e1ffffd812629cc7e7377f7 Reviewed-by: Ahmad Samir <a.samirh78@gmail.com> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
09edc851c5
commit
0409fa9edd
@ -21,11 +21,7 @@
|
||||
#include <QtCore/private/qtools_p.h>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/uio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h> // for MAP_FAILED
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -53,71 +49,15 @@ namespace CrashHandler {
|
||||
class Q_TESTLIB_EXPORT FatalSignalHandler
|
||||
{
|
||||
public:
|
||||
# define OUR_SIGNALS(F) \
|
||||
F(HUP) \
|
||||
F(INT) \
|
||||
F(QUIT) \
|
||||
F(ABRT) \
|
||||
F(ILL) \
|
||||
F(BUS) \
|
||||
F(FPE) \
|
||||
F(SEGV) \
|
||||
F(PIPE) \
|
||||
F(TERM) \
|
||||
/**/
|
||||
# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
|
||||
# define ENUMERATE_SIGNALS(S) SIG ## S,
|
||||
static const char *signalName(int signum) noexcept
|
||||
{
|
||||
switch (signum) {
|
||||
OUR_SIGNALS(CASE_LABEL)
|
||||
}
|
||||
|
||||
# if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
|
||||
// get the other signal names from glibc 2.32
|
||||
// (accessing the sys_sigabbrev variable causes linker warnings)
|
||||
if (const char *p = sigabbrev_np(signum))
|
||||
return p;
|
||||
# endif
|
||||
return "???";
|
||||
}
|
||||
static constexpr std::array fatalSignals = {
|
||||
OUR_SIGNALS(ENUMERATE_SIGNALS)
|
||||
};
|
||||
# undef CASE_LABEL
|
||||
# undef ENUMERATE_SIGNALS
|
||||
|
||||
static constexpr std::array crashingSignals = {
|
||||
// Crash signals are special, because if we return from the handler
|
||||
// without adjusting the machine state, the same instruction that
|
||||
// originally caused the crash will get re-executed and will thus cause
|
||||
// the same crash again. This is useful if our parent process logs the
|
||||
// exit result or if core dumps are enabled: the core file will point
|
||||
// to the actual instruction that crashed.
|
||||
SIGILL, SIGBUS, SIGFPE, SIGSEGV
|
||||
};
|
||||
using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
|
||||
|
||||
FatalSignalHandler();
|
||||
~FatalSignalHandler();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY_MOVE(FatalSignalHandler)
|
||||
|
||||
static OldActionsArray &oldActions();
|
||||
auto alternateStackSize();
|
||||
int setupAlternateStack();
|
||||
void freeAlternateStack();
|
||||
|
||||
static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */);
|
||||
|
||||
[[maybe_unused]] static void regularHandler(int signum)
|
||||
{
|
||||
actionHandler(signum, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void *alternateStackBase = MAP_FAILED;
|
||||
static bool pauseOnCrash;
|
||||
};
|
||||
#else // Q_OS_WASM or weird systems
|
||||
class Q_TESTLIB_EXPORT FatalSignalHandler {};
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
# if !defined(Q_OS_INTEGRITY)
|
||||
@ -68,6 +69,51 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
# define OUR_SIGNALS(F) \
|
||||
F(HUP) \
|
||||
F(INT) \
|
||||
F(QUIT) \
|
||||
F(ABRT) \
|
||||
F(ILL) \
|
||||
F(BUS) \
|
||||
F(FPE) \
|
||||
F(SEGV) \
|
||||
F(PIPE) \
|
||||
F(TERM) \
|
||||
/**/
|
||||
# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
|
||||
# define ENUMERATE_SIGNALS(S) SIG ## S,
|
||||
static const char *signalName(int signum) noexcept
|
||||
{
|
||||
switch (signum) {
|
||||
OUR_SIGNALS(CASE_LABEL)
|
||||
}
|
||||
|
||||
# if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
|
||||
// get the other signal names from glibc 2.32
|
||||
// (accessing the sys_sigabbrev variable causes linker warnings)
|
||||
if (const char *p = sigabbrev_np(signum))
|
||||
return p;
|
||||
# endif
|
||||
return "???";
|
||||
}
|
||||
static constexpr std::array fatalSignals = {
|
||||
OUR_SIGNALS(ENUMERATE_SIGNALS)
|
||||
};
|
||||
# undef CASE_LABEL
|
||||
# undef ENUMERATE_SIGNALS
|
||||
|
||||
static constexpr std::array crashingSignals = {
|
||||
// Crash signals are special, because if we return from the handler
|
||||
// without adjusting the machine state, the same instruction that
|
||||
// originally caused the crash will get re-executed and will thus cause
|
||||
// the same crash again. This is useful if our parent process logs the
|
||||
// exit result or if core dumps are enabled: the core file will point
|
||||
// to the actual instruction that crashed.
|
||||
SIGILL, SIGBUS, SIGFPE, SIGSEGV
|
||||
};
|
||||
using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
|
||||
|
||||
template <typename... Args> static ssize_t writeToStderr(Args &&... args)
|
||||
{
|
||||
auto makeIovec = [](auto &&arg) {
|
||||
@ -146,6 +192,11 @@ struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitia
|
||||
|
||||
namespace QTest {
|
||||
namespace CrashHandler {
|
||||
Q_CONSTINIT static OldActionsArray oldActions {};
|
||||
static bool pauseOnCrash = false;
|
||||
|
||||
static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */);
|
||||
|
||||
bool alreadyDebugging()
|
||||
{
|
||||
#if defined(Q_OS_LINUX)
|
||||
@ -361,7 +412,7 @@ void blockUnixSignals()
|
||||
sigfillset(&set);
|
||||
|
||||
// we allow the crashing signals, in case we have bugs
|
||||
for (int signo : FatalSignalHandler::fatalSignals)
|
||||
for (int signo : fatalSignals)
|
||||
sigdelset(&set, signo);
|
||||
|
||||
pthread_sigmask(SIG_BLOCK, &set, nullptr);
|
||||
@ -394,7 +445,10 @@ printCrashingSignalInfo(T *info)
|
||||
}
|
||||
[[maybe_unused]] static void printCrashingSignalInfo(...) {}
|
||||
|
||||
bool FatalSignalHandler::pauseOnCrash = false;
|
||||
[[maybe_unused]] static void regularHandler(int signum)
|
||||
{
|
||||
actionHandler(signum, nullptr, nullptr);
|
||||
}
|
||||
|
||||
FatalSignalHandler::FatalSignalHandler()
|
||||
{
|
||||
@ -402,16 +456,16 @@ FatalSignalHandler::FatalSignalHandler()
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = SIG_DFL;
|
||||
oldActions().fill(act);
|
||||
oldActions.fill(act);
|
||||
|
||||
// Remove the handler after it is invoked.
|
||||
act.sa_flags = SA_RESETHAND | setupAlternateStack();
|
||||
|
||||
# ifdef SA_SIGINFO
|
||||
act.sa_flags |= SA_SIGINFO;
|
||||
act.sa_sigaction = FatalSignalHandler::actionHandler;
|
||||
act.sa_sigaction = actionHandler;
|
||||
# else
|
||||
act.sa_handler = FatalSignalHandler::regularHandler;
|
||||
act.sa_handler = regularHandler;
|
||||
# endif
|
||||
|
||||
// Block all fatal signals in our signal handler so we don't try to close
|
||||
@ -421,7 +475,7 @@ FatalSignalHandler::FatalSignalHandler()
|
||||
sigaddset(&act.sa_mask, signal);
|
||||
|
||||
for (size_t i = 0; i < fatalSignals.size(); ++i)
|
||||
sigaction(fatalSignals[i], &act, &oldActions()[i]);
|
||||
sigaction(fatalSignals[i], &act, &oldActions[i]);
|
||||
}
|
||||
|
||||
FatalSignalHandler::~FatalSignalHandler()
|
||||
@ -430,15 +484,15 @@ FatalSignalHandler::~FatalSignalHandler()
|
||||
// If ours has been replaced, leave the replacement alone.
|
||||
auto isOurs = [](const struct sigaction &old) {
|
||||
# ifdef SA_SIGINFO
|
||||
return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler;
|
||||
return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == actionHandler;
|
||||
# else
|
||||
return old.sa_handler == FatalSignalHandler::regularHandler;
|
||||
return old.sa_handler == regularHandler;
|
||||
# endif
|
||||
};
|
||||
struct sigaction action;
|
||||
|
||||
for (size_t i = 0; i < fatalSignals.size(); ++i) {
|
||||
struct sigaction &act = oldActions()[i];
|
||||
struct sigaction &act = oldActions[i];
|
||||
if (sigaction(fatalSignals[i], nullptr, &action))
|
||||
continue; // Failed to query present handler
|
||||
if (action.sa_flags == 0 && action.sa_handler == SIG_DFL)
|
||||
@ -450,13 +504,7 @@ FatalSignalHandler::~FatalSignalHandler()
|
||||
freeAlternateStack();
|
||||
}
|
||||
|
||||
FatalSignalHandler::OldActionsArray &FatalSignalHandler::oldActions()
|
||||
{
|
||||
Q_CONSTINIT static OldActionsArray oldActions {};
|
||||
return oldActions;
|
||||
}
|
||||
|
||||
auto FatalSignalHandler::alternateStackSize()
|
||||
static auto alternateStackSize() noexcept
|
||||
{
|
||||
struct R { size_t size, pageSize; };
|
||||
static constexpr size_t MinStackSize = 32 * 1024;
|
||||
@ -514,7 +562,7 @@ void FatalSignalHandler::freeAlternateStack()
|
||||
# endif
|
||||
}
|
||||
|
||||
void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *)
|
||||
void actionHandler(int signum, siginfo_t *info, void *)
|
||||
{
|
||||
writeToStderr("Received signal ", asyncSafeToString(signum),
|
||||
" (SIG", signalName(signum), ")");
|
||||
@ -540,7 +588,7 @@ void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *)
|
||||
|
||||
// chain back to the previous handler, if any
|
||||
for (size_t i = 0; i < fatalSignals.size(); ++i) {
|
||||
struct sigaction &act = oldActions()[i];
|
||||
struct sigaction &act = oldActions[i];
|
||||
if (signum != fatalSignals[i])
|
||||
continue;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user