Stability Analysis Methods
Stability analysis的目标不是直接猜测根因,而是基于现象,日志和线程状态逐步缩小范围. For stability problems,推荐按"先分类,再取证,后定位"的顺序推进. In actual work,通常可以分为人工分析和 AI分析两种方式.
1. Analysis Methods
1.1 AI Analysis
In RNOH scenario,推荐结合 stability skill来做 AI分析, skill名称为 rnoh-stability-triage. This skill会优先帮助识别问题画像,匹配历史稳定性修复,结合当前代码验证,并给出升级,补丁回捞或规避建议.
AI分析的基本使用方式如下:
- 先准备输入信息,至少包括 crash日志或异常消息, hlog或 hilog,版本信息,以及问题触发场景.
- 在对话中直接说明当前问题现象,例如启动崩溃,页面退出后崩溃,卡死, OOM,内存持续增长等.
- 明确提供版本范围,例如 0.72, 0.77, 0.82,或者直接给出分支名,提交号.
- 把关键日志,堆栈,报错信息贴给 AI,并说明是否希望对照历史稳定性修复汇总一起分析.
- AI会结合稳定性 skill,先提取问题画像,再去匹配历史修复,检查当前代码保护逻辑,最后输出结论摘要,归因判断和修复建议.
- 如果第一次分析证据不足,再补充符号化结果,更多 hilog,复现步骤或触发时机,继续让 AI收敛范围.
1.2 Manual Analysis
人工分析的核心是由研发人员自己完成日志阅读,调用链还原,代码比对和根因判断. 本文后续关于应用异常退出,应用冻屏,内存异常和资源泄漏的分析方法,主要就是人工分析时可直接参考的步骤.
面对任意稳定性问题,建议优先执行以下步骤:
- 明确现象:判断是进程退出,界面卡死,内存膨胀还是资源长期增长.
- 确定故障类型:先归类到应用异常退出,应用冻屏,内存异常或资源泄漏.
- 收集基础日志:包括 FaultLog, hilog,线程堆栈, trace,内存快照,资源统计信息.
- 锁定关键模块:确认问题更偏向 JS层, ArkTS适配层, C++框架层还是系统交互层.
- 结合触发阶段分析:重点看启动,页面切换,实例销毁,动画执行,布局提交和长期运行等阶段.
2. Application Abnormal Exit Analysis
Application abnormal exit通常分为 CppCrash和 JS Crash,两类问题的日志入口和定位手段不同.
2.1 CppCrash Analysis Method
Symptom Characteristics
- Application突然闪退,没有业务层兜底提示.
- FaultLog中通常能看到崩溃信号和 native调用栈.
- 常见信号包括 SIGSEGV, SIGABRT, SIGBUS, SIGTRAP.
- Crash address可能为明显异常地址,也可能接近空地址或已释放对象区域.
####定位步骤
- 先看 FaultLog中的 Reason, Signal,崩溃线程和前几帧栈信息.
- 根据信号判断大类,例如 SIGSEGV多为非法访问, SIGABRT多为主动终止或异常逃逸, SIGTRAP多与断言或异常返回路径有关.
- 对关键地址做符号化,还原到函数,文件和代码行.
- 结合汇编或调用链确认是空指针,野指针, UAF,异常终止还是非法状态继续执行.
- 回到代码看生命周期,线程切换和回调解绑是否完整.
Common Tools
- DevEco Studio FaultLog查看.
- addr2line或等效符号化工具.
- objdump或等效反汇编工具.
- AddressSanitizer等地址越界检测工具.
Analysis Focus
- 是否发生在实例销毁后回调继续执行.
- 是否存在裸指针,弱引用缺失或对象释放顺序错误.
- 是否在错误线程访问对象或持有失效上下文.
- 是否存在声明与实现不一致,返回路径不完整或异常边界缺失.
2.2 JS Crash Analysis Method
Symptom Characteristics
- Application退出,同时日志中伴随 JS异常信息.
- 常见错误为 TypeError, ReferenceError, RangeError.
- 典型报错包括访问 undefined属性,调用非函数对象,初始化阶段抛出异常.
####定位步骤
- 读取错误类型,错误消息和原始 JS堆栈.
- 使用 sourcemap将混淆或构建后的栈还原到源码位置.
- 确认触发点是在页面渲染,对象访问,模块初始化还是异步回调.
- 判断问题属于空值保护缺失,接口契约不一致,数据结构假设错误还是异常未被捕获.
- 进一步检查是否是 JS异常导致 native层被动退出,而不是 native先崩溃.
Analysis Focus
- 变量或对象是否可能为 undefined或 null.
- 初始化链路中是否把目录,对象,句柄等错误类型传入接口.
- Promise,事件回调或定时任务中的异常是否缺少兜底.
- 发布构建下是否因为混淆,接口映射或平台兼容导致调用失败.
3. Application Freeze Analysis
Application freeze的关键在于进程通常仍然存活,因此不能只看崩溃日志,而要重点看线程状态和调用阻塞位置.
3.1 Common Detection Types
- THREAD_BLOCK_6S:主线程长时间卡住.
- APP_INPUT_BLOCK:用户输入处理超时.
- LIFECYCLE_TIMEOUT:生命周期切换超过阈值.
3.2 Log Acquisition Method
- 使用 DevEco Studio自动收集冻结日志.
- 使用命令行工具导出 faultlogger目录下的相关文件.
- 结合 trace, hilog和线程快照一起分析.
3.3定位步骤
- 先看基础信息:进程号,故障类型,上报时间和前后台状态.
- 找到主线程或关键线程的堆栈,看是否长期停在同一位置.
- 判断阻塞类型: JS业务耗时, IPC等待,锁竞争, I/O阻塞或生命周期切换等待.
- 结合 trace定位耗时操作是在渲染,布局,模块调用还是页面创建阶段.
- 回到代码确认是否存在持锁回调,同步等待,错误的线程切换或高频重入.
3.4 Analysis Focus
- 主线程是否执行了不应放在前台交互线程中的耗时任务.
- JS线程与 UI线程之间是否互相等待.
- 生命周期切换是否依赖同步回调,锁释放或外部资源返回.
- 页面首次创建时是否触发了异常高频的组件构建或布局计算.
4. Memory Abnormality Analysis
Memory abnormality既可能表现为 OOM,也可能表现为长期泄漏或非法访问.
4.1 OOM Analysis Method
Symptom Characteristics
- Application突然退出,并伴随内存分配失败信息.
- 内存占用持续增长,最终达到系统上限.
- 在分配大对象时更容易暴露问题.
####定位步骤
- 查看崩溃前后的内存曲线和峰值变化.
- 对比历史基线,确认是短时突增还是长期累积.
- 抓取堆快照,对比操作前后的对象存活情况.
- 区分是 JS堆, Native堆还是系统共享内存异常增长.
- 结合 Allocation调用栈定位是谁持续创建对象却没有释放.
4.2 Address Boundary Analysis Method
Symptom Characteristics
- 调试或压测阶段更容易出现.
- 常伴随非法地址访问, UAF,空指针解引用或数组越界.
####定位步骤
- 启用地址越界检测工具或 Sanitizer.
- 根据报错地址和访问类型确认是读越界,写越界还是释放后访问.
- 对照调用栈和对象生命周期,检查所有权和释放时序.
- 回溯最近一次对象创建,转移,销毁和回调触发链路.
5. Resource Leak Analysis
Resource leak问题通常不会立即导致退出,但会在长时间运行后引发一系列二次故障.
5.1 Common Types
- 句柄泄漏:文件或系统句柄未关闭.
- 线程泄漏:线程不断创建但未结束或未回收.
- JS内存泄漏:对象被闭包,监听器或缓存长期持有.
- Native内存泄漏:实例,调度器,上下文或底层资源未释放.
5.2定位步骤
- 先看资源统计,确认异常增长的是句柄,线程还是内存.
- 对句柄泄漏,查看泄漏列表和相关系统调用栈.
- 对线程泄漏,按线程名分组统计,识别重复创建模式.
- 对内存泄漏,抓取快照并比较对象是否在页面退出或实例销毁后仍然存活.
- 结合代码审查监听器,回调,定时器,网络请求和模块析构逻辑是否形成释放闭环.
5.3 Analysis Focus
- 是否注册了回调但没有注销.
- 是否创建了线程池,异步任务或调度器但缺少回收.
- 是否在实例销毁后仍持有 JSVM,上下文或 TurboModule相关引用.
- 是否存在缓存未清理,文件遍历错误或目录对象被误当作文件处理的情况.