前言
现在大部分应用都会有Java层的崩溃日志收集机制,一般就是程序crash后,展示一个上报界面,用户点击就上传了。
但是Native程序crash了,很少有做处理的,几个方面原因:
- 大部分应用不用C/C++编写,或者说用也只是很小的一个模块。
- 编写一个高质量的Native crash工具本身就不是很容易,搞不好自己还引发崩溃:( 。
正题
今天发现了Google开源了一个工具:breakpad,研究了一下,感觉不错的,特意分享一下。
breakpad工作原理
breakpad并不是只针对Android,而是一个全平台的C/C++程序的崩溃日志收集工具,适配了Windows/MacOX/Linux,当然也支持了Android。
先来看张工作原理图:
解释一下:
breakpad其实一套解决方案,包含几个模块:
- client,编译进入项目中,随项目一起编译发布,发布出去的so是strip掉debug信息的。当在用户手机上崩溃的时候,client就收集信息,写入特定格式的崩溃文件。文件最后被收集到服务端。这个过程就是:
- server段工具,在server端,当你在编译so的时候,除了编译strip后的so,还得保留strip前的so。假如你是用gradle编译的话,两者都是存在的,分别在:app/build/intermediaters/transforms/mergejniLibs和stripDebugSymbol下:
找到未strip的so后,使用breakpad在编译出来的dump_syms
提取符号表:
dump_syms path/of/libxxx.so > libxxx.so.sym
注意libxxx.so.sym前面必须和libxxx.so同名。
上面所说对应图中:
- 利用符号表和崩溃文件还原崩溃时堆栈,这里面会包含崩溃时的线程(并会标明是哪个线程崩溃),线程在源码中的崩溃点,崩溃原因,加载的so,以及cpu信息等。
上面所说对应图中:
具体怎么使用操作呢?
a. 首先从符号表中的第一行提取标识符,比如:
MODULE Linux arm 489FF5B0639F40A4A961DDC068B5B0770 libnative-lib.so
就是489FF5B0639F40A4A961DDC068B5B0770
。
b. 建立如下目录结构:
|symbol
|--libnative-lib.so //用你的so名称创建子文件夹
|----489FF5B0639F40A4A961DDC068B5B0770 //用a中提取的标识符创建子文件夹
|------libnative-lib.so.sym //将前面提取的符号表放置在这里,注意文件名
注意:上述以libnative-lib.so
展示了文件目录结构,你需要根据自己的so名称做调整。为了简化这种重复且容易出错的工作,我已经将其自动化,见CrashDump。
c. 执行minidump_stackwalk name/of/xxx.dmp path/of/symbol > result.txt
d. result.txt
中就是可阅读的崩溃信息:
Operating system: Android
0.0.0 Linux 3.4.0-gd59db4e #1 SMP PREEMPT Mon Mar 17 15:16:36 PDT 2014 armv7l
CPU: arm
ARMv7 Qualcomm Krait features: swp,half,thumb,fastmult,vfpv2,edsp,neon,vfpv3,tls,vfpv4,idiva,idivt
4 CPUs
GPU: UNKNOWN
Crash reason: SIGSEGV
Crash address: 0x0
Process uptime: not available
Thread 0 (crashed)
0 libnative-lib.so!crash() [native-lib.cpp : 13 + 0x2]
r0 = 0x00000000 r1 = 0x00000001 r2 = 0xbef0d46c r3 = 0x532dbdca
r4 = 0x6dbfede0 r5 = 0x41a033d8 r6 = 0x00000004 r7 = 0xbef0d4d0
r8 = 0xbef0d4d8 r9 = 0x4160bca8 r10 = 0x41a033e8 r12 = 0x7591bed4
fp = 0xbef0d4ec sp = 0xbef0d3d4 lr = 0x758dd623 pc = 0x758dd596
Found by: given as instruction pointer in context
1 libnative-lib.so!Java_com_andr0day_ndktest_MainActivity_stringFromJNI [native-lib.cpp : 22 + 0x3]
r4 = 0x6dbfede0 r5 = 0x41a033d8 r6 = 0x00000004 r7 = 0xbef0d4d0
r8 = 0xbef0d4d8 r9 = 0x4160bca8 r10 = 0x41a033e8 fp = 0xbef0d4ec
sp = 0xbef0d3d8 pc = 0x758dd623
Found by: call frame info
2 libdvm.so + 0x1dd4e
r4 = 0x6dbfede0 r5 = 0x41a033d8 r6 = 0x00000004 r7 = 0x4160bcb0
r8 = 0xbef0d4d8 r9 = 0x4160bca8 r10 = 0x41a033e8 fp = 0xbef0d4ec
sp = 0xbef0d4d8 pc = 0x4156bd50
Found by: call frame info
......
Loaded modules:
0x400d9000 - 0x400dafff app_process ??? (main) (WARNING: No symbols, app_process, 231A2557AC947E46614FB6EAA0030AE80)
0x400dd000 - 0x400ebfff linker ??? (WARNING: No symbols, linker, BC3131FBBFE15F3BE40D5EBCF67A71AE0)
0x400f3000 - 0x400fbfff libcutils.so ???
0x400fe000 - 0x40100fff liblog.so ???
0x40103000 - 0x40149fff libc.so ??? (WARNING: No symbols, libc.so, FC9E828FFFAA5CE4E7C28E25988A9C1F0)
0x4015e000 - 0x4015efff libstdc++.so ???
0x4015f000 - 0x40160fff libstdc++.so ???
0x40161000 - 0x40178fff libm.so ???
...
从中看出:线程0在native-lib.cpp的13行处崩溃,原因是:SIGSEGV,cpu的信息等,足够用来定位问题。
- 其他辅助工具
breakpad还包含若干辅助工具,比如构建上述所说目录结构时,其实可以执行其自带的python命令得到,不再多说。
项目集成
不再多说,直接给出一个已经集成好的demo,依葫芦画瓢即可。