Geant4 Cross Reference |
1 // 1 2 // ******************************************* 3 // * License and Disclaimer 4 // * 5 // * The Geant4 software is copyright of th 6 // * the Geant4 Collaboration. It is provided 7 // * conditions of the Geant4 Software License 8 // * LICENSE and available at http://cern.ch/ 9 // * include a list of copyright holders. 10 // * 11 // * Neither the authors of this software syst 12 // * institutes,nor the agencies providing fin 13 // * work make any representation or warran 14 // * regarding this software system or assum 15 // * use. Please see the license in the file 16 // * for the full disclaimer and the limitatio 17 // * 18 // * This code implementation is the result 19 // * technical work of the GEANT4 collaboratio 20 // * By using, copying, modifying or distri 21 // * any work based on the software) you ag 22 // * use in resulting scientific publicati 23 // * acceptance of all terms of the Geant4 Sof 24 // ******************************************* 25 // 26 // G4Backtrace 27 // 28 // Description: 29 // 30 // Prints backtraces after signals are caught 31 // 32 // Usage: 33 // A standard set of signals are enabled by d 34 // 35 // SIGQUIT, SIGILL, SIGABRT, SIGKILL, SIGB 36 // 37 // These should not interfere with debuggers 38 // In order to turn off handling for one or m 39 // 40 // G4Backtrace::DefaultSignals() = std::set 41 // G4Backtrace::DefaultSignals() = std::set 42 // 43 // and so on, *before* creating the run-manag 44 // has been created, one should disable the s 45 // 46 // G4Backtrace::Disable(G4Backtrace::Defaul 47 // 48 // Additionally, at runtime, the environment 49 // be set to select a specific set of signals 50 // 51 // export G4BACKTRACE="SIGQUIT,SIGSEGV" 52 // export G4BACKTRACE="none" 53 // 54 // The environment variable is case-insensiti 55 // following delimiters: space, comma, semi-c 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) | 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) || 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<Func 104 105 // compatible OS and compiler 106 #if defined(G4UNIX) && 107 (defined(__GNUC__) || defined(__clang__) || 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 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, nu 132 if((_ret != nullptr) && _status == 0) 133 return G4String(const_cast<const char*>(_r 134 return _str; 135 #else 136 return _str; 137 #endif 138 } 139 140 //-------------------------------------------- 141 142 inline G4String G4Demangle(const G4String& _st 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 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 neverth 170 // UNIX systems, where its default action is 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(G4i 224 using frame_func_t = std::function<G4String 225 using signal_set_t = std::set<G4int>; 226 227 public: 228 struct actions 229 { 230 using id_entry_t = std::tuple<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 l 239 id_entry_t("SIGINT", SIGINT, "interrupt 240 id_entry_t("SIGQUIT", SIGQUIT, "quit pro 241 id_entry_t("SIGILL", SIGILL, "illegal in 242 id_entry_t("SIGTRAP", SIGTRAP, "trace tr 243 id_entry_t("SIGABRT", SIGABRT, "abort pr 244 id_entry_t("SIGEMT", SIGEMT, "emulate in 245 id_entry_t("SIGFPE", SIGFPE, "floating-p 246 id_entry_t("SIGKILL", SIGKILL, "kill pro 247 id_entry_t("SIGBUS", SIGBUS, "bus error" 248 id_entry_t("SIGSEGV", SIGSEGV, "segmenta 249 id_entry_t("SIGSYS", SIGSYS, "non-existe 250 id_entry_t("SIGPIPE", SIGPIPE, "write on 251 id_entry_t("SIGALRM", SIGALRM, "real-tim 252 id_entry_t("SIGTERM", SIGTERM, "software 253 id_entry_t("SIGURG", SIGURG, "urgent con 254 id_entry_t("SIGSTOP", SIGSTOP, "stop (ca 255 id_entry_t("SIGTSTP", SIGTSTP, "stop sig 256 id_entry_t("SIGCONT", SIGCONT, "continue 257 id_entry_t("SIGCHLD", SIGCHLD, "child st 258 id_entry_t("SIGTTIN", SIGTTIN, 259 "background read attempted fr 260 id_entry_t("SIGTTOU", SIGTTOU, 261 "background write attempted t 262 id_entry_t("SIGIO ", SIGIO, "I/O is poss 263 id_entry_t("SIGXCPU", SIGXCPU, "cpu time 264 id_entry_t("SIGXFSZ", SIGXFSZ, "file siz 265 id_entry_t("SIGVTALRM", SIGVTALRM, "virt 266 id_entry_t("SIGPROF", SIGPROF, "profilin 267 id_entry_t("SIGWINCH", SIGWINCH, "Window 268 id_entry_t("SIGINFO", SIGINFO, "status r 269 id_entry_t("SIGUSR1", SIGUSR1, "User def 270 id_entry_t("SIGUSR2", SIGUSR2, "User def 271 }; 272 }; 273 274 public: 275 // a functor called for each frame in the ba 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* si 281 // information message about the signal, per 282 // and prints back-trace 283 static void Message(G4int sig, siginfo_t* si 284 // calls user-provided functions after signa 285 static void ExitAction(G4int sig); 286 // enable signals via a string (which is tok 287 static G4int Enable(const std::string&); 288 // enable signals via set of integers, anyth 289 static G4int Enable(const signal_set_t& _sig 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 o 302 // to ignore initial frames (such as this fu 303 // can be provided to inspect and/or tweak t 304 template <std::size_t Depth, std::size_t Off 305 static std::array<G4ResultOf_t<FuncT, const 306 FuncT&& func = FrameFunctor()); 307 308 // gets a demangled backtrace of "Depth" fra 309 // used to ignore initial frames (such as th 310 // can be provided to inspect and/or tweak t 311 template <std::size_t Depth, std::size_t Off 312 static std::array<G4ResultOf_t<FuncT, const 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 back 326 inline G4Backtrace::frame_func_t& G4Backtrace: 327 { 328 static frame_func_t _instance = [](const cha 329 return _instance; 330 } 331 332 //-------------------------------------------- 333 334 // default set of signals 335 inline G4Backtrace::signal_set_t& G4Backtrace: 336 { 337 static signal_set_t _instance = { SIGQUIT, S 338 SIGKILL, S 339 return _instance; 340 } 341 342 //-------------------------------------------- 343 344 template <typename FuncT> 345 inline void G4Backtrace::AddExitAction(FuncT&& 346 { 347 GetData().exit_actions.emplace_back(std::for 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 Offse 361 inline std::array<G4ResultOf_t<FuncT, const ch 362 G4Backtrace::GetMangled(FuncT&& func) 363 { 364 static_assert((Depth - Offset) >= 1, "Error 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) ? 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 + O 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( 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 Offse 396 inline std::array<G4ResultOf_t<FuncT, const ch 397 G4Backtrace::GetDemangled(FuncT&& func) 398 { 399 auto demangle_bt = [&](const char* cstr) { 400 auto _trim = [](std::string& _sub, std::si 401 std::size_t _pos = 0; 402 while((_pos = _sub.find_first_of(' ')) = 403 { 404 _sub = _sub.erase(_pos, 1); 405 --_len; 406 } 407 while((_pos = _sub.find_last_of(' ')) == 408 { 409 _sub = _sub.substr(0, _sub.length() - 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: 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, si 453 { 454 // try to avoid as many dynamic allocations 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 mappe 476 break; 477 default: 478 os << "Unknown segmentation fault er 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 operat 507 break; 508 default: 509 os << "Unknown floating point except 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* 526 char prefix[64]; 527 snprintf(prefix, 64, "[PID=%i, TID=%i]", (G4 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) << st 543 << std::setw(_w) << std::right << sz << 544 << '\n'; 545 } 546 os << std::flush; 547 548 // exit action could cause more signals to b 549 // after the message has been printed 550 try 551 { 552 ExitAction(sig); 553 } catch(std::exception& e) 554 { 555 std::cerr << "ExitAction(" << sig << ") th 556 std::cerr << e.what() << std::endl; 557 } 558 } 559 560 //-------------------------------------------- 561 562 inline void G4Backtrace::Handler(G4int sig, si 563 { 564 Message(sig, sinfo, std::cerr); 565 566 char msg[1024]; 567 snprintf(msg, 1024, "%s", "\n"); 568 569 if((sinfo != nullptr) && G4PSIGINFO_AVAILABL 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_ 592 { 593 static G4bool _first = true; 594 if(_first) 595 { 596 std::string _msg = "!!! G4Backtrace is act 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( 604 << _spacer.str() << _msg << "\n" 605 << _spacer.str() << _filler.str( 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_mas 618 sigdelset(&(GetData().current[itr].sa_mask 619 GetData().current[itr].sa_sigaction = &Han 620 GetData().current[itr].sa_flags = SA_S 621 sigaction(itr, &(GetData().current[itr]), 622 } 623 return cnt; 624 } 625 626 //-------------------------------------------- 627 628 inline G4int G4Backtrace::Enable(const std::st 629 { 630 if(_signals.empty()) 631 return 0; 632 633 auto _add_signal = [](std::string sig, signa 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>(_max 645 std::copy( 646 std::sregex_token_iterator(_signals.begin( 647 std::sregex_token_iterator(), _result.begi 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 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]), 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: 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(G4 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) << st 701 << ", value = " << std::setw(4) << st 702 << ", description = " << std::get<2>( 703 return ss.str(); 704 } 705 } 706 std::stringstream ss; 707 ss << " signal = " << std::setw(8) << "unkno 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(G4i 736 using frame_func_t = std::function<G4String( 737 using signal_set_t = std::set<G4int>; 738 739 public: 740 struct actions 741 { 742 using id_entry_t = std::tuple<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:: 755 static void ExitAction(G4int) {} 756 static G4int Enable(const std::string&) { re 757 static G4int Enable(const signal_set_t& = De 758 static G4int Disable(signal_set_t = {}) { re 759 static G4int GetSignal(const std::string&) { 760 static std::string Description(G4int) { retu 761 762 template <typename FuncT> 763 static void AddExitAction(FuncT&&) 764 {} 765 766 template <std::size_t Depth, std::size_t Off 767 static std::array<G4ResultOf_t<FuncT, const 768 FuncT&& func = FrameFunctor()) 769 { 770 using type = G4ResultOf_t<FuncT, const cha 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 Off 777 static std::array<G4ResultOf_t<FuncT, const 778 FuncT&& func = FrameFunctor()) 779 { 780 using type = G4ResultOf_t<FuncT, const cha 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 ba 787 static frame_func_t& FrameFunctor() 788 { 789 static frame_func_t _instance = [](const c 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