📚往期笔录记录✏️:
✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
✏️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
✏️ 记录一场鸿蒙开发岗位面试经历~
✏️ 持续更新中……
简介
CppCrash是C/C++运行时崩溃,包括空指针异常、数组越界异常、栈溢出异常等。HarmonyOS系统针对这一类故障,基于系统级DFX能力,能够进行检测并生成故障日志,生成在/data/log/faultlog/faultlogger系统目录下,在DevEcoStudio中的Faultlog工具栏也能进行汇总显示。
CppCrash故障日志
日志格式和日志获取
CppCrash日志格式可参考 《日志格式》 。CppCrash故障根据报错场景可以分为运行态CppCrash故障和开发态CppCrash故障。
在开发态下,DevEco Studio会收集CppCrash、App Freeze、JS Crash、System Freeze、ASan的崩溃日志到FaultLog下,开发者可以通过FaultLog的CppCrash日志、ASAN日志定位问题的具体原因。此外,开发者可以自行获取/data/log/faultlog/faultlogger下的日志再进行分析。
在运行态下,开发者需要提前开通崩溃服务,收集运行状态下的CppCrash,具体步骤如下所示:
- 开发者需要在 AGC 提前创建项目和应用,详细创建步骤可以参考创建项目与应用。
- 在AGC上开通崩溃服务,详细的操作步骤可以参考开通HarmonyOS应用的服务。
- 添加配置文件,将配置文件添加到工程目录并集成AGC插件,AGC插件可以自动将您在AGC上的应用信息加载到开发环境,详情可参考添加配置文件。
- 添加配置文件后,需要在DevEco Studio项目中配置SDK依赖,详情可参考集成SDK。
- 配置完成后,需要测试崩溃服务是否正常运行,详情可以参考测试崩溃实现。
- 具体的崩溃日志,可以在“AGC->我的项目->崩溃”中找到详细日志,进而分析异常报错问题。
crash信号分类
进程崩溃基于Linux信号机制,目前主要支持对以下崩溃异常信号的处理:
表1 当前系统支持的崩溃异常信号表
信号值 | 信号 | 解释 | 触发原因 |
---|---|---|---|
4 | SIGILL | 非法指令 | 执行了非法指令、格式错误、未知或特权指令,通常是因为可执行文件本身出现错误,或者试图执行数据段,堆栈溢出时也有可能产生这个信号。 |
5 | SIGTRAP | 断点或陷阱异常 | 由断点指令或其它trap指令产生。 |
6 | SIGABRT | abort发出的信号 | 调用abort函数生成的信号。 |
7 | SIGBUS | 非法内存访问 | 非法地址,包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。 |
8 | SIGFPE | 浮点异常 | 在发生致命的算术运算错误时发出,不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。 |
11 | SIGSEGV | 无效内存访问 | 试图访问未分配给自己的内存,或试图往没有写权限的内存地址写数据。 |
16 | SIGSTKFLT | 栈溢出 | 堆栈溢出。 |
31 | SIGSYS | 系统调用异常 | 非法的系统调用(Seccomp 限制) |
CppCrash故障定位
辅助工具介绍
(1)反编译addr2line
Linux下addr2line命令用于将程序指令地址转换为所对应的函数名、以及函数所在的源文件名和行号。在问题定位时,可以使用llvm-addr2line.exe来替换addr2line,其对应的路径在"SDK安装路径\版本路径\base\native\llvm\bin"下,使用时可以参考以下命令。其中,libentry.so是报错的动态链接库,0000000000001c14是报错的内存地址。
llvm-addr2line.exe -a -C -i -f -e xxx\...\libentry.so 0000000000001c14
表2 参数说明
参数 | 功能描述 |
---|---|
-C | 去除名称修饰 |
-f | 在反汇编结果中显示函数名 |
-a | 显示内存地址 |
-i | 解开内联函数 |
-e | 指定待还原的so的路径 |
(2)反汇编objdump
objdump可以用来显示二进制文件的信息,它可以进行反汇编用于观察内存异常。在问题定位时,可以使用llvm-objdump.exe来替换objdump,其对应的路径在"SDK安装路径\版本路径\base\native\llvm\bin"下,使用时可以参考以下命令。其中,libentry.so是需要反汇编的动态链接库,text.txt用于存储反汇编的代码。
llvm-objdump.exe -S -d xxx\...\libentry.so > text.txt
**表3 **参数说明
选项 | 功能说明 |
---|---|
-C | 将低级符号名称解码(解映射)为用户级名称。除了删除系统预置的任何初始下划线外,这还使C++函数名可读。不同的编译器有不同的篡改样式。可选的 demangling 样式参数可用于为编译器选择适当的 demangling style 。 |
-d | 显示 objfile 中机器指令的汇编助记符。此选项仅反汇编那些预期包含指令的部分。 |
-D | 像 -d 一样,但反汇编所有部分的内容,而不仅仅是那些预期包含指令的部分。 |
-F | 在反汇编 sections 时,无论何时显示符号,都要显示要转储的数据区域的文件偏移量。如果跳过了零,那么当反汇编恢复时,告诉用户跳过了多少个零,以及反汇编恢复位置的文件偏移量。转储节时,显示转储开始位置的文件偏移量。0000000000000665 (File Offset: 0x665): so 共享库的某个反汇编函数入口000000000040052d (File Offset: 0x52d): 可执行文件的某个反汇编函数入口 |
-l | 用与显示的目标代码或重定位相对应的文件名和源行号标记显示(使用调试信息) |
-S | 如果可能的话(有调试信息),显示混合了反汇编的源代码。 |
(3)ASan
ASan(Address-Sanitizer)是内存检测的工具,用于发现内存飞踩第一现场,DevEco Studio已集成ASan为开发者提供面向C/C++的地址越界检测能力。ASan可以解决一些踩内存导致的异常crash的补充手段,对于一些明显不可能crash的场景可以尝试开启ASan。
CppCrash故障定位方法
CppCrash常用故障定位分为本地环境定位和系统环境定位,其主要的定位思路类似,详细的定位方法如下所示:
(1)找出问题必现条件,复现问题场景;
(2)先开多线程检测配置,排查多线程安全问题;
先打开多线程检测配置,确认问题是否属于多线程安全问题,若打开多线程检测配置之后还是报当前错误栈,则进行下一步排查。打开开关的hdc指令为:
hdc shell param set persist.ark.properties 0x107c
hdc shell reboot
说明
persist.ark.properties的默认值为0x105c(即关闭状态)。
(3)根据信号和错误码初步推断crash原因。如下图所示,错误原因为SIGSEGV(SEGV_ACCERR),说明是xmlparser崩溃相关原因。
(4)根据堆栈信息找出报错的栈顶函数。
如下图所示,在开发态发生CppCrash时,在本地环境的DevEco Studio中,Native栈帧和JS栈帧可以通过蓝色链接直接跳转到对应的代码行。
在运行态下,开发者可以找到报错的日志,根据对应的报错日志使用llvm_addr2line解析出栈帧的对应的代码行号。案例如下所示。
说明
使用addr2line后,如果得出的行号看起来不是很正确,可以考虑对地址进行微调(如减1),或者考虑关闭一些编译优化,已知使用LTO(Link Time Optimization)的二进制可能无法正确获得行号。
(5)若无法定位出具体原因,可以使用llvm-objdump反汇编代码,再进一步进行分析。
分析反汇编可参考以下步骤:
- 执行反汇编命令,找到报错的汇编代码;
- 追踪汇编代码中,寄存器数据的来源,确定导致程序出错的那部分代码。
- 结合报错信息和寄存器数据,推断程序报错的原因。
- 根据对报错原因的分析,进行代码修正或优化,以解决程序出错的问题。
(6)如果有理论上不应该发生堆栈报错,可以考虑开启ASan,再根据ASan定位分析具体原因。
如下所示,发生crash后&#