Android native crash解析

当某个进程发生crash时会出现下面的错误日志,它可能出现在logcat日志或者/data/tombstones目录下的tombstone文件中

--------- beginning of crash
10-24 00:29:11.558  8660  8934 F libc    : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x1c in tid 8934 (MainThread)
10-24 00:29:11.620   468   468 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
10-24 00:29:11.620   468   468 F DEBUG   : Build fingerprint: 'xxxx'
10-24 00:29:11.620   468   468 F DEBUG   : Revision: '0'
10-24 00:29:11.620   468   468 F DEBUG   : ABI: 'arm'
10-24 00:29:11.621   468   468 F DEBUG   : pid: 8660, tid: 8934, name: MainThread  >>> com.rockstargames.gtasa <<<
10-24 00:29:11.621   468   468 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x1c
10-24 00:29:11.712   468   468 F DEBUG   :     r0 00000000  r1 00000000  r2 00000000  r3 0000001c
10-24 00:29:11.712   468   468 F DEBUG   :     r4 00000000  r5 d8af0800  r6 0000000c  r7 d54ff474
10-24 00:29:11.712   468   468 F DEBUG   :     r8 ce6bf809  r9 00000000  sl 00000084  fp 0000547d
10-24 00:29:11.712   468   468 F DEBUG   :     ip c8dfdadc  sp d54ff474  lr ce6bf81b  pc ce525a94  cpsr 800f0030
10-24 00:29:11.714   468   468 F DEBUG   : 
10-24 00:29:11.714   468   468 F DEBUG   : backtrace:
10-24 00:29:11.714   468   468 F DEBUG   :     #00 pc 0019da94  /data/app/com.rockstargames.gtasa-1/lib/arm/libGTASA.so (_Z25RpMatFXMaterialGetEffectsPK10RpMaterial+11)
10-24 00:29:11.714   468   468 F DEBUG   :     #01 pc 00053817  /data/app/com.rockstargames.gtasa-1/lib/arm/libGTASA.so (offset 0x2e4000)
10-24 00:29:13.677   468   468 F DEBUG   : 
10-24 00:29:13.677   468   468 F DEBUG   : Tombstone written to: /data/tombstones/tombstone_00

64位的错误日志稍有不同:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'xxxx'
Revision: '0'
ABI: 'arm64'
pid: 29357, tid: 29357, name: cnss_diag  >>> /system/bin/cnss_diag <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x10
    x0   0000000000000000  x1   0000007fad6570a8  x2   0000007ff70fd720  x3   0000000000000000
    x4   0000000000000000  x5   0000007fac890540  x6   000000000000003f  x7   0000000000000000
    x8   000000556bff3000  x9   0000000000000004  x10  0000000000000000  x11  0000007fad4ac6b8
    x12  0000007fad4b1278  x13  0000007fad5d84f0  x14  00000000ffffffff  x15  0000007fad5d84f0
    x16  000000556bff2da8  x17  0000007fad548068  x18  0000000000000059  x19  0000000000000000
    x20  000000556c00f000  x21  000000556bff2000  x22  0000000000000000  x23  000000556c00f000
    x24  0000007ff70fd810  x25  000000556c00f6a0  x26  000000556bfd1800  x27  00000000ffffffff
    x28  0000000000000000  x29  0000007ff70fd6b0  x30  000000556bfc3e84
    sp   0000007ff70fd6b0  pc   0000007fad548078  pstate 0000000060000000
    v0   36373234323734315b203a47534d5746  v1   454f4320202d2058454f4345544c205d
    v2   5352455649445f544e415f53574d5f58  v3   30317830203436363678302020595449
    v4   00000000000000000000000000000000  v5   00000000000000000000000000000000
    v6   00000000000000000000000000000000  v7   30337830203038317830203130313031
    v8   00000000000000000000000000000000  v9   00000000000000000000000000000000
    v10  00000000000000000000000000000000  v11  00000000000000000000000000000000
    v12  00000000000000000000000000000000  v13  00000000000000000000000000000000
    v14  00000000000000000000000000000000  v15  00000000000000000000000000000000
    v16  40100401401004014010040140100401  v17  000000000000000000000000aa80aa00
    v18  00000000000000000000000000000000  v19  00000000000000000000000000000000
    v20  00000000000000000000000000000000  v21  00000000000000000000000000000000
    v22  00000000000000000000000000000000  v23  00000000000000000000000000000000
    v24  00000000000000000000000000000000  v25  00000000000000000000000000000000
    v26  00000000000000000000000000000000  v27  00000000000000000000000000000000
    v28  00000000000000000000000000000000  v29  00000000000000000000000000000000
    v30  00000000000000000000000000000000  v31  00000000000000000000000000000000
    fpsr 00000000  fpcr 00000000
