文件位置
data/tombstone/tombstone_xx.txt
获取tombstone文件命令:
adb shell cp /data/tombstones ./tombstones
触发时机
NDK程序在发生崩溃时,它会在路径/data/tombstones/
下产生导致程序crash的文件tombstone_xx
,记录了死亡了进程的基本信息(例如进程的进程号,线程号),死亡的地址(在哪个地址上发生了Crash),死亡时的现场是什么样的(记录了一系列的堆栈调用信息)等等。
内容示例
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'
Revision: '0'
ABI: 'x86'
pid: 1019, tid: 1019, name: surfaceflinger >>> /system/bin/surfaceflinger <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4eax a6265c06 ebx b7467d88 ecx b7631a22 edx a6265c06esi 00000000 edi b6867140xcs 00000073 xds 0000007b xes 0000007b xfs 00000000 xss 0000007beip b745a639 ebp bfcfc1e8 esp bfcfc150 flags 00010282backtrace:#00 pc 00006639 /system/lib/libui.so (android::Fence::waitForever(char const*)+41)#01 pc 00034b86 /system/lib/libsurfaceflinger.so#02 pc 0003229e /system/lib/libsurfaceflinger.so#03 pc 0002cb9c /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+652)#04 pc 000342f4 /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2580)#05 pc 0004eafb /system/lib/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int)+411)#06 pc 0004ce06 /system/lib/libgui.so (android::Surface::hook_queueBuffer(ANativeWindow*, ANativeWindowBuffer*, int)+38)#07 pc 00014bc6 /system/lib/egl/libGLES_android.so#08 pc 00017f73 /system/lib/egl/libGLES_android.so (eglSwapBuffers+163)#09 pc 00015fdb /system/lib/libEGL.so (eglSwapBuffers+203)#10 pc 000013ea /system/lib/hw/hwcomposer.x86.so#11 pc 00034730 /system/lib/libsurfaceflinger.so#12 pc 000256d4 /system/lib/libsurfaceflinger.so#13 pc 00024bf4 /system/lib/libsurfaceflinger.so#14 pc 000236fb /system/lib/libsurfaceflinger.so#15 pc 0002338a /system/lib/libsurfaceflinger.so#16 pc 0001e0ff /system/lib/libsurfaceflinger.so#17 pc 0001d9ce /system/lib/libutils.so (android::Looper::pollInner(int)+926)#18 pc 0001db73 /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+67)#19 pc 0001e561 /system/lib/libsurfaceflinger.so#20 pc 00022ce7 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::run()+39)#21 pc 00000ca3 /system/bin/surfaceflinger#22 pc 0001365a /system/lib/libc.so (__libc_init+106)#23 pc 00000da8 /system/bin/surfaceflingerstack:bfcfc110 00000000 bfcfc114 b6839270 bfcfc118 00000000 bfcfc11c 00000000 bfcfc120 b68394e0 bfcfc124 00000002 bfcfc128 00000002 bfcfc12c b75d8185 /system/lib/libutils.so (android::RefBase::incStrong(void const*) const+53)bfcfc130 b6839270 bfcfc134 bfcfc1e8 [stack]bfcfc138 00000002 bfcfc13c a6265c06 bfcfc140 b7467d88 /system/lib/libui.sobfcfc144 00000000 bfcfc148 b6867140 bfcfc14c b745a639 /system/lib/libui.so (android::Fence::waitForever(char const*)+41)#00 bfcfc150 b683af18 bfcfc154 bfcfc1e8 [stack]bfcfc158 00000000 bfcfc15c 00000000 bfcfc160 00000000 bfcfc164 b683af18 bfcfc168 b75ec9c4 /system/lib/libutils.sobfcfc16c b75d8285 /system/lib/libutils.so (android::RefBase::weakref_type::decWeak(void const*)+37)bfcfc170 00000000 bfcfc174 00000000 bfcfc178 00000000 bfcfc17c 00000000 bfcfc180 b7642968 /system/lib/libsurfaceflinger.sobfcfc184 bfcfc1e8 [stack]bfcfc188 b6867140 bfcfc18c b7622b87 /system/lib/libsurfaceflinger.so
构建指纹
Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'
典型的格式为:AOSP/[Android Version]/[Build ID]/[Build Date]
- “Android-x86”: 表示Android-x86工程
- “android_x86”: 表示Android-x86工程的分支名或者构建变体
- “x86:5.1.1”: 表示安卓版本为5.1.1, 即Lollipop。"x86"表示其针对x86架构CPU进行优化
- “LMY48W”: 表示构建号(build number),构建号可以自定义,比如某个feature、bugfix的提交号
- “woshijpf04211939:eng”: 自定义标签,比如构建者、构建目等,“eng” 可能表示使用了engineering配置
- “test-keys”: 表示此构建使用了测试key而不是正式发布的key文件
崩溃的过程和PID
pid: 1019, tid: 1019, name: surfaceflinger >>> /system/bin/surfaceflinger <<<
如果pid等于tid,那么就说明这个程序是在主线程中Crash掉的,名称的属性则表示Crash进程的名称以及在文件系统中位置。
终止信号和故障地址
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4
信息说明出现进程Crash的原因是因为程序产生了段错误的信号,访问了非法的内存空间,而访问的非法地址是0x4。
典型的内存地址访问错误:
- 野指针
- 内存泄露
- 堆栈溢出
- 初始化错误
- 类型转换错误
- 数字除0
Linux信号机制
信号机制是Linux进程间通信的一种重要方式,Linux信号一方面用于正常的进程间通信和同步,如任务控制(SIGINT,SIGTSTP,SIGKILL,SIGCONT,…);另一方面,它还负责监控系统异常及中断。当应用程序运行异常时,Linux内核将产生错误信号并通知当前进程。当前进程在接收到该错误信号后,可以有三种不同的处理方式:
- 忽略该信号。
- 捕捉该信号并执行对应的信号处理函数(信号处理程序)。
- 执行该信号的缺省操作(如SIGSEGV,其缺省操作是终止进程)。
当Linux应用程序在执行时发生严重错误,一般会导致程序崩溃。其中,Linux专门提供了一类crash信号,在程序接收到此类信号时,缺省操作是将崩溃的现场信息记录到核心文件,然后终止进程。
CPU寄存器
略
调用堆栈
backtrace:#00 pc 00006639 /system/lib/libui.so (android::Fence::waitForever(char const*)+41)#01 pc 00034b86 /system/lib/libsurfaceflinger.so#02 pc 0003229e /system/lib/libsurfaceflinger.so#03 pc 0002cb9c /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+652)#04 pc 000342f4 /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2580)
- #00,#01,#02表示函数调用栈中栈帧的编号,编号越小的栈帧表示着当前最近调用的函数信息,所以栈帧标号#00表示的就是当前正在执行并导致程序崩溃函数的信息。
- pc后面的16进制数值表示的是当前函数正在执行的语句在共享链接库或者可执行文件中的位置
- /system/lib/libui.so表示的是当前执行指令是在哪个文件当中
- 小括号则是注明对应的是哪个函数
例如,在上面的例子中,我们就可以定位到是程序是在Fence :: waitForever(char const *)中出现了错误,但是具体在那一行呢,我们还不是特别清楚,所以就需要我们进一步地使用更加高级的工具来帮助我们解析tombstone中有关调用栈的信息。
堆叠每个通话的内容
略
处理工具
Google在NDK包中为我们提供了一系列的调试工具:
- addr2line
- objdump
- ndk-stack
在介绍上述工具的使用方法之前有必要再次介绍一下so文件的构成,虽然这部分内容属于NDK范畴。
so
文件介绍
so文件组成
完整的 .so
文件由 C/C++代码和一些 debug 信息组成,这些 debug 信息会记录 .so中
所有方法的对照表,就是方法名和其偏移地址的对应表,也叫做符号表symbolic
信息,这种.so
被称为未strip
的,通常体积会比较大。
注意:
- 符号表可以类比为 Java 代码混淆中的 mapping 文件,只有拥有这个 mapping 文件才能进行堆栈分析。
- 编译完so文件后,需要保留「机器码+debug信息的完整so文件」或者「仅含debug信息的符号表文件」
可以通过file命令查看so文件基本信息,以判断是否包含debug信息:
# 查看已被stripped的so文件
file libbreakpad-core-s.so
libbreakpad-core-s.so: *******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, stripped
# 查看未被stripped的so文件
file libbreakpad-core.so
libbreakpad-core.so: ******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, with debug_info, not stripped
so文件获取
目前 Android Studio 无论是使用 mk 或者 Cmake 编译的方式都会同时输出 strip 和未 strip 的 so:
- strip 之前的 so 路径:{project}/app/build/intermediates/merged_native_libs
- strip 之后的 so 路径:{project}/app/build/intermediates/stripped_native_libs
如下图是 Cmake 编译 so 产生的两个对应的 so:
addr2line
该工具通过输入so文件和backtrace中的地址来输出源码中的行号,支持一次性输入多个地址。
工具路径:
- Mac OS:
$NDK_ROOT/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-addr2line
最佳实践:
alias addr2line='$NDK_HOME/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android-addr2line'
注意:
- so文件需要带符号表,位于
out/target/product/cc_company/symbols/system/lib
;- 由于是逐行解析,因此想要得到完整的源码调用栈需要自行封装写个脚本工具;
用法
Usage:android-addr2line [option(s)] [addr(s)]Convert addresses into line number/file name pairs.If no addresses are specified on the command line, they will be read from stdinThe options are:@<file> Read options from <file>-a --addresses Show addresses-b --target=<bfdname> Set the binary file format-e --exe=<executable> Set the input file name (default is a.out)-i --inlines Unwind inlined functions-j --section=<name> Read section-relative offsets instead of addresses-p --pretty-print Make the output easier to read for humans-s --basenames Strip directory names-f --functions Show function names-C --demangle[=style] Demangle function names-h --help Display this information-v --version Display the program's versionandroid-addr2line: supported targets: elf32-i386 elf32-iamcu a.out-i386-linux pei-i386 elf64-x86-64 elf32-x86-64 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex
Report bugs to <http://www.sourceware.org/bugzilla/>
推荐:
add2line -fCp -e /path/to/xxx.so \<addresses\>
示例
执行命令:
addr2line -f -e libui.so 00006639
输出结果:
_ZN7android5Fence11waitForeverEPKc
/home/woshijpf/newspace/android-x86/frameworks/native/libs/ui/Fence.cpp:59
ndk-stack
可将墓碑文件中的backtrace和stack完整还原成源码文件路径+代码行号的呈现方式。
工具路径:$NDK_HOME/ndk-stack
注意:
- 需要obj目录,例如
obj/local/x86/
- 通常用于本地调试阶段的问题排查,对于线上问题排查时不一定保留这些符号表文件
用法
Usage:ndk-stack -sym <path> [-dump <path>]-sym Contains full path to the root directory for symbols.-dump Contains full path to the file containing the crash dump.This is an optional parameter. If ommited, ndk-stack willread input data from stdin
- sym: 需要输入符号表文件的根目录,即obj目录
- dump:输入指定的trace文本文件路径或者若缺省则从标准输入读取
示例
原墓碑文件内容:
// tombstone_01 文件内容
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'
Revision: '0'
ABI: 'x86'
pid: 2125, tid: 2125, name: androidvncserve >>> androidvncserver <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0eax 00000000 ebx b76ffff4 ecx b6a37000 edx 00000000esi 00000000 edi 00000000xcs 00000073 xds 0000007b xes 0000007b xfs 00000000 xss 0000007beip b75a4ec5 ebp bfa9bd08 esp bfa9bbf0 flags 00010246backtrace:#00 pc 0004bec5 /system/bin/androidvncserver#01 pc 0004e3e4 /system/bin/androidvncserver#02 pc 0001365a /system/lib/libc.so (__libc_init+106)#03 pc 0001060c /system/bin/androidvncserverstack:bfa9bbb0 bfa9bbc8 [stack]bfa9bbb4 b72c6fb0 /system/lib/libdvnc_flinger_sdk22.sobfa9bbb8 b72c7004 /system/lib/libdvnc_flinger_sdk22.sobfa9bbbc b72c5b26 /system/lib/libdvnc_flinger_sdk22.so (readfb_flinger+38)bfa9bbc0 b68ae080 bfa9bbc4 00000000 bfa9bbc8 00000000 bfa9bbcc 00000000 bfa9bbd0 00000000 bfa9bbd4 b76ffff4 /system/bin/androidvncserverbfa9bbd8 b76a5e20 /system/bin/androidvncserverbfa9bbdc b75ac181 /system/bin/androidvncserverbfa9bbe0 b75ac16b /system/bin/androidvncserverbfa9bbe4 b76ffff4 /system/bin/androidvncserverbfa9bbe8 bfa9bd08 [stack]bfa9bbec b75a579b /system/bin/androidvncserver#00 bfa9bbf0 00000012 bfa9bbf4 bfa9bc1c [stack]bfa9bbf8 00000000 bfa9bbfc 00000000 bfa9bc00 bfa9bc14 [stack]bfa9bc04 00000000 bfa9bc08 00000000 bfa9bc0c b7498825 /system/lib/libc.so (je_free+453)bfa9bc10 0000000e bfa9bc14 00000400 bfa9bc18 00000000 bfa9bc1c b749538a /system/lib/libc.so (je_malloc+778)bfa9bc20 0000000c bfa9bc24 00000065 bfa9bc28 00000000 bfa9bc2c b6a37000 ........ ........#01 bfa9bd10 b6bb7300 bfa9bd14 00001b58 bfa9bd18 b76aa954 /system/bin/androidvncserverbfa9bd1c 0000000b bfa9bd20 00000005 bfa9bd24 00000000 bfa9bd28 00000000 bfa9bd2c 00000005 bfa9bd30 00000006 bfa9bd34 00000005 bfa9bd38 00000000 bfa9bd3c 00000000 bfa9bd40 00000000 bfa9bd44 bfa9bd24 [stack]bfa9bd48 b754f9c8 /system/bin/linkerbfa9bd4c b7557bd8 /system/bin/linker........ ........#02 bfa9bed0 00000001 bfa9bed4 bfa9bf14 [stack]bfa9bed8 bfa9bf1c [stack]bfa9bedc 00000000 bfa9bee0 b7556fec /system/bin/linkerbfa9bee4 bfa9bf10 [stack]bfa9bee8 00000000 bfa9beec b7556fec /system/bin/linkerbfa9bef0 bfa9bf10 [stack]bfa9bef4 00000000 bfa9bef8 bfa9bf0c [stack]bfa9befc b756960d /system/bin/androidvncserver#03 bfa9bf00 bfa9bf10 [stack]bfa9bf04 00000000 bfa9bf08 b756960d /system/bin/androidvncserverbfa9bf0c b7569612 /system/bin/androidvncserverbfa9bf10 00000001 bfa9bf14 bfa9cb05 [stack]bfa9bf18 00000000 bfa9bf1c bfa9cb16 [stack]bfa9bf20 bfa9cb35 [stack]bfa9bf24 bfa9cb48 [stack]bfa9bf28 bfa9cba3 [stack]bfa9bf2c bfa9cbae [stack]bfa9bf30 bfa9cbc1 [stack]bfa9bf34 bfa9cbdc [stack]bfa9bf38 bfa9cbe7 [stack]bfa9bf3c bfa9cbfd [stack]
执行命令:
ndk-stack -sym obj/local/x86/ -dump ~/android-x86-debug-log/tombstone_01
输出结果:
********** Crash dump: **********
Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'
pid: 2125, tid: 2125, name: androidvncserve >>> androidvncserver <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Stack frame #00 pc 0004bec5 /system/bin/androidvncserver: Routine update_screen_16 in /home/woshijpf/android_workspace/droidVNCserver-real/jni/vnc/updateScreen.c:68
Stack frame #01 pc 0004e3e4 /system/bin/androidvncserver: Routine main in /home/woshijpf/android_workspace/droidVNCserver-real/jni/vnc/droidvncserver.c:805
Stack frame #02 pc 0001365a /system/lib/libc.so (__libc_init+106)
Stack frame #03 pc 0001060c /system/bin/androidvncserver: Unable to locate routine information for address 1060c in module obj/local/x86//androidvncserver
Stack frame #00 pc 0007bf71 /system/lib/libc.so (nanosleep+17)
Stack frame #01 pc 00047ed6 /system/lib/libc.so (usleep+70)
Stack frame #02 pc 0004fa6b /system/bin/androidvncserver: Routine camera_io in /home/woshijpf/android_workspace/droidVNCserver-real/jni/vnc/camera_io.c:557
Stack frame #03 pc 0004af6a /system/bin/androidvncserver: Routine receive_camera in /home/woshijpf/android_workspace/droidVNCserver-real/jni/vnc/droidvncserver.c:273
Stack frame #04 pc 00022168 /system/lib/libc.so (__pthread_start(void*)+56)
Stack frame #05 pc 0001cc69 /system/lib/libc.so (__start_thread+25)
Stack frame #06 pc 000137c6 /system/lib/libc.so (__bionic_clone+70)
Stack frame #00 pc 0007cc33 /system/lib/libc.so (recvfrom+19)
Stack frame #01 pc 00050cdb /system/bin/androidvncserver: Routine handle_connections in /home/woshijpf/android_workspace/droidVNCserver-real/jni/vnc/gui.c:105
Stack frame #02 pc 00022168 /system/lib/libc.so (__pthread_start(void*)+56)
Stack frame #03 pc 0001cc69 /system/lib/libc.so (__start_thread+25)
Stack frame #04 pc 000137c6 /system/lib/libc.so (__bionic_clone+70)
objdump
使用objdump命令对目标文件(obj)或可执行文件进行反汇编,它以一种可阅读的格式让你更多的了解二进制文件可能带有的附加信息。
参考文章:objdump的使用
参考资料
- 谷歌官方教程:调试 Android 平台原生代码
- Android NDK墓碑/崩溃分析_android墓碑文件_Lixby的博客-CSDN博客
- Android的墓碑 - 掘金
- Android 平台 Native Crash 问题分析与定位