×

breakpad: Native crash 日志收集工具

96
difcareer
2016.12.30 10:38* 字数 795

前言

现在大部分应用都会有Java层的崩溃日志收集机制,一般就是程序crash后,展示一个上报界面,用户点击就上传了。
但是Native程序crash了,很少有做处理的,几个方面原因:

  1. 大部分应用不用C/C++编写,或者说用也只是很小的一个模块。
  2. 编写一个高质量的Native crash工具本身就不是很容易,搞不好自己还引发崩溃:( 。

正题

今天发现了Google开源了一个工具:breakpad,研究了一下,感觉不错的,特意分享一下。

breakpad工作原理

breakpad并不是只针对Android,而是一个全平台的C/C++程序的崩溃日志收集工具,适配了Windows/MacOX/Linux,当然也支持了Android。

先来看张工作原理图:

breakpad工作原理

解释一下:
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同名。

上面所说对应图中:

  1. 利用符号表和崩溃文件还原崩溃时堆栈,这里面会包含崩溃时的线程(并会标明是哪个线程崩溃),线程在源码中的崩溃点,崩溃原因,加载的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,依葫芦画瓢即可。

Android
Web note ad 1