Android的Crash 问题分为Java 的Runtime Crash 和使用Jni而导致的Native 代码Crash。Java Runtime Crash是非常容易定位的,使用 :
adb logcat > $path
在其中就可以很容易地看出Java Crash的问题代码位置。
然而,Native Crash 则没有那么容易定位,首先 一般relese版本的动态链接So库都是不带符号的,这就导致了无法进行分析和调试,同时,要知道一点,在Linux平台的Crash,也可以说进程的崩溃,一般都是内核直接发送signal 杀掉指定的进程的。
从上面的表述中,我们可以知道这么几点:
- 想要调试动态So库中的代码,首先你得有一份debug代码,即使用gcc 使用 -g 参数来编译的代码,这样编译出的So库是带有符号信息的,所以So的大小也大得多。
- 想要分析Native Crash ,那么需要在Linux信号到达指定进程杀死它之前,截获这个信号,并进行相应的处理,使得我们可以在进程被杀死时,收集有用的信息
那么,应该怎么做?
首先,在发布版本的时候,需要将使用的动态链接库编译两份代码,一份不带符号信息的relese版本,一份带符号信息的debug版本。relese版本是用来给用户使用的,debug版本是用于开发人员进行问题定位的。
然后,我们需要一份对于信号处理的信号处理器handler,这个过程说起来简单,但是工作还是很繁琐的,好在有相应的开源库供我们使用,那就是Google的BreakPad。使用该项目后,就可以相对来说较为简单地进行Crash信息收集流程。这个Crash信息不是Android独有的,是Linux中就有的CoreDump文件。
刚才说了,即使你拥有CoreDump文件,但是这时候的文件几乎可以说是没有作用的,因为没有符号信息,只有函数地址以及偏移地址,这样是得不出什么有用的信息的。这时候,我们第一步的debug版本So库就发挥了作用,使用它来将地址以及偏移地址进行映射你,就可还原出Crash时候的调用堆栈信息,这时候,配合我们自己加的日志以及对业务代码的理解就可以比较方便地定位出问题之所在了。
以上所说的,是处理Natiev Crash 较为通用的流程,使用BreakPad 将整个处理流程更加简化,我觉得依照此思路,朋友们估计会有更好的点子也说不一定。
关于BreakPad的使用,有什么问题,欢迎各位一起讨论。