目录

应用闪退-Native运行时崩溃(CppCrash)信号与定位指导

问题现象

应用在使用过程中出现闪退,系统日志或FaultLog中生成以cppcrash命名的故障日志文件。

背景知识

崩溃信号

崩溃错误码

故障根因

关键字段

解决方案

SIGABRT

SI_TKILL

断言检查失败

LastFatalMessage:Assertion failed

检查代码运行时的断言条件,修复业务逻辑异常。

SIGABRT

SI_TKILL

未处理抛出的异常

terminating due to uncaught exception of type

在抛出异常的位置使用try-catch捕获并处理异常。

SIGABRT

SI_TKILL

调用napi_fatal_error引发致命错误

LastFatalMessage:[(xxx)(napi_fatal_error)] FATAL ERROR

排查napi_fatal_error的调用逻辑是否符合预期。

SIGSEGV

SEGV_MAPERR

空指针解引用

probably caused by NULL pointer dereference

排查代码中的指针变量,是否存在使用未初始化、赋值为空或已释放的指针。

SIGSEGV

SEGV_ACCERR

访问越栈内存上界

probably caused by stack-buffer-overflow

排查递归调用时未设置递归终止条件导致栈内存耗尽;局部变量占用过多栈内存导致栈内存耗尽;在信号栈中使用超过系统限制的栈内存。

问题定位

进程在Native层发生崩溃后,系统会抓取崩溃相关的信息,在faultlogger目录下生成cppcrash-应用包名-应用UID-发生时间.log崩溃日志并上报崩溃事件,以供开发者分析定位。CppCrash日志规格说明可以参考日志规格。

步骤一:获取cppcrash-应用包名-应用UID-发生时间.log故障日志。

步骤二:分析故障原因Reason和崩溃堆栈Stack。

Reason字段规格如下:

一般规格包含崩溃类型Signal、崩溃地址和信号发送方信息,Signal信号的含义可见基本概念和实现原理。

Reason:Signal:SIGABRT(SI_TKILL)@0x01317bc900000a02 from:2562:20020169
字段解释如下:
Reason:Signal:信号值(tkill()函数信号)@崩溃地址 from:发送信号的Pid:发送信号的Uid

堆栈日志规格如下:

崩溃堆栈搜索关键字Fault thread info。

堆栈日志打印上限为256个栈帧,超出部分不打印。

Fault thread info:
#00 pc 00000000001b0958 /system/lib/ld-musl-aarch64.so.1(raise+216)(52299a28d60f0bb4073bd788bc023a3a)
#<序号> pc <pc在段内的偏移> <pc属于的段名称(函数名+函数内偏移的字节数)(BuildID)>

pc:程序计数器(Program Counter)的缩写,作用是储存当前程序正在执行指令的地址。
BuildID:是用于标识二进制文件的唯一标识符。

问题场景如下:

  1. 场景一:断言检查失败。
    • 关键字:LastFatalMessage:Assertion failed。
    • 定位:调试代码,检查断言条件中变量的值是否符合预期。
  2. 场景二:未处理抛出的异常。
    • 关键字:terminating due to uncaught exception of type。
    • 定位:定位抛出异常的代码行,检查抛出异常的业务逻辑是否符合预期。
  3. 场景三:调用napi_fatal_error引发致命错误。
    • 关键字:LastFatalMessage:[(xxx)(napi_fatal_error)] FATAL ERROR
    • 定位:排查napi_fatal_error的调用逻辑是否符合预期。
  4. 场景四:空指针解引用。
    • 关键字:probably caused by NULL pointer dereference
    • 定位:排查代码中的指针变量,是否存在使用未初始化、赋值为空或已释放的指针。
  5. 场景五:访问越栈内存上界。
    • 关键字:current thread stack low address = 0x0000005b479ef000, probably caused by stack-buffer-overflow
    • 定位:排查代码逻辑是否存在以下三种栈溢出场景。
      • 递归调用时未设置递归终止条件导致栈内存耗尽。
      • 局部变量占用过多栈内存导致栈内存耗尽。
      • 在信号栈中使用超过系统限制的栈内存。

步骤三:结合业务代码分析根因。

步骤四:异常崩溃场景排查地址越界(可选)。

如果多次崩溃产生了随机的崩溃调用栈,或堆栈指向的代码不应该发生此崩溃信号,则可能发生了地址越界,此时的崩溃栈已非第一现场,需要使用HWASan等内存检测工具,调试压测抓取第一现场的堆栈,详情参考地址越界类问题分析方法。

分析结论

修改建议