# 分析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](figures/20230407112159.png) 2. “设备/data/log/faultlog/temp/”路径下的故障日志,其文件名格式为“cppcrash-进程PID-系统毫秒级时间戳”,包含进程崩溃时栈内存、进程maps等信息。 ![cppcrash-temp-log](figures/20230407111853.png) - 方式二:通过DevEco Studio获取日志 DevEco Studio会收集“设备/data/log/faultlog/faultlogger/”路径下的进程崩溃故障日志到FaultLog下,根据进程名和故障和时间分类显示。获取日志的方法参见:[DevEco Studio使用指南-FaultLog](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ide-debug-hilog-0000001172459337-V2#section974519209435)。 - 方式三:通过faultlogger接口获取 FaultLogger对外提供了面向应用的故障查询接口,可以查询应用自己的故障记录,以结构化的数据返回。接口的使用以及获取的故障信息规格详见[@ohos.faultLogger (故障日志获取)](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md)。 ## 进程崩溃日志分析 ### 日志格式 以下是一份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 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] ``` 通过Shell获取的“/data/log/faultlog/temp”获取到的日志内容格式如下: ``` Timestamp:2017-08-06 00:54:30.000 <- 故障发生时间 Pid:1369 <- 进程号 Uid:0 <- 用户ID Process name:crasher <- 进程名 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] ``` ### 通过日志定位问题 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的二进制可能无法正确获得行号。