这里以一个实际的crash案例未demo进行分析和讲解。针对native的崩溃信息。一般来讲,较快的方式是直接检索到backtrace,然后通过分析和使用工具addr2line和 ndk-stack等定位到出问题的地方。这里截取了一段 崩溃日志,具体如下:
01-15 14:53:40.240 21741 21741 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-15 14:53:40.240 21741 21741 F DEBUG : Build fingerprint: 'Android/aosp_crosshatch/crosshatch:12/SP1A.210812.016.C1/eng.wangdsh.20221201.112332:userdebug/test-keys'
01-15 14:53:40.240 21741 21741 F DEBUG : Revision: 'MP1.0'
01-15 14:53:40.240 21741 21741 F DEBUG : ABI: 'arm64'
01-15 14:53:40.240 21741 21741 F DEBUG : Timestamp: 2023-01-15 14:53:39.511121659+0800
01-15 14:53:40.240 21741 21741 F DEBUG : Process uptime: 0s
01-15 14:53:40.240 21741 21741 F DEBUG : Cmdline: com.ags.test
01-15 14:53:40.240 21741 21741 F DEBUG : pid: 21179, tid: 21720, name: SL:21688_21707 >>> com.ags.test <<<
01-15 14:53:40.240 21741 21741 F DEBUG : uid: 10356
01-15 14:53:40.240 21741 21741 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc0
01-15 14:53:40.240 21741 21741 F DEBUG : Cause: null pointer dereference
01-15 14:53:40.240 21741 21741 F DEBUG : x0 0000006de705b3c0 x1 0000007010bf27cc x2 0000006d877a78c0 x3 0000006d4a1e79a8
01-15 14:53:40.240 21741 21741 F DEBUG : x4 0000000000000018 x5 0000006d7727f17d x6 3035343234303734 x7 0038303632303534
01-15 14:53:40.240 21741 21741 F DEBUG : x8 0000006ce0b67ef0 x9 00000000000000c0 x10 0000006d8702e000 x11 0000000000000030
01-15 14:53:40.240 21741 21741 F DEBUG : x12 000000000000000d x13 000000008ea8faa7 x14 0000000000000030 x15 0000006d4a1e7844
01-15 14:53:40.240 21741 21741 F DEBUG : x16 0000006ce0b68670 x17 0000006ce0b60f50 x18 0000006bd3d32000 x19 00000000000054b8
01-15 14:53:40.240 21741 21741 F DEBUG : x20 0000006e2705af10 x21 0000006cc2dd7dd8 x22 0000006cc2dd7e20 x23 0000006cc2dcc644
01-15 14:53:40.240 21741 21741 F DEBUG : x24 0000006cc2dcc1bb x25 0000006d877b0920 x26 0000006cc2dcc65f x27 0000006cc2dcc051
01-15 14:53:40.240 21741 21741 F DEBUG : x28 0000006e170f73d0 x29 0000006d4a1e7a30
01-15 14:53:40.240 21741 21741 F DEBUG : lr 0000006cc2dcfb04 sp 0000006d4a1e7a10 pc 0000006cc2dcfb0c pst 0000000060000000
01-15 14:53:40.240 21741 21741 F DEBUG : backtrace:
01-15 14:53:40.240 21741 21741 F DEBUG : #00 pc 0000000000006b0c /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #01 pc 0000000000007adc /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #02 pc 0000000000007e98 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #03 pc 00000000000082f4 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #04 pc 00000000000530e8 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsutils.so (SyncHandle_Loop+324) (BuildId: b828e7b2f3db33f9445278f8a9016949763625a2)
01-15 14:53:40.240 21741 21741 F DEBUG : #05 pc 00000000000b1910 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264) (BuildId: ba489d4985c0cf173209da67405662f9)
01-15 14:53:40.240 21741 21741 F DEBUG : #06 pc 00000000000513f0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: ba489d4985c0cf173209da67405662f9)
从提供的崩溃信息头中,也就是这一部分:
01-15 14:53:40.240 21741 21741 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-15 14:53:40.240 21741 21741 F DEBUG : Build fingerprint: 'Android/aosp_crosshatch/crosshatch:12/SP1A.210812.016.C1/eng.wangdsh.20221201.112332:userdebug/test-keys'
01-15 14:53:40.240 21741 21741 F DEBUG : Revision: 'MP1.0'
01-15 14:53:40.240 21741 21741 F DEBUG : ABI: 'arm64'
01-15 14:53:40.240 21741 21741 F DEBUG : Timestamp: 2023-01-15 14:53:39.511121659+0800
01-15 14:53:40.240 21741 21741 F DEBUG : Process uptime: 0s
01-15 14:53:40.240 21741 21741 F DEBUG : Cmdline: com.ags.test
01-15 14:53:40.240 21741 21741 F DEBUG : pid: 21179, tid: 21720, name: SL:21688_21707 >>> com.ags.test <<<
01-15 14:53:40.240 21741 21741 F DEBUG : uid: 10356
01-15 14:53:40.240 21741 21741 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc0
01-15 14:53:40.240 21741 21741 F DEBUG : Cause: null pointer dereference
我们可以得到以下基本信息:
从提供的崩溃信息头中,也就是中间这一部分:
01-15 14:53:40.240 21741 21741 F DEBUG : x0 0000006de705b3c0 x1 0000007010bf27cc x2 0000006d877a78c0 x3 0000006d4a1e79a8
01-15 14:53:40.240 21741 21741 F DEBUG : x4 0000000000000018 x5 0000006d7727f17d x6 3035343234303734 x7 0038303632303534
01-15 14:53:40.240 21741 21741 F DEBUG : x8 0000006ce0b67ef0 x9 00000000000000c0 x10 0000006d8702e000 x11 0000000000000030
01-15 14:53:40.240 21741 21741 F DEBUG : x12 000000000000000d x13 000000008ea8faa7 x14 0000000000000030 x15 0000006d4a1e7844
01-15 14:53:40.240 21741 21741 F DEBUG : x16 0000006ce0b68670 x17 0000006ce0b60f50 x18 0000006bd3d32000 x19 00000000000054b8
01-15 14:53:40.240 21741 21741 F DEBUG : x20 0000006e2705af10 x21 0000006cc2dd7dd8 x22 0000006cc2dd7e20 x23 0000006cc2dcc644
01-15 14:53:40.240 21741 21741 F DEBUG : x24 0000006cc2dcc1bb x25 0000006d877b0920 x26 0000006cc2dcc65f x27 0000006cc2dcc051
01-15 14:53:40.240 21741 21741 F DEBUG : x28 0000006e170f73d0 x29 0000006d4a1e7a30
01-15 14:53:40.240 21741 21741 F DEBUG : lr 0000006cc2dcfb04 sp 0000006d4a1e7a10 pc 0000006cc2dcfb0c pst 0000000060000000
其中:
x0-x29分别表示30个寄存器。x0-x29 寄存器是 ARM64 架构中的通用寄存器,用于存储临时数据、函数参数或局部变量等信息。每个寄存器的命名以 x 开头,后面跟着一个数字。这些寄存器整体说明如下:
这些寄存器在程序执行过程中起着关键的作用,用于存储数据、控制流程和传递参数等。它们的具体含义和使用方式可能会根据编程语言、编译器和操作系统的不同而有所差异。
最后一行,也是一组特殊的寄存器,这一组寄存器的含义解读如下:
这些寄存器的值提供了关于崩溃发生时处理器状态的信息。它们对于分析崩溃原因和定位问题可能会有所帮助,但需要结合符号解析和其他调试信息来进行更深入的分析。
接下来是我们查找错误中最好的入口,如下所示:
01-15 14:53:40.240 21741 21741 F DEBUG : backtrace:
01-15 14:53:40.240 21741 21741 F DEBUG : #00 pc 0000000000006b0c /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #01 pc 0000000000007adc /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #02 pc 0000000000007e98 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #03 pc 00000000000082f4 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #04 pc 00000000000530e8 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsutils.so (SyncHandle_Loop+324) (BuildId: b828e7b2f3db33f9445278f8a9016949763625a2)
01-15 14:53:40.240 21741 21741 F DEBUG : #05 pc 00000000000b1910 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264) (BuildId: ba489d4985c0cf173209da67405662f9)
01-15 14:53:40.240 21741 21741 F DEBUG : #06 pc 00000000000513f0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: ba489d4985c0cf173209da67405662f9)
根据这个backtrace 信息,可以看到它列出了导致崩溃的函数调用链。这些信息对于分析崩溃原因和定位问题非常有帮助。下面是对 backtrace 中主要内容的整体说明:
@1 libagsnetservice.so 中的函数调用链解读:
@2 libagsutils.so 中的函数调用链解读:
@3 libc.so 中的函数调用链:
这些函数调用链提供了导致崩溃的函数及其所在的库的信息,可以作为定位问题的起点。通过分析这些函数调用链,可以尝试确定导致崩溃的具体函数和库,以便进一步进行调试和修复。
那么怎么查看backtrace确定我们代码中哪一行出问题了呢,这就需要我们使用一些工具将 这些地址转换成具体的函数调用。接下来我们主要使用一些工具及具体的步骤来进行操作。
addr2line 是 GNU binutils 工具集中的一部分,用于将地址转换为源代码行号和函数名。它常用于调试和分析崩溃堆栈,以确定代码中出现问题的具体位置。addr2line 工具的主要功能是根据给定的可执行文件和地址,查找对应的源代码行号和函数名。它可以将机器代码地址映射到编译时生成的源代码位置,提供更具体的错误定位和调试信息。
使用 addr2line 工具进行地址转换的一般步骤如下:
@1 安装 GNU binutils:addr2line在Ubuntu 上可以运行以下命令来安装:
$sudo apt-get install binutils
@2 获取可执行文件和符号表文件:
确保有崩溃时生成的可执行文件(通常是APK或so文件)以及相应的符号表文件(.sym 文件)。符号表文件通常与可执行文件一起生成,并且应该与崩溃时的版本相匹配。
打开终端:打开终端或命令行界面,进入到包含可执行文件和符号表文件的目录。
@3 运行 addr2line 命令:
addr2line命令基本用法解读如下:
addr2line -f -e <可执行文件路径> <地址>
-f:显示完整的函数名和文件名。
-e:指定可执行文件的路径。
<地址>:要转换的地址,通常是崩溃堆栈中的地址。
例如,如果你的可执行文件名为 app.so,符号表文件名为 app.so,要转换的地址为 0x12345678,则命令如下:
$addr2line -f -e app.so 0x12345678
针对于该例子,针对0000000000006b0c地址,有:
$addr2line -f -e libagsnetservice.so -a 6b0c
以此类推,就可以得到完整的调用栈的错误了,也就将整个错误信息的地质值转换成代码错误的行数了。
@4 查看转换结果:
运行命令后,addr2line 将会输出对应地址的函数名和源代码行号信息,以及可能的内联函数信息。具体如下所示:
$addr2line -f -e libagsnetservice.so -a 6b0c
0x0000000000006b0c
_ZN4ags18NetServicer9startScanEi
/home/ags/test/core/.../NetService.cpp:473
可以根据这些信息来定位崩溃发生的具体代码行,以此类推,可以得到完整的崩溃栈信息。
注意:addr2line 工具适用于本地崩溃堆栈的地址转换,而不适用于远程崩溃报告。
ndk-stack 是 Android NDK(Native Development Kit)提供的一个命令行工具,用于将崩溃堆栈中的地址转换为可读的函数和源代码行号信息。它是开发和调试 Android 应用程序时的有用工具之一。使用 ndk-stack 工具可以帮助开发者定位和分析发生在 Native 层的崩溃问题,例如 C/C++ 代码或使用 JNI(Java Native Interface)与 Java 层交互的代码。
使用 ndk-stack 工具的一般步骤如下:
@1 获取崩溃日志信息(说明:直接复制崩溃信息到crash.log也是可以的)。就是这个信息:
01-15 14:53:40.240 21741 21741 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-15 14:53:40.240 21741 21741 F DEBUG : Build fingerprint: 'Android/aosp_crosshatch/crosshatch:12/SP1A.210812.016.C1/eng.wangdsh.20221201.112332:userdebug/test-keys'
01-15 14:53:40.240 21741 21741 F DEBUG : Revision: 'MP1.0'
01-15 14:53:40.240 21741 21741 F DEBUG : ABI: 'arm64'
01-15 14:53:40.240 21741 21741 F DEBUG : Timestamp: 2023-01-15 14:53:39.511121659+0800
01-15 14:53:40.240 21741 21741 F DEBUG : Process uptime: 0s
01-15 14:53:40.240 21741 21741 F DEBUG : Cmdline: com.ags.test
01-15 14:53:40.240 21741 21741 F DEBUG : pid: 21179, tid: 21720, name: SL:21688_21707 >>> com.ags.test <<<
01-15 14:53:40.240 21741 21741 F DEBUG : uid: 10356
01-15 14:53:40.240 21741 21741 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc0
01-15 14:53:40.240 21741 21741 F DEBUG : Cause: null pointer dereference
01-15 14:53:40.240 21741 21741 F DEBUG : x0 0000006de705b3c0 x1 0000007010bf27cc x2 0000006d877a78c0 x3 0000006d4a1e79a8
01-15 14:53:40.240 21741 21741 F DEBUG : x4 0000000000000018 x5 0000006d7727f17d x6 3035343234303734 x7 0038303632303534
01-15 14:53:40.240 21741 21741 F DEBUG : x8 0000006ce0b67ef0 x9 00000000000000c0 x10 0000006d8702e000 x11 0000000000000030
01-15 14:53:40.240 21741 21741 F DEBUG : x12 000000000000000d x13 000000008ea8faa7 x14 0000000000000030 x15 0000006d4a1e7844
01-15 14:53:40.240 21741 21741 F DEBUG : x16 0000006ce0b68670 x17 0000006ce0b60f50 x18 0000006bd3d32000 x19 00000000000054b8
01-15 14:53:40.240 21741 21741 F DEBUG : x20 0000006e2705af10 x21 0000006cc2dd7dd8 x22 0000006cc2dd7e20 x23 0000006cc2dcc644
01-15 14:53:40.240 21741 21741 F DEBUG : x24 0000006cc2dcc1bb x25 0000006d877b0920 x26 0000006cc2dcc65f x27 0000006cc2dcc051
01-15 14:53:40.240 21741 21741 F DEBUG : x28 0000006e170f73d0 x29 0000006d4a1e7a30
01-15 14:53:40.240 21741 21741 F DEBUG : lr 0000006cc2dcfb04 sp 0000006d4a1e7a10 pc 0000006cc2dcfb0c pst 0000000060000000
01-15 14:53:40.240 21741 21741 F DEBUG : backtrace:
01-15 14:53:40.240 21741 21741 F DEBUG : #00 pc 0000000000006b0c /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #01 pc 0000000000007adc /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #02 pc 0000000000007e98 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #03 pc 00000000000082f4 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
01-15 14:53:40.240 21741 21741 F DEBUG : #04 pc 00000000000530e8 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsutils.so (SyncHandle_Loop+324) (BuildId: b828e7b2f3db33f9445278f8a9016949763625a2)
01-15 14:53:40.240 21741 21741 F DEBUG : #05 pc 00000000000b1910 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264) (BuildId: ba489d4985c0cf173209da67405662f9)
01-15 14:53:40.240 21741 21741 F DEBUG : #06 pc 00000000000513f0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: ba489d4985c0cf173209da67405662f9)
@2 打开终端或命令行界面:
使用ndk-stack时候要注意:和编译app时使用的必须是同一个版本,一般在对应版本的android studio中的sdk目录中。找到自己对应的ndk版本,比如:~/Android/Sdk/ndk/23.0.7599858这种。直接进入到该目录下,后面直接使用该目录下的ndk-stack命令即可,不用再可以配置环境变量等。
@3 运行ndk-stack命令:
ndk-stack命令基本用法解读如下:
ndk-stack -sym <符号表目录> -dump <日志文件路径>
-sym:指定包含符号表文件(.so 文件)的目录路径。
例如:如果你的crash信息在文件log2023.txt中,则可以:
$ndk-stack -sym path/to/your/symbols -dump log2023.txt
针对于该例子有:
$./ndk-stack -sym path/to/your/symbols -dump ./crash.log
其中 path/to/your/symbols 是包含符号表文件的目录路径。ndk-stack 将解析崩溃日志并输出转换后的堆栈跟踪信息,其中包含源代码行数。
@4 查看转换结果:
ndk-stack 将输出转换后的函数名和源代码行号信息,对应于崩溃堆栈中的地址。
$./ndk-stack -sym path/to/your/symbols -dump ./crash.log
运行后结果如下所示:
********** Crash dump: **********
Build fingerprint: 'Android/aosp_crosshatch/crosshatch:12/SP1A.210812.016.C1/eng.ags.20221201.112332:userdebug/test-keys'
#00 0x0000000000006b0c /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
ags::NetService::startScan(int)
/home/ags/.../NetService.cpp:473:21
#01 0x0000000000007adc /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
IPC_NetService_startScan /home/ags/.../NetServiceStub.cpp:27:45
#02 0x0000000000007e98 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
IPC_NetService_startScan_Stub /home/ags/.../NetServiceStub.c:28:15
#03 0x00000000000082f4 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsnetservice.so (BuildId: bf73aceda3d02c396e5e3ed10ccdd024f0b0ae2c)
IPC_NetService_Request_HandleFunc /home/ags/.../NetServiceStub.c:204:13
#04 0x00000000000530e8 /data/app/~~MMvSo__i24fnwmlXX8sFww==/com.ags.test-oDZ54tlfSncUOdIYkSEtWw==/lib/arm64/libagsutils.so (Handle_Loop+324) (BuildId: b828e7b2f3db33f9445278f8a9016949763625a2)
Handle_Loop /home/ags/.../IDL/EventLooper.c:258:17
#05 0x00000000000b1910 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264) (BuildId: ba489d4985c0cf173209da67405662f9)
#06 0x00000000000513f0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: ba489d4985c0cf173209da67405662f9)
Crash dump is completed
通过分析ndk-stack的输出,开发者可以确定崩溃发生的具体函数和源代码行号,以及可能的调用栈信息,有助于定位和解决Native层的崩溃问题。实际上这里的一个完整的调用栈就出来了。
注意:ndk-stack 工具也是仅适用于 Native 层的崩溃堆栈转换。