浅谈Linux系统中的异常堆栈跟踪的简单实现

2019-10-13 11:39:08于丽

接下来一步一步的讲解如何使用以上这些函数来获取程序的堆栈

 一、第一版代码如下

#define MAX_FRAMES 100
void GetStackTrace (std::string* stack)
{
  void* addresses[MAX_FRAMES];
  int size = backtrace (addresses, MAX_FRAMES);
  std::unique_ptr<char*, void(*)(void*)> symbols {
    backtrace_symbols (addresses, size),
    std::free
  };
  for (int i = 0; i < size; ++i) {
    stack->append (symbols.get()[i]);
    stack->append ("n");
  }
}

void TestFunc (std::string& stack, int value)
{
  while (--value);
  GetStackTrace (&stack);
}
int main(int argc, char* argv[])
{
  std::string stack;
  TestFunc (stack, 5);
  std::cout << stack << std::endl;
  return 0;
}

编译成可执行文件StackTrace后执行输出如下结果:

./StackTrace(_Z13GetStackTracePSs+0x27) [0x4035d5]
./StackTrace(_Z8TestFuncRSsi+0x2a) [0x4036e6]
./StackTrace(main+0x2d) [0x403715]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f7302027de5]
./StackTrace() [0x403139]

从输出的结果中可以得知程序的调用过程,但是看起来比较难以理解。让我们来稍微改动一下GetStackTrace函数。

二、进阶版代码,在第一点中的代码基础上改动

void DemangleSymbol (std::string* symbol)
{
  size_t size = 0;
  int status = -4;
  char temp[256] = {''};
  //first, try to demangle a c++ name
  if (1 == sscanf (symbol->c_str (), "%*[^(]%*[^_]%[^)+]", temp)) {
    std::unique_ptr<char, void(*)(void*)> demangled {
      abi::__cxa_demangle (temp, NULL, &size, &status),
      std::free
    };
    if (demangled.get ()) {
      symbol->clear ();
      symbol->append (demangled.get ());
      return;
    }
  }
  //if that didn't work, try to get a regular c symbol
  if (1 == sscanf(symbol->c_str (), "%255s", temp)) {
    symbol->clear ();
    symbol->append (temp);
  }
}

void GetStackTrace (std::string* stack)
{
  void* addresses[MAX_FRAMES];
  int size = backtrace (addresses, MAX_FRAMES);
  std::unique_ptr<char*, void(*)(void*)> symbols {
    backtrace_symbols (addresses, size),
    std::free
  };
  for (int i = 0; i < size; ++i) {
    std::string demangled (symbols.get()[i]);
    DemangleSymbol (&demangled);
    stack->append (demangled);
    stack->append ("n");
  }
}

该版本通过__cxa_demangle来将backtrace_symbols返回的字符串逐个解析成可以方便看懂的字符串,由于__cxa_demangle只能解析_Z13GetStackTracePSs这样的字符串,所以使用sscanf来简单的截取backtrace_symbols函数返回的数据,当然,现在已不这么提倡使用sscanf函数了。编译成可执行文件StackTrace后执行输出如下结果:

GetStackTrace(std::string*)
TestFunc(std::string&, int)
./StackTrace(main+0x2d)
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)
./StackTrace()

从输出的结果中可以得知程序的调用过程,但是少了一些其他的信息,让我们来改动一下DemangleSymbol函数