分析CppCrash(进程崩溃)

进程崩溃指C/C++运行时崩溃。FaultLogger模块提供进程崩溃故障检测、日志采集、日志存储、日志上报的能力,为开发者提供详细的维测日志以辅助故障定位。

本文将分别介绍进程崩溃检测能力、崩溃日志获取和进程崩溃日志分析。在使用本指导分析日志前,需要开发者对C/C++程序堆栈信息有相关基础知识。

进程崩溃检测能力

进程崩溃基于Linux信号机制,目前主要支持对以下崩溃异常信号的处理:

信号值 信号 解释 触发原因
4 SIGILL 非法指令。 进程执行了非法、格式错误、未知或特权指令。
5 SIGTRAP 断点或陷阱异常。 异常或trap指令发生。
6 SIGABRT 进程终止。 进程异常终止,通常为进程自身调用标准函数库的abort()函数。
7 SIGBUS 非法内存访问。 进程访问了对齐或者不存在的物理地址。
8 SIGFPE 浮点异常。 进程执行了错误的算术运算,如除数为0、浮点溢出、整数溢出等。
11 SIGSEGV 无效内存访问。 进程访问了无效内存引用。
16 SIGSTKFLT 栈错误。 处理器执行了错误的栈操作,如栈空时弹出、栈满时压入。
31 SIGSYS 错误的系统调用。 系统调用时使用了错误或非法参数。

崩溃日志获取

进程崩溃日志是一种故障日志,与应用无响应日志、JS应用崩溃等都由FaultLogger模块进行管理,可通过如下三种方式获取:

  • 方式一:通过shell获取日志

    1. 进程崩溃后,CppCrash文件会生成在“设备/data/log/faultlog/faultlogger/”路径下,故障日志文件名格式为“cppcrash-进程名-进程UID-秒级时间”,包含设备名、系统版本、进程崩溃调用栈等信息。

      cppcrash-faultlogger-log

    2. “设备/data/log/faultlog/temp/”路径下的故障日志,其文件名格式为“cppcrash-进程PID-系统毫秒级时间戳”,包含进程崩溃时栈内存、进程maps等信息。

      cppcrash-temp-log

  • 方式二:通过DevEco Studio获取日志

    DevEco Studio会收集“设备/data/log/faultlog/faultlogger/”路径下的进程崩溃故障日志到FaultLog下,根据进程名和故障和时间分类显示。获取日志的方法参见:DevEco Studio使用指南-FaultLog

  • 方式三:通过faultlogger接口获取

    FaultLogger对外提供了面向应用的故障查询接口,可以查询应用自己的故障记录,以结构化的数据返回。接口的使用以及获取的故障信息规格详见@ohos.faultLogger (故障日志获取)

进程崩溃日志分析

日志格式

以下是一份DevEco Studio归档在FaultLog的进程崩溃日志的核心内容,与“设备/data/log/faultlog/faultlogger”下归档的日志内容相同。

