安装addr2line后才能有更详细的信息
common/stacktrace.h
直接复制过去用。注意编译选项。
- 监听SIGSEGV信号,即段错误信号收到后,输出调用栈。
- 捕获std::terminate异常,设置了handler,handler中打调用栈。处理了C++抛异常导致的崩溃
- 输出行号:加上编译选项-g和链接选项-rdynamic.保证打出来的二进制符号表中有函数名和行号
- VMA地址转换:backtrace_symbols返回的地址可能是不是二进制中的地址。因为不同链接器可能链接的起始相对地址不同。这还需要加上链接选项-ldl。通过dladdr2获取二进制中的地址
- 使用addr2line将backtrace_symbols返回的地址转为好看的代码,函数名,行号信息。这要求系统安装了addr2line
- popen用来调用addr2line,并拿到返回结果
#pragma once
#include <signal.h>
#include <unistd.h>
#include <execinfo.h>
#include <malloc.h>
#include <cstdio>
#include <cstring>
#include <link.h>
namespace AdsonLib {
class PrintCoreStack{
public:
static void RegisterCoreHandler() {
struct sigaction sig;
memset(&sig, 0, sizeof(sig));
sig.sa_handler = &PrintCoreStack::CoreHandler;
sig.sa_flags |= SA_RESETHAND; //one-time only
sigaction(SIGSEGV, &sig, NULL);
std::set_terminate([](){
PrintStack();
std::abort();
});
}
private:
static std::string RunCmd(const std::string &cmd) {
FILE* fd = popen(cmd.c_str(), "r");
if(fd == NULL) return "";
char buf[2048]={'\0'};
std::string line;
while(fgets(buf, 2048, fd)){
line += buf;
}
while(line.size() > 0 && line.back() == '\n'){
line.pop_back();
}
return line;
}
static std::string Int2Hex(size_t d){
char tmp[128];
snprintf(tmp, 128, "%p", d);
return tmp;
}
static std::string DetailInfo(const std::string &binary_file, size_t vma_addr){
return RunCmd("addr2line -f -C -p -e " + binary_file + " " + Int2Hex(vma_addr));
}
static size_t AddrToVMA(size_t addr){
Dl_info dinfo;
link_map* lm;
dladdr1((void*)addr, &dinfo,(void**)&lm, RTLD_DL_LINKMAP);
return addr - lm->l_addr;
}
static void PrintStack() {
char *full_stack[100] = {0};
int depth = backtrace(reinterpret_cast<void**>(full_stack), sizeof(full_stack)/sizeof(full_stack[0]));
if (depth){
char** syms = backtrace_symbols(reinterpret_cast<void**>(full_stack), depth);
if (syms){
for(size_t i = 0; i < depth; i++){
int j = 0;
while(syms[i][j] != '(') j++;
std::string f(syms[i], j);
size_t vma_addr = AddrToVMA((size_t)full_stack[i]);
printf("=== [%lu]:%s [vma: %p] [%s]\n", (i+1), syms[i], vma_addr, DetailInfo(f, vma_addr).c_str());
}
}
free(syms);
};
}
static void CoreHandler(int signo){
PrintStack();
raise(SIGSEGV);
}
};
} //namespace AdsonLib
使用
trans_acc.cpp
使用起来非常简单,在main函数里调用RegisterCoreHandler即可
#include "stacktrace.h"
int main(int argc, char *argv[]) {
PrintCoreStack::RegisterCoreHandler();
throw 333;
}
bazel BUILD文件
注意链接要加 "-ldl"和"-rdynamic"
cc_library(
name = "common",
srcs = glob(["common/*.cpp"]),
hdrs = glob(["common/*.h"]),
)
cc_binary(
name = "trans_acc",
srcs = ["trans_acc.cpp"],
deps = [
":common",
],
linkopts = [
"-lpthread", "-ldl", "-rdynamic",
]
)
.bazel文件
注意编译要加-g选项
build --cxxopt="-std=c++17" --cxxopt="-g" -c dbg
build.sh
bazel build :trans_acc
运行结果
可以看出,前边打出来的是backtrace返回 symbold.最后一个中括号里返回的是addr2line的详细信息。都打出来是因为防止addr2line转换失败。
# bazel-bin/trans_acc
=== [1]:bazel-bin/trans_acc(_ZN8AdsonLib14PrintCoreStack10PrintStackEv+0x46) [0x40b90a] [vma: 0x40b90a] [AdsonLib::PrintCoreStack::PrintStack() at /proc/self/cwd/common/stacktrace.h:54]
=== [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]
=== [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]
=== [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]
=== [5]:/lib64/libstdc++.so.6(+0x91e61) [0x7f0e2bd69e61] [vma: 0x91e61] [std::terminate() at ??:?]
=== [6]:/lib64/libstdc++.so.6(+0x92094) [0x7f0e2bd6a094] [vma: 0x92094] [__cxa_throw at ??:?]
=== [7]:bazel-bin/trans_acc(_Z7TestLogv+0x428) [0x40a67b] [vma: 0x40a67b] [TestLog() at /proc/self/cwd/trans_acc.cpp:48 (discriminator 10)]
=== [8]:bazel-bin/trans_acc(main+0x19) [0x40a783] [vma: 0x40a783] [main at /proc/self/cwd/trans_acc.cpp:53]
=== [9]:/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f0e2b412555] [vma: 0x22555] [__libc_start_main at ??:?]
=== [10]:bazel-bin/trans_acc() [0x409bd9] [vma: 0x409bd9] [_start at ??:?]
Aborted (core dumped)