EN

Android Systrace 基础知识 -- 分析 Systrace 预备知识

字数统计: 4.1k阅读时长: 15 min
2019/07/23
loading

2026-10-13 技术更新:本文保留 2019 年起积累的旧 Systrace 截图,其中也有后补的案例。颜色与快捷键属于 legacy UI;Linux 状态说明参照 v6.6 文档,现代入口核对到 Perfetto UI v58.3(11fbaed83)。原图里的 Self Time、唤醒及调度状态注释有几处不准确,已在相邻正文说明;截图不等于本次设备实测。

本文是 Systrace 系列文章的第二篇,主要是讲解一些分析 Systrace 的预备知识, 有了这些预备知识, 分析 Systrace 才会事半功倍, 更快也更有效率地找到问题点.

本文介绍了如何查看 Systrace 中的线程状态, 如何对线程的唤醒信息进行分析, 如何解读信息区的数据, 以及介绍了常用的快捷键. 通过本篇文章的学习, 相信你可以掌握进程和线程相关的一些信息, 也知道如何查看复杂的 Systrace 中包含的关键信息

本文目录

系列文章目录

  1. Systrace 简介
  2. Systrace 基础知识 - Systrace 预备知识
  3. Systrace 基础知识 - Why 60 fps ?
  4. Systrace 基础知识 - SystemServer 解读
  5. Systrace 基础知识 - SurfaceFlinger 解读
  6. Systrace 基础知识 - Input 解读
  7. Systrace 基础知识 - Vsync 解读
  8. Systrace 基础知识 - Vsync-App :基于 Choreographer 的渲染机制详解
  9. Systrace 基础知识 - MainThread 和 RenderThread 解读
  10. Systrace 基础知识 - Binder 和锁竞争解读
  11. Systrace 基础知识 - Triple Buffer 解读
  12. Systrace 基础知识 - CPU Info 解读
  13. Systrace 流畅性实战 1 :了解卡顿原理
  14. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
  15. Systrace 流畅性实战 3 :卡顿分析过程中的一些疑问
  16. Systrace 响应速度实战 1 :了解响应速度原理
  17. Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
  18. Systrace 响应速度实战 3 :响应速度延伸知识
  19. Systrace 线程 CPU 运行状态分析技巧 - Runnable 篇
  20. Systrace 线程 CPU 运行状态分析技巧 - Running 篇
  21. Systrace 线程 CPU 运行状态分析技巧 - Sleep 和 Uninterruptible Sleep 篇

正文

用 Perfetto 阅读本文

本文讲的是分析 Trace 前需要建立的几个基础能力:线程状态、唤醒关系、信息区字段和快捷操作。旧 Systrace UI 和新 Perfetto UI 的界面不一样,但这几个能力仍然是同一套分析基础。

旧流程里,线程状态通过 Systrace HTML 里的颜色直接展示,点击 Slice 后在下方信息区看 Duration、Thread、Process、唤醒关系等字段;新流程里,Perfetto 会把这些信息拆到线程轨道、CPU 调度轨道、选择面板、搜索结果和 SQL 表中。读法要从“看一张 HTML 图”变成“先看轨道,再用搜索/SQL 收敛证据”。

对应到 Perfetto 系列,可以这样读:

本文知识点 Perfetto 中的对应入口
打开、缩放、搜索、选中 Slice Perfetto 3:熟悉 Perfetto View
Running、Runnable、Sleep 等线程状态 Perfetto 9:CPU 信息解读
主线程等待、RenderThread 等待、Binder 等待 Perfetto 7:MainThread 和 RenderThread / Perfetto 10:Binder 调度与锁竞争

线程状态查看

旧 Systrace 在已采集调度数据时,用线程状态条辅助解释 Slice。Slice 是埋点区间,不一定对应一个完整方法;颜色和状态能帮助缩小排查方向,不能单独证明 Binder、锁、I/O 或 CPU 是根因。下面的颜色对应历史截图,实际分析以 State 文本与事件为准。

