查看: 475|回复: 3

鸿蒙应用崩溃卡死故障诊断:事件采集、归因与自恢复适配

[复制链接]
发表于 4 小时前 | 显示全部楼层 |阅读模式
线上最伤体验的三类故障是崩溃(Crash)、卡死(Freeze)和被系统查杀(Killed)。传统处理链路通常是用户报障、研发翻日志、复现、修复,周期以“天”计,而且后台被 LowMemoryKill、主线程超时等隐性故障往往没人知道。HarmonyOS 7 的思路分两层:底层由框架在进程退出前后自动打点,做系统级结构化故障信号采集,开发者无需自行埋点即可拿到崩溃栈、卡死原因和查杀理由;上层用 AI 对结构化信号做聚类与归因,生成修复建议,并与 appRecovery 自恢复联动。下面按“智能发现、精准定位、高效修复”三段拆解落地方法。

系统级故障信号底座主要由 Performance Analysis Kit / AbilityKit 提供三种能力:一是通过 errorManager 注册 ErrorObserver,捕获未处理异常 onUnhandledException;二是通过 appRecovery 在故障发生后自动保存状态并重启应用,恢复到故障前界面;三是通过 faultLogger 或启动时的 LastExitReason 查询上一次故障信息。与此同时,HiAppEvent 会在进程退出前后自动记录 APP_CRASH、APP_FREEZE、APP_KILLED 等结构化事件,是“智能发现”的数据源。没有结构化信号,AI 就无从诊断,因此采集底座是后续归因和修复的前提。

智能发现的第一步,是让应用在故障发生时自动拿到结构化信号,而不是依赖用户手动反馈。可以用 hiAppEvent.addWatcher 订阅系统故障事件:
  1. import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';
  2. // 订阅崩溃 / 卡死 / 应用终止事件,实现“智能发现”
  3. hiAppEvent.addWatcher({
  4.   name: 'faultWatcher',
  5.   appEventFilters: [
  6.     {
  7.       domain: hiAppEvent.domain.OS,
  8.       names: [
  9.         hiAppEvent.event.APP_CRASH,
  10.         hiAppEvent.event.APP_FREEZE,
  11.         hiAppEvent.event.APP_KILLED
  12.       ]
  13.     }
  14.   ],
  15.   onReceive: (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>): void => {
  16.     for (const group of appEventGroups) {
  17.       for (const info of group.appEventInfos) {
  18.         // 结构化字段直接喂给 AI 做聚类归因
  19.         hilog.error(0x0000, 'fault', `event=${info.name}`);
  20.         hilog.error(0x0000, 'fault', `reason=${info.params['reason']}`);
  21.         hilog.error(0x0000, 'fault', `foreground=${info.params['foreground']}`);
  22.         // external_log 是故障日志在沙箱中的完整路径,可上传做深度分析
  23.         hilog.error(0x0000, 'fault', `external_log=${info.params['external_log']}`);
  24.       }
  25.     }
  26.   }
  27. });
复制代码
这里要重点关注 APP_KILLED 的 reason 字段,它覆盖了真实线上常见杀手:整机低内存 LowMemoryKill、RSS 超阈值 RssThresholdKiller、主线程卡死超时 ThreadBlock6S、输入响应超时 AppInputBlock、生命周期超时 LifecycleTimeout、温度超限 HighTemperature 等。订阅后,过去“只有用户知道卡死、研发一无所知”的盲区就被填平。

发现之后是精准定位,AI 的诊断价值主要来自对结构化字段的聚类与关联。第一,按 reason 聚类,把 APP_KILLED 的原因聚合后,可以快速判断是“内存压力主导”还是“主线程超时主导”。原文给出的真实案例中,RssThresholdKiller 与 LowMemoryKill 合计占 75%,直接指向内存治理。第二,关联同生命周期故障,用 app_running_unique_id 把一次运行里的 APP_KILLED 与对应 APP_CRASH 日志关联,定位崩溃发生前的最后一次后台状态。第三,过滤正常退出,reason = UserRequest(上滑清理)属于正常行为,聚类时按 foreground 过滤,只关注“前台闪退”等影响体验的异常。对于 APP_CRASH,exception 字段里的 name(异常类型)、frame(崩溃栈)、thread(线程)已经结构化呈现“哪行代码、哪个线程、什么异常”,AI 拿到后可直接给出疑似根因与修复方向,无需人工从海量日志里捞栈。errorManager 还可以作为实时定位入口:
  1. // errorManager 捕获未处理异常,作为“精准定位”的实时入口
  2. import { errorManager } from '@kit.AbilityKit';
  3. import { hilog } from '@kit.PerformanceAnalysisKit';
  4. const faultObserver: errorManager.ErrorObserver = {
  5.   onUnhandledException(errMsg: string): void {
  6.     // errMsg 已包含异常类型与栈摘要,可直接归档 / 上报诊断
  7.     hilog.error(0x0000, 'fault', `未捕获异常: ${errMsg}`);
  8.   }
  9. };
  10. // 在 Ability 初始化时注册
  11. errorManager.on('error', faultObserver);
