一、进程崩溃检测能力
CppCrash进程崩溃检测基于操作系统信号机制,目前支持对以下崩溃信号的处理:
以上部分故障信号,根据具体的场景还有二级分类:
SIGILL是一个在Unix和类Unix操作系统中的信号,它表示非法指令异常。SIGILL信号通常由以下几种类型的问题场景引起:
1.ILL_ILLOPC:非法操作码异常。这种异常通常发生在执行不被CPU支持的指令时,或者在尝试执行特权指令时。
2.ILL_ILLOPN:非法操作数异常。这种异常通常发生在指令使用了不正确的操作数,或者是操作数的类型不正确时。
3.ILL_ILLADR:非法地址异常。这种异常通常发生在程序尝试访问无效的内存地址时,或者是在尝试执行未对齐的内存访问时。
4ILL_ILLTRP:非法陷阱异常。这种异常通常发生在程序尝试执行一个非法的陷阱指令时,或者是在尝试执行一个未定义的操作时。
5.ILL_PRVOPC:特权操作码异常。这种异常通常发生在普通用户尝试执行特权指令时。
6.ILL_PRVREG:特权寄存器异常。这种异常通常发生在普通用户尝试访问特权寄存器时。
7.ILL_COPROC:协处理器异常。这种异常通常发生在程序尝试使用未定义的协处理器指令时。
8.ILL_BADSTK:无效的堆栈异常。这种异常通常发生在程序尝试在无效的堆栈地址上执行操作时,或者是在堆栈溢出时。
SIGTRAP信号通常用于调试和跟踪程序的执行。下面是上面列出的四种SIGTRAP信号类别的问题场景介绍:
1.TRAP_BRKPT:这个信号是由软件断点引起的,当程序执行到设置的断点时会触发该信号。软件断点通常用于调试程序,可以在程序的关键位置设置断点,以便在调试时暂停程序的执行并检查变量值等信息。
2.TRAP_TRACE:这个信号是由单步执行引起的,当程序执行单个指令时会触发该信号。单步执行通常用于调试程序,可以逐步执行程序并检查每个指令的执行结果。
3.TRAP_BRANCH:这个信号是由分支指令引起的,当程序执行分支指令时会触发该信号。分支指令通常用于控制程序的执行流程,例如if语句和循环语句等。
4.TRAP_HWBKPT:这个信号是由硬件断点引起的,当程序执行到设置的硬件断点时会触发该信号。硬件断点通常用于调试程序,可以在程序的关键位置设置断点,以便在调试时暂停程序的执行并检查变量值等信息。与软件断点不同的是,硬件断点是由CPU硬件实现的,因此可以在程序执行过程中实时检测断点是否被触发。
SIGBUS是一种由操作系统向进程发送的信号,通常表示内存访问错误。其中,不同的信号类别表示不同的错误场景:
1.BUS_ADRALN:表示内存地址对齐错误。这种错误通常发生在尝试访问未对齐的内存地址时,例如尝试访问一个4字节整数的非偶数地址。
2.BUS_ADRERR:表示非法内存地址错误。这种错误通常发生在尝试访问不属于进程地址空间的内3.存地址时,例如尝试访问一个空指针。
4.BUS_OBJERR:表示对象访问错误。这种错误通常发生在尝试访问一个已经被删除或未初始化的对象时。
5.BUS_MCEERR_AR:表示硬件内存校验错误,发生在访问内存时检测到校验和错误。
6.BUS_MCEERR_AO:表示硬件内存校验错误,发生在访问内存时检测到地址和校验和错误。
SIGFPE是一个信号,它表示浮点异常或算术异常。下面是这些SIGFPE信号类别的问题场景:
1.FPE_INTDIV:整数除法错误。这个信号表示整数除法中的除数为零的情况。当一个程序尝试进行整数除法,但除数为零时,会发出这个信号。
2.FPE_INTOVF:整数溢出错误。这个信号表示整数除法中的除数为负数的情况。当一个程序尝试进行整数除法,但除数为负数时,会发出这个信号。
3.FPE_FLTDIV:浮点除法错误。这个信号表示浮点数除法中的除数为零的情况。当一个程序尝试进行浮点数除法,但除数为零时,会发出这个信号。
4.FPE_FLTOVF:浮点溢出错误。这个信号表示浮点数除法中的除数为负数的情况。当一个程序尝试进行浮点数除法,但除数为负数时,会发出这个信号。
5.FPE_FLTUND:浮点下溢错误。这个信号表示浮点数除法中的除数为零的情况。当一个程序尝试进行浮点数除法,但除数为零时,会发出这个信号。
6.FPE_FLTRES:浮点结果未定义错误。这个信号表示浮点数除法中的除数为正数的情况。当一个程序尝试进行浮点数除法,但除数为正数时,会发出这个信号。
7.FPE_FLTINV:无效浮点操作错误。这个信号表示浮点数除法中的除数为负数的情况。当一个程序尝试进行浮点数除法,但除数为负数时,会发出这个信号。
8.FPE_FLTSUB:浮点陷阱错误。这个信号表示浮点数除法中的除数为零的情况。当一个程序尝试进行浮点数除法,但除数为零时,会发出这个信号。
SIGSEGV是一种信号,它表示进程试图访问一个不属于它的内存地址,或者试图访问一个已被操作系统标记为不可访问的内存地址。SIGSEGV信号通常是由以下两种情况引起的:
1.SEGV_MAPERR:进程试图访问一个不存在的内存地址,或者试图访问一个没有映射到进程地址空间的内存地址。这种情况通常是由于程序中的指针错误或内存泄漏引起的。
2.SEGV_ACCERR:进程试图访问一个已被操作系统标记为不可访问的内存地址,例如只读内存或没有执行权限的内存。这种情况通常是由于程序中的缓冲区溢出或者试图修改只读内存等错误引起的。
信号还有通用分类,每个信号都有一个唯一的编号,用于在进程间传递信息或者通知进程发生了某些事件。下面是每个信号类别的场景介绍:
1.SI_USER:该信号是由用户空间的进程发送给另一个进程的,通常是通过 kill() 系统调用发送的。例如,当用户在终端中按下Ctrl+C时,会发送一个SIGINT信号给前台进程组中的所有进程。
2.SI_KERNEL:该信号是由内核发送给进程的,通常是由内核检测到某些错误或异常情况时发出的。例如,当进程访问无效的内存地址或者执行非法指令时,内核会发送一个SIGSEGV信号给进程。
3.SI_QUEUE:该信号是由sigqueue()系统调用发送的,可以携带一个附加的整数值和一个指针。通常用于进程间高级通信,例如传递数据或者通知进程某个事件已经发生。
4.SI_TIMER:该信号是由定时器发送的,通常用于定时任务或者周期性任务的执行。例如,当一个定时器到期时,内核会向进程发送一个SIGALRM信号。
5.SI_MESGQ:该信号是由消息队列发送的,通常用于进程间通信。例如,当一个进程向一个消息队列发送消息时,内核会向接收进程发送一个SIGIO信号。
6.SI_ASYNCIO:该信号是由异步I/O操作发送的,通常用于非阻塞I/O操作。例如,当一个文件描述符上的I/O操作完成时,内核会向进程发送一个SIGIO信号。
7.SI_SIGIO:该信号是由异步I/O操作发送的,通常用于非阻塞I/O操作。例如,当一个文件描述符上的I/O操作完成时,内核会向进程发送一个SIGIO信号。
8.SI_TKILL:该信号是由tkill()系统调用发送的,与kill()系统调用类似,但是可以指定发送信号的线程ID。通常用于多线程程序中,向指定线程发送信号。
2 问题定位步骤与思路
1. 崩溃日志获取
进程崩溃日志是一种故障日志,与应用无响应日志、JS应用崩溃等都由FaultLogger模块进行管理,可通过如下四种方式获取:
方式一:通过DevEco Studio获取日志
DevEco Studio会收集设备“/data/log/faultlog/faultlogger/”路径下的进程崩溃故障日志到FaultLog下,根据进程名和故障和时间分类显示。获取日志的方法参见:
FaultLog-故障分析-应用/服务调试-DevEco Studio使用指南-工具-HarmonyOS应用开发 (hwcloudtest.cn)
方式二:通过faultlogger接口获取
FaultLogger对外提供了面向应用的故障查询接口,可以查询应用自己的故障记录,以结构化的数据返回。接口的使用以及获取的故障信息规格详见@ohos.faultLogger (故障日志获取)
方式三:通过hiAppEvent接口订阅
hiAppEvent对外提供了故障订阅接口,可以订阅各类故障打点,详见@ohos.hiviewdfx.hiAppEvent(应用事件打点)。
方式四:设备ROOT模式下通过shell获取日志
1.进程崩溃后,系统会在设备“/data/log/faultlog/temp/”路径下的故障日志,其文件名格式为“cppcrash-进程PID-系统毫秒级时间戳”,日志内容包含进程崩溃调用栈,进程崩溃现场寄存器、栈内存、maps,进程文件句柄列表等信息。
2.CppCrash故障会同步在“/data/log/faultlog/faultlogger/”路径下生成一份完善日志,故障日志文件名格式为“cppcrash-进程名-进程UID-秒