编写Java代码的时候可以通过打印Throwable来看函数的调用关系。但是在C++里面没有相关的接口来实现。所以我们只用通过C接口backtrace/backtrace_symbols来实现相关功能。和网上的大多数介绍一下,如果需要backtrace_symbols解析出相关trace,需要添加编译选项:-rdynamic。在Obotcha中,有一个单独的Exception类的printStack就有这个功能,代码如下:
void _Exception::printStack() {
printf("-----------------[maps dump start]--------------- \n");
char *path = "/proc/self/maps";
int fd = open(path,O_RDONLY);
if(fd >=0) {
char *buff[1024*256];
read(fd,buff,1024*256);
close(fd);
printf("%s \n",buff);
}
printf("-----------------[maps dump end]---------------- \n");
printf("\n");
printf("\n");
printf("-----------------[stack dump start]-------------- \n");
int size = 16;
void * array[16];
int stack_num = backtrace(array, size);
char ** stacktrace = backtrace_symbols(array, stack_num);
for (int i = 0; i < stack_num; ++i) {
printf("%s\n", stacktrace[i]);
}
printf("-----------------[stack dump end]--------------- \n");
free(stacktrace);
}
我们来看一下调用后出现的打印:
-----------------[maps dump start]---------------
555a4f0cc000-555a4f0cf000 r-xp 00000000 08:05 10621902 /home/sunliwang/mysource/Obotcha/test/testFileNotFoundException/mytest
555a4f2ce000-555a4f2cf000 r--p 00002000 08:05 10621902 /home/sunliwang/mysource/Obotcha/test/testFileNotFoundException/mytest
555a4f2cf000-555a4f2d0000 rw-p 00003000 08:05 10621902 /home/sunliwang/mysource/Obotcha/test/testFileNotFoundException/mytest
555a509ad000-555a509ce000 rw-p 00000000 00:00 0 [heap]
7f5f76cc1000-7f5f76e5e000 r-xp 00000000 08:05 5772002 /lib/x86_64-linux-gnu/libm-2.27.so
7f5f76e5e000-7f5f7705d000 ---p 0019d000 08:05 5772002 /lib/x86_64-linux-gnu/libm-2.27.so
7f5f7705d000-7f5f7705e000 r--p 0019c000 08:05 5772002 /lib/x86_64-linux-gnu/libm-2.27.so
7f5f7705e000-7f5f7705f000 rw-p 0019d000 08:05 5772002 /lib/x86_64-linux-gnu/libm-2.27.so
7f5f7705f000-7f5f77062000 r-xp 00000000 08:05 5771962 /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77062000-7f5f77261000 ---p 00003000 08:05 5771962 /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77261000-7f5f77262000 r--p 00002000 08:05 5771962 /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77262000-7f5f77263000 rw-p 00003000 08:05 5771962 /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77263000-7f5f7727d000 r-xp 00000000 08:05 5772072 /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7727d000-7f5f7747c000 ---p 0001a000 08:05 5772072 /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7747c000-7f5f7747d000 r--p 00019000 08:05 5772072 /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7747d000-7f5f7747e000 rw-p 0001a000 08:05 5772072 /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7747e000-7f5f77482000 rw-p 00000000 00:00 0
7f5f77482000-7f5f77489000 r-xp 00000000 08:05 5772080 /lib/x86_64-linux-gnu/librt-2.27.so
7f5f77489000-7f5f77688000 ---p 00007000 08:05 5772080 /lib/x86_64-linux-gnu/librt-2.27.so
7f5f77688000-7f5f77689000 r--p 00006000 08:05 5772080 /lib/x86_64-linux-gnu/librt-2.27.so
7f5f77689000-7f5f7768a000 rw-p 00007000 08:05 5772080 /lib/x86_64-linux-gnu/librt-2.27.so
7f5f7768a000-7f5f77871000 r-xp 00000000 08:05 5771939 /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77871000-7f5f77a71000 ---p 001e7000 08:05 5771939 /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77a71000-7f5f77a75000 r--p 001e7000 08:05 5771939 /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77a75000-7f5f77a77000 rw-p 001eb000 08:05 5771939 /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77a77000-7f5f77a7b000 rw-p 00000000 00:00 0
7f5f77a7b000-7f5f77a92000 r-xp 00000000 08:05 5767842 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77a92000-7f5f77c91000 ---p 00017000 08:05 5767842 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77c91000-7f5f77c92000 r--p 00016000 08:05 5767842 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77c92000-7f5f77c93000 rw-p 00017000 08:05 5767842 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77c93000-7f5f77e0c000 r-xp 00000000 08:05 13369538 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f77e0c000-7f5f7800c000 ---p 00179000 08:05 13369538 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f7800c000-7f5f78016000 r--p 00179000 08:05 13369538 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f78016000-7f5f78018000 rw-p 00183000 08:05 13369538 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f78018000-7f5f7801c000 rw-p 00000000 00:00 0
7f5f7801c000-7f5f787ca000 r-xp 00000000 08:05 9721046 /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
7f5f787ca000-7f5f789ca000 ---p 007ae000 08:05 9721046 /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
-----------------[maps dump end]----------------
-----------------[stack dump start]--------------
../../out/lib/libobotcha.so(_ZN7obotcha10_Exception10printStackEv+0xf4) [0x7f5f7842b1f8]
./mytest(main+0x146) [0x555a4f0cd4d0]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f5f776abb97]
./mytest(+0x12aa) [0x555a4f0cd2aa]
-----------------[stack dump end]---------------
呵呵和java的trace比起来,上面的这个可读性很差,但是能有trace和map就很不错了,我们就不要挑剔了。重点来了,接下来,我们通过2中方法来追一下函数调用的栈哈。
方法1(通过函数的偏移0xf4来看函数呢运行到哪个位置):
步骤1:
使用c++filt来解析出_ZN7obotcha10_Exception10printStackEv具体是哪个函数。
命令:
c++filt _ZN7obotcha10_Exception10printStackEv
结果:
obotcha::_Exception::printStack()
步骤2:
解析libobotcha.so中的printStack的地址,
命令:
readelf -a ../../out/lib/libobotcha.so |grep -i _ZN7obotcha10_Exception10
感觉是elfread好像没有把函数名称全打印出来所以只能通过前面的字符串匹配,结果如下:
5993: 000000000040f0f2 17 FUNC GLOBAL DEFAULT 12 _ZN7obotcha10_Exception10
6193: 000000000040f0c4 45 FUNC GLOBAL DEFAULT 12 _ZN7obotcha10_Exception10
14202: 000000000040f104 400 FUNC GLOBAL DEFAULT 12 _ZN7obotcha10_Exception10
14942: 000000000040f104 400 FUNC GLOBAL DEFAULT 12 _ZN7obotcha10_Exception10
20681: 000000000040f0f2 17 FUNC GLOBAL DEFAULT 12 _ZN7obotcha10_Exception10
23242: 000000000040f0c4 45 FUNC GLOBAL DEFAULT 12 _ZN7obotcha10_Exception10
这样的话,会出现6个结果,实际上,这6个就是Exception的6个接口。这样,没办法,我们只好用addr2line来一个个看哪个是printStack函数。
命令:
addr2line -e ../../out/lib/libobotcha.so 40f0f2
结果:
/home/dji/mysource/Obotcha/lang/Exception.cpp:36
查看代码,36行不是printStack.继续找,我们会找到4f104这个地址是对应的printStack函数。
步骤3:
printStack的起始40f104找到了,然后再加上偏移0xf4,结果:40f1f8
再用addr2line看一下是哪一行:
/home/dji/mysource/Obotcha/lang/Exception.cpp:57
这个就是抓取backtrace的点。
方法2(通过读取smap中so库加载的起始地址算出偏移)
步骤1:
查找libobotcha.so 加载的起始地址:
7f5f7801c000-7f5f787ca000 r-xp 00000000 08:05 9721046 /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
7f5f787ca000-7f5f789ca000 ---p 007ae000 08:05 9721046 /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
所以起始地址是7f5f7801c000
步骤2:
../../out/lib/libobotcha.so(_ZN7obotcha10_Exception10printStackEv+0xf4) [0x7f5f7842b1f8]
中的地址减去步骤1中的起始地址,就是函数指针的偏移了。
0x7f5f7842b1f8 - 7f5f7801c000 = 40f1f8
这个地址和方法1计算出来是一样的。接下来用addr2line就可以计算了。
总结:
方法2比方法1简单了很多,但是方法1可以更加清晰的看到so库中函数/全局变量的地址,在有些稳定性问题(例如指向全局数据的指针越界)上比较有用。具体的案例下次有机会可以show给大家看一下。