复制代码

定位之后是高效修复。HarmonyOS 提供两套恢复机制,让故障对用户尽量无感。推荐方式是 appRecovery 主动自恢复:在 AbilityStage.onCreate 中使能,故障时自动保存状态并重启。
  1. import { AbilityStage, appRecovery } from '@kit.AbilityKit';
  2. import { hilog } from '@kit.PerformanceAnalysisKit';
  3. export default class MyAbilityStage extends AbilityStage {
  4.   onCreate(): void {
  5.     appRecovery.enableAppRecovery(
  6.       appRecovery.RestartFlag.ALWAYS_RESTART,
  7.       appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR | appRecovery.SaveOccasionFlag.SAVE_WHEN_BACKGROUND,
  8.       appRecovery.SaveModeFlag.SAVE_WITH_FILE
  9.     );
  10.   }
  11. }
复制代码
并在 module.json5 中标记可恢复:
  1. {
  2.   "abilities": [
  3.     { "name": "EntryAbility", "recoverable": true }
  4.   ]
  5. }
复制代码
在 UIAbility 里保存关键状态,原文说明状态大小不超过 200KB、保留 7 天,下次启动可自动恢复页面栈与数据:
  1. import { appRecovery, errorManager, UIAbility } from '@kit.AbilityKit';
  2. import { window } from '@kit.ArkUI';
  3. import { hilog } from '@kit.PerformanceAnalysisKit';
  4. let registerId = -1;
  5. const callback: errorManager.ErrorObserver = {
  6.   onUnhandledException(errMsg: string): void {
  7.     hilog.error(0x0000, 'fault', `未捕获异常: ${errMsg}`);
  8.     appRecovery.saveAppState(); // 保存现场
  9.     appRecovery.restartApp(); // 自动恢复
  10.   }
  11. };
  12. export default class EntryAbility extends UIAbility {
  13.   onWindowStageCreate(windowStage: window.WindowStage): void {
  14.     registerId = errorManager.on('error', callback);
  15.     windowStage.loadContent('pages/index', (err) => { /* ... */ });
  16.   }
  17.   onSaveState(state: object): void {
  18.     // 保存关键业务状态,下次启动恢复
  19.     (state as Record<string, Object>)['lastPage'] = 'pages/Detail';
  20.   }
  21. }
复制代码
另一套是 FaultLogExtensionAbility(API 21+ 延迟上报):当应用崩溃后长时间未重启,系统会在约 30 分钟后拉起该 ExtensionAbility 进程,回调订阅的 crash / freeze 事件,保证“应用起不来也能拿到故障日志”。在根因定位后,诊断智能体还可基于故障日志生成修复代码,例如原文提到的 DevEco Code 自动修复 Agent,把修复从人工排障压缩到分钟级。

适配时要留意支持矩阵:JS_CRASH / APP_FREEZE 支持“监听 + 状态保存 + 自动重启 + 日志查询”全链路;CPP_CRASH 不支持监听与自恢复,需要依赖 faultLogger 查询,设计容错时要区别对待。

把三段串起来,落地动作可以分三层:端上使用 hiAppEvent.addWatcher 与 errorManager.on('error') 采集,并用 appRecovery 自恢复保体验;云上将结构化事件(含 external_log 路径)上报到 HarmonyOS 开发者中心崩溃服务,获得无需写代码的可视化数据报告与实时聚类;研发侧由诊断智能体消费聚类结果,自动开 Issue 并附修复建议。这样才从“被动救火”转向“主动治未病”。