Generated by HiviewDFX@OpenHarmony
==================================================================
Device info:OpenHarmony 3.2     <- 设备信息
Build info:OpenHarmony 4.0.5.5  <- 版本信息
Fingerprint:5e6a663122524ea7c1772b0f16f23b50a2b5753c930ed681a721ac2e8c197645 <- 标识故障特征
Module name:crasher_c   <- 模块名
Version:1.0.0 <- 版本号
VersionCode:1000000 <- 版本代码
PreInstalled:No <- 是否预置
Foreground:Yes  <- 崩溃时刻应用是否在前台
Timestamp:2017-08-05 18:23:53.000 <- 故障发生时间戳
Pid:1369  <- 进程号
Uid:0  <- 用户ID
Process life time:29s   <- 进程存活时间
Reason:Signal:SIGSEGV(SEGV_MAPERR)@00000000 <- 故障原因
Fault thread Info:
Tid:1369 Name:crasher <- 故障线程号,线程名
#00 pc 00007be6 /data/crasher_c(SegmentFaultException+1)(1a75896316b332b9e7469de9d5a3e4e4)  <- 调用栈
#01 pc 000082c1 /data/crasher_c(ParseAndDoCrash+412)(1a75896316b332b9e7469de9d5a3e4e4)
#02 pc 000083e1 /data/crasher_c(main+32)(1a75896316b332b9e7469de9d5a3e4e4)
#03 pc 0006ef10 /system/lib/ld-musl-arm.so.1
#04 pc 000038a0 /data/crasher_c(_start_c+84)(1a75896316b332b9e7469de9d5a3e4e4)
#05 pc 00003844 /data/crasher_c(1a75896316b332b9e7469de9d5a3e4e4)
...
Registers:  <-  故障现场寄存器
r0:00000000 r1:008a262b r2:00000000 r3:00000000
r4:ff9b3e39 r5:00000002 r6:008a93c1 r7:f7872788
r8:f7777976 r9:f7872318 r10:f7872418
fp:ff9b39a8 ip:f777deea sp:ff9b3968 lr:008a92c5 pc:008a8be6
Other thread info: <- 其他线程信息
Tid:1370, Name:crasher <- 线程号,线程名
#00 pc 00000000001aa2e0 /system/lib/ld-musl-aarch64.so.1(sleep+52) <- 调用栈
#01 pc 0000000000005188 /data/crasher_c(abea41f851573f032e3e73a1e17d209d)
#02 pc 000000000019f234 /system/lib/ld-musl-aarch64.so.1
#03 pc 000000000009f8ac /system/lib/ld-musl-aarch64.so.1
...
Memory near registers:  <-  故障现场寄存器附近内存
r1(/data/crasher_c):
    008a2620 53697274
    008a2624 45534749
    ...
    008a269c 20202020
r4([stack]):
    ff9b3e30 68736172
    ff9b3e34 635f7265
    ...
    ff9b3eac 3d455a49
r6(/data/crasher_c):
    008a93b8 bf00bd80
    008a93bc ffff96d7
    ...
    008a9434 bd802000
r7(/system/lib/ld-musl-arm.so.1):
    f7872780 00000000
    f7872784 00000000
    ...
    f78727fc 00000000
r8(/system/lib/ld-musl-arm.so.1):
    f777796c 00646e65
    f7777970 255f7325
    ...
    f77779e8 6d20726f
r9(/system/lib/ld-musl-arm.so.1):
    f7872310 00000001
    f7872314 00000000
    ...
    f787238c 00000000
r10(/system/lib/ld-musl-arm.so.1):
    f7872410 00010000
    f7872414 00000000
    ...
    f787248c 00000000
fp([stack]):
    ff9b39a0 00000001
    ff9b39a4 f7872418
    ...
    ff9b3a1c ff9b3fd8
r12(/system/lib/ld-musl-arm.so.1):
    f777dee0 203e4425
    f777dee4 25207461
    ...
    f777df5c 75747372
sp([stack]):
    ff9b3960 ff9b39a8
    ff9b3964 008a91c1
    ...
    ff9b39dc 00000000
lr(/data/crasher_c):
    008a92bc e01cfc1b
    008a92c0 fc90f7ff
    ...
    008a9338 ffff9609
pc(/data/crasher_c):
    008a8bdc ffff9f07
    008a8be0 ffff9eb7
    ...
    008a8c58 edeaf000
FaultStack:  <- 崩溃线程栈的地址空间
	...
    ff9b3964 008a91c1
sp0:ff9b3968 0a9b39d4 <-#00栈顶
    ff9b396c f5275648
    ...
    ff9b4168 ffffffff

Maps:   <-  故障时进程maps
8a1000-8a4000 r--p 00000000 /data/crasher_c
8a4000-8aa000 r-xp 00002000 /data/crasher_c
8aa000-8ab000 r--p 00007000 /data/crasher_c
8ab000-8ac000 rw-p 00007000 /data/crasher_c
ada000-adb000 ---p 00000000 [heap]
adb000-adc000 rw-p 00000000 [heap]
...
f7759000-f77af000 r--p 00000000 /system/lib/ld-musl-arm.so.1
f77af000-f786f000 r-xp 00055000 /system/lib/ld-musl-arm.so.1
f786f000-f7871000 r--p 00114000 /system/lib/ld-musl-arm.so.1
f7871000-f7873000 rw-p 00115000 /system/lib/ld-musl-arm.so.1
f7873000-f787f000 rw-p 00000000 [anon:ld-musl-arm.so.1.bss]
ff993000-ff9b4000 rw-p 00000000 [stack]
ffff0000-ffff1000 r-xp 00000000 [vectors]

