# CppCrash故障检测机制及规格说明

#### 概述

进程崩溃后，系统首先感知到崩溃，随即采集相关信息生成崩溃日志并上报崩溃事件，为开发者提供详细的维测日志以辅助故障定位。

#### 故障检测机制

系统的进程崩溃检测能力主要基于POSIX信号机制，当进程崩溃后收到崩溃信号，进入系统注册信号处理流程，进行崩溃信息收集、事件生成、事件上报等操作，最终将崩溃事件发布给应用进程的崩溃事件订阅者。

系统检测进程崩溃的详细流程如下：

1.  进程运行时崩溃后收到来自内核发送的崩溃信号，由进程在启动时注册的信号处理模块进行处理。
2.  进程接收到崩溃信号后，保存当前进程上下文并fork出子进程执行ProcessDump二进制抓取崩溃信息。
3.  ProcessDump进程收集完崩溃信息后，上报给Hiview进程。Hiview进程将事件信息存储到应用沙箱目录。
4.  HiAppEvent注册的崩溃事件观察者监听到应用沙箱目录的文件变化，将事件回调给应用进程。

根据当前HarmonyOS支持处理的信号，系统将Native侧的崩溃划分为8类二级根因，以及31类三级根因，如下表所示。

| 一级根因 | 二级根因 | 二级根因描述 | 三级根因 | 三级根因描述 |
| --- | --- | --- | --- | --- |
| CppCrash | SIGILL (非法指令) | 进程执行了非法、格式错误、未知或特权指令 | ILL_ILLOPC 非法操作码异常 | CPU读到了无法解析的机器码，如ud2 |
| ILL_ILLOPN 非法操作数异常 | 发生在指令使用了不正确的操作数，或者是操作数的类型不正确时。 |  |  |  |
| ILL_ILLADR 非法地址异常 | 发生在程序尝试访问无效的内存地址时，或者是在尝试执行未对齐的内存访问时，与SIGSEGV不同，这个错误是发生在解码阶段，而SIGSEGV是发生在执行阶段 |  |  |  |
| ILL_ILLTRP 非法陷阱异常 | 发生在程序尝试执行一个非法的陷阱指令时，或者是在尝试执行一个未定义的操作时。 |  |  |  |
| ILL_PRVOPC 特权操作码异常 | 发生在普通用户尝试执行特权指令时。 |  |  |  |
| ILL_PRVREG 特权寄存器异常 | 发生在普通用户尝试访问特权寄存器时。 |  |  |  |
| ILL_COPROC 协处理器异常 | 发生在程序尝试使用未定义的协处理器指令时。 |  |  |  |
| ILL_BADSTK 无效的堆栈异常 | 发生在程序尝试在无效的堆栈地址上执行操作时，或者是在堆栈溢出时。 |  |  |  |
| ILL_ILLPACCFI 指针校验异常 | 发生在程序校验指针失败时。 |  |  |  |
| SIGTRAP (断点或陷阱异常) | 异常或trap指令发生 | TRAP_BRKPT 软件断点 | 栈保护校验，为了保护现场在代码中插入brk，以强制中止 由软件断点引起的，当程序执行到设置的断点时会触发该信号。软件断点通常用于调试程序，可以在程序的关键位置设置断点，以便在调试时暂停程序的执行并检查变量值等信息 |  |
| TRAP_TRACE 单步调试 | 由单步执行引起的，当程序执行单个指令时会触发该信号。单步执行通常用于调试程序，可以逐步执行程序并检查每个指令的执行结果。 |  |  |  |
| TRAP_BRANCH 分支跟踪 | 由分支指令引起的，当程序执行分支指令时会触发该信号。分支指令通常用于控制程序的执行流程，例如if语句和循环语句等。 |  |  |  |
| TRAP_HWBKPT 硬件断点 | 由硬件断点引起的，当程序执行到设置的硬件断点时会触发该信号。硬件断点通常用于调试程序，可以在程序的关键位置设置断点，以便在调试时暂停程序的执行并检查变量值等信息。与软件断点不同的是，硬件断点是由CPU硬件实现的，因此可以在程序执行过程中实时检测断点是否被触发。 |  |  |  |
| SIGABRT (进程主动终止) | 进程异常终止，通常为进程自身调用标准函数库的abort()函数 | 应用主动终止SIGABRT | 进程异常终止，通常为进程自身调用标准函数库的abort()函数。 |  |
| SIGBUS (非法内存访问) | 进程访问了对齐或者不存在的物理地址 | BUS_ADRALN 内存地址对齐错误 | 某些原子指令对地址对齐的要求 对齐错误。在非x86架构或开启了 AC 标志的x86上访问未对齐内存。 |  |
| BUS_ADRERR 非法内存地址错误 | 非物理地址。访问mmap区域但文件被截断，或物理内存不存在。 |  |  |  |
| BUS_OBJERR 对象访问错误 | mmap文件大小与访问地址不一致 |  |  |  |
| BUS_MCEERR_AR 硬件内存校验错误 | 硬件内存损坏(Action Required)。ECC 校验发现无法纠正的错误，必须处理。 |  |  |  |
| BUS_MCEERR_AO 硬件内存校验错误 | 硬件内存损坏(Action Optional)。通知进程某处内存坏了，但当前未访问。 |  |  |  |
| SIGFPE (浮点异常) | 进程执行了错误的算术运算 | FPE_INTDIV 整数除法错误 | 表示整数除法中的除数为零的情况。当一个程序尝试进行整数除法，但除数为零时，会发出这个信号。 |  |
| FPE_INTOVF 整数溢出错误 | 表示整数溢出错误。当一个程序尝试进行整数运算，结果超出整数范围时，会发出这个信号。 |  |  |  |
| FPE_FLTDIV 浮点除法错误 | 表示浮点数除法中的除数为零的情况。当一个程序尝试进行浮点数除法，但除数为零时，会发出这个信号。 |  |  |  |
| FPE_FLTOVF 浮点溢出错误 | 表示浮点溢出错误。当一个程序尝试进行浮点数运算，结果超出浮点数上限范围时，会发出这个信号。 |  |  |  |
| FPE_FLTUND 浮点下溢错误 | 表示浮点下溢错误。当一个程序尝试进行浮点数运算，结果小于浮点数下限范围时，会发出这个信号。 |  |  |  |
| FPE_FLTRES 浮点结果未定义错误 | 表示浮点结果未定义错误。当一个程序尝试进行浮点数运算，结果未定义时，会发出这个信号。 |  |  |  |
| FPE_FLTINV 无效浮点操作错误 | 表示无效浮点操作错误。当一个程序尝试进行无效的浮点数运算时，会发出这个信号。 |  |  |  |
| FPE_FLTSUB 浮点陷阱错误 | 表示浮点运算结果越界错误。当一个程序尝试进行浮点数运算，浮点数结果越界，会发出这个信号 |  |  |  |
| SIGSEGV (无效内存访问) | 进程访问了无效内存引用 | SEGV_MAPERR 不存在的内存地址 | 进程试图访问一个不存在的内存地址，或者试图访问一个没有映射到进程地址空间的内存地址。这种情况通常是由于程序中的指针错误或内存泄漏引起的，例如空指针解引用等 |  |
| SEGV_ACCERR 不可访问的内存地址 | 权限错误。映射存在，但读写权限不符（写只读段，执行数据段）如：修改字符串常量 进程试图访问一个已被操作系统标记为不可访问的内存地址，例如向只读内存写入数据或执行没有执行权限的内存。这种情况通常是由于程序中的缓冲区溢出或者试图修改只读内存等错误引起的。 |  |  |  |
| SIGSTKFLT（栈错误） | 处理器执行了错误的栈操作，如栈空时弹出、栈满时压入 | 递归调用过深 | 协处理器栈错误：在现代 Linux/x86 比较少见，基本废弃。栈溢出通常会触发SIGSEGV |  |
| SIGSYS（非法的系统调用） | 非法的系统调用 | Seccomp拦截 | 沙箱等使用Seccomp过滤系统调用等 |  |

