libuv 编译选项:
CFLAGS='-g -O0 -funwind-tables' ./configure --disable-silent-rules --disable-udev --enable-debug-log
make -j8 V=1
其中 -funwind-tables 可以打印详细调用堆栈。
写程序使用:t1.cc
#include <ux.h>
#include <unistd.h>
#include <signal.h>
#include <execinfo.h>
#include <cstdio>
#include <cstdlib>
#include <thread>
using namespace std;
void handler(int sig)
{
void* buffer[10];
FILE* fp = fopen("callstack.txt", "w");
backtrace_symbols_fd(buffer, backtrace(buffer, sizeof(buffer) / sizeof(buffer[0])), fileno(fp));
fclose(fp);
_exit(1);
}
int main(void)
{
signal(SIGSEGV, handler);
auto loop = uv_default_loop();
uv_tcp_t *tcp = (uv_tcp_t*)-1;
uv_tcp_init(loop, tcp);
return 0;
}
/* 此处使用静态库,转化堆栈地址更直接,如果是动态库,首先要找到偏移地址,再加上移动地址,才是真正地址
g++ -g -O0 -I./build/include -o t1 t1.cc ./.libs/libux.a -pthread
*/
运行:./t1
查看 callstack.txt
#如果是动态库,显示如下,拿第4行来看:这时真正地址为 uv_tcp_init_ex+0x74
#可以使用 nm /home/test/Desktop/libuv/.libs/libux.so.1 | grep uv_tcp_init_ex 来找到偏移地址(为16进制),这时再加上 0x74 即为真正地址
./t1(+0xc1c)[0x557f338c1c]
linux-vdso.so.1(__kernel_rt_sigreturn+0x0)[0x7faaa866a0]
/home/test/Desktop/libuv/.libs/libux.so.1(+0x24510)[0x7faaa32510]
/home/test/Desktop/libuv/.libs/libux.so.1(uv_tcp_init_ex+0x74)[0x7faaa35da0]
/home/test/Desktop/libuv/.libs/libux.so.1(uv_tcp_init+0x20)[0x7faaa35e38]
./t1(+0xc84)[0x557f338c84]
/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe0)[0x7faa8b46e0]
./t1(+0xb04)[0x557f338b04]
#如果是静态库就简单多了,显示的就是真实地址
./t1(+0x3e2c)[0x556a706e2c]
linux-vdso.so.1(__kernel_rt_sigreturn+0x0)[0x7f934366a0]
./t1(+0x10e7c)[0x556a713e7c]
./t1(+0x1470c)[0x556a71770c]
./t1(+0x147a4)[0x556a7177a4]
./t1(+0x3e94)[0x556a706e94]
/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe0)[0x7f932856e0]
./t1(+0x3d14)[0x556a706d14]
使用 addr2line 查看堆栈信息:
addr2line -e ./t1 0x147a4
或者使用下面的工具查看:addrtool.cc
/*
* Compile with g++ -std=c++11 -O2 -o addrtool addrtool.cc
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <regex>
#include <string>
using namespace std;
#define ADDR2LINE "addr2line"
string do_addr2line(const char *exec_file, const string &address)
{
char cmd[256];
char output[1035];
snprintf(cmd, sizeof(cmd), "%s -e %s %s", ADDR2LINE, exec_file, address.c_str());
FILE *fp = popen(cmd, "r");
if (!fp) return string();
if (!fgets(output, sizeof(output)-1, fp))
{
pclose(fp);
return string();
}
pclose(fp);
return string(output);
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("Usage: %s <exec_file> <callstack_dump>\n", argv[0]);
exit(1);
}
FILE *fp = fopen(argv[2], "r");
if (!fp)
{
fprintf(stderr, "Error: open %s error: %s\n", argv[1], strerror(errno));
exit(1);
}
char line[1025];
smatch sm1, sm2;
const regex pat1("\\(\\+(0x[0-9a-f]+)\\)"), pat2("^\\?{0,}\\:\\?{0,}");
printf("CallStack is\n==================================\n");
while (fgets(line, sizeof(line)-1, fp) != nullptr)
{
auto s = string(line);
if (regex_search(s, sm1, pat1))
{
auto output = do_addr2line(argv[1], sm1[1].str());
printf("%s", output.empty() || regex_search(output, sm2, pat2) ? line : output.c_str());
}
else
printf("%s", line);
}
fclose(fp);
return 0;
}
用法:addrtool ./t1 callstack.txt
test:~/Desktop/libuv$ ~/addrtool t1 callstack.txt
CallStack is
==================================
/home/test/Desktop/libuv/t1.cc:17
linux-vdso.so.1(__kernel_rt_sigreturn+0x0)[0x7f934366a0]
/home/test/Desktop/libuv/uv/unix/stream.c:82
/home/test/Desktop/libuv/uv/unix/tcp.c:133
/home/test/Desktop/libuv/uv/unix/tcp.c:147
/home/test/Desktop/libuv/t1.cc:28
/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0xe0)[0x7f932856e0]
./t1(+0x3d14)[0x556a706d14]