Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/global/management/include/G4Backtrace.hh

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 // G4Backtrace
 27 //
 28 // Description:
 29 //
 30 //  Prints backtraces after signals are caught. Available on Unix.
 31 //
 32 // Usage:
 33 //  A standard set of signals are enabled by default:
 34 //
 35 //     SIGQUIT, SIGILL, SIGABRT, SIGKILL, SIGBUS, SIGSEGV
 36 //
 37 //  These should not interfere with debuggers and/or G4FPEDetection.
 38 //  In order to turn off handling for one or more signals, one can do:
 39 //
 40 //    G4Backtrace::DefaultSignals() = std::set<int>{};
 41 //    G4Backtrace::DefaultSignals() = std::set<int>{ SIGSEGV };
 42 //
 43 //  and so on, *before* creating the run-manager. After the run-manager
 44 //  has been created, one should disable the signals:
 45 //
 46 //    G4Backtrace::Disable(G4Backtrace::DefaultSignals());
 47 //
 48 //  Additionally, at runtime, the environment variable "G4BACKTRACE" can
 49 //  be set to select a specific set of signals or none, e.g. in bash:
 50 //
 51 //    export G4BACKTRACE="SIGQUIT,SIGSEGV"
 52 //    export G4BACKTRACE="none"
 53 //
 54 //  The environment variable is case-insensitive and can use any of the
 55 //  following delimiters: space, comma, semi-colon, colon
 56 //
 57 // Author: J.Madsen, 19 October 2020
 58 // --------------------------------------------------------------------
 59 
 60 #ifndef G4Backtrace_hh
 61 #define G4Backtrace_hh 1
 62 
 63 #include "G4Types.hh"
 64 #include "G4String.hh"
 65 #include "G4Threading.hh"
 66 
 67 #if defined(__APPLE__) || defined(__MACH__)
 68 #  if !defined(G4MACOS)
 69 #    define G4MACOS
 70 #  endif
 71 #  if !defined(G4UNIX)
 72 #    define G4UNIX
 73 #  endif
 74 #elif defined(__linux__) || defined(__linux) || defined(linux) ||              \
 75   defined(__gnu_linux__)
 76 #  if !defined(G4LINUX)
 77 #    define G4LINUX
 78 #  endif
 79 #  if !defined(G4UNIX)
 80 #    define G4UNIX
 81 #  endif
 82 #elif defined(__unix__) || defined(__unix) || defined(unix)
 83 #  if !defined(G4UNIX)
 84 #    define G4UNIX
 85 #  endif
 86 #endif
 87 
 88 #if defined(G4UNIX) && !defined(WIN32)
 89 #  include <cxxabi.h>
 90 #  include <execinfo.h>
 91 #  include <unistd.h>
 92 #endif
 93 
 94 #if defined(G4LINUX)
 95 #  include <features.h>
 96 #endif
 97 
 98 #include <cfenv>
 99 #include <csignal>
