diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2022-05-02 19:28:04 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2022-05-20 12:01:38 -0700 |
commit | 3d9e56aa6cc20a5a0822877e86a43e9a0affb503 (patch) | |
tree | 09cddf8faa33c393023434bc3ce00275b4ac476c /src/testlib/qtestcase.cpp | |
parent | 29dd43d5ab18ec01df0d173e974d261afd16a311 (diff) |
FatalSignalHandler: chain back to the original crash handler
If a previous handler was already installed, ensure it is called,
because there may be a reason why it was there. For example, the Android
ART adds a signal action to every fatal signal for logging purposes. We
do that by restoring the signal handler we had and re-raising the
signal.
If our handler was overridden by something else, then that handler was
already called, but will get uninstalled after our code runs. It won't
be a problem, because the application is exiting anyway.
[ChangeLog][QtTest][Behavior Change] On Unix, the QtTest code to
handle Unix/POSIX fatal signals will now call back to the original
handler that was installed, if there was one. This allows logging
frameworks (such as Android ART's), for example, to log the crash too.
Additionally, if there was no handler, the application should exit with
the correct signal instead of SIGABRT.
Fixes: QTBUG-97652
Pick-to: 6.3
Change-Id: Ifc4fca159b490d8d0b614d736e46caefcb903a4c
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/testlib/qtestcase.cpp')
-rw-r--r-- | src/testlib/qtestcase.cpp | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 7dbe13bb6d..b06c88b6cb 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1839,6 +1839,15 @@ public: static constexpr std::array fatalSignals = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM }; + 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() @@ -1938,6 +1947,28 @@ private: writeToStderr("Received signal ", asyncSafeToString(signum), "\n Function time: ", asyncSafeToString(msecsFunctionTime), "ms Total time: ", asyncSafeToString(msecsTotalTime), "ms\n"); + + bool isCrashingSignal = + std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end(); + + // chain back to the previous handler, if any + for (size_t i = 0; i < fatalSignals.size(); ++i) { + struct sigaction &act = oldActions()[i]; + if (signum != fatalSignals[i]) + continue; + + // restore the handler (if SA_RESETHAND hasn't done the job for us) + if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags) + (void) sigaction(signum, &act, nullptr); + + if (!isCrashingSignal) + raise(signum); + + // signal is blocked, so it'll be delivered when we return + return; + } + + // we shouldn't reach here! std::abort(); } |