一、addr2line简介
1、addr2line可以通过解析地址来定位异常发生在哪个文件,哪个函数,哪一行;不过只对编译时带-g参数的进程或者库文件有效,因此想使用addr2line定义进程发生crash时的位置,需要在编译时带上-g参数编译。
2、可以通过addr2line --h指令查看addr2line各参数的说明:
这里用的最多的参数是
-e:指定需要转换地址的可执行文件名(默认文件是a.out)
-f:显示函数名
二、简单示例
#include
void print_crash(void)
{
char* s = "hello world!";
*s = 'H';
}
int main()
{
print_crash();
return 0;
}
[2513891.249108] test_has_g[50957]: segfault at 4005a4 ip 00000000004004fd sp 00007ffc1c2e27e0 error 7 in test_has_g[400000+1000]
[2513899.702424] test_no_g[51013]: segfault at 4005a4 ip 00000000004004fd sp 00007fff6425a8d0 error 7 in test_no_g[400000+1000]
从报错的地址来看,编译时带-g跟不带-g报的地址都是00000000004004fd,这现象说明在程序正常运行的项目中可以不带-g来编译模块,当出现问题时再加上带-g参数来编译模块,然后用addr2line工具分析带-g参数编译出来的文件,因为带-g参数编译出来的文件比不带-g参数编译出来的文件大很多,所以在机器运行中的文件要不带-g参数编译。
从addr2line -e test_has_g 00000000004004fd -f跟addr2line -e test_no_g 00000000004004fd -f
的执行结果可以看出,不带-g参数编译的test_no_g定位不到异常的位置,打印信息是??:?;带-g参数编译的进程test_has_g可以定位到异常的位置是test.c:5;就是test.c文件中的第5行出错,第5行代码是*s = 'H';这里报错是因为程序被装载时,系统把“hello world” 连同其它的字符串和const型数据放入到内存的只读区。执行时,一个变量s被设为指向该字符串的位置,当再试图向该位置写时,就会产生段错误。简单理解就是*s只可以读,不可以写。
三、Android中的示例
1、在Android.bp或者Android.mk添加-g跟strip信息,不然使用addr2line定位不到异常位置
- cflags: ["-g"],
- cppflags: ["-g"],
- strip: {
- none:true
- },
- LOCAL_CFLAGS += -g
- LOCAL_CPPFLAGS += -g
- LOCAL_STRIP_MODULE :=false
2、在墓碑tombstone_00文件中找到报错的地址及对应文件,这里报文的文件是/system/lib64/libandroid_servers.so,地址是0000000000037918。
#04 pc 0000000000037918 /system/lib64/libandroid_servers.so (android::server::BroadcastRadio::BroadcastRadioService::nativeOpenTuner(_JNIEnv*, _jobject*, long, int, _jobject*, bool, _jobject*)+1520)
这里是收音机服务异常导致system_server进程崩掉,通过addr2line工具定位到具体出错的位置。
3、addr2line定位出错位置
在Android.bp或者Android.mk添加-g跟strip信息后,重新编译frameworks/base/services目录得到新的/system/lib64/libandroid_servers.so库文件,然后对该库文件进行如下操作:
定位到异常的位置是:
frameworks/base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp:276