Files
harmony-crash-analysis-report/HarmonyOS APM分析报告_20260731.md
T

19 KiB
Raw Blame History

HarmonyOS APM分析报告

崩溃问题

1. 结论摘要

最新7日数据中(2026.7.24-2026.7.30):

数据源 七日数量 7月30日数量 当前统计口径
华为平台 12 6 JS_ERROR、CPP_CRASH等崩溃事件
Grafana 33 17 “崩溃类型 = All”,包含OOM

两边的汇总事件类型未对齐。Grafana当前的“崩溃数”实际包含普通崩溃和OOM异常,而华为平台的“崩溃”统计不包含OOM异常。

核心结论:

  1. 主要差异由统计不一致造成。 Grafana将13次OOM计入“崩溃数”,华为平台则将OOM单独展示。
  2. 排除OOM后仍有2次差异。 华为平台包含3次CPP_CRASH,而Grafana非OOM明细中只有3条JS类异常和1条unKnowException(已确认为CPP_CRASH)。
  3. 因此Grafana平台统计的崩溃率与实际值相比偏高,后续应将OOM异常排除崩溃统计,并入内存泄漏统计表盘。并进一步分析CPP_CRASH缺失原因。

2. 数据截图展示和分析

2.1 核对范围

维度 核对值
应用 mobile-archive-am-qihuo-harmony
应用版本 FUHM037.08.390.10.32 / 1.20.02 / 10031
统计周期 2026-07-24至2026-07-30
重点核对日 2026-07-30

2.2 最新7日数据

2.2.1 华为平台最新7日数据

日期 华为平台崩溃次数
7月24日 0
7月25日 0
7月26日 0
7月27日 0
7月28日 3
7月29日 3
7月30日 6
合计 12

华为平台最新7日崩溃趋势

图1:华为平台最新7日数据。7月30日崩溃次数为6,七日合计12次。

2.2.2 Grafana最新7日数据

指标 数量
七日Grafana崩溃事件数 33
七日影响人数 32
7月30日Grafana崩溃事件数 17

Grafana最新7日崩溃趋势

图2:Grafana最新7日数据。该版本七日共33次,7月30日为17次。

2.2.3 七日汇总分析

Grafana 33次 = 崩溃(7次) + OOM(26次) + 可能的其他异常类型
华为平台 12次    = 崩溃事件

Grafana平台统计崩溃中大部分是OOM异常(26次),实际崩溃事件仅为(7次),且少于华为平台的12次崩溃。

2.3 华为平台7月30日数据

华为平台7月30日崩溃次数

图3:华为平台7月30日崩溃次数为6。

华为平台7月30日完整问题列表

图4:华为平台7月30日共6条问题,每条发生1次。

华为平台6条事件构成如下:

问题边界 类型 模块或位置摘要 次数
SYSTEM CPP_CRASH /system/lib64/platformsdk/libuv.so 1
APP JS_ERROR MultiTypeInputRule.ts:22 / processInsert 1
APP CPP_CRASH libweituo_sdk_ohos.so 1
APP JS_ERROR MultiTypeInputRule.ts:22 / processInsert 1
SYSTEM CPP_CRASH libarkweb_engine.so 1
APP JS_ERROR NumberInputRule.ts:27 / shouldInsert 1

类型汇总:

JS_ERROR    3次
CPP_CRASH   3次
合计        6次

2.4 Grafana 7月30日数据

Grafana7月30日汇总

图5Grafana在7月30日共17次,影响17人。

Grafana使用“崩溃类型 = All”,下方明细把OOM与普通崩溃共同列入“对应版本崩溃列表”。

Grafana7月30日明细上半部分

图6:Grafana明细上半部分,包含3条Cannot read、1条Default OOM及多条OutOfMemory

Grafana7月30日明细下半部分

图7:Grafana明细下半部分,包含其余OutOfMemory记录及1条unKnowException

Grafana当日17条事件分类如下:

Grafana描述类别 条数 是否计入华为平台“崩溃”页
Cannot read property index of undefined 3 是,属于JS类崩溃
unKnowException 1 是Native崩溃
Default oom error 1 否,应归入OOM
OutOfMemory when trying to allocate... 12 否,应归入OOM
合计 17

2.5 OOM明细及核算

