文件位置
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 問題分析與定位