如果现在用 Perfetto 看同类问题,不要只找和 Systrace 完全一样的颜色。更稳定的做法是同时看三类信息:线程轨道上的 Slice,CPU 轨道上的实际 Running 区间,以及调度状态里的 Runnable/Sleep/Uninterruptible Sleep。这样才能区分“函数自己执行慢”“线程醒了但没抢到 CPU”“线程在等 Binder/锁/IO”这几类完全不同的问题。

线程状态主要有下面几个

绿色 : 运行中(Running)

在查看器的这个区间内,线程被调度到某个逻辑 CPU 上执行;它不是“准备执行但还在排队”。Linux 的 TASK_RUNNING / ps 的 R 则同时包含正在执行与可运行等待两种情况,查看器结合调度事件把二者分开。一个线程不能同时在两个逻辑 CPU 上执行,但可以在不同区间迁移到其他 CPU。

我们经常会拿同一场景的 Running 时间作对比:为什么这边快、那边慢?频率不同吗,跑的核不同吗,还是在 Running、Runnable、Sleep 之间来回切换?这些都要结合 CPU 拓扑和实际调度去看。跑在小核或频率较低本身不等于调度错误;还需确认负载、功耗策略、温度和关键路径。图中 android.bg 在 CPU 0 上的 Running 区间为 41.743 ms,只描述该区间,不代表整个方法或全部进程都执行了这么久。

蓝色 : 可运行(Runnable)

线程具备运行条件,但当前没有在 CPU 上执行。它可能刚被唤醒,也可能刚被抢占;等待多久取决于允许使用的 CPU、优先级、调度类、CPU 配额/限流及其他竞争者。

长 Runnable 是调度延迟线索,不直接证明所有 CPU 都忙,也不能仅凭它认定频率太低或后台应用过多。先看线程允许在哪些 CPU 运行、这些 CPU 当时在做什么,以及是否存在限流、抢占或唤醒迁移。

白色 : 休眠中(Sleep)

通常对应可中断睡眠:线程在等待事件、定时器、Binder 或同步条件,既可能是正常空闲,也可能在关键路径上等待结果。“正在等”不等于“没有工作要完成”。

图中 audioserver 的 Binder:1506_2 显示 Sleeping,阻塞位置是 binder_thread_read,这是等待 Binder 相关事件的线索,不能只凭这一段认定互斥锁竞争或性能故障。

橘色 : 不可中断的睡眠态 (Uninterruptible Sleep - IO Block)

图中 State 是 Uninterruptible Sleep | WakeKill - Block I/O,阻塞位置实际写的是 wait_on_page_bit_killable,并非原文所写的 wait_on_page_locked_killable。这类信息通常由调度事件及额外的阻塞原因/I/O 等待标记组合而来,是否能看到取决于内核、权限和采集配置。

它提示页面相关等待,应继续结合缺页、回收、存储请求和阻塞栈确认原因。冷文件读取、页面锁竞争或内存压力都可能参与,不能由橙色或这一函数名直接得出“低内存”“磁盘一定繁忙”的结论。图中的 WakeKill 还说明不能把“不可中断”理解成所有信号都永远无效;Linux 的 killable 等待包含致命信号唤醒语义。

紫色 : 不可中断的睡眠态(Uninterruptible Sleep)

这张图的 State 明确显示 Uninterruptible Sleep;它不表示“通常就是内存管理”,也不只是“进入了内核态”。正在执行系统调用的线程仍可能处于 Running。不可中断等待可能涉及驱动、同步或其他内核资源,需沿实际阻塞位置查证。

图中线程是 [crtc_commit:127],附近还有显示合成相关轨道,不能将它直接归为内存不足。旧查看器还会为若干其他状态复用相近颜色,所以应读取 State 字段,不能只看紫色。

线程唤醒信息分析

Systrace 会标识出一个非常有用的信息,可以帮助我们进行线程调用等待相关的分析。

唤醒信息给出了调度事件中的一个关联点:谁让目标线程变得可运行。这个线程不一定就是锁持有者、等待结果的生产者或最早的根因;中断上下文、转交唤醒、超时或缺失事件都需要另查。唤醒之后还可能继续等待 CPU,因此要把睡眠时间、唤醒到运行的延迟与实际 Running 时间分开。

例如,App 主线程发出同步 Binder 请求后,可能因服务端执行、锁或调度而等待;这是一种待验证的路径,不能直接当作“跑完 Monkey 或后台进程多必然变慢”的解释。应关联请求/回复、对端线程和持锁者证据。