100 #include <type_traits>
101 
102 template <typename FuncT, typename... ArgTypes>
103 using G4ResultOf_t = std::invoke_result_t<FuncT, ArgTypes...>;
104 
105 // compatible OS and compiler
106 #if defined(G4UNIX) &&                                                         \
107   (defined(__GNUC__) || defined(__clang__) || defined(_INTEL_COMPILER))
108 #  if !defined(G4SIGNAL_AVAILABLE)
109 #    define G4SIGNAL_AVAILABLE
110 #  endif
111 #  if !defined(G4DEMANGLE_AVAILABLE)
112 #    define G4DEMANGLE_AVAILABLE
113 #  endif
114 #endif
115 
116 #if !defined(G4PSIGINFO_AVAILABLE)
117 #  if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
118 #    define G4PSIGINFO_AVAILABLE 1
119 #  else
120 #    define G4PSIGINFO_AVAILABLE 0
121 #  endif
122 #endif
123 
124 //----------------------------------------------------------------------------//
125 
126 inline G4String G4Demangle(const char* _str)
127 {
128 #if defined(G4DEMANGLE_AVAILABLE)
129   // demangling a string when delimiting
130   G4int _status = 0;
131   char* _ret  = ::abi::__cxa_demangle(_str, nullptr, nullptr, &_status);
132   if((_ret != nullptr) && _status == 0)
133     return G4String(const_cast<const char*>(_ret));
134   return _str;
135 #else
136   return _str;
137 #endif
138 }
139 
140 //----------------------------------------------------------------------------//
141 
142 inline G4String G4Demangle(const G4String& _str)
143 {
144   return G4Demangle(_str.c_str());
145 }
146 
147 //----------------------------------------------------------------------------//
148 
149 template <typename Tp>
150 inline G4String G4Demangle()
151 {
152   return G4Demangle(typeid(Tp).name());
153 }
154 
155 //----------------------------------------------------------------------------//
156 //
157 //      ONLY IF G4SIGNAL_AVAILABLE
158 //
159 //----------------------------------------------------------------------------//
160 //
161 #if defined(G4SIGNAL_AVAILABLE)
162 //
163 //  these are not in the original POSIX.1-1990 standard so we are defining
164 //  them in case the OS hasn't
165 //  POSIX-1.2001
166 #  ifndef SIGTRAP
167 #    define SIGTRAP 5
168 #  endif
169 //  not specified in POSIX.1-2001, but nevertheless appears on most other
170 //  UNIX systems, where its default action is typically to terminate the
171 //  process with a core dump.
172 #  ifndef SIGEMT
173 #    define SIGEMT 7
174 #  endif
175 //  POSIX-1.2001
176 #  ifndef SIGURG
177 #    define SIGURG 16
178 #  endif
179 //  POSIX-1.2001
180 #  ifndef SIGXCPU
181 #    define SIGXCPU 24
182 #  endif
183 //  POSIX-1.2001
184 #  ifndef SIGXFSZ
185 #    define SIGXFSZ 25
186 #  endif
187 //  POSIX-1.2001
188 #  ifndef SIGVTALRM
189 #    define SIGVTALRM 26
190 #  endif
191 //  POSIX-1.2001
192 #  ifndef SIGPROF
193 #    define SIGPROF 27
194 #  endif
195 //  POSIX-1.2001
196 #  ifndef SIGINFO
197 #    define SIGINFO 29
198 #  endif
199 
200 //----------------------------------------------------------------------------//
201 
202 #  include <algorithm>
203 #  include <array>
204 #  include <cstdlib>
205 #  include <cstdio>
206 #  include <functional>
207 #  include <iomanip>
208 #  include <iostream>
209 #  include <map>
210 #  include <regex>
211 #  include <set>
212 #  include <sstream>
213 #  include <string>
214 #  include <tuple>
215 #  include <vector>
216 
217 //----------------------------------------------------------------------------//
218 
219 class G4Backtrace
220 {
221  public:
222   using sigaction_t   = struct sigaction;
223   using exit_action_t = std::function<void(G4int)>;
224   using frame_func_t  = std::function<G4String(const char*)>;
225   using signal_set_t  = std::set<G4int>;
226 
227  public:
228   struct actions
229   {
230     using id_entry_t = std::tuple<std::string, G4int, std::string>;
231     using id_list_t  = std::vector<id_entry_t>;
232 
233     std::map<G4int, G4bool> is_active           = {};
234     std::map<G4int, sigaction_t> current      = {};
235     std::map<G4int, sigaction_t> previous     = {};
236     std::vector<exit_action_t> exit_actions = {};
237     const id_list_t identifiers             = {
238       id_entry_t("SIGHUP", SIGHUP, "terminal line hangup"),
239       id_entry_t("SIGINT", SIGINT, "interrupt program"),
240       id_entry_t("SIGQUIT", SIGQUIT, "quit program"),
241       id_entry_t("SIGILL", SIGILL, "illegal instruction"),
242       id_entry_t("SIGTRAP", SIGTRAP, "trace trap"),
243       id_entry_t("SIGABRT", SIGABRT, "abort program (formerly SIGIOT)"),
244       id_entry_t("SIGEMT", SIGEMT, "emulate instruction executed"),
245       id_entry_t("SIGFPE", SIGFPE, "floating-point exception"),
246       id_entry_t("SIGKILL", SIGKILL, "kill program"),
247       id_entry_t("SIGBUS", SIGBUS, "bus error"),
248       id_entry_t("SIGSEGV", SIGSEGV, "segmentation violation"),
249       id_entry_t("SIGSYS", SIGSYS, "non-existent system call invoked"),
250       id_entry_t("SIGPIPE", SIGPIPE, "write on a pipe with no reader"),
251       id_entry_t("SIGALRM", SIGALRM, "real-time timer expired"),
252       id_entry_t("SIGTERM", SIGTERM, "software termination signal"),
253       id_entry_t("SIGURG", SIGURG, "urgent condition present on socket"),
254       id_entry_t("SIGSTOP", SIGSTOP, "stop (cannot be caught or ignored)"),
255       id_entry_t("SIGTSTP", SIGTSTP, "stop signal generated from keyboard"),
256       id_entry_t("SIGCONT", SIGCONT, "continue after stop"),
257       id_entry_t("SIGCHLD", SIGCHLD, "child status has changed"),
258       id_entry_t("SIGTTIN", SIGTTIN,
259                  "background read attempted from control terminal"),
260       id_entry_t("SIGTTOU", SIGTTOU,
261                  "background write attempted to control terminal"),
262       id_entry_t("SIGIO ", SIGIO, "I/O is possible on a descriptor"),
263       id_entry_t("SIGXCPU", SIGXCPU, "cpu time limit exceeded"),
264       id_entry_t("SIGXFSZ", SIGXFSZ, "file size limit exceeded"),
265       id_entry_t("SIGVTALRM", SIGVTALRM, "virtual time alarm"),
266       id_entry_t("SIGPROF", SIGPROF, "profiling timer alarm"),
267       id_entry_t("SIGWINCH", SIGWINCH, "Window size change"),
268       id_entry_t("SIGINFO", SIGINFO, "status request from keyboard"),
269       id_entry_t("SIGUSR1", SIGUSR1, "User defined signal 1"),
270       id_entry_t("SIGUSR2", SIGUSR2, "User defined signal 2")
271     };
272   };
273 
274  public:
275   // a functor called for each frame in the backtrace
276   static frame_func_t& FrameFunctor();
277   // default set of signals
278   static signal_set_t& DefaultSignals();
279   // the signal handler
280   static void Handler(G4int sig, siginfo_t* sinfo, void* context);
281   // information message about the signal, performs exit-actions
282   // and prints back-trace
283   static void Message(G4int sig, siginfo_t* sinfo, std::ostream&);
284   // calls user-provided functions after signal is caught but before abort
285   static void ExitAction(G4int sig);
286   // enable signals via a string (which is tokenized)
287   static G4int Enable(const std::string&);
288   // enable signals via set of integers, anything less than zero is ignored
289   static G4int Enable(const signal_set_t& _signals = DefaultSignals());
290   // disable signals
291   static G4int Disable(signal_set_t _signals = {});
292   // gets the numeric value for a signal name
293   static G4int GetSignal(const std::string&);
294   // provides a description of the signal
295   static std::string Description(G4int sig);
296 
297   // adds an exit action
298   template <typename FuncT>
299   static void AddExitAction(FuncT&& func);
300 
301   // gets a backtrace of "Depth" frames. The offset parameter is used
302   // to ignore initial frames (such as this function). A callback
303   // can be provided to inspect and/or tweak the frame string
304   template <std::size_t Depth, std::size_t Offset = 0, typename FuncT = frame_func_t>
305   static std::array<G4ResultOf_t<FuncT, const char*>, Depth> GetMangled(
306     FuncT&& func = FrameFunctor());
307 
308   // gets a demangled backtrace of "Depth" frames. The offset parameter is
309   // used to ignore initial frames (such as this function). A callback
310   // can be provided to inspect and/or tweak the frame string
311   template <std::size_t Depth, std::size_t Offset = 0, typename FuncT = frame_func_t>
312   static std::array<G4ResultOf_t<FuncT, const char*>, Depth> GetDemangled(
313     FuncT&& func = FrameFunctor());
314 
315  private:
316   static actions& GetData()
317   {
318     static auto _instance = actions{};
319     return _instance;
320   }
321 };
322 
323 //----------------------------------------------------------------------------//
324 
325 // a functor called for each frame in the backtrace
326 inline G4Backtrace::frame_func_t& G4Backtrace::FrameFunctor()
327 {
328   static frame_func_t _instance = [](const char* inp) { return G4String(inp); };
329   return _instance;
330 }
331 
332 //----------------------------------------------------------------------------//
333 
334 // default set of signals
335 inline G4Backtrace::signal_set_t& G4Backtrace::DefaultSignals()
336 {
337   static signal_set_t _instance = { SIGQUIT, SIGILL, SIGABRT,
338                                     SIGKILL, SIGBUS, SIGSEGV };
339   return _instance;
340 }
341 
342 //----------------------------------------------------------------------------//
343 
344 template <typename FuncT>
345 inline void G4Backtrace::AddExitAction(FuncT&& func)
346 {
347   GetData().exit_actions.emplace_back(std::forward<FuncT>(func));
348 }
349 
350 //----------------------------------------------------------------------------//
351 
352 inline void G4Backtrace::ExitAction(G4int sig)
353 {
354   for(auto& itr : GetData().exit_actions)
355     itr(sig);
356 }
357 
358 //----------------------------------------------------------------------------//
359 
360 template <std::size_t Depth, std::size_t Offset, typename FuncT>
361 inline std::array<G4ResultOf_t<FuncT, const char*>, Depth>
362 G4Backtrace::GetMangled(FuncT&& func)
363 {
364   static_assert((Depth - Offset) >= 1, "Error Depth - Offset should be >= 1");
365 
366   using type = G4ResultOf_t<FuncT, const char*>;
367   // destination
368   std::array<type, Depth> btrace;
369   btrace.fill((std::is_pointer<type>::value) ? nullptr : type{});
370 
371   // plus one for this stack-frame
372   std::array<void*, Depth + Offset> buffer;
373   // size of returned buffer
374   auto sz = backtrace(buffer.data(), Depth + Offset);
375   // size of relevant data
376   auto n = sz - Offset;
377 
378   // skip ahead (Offset + 1) stack frames
379   char** bsym = backtrace_symbols(buffer.data() + Offset, (G4int)n);
380 
381   // report errors
382   if(bsym == nullptr)
383     perror("backtrace_symbols");
384   else
385   {
386     for(decltype(n) i = 0; i < n; ++i)
387       btrace[i] = func(bsym[i]);
388     free(bsym);
389   }
390   return btrace;
391 }
392 
393 //----------------------------------------------------------------------------//
394 
395 template <std::size_t Depth, std::size_t Offset, typename FuncT>
396 inline std::array<G4ResultOf_t<FuncT, const char*>, Depth>
397 G4Backtrace::GetDemangled(FuncT&& func)
398 {
399   auto demangle_bt = [&](const char* cstr) {
400     auto _trim = [](std::string& _sub, std::size_t& _len) {
401       std::size_t _pos = 0;
402       while((_pos = _sub.find_first_of(' ')) == 0)
403       {
404         _sub = _sub.erase(_pos, 1);
405         --_len;
406       }
407       while((_pos = _sub.find_last_of(' ')) == _sub.length() - 1)
408       {
409         _sub = _sub.substr(0, _sub.length() - 1);
410         --_len;
411       }
412       return _sub;
413     };
414 
415     auto str = G4Demangle(std::string(cstr));
416     auto beg = str.find('(');
417     if(beg == std::string::npos)
418     {
419       beg = str.find("_Z");
420       if(beg != std::string::npos)
421         beg -= 1;
422     }
423     auto end = str.find('+', beg);
424     if(beg != std::string::npos && end != std::string::npos)
425     {
426       auto len = end - (beg + 1);
427       auto sub = str.substr(beg + 1, len);
428       auto dem = G4Demangle(_trim(sub, len));
429       str      = str.replace(beg + 1, len, dem);
430     }
431     else if(beg != std::string::npos)
432     {
433       auto len = str.length() - (beg + 1);
434       auto sub = str.substr(beg + 1, len);
435       auto dem = G4Demangle(_trim(sub, len));
436       str      = str.replace(beg + 1, len, dem);
437     }
438     else if(end != std::string::npos)
439     {
440       auto len = end;
441       auto sub = str.substr(beg, len);
442       auto dem = G4Demangle(_trim(sub, len));
443       str      = str.replace(beg, len, dem);
444     }
445     return func(str.c_str());
446   };
447   return GetMangled<Depth, Offset>(demangle_bt);
448 }
449 
450 //----------------------------------------------------------------------------//
451 
452 inline void G4Backtrace::Message(G4int sig, siginfo_t* sinfo, std::ostream& os)
453 {
454   // try to avoid as many dynamic allocations as possible here to avoid
455   // overflowing the signal stack
456 
457   // ignore future signals of this type
458   signal(sig, SIG_IGN);
459 
460   os << "\n### CAUGHT SIGNAL: " << sig << " ### ";
461   if(sinfo != nullptr)
462     os << "address: " << sinfo->si_addr << ", ";
463   os << Description(sig) << ". ";
464 
465   if(sig == SIGSEGV)
466   {
467     if(sinfo != nullptr)
468     {
469       switch(sinfo->si_code)
470       {
471         case SEGV_MAPERR:
472           os << "Address not mapped to object.";
473           break;
474         case SEGV_ACCERR:
475           os << "Invalid permissions for mapped object.";
476           break;
477         default:
478           os << "Unknown segmentation fault error: " << sinfo->si_code << ".";
479           break;
480       }
481     }
482     else
483     {
484       os << "Segmentation fault (unknown).";
485     }
486   }
487   else if(sig == SIGFPE)
488   {
489     if(sinfo != nullptr)
490     {
491       switch(sinfo->si_code)
492       {
493         case FE_DIVBYZERO:
494           os << "Floating point divide by zero.";
495           break;
496         case FE_OVERFLOW:
497           os << "Floating point overflow.";
498           break;
499         case FE_UNDERFLOW:
500           os << "Floating point underflow.";
501           break;
502         case FE_INEXACT:
503           os << "Floating point inexact result.";
504           break;
505         case FE_INVALID:
506           os << "Floating point invalid operation.";
507           break;
508         default:
509           os << "Unknown floating point exception error: " << sinfo->si_code
510              << ".";
511           break;
512       }
513     }
514     else
515     {
516       os << "Unknown floating point exception";
517       if(sinfo != nullptr)
518         os << ": " << sinfo->si_code;
519       os << ". ";
520     }
521   }
522 
523   os << '\n';
524 
525   auto bt = GetMangled<256, 3>([](const char* _s) { return _s; });
526   char prefix[64];
527   snprintf(prefix, 64, "[PID=%i, TID=%i]", (G4int) getpid(),
528            (G4int) G4Threading::G4GetThreadId());
529   std::size_t sz = 0;
530   for(auto& itr : bt)
531   {
532     if(itr == nullptr)
533       break;
534     if(strlen(itr) == 0)
535       break;
536     ++sz;
537   }
538   os << "\nBacktrace:\n";
539   auto _w = std::log10(sz) + 1;
540   for(std::size_t i = 0; i < sz; ++i)
541   {
542     os << prefix << "[" << std::setw(_w) << std::right << i << '/'
543        << std::setw(_w) << std::right << sz << "]> " << std::left << bt.at(i)
544        << '\n';
545   }
546   os << std::flush;
547 
548   // exit action could cause more signals to be raise so make sure this is done
549   // after the message has been printed
550   try
551   {
552     ExitAction(sig);
553   } catch(std::exception& e)
554   {
555     std::cerr << "ExitAction(" << sig << ") threw an exception" << std::endl;
556     std::cerr << e.what() << std::endl;
557   }
558 }
559 
560 //----------------------------------------------------------------------------//
561 
562 inline void G4Backtrace::Handler(G4int sig, siginfo_t* sinfo, void*)
563 {
564   Message(sig, sinfo, std::cerr);
565 
566   char msg[1024];
567   snprintf(msg, 1024, "%s", "\n");
568 
569   if((sinfo != nullptr) && G4PSIGINFO_AVAILABLE > 0)
570   {
571 #  if G4PSIGINFO_AVAILABLE > 0
572     psiginfo(sinfo, msg);
573     fflush(stdout);
574     fflush(stderr);
575 #  endif
576   }
577   else
578   {
579     std::cerr << msg << std::flush;
580   }
581 
582   // ignore any termination signals
583   signal(SIGKILL, SIG_IGN);
584   signal(SIGTERM, SIG_IGN);
585   signal(SIGABRT, SIG_IGN);
586   abort();
587 }
588 
589 //----------------------------------------------------------------------------//
590 
591 inline G4int G4Backtrace::Enable(const signal_set_t& _signals)
592 {
593   static G4bool _first = true;
594   if(_first)
595   {
596     std::string _msg = "!!! G4Backtrace is activated !!!";
597     std::stringstream _filler;
598     std::stringstream _spacer;
599     _filler.fill('#');
600     _filler << std::setw((G4int)_msg.length()) << "";
601     _spacer << std::setw(10) << "";
602     std::cout << "\n\n"
603               << _spacer.str() << _filler.str() << "\n"
604               << _spacer.str() << _msg << "\n"
605               << _spacer.str() << _filler.str() << "\n\n"
606               << std::flush;
607   }
608   _first  = false;
609   G4int cnt = 0;
610   for(auto& itr : _signals)
611   {
612     if(itr < 0)
613       continue;
614     if(GetData().is_active[itr])
615       continue;
616     ++cnt;
617     sigfillset(&(GetData().current[itr].sa_mask));
618     sigdelset(&(GetData().current[itr].sa_mask), itr);
619     GetData().current[itr].sa_sigaction = &Handler;
620     GetData().current[itr].sa_flags     = SA_SIGINFO;
621     sigaction(itr, &(GetData().current[itr]), &(GetData().previous[itr]));
622   }
623   return cnt;
624 }
625 
626 //----------------------------------------------------------------------------//
627 
628 inline G4int G4Backtrace::Enable(const std::string& _signals)
629 {
630   if(_signals.empty())
631     return 0;
632 
633   auto _add_signal = [](std::string sig, signal_set_t& _targ) {
634     if(!sig.empty())
635     {
636       for(auto& itr : sig)
637         itr = (char)std::toupper(itr);
638       _targ.insert(G4Backtrace::GetSignal(sig));
639     }
640   };
641 
642   const std::regex wsp_re("[ ,;:\t\n]+");
643   auto _maxid  = GetData().identifiers.size();
644   auto _result = std::vector<std::string>(_maxid, "");
645   std::copy(
646     std::sregex_token_iterator(_signals.begin(), _signals.end(), wsp_re, -1),
647     std::sregex_token_iterator(), _result.begin());
648   signal_set_t _sigset{};
649   for(auto& itr : _result)
650     _add_signal(itr, _sigset);
651   return Enable(_sigset);
652 }
653 
654 //----------------------------------------------------------------------------//
655 
656 inline G4int G4Backtrace::Disable(signal_set_t _signals)
657 {
658   if(_signals.empty())
659   {
660     for(auto& itr : GetData().is_active)
661       _signals.insert(itr.first);
662   }
663 
664   G4int cnt = 0;
665   for(auto& itr : _signals)
666   {
667     if(itr < 0)
668       continue;
669     if(!GetData().is_active[itr])
670       continue;
671     ++cnt;
672     sigaction(itr, &(GetData().previous[itr]), nullptr);
673     GetData().current.erase(itr);
674     GetData().is_active[itr] = false;
675   }
676   return cnt;
677 }
678 
679 //----------------------------------------------------------------------------//
680 
681 inline G4int G4Backtrace::GetSignal(const std::string& sid)
682 {
683   for(auto&& itr : GetData().identifiers)
684   {
685     if(std::get<0>(itr) == sid)
686       return std::get<1>(itr);
687   }
688   return -1;
689 }
690 
691 //----------------------------------------------------------------------------//
692 
693 inline std::string G4Backtrace::Description(G4int sig)
694 {
695   for(auto&& itr : GetData().identifiers)
696   {
697     if(std::get<1>(itr) == sig)
698     {
699       std::stringstream ss;
700       ss << " signal = " << std::setw(8) << std::get<0>(itr)
701          << ", value = " << std::setw(4) << std::get<1>(itr)
702          << ", description = " << std::get<2>(itr);
703       return ss.str();
704     }
705   }
706   std::stringstream ss;
707   ss << " signal = " << std::setw(8) << "unknown"
708      << ", value = " << std::setw(4) << sig;
709   return ss.str();
710 }
711 
712 //----------------------------------------------------------------------------//
713 
714 #else
715 
716 #  include <array>
717 #  include <functional>
718 #  include <map>
719 #  include <set>
720 #  include <string>
721 #  include <tuple>
722 #  include <vector>
723 
724 // dummy implementation
725 class G4Backtrace
726 {
727  public:
728   struct fake_siginfo
729   {};
730   struct fake_sigaction
731   {};
732 
733   using siginfo_t = fake_siginfo;
734   using sigaction_t = fake_sigaction;
735   using exit_action_t = std::function<void(G4int)>;
736   using frame_func_t = std::function<G4String(const char*)>;
737   using signal_set_t = std::set<G4int>;
738 
739  public:
740   struct actions
741   {
742     using id_entry_t = std::tuple<std::string, G4int, std::string>;
743     using id_list_t = std::vector<id_entry_t>;
744 
745     std::map<G4int, G4bool> is_active = {};
746     std::map<G4int, sigaction_t> current = {};
747     std::map<G4int, sigaction_t> previous = {};
748     std::vector<exit_action_t> exit_actions = {};
749     const id_list_t identifiers = {};
750   };
751 
752  public:
753   static void Handler(G4int, siginfo_t*, void*) {}
754   static void Message(G4int, siginfo_t*, std::ostream&) {}
755   static void ExitAction(G4int) {}
756   static G4int Enable(const std::string&) { return 0; }
757   static G4int Enable(const signal_set_t& = DefaultSignals()) { return 0; }
758   static G4int Disable(signal_set_t = {}) { return 0; }
759   static G4int GetSignal(const std::string&) { return -1; }
760   static std::string Description(G4int) { return std::string{}; }
761 
762   template <typename FuncT>
763   static void AddExitAction(FuncT&&)
764   {}
765 
766   template <std::size_t Depth, std::size_t Offset = 0, typename FuncT = frame_func_t>
767   static std::array<G4ResultOf_t<FuncT, const char*>, Depth> GetMangled(
768     FuncT&& func = FrameFunctor())
769   {
770     using type = G4ResultOf_t<FuncT, const char*>;
771     auto ret = std::array<type, Depth>{};
772     ret.fill(func(""));
773     return ret;
774   }
775 
776   template <std::size_t Depth, std::size_t Offset = 0, typename FuncT = frame_func_t>
777   static std::array<G4ResultOf_t<FuncT, const char*>, Depth> GetDemangled(
778     FuncT&& func = FrameFunctor())
779   {
780     using type = G4ResultOf_t<FuncT, const char*>;
781     auto ret = std::array<type, Depth>{};
782     ret.fill(func(""));
783     return ret;
784   }
785 
786   // a functor called for each frame in the backtrace
787   static frame_func_t& FrameFunctor()
788   {
789     static frame_func_t _instance = [](const char* _s) { return G4String(_s); };
790     return _instance;
791   }
792 
793   // default set of signals
794   static signal_set_t& DefaultSignals()
795   {
796     static signal_set_t _instance = {};
797     return _instance;
798   }
799 
800   static actions& GetData()
801   {
802     static auto _instance = actions{};
803     return _instance;
804   }
805 };
806 
807 //----------------------------------------------------------------------------//
808 
809 #endif  // G4SIGNAL_AVAILABLE
810 #endif  // G4Backtrace_hh
811