• Linux 解码backtrace返回信息


    目录

      前一节Linux backtrace()系列函数 ,已经知道可以通过backtrace,backtrace_symbols得到函数的调用栈信息。不过,在C++中,得到的是一堆难以识别的符号,如何解码得到准确的函数名信息?

      如,前面得到的函数调用栈信息:

      $ ./backtrace 2
      backtrace() return 7 address
      ./backtrace(_Z7myfunc3v+0x1f) [0x400a8c]
      ./backtrace() [0x400b45]
      ./backtrace(_Z6myfunci+0x25) [0x400b6c]
      ./backtrace(_Z6myfunci+0x1e) [0x400b65]
      ./backtrace(main+0x59) [0x400bc7]
      /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f7170ed1f45]
      ./backtrace() [0x4009a9]
      

      (_Z7myfunc3v+0x1f) [0x400a8c],晦涩难懂。

      我们可以用glibc提供的abi::__cxa_demangle(),对得到的函数符号信息进行解码。
      __cxa_demangle详细参见:如何在C++中获得完整的类型名称 | CSDN

      下面一段代码来自chensuo muduo,对其做了简单修改,以便打印完整信息。

      // from chensuo muduo project
      // https://github.com/chenshuo/muduo
      #include <string>
      #include <stdio.h>
      #include <unistd.h>
      #include <sys/syscall.h>
      #include <sys/types.h>
      #include <cxxabi.h>
      #include <execinfo.h>
      
      string stackTrace(bool demangle)
      {
          string stack;
          const int max_frames = 200;
          void* frame[max_frames];
          int nptrs = ::backtrace(frame, max_frames); // GNU extensions built-in function
          char** strings = ::backtrace_symbols(frame, nptrs);
          if (strings)
          {
              size_t len = 256;
              char* demangled = demangle ? static_cast<char*>(::malloc(len)) : nullptr;
              for (int i = 1; i < nptrs; ++i)
              { // skipping the 0-th, which is this function
                  if (demangle)
                  {
                      // https://panthema.net/2008/0901-stacktrace-demangled/
                      // bin/exception_test(_ZN3Bar4testEv+0x79) [0x401909]
                      // demangle "_ZN3Bar4testEv" between "(" and "+" to function name
                      char* left_par = nullptr;
                      char* plus = nullptr;
                      for (char* p = strings[i]; *p; ++p)
                      {
                          if (*p == '(')
                              left_par = p;
                          else if (*p == '+')
                              plus = p;
                      }
      
                      if (left_par && plus)
                      {
                          *plus = '\0';
                          int status = 0;
                          char* ret = abi::__cxa_demangle(left_par+1, demangled, &len, &status);
                          *plus = '+';
                          if (status == 0)
                          { // demangle success
                              demangled = ret;
                              stack.append(strings[i], left_par + 1);
                              stack.append(demangled);
                              stack.push_back(')'); // add ')' to form "(...)"
                              stack.push_back('\n');
                              continue;
                          }
                      }
                  }
                  // Fallback to mangled names
                  stack.append(strings[i]);
                  stack.push_back('\n');
              }
              free(demangled);
              free(strings);
          }
          return stack;
      }
      

      还是用来运行前一节的示例,可以得到

      $./test_demo 2
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc3())
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo() [0x40133d]
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int))
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int))
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(main+0xd2) [0x4017bc]
      /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f8fb1213f45]
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo() [0x401209]
      

      注意到这里有2个myfunc,而没有myfunc2。检查代码发现myfunc2是static函数,推测可能是static修饰符的影响,导致无法正确解码myfunc2符号信息。

      去掉static限制:

      static void myfunc2()
      {
          myfunc3();
      }
      
      // 修改为
      void myfunc2() // 去掉了static
      {
          myfunc3();
      }
      

      再次运行之

      $./test_demo 2
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc3())
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc2())
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int))
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(myfunc(int))
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo(main+0xd2) [0x4017ec]
      /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7faf0ff12f45]
      /home/martin/workspace/CLionProjects/c++/test_demo/bin/test_demo() [0x401239]
      

      可以发现,函数名的顺序,已经之前的推测的调用顺序完全一致。

    • 相关阅读:
      PHP 文件上传下载
      php文件类型MIME对照表
      如何书写安全的PHP代码
      wordpress顶部空白解决方案
      PHP发送邮件
      UTF8下面截取中文字符。
      dedecms 5.5 实现tag分页伪静态
      PHP MySQL 函数
      php异步调用
      归并排序及序列逆序数
    • 原文地址:https://www.cnblogs.com/fortunely/p/15895911.html
    Copyright © 2020-2023  润新知