812 lines
24 KiB
C++
812 lines
24 KiB
C++
//
|
|
// ********************************************************************
|
|
// * License and Disclaimer *
|
|
// * *
|
|
// * The Geant4 software is copyright of the Copyright Holders of *
|
|
// * the Geant4 Collaboration. It is provided under the terms and *
|
|
// * conditions of the Geant4 Software License, included in the file *
|
|
// * LICENSE and available at http://cern.ch/geant4/license . These *
|
|
// * include a list of copyright holders. *
|
|
// * *
|
|
// * Neither the authors of this software system, nor their employing *
|
|
// * institutes,nor the agencies providing financial support for this *
|
|
// * work make any representation or warranty, express or implied, *
|
|
// * regarding this software system or assume any liability for its *
|
|
// * use. Please see the license in the file LICENSE and URL above *
|
|
// * for the full disclaimer and the limitation of liability. *
|
|
// * *
|
|
// * This code implementation is the result of the scientific and *
|
|
// * technical work of the GEANT4 collaboration. *
|
|
// * By using, copying, modifying or distributing the software (or *
|
|
// * any work based on the software) you agree to acknowledge its *
|
|
// * use in resulting scientific publications, and indicate your *
|
|
// * acceptance of all terms of the Geant4 Software license. *
|
|
// ********************************************************************
|
|
//
|
|
// G4Backtrace
|
|
//
|
|
// Description:
|
|
//
|
|
// Prints backtraces after signals are caught. Available on Unix.
|
|
//
|
|
// Usage:
|
|
// A standard set of signals are enabled by default:
|
|
//
|
|
// SIGQUIT, SIGILL, SIGABRT, SIGKILL, SIGBUS, SIGSEGV
|
|
//
|
|
// These should not interfere with debuggers and/or G4FPEDetection.
|
|
// In order to turn off handling for one or more signals, one can do:
|
|
//
|
|
// G4BackTrace::DefaultSignals() = std::set<int>{};
|
|
// G4BackTrace::DefaultSignals() = std::set<int>{ SIGSEGV };
|
|
//
|
|
// and so on, *before* creating the run-manager. After the run-manager
|
|
// has been created, one should disable the signals:
|
|
//
|
|
// G4BackTrace::Disable(G4BackTrace::DefaultSignals());
|
|
//
|
|
// Additionally, at runtime, the environment variable "G4BACKTRACE" can
|
|
// be set to select a specific set of signals or none, e.g. in bash:
|
|
//
|
|
// export G4BACKTRACE="SIGQUIT,SIGSEGV"
|
|
// export G4BACKTRACE="none"
|
|
//
|
|
// The environment variable is case-insensitive and can use any of the
|
|
// following delimiters: space, comma, semi-colon, colon
|
|
//
|
|
// Author: J.Madsen, 19 October 2020
|
|
// --------------------------------------------------------------------
|
|
|
|
#ifndef G4Backtrace_hh
|
|
#define G4Backtrace_hh 1
|
|
|
|
#include "G4Types.hh"
|
|
#include "G4String.hh"
|
|
#include "G4Threading.hh"
|
|
|
|
#if defined(__APPLE__) || defined(__MACH__)
|
|
# if !defined(G4MACOS)
|
|
# define G4MACOS
|
|
# endif
|
|
# if !defined(G4UNIX)
|
|
# define G4UNIX
|
|
# endif
|
|
#elif defined(__linux__) || defined(__linux) || defined(linux) || \
|
|
defined(__gnu_linux__)
|
|
# if !defined(G4LINUX)
|
|
# define G4LINUX
|
|
# endif
|
|
# if !defined(G4UNIX)
|
|
# define G4UNIX
|
|
# endif
|
|
#elif defined(__unix__) || defined(__unix) || defined(unix)
|
|
# if !defined(G4UNIX)
|
|
# define G4UNIX
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(G4UNIX)
|
|
# include <cxxabi.h>
|
|
# include <execinfo.h>
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(G4LINUX)
|
|
# include <features.h>
|
|
#endif
|
|
|
|
#include <cfenv>
|
|
#include <csignal>
|
|
#include <type_traits>
|
|
|
|
template <typename FuncT>
|
|
using G4ResultOf_t = typename std::result_of<FuncT>::type;
|
|
|
|
// compatible OS and compiler
|
|
#if defined(G4UNIX) && \
|
|
(defined(__GNUC__) || defined(__clang__) || defined(_INTEL_COMPILER))
|
|
# if !defined(G4SIGNAL_AVAILABLE)
|
|
# define G4SIGNAL_AVAILABLE
|
|
# endif
|
|
# if !defined(G4DEMANGLE_AVAILABLE)
|
|
# define G4DEMANGLE_AVAILABLE
|
|
# endif
|
|
#endif
|
|
|
|
#if !defined(G4PSIGINFO_AVAILABLE)
|
|
# if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
|
|
# define G4PSIGINFO_AVAILABLE 1
|
|
# else
|
|
# define G4PSIGINFO_AVAILABLE 0
|
|
# endif
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline G4String G4Demangle(const char* _str)
|
|
{
|
|
#if defined(G4DEMANGLE_AVAILABLE)
|
|
// demangling a string when delimiting
|
|
int _status = 0;
|
|
char* _ret = ::abi::__cxa_demangle(_str, nullptr, nullptr, &_status);
|
|
if(_ret && _status == 0)
|
|
return G4String(const_cast<const char*>(_ret));
|
|
return _str;
|
|
#else
|
|
return _str;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline G4String G4Demangle(const G4String& _str)
|
|
{
|
|
return G4Demangle(_str.c_str());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
template <typename Tp>
|
|
inline G4String G4Demangle()
|
|
{
|
|
return G4Demangle(typeid(Tp).name());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
//
|
|
// ONLY IF G4SIGNAL_AVAILABLE
|
|
//
|
|
//----------------------------------------------------------------------------//
|
|
//
|
|
#if defined(G4SIGNAL_AVAILABLE)
|
|
//
|
|
// these are not in the original POSIX.1-1990 standard so we are defining
|
|
// them in case the OS hasn't
|
|
// POSIX-1.2001
|
|
# ifndef SIGTRAP
|
|
# define SIGTRAP 5
|
|
# endif
|
|
// not specified in POSIX.1-2001, but nevertheless appears on most other
|
|
// UNIX systems, where its default action is typically to terminate the
|
|
// process with a core dump.
|
|
# ifndef SIGEMT
|
|
# define SIGEMT 7
|
|
# endif
|
|
// POSIX-1.2001
|
|
# ifndef SIGURG
|
|
# define SIGURG 16
|
|
# endif
|
|
// POSIX-1.2001
|
|
# ifndef SIGXCPU
|
|
# define SIGXCPU 24
|
|
# endif
|
|
// POSIX-1.2001
|
|
# ifndef SIGXFSZ
|
|
# define SIGXFSZ 25
|
|
# endif
|
|
// POSIX-1.2001
|
|
# ifndef SIGVTALRM
|
|
# define SIGVTALRM 26
|
|
# endif
|
|
// POSIX-1.2001
|
|
# ifndef SIGPROF
|
|
# define SIGPROF 27
|
|
# endif
|
|
// POSIX-1.2001
|
|
# ifndef SIGINFO
|
|
# define SIGINFO 29
|
|
# endif
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
# include <algorithm>
|
|
# include <array>
|
|
# include <cstdlib>
|
|
# include <functional>
|
|
# include <iomanip>
|
|
# include <iostream>
|
|
# include <map>
|
|
# include <mutex>
|
|
# include <regex>
|
|
# include <set>
|
|
# include <sstream>
|
|
# include <string>
|
|
# include <tuple>
|
|
# include <vector>
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
class G4Backtrace
|
|
{
|
|
public:
|
|
using sigaction_t = struct sigaction;
|
|
using exit_action_t = std::function<void(int)>;
|
|
using frame_func_t = std::function<G4String(const char*)>;
|
|
using signal_set_t = std::set<int>;
|
|
|
|
public:
|
|
struct actions
|
|
{
|
|
using id_entry_t = std::tuple<std::string, int, std::string>;
|
|
using id_list_t = std::vector<id_entry_t>;
|
|
|
|
std::shared_ptr<std::mutex> lock = std::make_shared<std::mutex>();
|
|
std::map<int, bool> is_active = {};
|
|
std::map<int, sigaction_t> current = {};
|
|
std::map<int, sigaction_t> previous = {};
|
|
std::vector<exit_action_t> exit_actions = {};
|
|
const id_list_t identifiers = {
|
|
id_entry_t("SIGHUP", SIGHUP, "terminal line hangup"),
|
|
id_entry_t("SIGINT", SIGINT, "interrupt program"),
|
|
id_entry_t("SIGQUIT", SIGQUIT, "quit program"),
|
|
id_entry_t("SIGILL", SIGILL, "illegal instruction"),
|
|
id_entry_t("SIGTRAP", SIGTRAP, "trace trap"),
|
|
id_entry_t("SIGABRT", SIGABRT, "abort program (formerly SIGIOT)"),
|
|
id_entry_t("SIGEMT", SIGEMT, "emulate instruction executed"),
|
|
id_entry_t("SIGFPE", SIGFPE, "floating-point exception"),
|
|
id_entry_t("SIGKILL", SIGKILL, "kill program"),
|
|
id_entry_t("SIGBUS", SIGBUS, "bus error"),
|
|
id_entry_t("SIGSEGV", SIGSEGV, "segmentation violation"),
|
|
id_entry_t("SIGSYS", SIGSYS, "non-existent system call invoked"),
|
|
id_entry_t("SIGPIPE", SIGPIPE, "write on a pipe with no reader"),
|
|
id_entry_t("SIGALRM", SIGALRM, "real-time timer expired"),
|
|
id_entry_t("SIGTERM", SIGTERM, "software termination signal"),
|
|
id_entry_t("SIGURG", SIGURG, "urgent condition present on socket"),
|
|
id_entry_t("SIGSTOP", SIGSTOP, "stop (cannot be caught or ignored)"),
|
|
id_entry_t("SIGTSTP", SIGTSTP, "stop signal generated from keyboard"),
|
|
id_entry_t("SIGCONT", SIGCONT, "continue after stop"),
|
|
id_entry_t("SIGCHLD", SIGCHLD, "child status has changed"),
|
|
id_entry_t("SIGTTIN", SIGTTIN,
|
|
"background read attempted from control terminal"),
|
|
id_entry_t("SIGTTOU", SIGTTOU,
|
|
"background write attempted to control terminal"),
|
|
id_entry_t("SIGIO ", SIGIO, "I/O is possible on a descriptor"),
|
|
id_entry_t("SIGXCPU", SIGXCPU, "cpu time limit exceeded"),
|
|
id_entry_t("SIGXFSZ", SIGXFSZ, "file size limit exceeded"),
|
|
id_entry_t("SIGVTALRM", SIGVTALRM, "virtual time alarm"),
|
|
id_entry_t("SIGPROF", SIGPROF, "profiling timer alarm"),
|
|
id_entry_t("SIGWINCH", SIGWINCH, "Window size change"),
|
|
id_entry_t("SIGINFO", SIGINFO, "status request from keyboard"),
|
|
id_entry_t("SIGUSR1", SIGUSR1, "User defined signal 1"),
|
|
id_entry_t("SIGUSR2", SIGUSR2, "User defined signal 2")
|
|
};
|
|
};
|
|
|
|
public:
|
|
// a functor called for each frame in the backtrace
|
|
static frame_func_t& FrameFunctor();
|
|
// default set of signals
|
|
static signal_set_t& DefaultSignals();
|
|
// the signal handler
|
|
static void Handler(int sig, siginfo_t* sinfo, void* context);
|
|
// information message about the signal, performs exit-actions
|
|
// and prints back-trace
|
|
static void Message(int sig, siginfo_t* sinfo, std::ostream&);
|
|
// calls user-provided functions after signal is caught but before abort
|
|
static void ExitAction(int sig);
|
|
// enable signals via a string (which is tokenized)
|
|
static int Enable(const std::string&);
|
|
// enable signals via set of integers, anything less than zero is ignored
|
|
static int Enable(const signal_set_t& _signals = DefaultSignals());
|
|
// disable signals
|
|
static int Disable(signal_set_t _signals = {});
|
|
// gets the numeric value for a signal name
|
|
static int GetSignal(const std::string&);
|
|
// provides a description of the signal
|
|
static std::string Description(int sig);
|
|
|
|
// adds an exit action
|
|
template <typename FuncT>
|
|
static void AddExitAction(FuncT&& func);
|
|
|
|
// gets a backtrace of "Depth" frames. The offset parameter is used
|
|
// to ignore initial frames (such as this function). A callback
|
|
// can be provided to inspect and/or tweak the frame string
|
|
template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
|
|
static std::array<G4ResultOf_t<FuncT(const char*)>, Depth> GetMangled(
|
|
FuncT&& func = FrameFunctor());
|
|
|
|
// gets a demangled backtrace of "Depth" frames. The offset parameter is
|
|
// used to ignore initial frames (such as this function). A callback
|
|
// can be provided to inspect and/or tweak the frame string
|
|
template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
|
|
static std::array<G4ResultOf_t<FuncT(const char*)>, Depth> GetDemangled(
|
|
FuncT&& func = FrameFunctor());
|
|
|
|
private:
|
|
static actions& GetData()
|
|
{
|
|
static auto _instance = actions{};
|
|
return _instance;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
// a functor called for each frame in the backtrace
|
|
inline G4Backtrace::frame_func_t& G4Backtrace::FrameFunctor()
|
|
{
|
|
static frame_func_t _instance = [](const char* inp) { return G4String(inp); };
|
|
return _instance;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
// default set of signals
|
|
inline G4Backtrace::signal_set_t& G4Backtrace::DefaultSignals()
|
|
{
|
|
static signal_set_t _instance = { SIGQUIT, SIGILL, SIGABRT,
|
|
SIGKILL, SIGBUS, SIGSEGV };
|
|
return _instance;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
template <typename FuncT>
|
|
inline void G4Backtrace::AddExitAction(FuncT&& func)
|
|
{
|
|
GetData().exit_actions.emplace_back(std::forward<FuncT>(func));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline void G4Backtrace::ExitAction(int sig)
|
|
{
|
|
for(auto& itr : GetData().exit_actions)
|
|
itr(sig);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
template <size_t Depth, size_t Offset, typename FuncT>
|
|
inline std::array<G4ResultOf_t<FuncT(const char*)>, Depth>
|
|
G4Backtrace::GetMangled(FuncT&& func)
|
|
{
|
|
static_assert((Depth - Offset) >= 1, "Error Depth - Offset should be >= 1");
|
|
|
|
using type = G4ResultOf_t<FuncT(const char*)>;
|
|
// destination
|
|
std::array<type, Depth> btrace;
|
|
btrace.fill((std::is_pointer<type>::value) ? nullptr : type{});
|
|
|
|
// plus one for this stack-frame
|
|
std::array<void*, Depth + Offset> buffer;
|
|
// size of returned buffer
|
|
auto sz = backtrace(buffer.data(), Depth + Offset);
|
|
// size of relevant data
|
|
auto n = sz - Offset;
|
|
|
|
// skip ahead (Offset + 1) stack frames
|
|
char** bsym = backtrace_symbols(buffer.data() + Offset, n);
|
|
|
|
// report errors
|
|
if(bsym == nullptr)
|
|
perror("backtrace_symbols");
|
|
else
|
|
{
|
|
for(decltype(n) i = 0; i < n; ++i)
|
|
btrace[i] = func(bsym[i]);
|
|
free(bsym);
|
|
}
|
|
return btrace;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
template <size_t Depth, size_t Offset, typename FuncT>
|
|
inline std::array<G4ResultOf_t<FuncT(const char*)>, Depth>
|
|
G4Backtrace::GetDemangled(FuncT&& func)
|
|
{
|
|
auto demangle_bt = [&](const char* cstr) {
|
|
auto _trim = [](std::string& _sub, size_t& _len) {
|
|
size_t _pos = 0;
|
|
while((_pos = _sub.find_first_of(' ')) == 0)
|
|
{
|
|
_sub = _sub.erase(_pos, 1);
|
|
--_len;
|
|
}
|
|
while((_pos = _sub.find_last_of(' ')) == _sub.length() - 1)
|
|
{
|
|
_sub = _sub.substr(0, _sub.length() - 1);
|
|
--_len;
|
|
}
|
|
return _sub;
|
|
};
|
|
|
|
auto str = G4Demangle(std::string(cstr));
|
|
auto beg = str.find("(");
|
|
if(beg == std::string::npos)
|
|
{
|
|
beg = str.find("_Z");
|
|
if(beg != std::string::npos)
|
|
beg -= 1;
|
|
}
|
|
auto end = str.find("+", beg);
|
|
if(beg != std::string::npos && end != std::string::npos)
|
|
{
|
|
auto len = end - (beg + 1);
|
|
auto sub = str.substr(beg + 1, len);
|
|
auto dem = G4Demangle(_trim(sub, len));
|
|
str = str.replace(beg + 1, len, dem);
|
|
}
|
|
else if(beg != std::string::npos)
|
|
{
|
|
auto len = str.length() - (beg + 1);
|
|
auto sub = str.substr(beg + 1, len);
|
|
auto dem = G4Demangle(_trim(sub, len));
|
|
str = str.replace(beg + 1, len, dem);
|
|
}
|
|
else if(end != std::string::npos)
|
|
{
|
|
auto len = end;
|
|
auto sub = str.substr(beg, len);
|
|
auto dem = G4Demangle(_trim(sub, len));
|
|
str = str.replace(beg, len, dem);
|
|
}
|
|
return func(str.c_str());
|
|
};
|
|
return GetMangled<Depth, Offset>(demangle_bt);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline void G4Backtrace::Message(int sig, siginfo_t* sinfo, std::ostream& os)
|
|
{
|
|
std::stringstream message;
|
|
message << "\n### CAUGHT SIGNAL: " << sig << " ### ";
|
|
if(sinfo)
|
|
message << "address: " << sinfo->si_addr << ", ";
|
|
message << Description(sig) << ". ";
|
|
|
|
if(sig == SIGSEGV)
|
|
{
|
|
if(sinfo)
|
|
{
|
|
switch(sinfo->si_code)
|
|
{
|
|
case SEGV_MAPERR:
|
|
message << "Address not mapped to object.";
|
|
break;
|
|
case SEGV_ACCERR:
|
|
message << "Invalid permissions for mapped object.";
|
|
break;
|
|
default:
|
|
message << "Unknown segmentation fault error: " << sinfo->si_code
|
|
<< ".";
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
message << "Segmentation fault (unknown).";
|
|
}
|
|
}
|
|
else if(sig == SIGFPE)
|
|
{
|
|
if(sinfo)
|
|
{
|
|
switch(sinfo->si_code)
|
|
{
|
|
case FE_DIVBYZERO:
|
|
message << "Floating point divide by zero.";
|
|
break;
|
|
case FE_OVERFLOW:
|
|
message << "Floating point overflow.";
|
|
break;
|
|
case FE_UNDERFLOW:
|
|
message << "Floating point underflow.";
|
|
break;
|
|
case FE_INEXACT:
|
|
message << "Floating point inexact result.";
|
|
break;
|
|
case FE_INVALID:
|
|
message << "Floating point invalid operation.";
|
|
break;
|
|
default:
|
|
message << "Unknown floating point exception error: "
|
|
<< sinfo->si_code << ".";
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
message << "Unknown floating point exception";
|
|
if(sinfo)
|
|
message << ": " << sinfo->si_code;
|
|
message << ". ";
|
|
}
|
|
}
|
|
|
|
message << std::endl;
|
|
try
|
|
{
|
|
sigignore(sig);
|
|
ExitAction(sig);
|
|
} catch(std::exception& e)
|
|
{
|
|
std::cerr << "ExitAction(" << sig << ") threw an exception" << std::endl;
|
|
std::cerr << e.what() << std::endl;
|
|
}
|
|
|
|
auto bt = GetDemangled<256, 3>(FrameFunctor());
|
|
std::stringstream prefix;
|
|
prefix << "[PID=" << getpid() << ", TID=" << G4Threading::G4GetThreadId()
|
|
<< "]";
|
|
std::vector<G4String> btvec;
|
|
for(auto& itr : bt)
|
|
{
|
|
if(itr.length() > 0)
|
|
btvec.emplace_back(std::move(itr));
|
|
}
|
|
std::stringstream serr;
|
|
serr << "\nBacktrace:\n";
|
|
auto _w = std::log10(btvec.size()) + 1;
|
|
for(size_t i = 0; i < btvec.size(); ++i)
|
|
{
|
|
serr << prefix.str() << "[" << std::setw(_w) << std::right << i << '/'
|
|
<< std::setw(_w) << std::right << btvec.size() << "]> " << std::left
|
|
<< btvec.at(i) << '\n';
|
|
}
|
|
os << serr.str().c_str() << '\n';
|
|
os << message.str() << std::flush;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline void G4Backtrace::Handler(int sig, siginfo_t* sinfo, void*)
|
|
{
|
|
std::unique_lock<std::mutex> lk{ *(GetData().lock) };
|
|
|
|
{
|
|
std::stringstream msg;
|
|
Message(sig, sinfo, msg);
|
|
std::cerr << msg.str() << std::flush;
|
|
}
|
|
|
|
std::stringstream msg;
|
|
msg << "\n\n";
|
|
|
|
if(sinfo && G4PSIGINFO_AVAILABLE > 0)
|
|
{
|
|
# if G4PSIGINFO_AVAILABLE > 0
|
|
psiginfo(sinfo, msg.str().c_str());
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
std::cerr << msg.str() << std::endl;
|
|
}
|
|
|
|
// ignore any termination signals
|
|
sigignore(SIGKILL);
|
|
sigignore(SIGTERM);
|
|
sigignore(SIGABRT);
|
|
abort();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline int G4Backtrace::Enable(const signal_set_t& _signals)
|
|
{
|
|
static bool _first = true;
|
|
std::unique_lock<std::mutex> lk{ *(GetData().lock) };
|
|
if(_first)
|
|
{
|
|
std::string _msg = "!!! G4Backtrace is activated !!!";
|
|
std::stringstream _filler;
|
|
std::stringstream _spacer;
|
|
_filler.fill('#');
|
|
_filler << std::setw(_msg.length()) << "";
|
|
_spacer << std::setw(10) << "";
|
|
std::cout << "\n\n"
|
|
<< _spacer.str() << _filler.str() << "\n"
|
|
<< _spacer.str() << _msg << "\n"
|
|
<< _spacer.str() << _filler.str() << "\n\n"
|
|
<< std::flush;
|
|
}
|
|
_first = false;
|
|
int cnt = 0;
|
|
for(auto& itr : _signals)
|
|
{
|
|
if(itr < 0)
|
|
continue;
|
|
if(GetData().is_active[itr])
|
|
continue;
|
|
++cnt;
|
|
sigfillset(&(GetData().current[itr].sa_mask));
|
|
sigdelset(&(GetData().current[itr].sa_mask), itr);
|
|
GetData().current[itr].sa_sigaction = &Handler;
|
|
GetData().current[itr].sa_flags = SA_SIGINFO;
|
|
sigaction(itr, &(GetData().current[itr]), &(GetData().previous[itr]));
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline int G4Backtrace::Enable(const std::string& _signals)
|
|
{
|
|
if(_signals.empty())
|
|
return 0;
|
|
|
|
auto _add_signal = [](std::string sig, signal_set_t& _targ) {
|
|
if(!sig.empty())
|
|
{
|
|
for(auto& itr : sig)
|
|
itr = toupper(itr);
|
|
_targ.insert(G4Backtrace::GetSignal(sig));
|
|
}
|
|
};
|
|
|
|
const std::regex wsp_re("[ ,;:\t\n]+");
|
|
auto _maxid = GetData().identifiers.size();
|
|
auto _result = std::vector<std::string>(_maxid, "");
|
|
std::copy(
|
|
std::sregex_token_iterator(_signals.begin(), _signals.end(), wsp_re, -1),
|
|
std::sregex_token_iterator(), _result.begin());
|
|
signal_set_t _sigset{};
|
|
for(auto& itr : _result)
|
|
_add_signal(itr, _sigset);
|
|
return Enable(_sigset);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline int G4Backtrace::Disable(signal_set_t _signals)
|
|
{
|
|
std::unique_lock<std::mutex> lk{ *(GetData().lock) };
|
|
|
|
if(_signals.empty())
|
|
{
|
|
for(auto& itr : GetData().is_active)
|
|
_signals.insert(itr.first);
|
|
}
|
|
|
|
int cnt = 0;
|
|
for(auto& itr : _signals)
|
|
{
|
|
if(itr < 0)
|
|
continue;
|
|
if(!GetData().is_active[itr])
|
|
continue;
|
|
++cnt;
|
|
sigaction(itr, &(GetData().previous[itr]), nullptr);
|
|
GetData().current.erase(itr);
|
|
GetData().is_active[itr] = false;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline int G4Backtrace::GetSignal(const std::string& sid)
|
|
{
|
|
for(auto&& itr : GetData().identifiers)
|
|
{
|
|
if(std::get<0>(itr) == sid)
|
|
return std::get<1>(itr);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
inline std::string G4Backtrace::Description(int sig)
|
|
{
|
|
for(auto&& itr : GetData().identifiers)
|
|
{
|
|
if(std::get<1>(itr) == sig)
|
|
{
|
|
std::stringstream ss;
|
|
ss << " signal = " << std::setw(8) << std::get<0>(itr)
|
|
<< ", value = " << std::setw(4) << std::get<1>(itr)
|
|
<< ", description = " << std::get<2>(itr);
|
|
return ss.str();
|
|
}
|
|
}
|
|
std::stringstream ss;
|
|
ss << " signal = " << std::setw(8) << "unknown"
|
|
<< ", value = " << std::setw(4) << sig;
|
|
return ss.str();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#else
|
|
|
|
# include <array>
|
|
# include <functional>
|
|
# include <map>
|
|
# include <set>
|
|
# include <string>
|
|
# include <tuple>
|
|
# include <vector>
|
|
|
|
// dummy implementation
|
|
class G4Backtrace
|
|
{
|
|
public:
|
|
struct fake_siginfo
|
|
{};
|
|
struct fake_sigaction
|
|
{};
|
|
|
|
using siginfo_t = fake_siginfo;
|
|
using sigaction_t = fake_sigaction;
|
|
using exit_action_t = std::function<void(int)>;
|
|
using frame_func_t = std::function<G4String(const char*)>;
|
|
using signal_set_t = std::set<int>;
|
|
|
|
public:
|
|
struct actions
|
|
{
|
|
using id_entry_t = std::tuple<std::string, int, std::string>;
|
|
using id_list_t = std::vector<id_entry_t>;
|
|
|
|
std::map<int, bool> is_active = {};
|
|
std::map<int, sigaction_t> current = {};
|
|
std::map<int, sigaction_t> previous = {};
|
|
std::vector<exit_action_t> exit_actions = {};
|
|
const id_list_t identifiers = {};
|
|
};
|
|
|
|
public:
|
|
static void Handler(int, siginfo_t*, void*) {}
|
|
static void Message(int, siginfo_t*, std::ostream&) {}
|
|
static void ExitAction(int) {}
|
|
static int Enable(const std::string&) { return 0; }
|
|
static int Enable(const signal_set_t& = DefaultSignals()) { return 0; }
|
|
static int Disable(signal_set_t = {}) { return 0; }
|
|
static int GetSignal(const std::string&) { return -1; }
|
|
static std::string Description(int) { return std::string{}; }
|
|
|
|
template <typename FuncT>
|
|
static void AddExitAction(FuncT&&)
|
|
{}
|
|
|
|
template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
|
|
static std::array<G4ResultOf_t<FuncT(const char*)>, Depth> GetMangled(
|
|
FuncT&& func = FrameFunctor())
|
|
{
|
|
using type = G4ResultOf_t<FuncT(const char*)>;
|
|
auto ret = std::array<type, Depth>{};
|
|
ret.fill(func(""));
|
|
return ret;
|
|
}
|
|
|
|
template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
|
|
static std::array<G4ResultOf_t<FuncT(const char*)>, Depth> GetDemangled(
|
|
FuncT&& func = FrameFunctor())
|
|
{
|
|
using type = G4ResultOf_t<FuncT(const char*)>;
|
|
auto ret = std::array<type, Depth>{};
|
|
ret.fill(func(""));
|
|
return ret;
|
|
}
|
|
|
|
// a functor called for each frame in the backtrace
|
|
static frame_func_t& FrameFunctor()
|
|
{
|
|
static frame_func_t _instance = [](const char* _s) { return G4String(_s); };
|
|
return _instance;
|
|
}
|
|
|
|
// default set of signals
|
|
static signal_set_t& DefaultSignals()
|
|
{
|
|
static signal_set_t _instance = {};
|
|
return _instance;
|
|
}
|
|
|
|
static actions& GetData()
|
|
{
|
|
static auto _instance = actions{};
|
|
return _instance;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#endif // G4SIGNAL_AVAILABLE
|
|
#endif // G4Backtrace_hh
|