使用冻屏增强日志定位繁忙类问题
头像 鸿蒙小助手 2026-06-29 11:13:55    发布
3756 浏览 28 点赞 5 收藏

本原创文章帖发布在华为开发者联盟社区,欢迎开发者前往访问评论交流,更多与该内容相关讨论,请点击原帖查看:

使用冻屏增强日志定位繁忙类问题-华为开发者话题 | 华为开发者联盟


概述

      用户在使用应用时,如果出现点击无反应或应用无响应等情况,并且持续时间超过一定限制,就会被定义为应用冻屏(AppFreeze),即应用无响应。系统会检测应用无响应,并生成AppFreeze日志,供应用开发者分析。从API 21开始,支持获取​AppFreeze的增强日志​。该日志通过采集整机及主线程的运行负载,并抓取多份主线程调用栈,帮助开发者知晓函数调用耗时。

      日志获取及规格可以参考:AppFreeze(应用冻屏)检测-故障检测-Performance Analysis Kit(性能分析服务)-调测调优-系统 - 华为HarmonyOS开发者

冻屏的两种成因


类型特征传统栈追踪效果
阻塞类线程因资源无法获得而挂起3s/6s栈能支撑锁定耗时任务
繁忙类线程频繁执行业务逻辑仅靠2个瞬时栈无法锁定耗时函数

传统方案的痛点

      当 ThreadBlock3s 和 InputBlock 事件的调用栈出现以下情况时,传统日志难以定位问题:

  • 系统栈 - 主线程全是系统栈

  • 瞬时栈 - 栈指向的函数并非真正耗时,只是一个切面

      此时只能看到瞬时的调用栈片段,无法确认具体哪个函数在耗时。

增强日志的核心能力

      增强日志通过以下方式解决上述痛点:

  1. 采集多份主线程调用栈 - 在采样周期内多次抓取调用栈

  2. 记录整机及主线程运行负载 - 辅助判断线程是否真正获得CPU执行时间

  3. 采样栈与耗时关联分析 - 从多次采样中识别耗时函数

增强日志用法(重点)

Step 1: 判断线程是否获得CPU执行

      查看 CPU耗时统计 部分:


CpuTime: 0 ms                      <- 主线程实际运行时间
SyncWaitTime: 2995 ms              <- 主线程等待时间
StaticsDuration: 2995 ms           <- 统计总时间

      判断逻辑:


场景现象结论
CpuTime ≈ 0SyncWaitTime ≈ StaticsDuration线程未获得CPU,可能是等待锁、IO阻塞等阻塞类问题
CpuTime ≈ StaticsDurationSyncWaitTime ≈ 0线程一直在运行,属于繁忙类问题
CpuTime 居中两者都有值混合问题,需进一步分析

Step 2: 分析CPU使用率

      查看 CpuFreq Usage 部分:


cpu0 Usage 23.5%, 1430MHZ 21.04%
cpu1 Usage 23.5%, 1430MHZ 21.04%
cpu2 Usage 23.5%, 1430MHZ 21.04%
cpu3 Usage 23.5%, 1430MHZ 21.04%

      判断逻辑:
      调用栈栈顶未阻塞,CPU趋近100%,考虑系统高负载类问题,可忽略本次故障。

Step 3: 解析采样栈

      增强日志会采集 多份 主线程调用栈,查看 #ThreadInfos 部分:


#ThreadInfos Tid: 2204, Name: com.example.freeze
SnapshotTime: 2021-01-01-20-05-58.292875
#00 pc 00000000000015b8 [shmm](__kernel_gettimeofday+72)
#01 pc 00000000001d7e44 /system/lib64/ld-musl-aarck64.so.1(clock_gettime+48)
#02 pc 00000000001d9f20 /system/lib64/ld-musl-aarck64.so.1(time+32)
#03 pc 0000000000007e2c .../libsample.so(WaitSomeTime()+76)
...

========SubmitterStacktrace========
#00 pc 0000000000013108 /system/lib64/platformsdk/libuv.so(uv_queue_work+292)
#01 pc 0000000000008cdc .../libsample.so
#02 pc 000000000005ae00 .../libace_napi.z.so(...)
...

      分析步骤:

  • 统计调用路径出现次数 - 从多次采样中找到重复出现的函数调用

  • 抽取最大公共路径 - 识别频繁执行的代码路径

  • 估算耗时 - 根据采样次数估算函数累计耗时

      示例:

      假设从 ThreadBlock3s 到 InputBlock 期间有6次采样:

  • 3次采样命中 Task3 → Task3 耗时至少 900ms

  • 3次采样命中 Task4 → Task4 耗时至少 900ms

Step 4: 结合多种栈信息分析

      可结合以下堆栈信息一同分析:


堆栈类型触发条件用途
ThreadBlock3S主线程卡死超过3秒定位长时间阻塞
ThreadBlock6S主线程卡死超过6秒定位严重卡死
AppInputBlock输入事件超时定位UI响应问题
采样栈增强日志采集定位繁忙类耗时

      优化优先级:根据调用链路出现的次数来决定优化优先级。

与Android ANR日志的对比

      在Android开发中,ANR(Application Not Responding)问题定位主要依赖traces.txt文件。以下是对比分析:

核心机制对比