下面的历史案例中,选区覆盖 Choreographer#doFrame / traversal 的约 152.78 ms 跨度,状态条显示大量非 Running 时间。这个跨度值得排查,但不等于 CPU 执行了 152.78 ms,也不能凭单张图计算完整输入延迟或当前设备掉帧率。

image-20211210185851589

放大后,选中的 Runnable 信息包含 wakeup from tid: 20424;这里的 Running instead 是另一项调度观察,不等于唤醒者或锁持有者。Perfetto 中可结合 thread_state 的 state、waker_utid、io_wait、blocked_function 与 CPU 调度数据查看;这些字段可能为空,IRQ 上下文也不能简单归因于被中断的用户线程。

image-20211213145728467

原案例将 TID 20424 对应到 RenderHeartbeat。下方裁图能看到这个名称,但没有显示 TID,因此应在原始 Trace 的同一进程、同一时间段核对映射,再检查应用逻辑。一次唤醒记录不足以证明 RenderHeartbeat 就是整段长等待的根因。

image-20211210190921614

从睡眠唤醒后,线程会先变为可运行,再被调度执行;但它也可能因抢占从 Running 回到 Runnable,而没有一次新的唤醒事件。短等待可能被缩放隐藏,事件丢失也会使信息不完整。下面另一个历史示例记录了 wakeup from tid: 31388,不要与上面的 20424 案例混为一条调用链。

CPU Slice 中的 stateWhenDescheduled 表示该次运行区间结束、线程被切出 CPU 时的状态。下面图里的 S 是切出时进入睡眠,不是“唤醒前状态”;原图红字在这一点上不准确:

Linux 的状态与 ps 的显示修饰符还需要分开。下面按 procps-ng ps 手册 说明;Android 的 ps 实现/选项可能不同,不要把整列 STAT 原样当作 Perfetto 状态名。

主状态 含义
R 正在执行或可运行等待,不能只解释为排队
S 可中断睡眠,等待条件或事件
D 不可中断睡眠,常涉及 I/O,但不是磁盘根因结论
I 空闲内核线程
T / t T 为作业控制停止;t 为调试器跟踪停止
Z / X Z 为已退出但尚未回收的僵尸;X 为死亡状态,通常不应看到
W 旧 paging 状态,Linux 2.6 起已不再有效

BSD 风格 STAT 的附加字符包括:< / N 表示较高/较低优先级,L 表示存在锁定内存页,s 是 session leader(不以“有子进程”定义),l 表示多线程,+ 表示前台进程组,不是后台。这里的 + 不应与调度 Trace 中 R+ 的抢占标记混为一谈。Linux v6.6 内核状态另见 proc 文档。

信息区数据解析

线程状态信息解析

图中选中的是 Weibo 主线程的一段 Running:Start 约 1358.039 ms、Duration 5.360 ms、On CPU 为 CPU 4。它描述这段调度状态,不是整个 bindApplication 的耗时。PID 与线程名用于定位对象;CPU 编号只有结合设备拓扑才知道大小核,不能仅凭“CPU 4”猜核心类型。

函数 Slice 信息解析

图中 bindApplication 的 Wall Duration 为 593.686 ms,Self Time 为 116.462 ms。Wall Duration 包含区间内的运行和等待;Self Time 是扣除嵌套子 Slice 后的时间,仍可能包含等待。原图把 Self Time 标成“不包含等待时间”是错误的,它不是 CPU Duration。没有埋点的子调用也不会凭空被扣除;Category 是事件分类,不等于完整调用栈。计算方式可对照 Catapult SliceGroup。

Counter Sample 信息解析

这里选中的是一条 counter 序列在某个时间点的样本,Value 为 3。它是该指标的数值,不是“Counter 的个数”。Counter/Series 用于辨认指标与序列;单位、是否累计、是否会下降以及采样间隔都需看数据源定义,不能把一个样本自动当成区间总量。

Async Slice 信息解析

图中 animator 的 Wall Duration 为 199.579 ms,表示这条异步事件从开始到结束的跨度;不是某一线程持续执行 199.579 ms,也不是 GPU 时间。异步区间需要按事件关联标识理解,不能把跨线程或重叠区间简单相加成 CPU 消耗。

