• C++造轮子之: 打印调用栈


    安装addr2line后才能有更详细的信息

    common/stacktrace.h

    直接复制过去用。注意编译选项。

    1. 监听SIGSEGV信号,即段错误信号收到后,输出调用栈。
    2. 捕获std::terminate异常,设置了handler,handler中打调用栈。处理了C++抛异常导致的崩溃
    3. 输出行号:加上编译选项-g和链接选项-rdynamic.保证打出来的二进制符号表中有函数名和行号
    4. VMA地址转换:backtrace_symbols返回的地址可能是不是二进制中的地址。因为不同链接器可能链接的起始相对地址不同。这还需要加上链接选项-ldl。通过dladdr2获取二进制中的地址
    5. 使用addr2line将backtrace_symbols返回的地址转为好看的代码,函数名,行号信息。这要求系统安装了addr2line
    6. popen用来调用addr2line,并拿到返回结果
    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. namespace AdsonLib {
    10. class PrintCoreStack{
    11. public:
    12. static void RegisterCoreHandler() {
    13. struct sigaction sig;
    14. memset(&sig, 0, sizeof(sig));
    15. sig.sa_handler = &PrintCoreStack::CoreHandler;
    16. sig.sa_flags |= SA_RESETHAND; //one-time only
    17. sigaction(SIGSEGV, &sig, NULL);
    18. std::set_terminate([](){
    19. PrintStack();
    20. std::abort();
    21. });
    22. }
    23. private:
    24. static std::string RunCmd(const std::string &cmd) {
    25. FILE* fd = popen(cmd.c_str(), "r");
    26. if(fd == NULL) return "";
    27. char buf[2048]={'\0'};
    28. std::string line;
    29. while(fgets(buf, 2048, fd)){
    30. line += buf;
    31. }
    32. while(line.size() > 0 && line.back() == '\n'){
    33. line.pop_back();
    34. }
    35. return line;
    36. }
    37. static std::string Int2Hex(size_t d){
    38. char tmp[128];
    39. snprintf(tmp, 128, "%p", d);
    40. return tmp;
    41. }
    42. static std::string DetailInfo(const std::string &binary_file, size_t vma_addr){
    43. return RunCmd("addr2line -f -C -p -e " + binary_file + " " + Int2Hex(vma_addr));
    44. }
    45. static size_t AddrToVMA(size_t addr){
    46. Dl_info dinfo;
    47. link_map* lm;
    48. dladdr1((void*)addr, &dinfo,(void**)&lm, RTLD_DL_LINKMAP);
    49. return addr - lm->l_addr;
    50. }
    51. static void PrintStack() {
    52. char *full_stack[100] = {0};
    53. int depth = backtrace(reinterpret_cast<void**>(full_stack), sizeof(full_stack)/sizeof(full_stack[0]));
    54. if (depth){
    55. char** syms = backtrace_symbols(reinterpret_cast<void**>(full_stack), depth);
    56. if (syms){
    57. for(size_t i = 0; i < depth; i++){
    58. int j = 0;
    59. while(syms[i][j] != '(') j++;
    60. std::string f(syms[i], j);
    61. size_t vma_addr = AddrToVMA((size_t)full_stack[i]);
    62. printf("=== [%lu]:%s [vma: %p] [%s]\n", (i+1), syms[i], vma_addr, DetailInfo(f, vma_addr).c_str());
    63. }
    64. }
    65. free(syms);
    66. };
    67. }
    68. static void CoreHandler(int signo){
    69. PrintStack();
    70. raise(SIGSEGV);
    71. }
    72. };
    73. } //namespace AdsonLib

    使用 

    trans_acc.cpp

    使用起来非常简单,在main函数里调用RegisterCoreHandler即可

    1. #include "stacktrace.h"
    2. int main(int argc, char *argv[]) {
    3. PrintCoreStack::RegisterCoreHandler();
    4. throw 333;
    5. }

    bazel BUILD文件

    注意链接要加 "-ldl"和"-rdynamic"

    1. cc_library(
    2. name = "common",
    3. srcs = glob(["common/*.cpp"]),
    4. hdrs = glob(["common/*.h"]),
    5. )
    6. cc_binary(
    7. name = "trans_acc",
    8. srcs = ["trans_acc.cpp"],
    9. deps = [
    10. ":common",
    11. ],
    12. linkopts = [
    13. "-lpthread", "-ldl", "-rdynamic",
    14. ]
    15. )

    .bazel文件

    注意编译要加-g选项

    build --cxxopt="-std=c++17" --cxxopt="-g" -c dbg

    build.sh

    bazel build :trans_acc

    运行结果

    可以看出,前边打出来的是backtrace返回 symbold.最后一个中括号里返回的是addr2line的详细信息。都打出来是因为防止addr2line转换失败。

    1. # bazel-bin/trans_acc
    2. === [1]:bazel-bin/trans_acc(_ZN8AdsonLib14PrintCoreStack10PrintStackEv+0x46) [0x40b90a] [vma: 0x40b90a] [AdsonLib::PrintCoreStack::PrintStack() at /proc/self/cwd/common/stacktrace.h:54]
    3. === [2]:bazel-bin/trans_acc(_ZZN8AdsonLib14PrintCoreStack19RegisterCoreHandlerEvENKUlvE_clEv+0x11) [0x40b41d] [vma: 0x40b41d] [AdsonLib::PrintCoreStack::RegisterCoreHandler()::{lambda()#1}::operator()() const at /proc/self/cwd/common/stacktrace.h:20]
    4. === [3]:bazel-bin/trans_acc(_ZZN8AdsonLib14PrintCoreStack19RegisterCoreHandlerEvENUlvE_4_FUNEv+0xe) [0x40b430] [vma: 0x40b430] [AdsonLib::PrintCoreStack::RegisterCoreHandler()::{lambda()#1}::_FUN() at /proc/self/cwd/common/stacktrace.h:21]
    5. === [4]:/lib64/libstdc++.so.6(+0x91e26) [0x7f0e2bd69e26] [vma: 0x91e26] [__cxxabiv1::__terminate(void (*)()) at /opt/gcc-gcc-8_3_0-release/x86_64-pc-linux-gnu/libstdc++-v3/libsupc++/../../.././libstdc++-v3/libsupc++/eh_terminate.cc:47]
    6. === [5]:/lib64/libstdc++.so.6(+0x91e61) [0x7f0e2bd69e61] [vma: 0x91e61] [std::terminate() at ??:?]
    7. === [6]:/lib64/libstdc++.so.6(+0x92094) [0x7f0e2bd6a094] [vma: 0x92094] [__cxa_throw at ??:?]
    8. === [7]:bazel-bin/trans_acc(_Z7TestLogv+0x428) [0x40a67b] [vma: 0x40a67b] [TestLog() at /proc/self/cwd/trans_acc.cpp:48 (discriminator 10)]
    9. === [8]:bazel-bin/trans_acc(main+0x19) [0x40a783] [vma: 0x40a783] [main at /proc/self/cwd/trans_acc.cpp:53]
    10. === [9]:/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f0e2b412555] [vma: 0x22555] [__libc_start_main at ??:?]
    11. === [10]:bazel-bin/trans_acc() [0x409bd9] [vma: 0x409bd9] [_start at ??:?]
    12. Aborted (core dumped)

  • 相关阅读:
    go使用dlib人脸检测和人脸识别
    java计算机毕业设计springboot+vue青少年编程在线考试系统
    APP开发_ js 控制手机是否显示状态栏
    ai批量剪辑矩阵无人直播一站式托管系统源头技术开发
    JVM学习(一)--程序计数器
    uniapp 接口请求封装
    CCE云原生混部场景下的测试案例
    Metasploit(MSF)使用
    三位球形模型应用
    Cesium展示——wkt 数据绘制
  • 原文地址:https://blog.csdn.net/wyg_031113/article/details/126943175