#### 日志规格

HarmonyOS系统在应用崩溃之后，采集到的崩溃现场信息会生成崩溃日志，崩溃日志中包含进程基本信息、故障信号描述、寄存器快照、调用栈回溯、内存maps等结构化字段，详见CppCrash崩溃日志规格。

掌握日志规格是问题定位的前提，开发者需要熟悉各字段的语义，才能从日志关键段落中快速定位问题。

#### 日志获取方式

HarmonyOS提供多种方式获取崩溃日志，包括开发态和运维态，开发者可根据当前所处的开发阶段选择对应方式，详见CppCrash故障日志获取方式。

#### 订阅崩溃事件

详见崩溃事件介绍。

HiAppEvent定义的崩溃事件包含CppCrash故障和JS Crash故障，本文介绍的是CppCrash故障。

#### 聚类规则

为避免重复分析多份故障信息，提高应用故障问题的分析效率，需要对CppCrash故障信息进行聚类，聚类能帮助开发者对不同原因问题进行分类统计。

CppCrash故障日志信息中的故障线程信息表示业务线程发生故障时代码调用信息，相同的故障线程调用栈信息必然表示相同的故障原因。因此，将故障线程信息作为聚类范围是最为准确的，开发者可根据业务聚类的需求调整增加其他故障日志的信息

开发者可以直接访问AppGallery Connect（简称AGC）提供的APMS服务查看聚类后的崩溃事件和日志，也可以将事件和日志，上传到自检云侧大数据网站进行分析，可按照HarmonyOS推荐的聚类规则进行操作。