backtrace:
    #00 pc 0000000000055078  /system/lib64/libc.so (fclose+16)
    #01 pc 000000000000be80  /system/bin/cnss_diag
    #02 pc 000000000000a508  /system/bin/cnss_diag (main+856)
    #03 pc 000000000001bf50  /system/lib64/libc.so (__libc_init+100)
#04 pc 000000000000a654  /system/bin/cnss_diag

有时候,crash日志只出现在kernel日志中(当CONFIG_DEBUG_USE使能时),而没有出现在logcat日志或tombstone文件中。

<7>[29216.377747] PowerManagerSer: unhandled page fault (11) at 0x00001eef, code 0x017 
<1>[29216.377747] pgd = d7748000
<1>[29216.377777] [00001eef] *pgd=9ea1e831, *pte=00000000, *ppte=00000000 
<4>[29216.377777] 
<4>[29216.377808] Pid: 591, comm: PowerManagerSer 
<4>[29216.377808] CPU: 0 Tainted: G W (3.0.8-00016-g9392d0c #2) 
<4>[29216.377838] PC is at 0x406698c0 <4>[29216.377838] LR is at 0x406cf724 
<4>[29216.377838] pc : [<406698c0>] lr : [<406cf724>] psr: 20000010

当一个程序发生crash时,debuggerd会创建dump信息。此时,kernel会向即将死亡的程序发送一个信号。这个信号被(所有android native app都安装的一个特别的)signal handler捕获。通过bionic C库,这个signal handler通知debuggerd。Debuggerd通知即将死亡的进程使用ptrace读取寄存器和内存,生成tombstone和日志。

debuggerd

在Android系统中crash处理的应用程序叫debuggerd,它将部分堆栈信息写入日志中并把全部堆栈信息写入tombstone文件。具体地实现详system/core/debuggerd/debuggerd.cpp。

当一个动态链接程序启动,一些连接debuggerd(或debugger64)的signal handler被注册,用于处理进程收到signal的事件。Deuggerd披露堆栈信息和寄存器信息。

举例:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'xxx'
Revision: '0'
ABI: 'arm'
pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
    r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
    r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
    ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
backtrace:
    #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87  /system/lib/libc.so (raise+10)
    #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8  /system/lib/libc.so (abort+4)
    #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21  /system/xbin/crasher
    #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc  /system/xbin/crasher
Tombstone written to: /data/tombstones/tombstone_06

把这段日志输入到development/scripts/stack中能展开代码所在行的详细信息(如果能找到未清除符号的二进制库)。如果在编译的时候一些库指定 LOCAL_STRIP_MODULE := keep_symbols 标志,debuggerd能够直接提供有用的backtrace。

【提示】共享库的符号(Library symbols)
使用addr2line、objdump、stack等工具解析地址定位问题时,需要没有清除符号的共享库。这些共享库通常存放在out/target/product/<product_name>/symbols/(system/lib)目录。
清除符号的共享库也包含少量的符号,但是对于分析native crash问题一般是不够的。清除符号的共享库存放在设备的/system/lib/目录或源代码的out/target/product/<product_name>/(system/lib)目录。

Crash dumps

如果当前你没有一个正在研究的crash例子,Android系统源代码中提供了一个用于测试debuggerd的工具,叫做crasher。在 system/core/debuggerd/ 目录下执行 mm 命令会编译得到 crasher 和 crasher64 两个工具(crasher64 用于测试64-bit的crash)。crasher通过不同的命令行参数能够触发各种各样的crash。

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

这行的星号和空格用于在日志中搜索natvie crash日志。一般地,natvie crash日志是以字符串"*** ***"起始。

Build fingerprint:'xxxx'

Fingerprint 信息用于辨别发生crash的编译版本。它与system property ro.build.fingerprint 一致。

Revision: '0'

Revision信息与硬件相关,通常不用。

ABI: 'arm'

ABI值是arm(32位), arm64(64位), mips, mips64,x86或者x86-64。这对前面提到的 stack 脚本非常有用,知道工具链信息。

pid: 1656, tid: 1656, name: crasher >>> crasher <<<

这行表明进程中导致crash的具体线程。在本例中,它是进程的主线程,因此进程ID和线程ID相同。第一个name是线程的名字,符号>>> 和 <<<包围的name是进程的名字。在查找crash发生之前有关的日志时,pid和tid信息也是有用的。

signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------

这行显示收到的信号(SIGABRT)还有是怎样收到的(SI_TKILL),它是进程退出的原因。由debuggerd 报告的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGSEGV和SIGTRAP。

【提示】Linux信号机制
信号机制是 Linux 进程间通信的一种重要方式,Linux 信号一方面用于正常的进程间通信和同步,如任务控制(SIGINT, SIGTSTP,SIGKILL, SIGCONT,……);另一方面,它还负责监控系统异常及中断。 当应用程序运行异常时, Linux 内核将产生错误信号并通知当前进程。 当前进程在接收到该错误信号后,可以有三种不同的处理方式: 忽略该信号、捕捉该信号并执行对应的信号处理函数(signal handler)、执行该信号的缺省操作(如 SIGSEGV, 其缺省操作是终止进程)。
当 Linux 应用程序在执行时发生严重错误,一般会导致程序 crash。其中,Linux 专门提供了一类 crash 信号,在程序接收到此类信号时,缺省操作是将 crash 的现场信息记录到 core 文件,然后终止进程。
Crash 信号列表:
Signal Description
SIGSEGV:Invalid memory reference.
SIGBUS:Access to an undefined portion of a memory object.
SIGFPE:Arithmetic operation error, like divide by zero.
SIGILL:Illegal instruction, like execute garbage or a privileged instruction
SIGSYS:Bad system call.
SIGXCPU:CPU time limit exceeded.
SIGXFSZ:File size limit exceeded.

Abort message: 'some_file.c:123: some_function: assertion "false" failed'

不是所有的crash日志都有abort message这一行。它是logcat日志中此pid/tid的fatal消息的最后一行日志。一般它会解释程序为什么退出,例如:

Abort message: 'Invalid address 0xc420b06c passed to free: value not allocated'
r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8
r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c
r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c
ip 00000006 sp ff96ad18 lr f700ced5 pc **f700dc98** cpsr 400b0010
d0  ffffffffffffffff  d1  76203a6565726620
d2  746f6e2065756c61  d3  7461636f6c6c6120
...
d30 3ff55d890c229271  d31 bf788480fcb07257

寄存器显示的是在收到signal的时刻CPU寄存器中的信息,它们是否有用取决于当前的crash。通常需要关注r0到pc寄存器,pc(program counter)和lr(link register)寄存器最有用。d0-d31寄存器通常是数据存取时使用。

backtrace:
    #00 pc **00042c98** /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87 /system/lib/libc.so (raise+10)
    #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8 /system/lib/libc.so (abort+4)
    #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35 /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21 /system/xbin/crasher
    #08 pc 00016795 /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc /system/xbin/crasher

这里backtrace描述了crash发生时刻代码的位置。第一列是frame号(gdb样式)。PC(program counter)数值是基于shared library的相对地址。第四列是内存映射区的名字(通常是一个shared library或者可执行程序)。如果符号表存在的话,PC数值对应的符号和位移显示在最后一列。再配合 objdump能找到对应的汇编指令。

注意:PC寄存器的值f700dc98与backtrace中pc 00042c98不同,是由于dumper程序自动的减去了基地址。

memory near r1:
    efaf4a2c 2064696c 64697376 2078253a 6320726f  lid vsid:%x or c
    efaf4a3c 5f6c6c61 74617473 64253a65 00000000  all_state:%d....
    efaf4a4c 69766564 6d5f6563 00657475 65726964  device_mute.dire
    efaf4a5c 6f697463 0000006e 203a7325 65726964  ction...%s: dire
    efaf4a6c 6f697463 656b206e 6f6e2079 6f662074  ction key not fo
    efaf4a7c 00646e75 203a7325 6c696146 74206465  und.%s: Failed t
    efaf4a8c 6573206f 756d2074 65206574 253a7272  o set mute err:%
    efaf4a9c 00000064 6c6c6163 7079745f 00000065  d...call_type...
    efaf4aac 203a7325 6c6c6163 70797420 73692065  %s: call type is
    efaf4abc 00732520 004d5347 69647561 6f6d5f6f   %s.GSM.audio_mo
    efaf4acc 00006564 5f6c6c61 6c6c6163 6174735f  de..all_call_sta
    efaf4adc 00736574 253a6425 00002c64 706d6f63  tes.%d:%d,..comp
    efaf4aec 73736572 696f765f 00000070 203a7325  ress_voip...%s: 
    efaf4afc 65746e65 66202c72 616d726f 64253d74  enter, format=%d
    efaf4b0c 00000000 203a7325 69726544 20646576  ....%s: Derived 
efaf4b1c 65646f6d 25203d20 00000064 70696f56  mode = %d...Voip
...

这部分显示程序发生crash时刻寄存器地址附近的内存信息。通常,检查内存信息能够进一步定位发生crash的原因。

Tombstones

Tombstone包含的信息有:

  • 编译版本
  • 发生crash的进程及PID
  • 终止信号和错误地址
  • CPU寄存器信息
  • 调用堆栈
  • 每个调用的堆栈内容

Tombstone包含和crash dump一样的信息,还有一些额外的信息。例如,它包含所有线程的backtrace信息(不只是发生crash的线程)、浮点寄存器、raw stack dumps和寄存器地址附近的内存信息。最有用的,它还包含一个全面的内存地址映射关系表(类似/proc/pid/maps)。

【提示】如何查看进程的内存地址映射
$ adb shell ps | grep mediaserver
media 779 1 419800 237360 binder_thr 00f6e0b058 S /system/bin/mediaserver
$ adb shell cat /proc/779/maps
aaef0000-aaef5000 r-xp 00000000 103:03 1009 /system/bin/mediaserver
aaef5000-aaef6000 r–p 00004000 103:03 1009 /system/bin/mediaserver
aaef6000-aaef7000 rw-p 00000000 00:00 0
dc300000-dc380000 rw-p 00000000 00:00 0 [anon:libc_malloc]
dc400000-dc480000 rw-p 00000000 00:00 0 [anon:libc_malloc]
dc680000-dc700000 rw-p 00000000 00:00 0 [anon:libc_malloc]
dd080000-dd480000 rw-s 00000000 00:01 52609 /dev/ashmem/AudioFlinger::Client (deleted)
dd480000-dd880000 rw-s 00000000 00:01 47141 /dev/ashmem/AudioFlinger::Client (deleted)
dd880000-dd900000 rw-p 00000000 00:00 0 [anon:libc_malloc]
dda00000-dda80000 rw-p 00000000 00:00 0 [anon:libc_malloc]
ddda9000-dde78000 r-xp 00000000 103:03 5640 /system/vendor/lib/libmmparser_lite.so
dde78000-dde7e000 r–p 000ce000 103:03 5640 /system/vendor/lib/libmmparser_lite.so
dde7e000-dde7f000 rw-p 000d4000 103:03 5640 /system/vendor/lib/libmmparser_lite.so

举例:

memory map: (fault address prefixed with --->)
--->ab15f000-ab162fff r-x 0 4000 /system/xbin/crasher (BuildId:b9527db01b5cf8f5402f899f64b9b121)

有两个值得注意的地方。首先这一行以"—>"为前缀。对非空指针引起的crash内存映射关系非常有用。如果fault address的值很小,它可能是某个变量的空指针引起的。通过查看memory map可识别的一些可能存在的问题包括:读/写延伸到内存块末尾之外;在内存块开始之前读/写;尝试执行非代码内容;在堆栈末尾之外运行; 尝试写数据到代码段。第二个值得注意的地方是可执行程序和共享库文件显示BuildId信息,因此能准确地知道代码的哪个版本发生过crash。

ab163000-ab163fff r--      3000      1000  /system/xbin/crasher
ab164000-ab164fff rw-         0      1000
f6c80000-f6d7ffff rw-         0    100000  [anon:libc_malloc]

在Android中堆不必是一块单独的区域。堆区域会以[anon:libc_malloc]标记。

分析工具及使用方法

addr2line

addr2line是用来获得指定动态链接库文件或者可执行文件中指定地址对应的源代码信息。通过backtrace提供的PC地址查询对应的符号,定位到文件、函数和行数。查看帮助:

$ addr2line -h
Usage: 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 stdin
 The 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

使用addr2line需要指定一个包含符号的共享库和(一个或多个)地址:

addr2line -aCpife <library> [addr(s)]

【提示】Mangle和Demangle
C++和Java语言支持函数重载功能,即传入不同参数的函数名可以重名。为了区分这些名称相同的函数,C++和Java把它们编码成底层汇编时为每个版本确定唯一标识。这个过程被称为mangle。相反,将底层名字转化为用户层可以阅读的名字叫demangle。

例如:

$ aarch64-linux-android-addr2line -aCpife audio.primary.so 0x13b50
0x00013b50: voice_set_parameters at /hardware/qcom/audio/hal/voice.c:562

【提示】NDK中的addr2line的位置
android-ndk-xx/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-addr2line
android-ndk-xx/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-addr2line

在Ubuntu系统中已集成addr2line,但是一般建议使用android源码或NDK中提供的版本。各个版本addr2line的区别在于支持的目标不同:

$ source build/envsetup.sh && lunch

$ arm-linux-androideabi-addr2line –h
arm-linux-androideabi-addr2line: supported targets: **elf32-littlearm** elf32-bigarm elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex

$ aarch64-linux-android-addr2line -h
aarch64-linux-android-addr2line: supported targets: elf64-littleaarch64 elf64-bigaarch64 elf32-littleaarch64 elf32-bigaarch64 **elf32-littlearm** elf32-bigarm elf64-little elf64-big elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex

$ addr2line -h
addr2line: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 a.out-i386-linux pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 plugin srec symbolsrec verilog tekhex binary ihex

查看共享库的ELF信息,选择合适的addr2line版本。

$ aarch64-linux-android-readelf -h audio.primary.so
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             **ELF32**
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              **DYN (Shared object file)**
  Machine:                           **ARM**
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1121040 (bytes into file)
  Flags:                             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         39
  Section header string table index: 38

ndk-stack

Andorid NDK中提供的一个工具,位置是android-ndk-path/ndk-stack。在logcat日志中可以使用ndk-stack过滤出现的stack trace,也用于将shared library中的任何地址转换成源代码中的<source-file>:<line-number>值,使问题精准定位。例如,它将下面的日志:

I/DEBUG   (   31): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (   31): Build fingerprint: 'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys'
I/DEBUG   (   31): pid: 351, tid: 351  %gt;%gt;%gt; /data/local/ndk-tests/crasher <<<
I/DEBUG   (   31): signal 11 (SIGSEGV), fault addr 0d9f00d8
I/DEBUG   (   31):  r0 0000af88  r1 0000a008  r2 baadf00d  r3 0d9f00d8
I/DEBUG   (   31):  r4 00000004  r5 0000a008  r6 0000af88  r7 00013c44
I/DEBUG   (   31):  r8 00000000  r9 00000000  10 00000000  fp 00000000
I/DEBUG   (   31):  ip 0000959c  sp be956cc8  lr 00008403  pc 0000841e  cpsr 60000030
I/DEBUG   (   31):          #00  pc 0000841e  /data/local/ndk-tests/crasher
I/DEBUG   (   31):          #01  pc 000083fe  /data/local/ndk-tests/crasher
I/DEBUG   (   31):          #02  pc 000083f6  /data/local/ndk-tests/crasher
I/DEBUG   (   31):          #03  pc 000191ac  /system/lib/libc.so
I/DEBUG   (   31):          #04  pc 000083ea  /data/local/ndk-tests/crasher
I/DEBUG   (   31):          #05  pc 00008458  /data/local/ndk-tests/crasher
I/DEBUG   (   31):          #06  pc 0000d362  /system/lib/libc.so
I/DEBUG   (   31):

转化成易于阅读的输出(相当于按行多次执行addr2line):

********** Crash dump: **********
Build fingerprint: 'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys'
pid: 351, tid: 351  >>> /data/local/ndk-tests/crasher <<<
signal 11 (SIGSEGV), fault addr 0d9f00d8
Stack frame #00  pc 0000841e  /data/local/ndk-tests/crasher : Routine zoo in /tmp/foo/crasher/jni/zoo.c:13
Stack frame #01  pc 000083fe  /data/local/ndk-tests/crasher : Routine bar in /tmp/foo/crasher/jni/bar.c:5
Stack frame #02  pc 000083f6  /data/local/ndk-tests/crasher : Routine my_comparison in /tmp/foo/crasher/jni/foo.c:9
Stack frame #03  pc 000191ac  /system/lib/libc.so
Stack frame #04  pc 000083ea  /data/local/ndk-tests/crasher : Routine foo in /tmp/foo/crasher/jni/foo.c:14
Stack frame #05  pc 00008458  /data/local/ndk-tests/crasher : Routine main in /tmp/foo/crasher/jni/main.c:19
Stack frame #06  pc 0000d362  /system/lib/libc.so

使用ndk-stack需要指定一个包含符号的共享库目录。如果是使用NDK编译的(ndk-build),这些共享库存放$PROJECT_PATH/obj/local/<abi>下,其中<abi>代表设备的ABI。默认系统使用armeabi ABI。有两种用法,一种是logcat日志直接输入到ndk-stack:

adb logcat | $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi

也可以使用-dump选项指定一个logcat日志作为输入,例如:

adb logcat > /tmp/foo.txt
$NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi -dump foo.txt

当它解析logcat日志时会以星号行作为解析起始行,例如:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

案例

$./ndk-stack –sym  ~/symbols/system/lib -dump ~/foo.txt
********** Crash dump: **********
Build fingerprint: 'xxxx'
pid: 774, tid: 2212, name: Binder_3 >>> /system/bin/mediaserver <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
Stack frame #00 pc 00004cd0 /system/lib/libcutils.so (hashmapGet+3): Routine hashmapGet at ~/system/core/libcutils/hashmap.c:222
Stack frame #01 pc 00005e69 /system/lib/libcutils.so (str_parms_get_str+8): Routine str_parms_get_str at ~/system/core/libcutils/str_parms.c:274
Stack frame #02 pc 00027e64 /system/lib/hw/audio.primary.so (voice_extn_set_parameters+484)
Stack frame #03 pc 00013b5c /system/lib/hw/audio.primary.so (voice_set_parameters+56)
Stack frame #04 pc 0000e3d8 /system/lib/hw/audio.primary.so
Stack frame #05 pc 0002bab9 /system/lib/libaudioflinger.so: Routine android::AudioFlinger::setParameters(int, android::String8 const&) at ~/frameworks/av/services/audioflinger/AudioFlinger.cpp:1102 (discriminator 1)
Stack frame #06 pc 00063893 /system/lib/libmedia.so (_ZN7android14BnAudioFlinger10onTransactEjRKNS_6ParcelEPS1_j+1438):
...

stack

与ndk-stack类似,stack是android源码中提供分析crash日志的工具,位置是<android-source-code>/development/scripts/stack。用于将shared library中的任何地址转换成源代码中的<source-file>:<line-number>值,使问题精准定位。

使用stack需要指定一个包含符号的共享库目录、crash日志和ABI。例如:

stack --arch=arm --symbols-dir=$(symbols_directory) $(crash_log_file)

$ stack -h
  usage: stack [options] [FILE]
  --arch=arm|arm64|mips|mips64|x86|x86_64       the target architecture
  --symbols-dir=path
       the path to a symbols dir, such as =/tmp/out/target/product/dream/symbols
FILE should contain a stack trace in it somewhere
       the tool will find that and re-print it with
       source files and line numbers.  If you don't
       pass FILE, or if file is -, it reads from
       stdin.

案例

$./development/scripts/stack --arch=arm --symbols-dir=~/symbols/ ~/workspace/foo.txt
Searching for native crashes in ~/workspace/foo.txt
Reading symbols from ~/symbols/
Revision: '0'
ABI: 'arm'
pid: 774, tid: 2212, name: Binder_3 >>> /system/bin/mediaserver <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
r0 00000000 r1 efaf4a4c r2 de00095c r3 00000100
r4 00000100 r5 00000000 r6 de00095c r7 0001581c
r8 f6d57ec0 r9 e0fb63f0 sl efaf4a4c fp 00000000
ip f6ff1e30 sp de000918 lr f6fe6e6d pc f6fe5cd0 cpsr 600f0030
Using arm toolchain from: ~/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/
Stack Trace:
  RELADDR   FUNCTION                                                    FILE:LINE
  00004cd0  hashmapGet+4                                                                                                 ~/system/core/libcutils/hashmap.c:222
  00005e69  str_parms_get_str+8                                                                                             ~/system/core/libcutils/str_parms.c:274
  00027e64  voice_extn_set_parameters+484                                                                                   ~/hardware/qcom/audio/hal/voice_extn/voice_extn.c:481
  00013b5c  voice_set_parameters+56                                                                                         ~/hardware/qcom/audio/hal/voice.c:564
  0000e3d8  adev_set_parameters+220                                                                                         ~/hardware/qcom/audio/hal/audio_hw.c:4149
  0002bab9  android::AudioFlinger::setParameters(int, android::String8 const&)+120                                          ~/frameworks/av/services/audioflinger/AudioFlinger.cpp:1102 (discriminator 1)
  00063893  android::BnAudioFlinger::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+1438  ~/frameworks/av/media/libmedia/IAudioFlinger.cpp:1078
  000199c9  android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+60             ~/frameworks/native/libs/binder/Binder.cpp:108
  0001ed5b  android::IPCThreadState::executeCommand(int)+550                                                                ~/frameworks/native/libs/binder/IPCThreadState.cpp:1091

objdump

反汇编工具。

Usage: aarch64-linux-android-objdump <option(s)> <file(s)>
 Display information from object <file(s)>.
 At least one of the following switches must be given:
  -a, --archive-headers    Display archive header information
  -f, --file-headers       Display the contents of the overall file header
  -p, --private-headers    Display object format specific file header contents
  -P, --private=OPT,OPT... Display object format specific contents
  -h, --[section-]headers  Display the contents of the section headers
  -x, --all-headers        Display the contents of all headers
  -d, --disassemble        Display assembler contents of executable sections
  -D, --disassemble-all    Display assembler contents of all sections
  -S, --source             Intermix source code with disassembly
  -s, --full-contents      Display the full contents of all sections requested
  -g, --debugging          Display debug information in object file
  -e, --debugging-tags     Display debug information using ctags style
  -G, --stabs              Display (in raw form) any STABS info in the file
  -W[lLiaprmfFsoRt] or
  --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
          =frames-interp,=str,=loc,=Ranges,=pubtypes,
          =gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
          =addr,=cu_index]
                           Display DWARF info in the file
  -t, --syms               Display the contents of the symbol table(s)
  -T, --dynamic-syms       Display the contents of the dynamic symbol table
  -r, --reloc              Display the relocation entries in the file
  -R, --dynamic-reloc      Display the dynamic relocation entries in the file
  @<file>                  Read options from <file>
  -v, --version            Display this program's version number
  -i, --info               List object formats and architectures supported
  -H, --help               Display this information

