我们知道 android NDK 程序在崩溃时会生成一个 tombstone 的 backtrace (也可利用 ADB logcat 抓取),从这个 backtrace 中我们可以了解是哪个函数引发的崩溃,但是通常由于我们发布时都是 release 版,无法利用 backtrace 中的地址信息直接定位到源码和行号,当引发崩溃的错误不是很明显时,对于我们解决问题的帮助就不大。
这时通常我们是重编一个 debug 版本并设法重现 crash。这样做有个两个问题,一是如果我们不知道复现步骤,或者复现概率很低时,效率不高;二是我们不一定总能满足 crash 产生的条件,而一旦版本发布后,使用 debug 版本去替换用户场景中的 release 版又非常麻烦(甚至可能无法实现)。
事实上,有一个办法可以解决这个问题。我们知道,release 版本的库文件也是有 DYNAMIC SYMBOL TABLE 的,你可以使用命令 objdump -T -R <your so> (并重定向到一个文本文件中)来查看,然后你可以用同样的命令去查看 debug 版本的 DYNAMIC SYMBOL TABLE,通过比较,不难发现,他们的格式是一样的,你需要关注的只是第一列(symbol address)和最后一列(symbol name)。
接下来,你在 debug 版本的 DYNAMIC SYMBOL TABLE 中搜索引发崩溃的函数名,并记下它的 symbol address,然后,你只需要在这个地址上增加一个偏移量,就可以使用 addr2line 命令来定位源码和行号了。这个偏移量是多少?请注意 backtrace 中 #00 pc 的最后一列(使用括号括住),其中在函数名的后面有一个+N,这个N就是崩溃位置的偏移量(注意它是10进制数,而 symbol address 是16进制数,你需要先做转换才能相加)。
需要说明的是,由于 release 版通常都经过了优化,backtrace 中的偏移量并不一定非常准确,但离实际值差得不会太远,你可以使用 debug 版做一个对比测试就可以验证。现在你可以去试试这个方法了,希望这个方法能帮助你提高解决 crash 问题的效率。