一次典型崩溃的端到端链路是:未捕获异常触发 errorManager 观测器入队,HiAppEvent 在退出前拿到崩溃事件与 faultlog 路径,回调末尾同步退出,系统触发 onSaveState 保存状态,下次启动恢复界面并上报故障,云端 AI 聚类归因,研发收到带修复建议的告警。用户可能只感觉到一次无感重启,而研发已经在后台拿到了归因与补丁。
回复

使用道具 举报

发表于 3 小时前 | 显示全部楼层

Re: 鸿蒙应用崩溃卡死故障诊断:事件采集、归因与自恢复适配

楼主这个三段式拆解得很清楚,尤其是把“智能发现、精准定位、高效修复”跟传统的“用户报障再翻日志”对比,把痛点讲透了。之前做线上问题排查最头疼的就是卡死和后台被杀这类隐性故障,用户不反馈就基本石沉大海,HiAppEvent 能自动把 APP_CRASH、APP_FREEZE、APP_KILLED 结构化落下来,确实是把盲区补上了。 我自己比较有共鸣的是 APP_KILLED 的 reason 聚类这一段。实际排查时经常把低内存杀和主线程超时混在一起看,导致优化方向打偏,按 reason 分开聚合之后,判断是内存压力主导还是主线程问题主导就直观多了。另外用 foreground 过滤掉 UserRequest 那个细节也很实用,上滑清理属于正常退出,混进异常聚类里会稀释信号,这个坑很容易踩。 有个小疑问想请教下:external_log 那个沙箱路径上传做深度分析时,一般是怎么控制量和时机的?是全量传还是只在前台异常时传。另外正文末尾好像被截断了,appRecovery 在 AbilityStage.onCreate 里使能那部分和后面跟自恢复联动的完整代码没贴完,方便的话能补一下吗,想看看状态保存到恢复界面这块具体怎么接。感谢分享。
回复 支持 反对

使用道具 举报

发表于 3 小时前 | 显示全部楼层

Re: 鸿蒙应用崩溃卡死故障诊断:事件采集、归因与自恢复适配

楼主这篇把“发现—定位—修复”三段串得挺清楚的,尤其是把 APP_KILLED 的 reason 摊开讲这一点,以前确实容易被忽略,LowMemoryKill、RssThresholdKiller、ThreadBlock6S 这些只能靠用户反馈或者运气好撞上,现在能结构化订阅出来,等于把隐性故障变成了可见指标,思路很实用。 另外两个细节我觉得挺关键:一是聚类时按 reason 划分内存压力和主线程超时,再看 RssThresholdKiller 加 LowMemoryKill 占比的判断路径,比单纯堆日志有效;二是用 app_running_unique_id 把 KILLED 和之前的 CRASH 串起来,还有用 foreground 过滤掉 UserRequest 这种正常上滑清理,避免把正常退出当故障算,这两点很容易被忽略。 有个小问题想请教下:帖子末尾 appRecovery 那段代码好像被截断了,只到 export d 就没了,AbilityStage.onCreate 里使能的具体写法和 saveAppState 的调用时机能不能再补一下?另外想确认下,appRecovery 和 errorManager 同时开启,会不会对同一个异常重复触发恢复流程,还是要自己做去重?提前谢过。
回复 支持 反对

使用道具 举报

发表于 3 小时前 | 显示全部楼层

Re: 鸿蒙应用崩溃卡死故障诊断:事件采集、归因与自恢复适配

这套链路总结得很清楚,核心是把崩溃、卡死和查杀统一成结构化信号来处理,而不是等用户报障后再翻日志。HiAppEvent 自动采集 APP_CRASH、APP_FREEZE、APP_KILLED,errorManager 捕获未处理异常,再结合 reason、app_running_unique_id、exception 等字段做聚类和关联,最后用 appRecovery 做自恢复,整体思路比较完整。特别是 APP_KILLED 的 reason 覆盖 LowMemoryKill、RssThresholdKiller、ThreadBlock6S、AppInputBlock、LifecycleTimeout、HighTemperature,确实能把过去“用户知道卡死、研发不知道”的盲区补上。 落地时我比较关注几个细节:Watcher 的注册时机和事件去重策略、external_log 上报时的隐私与体积控制、UserRequest 过滤时 foreground 的判定准确性,以及 appRecovery 保存状态的大小和恢复后界面一致性。首帖最后 AbilityStage.onCreate 使能 appRecovery 的示例好像没贴完,期待补全。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-3 04:04 , Processed in 0.022568 second(s), 29 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部