使用方法

Usage: objdump -S $(objfile) > $(output_file)

例如,反汇编动态链接库文件并存入一个文件:
objdump -S -D libc.so > deassmble_libc.txt

通过汇编代码定位错误的原因,一些复杂的问题可以通过这种方式得到解决。

案例

$ aarch64-linux-android-objdump -D ~/symbols/system/lib/libcutils.so > libcutils.so.txt

也可以只显示一部分:

$ aarch64-linux-android-objdump -d -l --start-address=0x4ca0 --stop-address=0x4ce0 ~/symbols/system/lib/libcutils.so | aarch64-linux-android-c++filt

【提示】NDK中的objdump的位置
android-ndk-rxx/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump
android-ndk-rxx/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objdump

【提示】ARM汇编基本指令
ldr 从指定地址加载寄存器运算数
str 将寄存器运算数存到指定地址
add 两个寄存器相加
adds 寄存器和数值相加
mov 寄存器之间赋值
movs 将数值赋给寄存器
cmp 比较两个寄存器
bx lr 表示一个函数执行结束,跳转到lr中存放的地址处。
lr就是连接寄存器(Link Register, LR),在ARM体系结构中LR的特殊用途有两种:一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行。
当通过BL或BLX指令调用子程序时,硬件自动将子程序返回地址保存在R14寄存器中。在子程序返回时,把LR的值复制到程序计数器PC即可实现子程序返回。如,可以使用MOV PC, LR或者BX LR来完成子程序返回。另外,也可以在在子程序入口处使用下面的指令将LR保存到栈中

