最近项目出现了断错误,由于程序deamon运行在arm上,所以想找到一种方式,找到端错误的出错位置,然后找到了backtrace
,但是后来发现安卓的NDK不支持execinfo.h
,要想使用的话就要自己去提取相关的库到NDK中了,日后有空在去搞吧,虽然在本项目上面用不到,但是还是总结一下把,下一篇去介绍gdb在arm平台上的使用。
实例程序
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stddef.h>
#include <execinfo.h>
void signal_handle()
{
void *buf[32] = {0};
char **strings;
int i;
size_t size;
size = backtrace(buf, 32);
if (size < 0)
printf("backtrace error\n");
else
fprintf(stdout, "backtrace size %zd\n", size);
strings = backtrace_symbols(buf, size);
if (strings == NULL) {
perror("backtrace_symbols.");
exit(EXIT_FAILURE);
}
for(i = 0; i < size; i++) {
fprintf(stdout, "%s\n", strings[i]);
}
free(strings);
strings = NULL;
exit(0);
}
void func_c()
{
*((volatile char *)0x0) = 0x9999;
}
void func_b()
{
func_c();
}
void func_a()
{
func_b();
}
int main()
{
if (signal(SIGSEGV, signal_handle) == SIG_ERR)
perror("cat not catch signal");
func_a();
return 0;
}
int backtrace(void **buffer, int size); | backtrace()函数的返回值为buffer中的条目数量,这个值不一定等于size,因为如果为得到完整回溯信息而将size设置的足够大,则该函数的返回值为buffer中实际得到的返回地址数量。 |
---|---|
char **backtrace_symbols(void *const *buffer, int size); | 通过backtrace()函数得到buffer之后,backtrace_symbols()可以将其中的返回地址都对应到具体的函数名,参数size为buffer中的条目数。backtrace_symbols()函数可以将每一个返回值都翻译成“函数名+函数内偏移量+函数返回值”,经过翻译后的函数回溯信息放到backtrace_symbols()的返回值中,如果失败则返回NULL。需要注意,返回值本身是在backtrace_symbols()函数内部进行malloc的,所以必须在后续显式地free掉 |
void backtrace_symbols_fd(void *const *buffer, int size, int fd); | backtrace_symbols_fd()的buffer和size参数和backtrace_symbols()函数相同,只是它翻译后的函数回溯信息不是放到返回值中,而是一行一行的放到文件描述符fd对应的文件中 |
声明:
- 1、在编译过程在不能添加非零的优化等级,否则会使得不能得到正确的程序栈信息,加-g将关闭所有优化信息
- 2、在编译的时候需要加上-rdynamic选项让链接器将所有符号添加到动态符号表中,这样才能将函数地址翻译成函数名。另外,这个选项不会处理static函数,所以,static函数的符号无法得到
- 3、内联函数没有栈帧,它在编译过程中被展开在调用的位置
- 4、尾调用优化(Tail-call Optimization)将复用当前函数栈,而不再生成新的函数栈,这将导致栈信息不能正确被获取
生成backtrace
-
cjj@cjj-MyThink:~/mytoy/调试方式/backtrace$: gcc -g -rdynamic test.c -o backtrace
-
cjj@cjj-MyThink:~/mytoy/调试方式/backtrace$ ./backtrace
backtrace size 8
./backtrace(signal_handle+0x45) [0x400a9b]
/lib/x86_64-linux-gnu/libc.so.6(+0x354b0) [0x7f16044024b0]
./backtrace(func_c+0x9) [0x400b8a]
./backtrace(func_b+0xe) [0x400b9e]
./backtrace(func_a+0xe) [0x400baf]
./backtrace(main+0x2d) [0x400bdf]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f16043ed830]
./backtrace(_start+0x29) [0x400989]
- cjj@cjj-MyThink:~/mytoy/调试方式/backtrace$ addr2line -e backtrace 0x400b8a
/home/cjj/mytoy/调试方式/backtrace/test.c:34
可以看出来在test.c文件的第34行出现了断错误
**后续:**如果能够看到这里,那么我再多说一点,之前说在arm上面没有找到去backtrace调试断错误,但是后来我发现可以在arm上去调试。
上方式:
一般在安卓下出现程序断错误,可以通过logcat去看,在main log里面,关键字是“DEBUG”
一般文件是被保存在/data/tombstones/里面,名字为tombstones_xxx
工具及使用说明:
使用cjj@cjj-MyThink:~$ arm-linux-androideabi-addr2line
这里我用的是安卓板子里面跑得测试程序,这个测试程序是使用arm-linux-androideabi-gcc编译的
该工具可以根据
debug信息中提供的address(如#00 PC xxxxxxxx),直接定位到代码行。使用及分析过程如下:在debug信息中看到,
#00 pc 0000bb96 /system/lib/libreference-ril.so
#01 pc 000049e6 /system/lib/libreference-ril.so
#02 pc 000070d0 /system/lib/libreference-ril.so
#03 pc 000052a6 /system/lib/libril.so
查看第一条出错信息:
arm-linux-androideabi-addr2line –f –e libreference-ril.so 0000bb96
at_response_free
atchannel_mch.c: 1071
这告诉我们出错的代码在文件
atchannel_mch.c的at_response_free中,在1071行;
**注意:**
如果出现转换的地址显示为 " ? ? 0 “(一个so库的错误地址信息跑到另外一个so库去查,当然查不到,除非两个错误位置在两个so库中的堆栈地址相同,查不到的时候,就会输出” ? ? 0 "),所以`一定要用跟你调试应用程序相同的库文件`.PC上自带的有addr2line,直接去调试也能显示哪个函数出现的断错误,但是我自己发现不能打印行号,以后找找原因,方式与使用NDK的调试工具相同`addr2line –f –e libreference-ril.so 0000bb96 `。