BmnRoot 26.10.0-18-g4e3414f9
Software framework of the BM@N experiment
Loading...
Searching...
No Matches
BmnCoreDumpGuard.cxx
Go to the documentation of this file.
1#include "BmnCoreDumpGuard.h"
2
3#include <csignal>
4#include <cstdlib>
5#include <cstring>
6
7#ifdef __linux__
8#include <pthread.h>
9#include <sys/prctl.h>
10#endif
11
12// signals whose default action is to terminate the process and to write a core dump.
13// In practice BmnRoot dies from SIGABRT: abort() is called by glibc when the heap is corrupted
14// ("double free or corruption"), by a failed assert() and by std::terminate() for an uncaught exception.
15// SIGSEGV and the others are intercepted by ROOT itself (see 'Root.ErrorHandlers' in system.rootrc), which prints the
16// stack trace and exits without dumping the core
17static const int gFatalSignals[] = {SIGABRT, SIGSEGV, SIGBUS, SIGFPE, SIGILL};
18static const int gFatalSignalCount = sizeof(gFatalSignals) / sizeof(gFatalSignals[0]);
19
20// actions replaced by Install(), they are called by the guard and restored by Uninstall()
21static struct sigaction gPreviousActions[gFatalSignalCount];
23// whether the guard is already processing a fatal signal (the previous handler may raise the signal once again)
24static volatile sig_atomic_t gIsHandlingSignal = 0;
25
26// get index of the signal in the gFatalSignals array; return -1 if not found
27static int GetFatalSignalIndex(int signalNumber)
28{
29 for (int i = 0; i < gFatalSignalCount; i++)
30 if (gFatalSignals[i] == signalNumber)
31 return i;
32
33 return -1;
34}
35
36static void BmnFatalSignalHandler(int signalNumber, siginfo_t* signalInfo, void* signalContext)
37{
38#ifdef __linux__
39 // the kernel does not dump a process marked as non-dumpable. In contrast to 'ulimit -c 0' this works also when
40 // /proc/sys/kernel/core_pattern pipes the dump to a helper program (apport on Ubuntu, abrt-hook-ccpp on CentOS 7
41 // and AlmaLinux 9): for a pipe pattern the kernel ignores the RLIMIT_CORE limit and calls the helper anyway
42 prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
43#endif
44
45 // let the replaced handler do its job (it can print the stack trace and terminate the process by itself),
46 // but only once, otherwise a handler raising the same signal again would call the guard recursively
47 int signalIndex = GetFatalSignalIndex(signalNumber);
48 if ((signalIndex >= 0) && (gIsHandlingSignal == 0)) {
50
51 const struct sigaction& previousAction = gPreviousActions[signalIndex];
52 if (((previousAction.sa_flags & SA_SIGINFO) != 0) && (previousAction.sa_sigaction != nullptr))
53 previousAction.sa_sigaction(signalNumber, signalInfo, signalContext);
54 else if ((previousAction.sa_handler != SIG_DFL) && (previousAction.sa_handler != SIG_IGN)
55 && (previousAction.sa_handler != nullptr))
56 previousAction.sa_handler(signalNumber);
57 }
58
59 // the previous handler has returned or there was none: die from the original signal, so that the shell and the
60 // batch system still get the correct termination status, but without the core dump disabled above
61 struct sigaction defaultAction;
62 memset(&defaultAction, 0, sizeof(defaultAction));
63 defaultAction.sa_handler = SIG_DFL;
64 sigemptyset(&defaultAction.sa_mask);
65 sigaction(signalNumber, &defaultAction, nullptr);
66
67 // the signal is blocked while its handler is running, unblock it to be killed by the raise() below
68 sigset_t signalMask;
69 sigemptyset(&signalMask);
70 sigaddset(&signalMask, signalNumber);
71 pthread_sigmask(SIG_UNBLOCK, &signalMask, nullptr);
72
73 raise(signalNumber);
74}
75
77
79
81{
82 if (fgIsInstalled)
83 return true;
84
85 // ENABLE_CORE_DUMP=1 keeps the core dumps enabled, e.g. to investigate a crash with gdb
86 const char* isCoreDumpRequested = getenv("ENABLE_CORE_DUMP");
87 if ((isCoreDumpRequested != nullptr) && (isCoreDumpRequested[0] != '\0') && (strcmp(isCoreDumpRequested, "0") != 0))
88 return false;
89
90 struct sigaction guardAction;
91 memset(&guardAction, 0, sizeof(guardAction));
92 guardAction.sa_sigaction = BmnFatalSignalHandler;
93 sigemptyset(&guardAction.sa_mask);
94 guardAction.sa_flags = SA_SIGINFO;
95
96 for (int i = 0; i < gFatalSignalCount; i++) {
97 struct sigaction currentAction;
98 if (sigaction(gFatalSignals[i], nullptr, &currentAction) != 0)
99 continue;
100
101 // SIGABRT is guarded in any case: it is the signal the core dumps come from, and no handler prints a stack
102 // trace for it. The other signals are guarded only if nobody processes them yet, in order not to replace the
103 // ROOT handler printing the stack trace
104 bool isSignalProcessed = ((currentAction.sa_flags & SA_SIGINFO) != 0) || (currentAction.sa_handler != SIG_DFL);
105 if ((gFatalSignals[i] != SIGABRT) && isSignalProcessed)
106 continue;
107
108 gPreviousActions[i] = currentAction;
109 if (sigaction(gFatalSignals[i], &guardAction, nullptr) == 0) {
110 gIsSignalGuarded[i] = true;
111 fgIsInstalled = true;
112 }
113 }
114
115 return fgIsInstalled;
116}
117
119{
120 if (!fgIsInstalled)
121 return false;
122
123 for (int i = 0; i < gFatalSignalCount; i++) {
124 if (!gIsSignalGuarded[i])
125 continue;
126
127 // restore the action which was replaced by Install()
128 if (sigaction(gFatalSignals[i], &gPreviousActions[i], nullptr) == 0)
129 gIsSignalGuarded[i] = false;
130 }
131
132 fgIsInstalled = false;
133 return true;
134}
135
136namespace
137{
138// install the guard as soon as the library is loaded to cover every macro and program using BmnRoot
139struct BmnCoreDumpGuardInstaller
140{
141 BmnCoreDumpGuardInstaller() { BmnCoreDumpGuard::Install(); }
142} gBmnCoreDumpGuardInstaller;
143} // namespace
static void BmnFatalSignalHandler(int signalNumber, siginfo_t *signalInfo, void *signalContext)
static bool gIsSignalGuarded[gFatalSignalCount]
static const int gFatalSignalCount
static volatile sig_atomic_t gIsHandlingSignal
static int GetFatalSignalIndex(int signalNumber)
static struct sigaction gPreviousActions[gFatalSignalCount]
static const int gFatalSignals[]
void memset(T *dest, T i, size_t num)
Copy to memory block [@dest, @dest+@num] num number of times the value of i of type @T with size @typ...
Definition L1Grid.h:27
int i
Definition P4_F32vec4.h:22
#define ClassImp(name)
Class preventing 'core.
static bool fgIsInstalled
static bool Uninstall()
static bool Install()