C++获取throw调用栈(Linux)
发布时间:2023-05-17 10:41:09.048 文章来源:AiSoftCloud 浏览次数:593 下载次数:1 

实现方法

头文件:hook_cxa_throw.h

  1. #pragma once
  2. #include <dlfcn.h>
  3. #include <cstddef>
  4. #include <exception>
  5. #include <signal.h>
  6. #include <execinfo.h>
  7. #include <ucontext.h>
  8. #include <thread>
  9. #ifdef __GNUC__
  10. typedef void __attribute__((__noreturn__)) (*cxa_throw_type)(void*, void*, void(*)(void*));
  11. #elif defined(__clang__)
  12. typedef void __attribute__((__noreturn__)) (*cxa_throw_type)(void*, std::type_info*, void (_GLIBCXX_CDTOR_CALLABI*) (void*));
  13. #endif
  14. extern "C" {
  15. #define STR_HELPER(x) #x
  16. #define STR(x) STR_HELPER(x)
  17. #ifdef __clang__
  18. void __cxa_throw(void* thrown_exception, std::type_info* typeifo, void (_GLIBCXX_CDTOR_CALLABI* dest)(void*));
  19. #elif defined(__GNUC__)
  20. void __cxa_throw(void* thrown_exception, void* pvtinfo, void (*dest)(void *));
  21. #endif

头文件:hook_cxa_throw.cc

  1. #include "hook_cxa_throw.h"
  2. #include <iostream>
  3. #include <vector>
  4. #include <string>
  5. #include <sys/time.h>
  6. cxa_throw_type orig_cxa_throw = nullptr;
  7. void load_orig_throw_code() {
  8. orig_cxa_throw = (cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw");
  9. }
  10. std::string get_substring(const std::string& str, const std::string& str1, const std::string& str2) {
  11. if (str.empty()) {
  12. return "";
  13. }
  14. std::size_t index1 = 0;
  15. std::size_t index2 = std::string::npos;
  16. if (!str1.empty()) {
  17. index1 = str.find(str1);
  18. }
  19. if (!str2.empty()) {
  20. index2 = str.find(str2);
  21. }
  22. if (index1 != std::string::npos) {
  23. if (index2 != std::string::npos) {
  24. return str.substr(index1 + str1.length(), index2 - index1 - str1.length());
  25. } else {
  26. return str.substr(index1 + str1.length());
  27. }
  28. }
  29. return "";
  30. }
  31. struct SymbolData {
  32. std::string symbol;
  33. std::string file;
  34. std::string func1;
  35. std::string func2;
  36. std::string addr1;
  37. std::string addr2;
  38. std::string line;
  39. };
  40. void get_backtrace_symbol(char ** strings, int32_t nptrs, std::vector<SymbolData>* res) {
  41. if (strings == nullptr || nptrs <= 0 || res == nullptr) {
  42. return;
  43. }
  44. for (int j = 0; j < nptrs; ++j) {
  45. SymbolData data;
  46. data.symbol = strings[j];
  47. data.file = get_substring(data.symbol, "", "(");
  48. std::string func_addr_str = get_substring(data.symbol, "(", ")");
  49. data.func1 = get_substring(func_addr_str, "", "+");
  50. data.addr1 = get_substring(func_addr_str, "+", "");
  51. data.addr2 = get_substring(data.symbol, "[", "]");
  52. res->push_back(data);
  53. }
  54. }
  55. #ifdef __clang__
  56. void __cxa_throw(void* thrown_exception, std::type_info* typeifo, void (_GLIBCXX_CDTOR_CALLABI* dest)(void*)) {
  57. #elif defined(__GNUC__)
  58. void __cxa_throw(void* thrown_exception, void* pvtinfo, void (*dest)(void *)) {
  59. #endif
  60. if (orig_cxa_throw == nullptr) {
  61. load_orig_throw_code();
  62. }
  63. timeval now;
  64. gettimeofday(&now, NULL);
  65. void *buffer[32];
  66. int32_t nptrs = backtrace(buffer, 32);
  67. char** strings = backtrace_symbols(buffer, nptrs);
  68. std::vector<SymbolData> vec;
  69. get_backtrace_symbol(strings, nptrs, &vec);
  70. if (strings != nullptr) {
  71. free(strings);
  72. }
  73. for (auto& it : vec) {
  74. if (!it.func1.empty() || it.addr1.empty() || it.file.empty()) {
  75. it.func2 = "(" + it.func1 + "+" + it.addr1 + ")";
  76. it.line = "??:?";
  77. continue;
  78. }
  79. std::string cmd = "addr2line " + it.addr1 + " -e " + it.file + " -f -C";
  80. FILE* p = popen(cmd.c_str(), "r");
  81. if (!p) {
  82. continue;
  83. }
  84. char buff[1024] = { 0 };
  85. int32_t res_line_count = 0;
  86. while (fgets(buff, sizeof(buff), p) != nullptr) {
  87. std::string str(buff);
  88. if (!str.empty() && str.back() == '\n') {
  89. str = str.substr(0, str.length() - 1);
  90. }
  91. if (res_line_count == 0) {
  92. it.func2 = str;
  93. } else if (res_line_count == 1) {
  94. it.line = str;
  95. }
  96. ++res_line_count;
  97. }
  98. pclose(p);
  99. }
  100. int32_t index = 0;
  101. std::cout << "*** Exeception catched at " << now.tv_sec << "(unix time) ***" << std::endl;
  102. for (const auto& it : vec) {
  103. std::cout << " #" << index << " " << it.addr2 << " " << it.func2
  104. << " at " << it.line << std::endl;
  105. ++index;
  106. }
  107. #ifdef __clang__
  108. orig_cxa_throw(thrown_exception, typeifo, dest);
  109. #elif defined(__GNUC__)
  110. orig_cxa_throw(thrown_exception, pvtinfo, dest);
  111. #endif
  112. }

测试程序:hook_cxa_throw_test.cc

  1. #include <iostream>
  2. #include <stdexcept>
  3. #include "hook_cxa_throw.h"
  4. #include <unistd.h>
  5. int mytest()
  6. {
  7. try
  8. {
  9. throw "mythrow char strings";
  10. }
  11. catch(const char* sz)
  12. {
  13. std::cout << sz << "\n";
  14. }
  15. ::sleep(1);
  16. return 0;
  17. }
  18. int main(int argc, char* argv[])
  19. {
  20. printf("\n");
  21. mytest();
  22. mytest();
  23. mytest();
  24. mytest();
  25. return 0;
  26. }

CMakeLists.txt

  1. cmake_minimum_required(VERSION 3.10)
  2. project(trycatch_thread)
  3. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
  4. add_executable(hook_cxa_throw_test hook_cxa_throw_test.cc hook_cxa_throw.cc)
  5. target_link_libraries(hook_cxa_throw_test pthread dl)

参考文章

linux下抓取C++ throw调用栈

更多文章可关注公众号
aisoftcloud