鸿蒙开发-崩溃日志分析全指南
鸿蒙进程崩溃问题定位指南 本文介绍了鸿蒙系统中进程崩溃问题的定位方法,主要包含三部分内容: 崩溃日志获取:提供三种获取方式,包括DevEco Studio直接收集、hiAppEvent接口订阅和设备ROOT模式下通过shell获取,详细说明了日志存储路径和内容格式。 崩溃栈分析: DevEco Studio可直接跳转代码行号 使用SDK的llvm-addr2line工具解析行号 DevEco St
📑往期推文全新看点(文中附带最新·鸿蒙全栈学习笔记)
✒️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
✒️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
✒️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
✒️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
✒️ 记录一场鸿蒙开发岗位面试经历~
✒️ 持续更新中……
进程崩溃指C/C++运行时崩溃。FaultLogger模块提供进程崩溃故障检测、日志采集、日志存储、日志上报的能力,为开发者提供详细的维测日志以辅助故障定位。
本文将分别介绍进程崩溃检测能力、崩溃问题定位分析思路。在使用本指导分析处理崩溃日志前,需要开发者了解C/C++程序堆栈信息的基础知识。
问题定位步骤与思路
崩溃日志获取
进程崩溃日志是一种故障日志,与应用无响应日志、JS应用崩溃等都由FaultLogger模块进行管理,可通过以下方式获取:
-
方式一:通过DevEco Studio获取日志
DevEco Studio会收集设备/data/log/faultlog/faultlogger/路径下的进程崩溃故障日志到FaultLog下,根据进程名和故障和时间分类显示。
-
方式二:通过hiAppEvent接口订阅
hiAppEvent 提供了故障订阅接口,可以订阅各类故障打点。
-
方式三:设备ROOT模式下通过shell获取日志
- 进程崩溃后,系统会在设备/data/log/faultlog/temp/路径下的故障日志,其文件名格式为cppcrash-进程PID-系统毫秒级时间戳,日志内容包含进程崩溃调用栈,进程崩溃现场寄存器、栈内存、maps,进程文件句柄列表等信息。

2. CppCrash故障会同步在/data/log/faultlog/faultlogger/路径下生成一份完善日志,故障日志文件名格式为cppcrash-进程名-进程UID-秒级时间,日志内容较/data/log/faultlog/temp下日志更加完善,增加有设备名,系统版本,进程流水日志等信息。

说明
目前主要支持的崩溃异常信号类型参见 故障类型,对获取的日志进行分析详见日志规格 。
基于崩溃栈定位行号
方式一:DevEco Studio 开发者环境下,支持调用栈直接跳转到对应行号
在应用开发场景,对于应用自身的动态库,生成的cppcrash堆栈可直接跳转到代码行处,支持Native栈帧和JS栈帧,无需开发者自行进行解行号操作。对于部分未能解析跳转到对应行号的栈帧,可参考方式二解析。

