1. 从崩溃日志到崩溃点:为什么只看日志远远不够
做Android开发,尤其是涉及到NDK、音视频、图像处理这些需要和C/C++打交道的领域,最头疼的莫过于遇到一个来自SO库的崩溃。你看着日志里那一串串十六进制的地址和不知所云的函数名,是不是感觉像在看天书?我刚开始接触这块的时候,也是两眼一抹黑,完全不知道从何下手。
一个典型的崩溃日志片段可能是这样的:
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x00000000
backtrace:
#00 pc 0003a8f4 /data/app/com.example.myapp/lib/arm/libnative-lib.so (Java_com_example_myapp_MainActivity_processImage+108)
#01 pc 00001234 /system/lib/libart.so (art_quick_generic_jni_trampoline+72)
这里的关键信息是 #00 这一行。它告诉我们,崩溃发生在 libnative-lib.so 这个动态库里,具体是在 Java_com_example_myapp_MainActivity_processImage 这个JNI函数偏移 108 字节的地方。但“偏移108字节”到底对应源代码的哪一行?是数组越界了,还是空指针解引用了?日志本身给不了答案。
这时候,很多朋友的第一反应可能是去翻代码,凭感觉猜是哪一行出了问题。我试过,效率极低,尤其是面对一个几百上千行的C++文件时,简直是大海捞针。所以,我们必须借助工具,把冷冰冰的机器地址,翻译成我们熟悉的“文件名:行号”。这个过程,就是符号解析。而 addr2line,正是完成这项翻译工作的“标配”工具。但工具用得好不好,里面门道可多了,比如你有没有带符号表的SO文件,你的编译环境对不对,都会直接影响结果。接下来,我就带你一步步拆解这个过程,把踩过的坑和总结的经验都分享给你。
2. 初阶定位:用好addr2line这把“快刀”
addr2line 是GNU Binutils工具集里的一个神器,它的任务非常单纯:给定一个可执行文件(或共享库)和一个地址,告诉你这个地址对应的源代码文件名和行号。听起来很简单,对吧?但要用好它,你得先准备好正确的“原料”。
2.1 准备工作:找到对的SO文件
这是最关键,也最容易出错的一步。你手机里崩溃的那个SO文件,和你电脑上编译出来的SO文件,很可能不是同一个!为什么?因为发布到市场的应用,为了减小安装包体积,通常会使用 strip 命令移除调试符号。而没有符号的SO文件,addr2line 是查不出行号的。
所以,你需要的是带有调试符号的、与崩溃环境完全匹配的SO文件。通常,它就在你的项目编译输出目录里。比如,如果你用的是CMake,调试版本的SO可能在 <项目目录>/app/build/intermediates/cmake/debug/obj/armeabi-v7a/ 下面。记住,一定要用和你编译打包时完全一致的版本,连一次git pull或代码修改都不要有,否则地址就对不上了。
2.2 实战解析:一条命令搞定行号定位
假设我们已经拿到了正确的、带符号的 libnative-lib.so 文件,并且崩溃地址是 0003a8f4。打开终端,进入该SO文件所在目录,执行:
arm-linux-androideabi-addr2line -f -C -e libnative-lib.so 0003a8f4
我们来拆解一下这个命令:
arm-lin


315

被折叠的 条评论
为什么被折叠?



