53#ifndef PTL_Backtrace_hh
54#define PTL_Backtrace_hh 1
77template <
typename FuncT,
typename... ArgTypes>
78#if __cpp_lib_is_invocable >= 201703
79using ResultOf_t = std::invoke_result_t<FuncT, ArgTypes...>;
81using ResultOf_t =
typename std::result_of<FuncT(ArgTypes...)>::type;
86#if defined(PTL_UNIX) && \
87 (defined(__GNUC__) || defined(__clang__) || defined(_INTEL_COMPILER))
88# if !defined(PTL_SIGNAL_AVAILABLE)
89# define PTL_SIGNAL_AVAILABLE
91# if !defined(PTL_DEMANGLE_AVAILABLE)
92# define PTL_DEMANGLE_AVAILABLE
96#if !defined(PTL_PSIGINFO_AVAILABLE)
97# if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
98# define PTL_PSIGINFO_AVAILABLE 1
100# define PTL_PSIGINFO_AVAILABLE 0
111#if defined(PTL_DEMANGLE_AVAILABLE)
114 char* _ret = ::abi::__cxa_demangle(_str,
nullptr,
nullptr, &_status);
115 if(_ret && _status == 0)
116 return std::string(
const_cast<const char*
>(_ret));
133template <
typename Tp>
147#if defined(PTL_SIGNAL_AVAILABLE)
192# include <functional>
215 using frame_func_t = std::function<std::string(
const char*)>;
221 using id_entry_t = std::tuple<std::string, int, std::string>;
222 using id_list_t = std::vector<id_entry_t>;
225 std::map<int, sigaction_t>
current = {};
226 std::map<int, sigaction_t>
previous = {};
229 id_entry_t(
"SIGHUP", SIGHUP,
"terminal line hangup"),
230 id_entry_t(
"SIGINT", SIGINT,
"interrupt program"),
231 id_entry_t(
"SIGQUIT", SIGQUIT,
"quit program"),
232 id_entry_t(
"SIGILL", SIGILL,
"illegal instruction"),
234 id_entry_t(
"SIGABRT", SIGABRT,
"abort program (formerly SIGIOT)"),
235 id_entry_t(
"SIGEMT", SIGEMT,
"emulate instruction executed"),
236 id_entry_t(
"SIGFPE", SIGFPE,
"floating-point exception"),
237 id_entry_t(
"SIGKILL", SIGKILL,
"kill program"),
239 id_entry_t(
"SIGSEGV", SIGSEGV,
"segmentation violation"),
240 id_entry_t(
"SIGSYS", SIGSYS,
"non-existent system call invoked"),
241 id_entry_t(
"SIGPIPE", SIGPIPE,
"write on a pipe with no reader"),
242 id_entry_t(
"SIGALRM", SIGALRM,
"real-time timer expired"),
243 id_entry_t(
"SIGTERM", SIGTERM,
"software termination signal"),
244 id_entry_t(
"SIGURG", SIGURG,
"urgent condition present on socket"),
245 id_entry_t(
"SIGSTOP", SIGSTOP,
"stop (cannot be caught or ignored)"),
246 id_entry_t(
"SIGTSTP", SIGTSTP,
"stop signal generated from keyboard"),
247 id_entry_t(
"SIGCONT", SIGCONT,
"continue after stop"),
248 id_entry_t(
"SIGCHLD", SIGCHLD,
"child status has changed"),
250 "background read attempted from control terminal"),
252 "background write attempted to control terminal"),
253 id_entry_t(
"SIGIO ", SIGIO,
"I/O is possible on a descriptor"),
254 id_entry_t(
"SIGXCPU", SIGXCPU,
"cpu time limit exceeded"),
255 id_entry_t(
"SIGXFSZ", SIGXFSZ,
"file size limit exceeded"),
256 id_entry_t(
"SIGVTALRM", SIGVTALRM,
"virtual time alarm"),
257 id_entry_t(
"SIGPROF", SIGPROF,
"profiling timer alarm"),
258 id_entry_t(
"SIGWINCH", SIGWINCH,
"Window size change"),
259 id_entry_t(
"SIGINFO", SIGINFO,
"status request from keyboard"),
260 id_entry_t(
"SIGUSR1", SIGUSR1,
"User defined signal 1"),
261 id_entry_t(
"SIGUSR2", SIGUSR2,
"User defined signal 2")
278 static int Enable(
const std::string&);
284 static int GetSignal(
const std::string&);
289 template <
typename FuncT>
295 template <
size_t Depth,
size_t Offset = 0,
typename FuncT = frame_func_t>
296 static std::array<ResultOf_t<FuncT, const char*>, Depth>
GetMangled(
302 template <
size_t Depth,
size_t Offset = 0,
typename FuncT = frame_func_t>
303 static std::array<ResultOf_t<FuncT, const char*>, Depth>
GetDemangled(
309 static auto _instance = actions{};
320 static frame_func_t _instance = [](
const char* inp) {
return std::string(inp); };
331 SIGQUIT, SIGILL, SIGABRT, SIGKILL, SIGBUS, SIGSEGV
338template <
typename FuncT>
350 for(
auto& itr :
GetData().exit_actions)
356template <
size_t Depth,
size_t Offset,
typename FuncT>
357inline std::array<ResultOf_t<FuncT, const char*>, Depth>
360 static_assert((Depth - Offset) >= 1,
"Error Depth - Offset should be >= 1");
364 std::array<type, Depth> btrace;
365 btrace.fill((std::is_pointer<type>::value) ?
nullptr : type{});
368 std::array<void*, Depth + Offset> buffer;
370 auto sz = backtrace(buffer.data(), Depth + Offset);
372 auto n = sz - Offset;
375 char** bsym = backtrace_symbols(buffer.data() + Offset, n);
379 perror(
"backtrace_symbols");
382 for(
decltype(n) i = 0; i <
n; ++i)
383 btrace[i] = func(bsym[i]);
391template <
size_t Depth,
size_t Offset,
typename FuncT>
392inline std::array<ResultOf_t<FuncT, const char*>, Depth>
395 auto demangle_bt = [&](
const char* cstr) {
396 auto _trim = [](std::string& _sub,
size_t& _len) {
398 while((_pos = _sub.find_first_of(
' ')) == 0)
400 _sub = _sub.erase(_pos, 1);
403 while((_pos = _sub.find_last_of(
' ')) == _sub.length() - 1)
405 _sub = _sub.substr(0, _sub.length() - 1);
411 auto str =
Demangle(std::string(cstr));
412 auto beg = str.find(
"(");
413 if(beg == std::string::npos)
415 beg = str.find(
"_Z");
416 if(beg != std::string::npos)
419 auto end = str.find(
"+", beg);
420 if(beg != std::string::npos && end != std::string::npos)
422 auto len = end - (beg + 1);
423 auto sub = str.substr(beg + 1, len);
424 auto dem =
Demangle(_trim(sub, len));
425 str = str.replace(beg + 1, len, dem);
427 else if(beg != std::string::npos)
429 auto len = str.length() - (beg + 1);
430 auto sub = str.substr(beg + 1, len);
431 auto dem =
Demangle(_trim(sub, len));
432 str = str.replace(beg + 1, len, dem);
434 else if(end != std::string::npos)
437 auto sub = str.substr(beg, len);
438 auto dem =
Demangle(_trim(sub, len));
439 str = str.replace(beg, len, dem);
441 return func(str.c_str());
455 signal(sig, SIG_IGN);
457 os <<
"\n### CAUGHT SIGNAL: " << sig <<
" ### ";
459 os <<
"address: " << sinfo->si_addr <<
", ";
466 switch(sinfo->si_code)
468 case SEGV_MAPERR: os <<
"Address not mapped to object.";
break;
469 case SEGV_ACCERR: os <<
"Invalid permissions for mapped object.";
break;
471 os <<
"Unknown segmentation fault error: " << sinfo->si_code <<
".";
477 os <<
"Segmentation fault (unknown).";
480 else if(sig == SIGFPE)
484 switch(sinfo->si_code)
486 case FE_DIVBYZERO: os <<
"Floating point divide by zero.";
break;
487 case FE_OVERFLOW: os <<
"Floating point overflow.";
break;
488 case FE_UNDERFLOW: os <<
"Floating point underflow.";
break;
489 case FE_INEXACT: os <<
"Floating point inexact result.";
break;
490 case FE_INVALID: os <<
"Floating point invalid operation.";
break;
492 os <<
"Unknown floating point exception error: " << sinfo->si_code
499 os <<
"Unknown floating point exception";
501 os <<
": " << sinfo->si_code;
510 snprintf(
prefix, 64,
"[PID=%i, TID=%i]", (
int) getpid(),
521 os <<
"\nBacktrace:\n";
522 auto _w = std::log10(sz) + 1;
523 for(
size_t i = 0; i < sz; ++i)
525 os <<
prefix <<
"[" << std::setw(_w) << std::right << i <<
'/' << std::setw(_w)
526 << std::right << sz <<
"]> " << std::left << bt.at(i) <<
'\n';
535 }
catch(std::exception& e)
537 std::cerr <<
"ExitAction(" << sig <<
") threw an exception" << std::endl;
538 std::cerr << e.what() << std::endl;
547 Message(sig, sinfo, std::cerr);
550 snprintf(msg, 1024,
"%s",
"\n");
554# if PTL_PSIGINFO_AVAILABLE > 0
555 psiginfo(sinfo, msg);
562 std::cerr << msg << std::flush;
566 for(
auto itr : { SIGKILL, SIGTERM, SIGABRT })
567 signal(itr, SIG_IGN);
576 static bool _first =
true;
579 std::string _msg =
"!!! Backtrace is activated !!!";
580 std::stringstream _filler;
581 std::stringstream _spacer;
583 _filler << std::setw(_msg.length()) <<
"";
584 _spacer << std::setw(10) <<
"";
586 << _spacer.str() << _filler.str() <<
"\n"
587 << _spacer.str() << _msg <<
"\n"
588 << _spacer.str() << _filler.str() <<
"\n\n"
593 for(
const auto& itr : _signals)
600 sigfillset(&(
GetData().current[itr].sa_mask));
601 sigdelset(&(
GetData().current[itr].sa_mask), itr);
604 sigaction(itr, &(
GetData().current[itr]), &(
GetData().previous[itr]));
617 auto _add_signal = [](std::string sig,
signal_set_t& _targ) {
626 const std::regex wsp_re(
"[ ,;:\t\n]+");
628 auto _result = std::vector<std::string>(_maxid,
"");
629 std::copy(std::sregex_token_iterator(_signals.begin(), _signals.end(), wsp_re, -1),
630 std::sregex_token_iterator(), _result.begin());
632 for(
auto& itr : _result)
633 _add_signal(itr, _sigset);
644 for(
auto& itr :
GetData().is_active)
645 _signals.insert(itr.first);
649 for(
const auto& itr : _signals)
656 sigaction(itr, &(
GetData().previous[itr]),
nullptr);
668 for(
auto&& itr :
GetData().identifiers)
670 if(std::get<0>(itr) == sid)
671 return std::get<1>(itr);
681 for(
auto&& itr :
GetData().identifiers)
683 if(std::get<1>(itr) == sig)
685 std::stringstream ss;
686 ss <<
" signal = " << std::setw(8) << std::get<0>(itr)
687 <<
", value = " << std::setw(4) << std::get<1>(itr)
688 <<
", description = " << std::get<2>(itr);
692 std::stringstream ss;
693 ss <<
" signal = " << std::setw(8) <<
"unknown"
694 <<
", value = " << std::setw(4) << sig;
705# include <functional>
732 using id_entry_t = std::tuple<std::string, int, std::string>;
746 static int Enable(
const std::string&) {
return 0; }
752 template <
typename FuncT>
756 template <
size_t Depth,
size_t Offset = 0,
typename FuncT = frame_func_t>
757 static std::array<ResultOf_t<FuncT, const char*>, Depth>
GetMangled(
761 auto ret = std::array<type, Depth>{};
766 template <
size_t Depth,
size_t Offset = 0,
typename FuncT = frame_func_t>
771 auto ret = std::array<type, Depth>{};
779 static frame_func_t _instance = [](
const char* _s) {
return std::string(_s); };
792 static auto _instance =
actions{};
#define PTL_PSIGINFO_AVAILABLE
static int Enable(const std::string &)
static signal_set_t & DefaultSignals()
static std::string Description(int)
static void AddExitAction(FuncT &&)
static void ExitAction(int)
static void Message(int, siginfo_t *, std::ostream &)
static int GetSignal(const std::string &)
static frame_func_t & FrameFunctor()
static int Disable(signal_set_t={})
std::function< std::string(const char *)> frame_func_t
static int Enable(const signal_set_t &=DefaultSignals())
std::set< int > signal_set_t
std::function< void(int)> exit_action_t
fake_sigaction sigaction_t
static std::array< ResultOf_t< FuncT, const char * >, Depth > GetDemangled(FuncT &&func=FrameFunctor())
static std::array< ResultOf_t< FuncT, const char * >, Depth > GetMangled(FuncT &&func=FrameFunctor())
static actions & GetData()
static void Handler(int, siginfo_t *, void *)
typename std::result_of< FuncT(ArgTypes...)>::type ResultOf_t
std::vector< id_entry_t > id_list_t
std::map< int, bool > is_active
const id_list_t identifiers
std::tuple< std::string, int, std::string > id_entry_t
std::map< int, sigaction_t > previous
std::vector< exit_action_t > exit_actions
std::map< int, sigaction_t > current