【NDK 开发】一文读懂 Android Native 崩溃:日志结构、信号含义与符号解析
文章目录一、基本知识1.1 Native 崩溃日志1.1.1 logcat 中的 Native 崩溃日志摘要1.1.2 墓碑日志 Tombstones1.2 崩溃日志组成结构1.2.1 崩溃信号和基本信息1.2.2 调用堆栈二、日志分析工具2.1 so 文件与调试符号2.1.1 so 文件的调试信息2.1.2 如何获取带调试符号的 .so 文件2.2 映射源代码行号2.2.1 addr2line精确地址映射工具2.2.2 ndk-stack自动化崩溃分析工具参考资料一、基本知识1.1 Native 崩溃日志当 Native 进程崩溃如发生 SIGSEGV、SIGABRT 等信号时Android 系统的崩溃处理流程如下信号触发进程接收到致命信号操作系统立即调用信号处理程序。logcat 实时摘要信号处理程序会立即向 logcat 的系统缓冲区 实时打印一条简短的崩溃消息。这个日志相对简略仅包含崩溃信号、粗略的调用栈可能不完整和进程信息适合快速定位问题。tombstoned 监听系统服务tombstoned会监听到崩溃事件。debuggerd 抓取详细信息tombstoned通知debuggerd守护进程debuggerd会附加attach到崩溃进程抓取进程在崩溃瞬间的完整状态内存、寄存器、堆栈等。生成完整报告debuggerd将收集到的详细信息写入一个独立的文件即 Tombstone 文件。1.1.1 logcat 中的 Native 崩溃日志摘要这是崩溃发生时在 logcat 中实时打印出的摘要信息首条多为libc的 Fatal signal后续详细块多为DEBUG/crash_dump64等。内容较为简洁通常包含崩溃信号如 SIGSEGV粗略的调用栈可能不完整进程和线程信息如果想要查看更加详细的日志可以使用 adb logcat 来查看。1.1.2 墓碑日志 Tombstones墓碑日志Tombstone是 Android 系统中用于记录原生代码Native Code崩溃的详细崩溃报告文件。当 Android 应用中的 C/C 代码通过 JNI 调用发生崩溃时系统会生成墓碑日志来帮助开发者诊断问题。核心特征文件名格式tombstone_XXXX 为递增数字如 tombstone_00, tombstone_01存储位置/data/tombstones/目录需要 root 权限生成时机当发生 SIGSEGV、SIGABRT、SIGILL 等致命信号时内容类型纯文本格式的详细崩溃报告当设备没有 root 权限时可通过adb bugreport例如adb bugreport bugreport_output.zip打包导出从中取出墓碑相关文件。1.2 崩溃日志组成结构一个典型的崩溃日志通常包含如下内容崩溃类型和信号:例如Fatal signal 11 (SIGSEGV)表示发生了段错误非法内存访问。可能还包括信号的其他信息如code和fault addr。进程和线程信息tid线程 ID、pid进程 ID、线程名称如Thread-6和进程名称如com.example.ndk.basic。调用堆栈Backtrace:列出崩溃时的调用链。#00为崩溃所在栈帧最内层编号增大表示向外层的调用者经 JNI 可回到 Java/Kotlin 帧如示例中的MainActivity靠后的帧接近线程入口如Looper、main。其他上下文信息如时间戳、进程状态、寄存器值某些情况下等。一个典型的非法内存访问的 Native 崩溃日志如下所示--------- beginning of crash 04-30 10:17:44.915 5094 5094 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 5094 (ample.ndk.basic), pid 5094 (ample.ndk.basic) 04-30 10:17:45.034 7540 7540 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto 04-30 10:17:45.035 662 662 I tombstoned: received crash request for pid 5094 04-30 10:17:45.035 7540 7540 I crash_dump64: performing dump of process 5094 (target tid 5094) 04-30 10:17:45.047 7540 7540 E DEBUG : failed to read /proc/uptime: Permission denied 04-30 10:17:45.364 7540 7540 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 04-30 10:17:45.364 7540 7540 F DEBUG : Build fingerprint: OPPO/PDCM00/OP4ADD:12/SP1A.210812.016/Q.1fbc20c-a332:user/release-keys 04-30 10:17:45.364 7540 7540 F DEBUG : Revision: 0 04-30 10:17:45.364 7540 7540 F DEBUG : ABI: arm64 04-30 10:17:45.364 7540 7540 F DEBUG : Timestamp: 2026-04-30 10:17:45.0468905520800 04-30 10:17:45.364 7540 7540 F DEBUG : Process uptime: 0s 04-30 10:17:45.364 7540 7540 F DEBUG : Cmdline: com.example.ndk.basic 04-30 10:17:45.364 7540 7540 F DEBUG : pid: 5094, tid: 5094, name: ample.ndk.basic com.example.ndk.basic 04-30 10:17:45.364 7540 7540 F DEBUG : uid: 10100 04-30 10:17:45.364 7540 7540 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 04-30 10:17:45.364 7540 7540 F DEBUG : Cause: null pointer dereference 04-30 10:17:45.364 7540 7540 F DEBUG : x0 b400007ef9ab1500 x1 0000007ec2af51e8 x2 0000007f9d34a500 x3 0000000000000000 04-30 10:17:45.364 7540 7540 F DEBUG : x4 0000007f9d3b9000 x5 0000007fe1f0ac40 x6 0000007eea40b2f0 x7 0000000000000001 04-30 10:17:45.364 7540 7540 F DEBUG : x8 0000000000000001 x9 0000000000000000 x10 0000000000000105 x11 0000000000000001 04-30 10:17:45.364 7540 7540 F DEBUG : x12 0000000000000006 x13 16bcabe6e5567443 x14 0000000000000006 x15 ffffffffffffffff 04-30 10:17:45.364 7540 7540 F DEBUG : x16 0000000000000001 x17 0000007eaf096ba0 x18 0000007f9d928000 x19 b400007ef9a10800 04-30 10:17:45.364 7540 7540 F DEBUG : x20 0000000000000000 x21 0000000000000000 x22 0000007f957e0354 x23 0000000000000071 04-30 10:17:45.364 7540 7540 F DEBUG : x24 0000007ee9e00880 x25 0000007fe1f0ada8 x26 0000007fe1f0adac x27 0000007fe1f0ada8 04-30 10:17:45.364 7540 7540 F DEBUG : x28 0000007fe1f0acb0 x29 0000007fe1f0acb0 04-30 10:17:45.364 7540 7540 F DEBUG : lr 0000007eea040558 sp 0000007fe1f0ac80 pc 0000007eaf096bb8 pst 0000000060001000 04-30 10:17:45.364 7540 7540 F DEBUG : backtrace: 04-30 10:17:45.364 7540 7540 F DEBUG : #00 pc 0000000000000bb8 /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk!libcmakedemo.so (Java_com_example_jni_cmake_JniDemoCMake_triggerNativeCrashForStackAnalysis24) (BuildId: 1fa4717032f5387a43d93440df22519b63eaa3f2) 04-30 10:17:45.364 7540 7540 F DEBUG : #01 pc 0000000000440554 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline148) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.364 7540 7540 F DEBUG : #02 pc 0000000000209398 /apex/com.android.art/lib64/libart.so (nterp_helper152) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.364 7540 7540 F DEBUG : #03 pc 0000000000000354 [anon:dalvik-classes6.dex extracted in memory from /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk!classes6.dex] (com.example.ndk.basic.MainActivity.lambda$onCreate$00) 04-30 10:17:45.364 7540 7540 F DEBUG : #04 pc 0000000000209334 /apex/com.android.art/lib64/libart.so (nterp_helper52) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.364 7540 7540 F DEBUG : #05 pc 0000000000000324 [anon:dalvik-classes6.dex extracted in memory from /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk!classes6.dex] (com.example.ndk.basic.MainActivity$$ExternalSyntheticLambda0.onClick0) 04-30 10:17:45.364 7540 7540 F DEBUG : #06 pc 000000000020b074 /apex/com.android.art/lib64/libart.so (nterp_helper7540) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.364 7540 7540 F DEBUG : #07 pc 0000000000381732 /system/framework/framework.jar (android.view.View.performClick106) 04-30 10:17:45.364 7540 7540 F DEBUG : #08 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper3924) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.364 7540 7540 F DEBUG : #09 pc 00000000003936a2 [anon:dalvik-classes.dex extracted in memory from /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk] (com.google.android.material.button.MaterialButton.performClick34) 04-30 10:17:45.364 7540 7540 F DEBUG : #10 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper3924) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.364 7540 7540 F DEBUG : #11 pc 00000000003817f6 /system/framework/framework.jar (android.view.View.performClickInternal6) 04-30 10:17:45.365 7540 7540 F DEBUG : #12 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper3924) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #13 pc 000000000037c7d4 /system/framework/framework.jar (android.view.View.access$37000) 04-30 10:17:45.365 7540 7540 F DEBUG : #14 pc 0000000000209334 /apex/com.android.art/lib64/libart.so (nterp_helper52) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #15 pc 0000000000355594 /system/framework/framework.jar (android.view.View$PerformClick.run16) 04-30 10:17:45.365 7540 7540 F DEBUG : #16 pc 000000000020b074 /apex/com.android.art/lib64/libart.so (nterp_helper7540) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #17 pc 0000000000440d7c /system/framework/framework.jar (android.os.Handler.handleCallback4) 04-30 10:17:45.365 7540 7540 F DEBUG : #18 pc 0000000000209334 /apex/com.android.art/lib64/libart.so (nterp_helper52) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #19 pc 0000000000440bf0 /system/framework/framework.jar (android.os.Handler.dispatchMessage8) 04-30 10:17:45.365 7540 7540 F DEBUG : #20 pc 000000000020a254 /apex/com.android.art/lib64/libart.so (nterp_helper3924) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #21 pc 000000000046f5c6 /system/framework/framework.jar (android.os.Looper.loopOnce438) 04-30 10:17:45.365 7540 7540 F DEBUG : #22 pc 0000000000209334 /apex/com.android.art/lib64/libart.so (nterp_helper52) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #23 pc 000000000046fd0e /system/framework/framework.jar (android.os.Looper.loop178) 04-30 10:17:45.365 7540 7540 F DEBUG : #24 pc 0000000000209334 /apex/com.android.art/lib64/libart.so (nterp_helper52) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #25 pc 00000000001b536a /system/framework/framework.jar (android.app.ActivityThread.main270) 04-30 10:17:45.365 7540 7540 F DEBUG : #26 pc 0000000000436e00 /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub576) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #27 pc 0000000000469534 /apex/com.android.art/lib64/libart.so (_jobject* art::InvokeMethod(art::PointerSize)8(art::ScopedObjectAccessAlreadyRunnable const, _jobject*, _jobject*, _jobject*, unsigned long)1960) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #28 pc 0000000000468d64 /apex/com.android.art/lib64/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) (.__uniq.165753521025965369065708152063621506277)48) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #29 pc 0000000000440554 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline148) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #30 pc 000000000020a2b0 /apex/com.android.art/lib64/libart.so (nterp_helper4016) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #31 pc 00000000002bbc86 /system/framework/framework.jar (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run22) 04-30 10:17:45.365 7540 7540 F DEBUG : #32 pc 000000000020b074 /apex/com.android.art/lib64/libart.so (nterp_helper7540) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #33 pc 00000000002c1ec6 /system/framework/framework.jar (com.android.internal.os.ZygoteInit.main714) 04-30 10:17:45.365 7540 7540 F DEBUG : #34 pc 0000000000436e00 /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub576) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #35 pc 0000000000582718 /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeWithVarArgs_jmethodID*(art::ScopedObjectAccessAlreadyRunnable const, _jobject*, _jmethodID*, std::__va_list)900) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #36 pc 00000000005f611c /apex/com.android.art/lib64/libart.so (art::JNItrue::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)160) (BuildId: d307dc6adc4105b5e392ad710770385d) 04-30 10:17:45.365 7540 7540 F DEBUG : #37 pc 00000000000b0b28 /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)120) (BuildId: 4d86775b530f327311a633d31c014dc2) 04-30 10:17:45.365 7540 7540 F DEBUG : #38 pc 00000000000bc214 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vectorandroid::String8 const, bool)948) (BuildId: 4d86775b530f327311a633d31c014dc2) 04-30 10:17:45.365 7540 7540 F DEBUG : #39 pc 0000000000002580 /system/bin/app_process64 (main1320) (BuildId: c4e0717e77dbaf6f3710de1f5d169e0e) 04-30 10:17:45.365 7540 7540 F DEBUG : #40 pc 0000000000071538 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init96) (BuildId: f24f65acf653c409ca4332aced817a71)1.2.1 崩溃信号和基本信息以下是日志中的关键行分析Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 5094 (ample.ndk.basic), pid 5094 (ample.ndk.basic)关键信息解读信号类型SIGSEGV段错误Signal Segmentation Violation表示进程尝试访问了未被允许访问的内存区域。信号代码SEGV_MAPERR表示访问了一个未映射的内存地址即该地址不在进程的地址空间内。错误地址0x0这是一个空指针地址。在大多数系统中地址 0x0 是受保护的不允许读写因此这通常是空指针解引用的典型表现。进程/线程信息进程IDpid5094进程名称ample.ndk.basic可能被截断线程IDtid5094常见信号类型SIGSEGV11段错误非法内存访问SIGABRT6程序主动调用 abort() 或发生严重错误SIGILL4非法指令SIGBUS7总线错误对齐问题等SIGFPE8算术异常如除零1.2.2 调用堆栈调用堆栈是崩溃分析中最关键的部分它展示了函数调用的层级关系。在调用堆栈中每一行包含帧编号Frame Number#00、#01、#02等表示调用深度。数字越小越靠近实际崩溃点数字越大越靠近调用链外层直至线程入口。通常从#00开始最内层/崩溃点个别情况下首帧含义以具体日志为准。pc在堆栈每一行里一般给出相对对应 ELF如.so映射基址的偏移与readelf/加载地址配合可做符号解析同一日志里寄存器段中的完整虚拟地址如pc 0000007eaf096bb8与addr2line所用偏移不是同一个数不要混用。库或文件的路径如/data/app/.../base.apk!libcmakedemo.so。路径的组成较为复杂基础路径/data/app/— Android 应用安装目录前缀应用签名标识~~xxxx用于按安装实例隔离目录包名与安装会话后缀com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA— 包名 分隔符 该次安装的会话/随机后缀库文件路径base.apk!ABI目录/libxxx.so函数名若有符号括号内的24等形式表示相对函数入口的字节偏移示例见下行。可选的BuildId用于匹配.so版本。#00 pc 0000000000000bb8 /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk!libcmakedemo.so (Java_com_example_jni_cmake_JniDemoCMake_triggerNativeCrashForStackAnalysis24) (BuildId: 1fa4717032f5387a43d93440df22519b63eaa3f2)二、日志分析工具在获得 Native 崩溃日志后下一步是将日志中的地址和函数名映射到具体的源代码行号以便定位问题。这需要使用包含调试信息的 .so 文件和相应的分析工具。2.1 so 文件与调试符号2.1.1 so 文件的调试信息注意必须使用带调试符号的 .so 文件分析工具才能正确映射到源代码行号。调试符号包含了函数名、变量名、源代码位置等关键信息但在发布版本中通常会被移除以减小文件体积。.so 文件的编译和符号处理流程如下源代码 (.c/.cpp/.cc) ↓ 编译 目标文件 (.o) - 包含调试符号 ↓ 链接 未strip的.so文件 - 包含完整调试符号 ↓ strip处理移除调试符号 去符号的.so文件- 体积小用于发布2.1.2 如何获取带调试符号的 .so 文件单独编译文件so 文件位置位于cmakebuild/lib/ABI/libxxx.sondk-buildobj/local/ABI/libxxx.so如果集成到 Android 的 Gradle 里面带调试符号的 so 文件位置位于cmakebuild/intermediates/cxx/Debug/哈希值/obj/ABI/libxxx.sondk-buildbuild/intermediates/ndkBuild/debug/obj/local/ABI/libxxx.so2.2 映射源代码行号将崩溃日志中的地址映射到源代码行号主要有两种工具addr2lineGNU Binutils 工具链里的基础工具不限于 Android任何 ELF 文件.so/.exe都能用。ndk-stackAndroid 专用的日志分析器。2.2.1 addr2line精确地址映射工具addr2line可将地址映射到源代码位置。基本用法为$ addr2line -f -C -e so文件路径 十六进制地址 # 常用参数 # -f显示函数名 # -C解析 C 名称demangle将 _Z3foo 转为 foo() # -e指定可执行文件.so 文件 # 地址崩溃栈里本帧的 pc(程序计数器)的十六进制假设崩溃日志显示#00 pc 0000000000000bb8 /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk!libcmakedemo.so (Java_com_example_jni_cmake_JniDemoCMake_triggerNativeCrashForStackAnalysis24) (BuildId: 1fa4717032f5387a43d93440df22519b63eaa3f2)根据崩溃设备的 ABI 选择对应工具位于$NDK/toolchains/llvm/prebuilt/主机/bin/主机如windows-x86_64、darwin-x86_64、linux-x86_64使用与arm64-v8a调试产物匹配的带符号libcmakedemo.so# arm64-v8a64 位 ARM示例优先使用 LLVM 工具链中的 llvm-addr2line$NDK/toolchains/llvm/prebuilt/windows-x86_64/bin/llvm-addr2line-f-C-elibcmakedemo.so 0xbb8 Java_com_example_jni_cmake_JniDemoCMake_triggerNativeCrashForStackAnalysis F:/Android-Demos/android-ndk/ndk-basic-java-view/jni-demo-cmake/src/main/cpp/jni-demo-cmake-static.cpp:15# 同目录下也可能存在 aarch64-linux-android-addr2line用法与上类似# armeabi-v7a32 位 ARM请改用 armv7 对应 triple 的 addr2line并匹配 armeabi-v7a 的 .so2.2.2 ndk-stack自动化崩溃分析工具不同于需要明确指定文件ndk-stack会自动扫描指定目录及其子目录查找所有 .so 文件 和对应的调试符号根据崩溃日志中的 BuildId 自动匹配正确的 .so 文件。其基本用法为$ ndk-stack-sym符号目录-dump崩溃日志文件使用案例为# 1. 保存崩溃日志到文件$ adb logcat-dcrash_log.txt# 2. 使用 ndk-stack 分析$ C:\Users\你的用户名\AppData\Local\Android\Sdk\ndk\版本号\ndk-stack.cmd-sym.-dumpcrash_log.txt ********** Crash dump: ********** Build fingerprint:OPPO/PDCM00/OP4ADD:12/SP1A.210812.016/Q.1fbc20c-a332:user/release-keys#00 0x0000000000000bb8 /data/app/~~iPtq6_UAMe4Cw31daESCeA/com.example.ndk.basic-6nYjGTk3QPl7wI90IeLFkA/base.apk!libcmakedemo.so (Java_com_example_jni_cmake_JniDemoCMake_triggerNativeCrashForStackAnalysis24) (BuildId: 1fa4717032f5387a43d93440df22519b63eaa3f2)Java_com_example_jni_cmake_JniDemoCMake_triggerNativeCrashForStackAnalysis F:/Android-Demos/android-ndk/ndk-basic-java-view/jni-demo-cmake/src/main/cpp/jni-demo-cmake-static.cpp:15:15#01 0x0000000000440554 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline148) (BuildId: d307dc6adc4105b5e392ad710770385d)...#40 0x0000000000071538 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init96) (BuildId: f24f65acf653c409ca4332aced817a71)Crash dump is completed注意如果没有输出行号的话更换到新版本 ndk 的 ndk-stack 工具或者对应版本的工具。参考资料ndk-stack | Android NDK | Android Developers