方式二:通过SDK llvm-addr2line 工具定位行号
- 获取符号表 获取崩溃栈中so文件对应的带符号版本,保证与应用/系统内运行时的so文件版本一致。 对于应用自身的动态库,经DevEco编译构建,生成在工程的 /build/default/intermediates/libs 目录下,默认是带符号的版本。可通过Linux file 命令查询二进制文件的 BuildID 以核对是否匹配。其中,BuildID 是用于标识二进制文件的唯一标识符,通常由编译器在编译时生成,not stripped 表示该动态库是包含符号表的。
$ file libbabel.so
libbabel.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=fdb1b5432b9ea4e2a3d29780c3abf30e2a22da9d, with debug_info, not stripped
**说明:**对于系统动态库符号表,随版本进行归档。
- 通过 llvm-addr2line 工具定位行号 llvm-addr2line 工具归档在:[SDK DIR PATH]\OpenHarmony\11\native\llvm\bin 路径下。根据实际的SDK版本路径略有不同,开发者请自行识别或在路径下搜索。 例如有堆栈如下(有省略):
Generated by HiviewDFX@OpenHarmony
================================================================
Device info:OpenHarmony 3.2
Build info:OpenHarmony 5.0.0.22
Fingerprint:50577c0a1a1b5644ac030ba8f08c241cca0092026b59f29e7b142d5d4d5bb934
Module name:com.samples.recovery
Version:1.0.0
VersionCode:1000000
PreInstalled:No
Foreground:No
Timestamp:2017-08-05 17:03:40.000
Pid:2396
Uid:20010044
Process name:com.samples.recovery
Process life time:7s
Reason:Signal:SIGSEGV(SEGV_MAPERR)@0000000000 probably caused by NULL pointer dereference
Tid:2396, Name:amples.recovery
# 00 pc 00003510 /data/storage/el1/bundle/libs/arm/libentry.so(TriggerCrash(napi_env__*, napi_callback_info__*)+24)(446ff75d3f6a518172cc52e8f8055650b02b0e54)
# 01 pc 0002b0c5 /system/lib/platformsdk/libace_napi.z.so(panda::JSValueRef ArkNativeFunctionCallBack<true>(panda::JsiRuntimeCallInfo*)+448)(a84fbb767fd826946623779c608395bf)
# 02 pc 001e7597 /system/lib/platformsdk/libark_jsruntime.so(panda::ecmascript::EcmaInterpreter::RunInternal(panda::ecmascript::JSThread*, unsigned char const*, unsigned long long*)+14710)(106c552f6ce4420b9feac95e8b21b792)
# 03 pc 001e0439 /system/lib/platformsdk/libark_jsruntime.so(panda::ecmascript::EcmaInterpreter::Execute(panda::ecmascript::EcmaRuntimeCallInfo*)+984)(106c552f6ce4420b9feac95e8b21b792)
...
# 39 pc 00072998 /system/lib/ld-musl-arm.so.1(libc_start_main_stage2+56)(5b1e036c4f1369ecfdbb7a96aec31155)
# 40 pc 00005b48 /system/bin/appspawn(_start_c+84)(cb0631260fa74df0bc9b0323e30ca03d)
# 41 pc 00005aec /system/bin/appspawn(cb0631260fa74df0bc9b0323e30ca03d)
Registers:
r0:00000000 r1:ffc47af8 r2:00000001 r3:f6555c94
r4:00000000 r5:f4d90f64 r6:bd8434f8 r7:00000000
r8:00000000 r9:ffc48808 r10:ffc47b70
fp:f7d8a5a0 ip:00000000 sp:ffc47aac lr:f4d6b0c7 pc:bd843510
基于SDK llvm-addr2line解析行号如下所示:
[SDK DIR PATH]\OpenHarmony\11\native\llvm\bin> .\llvm-addr2line.exe -Cfie libentry.so 3150
TriggerCrash(napi_env__*, napi_callback_info__*)
D:/code/apprecovery-demo/entry/src/main/cpp/hello.cpp:48
llvm-addr2line 逐行解析的命令为:llvm-addr2line.exe -fCpie libutils.z.so 偏移量,偏移量可以多个一起解:llvm-addr2line.exe -fCpie libxxx.so 0x1bc868 0x1be28c xxx。使用llvm-addr2line后,如果得出的行号看起来不是很正确,可以考虑对 地址进行微调(如减1),或者考虑关闭一些编译优化。
方式三:通过 DevEco Studio hstack 工具解析堆栈信息
hstack是DevEco Studio为开发人员提供的用于将release应用混淆后的crash堆栈还原为源码对应堆栈的工具,支持Windows、Mac、Linux三个平台。
结合业务检视代码
根据基于崩溃栈定位行号章节中介绍的三种方式获取到栈顶对应的行号后,回到代码中,检视上下文。如下图所示,hello.cpp中的48行是一个空指针解引用的代码问题。

