Android Native崩溃排查实战:用addr2line精准定位SIGSEGV错误行号(附完整命令行)
调试Android应用时,最让人头疼的莫过于那些来自Native层的崩溃。屏幕上突然弹出一个“应用已停止运行”,而日志里只有一堆令人费解的十六进制地址和寄存器信息,尤其是那个经典的signal 11 (SIGSEGV)。对于习惯了Java层清晰堆栈的开发者来说,这感觉就像被蒙上眼睛在迷宫里找路。但别担心,只要掌握了正确的工具链,这些看似神秘的崩溃日志,恰恰是通往问题根源最直接的线索。这篇文章,就是为你准备的Native崩溃“破译”指南,我们将深入实战,一步步拆解如何利用addr2line这把利器,将崩溃地址精准定位到C/C++代码的具体行号,让你在面对SIGSEGV时不再束手无策。
1. 理解SIGSEGV与崩溃日志:从乱码中寻找线索
当你的应用在Native层发生崩溃时,Android系统会生成一份详细的崩溃报告,通常可以在logcat中通过A/DEBUG标签找到。这份报告是后续所有分析工作的基石。一个典型的SIGSEGV崩溃日志,核心信息通常集中在几个关键部分。
首先,你需要找到崩溃信号和地址。日志中类似 Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x720064 的行,直接指明了崩溃类型和触发崩溃的非法内存地址(fault addr)。SIGSEGV通常意味着程序试图访问它无权访问的内存,比如空指针解引用、访问已释放内存或数组越界。
其次,也是最关键的,是backtrace部分。它展示了崩溃发生时的函数调用栈。例如:
backtrace:
#00 pc 00720064 <unknown>
#01 pc 000db9a7 /data/app/.../lib/arm/libsuperplayer.so (VideoChannel::video_play()+258)
#02 pc 000db89b /data/app/.../lib/arm/libsuperplayer.so (task_video_play(void*)+14)
这里,pc后面的值就是程序计数器(Program Counter)的地址,它指向下一条要执行的指令。对于在动态库(.so文件)中发生的崩溃,我们需要关注的是偏移地址。注意看#01行,它包含两部分信息:
pc 000db9a7:这是在该so文件内部的偏移地址(十六进制)。/data/app/.../libsuperplayer.so (VideoChannel::video_play()+258):括号内的信息是链接器(或带部分调试信息的so)尝试解析出的函数名和大致偏移(+258字节),但这不够精确,无法定位到具体代码行。
注意:括号内的函数名和偏移(如
+258)是运行时链接器根据符号表解析的,它只能告诉你崩溃发生在哪个函数附近,但无法给出精确的源代码行号。要得到行号,必须使用带有完整调试符号的so文件和addr2line工具。
最后,日志开头的ABI: 'arm'信息至关重要,它指明了崩溃发生时进程运行的CPU架构。这直接决定了你应该使用NDK工具链中哪个版本的addr2line。
2. 准备调试环境:获取带符号的SO文件与正确工具链
工欲善其事,必先利其器。在开始解析地址之前,我们必须准备好两样东西:带有调试符号的Native库文件和与目标设备ABI匹配的addr2line工具。这一步如果出错,后续所有工作都将白费。
2.1 编译并定位带调试符号的SO文件
默认的Release构建会剥离调试符号以减小体积,因此我们需要一个特殊的构建变体。在CMake或ndk-build的配置中,确保为调试版本生成包含完整符号的so。
- CMake: 在
build.gradle的android块中,为externalNativeBuild的cmake配置a

&spm=1001.2101.3001.5002&articleId=151989526&d=1&t=3&u=f409e5806be047ec97735e121b14117e)
1542

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