对比项Android ANRHarmonyOS AppFreeze
dump机制系统超时后dump一次全线程栈采样周期内多次抓取调用栈
栈数量通常只有0-1份瞬时栈多份采样栈(时间序列)
CPU信息无CPU运行负载统计完整的CpuTime/SyncWaitTime统计
任务溯源SubmitterStacktrace追溯任务发起者

Android ANR日志示例


"main" tid=1 Native
  #00 pc 0x00000000000015b8  [kernel]
  #01 pc 0x00000000001d7e44  libmusl.so (clock_gettime+48)
  #02 pc 0x00000000001d9f20  libmusl.so (time+32)
  #03 pc 0x0000000000007e2c  libsample.so (WaitSomeTime+76)
  ...

      问题:仅能看到一个时间点的调用栈,无法确认WaitSomeTime是瞬时执行还是持续阻塞。

HarmonyOS 增强日志示例

      同样的场景,增强日志提供更多信息:


# CPU耗时分析
CpuTime: 1500 ms           <- 线程实际运行了1500ms
SyncWaitTime: 1500 ms      <- 同时等待了1500ms
StaticsDuration: 3000 ms   <- 总统计时间3000ms

# 第一次采样 (T=0ms)
SnapshotTime: 2021-01-01-20-05-58.292875
#00 WaitSomeTime()+76
#01 process()+200
#02 onInputEvent()+150

# 第二次采样 (T=300ms)
SnapshotTime: 2021-01-01-20-05-58.592875
#00 WaitSomeTime()+76
#01 process()+200
#02 onInputEvent()+150

# 第三次采样 (T=600ms)
SnapshotTime: 2021-01-01-20-05-58.892875
#00 WaitSomeTime()+76
#01 process()+200
#02 onInputEvent()+150

      价值:3次采样都命中WaitSomeTime,证明该函数持续阻塞至少600ms。

采样栈的定位效果

场景1:系统栈无法定位问题


问题AndroidHarmonyOS增强日志
主线程栈全是系统调用无法定位业务代码通过多次采样可识别耗时业务函数
瞬时栈指向process无法确认具体耗时采样序列显示process累计耗时

      Android日志:


#00 [kernel]
#01 EventHandler.process()
#02 EventRunner.Run()

      只能知道在process中,无法确认具体哪个子函数耗时。

      HarmonyOS增强日志:


# 采样1: process() -> group_api() -> render()
# 采样2: process() -> group_api() -> render()
# 采样3: process() -> list_builder() -> item_create()

      可明确group_api被多次采样命中,是耗时根因。

场景2:繁忙类问题定位


问题AndroidHarmonyOS增强日志
线程Busy但不知在做什么仅有1份栈,可能错过关键调用多次采样覆盖完整执行路径
无法估算各函数耗时无法估算根据采样次数估算相对耗时

      Android日志:1份栈只能看到瞬时状态。

      HarmonyOS增强日志:


采样1: Task3 -> funcA -> funcB (命中)
采样2: Task3 -> funcA -> funcC (命中)
采样3: Task4 -> funcD -> funcE (命中)
采样4: Task4 -> funcD -> funcE (命中)

      可确认Task3和Task4各执行2次,结合总耗时可估算单次执行时间。

场景3:异步任务溯源


问题AndroidHarmonyOS增强日志
任务由谁提交无法追溯SubmitterStacktrace完整链路
uv_queue_work来源无记录拼接任务提交者调用栈

      当采样任务来源于uv_queue_work时,HarmonyOS增强日志会拼接完整的任务提交者调用栈:


========SubmitterStacktrace========
#00 uv_queue_work+292
#01 libsample.so
#02 ArkNativeFunctionCallBack()
#03 RTStub_PushCallArgsAndDispatchNative()
#04 BCStub_HandleCallthis0Imm8V8StwCopy+372
#05 at Index.ts:381:36  <- 追溯到具体JS代码行

总结对比


场景Android ANRHarmonyOS AppFreeze
阻塞类(等锁/IO)3s/6s栈可定位同样可定位
系统栈占比高难以穿透采样栈可穿透
瞬时栈指向模糊无法确认耗时采样序列可确认
繁忙类(频繁执行)仅1份栈,难以分析多次采样,轻松定位
异步任务溯源无法追溯SubmitterStacktrace可追溯

      核心优势:HarmonyOS增强日志的采样栈机制,将"瞬时切面"转化为"时间序列",特别适合Android难以定位的繁忙类和系统栈密集类问题。如下图,可以完整的看出调用序列中的瓶颈在appfreeze_threadblock模拟函数中。

总结

      冻屏增强日志的核心价值在于:将"瞬时切面"转化为"时间序列",让开发者能够追踪主线程在一段时间内的完整执行轨迹,从而精准定位繁忙类冻屏的根因。

      增强日志使用口诀:

  • 先看CpuTime - 判断是阻塞还是繁忙

  • 再看Cpu使用率 - 确认是否为调度问题

  • 后解析采样栈 - 找出耗时函数

  • 结合多栈分析 - 确定优化优先级


©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
HarmonyOS
头像

鸿蒙小助手

致力于为鸿蒙开发者谋福利

1099

帖子

8

提问

13572

粉丝

关注
热门推荐
地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除

京ICP备:2022009079号-2

京公网安备:11010502051901号

ICP证:京B2-20230255