本场景是一个故障构造的应用,实际的场景往往不会这么简单,需要结合实际业务进行分析。
反汇编(可选)
一般而言,如果是比较明确的问题,反编译定位到代码行就能够定位;较少数的情况,比如定位到某一行里面调用的方法有多个参数,参数又涉及到结构体等,就需要借助反汇编来进一步分析。
objdump -S xxx.so > xxx.txt
objdump -d xxxx 对 xxxx 文件反汇编
objdump -S -l xxxx 对 xxxx 文件反汇编,同时将指令对应的源码行显示出来
CppCrash 常见问题分类与原因
-
空指针解引用 NULL pointer dereference 形如 SIGSEGV(SEGV_MAPERR)@0x00000000 或 cppcrash日志的Register中打印的r0,r1 等传参寄存器的值为0时,应首先考虑调用时是否传入了空指针。 形如 SIGSEGV(SEGV_MAPERR)@0x0000000c 或 cppcrash日志Register中打印的r1 等传参寄存器的值为一个很小的值时应考虑调用入参的结构体成员是否包含空指针。
-
程序主动终止SIGABRT 一般为用户/框架/C库主动触发,大部分场景下跳过C库/abort发起的框架库的第一帧即为崩溃原因,这里主要检测的是资源使用类的问题,如线程创建,文件描述符使用,接口调用时序等。
-
SIGSEGV无效内存访问
-
多线程操作集合,std库的集合为非线程安全,如果多线程添加删除,容易出现SIGSEGV类崩溃,如果使用 llvm-addr2line 后的代码行与集合相关,可以考虑这个原因。
-
不匹配的对象生命周期,比如使用裸指针(不含有封装、自动内存管理等特性的指针)保存sptr类型以及shared_ptr类型,会导致内存泄漏和悬空指针问题。裸指针是指不含有封装、自动内存管理等特性的指针。它只是一个指向内存地址的简单指针,没有对指针指向的内存进行保护或管理。裸指针可以直接访问指向的内存,但也容易出现内存泄漏、空指针引用等问题。因此,在使用裸指针时需要特别小心,避免出现潜在的安全问题;推荐使用智能指针来管理内存。
-
-
use after free问题 返回临时变量、野指针:比如返回栈变量的引用,释放后未置空继续访问。
# include <iostream>
int& getStackReference() {
int x = 5;
return x; // 返回 x 的引用
}
int main() {
int& ref = getStackReference(); // 获取 x 的引用
// x 在 getStackReference 函数返回后被释放
// ref 现在是悬空引用,继续访问会导致未定义行为
std::cout << ref << std::endl; // 试图输出 x 的值,这是未定义行为
return 0;
}
- 栈溢出:如递归调用,析构函数相互调用,特殊的栈(信号栈)中使用大块栈内存。
# include <iostream>
class RecursiveClass {
public:
RecursiveClass() {
std::cout << "Constructing RecursiveClass" << std::endl;
}
~RecursiveClass() {
std::cout << "Destructing RecursiveClass" << std::endl;
// 在析构函数中递归调用
RecursiveClass obj;
}
};
int main() {
RecursiveClass obj;
return 0;
}
创建一个 RecursiveClass 对象时,它的构造函数被调用。销毁这个对象时,它的析构函数被调用。在析构函数中,创建了一个新的RecursiveClass对象,这会导致递归调用,直到栈溢出。递归调用导致了无限的函数调用,最终导致栈空间耗尽,程序崩溃。
- 二进制不匹配:通常由ABI(应用程序二进制接口)不匹配引起,如自己编译二进制与实际运行的二进制接口存在差异,数据结构定义存在差异,这种一般会产生随机的崩溃栈。
- 地址越界:使用有效的野指针,并修改了其中的内存为非法值,访问越界,覆盖了正常的数据这种一般会产生随机的崩溃栈。
- SIGBUS (Alignment)考虑对指针进行强转之后地址是否已经处于非对齐状态。

更多推荐
所有评论(0)