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

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

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

// The prefix used for mangled symbols, per the Itanium C++ ABI:
// http://www.codesourcery.com/cxx-abi/abi.html#mangling
const char kMangledSymbolPrefix[] = "_Z";
// Characters that can be used for symbols, generated by Ruby:
// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
const char kSymbolCharacters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
// Demangles C++ symbols in the given text. Example:
// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
// =>
// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
void DemangleSymbol (std::string* symbol)
{
  std::string::size_type search_from = 0;
  while (search_from < symbol->size ()) {
    // Look for the start of a mangled symbol from search_from
    std::string::size_type mangled_start = symbol->find (kMangledSymbolPrefix, search_from);
    if (mangled_start == std::string::npos) {
      break; // Mangled symbol not found
    }
    // Look for the end of the mangled symbol
    std::string::size_type mangled_end = symbol->find_first_not_of (kSymbolCharacters, mangled_start);
    if (mangled_end == std::string::npos) {
      mangled_end = symbol->size ();
    }
    std::string mangled_symbol = std::move (symbol->substr (mangled_start, mangled_end - mangled_start));
    // Try to demangle the mangled symbol candidate
    int status = -4; // some arbitrary value to eliminate the compiler warning
    std::unique_ptr<char, void(*)(void*)> demangled_symbol {
      abi::__cxa_demangle (mangled_symbol.c_str (), nullptr, 0, &status),
      std::free
    };
    // 0 Demangling is success
    if (0 == status) {
      // Remove the mangled symbol
      symbol->erase (mangled_start, mangled_end - mangled_start);
      // Insert the demangled symbol
      symbol->insert (mangled_start, demangled_symbol.get ());
      // Next time, we will start right after the demangled symbol
      search_from = mangled_start + strlen (demangled_symbol.get ());
    }
    else {
      // Failed to demangle. Retry after the "_Z" we just found
      search_from = mangled_start + 2;
    }
  }
}

该版本的DemangleSymbol函数与第二版的DemangleSymbol函数稍有改动,该版本主要是找到_Z13GetStackTracePSs这样的字符串给__cxa_demangle函数解析,最后将解析后的内容替换掉原来的内容,编译成可执行文件StackTrace后执行输出结果入下:

./StackTrace(GetStackTrace(std::string*)+0x27) [0x403720]
./StackTrace(TestFunc(std::string&, int)+0x2a) [0x4038c0]
./StackTrace(main+0x2d) [0x4038ef]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fb9d560bde5]
./StackTrace() [0x403279]

以上输出结果在代码调试中能给我们带来很多的信息,但是还是少了一些辅助信息,例如:文件名、函数所在文件的代码行、进程或者线程号(这个在多线中很重要)。更多内容可以参考开源项目libunwind或者google-coredumper。