欢迎关注我的公众号灰度五十,分享各类音视频、移动开发知识~
在这里插入图片描述

文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~
在这里插入图片描述

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Android原生崩溃(android native crash)是指在Android平台上,由于代码执行错误或者资源耗尽等原因,导致应用程序无法正常运行而崩溃或者闪退的现象。原生崩溃产生的原因可能是由于C或者C++代码编写错误、内存溢出、线程竞争等。针对原生崩溃问题,开发人员需要使用调试工具进行定位和修复。 ### 回答2: Android Native Crash发生在安卓应用程序运行时,由于C或C++库的错误或者其他原因导致应用程序崩溃。有时候Native Crash可能会影响整个设备,尤其是当Native Crash发生在系统级别的代码中时。 产生Native Crash的原因通常包括以下几个方面: 1. 内存管理问题:Native Crash通常与内存管理问题相关,这可能是由于访问未初始化的内存,使用错误的指针或释放已释放的内存等原因引起的。 2. 硬件问题:Native Crash也可能与设备相关的硬件问题有关,例如访问不可用的硬件资源或硬件设备故障。 3. 应用程序代码问题:Native Crash可能发生在应用程序代码的错误、资源泄漏、堆栈溢出等问题引起的。 4. 第三方库问题:Native Crash也可能由第三方库中的错误或bug引起。这些库可能没有经过充分的测试,或者与设备硬件不兼容。 为了更好地解决Native Crash问题,开发者可以通过日志或崩溃报告(Crash Report)来检测和分析崩溃日志,并查看堆栈跟踪信息来确定导致Native Crash的来源。在开发过程中,经常使用除了自己编写的代码之外的第三方库时,还可以考虑使用崩溃的回溯工具,如Firebase Crashlytics等。 总之,Native CrashAndroid应用程序开发过程中经常遇到的问题,它可能会对用户体验和开发进度产生重大影响,因此开发者需要强化对Native Crash的理解和分析能力,以更好地解决Native Crash的问题。 ### 回答3: Android Native Crash,指的是在 Android 系统中发生的本地崩溃。本地崩溃是指应用程序使用本地代码,而不是 Java 代码,导致应用程序崩溃的问题。本地代码可以是编写在 C/C++ 等语言中的库,或是应用程序本身所编写的 Native 代码。 本地代码崩溃后,会在应用程序崩溃的同时发生。本地崩溃可发生在 Android 应用程序中的任何部分,比如,应用程序本地库、Android 系统库等等。大多数情况下,本地崩溃是由于访问无效内存、访问不合法指针、数组越界等问题引起的。 为了解决本地崩溃问题,Android 提供了一些工具和技术。比如,使用 ndk-stack 工具可以解析本地崩溃日志。Android Studio 也提供了一些工具来分析应用程序崩溃的原因。同时,我们也可以在应用程序中添加自定义的日志跟踪信息,以便更好地了解应用程序的崩溃原因。 还有一些其他的技术可以使用,如使用 Google 的 Crashlytics 来跟踪应用程序的崩溃问题。这个平台可以帮助开发者收集和分析应用程序在用户设备上的崩溃信息,并彻底解决这些问题。此外,Android 还提供了一些实用工具和技术,如 ANR(Application Not Responding)错误处理器、Tracer for OpenGL ES 和 Traceview 示例等。 总之,Android Native CrashAndroid 系统中常见的崩溃问题之一。了解它的原因并采用适当的解决方案可以使得我们更好地保持我们的应用程序的稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhanghui_cuc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值