File indexing completed on 2025-01-18 09:57:46
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053 #ifndef PTL_Backtrace_hh
0054 #define PTL_Backtrace_hh 1
0055
0056 #include "Threading.hh"
0057 #include "Types.hh"
0058
0059 #if defined(PTL_UNIX)
0060 # include <cxxabi.h>
0061 # include <execinfo.h>
0062 # include <unistd.h>
0063 #endif
0064
0065 #if defined(PTL_LINUX)
0066 # include <features.h>
0067 #endif
0068
0069 #include <cfenv>
0070 #include <cmath>
0071 #include <csignal>
0072 #include <cstring>
0073 #include <type_traits>
0074
0075 namespace PTL
0076 {
0077 template <typename FuncT, typename... ArgTypes>
0078 #if __cpp_lib_is_invocable >= 201703
0079 using ResultOf_t = std::invoke_result_t<FuncT, ArgTypes...>;
0080 #else
0081 using ResultOf_t = typename std::result_of<FuncT(ArgTypes...)>::type;
0082 #endif
0083 }
0084
0085
0086 #if defined(PTL_UNIX) && \
0087 (defined(__GNUC__) || defined(__clang__) || defined(_INTEL_COMPILER))
0088 # if !defined(PTL_SIGNAL_AVAILABLE)
0089 # define PTL_SIGNAL_AVAILABLE
0090 # endif
0091 # if !defined(PTL_DEMANGLE_AVAILABLE)
0092 # define PTL_DEMANGLE_AVAILABLE
0093 # endif
0094 #endif
0095
0096 #if !defined(PTL_PSIGINFO_AVAILABLE)
0097 # if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
0098 # define PTL_PSIGINFO_AVAILABLE 1
0099 # else
0100 # define PTL_PSIGINFO_AVAILABLE 0
0101 # endif
0102 #endif
0103
0104
0105
0106 namespace PTL
0107 {
0108 inline std::string
0109 Demangle(const char* _str)
0110 {
0111 #if defined(PTL_DEMANGLE_AVAILABLE)
0112
0113 int _status = 0;
0114 char* _ret = ::abi::__cxa_demangle(_str, nullptr, nullptr, &_status);
0115 if(_ret && _status == 0)
0116 return std::string(const_cast<const char*>(_ret));
0117 return _str;
0118 #else
0119 return _str;
0120 #endif
0121 }
0122
0123
0124
0125 inline std::string
0126 Demangle(const std::string& _str)
0127 {
0128 return Demangle(_str.c_str());
0129 }
0130
0131
0132
0133 template <typename Tp>
0134 inline std::string
0135 Demangle()
0136 {
0137 return Demangle(typeid(Tp).name());
0138 }
0139 }
0140
0141
0142
0143
0144
0145
0146
0147 #if defined(PTL_SIGNAL_AVAILABLE)
0148
0149
0150
0151
0152 # ifndef SIGTRAP
0153 # define SIGTRAP 5
0154 # endif
0155
0156
0157
0158 # ifndef SIGEMT
0159 # define SIGEMT 7
0160 # endif
0161
0162 # ifndef SIGURG
0163 # define SIGURG 16
0164 # endif
0165
0166 # ifndef SIGXCPU
0167 # define SIGXCPU 24
0168 # endif
0169
0170 # ifndef SIGXFSZ
0171 # define SIGXFSZ 25
0172 # endif
0173
0174 # ifndef SIGVTALRM
0175 # define SIGVTALRM 26
0176 # endif
0177
0178 # ifndef SIGPROF
0179 # define SIGPROF 27
0180 # endif
0181
0182 # ifndef SIGINFO
0183 # define SIGINFO 29
0184 # endif
0185
0186
0187
0188 # include <algorithm>
0189 # include <array>
0190 # include <cstdio>
0191 # include <cstdlib>
0192 # include <functional>
0193 # include <iomanip>
0194 # include <iostream>
0195 # include <map>
0196 # include <regex>
0197 # include <set>
0198 # include <sstream>
0199 # include <string>
0200 # include <tuple>
0201 # include <vector>
0202
0203
0204 # include "Threading.hh"
0205
0206
0207
0208 namespace PTL
0209 {
0210 class Backtrace
0211 {
0212 public:
0213 using sigaction_t = struct sigaction;
0214 using exit_action_t = std::function<void(int)>;
0215 using frame_func_t = std::function<std::string(const char*)>;
0216 using signal_set_t = std::set<int>;
0217
0218 public:
0219 struct actions
0220 {
0221 using id_entry_t = std::tuple<std::string, int, std::string>;
0222 using id_list_t = std::vector<id_entry_t>;
0223
0224 std::map<int, bool> is_active = {};
0225 std::map<int, sigaction_t> current = {};
0226 std::map<int, sigaction_t> previous = {};
0227 std::vector<exit_action_t> exit_actions = {};
0228 const id_list_t identifiers = {
0229 id_entry_t("SIGHUP", SIGHUP, "terminal line hangup"),
0230 id_entry_t("SIGINT", SIGINT, "interrupt program"),
0231 id_entry_t("SIGQUIT", SIGQUIT, "quit program"),
0232 id_entry_t("SIGILL", SIGILL, "illegal instruction"),
0233 id_entry_t("SIGTRAP", SIGTRAP, "trace trap"),
0234 id_entry_t("SIGABRT", SIGABRT, "abort program (formerly SIGIOT)"),
0235 id_entry_t("SIGEMT", SIGEMT, "emulate instruction executed"),
0236 id_entry_t("SIGFPE", SIGFPE, "floating-point exception"),
0237 id_entry_t("SIGKILL", SIGKILL, "kill program"),
0238 id_entry_t("SIGBUS", SIGBUS, "bus error"),
0239 id_entry_t("SIGSEGV", SIGSEGV, "segmentation violation"),
0240 id_entry_t("SIGSYS", SIGSYS, "non-existent system call invoked"),
0241 id_entry_t("SIGPIPE", SIGPIPE, "write on a pipe with no reader"),
0242 id_entry_t("SIGALRM", SIGALRM, "real-time timer expired"),
0243 id_entry_t("SIGTERM", SIGTERM, "software termination signal"),
0244 id_entry_t("SIGURG", SIGURG, "urgent condition present on socket"),
0245 id_entry_t("SIGSTOP", SIGSTOP, "stop (cannot be caught or ignored)"),
0246 id_entry_t("SIGTSTP", SIGTSTP, "stop signal generated from keyboard"),
0247 id_entry_t("SIGCONT", SIGCONT, "continue after stop"),
0248 id_entry_t("SIGCHLD", SIGCHLD, "child status has changed"),
0249 id_entry_t("SIGTTIN", SIGTTIN,
0250 "background read attempted from control terminal"),
0251 id_entry_t("SIGTTOU", SIGTTOU,
0252 "background write attempted to control terminal"),
0253 id_entry_t("SIGIO ", SIGIO, "I/O is possible on a descriptor"),
0254 id_entry_t("SIGXCPU", SIGXCPU, "cpu time limit exceeded"),
0255 id_entry_t("SIGXFSZ", SIGXFSZ, "file size limit exceeded"),
0256 id_entry_t("SIGVTALRM", SIGVTALRM, "virtual time alarm"),
0257 id_entry_t("SIGPROF", SIGPROF, "profiling timer alarm"),
0258 id_entry_t("SIGWINCH", SIGWINCH, "Window size change"),
0259 id_entry_t("SIGINFO", SIGINFO, "status request from keyboard"),
0260 id_entry_t("SIGUSR1", SIGUSR1, "User defined signal 1"),
0261 id_entry_t("SIGUSR2", SIGUSR2, "User defined signal 2")
0262 };
0263 };
0264
0265 public:
0266
0267 static frame_func_t& FrameFunctor();
0268
0269 static signal_set_t& DefaultSignals();
0270
0271 static void Handler(int sig, siginfo_t* sinfo, void* context);
0272
0273
0274 static void Message(int sig, siginfo_t* sinfo, std::ostream&);
0275
0276 static void ExitAction(int sig);
0277
0278 static int Enable(const std::string&);
0279
0280 static int Enable(const signal_set_t& _signals = DefaultSignals());
0281
0282 static int Disable(signal_set_t _signals = {});
0283
0284 static int GetSignal(const std::string&);
0285
0286 static std::string Description(int sig);
0287
0288
0289 template <typename FuncT>
0290 static void AddExitAction(FuncT&& func);
0291
0292
0293
0294
0295 template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
0296 static std::array<ResultOf_t<FuncT, const char*>, Depth> GetMangled(
0297 FuncT&& func = FrameFunctor());
0298
0299
0300
0301
0302 template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
0303 static std::array<ResultOf_t<FuncT, const char*>, Depth> GetDemangled(
0304 FuncT&& func = FrameFunctor());
0305
0306 private:
0307 static actions& GetData()
0308 {
0309 static auto _instance = actions{};
0310 return _instance;
0311 }
0312 };
0313
0314
0315
0316
0317 inline Backtrace::frame_func_t&
0318 Backtrace::FrameFunctor()
0319 {
0320 static frame_func_t _instance = [](const char* inp) { return std::string(inp); };
0321 return _instance;
0322 }
0323
0324
0325
0326
0327 inline Backtrace::signal_set_t&
0328 Backtrace::DefaultSignals()
0329 {
0330 static signal_set_t _instance = {
0331 SIGQUIT, SIGILL, SIGABRT, SIGKILL, SIGBUS, SIGSEGV
0332 };
0333 return _instance;
0334 }
0335
0336
0337
0338 template <typename FuncT>
0339 inline void
0340 Backtrace::AddExitAction(FuncT&& func)
0341 {
0342 GetData().exit_actions.emplace_back(std::forward<FuncT>(func));
0343 }
0344
0345
0346
0347 inline void
0348 Backtrace::ExitAction(int sig)
0349 {
0350 for(auto& itr : GetData().exit_actions)
0351 itr(sig);
0352 }
0353
0354
0355
0356 template <size_t Depth, size_t Offset, typename FuncT>
0357 inline std::array<ResultOf_t<FuncT, const char*>, Depth>
0358 Backtrace::GetMangled(FuncT&& func)
0359 {
0360 static_assert((Depth - Offset) >= 1, "Error Depth - Offset should be >= 1");
0361
0362 using type = ResultOf_t<FuncT, const char*>;
0363
0364 std::array<type, Depth> btrace;
0365 btrace.fill((std::is_pointer<type>::value) ? nullptr : type{});
0366
0367
0368 std::array<void*, Depth + Offset> buffer;
0369
0370 auto sz = backtrace(buffer.data(), Depth + Offset);
0371
0372 auto n = sz - Offset;
0373
0374
0375 char** bsym = backtrace_symbols(buffer.data() + Offset, n);
0376
0377
0378 if(bsym == nullptr)
0379 perror("backtrace_symbols");
0380 else
0381 {
0382 for(decltype(n) i = 0; i < n; ++i)
0383 btrace[i] = func(bsym[i]);
0384 free(bsym);
0385 }
0386 return btrace;
0387 }
0388
0389
0390
0391 template <size_t Depth, size_t Offset, typename FuncT>
0392 inline std::array<ResultOf_t<FuncT, const char*>, Depth>
0393 Backtrace::GetDemangled(FuncT&& func)
0394 {
0395 auto demangle_bt = [&](const char* cstr) {
0396 auto _trim = [](std::string& _sub, size_t& _len) {
0397 size_t _pos = 0;
0398 while((_pos = _sub.find_first_of(' ')) == 0)
0399 {
0400 _sub = _sub.erase(_pos, 1);
0401 --_len;
0402 }
0403 while((_pos = _sub.find_last_of(' ')) == _sub.length() - 1)
0404 {
0405 _sub = _sub.substr(0, _sub.length() - 1);
0406 --_len;
0407 }
0408 return _sub;
0409 };
0410
0411 auto str = Demangle(std::string(cstr));
0412 auto beg = str.find("(");
0413 if(beg == std::string::npos)
0414 {
0415 beg = str.find("_Z");
0416 if(beg != std::string::npos)
0417 beg -= 1;
0418 }
0419 auto end = str.find("+", beg);
0420 if(beg != std::string::npos && end != std::string::npos)
0421 {
0422 auto len = end - (beg + 1);
0423 auto sub = str.substr(beg + 1, len);
0424 auto dem = Demangle(_trim(sub, len));
0425 str = str.replace(beg + 1, len, dem);
0426 }
0427 else if(beg != std::string::npos)
0428 {
0429 auto len = str.length() - (beg + 1);
0430 auto sub = str.substr(beg + 1, len);
0431 auto dem = Demangle(_trim(sub, len));
0432 str = str.replace(beg + 1, len, dem);
0433 }
0434 else if(end != std::string::npos)
0435 {
0436 auto len = end;
0437 auto sub = str.substr(beg, len);
0438 auto dem = Demangle(_trim(sub, len));
0439 str = str.replace(beg, len, dem);
0440 }
0441 return func(str.c_str());
0442 };
0443 return GetMangled<Depth, Offset>(demangle_bt);
0444 }
0445
0446
0447
0448 inline void
0449 Backtrace::Message(int sig, siginfo_t* sinfo, std::ostream& os)
0450 {
0451
0452
0453
0454
0455 signal(sig, SIG_IGN);
0456
0457 os << "\n### CAUGHT SIGNAL: " << sig << " ### ";
0458 if(sinfo)
0459 os << "address: " << sinfo->si_addr << ", ";
0460 os << Description(sig) << ". ";
0461
0462 if(sig == SIGSEGV)
0463 {
0464 if(sinfo)
0465 {
0466 switch(sinfo->si_code)
0467 {
0468 case SEGV_MAPERR: os << "Address not mapped to object."; break;
0469 case SEGV_ACCERR: os << "Invalid permissions for mapped object."; break;
0470 default:
0471 os << "Unknown segmentation fault error: " << sinfo->si_code << ".";
0472 break;
0473 }
0474 }
0475 else
0476 {
0477 os << "Segmentation fault (unknown).";
0478 }
0479 }
0480 else if(sig == SIGFPE)
0481 {
0482 if(sinfo)
0483 {
0484 switch(sinfo->si_code)
0485 {
0486 case FE_DIVBYZERO: os << "Floating point divide by zero."; break;
0487 case FE_OVERFLOW: os << "Floating point overflow."; break;
0488 case FE_UNDERFLOW: os << "Floating point underflow."; break;
0489 case FE_INEXACT: os << "Floating point inexact result."; break;
0490 case FE_INVALID: os << "Floating point invalid operation."; break;
0491 default:
0492 os << "Unknown floating point exception error: " << sinfo->si_code
0493 << ".";
0494 break;
0495 }
0496 }
0497 else
0498 {
0499 os << "Unknown floating point exception";
0500 if(sinfo)
0501 os << ": " << sinfo->si_code;
0502 os << ". ";
0503 }
0504 }
0505
0506 os << '\n';
0507
0508 auto bt = GetMangled<256, 3>([](const char* _s) { return _s; });
0509 char prefix[64];
0510 snprintf(prefix, 64, "[PID=%i, TID=%i]", (int) getpid(),
0511 (int) Threading::GetThreadId());
0512 size_t sz = 0;
0513 for(auto& itr : bt)
0514 {
0515 if(!itr)
0516 break;
0517 if(strlen(itr) == 0)
0518 break;
0519 ++sz;
0520 }
0521 os << "\nBacktrace:\n";
0522 auto _w = std::log10(sz) + 1;
0523 for(size_t i = 0; i < sz; ++i)
0524 {
0525 os << prefix << "[" << std::setw(_w) << std::right << i << '/' << std::setw(_w)
0526 << std::right << sz << "]> " << std::left << bt.at(i) << '\n';
0527 }
0528 os << std::flush;
0529
0530
0531
0532 try
0533 {
0534 ExitAction(sig);
0535 } catch(std::exception& e)
0536 {
0537 std::cerr << "ExitAction(" << sig << ") threw an exception" << std::endl;
0538 std::cerr << e.what() << std::endl;
0539 }
0540 }
0541
0542
0543
0544 inline void
0545 Backtrace::Handler(int sig, siginfo_t* sinfo, void*)
0546 {
0547 Message(sig, sinfo, std::cerr);
0548
0549 char msg[1024];
0550 snprintf(msg, 1024, "%s", "\n");
0551
0552 if(sinfo && PTL_PSIGINFO_AVAILABLE > 0)
0553 {
0554 # if PTL_PSIGINFO_AVAILABLE > 0
0555 psiginfo(sinfo, msg);
0556 fflush(stdout);
0557 fflush(stderr);
0558 # endif
0559 }
0560 else
0561 {
0562 std::cerr << msg << std::flush;
0563 }
0564
0565
0566 for(auto itr : { SIGKILL, SIGTERM, SIGABRT })
0567 signal(itr, SIG_IGN);
0568 abort();
0569 }
0570
0571
0572
0573 inline int
0574 Backtrace::Enable(const signal_set_t& _signals)
0575 {
0576 static bool _first = true;
0577 if(_first)
0578 {
0579 std::string _msg = "!!! Backtrace is activated !!!";
0580 std::stringstream _filler;
0581 std::stringstream _spacer;
0582 _filler.fill('#');
0583 _filler << std::setw(_msg.length()) << "";
0584 _spacer << std::setw(10) << "";
0585 std::cout << "\n\n"
0586 << _spacer.str() << _filler.str() << "\n"
0587 << _spacer.str() << _msg << "\n"
0588 << _spacer.str() << _filler.str() << "\n\n"
0589 << std::flush;
0590 }
0591 _first = false;
0592 int cnt = 0;
0593 for(const auto& itr : _signals)
0594 {
0595 if(itr < 0)
0596 continue;
0597 if(GetData().is_active[itr])
0598 continue;
0599 ++cnt;
0600 sigfillset(&(GetData().current[itr].sa_mask));
0601 sigdelset(&(GetData().current[itr].sa_mask), itr);
0602 GetData().current[itr].sa_sigaction = &Handler;
0603 GetData().current[itr].sa_flags = SA_SIGINFO;
0604 sigaction(itr, &(GetData().current[itr]), &(GetData().previous[itr]));
0605 }
0606 return cnt;
0607 }
0608
0609
0610
0611 inline int
0612 Backtrace::Enable(const std::string& _signals)
0613 {
0614 if(_signals.empty())
0615 return 0;
0616
0617 auto _add_signal = [](std::string sig, signal_set_t& _targ) {
0618 if(!sig.empty())
0619 {
0620 for(auto& itr : sig)
0621 itr = toupper(itr);
0622 _targ.insert(Backtrace::GetSignal(sig));
0623 }
0624 };
0625
0626 const std::regex wsp_re("[ ,;:\t\n]+");
0627 auto _maxid = GetData().identifiers.size();
0628 auto _result = std::vector<std::string>(_maxid, "");
0629 std::copy(std::sregex_token_iterator(_signals.begin(), _signals.end(), wsp_re, -1),
0630 std::sregex_token_iterator(), _result.begin());
0631 signal_set_t _sigset{};
0632 for(auto& itr : _result)
0633 _add_signal(itr, _sigset);
0634 return Enable(_sigset);
0635 }
0636
0637
0638
0639 inline int
0640 Backtrace::Disable(signal_set_t _signals)
0641 {
0642 if(_signals.empty())
0643 {
0644 for(auto& itr : GetData().is_active)
0645 _signals.insert(itr.first);
0646 }
0647
0648 int cnt = 0;
0649 for(const auto& itr : _signals)
0650 {
0651 if(itr < 0)
0652 continue;
0653 if(!GetData().is_active[itr])
0654 continue;
0655 ++cnt;
0656 sigaction(itr, &(GetData().previous[itr]), nullptr);
0657 GetData().current.erase(itr);
0658 GetData().is_active[itr] = false;
0659 }
0660 return cnt;
0661 }
0662
0663
0664
0665 inline int
0666 Backtrace::GetSignal(const std::string& sid)
0667 {
0668 for(auto&& itr : GetData().identifiers)
0669 {
0670 if(std::get<0>(itr) == sid)
0671 return std::get<1>(itr);
0672 }
0673 return -1;
0674 }
0675
0676
0677
0678 inline std::string
0679 Backtrace::Description(int sig)
0680 {
0681 for(auto&& itr : GetData().identifiers)
0682 {
0683 if(std::get<1>(itr) == sig)
0684 {
0685 std::stringstream ss;
0686 ss << " signal = " << std::setw(8) << std::get<0>(itr)
0687 << ", value = " << std::setw(4) << std::get<1>(itr)
0688 << ", description = " << std::get<2>(itr);
0689 return ss.str();
0690 }
0691 }
0692 std::stringstream ss;
0693 ss << " signal = " << std::setw(8) << "unknown"
0694 << ", value = " << std::setw(4) << sig;
0695 return ss.str();
0696 }
0697
0698
0699
0700 }
0701
0702 #else
0703
0704 # include <array>
0705 # include <functional>
0706 # include <map>
0707 # include <set>
0708 # include <string>
0709 # include <tuple>
0710 # include <vector>
0711
0712 namespace PTL
0713 {
0714
0715 class Backtrace
0716 {
0717 public:
0718 struct fake_siginfo
0719 {};
0720 struct fake_sigaction
0721 {};
0722
0723 using siginfo_t = fake_siginfo;
0724 using sigaction_t = fake_sigaction;
0725 using exit_action_t = std::function<void(int)>;
0726 using frame_func_t = std::function<std::string(const char*)>;
0727 using signal_set_t = std::set<int>;
0728
0729 public:
0730 struct actions
0731 {
0732 using id_entry_t = std::tuple<std::string, int, std::string>;
0733 using id_list_t = std::vector<id_entry_t>;
0734
0735 std::map<int, bool> is_active = {};
0736 std::map<int, sigaction_t> current = {};
0737 std::map<int, sigaction_t> previous = {};
0738 std::vector<exit_action_t> exit_actions = {};
0739 const id_list_t identifiers = {};
0740 };
0741
0742 public:
0743 static void Handler(int, siginfo_t*, void*) {}
0744 static void Message(int, siginfo_t*, std::ostream&) {}
0745 static void ExitAction(int) {}
0746 static int Enable(const std::string&) { return 0; }
0747 static int Enable(const signal_set_t& = DefaultSignals()) { return 0; }
0748 static int Disable(signal_set_t = {}) { return 0; }
0749 static int GetSignal(const std::string&) { return -1; }
0750 static std::string Description(int) { return std::string{}; }
0751
0752 template <typename FuncT>
0753 static void AddExitAction(FuncT&&)
0754 {}
0755
0756 template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
0757 static std::array<ResultOf_t<FuncT, const char*>, Depth> GetMangled(
0758 FuncT&& func = FrameFunctor())
0759 {
0760 using type = ResultOf_t<FuncT, const char*>;
0761 auto ret = std::array<type, Depth>{};
0762 ret.fill(func(""));
0763 return ret;
0764 }
0765
0766 template <size_t Depth, size_t Offset = 0, typename FuncT = frame_func_t>
0767 static std::array<ResultOf_t<FuncT, const char*>, Depth> GetDemangled(
0768 FuncT&& func = FrameFunctor())
0769 {
0770 using type = ResultOf_t<FuncT, const char*>;
0771 auto ret = std::array<type, Depth>{};
0772 ret.fill(func(""));
0773 return ret;
0774 }
0775
0776
0777 static frame_func_t& FrameFunctor()
0778 {
0779 static frame_func_t _instance = [](const char* _s) { return std::string(_s); };
0780 return _instance;
0781 }
0782
0783
0784 static signal_set_t& DefaultSignals()
0785 {
0786 static signal_set_t _instance = {};
0787 return _instance;
0788 }
0789
0790 static actions& GetData()
0791 {
0792 static auto _instance = actions{};
0793 return _instance;
0794 }
0795 };
0796
0797
0798
0799 }
0800
0801 #endif
0802 #endif