序号 OOM描述或申请字节数 次数
1 Default oom error 1
2 OutOfMemory: 79,953,920 bytes 1
3 OutOfMemory: 84,148,224 bytes 1
4 OutOfMemory: 89,915,392 bytes 1
5 OutOfMemory: 99,352,576 bytes 1
6 OutOfMemory: 100,663,296 bytes 1
7 OutOfMemory: 100,925,440 bytes 1
8 OutOfMemory: 119,013,376 bytes 1
9 OutOfMemory: 120,324,096 bytes 1
10 OutOfMemory: 122,159,104 bytes 1
11 OutOfMemory: 139,722,752 bytes 1
12 OutOfMemory: 143,130,624 bytes 1
13 OutOfMemory: 169,869,312 bytes 1
合计 OOM事件 13

排除OOM后的核算结果:

Grafana可比崩溃数
= Grafana当日全口径事件数 - OOM事件数
= 17 - 13
= 4

2.6 当日对比结果

核对项 数量
Grafana全口径事件数 17
Grafana OOM 13
Grafana排除OOM 4
华为平台崩溃 6
剩余差异 2

现有截图中的Grafana崩溃ID被截断,暂时无法与华为平台问题特征ID进行ID级关联。因此,13条OOM的分类结论明确;剩余2条差异的精确归属仍需ELK原始记录或完整详情确认。

3. 问题定性

3.1 统计事件类型并非完全是崩溃事件

Grafana统计“崩溃数量”,但却将OOM同时计入。

华为平台采用:

  • “崩溃”页展示JS_ERROR、CPP_CRASH等崩溃事件。
  • “OOM”页单独展示内存溢出事件。

因此,使用Grafana的崩溃总数与实际上相比偏大

该问题属于指标定义与界面表达不一致,容易让使用者误以为两边存在大规模数据缺失。

3.2 两次CPP/Native类事件未对齐

排除OOM后,两边类型构成对比:

类型 华为平台 Grafana明细 备注
JS类崩溃 3 3条Cannot read property... 经过对比两平台数据内容一致
CPP/Native类崩溃 3 1条unKnowException属于该类 华为平台的CPP_CRASH统计包含APP原因导致的崩溃,和系统原因导致的崩溃。

但是Grafana仅采集到了一条系统原因的CPP_CRASH,缺失另外两个CPP_CRASH
合计 6 4

3条JS类事件基本可以对应。unKnowException对应其中1条Native事件,但仍有2条CPP_CRASH未在Grafana中以明确崩溃类型呈现。

3.3 最终定性

本次问题由两个层面组成:

  1. 指标统计问题: Grafana把OOM计入“崩溃数量”,导致汇总数明显偏高。
  2. 数据链路问题: 排除OOM后,仍有2条CPP/Native崩溃未对齐,存在漏报或者识别错误问题。

最终定性:

Grafana崩溃指标口径过宽,同时Native崩溃采集或展示存在漏报问题,需进一步分析。

4. 线上崩溃预防措施

4.1 Debug阶段崩溃诊断

在 Debug、测试包或内部环境中增加崩溃诊断弹窗,让开发者能在测试时及时发现异常并获取定位信息。该能力不面向正式用户开放。

  • 进程级崩溃留存: JS Crash、CPP Crash 会导致当前进程退出,无法保证当场展示弹窗。因此应在应用下次启动后识别并读取上次异常退出的信息。
  • 本地历史: 缓存最近 10 条崩溃记录,按最新优先展示。提供独立“崩溃记录”页面,列表展示摘要,点击单条后再查看完整信息。
  • 启动提示策略: 应用启动后自动提示最新一条崩溃。“确认”只关闭本次弹窗,下次启动仍提示;“暂不处理”后续不再提示当前崩溃,直到出现新的崩溃记录。
  • 诊断操作: 支持清除本地历史,并将崩溃记录导出为压缩包保存到手机,便于附在缺陷单中。

该能力只在 Debug测试包或内部环境启用;正式包仍应使用线上崩溃平台完成聚合、告警和版本追踪。

启动耗时

1. 现状

目前与其他端相比,鸿蒙端APM缺失启动耗时的统计与上报功能,需要上线该功能来监控和分析启动耗时问题。

2. 可行性分析

2.1 获取应用开始启动的时间