CPU Slice 信息解析

图中 android.bg(TID 1416)在所选 CPU 上的运行区间为 37.654 ms,prio 为 130。prio 是内核调度优先级表示,不是 MHz,也不是 Java Thread 的 1—10 优先级。stateWhenDescheduled: R+ 表示切出后仍可运行,并带抢占标记;不能解释成唤醒前状态或前台进程组。Active slices 是关联的埋点区间,不保证列出所有函数。

User Expectation 信息解析

旧 Systrace 的 Interactions 区域会显示 Rendering Response、Input Response 等由查看器分析生成的区间。图中 Input Response 为 48.993 ms,这是该分析规则的时间跨度,不是人类耐受阈值,也不能直接当作触摸到屏幕发光的实测延迟。要核对其事件边界;现代帧诊断可另采集 Android 12 / API 31+ 的 FrameTimeline,但它也不自动覆盖完整输入链路。

快捷键使用

看 Trace 时经常要选中、平移、缩放,来回点按钮比较费事,熟悉几个快捷键会方便很多。下面是旧 Systrace/Catapult UI 的常用按键,字母按不带 Shift 的小写键理解。它们不能全部照搬到 Perfetto:

按键 legacy UI 用途
w / s 放大 / 缩小时间轴
a / d 左移 / 右移时间轴
m 将当前选区设置为关注时间范围,便于对照边界
1 Selection:点击选择事件
2 Pan:拖动平移
3 Zoom:拖动缩放
4 Timing:选起止位置测量时间

默认鼠标模式可能随查看器设置保留,可显式按 1 进入 Selection。编辑搜索框等文本输入时,也要注意键盘焦点。

本次核查的 Perfetto UI 为 v58.3(11fbaed83):在侧栏 Support 中打开 Keyboard shortcuts,或用 ? 查看当前帮助。帮助列出的鼠标操作包括点击选事件、拖动选择区域、Shift + 拖动平移;命令面板在 macOS 上为 Cmd + Shift + P,其他平台按界面显示的修饰键操作。旧版的 1—4 鼠标模式和 m 操作不要当作跨版本合同。

当前表字段可查 PerfettoSQL Prelude:调度区间常用 sched_slice,线程状态常用 thread_state。utid 是 Trace 内部唯一线程标识,不等同于可能复用的系统 TID;时间字段通常以纳秒保存,阅读毫秒时要换算。没有对应调度/唤醒数据时,空字段保留为未知,而不是零等待或“没有唤醒者”。

本文知乎地址

由于博客留言交流不方便,点赞或者交流,可以移步本文的知乎或者掘金页面
知乎 - Systrace 基础知识 – 分析 Systrace 预备知识
掘金 - Systrace 基础知识 – 分析 Systrace 预备知识

关于我 && 博客

下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!

  1. 博主个人介绍 :里面有个人的微信和微信群链接。
  2. 本博客内容导航 :个人博客内容的一个导航。
  3. 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
  4. Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。

一个人可以走的更快 , 一群人可以走的更远

微信扫一扫

CATALOG
  1. 1. 本文目录
  • 系列文章目录
  • 正文
    1. 1. 用 Perfetto 阅读本文
    2. 2. 线程状态查看
      1. 2.1. 绿色 : 运行中(Running)
      2. 2.2. 蓝色 : 可运行(Runnable)
      3. 2.3. 白色 : 休眠中(Sleep)
      4. 2.4. 橘色 : 不可中断的睡眠态 (Uninterruptible Sleep - IO Block)
      5. 2.5. 紫色 : 不可中断的睡眠态(Uninterruptible Sleep)
    3. 3. 线程唤醒信息分析
    4. 4. 信息区数据解析
      1. 4.1. 线程状态信息解析
      2. 4.2. 函数 Slice 信息解析
      3. 4.3. Counter Sample 信息解析
      4. 4.4. Async Slice 信息解析
      5. 4.5. CPU Slice 信息解析
      6. 4.6. User Expectation 信息解析
    5. 5. 快捷键使用
  • 本文知乎地址
  • 关于我 && 博客