summaryrefslogtreecommitdiffstats
path: root/src/testlib/qtestcase.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2022-05-02 19:28:04 -0700
committerThiago Macieira <thiago.macieira@intel.com>2022-05-20 12:01:38 -0700
commit3d9e56aa6cc20a5a0822877e86a43e9a0affb503 (patch)
tree09cddf8faa33c393023434bc3ce00275b4ac476c /src/testlib/qtestcase.cpp
parent29dd43d5ab18ec01df0d173e974d261afd16a311 (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.cpp31
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();
}