在鸿蒙开发中,要监听用户手指点击应用图标开始启动的时间,可以通过系统提供的启动耗时事件来获取。系统会在用户点击图标时自动打点记录,开发者通过订阅 HiAppEventAPP_LAUNCH 事件,即可在参数 icon_input_time 中拿到这个精确的时间戳。

示例代码

import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 订阅应用启动耗时事件
hiAppEvent.addWatcher({
  name: "appStartupWatcher",
  // 订阅应用启动事件
  events: ["APP_LAUNCH"], 
  onReceive: (domain: string, events: Array<hiAppEvent.AppEventPackage>) => {
    for (const event of events) {
      // 获取事件携带的参数
      const params = event.params as Record<string, Object>;
      // icon_input_time:用户点击桌面图标离手的时间戳(毫秒)
      const iconInputTime = params['icon_input_time'] as number;
      // extend_time:开发者调用 reportDrawnCompleted 的时间戳
      const extendTime = params['extend_time'] as number;
      // ......
    }
  }
});

注意,上述参数中的extend_time时间是根据开发者自定义调用reportDrawnCompleted时的时间戳,会在下述章节中阐述调用时机。

2.2 获取应用首屏渲染完毕的时间

要判断首页渲染完毕通常指首帧内容完整绘制到屏幕的时刻。可以通过 @kit.ArkUI.inspectorcreateComponentObserver 接口,监听首页根组件的 draw 事件,在回调中记录时间戳,即为渲染完成时刻。

示例代码

import { inspector } from '@kit.ArkUI';

@Entry
@Component
struct HomePage {
  @State startTime: number = 0;
  private listener: inspector.ComponentObserver = 
    this.getUIContext().getUIInspector().createComponentObserver('HOME_ROOT');

  private onDrawComplete = () => {
    const renderEndTime = new Date().getTime();
    console.info('首页渲染完成时间戳:', renderEndTime);
    // 可在此处计算启动耗时:renderEndTime - icon_input_time
  };

  aboutToAppear() {
    this.startTime = new Date().getTime();
    this.listener.on('draw', this.onDrawComplete);
  }

  aboutToDisappear() {
    this.listener.off('draw', this.onDrawComplete);
  }

  build() {
    Column() {
      // 首页核心内容
    }
    .id('HOME_ROOT')  // 必须与 createComponentObserver 的 id 一致
    .width('100%')
    .height('100%')
  }
}

3. 开发方案

3.1 首屏渲染

根据2.2可行性分析章节,可在应用的首页IndexM页面中监听整个页面的根组件:

struct IndexM {
	build() {
		Flex(...) { ... }
		.id('HOME_ROOT')
	}
}

在页面的aboutToAppear()的末尾使用inspector注册监听根组件HOME_ROOT的绘制完成:

// 创建首页根组件观察器,用于监听 HOME_ROOT 的绘制事件
private drawnListener = this.getUIContext().getUIInspector().createComponentObserver('HOME_ROOT');

// 页面绘制完成后通知系统,以便记录首帧渲染完成时间
private onDrawComplete = () => {
  // draw 事件可能因页面刷新重复触发,上报前先取消监听
  this.drawnListener.off('draw', this.onDrawComplete);
  this.context.reportDrawnCompleted(() => {});
};

aboutToAppear() {
  // ...
  // 页面即将显示时注册绘制监听;根组件完成绘制后触发回调
  this.drawnListener.on('draw', this.onDrawComplete);
}

aboutToDisappear() {
  // 页面退出时兜底取消监听,避免观察器残留
  this.drawnListener.off('draw', this.onDrawComplete);
}

整体步骤:

  1. 为页面根组件添加id HOME_ROOT
  2. 首页组件获取系统绘制完成inspector监听对象,用于监听HOME_ROOT的绘制完成事件draw
  3. draw 事件回调中调用reportDrawnCompleted函数,用于在HiAppEvent监听的APP_LAUNCH事件函数中获取渲染完毕的时间戳;
  4. 在项目已有的事件监听架构中,独立添加APP_LAUNCH事件的监听,并根据2.1可行性分析中获取到的icon_input_time启动时间和extend_time首页绘制完成时间来计算应用启动耗时;
  5. 复用已有的事件上传方法上传启动时间。

