DD4hep  1.36.0
Detector Description Toolkit for High Energy Physics
SignalHandler.cpp
Go to the documentation of this file.
1 //==========================================================================
2 // AIDA Detector description implementation
3 //--------------------------------------------------------------------------
4 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
5 // All rights reserved.
6 //
7 // For the licensing terms see $DD4hepINSTALL/LICENSE.
8 // For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
9 //
10 // Author : M.Frank
11 //
12 //==========================================================================
13 
14 // Framework include files
15 #include <DD4hep/Printout.h>
16 #include <DD4hep/SignalHandler.h>
17 
18 #include <csignal>
19 #include <map>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 #include <unistd.h>
24 #include <execinfo.h>
25 
26 using namespace dd4hep;
27 
29 
30 namespace {
31  static bool s_exit_handler_print = true;
32  static bool s_exit_handler_active = false;
33  static bool s_exit_handler_backtrace = false;
34  static bool s_exit_handler_sleep_on_fatal = false;
35 
36  template<class T> union func_cast {
37  void* ptr;
38  T fun;
39  explicit func_cast(T t) { fun = t; }
40  explicit func_cast(void* t) { ptr = t; }
41  };
42 }
43 
51 protected:
52  struct sig_entry_t {
53  void* user_context { nullptr };
55  };
56  struct sig_handler_t {
57  std::string name { };
58  struct sigaction old_action { };
59  struct sigaction handler_action { };
60  std::vector<sig_entry_t> user_handlers { };
61  };
62 
63 public:
64  typedef std::map<int, sig_handler_t> SigMap;
66 
67 public:
71  ~implementation();
73  static implementation& instance();
75  void init();
77  void install(int num, const std::string& name, struct sigaction& action);
79  int subscribe(int signum, void* user_context, signal_handler_t handler);
81  int unsubscribe(int signum, void* user_context);
83  void back_trace(int /* signum */);
85  static void handler(int signum, siginfo_t *info,void * );
86 };
87 
90 }
91 
94 }
95 
98  static std::unique_ptr<implementation> imp;
99  if ( !imp ) {
100  imp = std::make_unique<implementation>();
101  }
102  return *imp;
103 }
104 
107  struct sigaction new_action;
108  sigemptyset(&new_action.sa_mask);
109  new_action.sa_handler = 0;
110  new_action.sa_sigaction = handler;
111  new_action.sa_flags = SA_SIGINFO;
112 
113  install(SIGILL, "SIGILL", new_action);
114  install(SIGINT, "SIGINT", new_action);
115  install(SIGTERM, "SIGTERM", new_action);
116  install(SIGHUP, "SIGHUP", new_action);
117 
118  install(SIGQUIT, "SIGQUIT", new_action);
119  install(SIGBUS, "SIGBUS", new_action);
120  install(SIGXCPU, "SIGXCPU", new_action);
121  sigaddset(&new_action.sa_mask,SIGSEGV);
122  sigaddset(&new_action.sa_mask,SIGABRT);
123  sigaddset(&new_action.sa_mask,SIGFPE);
124  install(SIGABRT, "SIGABRT", new_action);
125  install(SIGFPE, "SIGFPE", new_action);
126  install(SIGSEGV, "SIGSEGV", new_action);
127 }
128 
130 int SignalHandler::implementation::subscribe(int signum, void* user_context, signal_handler_t user_handler) {
131  if ( m_map.empty() ) {
132  this->init();
133  }
134  auto ihandler = m_map.find(signum);
135  if ( ihandler == m_map.end() ) {
136  char text[32];
137  struct sigaction new_action;
138  sigemptyset(&new_action.sa_mask);
139  new_action.sa_handler = 0;
140  new_action.sa_sigaction = SignalHandler::implementation::handler;
141  new_action.sa_flags = SA_SIGINFO;
142  ::snprintf(text, sizeof(text),"%08X",signum);
143  install(signum, text, new_action);
144  ihandler = m_map.find(signum);
145  }
146  if ( ihandler != m_map.end() ) { // Should always be true
147  sig_entry_t entry {user_context, user_handler};
148  ihandler->second.user_handlers.emplace_back(entry);
149  return 1;
150  }
151  return 0;
152 }
153 
155 int SignalHandler::implementation::unsubscribe(int signum, void* user_context) {
156  auto ihandler = m_map.find(signum);
157  if ( ihandler != m_map.end() ) {
158  auto & handlers = ihandler->second.user_handlers;
159  for( auto it = handlers.begin(); it != handlers.end(); ++it ) {
160  if ( it->user_context == user_context ) {
161  handlers.erase(it);
162  return 1;
163  }
164  }
165  }
166  return 0;
167 }
168 
171  if ( s_exit_handler_backtrace ) {
172  void *bt[256];
173  char text[512];
174  int bt_size = ::backtrace(bt, sizeof(bt) / sizeof(void *));
175  size_t len = ::snprintf(text, sizeof(text), "\n[INFO] (ExitSignalHandler) %s\n",
176  "---------------------- Backtrace ----------------------\n");
177  text[sizeof(text)-2] = '\n';
178  text[sizeof(text)-1] = 0;
179  ::write(STDERR_FILENO, text, len);
180  len = ::snprintf(text, sizeof(text), "[INFO] Number of elements in backtrace: %d\n", bt_size);
181  text[sizeof(text)-2] = '\n';
182  text[sizeof(text)-1] = 0;
183  ::write(STDERR_FILENO, text, len);
184  ::backtrace_symbols_fd(bt, bt_size, STDERR_FILENO);
185  for (int i = 0; i < bt_size; i++) {
186  len = ::snprintf(text,sizeof(text),"[INFO] (SignalHandler) %02d --> %p\n", i, bt[i]);
187  text[sizeof(text)-2] = '\n';
188  text[sizeof(text)-1] = 0;
189  ::write(STDERR_FILENO, text, len);
190  }
191  }
192 }
193 
195 void SignalHandler::implementation::install(int num, const std::string& name, struct sigaction& action) {
196  auto& action_entry = m_map[num];
197  int res = ::sigaction (num, &action, &action_entry.old_action);
198  if ( res != 0 ) {
199  char text[512];
200  auto len = ::snprintf(text,sizeof(text),"Failed to install exit handler for %s", name.c_str());
201  text[sizeof(text)-2] = '\n';
202  text[sizeof(text)-1] = 0;
203  ::write(STDERR_FILENO, text, len);
204  return;
205  }
206  action_entry.handler_action = action;
207  action_entry.name = name;
208 }
209 
211 void SignalHandler::implementation::handler(int signum, siginfo_t *info, void *ptr) {
212  SigMap& m = instance().m_map;
213  SigMap::iterator iter_handler = m.find(signum);
214  s_exit_handler_active = true;
215  if ( iter_handler != m.end() ) {
216  auto hdlr = iter_handler->second.old_action.sa_handler;
217  func_cast<void (*)(int)> dsc0(hdlr);
218  func_cast<void (*)(int,siginfo_t*, void*)> dsc(dsc0.ptr);
219 
220  if ( s_exit_handler_print ) {{
221  char text[512];
222  size_t len = ::snprintf(text,sizeof(text),
223  "[FATAL] (SignalHandler) Handle signal: %d [%s] Old action:%p Mem:%p Code:%08X\n",
224  signum,iter_handler->second.name.c_str(),dsc.ptr,info->si_addr,info->si_code);
225  text[sizeof(text)-2] = '\n';
226  text[sizeof(text)-1] = 0;
227  ::write(STDERR_FILENO,text,len);
228  // Debugging hack, if enabled (default: NO)
229  if ( s_exit_handler_sleep_on_fatal ) {
230  bool _s_sleep = true;
231  len = ::snprintf(text,sizeof(text),
232  "[FATAL] (SignalHandler) Sleeping for debugging.... %s\n",
233  _s_sleep ? "YES" : "NO");
234  text[sizeof(text)-2] = '\n';
235  text[sizeof(text)-1] = 0;
236  ::write(STDERR_FILENO,text,len);
237  while ( _s_sleep ) ::usleep(100000);
238  }
239  }
240  if ( !iter_handler->second.user_handlers.empty() ) {
241  auto& handlers = iter_handler->second.user_handlers;
242  for( auto ih = handlers.rbegin(); ih != handlers.rend(); ++ih ) {
243  if ( ih->user_handler ) {
244  bool ret = (*(ih->user_handler))(ih->user_context, signum);
245  if ( ret ) {
246  return;
247  }
248  // Otherwise continue signal processing and eventually call default handlers
249  }
250  // No handler fired: call previously registered signal handler
251  auto& entry = iter_handler->second.old_action;
252  if ( entry.sa_handler )
253  (*entry.sa_handler)(signum);
254  else if ( entry.sa_sigaction )
255  (*entry.sa_sigaction)(signum, info, ptr);
256  }
257  }
258  if ( signum == SIGSEGV || signum == SIGBUS || signum == SIGILL || signum == SIGABRT ) {
259  instance().back_trace(signum);
260  }
261  else if ( info->si_signo == SIGSEGV || info->si_signo == SIGBUS || info->si_signo == SIGILL || info->si_signo == SIGABRT ) {
262  instance().back_trace(info->si_signo);
263  }
264  }
265  if ( signum == SIGINT || signum == SIGHUP || signum == SIGFPE || signum == SIGPIPE ) {
266  if ( dsc.fun && (dsc0.fun != SIG_IGN) )
267  dsc.fun(signum, info, ptr);
268  else if ( signum == SIGHUP )
269  ::_exit(128+signum);
270  }
271  else if ( signum == SIGSEGV && hdlr && hdlr != SIG_IGN && hdlr != SIG_DFL ) {
272  ::_exit(128+signum);
273  }
274  else if ( hdlr && hdlr != SIG_IGN && dsc.fun ) {
275  dsc.fun(signum, info, ptr);
276  }
277  else if ( hdlr == SIG_DFL ) {
278  ::_exit(128+signum);
279  }
280  }
281  s_exit_handler_active = false;
282 }
283 
286 {
287 }
288 
291 }
292 
295  auto& imp = implementation::instance();
296  struct sigaction old_action { };
297  printout(INFO, "SignalHandler", "++ Re-apply signal handlers");
298  for( const auto& e : imp.m_map ) {
299  ::sigaction (e.first, &e.second.handler_action, &old_action);
300  printout(DEBUG, "SignalHandler",
301  "++ Re-apply signal handler for %-10s [%3ld entries]",
302  e.second.name.c_str(), e.second.user_handlers.size());
303  }
304 }
305 
307 bool SignalHandler::registerHandler(int sig_num, void* param, signal_handler_t handler) {
308  return implementation::instance().subscribe(sig_num, param, handler) == 1;
309 }
dd4hep::SignalHandler::applyHandlers
void applyHandlers()
(Re-)apply registered interrupt handlers to override potentially later registrations by other librari...
SignalHandler::implementation::unsubscribe
int unsubscribe(int signum, void *user_context)
Unsubscribe from a given signal with a user context identifier.
Definition: SignalHandler.cpp:155
SignalHandler::implementation
Definition: SignalHandler.cpp:50
SignalHandler::implementation::sig_entry_t::user_handler
signal_handler_t user_handler
Definition: SignalHandler.cpp:54
SignalHandler::implementation::SigMap
std::map< int, sig_handler_t > SigMap
Definition: SignalHandler.cpp:64
dd4hep::info
std::size_t info(const std::string &src, const std::string &msg)
Definition: RootDictionary.h:65
dd4hep::SignalHandler::signal_handler_t
bool(* signal_handler_t)(void *user_context, int signal)
User signal handler definition.
Definition: SignalHandler.h:28
SignalHandler::implementation::sig_handler_t::name
std::string name
Definition: SignalHandler.cpp:57
SignalHandler::implementation::sig_entry_t
Definition: SignalHandler.cpp:52
SignalHandler::implementation::handler
static void handler(int signum, siginfo_t *info, void *)
Static handler callback for system signal handler.
Definition: SignalHandler.cpp:211
dd4hep::SignalHandler::SignalHandler
SignalHandler()
Default constructor.
SignalHandler::implementation::m_map
SigMap m_map
Definition: SignalHandler.cpp:65
SignalHandler::implementation::instance
static implementation & instance()
Singleton accessor.
Definition: SignalHandler.cpp:97
SignalHandler::implementation::back_trace
void back_trace(int)
Create simple backtrace.
Definition: SignalHandler.cpp:170
dd4hep::SignalHandler::~SignalHandler
virtual ~SignalHandler()
Default destructor.
SignalHandler::implementation::sig_handler_t::handler_action
Definition: SignalHandler.cpp:59
SignalHandler::implementation::subscribe
int subscribe(int signum, void *user_context, signal_handler_t handler)
Subscribe to a given signal with a user context and a user handler. The context MUST be unique!
Definition: SignalHandler.cpp:130
SignalHandler::implementation::install
void install(int num, const std::string &name, struct sigaction &action)
Install handler for a single signal.
Definition: SignalHandler.cpp:195
SignalHandler::implementation::~implementation
~implementation()
Default destructor.
Definition: SignalHandler.cpp:93
SignalHandler::implementation::implementation
implementation()
Default constructor.
Definition: SignalHandler.cpp:89
SignalHandler::implementation::sig_handler_t::old_action
Definition: SignalHandler.cpp:58
SignalHandler::implementation::sig_handler_t::user_handlers
std::vector< sig_entry_t > user_handlers
Definition: SignalHandler.cpp:60
SignalHandler::implementation::sig_entry_t::user_context
void * user_context
Definition: SignalHandler.cpp:53
SignalHandler.h
dd4hep::SignalHandler::registerHandler
bool registerHandler(int sig_num, void *param, signal_handler_t handler)
Specialized handler for any signal.
SignalHandler::implementation::sig_handler_t
Definition: SignalHandler.cpp:56
dd4hep
Namespace for the AIDA detector description toolkit.
Definition: AlignmentsCalib.h:28
SignalHandler::implementation::init
void init()
Initialize the exit handler. Subscribe to default signals.
Definition: SignalHandler.cpp:106
signal_handler_t
SignalHandler::signal_handler_t signal_handler_t
Definition: SignalHandler.cpp:28
Printout.h