OpenFiles:   <-  故障时进程打开文件Fd信息
0->/dev/null native object of unknown type 0
1->/dev/null native object of unknown type 0
2->/dev/null native object of unknown type 0
3->socket:[20842] native object of unknown type 0
...
46->/dev/null FILE* 4155732440
47->/dev/mali0 native object of unknown type 0
48->/dev/null FILE* 4155732136
49->anon_inode:[eventfd] native object of unknown type 0

通过Shell获取的“/data/log/faultlog/temp”获取到的日志内容格式如下:

Timestamp:2017-08-06 00:54:30.000   <- 故障发生时间
Pid:1369   <- 进程号
Uid:0  <- 用户ID
Process name:crasher   <- 进程名
Process life time:29s   <- 进程存活时间
Reason:Signal:SIGSEGV(SEGV_MAPERR)@00000000   <- 异常信息
Fault thread Info:
Tid:1369, Name:crasher  <- 异常线程号与线程名
#00 pc 00007be6 /data/crasher_c(SegmentFaultException+1)(1a75896316b332b9e7469de9d5a3e4e4)  <- 调用栈
#01 pc 000082c1 /data/crasher_c(ParseAndDoCrash+412)(1a75896316b332b9e7469de9d5a3e4e4)
#02 pc 000083e1 /data/crasher_c(main+32)(1a75896316b332b9e7469de9d5a3e4e4)
#03 pc 0006ef10 /system/lib/ld-musl-arm.so.1
#04 pc 000038a0 /data/crasher_c(_start_c+84)(1a75896316b332b9e7469de9d5a3e4e4)
#05 pc 00003844 /data/crasher_c(1a75896316b332b9e7469de9d5a3e4e4)
Registers:  <-  故障现场寄存器
r0:00000000 r1:008a262b r2:00000000 r3:00000000
r4:ff9b3e39 r5:00000002 r6:008a93c1 r7:f7872788
r8:f7777976 r9:f7872318 r10:f7872418
fp:ff9b39a8 ip:f777deea sp:ff9b3968 lr:008a92c5 pc:008a8be6
Other thread info: <- 其他线程信息
Tid:1370, Name:crasher <- 线程id,线程名
#00 pc 00000000001aa2e0 /system/lib/ld-musl-aarch64.so.1(sleep+52) <- 调用栈
#01 pc 0000000000005188 /data/crasher_c(abea41f851573f032e3e73a1e17d209d)
#02 pc 000000000019f234 /system/lib/ld-musl-aarch64.so.1
#03 pc 000000000009f8ac /system/lib/ld-musl-aarch64.so.1
Memory near registers:  <-  故障现场寄存器附近内存
r1(/data/crasher_c):
    008a2620 53697274
    008a2624 45534749
    ...
    008a269c 20202020
r4([stack]):
    ff9b3e30 68736172
    ff9b3e34 635f7265
    ...
    ff9b3eac 3d455a49
r6(/data/crasher_c):
    008a93b8 bf00bd80
    008a93bc ffff96d7
    ...
    008a9434 bd802000
r7(/system/lib/ld-musl-arm.so.1):
    f7872780 00000000
    f7872784 00000000
    ...
    f78727fc 00000000
r8(/system/lib/ld-musl-arm.so.1):
    f777796c 00646e65
    f7777970 255f7325
    ...
    f77779e8 6d20726f
r9(/system/lib/ld-musl-arm.so.1):
    f7872310 00000001
    f7872314 00000000
    ...
    f787238c 00000000
r10(/system/lib/ld-musl-arm.so.1):
    f7872410 00010000
    f7872414 00000000
    ...
    f787248c 00000000
fp([stack]):
    ff9b39a0 00000001
    ff9b39a4 f7872418
    ...
    ff9b3a1c ff9b3fd8
r12(/system/lib/ld-musl-arm.so.1):
    f777dee0 203e4425
    f777dee4 25207461
    ...
    f777df5c 75747372
sp([stack]):
    ff9b3960 ff9b39a8
    ff9b3964 008a91c1
    ...
    ff9b39dc 00000000