4. 时间校验

同一次冷启动中,应用日志与性能时间线的首帧耗时结果基本一致:日志记录为 1270 ms,性能时间线显示为约 1.2 s。说明 APP_LAUNCH 事件中的首屏绘制完成时间可用于启动耗时统计。

APP_LAUNCH 日志首屏绘制完成时间

图8:首屏绘制完成耗时为 1270 ms

性能时间线首帧耗时

图9:性能时间线中 First Frame - App Phase 显示首帧耗时约 1.2 s,与应用日志结果一致。

另外补充首帧效果图:

首页首帧效果图

**图10:首页首帧。

5. 启动性能劣化预防

冷启动自动化回归可分三个阶段执行:

  • 开发自测阶段: 首页初始化、SDK、路由或资源加载有改动时,开发者在本机或固定测试机进行少量冷启动采样,尽早发现明显劣化。
  • 合并主干阶段: 每次合入主干后由 CI 在固定真机执行冷启动回归,作为日常预警并持续积累稳定基线。
  • 发布候选阶段: 提测包、灰度包或正式发布包执行完整的多次采样和门禁判断;P95 明显劣化、启动失败或白屏时阻断发布。

发布候选阶段适合作为正式质量门禁;合并主干阶段是最有价值的预防点,因为问题仍容易定位和回滚。

5.1 开发自测:workfeature 分支对比

开发阶段可约定 feature 为稳定基线分支,work 为当前编码分支。通过脚本自动构建、安装并测试两个分支的冷启动耗时,输出首帧耗时及其差值;若 work 明显慢于 feature,开发者应在提交前检查新增初始化、资源加载或首页渲染逻辑。

为避免波动的影响,两个分支应在同一台真机、相同网络条件下交替执行多次(比如10次)冷启动,可使用平均数进行比较,并输出性能测试对比文档,该脚本用于开发阶段性能劣化预警。

5.2 feature合并主干或者主干发布前

合并主干后,CI 可在在固定真机上执行冷启动回归,持续对比当前版本与稳定基线的首帧耗时,作为日常预警。发现明显劣化时,开发者可问题进入发布流程前完成分析和修复。

正式发布前,应扩大采样次数,重点比较判断整体启动体验是否变慢,若明显劣化,或出现启动失败等问题,可暂停合并并保留测试日志和性能证据用于定位和修复。

APP_FREEZE

1. 现状

当前 APP_FREEZE 已具备捕获和上传能力。

2. 数据分析

版本号: FUHM37.08.390 / 1.20.02 时间范围: 2026-07-24 ~ 2026-07-30

平台 数量
华为 100
Grafnan 97

3. 目标

按数量排名,优先定位和治理排名靠前的10个 APP_FREEZE 异常。

改进目标与方向

改进目标

优先级 事项 具体措施 现状 验收标准 预计时间
P0 启动耗时 监听应用启动事件获取用户点击桌面事件和应用首屏渲染结束时间,计算出启动耗时。上传按照其他端后端上传方案。 期货鸿蒙缺失该功能 可准确获取应用启动耗时并上报统计。 4d(编码+自测+验收)
P0 app freeze(ANR) 目前APP_FREEZE已有捕获上传,但基本未治理。可先选取量级排名前10的先治理 FUHM37.08.390(1.20.02)版本冻屏幕率:
7.7 (日/万)
预计占比修复后降低到:
4(日/万)
4d(定位+修复)
P0 崩溃 目前的崩溃事件中的存在大量OOM事件,占比超过一半。需对OOM单独治理。 FUHM37.08.390(1.20.02)崩溃率:
5.08 (日/万)
目前OOM较难定位,还需继续定位问题。 1d定位
P1 崩溃开发阶段预防措施 在 Debug阶段增加崩溃诊断弹窗,在崩溃应用退出后下一次启动弹出,帮助开发者和测试在开发测试阶段发现和定位崩溃信息。
P1 启动性能劣化预防措施 编写自动化性能测试脚本,自动测试、获取和对比待合并和目标分支的启动性能。遇到性能劣化的情况可以及时修复。

后续方向

  1. 资源泄漏:问华为
  2. 网络相关。定位并降低HTTP请求慢率等指标。定位并优化WEB白屏和慢加载等问题。
  3. 待完善......