lr(/data/crasher_c):
    008a92bc e01cfc1b
    008a92c0 fc90f7ff
    ...
    008a9338 ffff9609
pc(/data/crasher_c):
    008a8bdc ffff9f07
    008a8be0 ffff9eb7
    ...
    008a8c58 edeaf000
FaultStack:   <- 崩溃线程的栈地址空间
	...
    ff9b3964 008a91c1
sp0:ff9b3968 0a9b39d4 <- #00栈顶
    ff9b396c f5275648
    ...
    ff9b4168 ffffffff

Maps:   <-  故障时进程maps
8a1000-8a4000 r--p 00000000 /data/crasher_c
8a4000-8aa000 r-xp 00002000 /data/crasher_c
8aa000-8ab000 r--p 00007000 /data/crasher_c
8ab000-8ac000 rw-p 00007000 /data/crasher_c
ada000-adb000 ---p 00000000 [heap]
adb000-adc000 rw-p 00000000 [heap]
...
f7759000-f77af000 r--p 00000000 /system/lib/ld-musl-arm.so.1
f77af000-f786f000 r-xp 00055000 /system/lib/ld-musl-arm.so.1
f786f000-f7871000 r--p 00114000 /system/lib/ld-musl-arm.so.1
f7871000-f7873000 rw-p 00115000 /system/lib/ld-musl-arm.so.1
f7873000-f787f000 rw-p 00000000 [anon:ld-musl-arm.so.1.bss]
ff993000-ff9b4000 rw-p 00000000 [stack]
ffff0000-ffff1000 r-xp 00000000 [vectors]

OpenFiles:   <-  故障时进程打开文件Fd信息
0->/dev/null native object of unknown type 0
1->/dev/null native object of unknown type 0
2->/dev/null native object of unknown type 0
3->socket:[20842] native object of unknown type 0
...
46->/dev/null FILE* 4155732440
47->/dev/mali0 native object of unknown type 0
48->/dev/null FILE* 4155732136
49->anon_inode:[eventfd] native object of unknown type 0

通过日志定位问题

  1. 通过故障日志等基础信息确定问题模块和故障类别。

    通过崩溃进程名一般能定界到故障的模块,通过信号能判断崩溃的原因,通过堆栈中的方法名,可以复原崩溃栈的函数调用链。

    如范例中的SIGSEGV是由Linux内核抛出,原因为访问了非法内存地址,问题发生在SegmentFaultException函数中。

    大部分场景下崩溃栈的最上层就是崩溃的原因,如空指针访问以及程序主动终止运行。少部分场景调用栈无法定位原因,需要查看其他信息,例如踩内存或者栈溢出的问题场景可以查看寄存器信息和maps等内容。

  2. 通过addr2line工具解析出代码行号来复原崩溃现场调用栈。

    使用Linux addr2line工具解析崩溃栈的行号,需要使用带调试信息的二进制。一般在版本编译或者应用编译时会生成带调试信息的二进制。

    应用二进制位置在DevEco Studio应用构建的临时目录中,如build/default/intermediates/libs。系统二进制位置在如下目录,对于直接获取的版本,二进制会归档在完整镜像包中。

    \代码根路径\out\产品\lib.unstripped
    \代码根路径\out\产品\exe.unstripped
    
    • Linux环境下,开发者可以通过apt-get install addr2line命令安装addr2line软件来使用。
    • 在应用开发环境下,开发者还可以使用SDK中归档的llvm-addr2line工具来解析行号,使用方法一致。

    使用addr2line工具根据偏移地址解析行号:

    [product name]为具体设备名。

    root:~/OpenHarmony/out/[product name]/exe.unstripped/hiviewdfx/faultloggerd$ addr2line -Cfie crasher 00007be6
    base/hiviewdfx/faultloggerd/tools/crasher/dfx_crasher.c:123
    

    示例中的崩溃故障是由于访问了空指针,代码行为dfx_crasher.c文件的123行。修改后可以避免发生此崩溃。

    另外,使用addr2line后,如果得出的行号看起来不是很正确,可以考虑对地址进行微调(如减1),或者考虑关闭一些编译优化,已知使用LTO的二进制可能无法正确获得行号。