Android 性能优化利器 —— Perfetto
介绍
Perfetto 是 Android Q 中推出的先进平台级追踪工具,旨在为 Android、Linux 和 Chrome 平台提供统一的性能监测和分析解决方案。其核心特色在于引入了一种全新的用户空间间追踪协议。
该协议基于 protobuf 序列化机制,将采集的数据存储到共享内存缓冲区,从而能够获取平台内部的各种数据源,如 ftrace、atrace 和 logcat。此外,Perfetto 还为上层 C++ 应用程序提供了 SDK 和库,支持自定义功能。用户可以通过灵活的配置文件对数据源抓取进行动态设置, 并能够将长时间的追踪数据流保存到文件系统中。
核心功能
Perfetto 的三大核心功能包括:记录追踪(record trace)、分析追踪(analyze traces)和可视化追踪(visualize traces)。
- 记录追踪(record trace):Perfetto 允许用户记录系统和应用程序的运行时数据,以生成详细的性能追踪信息。这包括系统级别的活动(如
CPU 使用情况、磁盘 I/O、网络流量)和应用级别的活动(如函数调用、内存分配):
- 系统级和应用级记录:可以捕捉操作系统和应用程序的性能数据。
- 灵活的记录选项:支持多种记录配置,包括选择特定的跟踪事件和设置记录的时间范围。
- 原生和 Java 堆分析:支持对原生代码和 Java 堆进行详细的分析,帮助识别内存泄漏和性能瓶颈。
- 分析追踪(analyze traces):Perfetto 提供了强大的工具来分析记录的追踪数据。用户可以使用 SQL
查询语言对追踪数据进行深入分析,以提取有用的信息和发现性能问题:
- SQL 查询支持:通过 SQL 查询语言进行数据分析,能够对复杂的追踪数据进行灵活查询和筛选。
- 详细的性能分析:分析记录的追踪数据,以识别潜在的性能瓶颈和优化点。
- 定制化分析:允许用户创建自定义的分析脚本和报告,以满足特定的需求。
- 可视化追踪(visualize traces):Perfetto 提供了一个基于 Web 的用户界面,用于可视化和浏览大规模的追踪数据,使得用户能够直观地查看和探索性能数据:
- 交互式可视化:通过图形化界面展示追踪数据,包括时间轴、事件和图表,帮助用户更好地理解系统行为。
- 多 GB 数据支持:能够处理和可视化大规模的追踪数据,使得用户可以分析复杂的系统状态和行为。
- 探索性分析:提供工具和功能,允许用户深入挖掘数据,识别性能瓶颈和系统问题。
与传统的 Systrace 相比,Perfetto 功能更强大,界面标记更醒目。


录制(Record)
Danger
Perfetto 从 Android 11(R)才默认启用。在 Android 9 (P) 和 10 (Q) 上,需要启用 Perfetto 服务。
# 仅在非 Pixel 手机上的 Android 9 (P) 和 10 (Q) 上需要。 adb shell setprop persist.traced.enable 1
在 Android 9(P)之前的版本,可以使用 基于 tools/record_android_trace 脚本 的方式抓取。
抓取 Perfetto 的方式有以下四种:

adb shell perfetto
确保设备通过 USB 连接到电脑,然后执行如下的命令:
adb shell perfetto -o /data/misc/perfetto-traces/trace_file.perfetto-trace -t 20s sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory
上述命令通过 adb shell perfetto
的方式,调用了 /system/bin/perfetto
可执行文件,然后抓取 20 秒(-t
参数)的
Perfetto。当抓取完成后将抓取到的
数据保存在 /data/misc/perfetto-traces/trace_file.perfetto-trace
路径下。
使用这种方式抓取 Perfetto,需要注意:
- Ctrl+C 通常会导致跟踪正常终止,但在使用时 ADB 不会传播该按钮
adb shell perfetto
,只有在通过进行基于 PTY 的交互式会话时才会传播该按钮adb shell
。 - 在 Android 10 之前的设备上,adb 无法直接拉取
/data/misc/perfetto-traces
。请使用adb shell cat /data/misc/perfetto-traces/trace > trace
来解决。 - 在 Android 12 之前的未 root 设备上,
cat config | adb shell perfetto -c -
由于 SELinux 规则过于严格,配置只能以(-: stdin)
形式传递。从 Android 12 开始,/data/misc/perfetto-configs
路径可用于存储。 - 在长时间抓取 Perfetto 时,可以使用
PID=$(perfetto --background)
然后kill $PID
停止 Perfetto。
除了在 adb shell perfetto
命令后传递参数,还可以通过将参数写入到配置文件中,然后使用 -c
命令传递文件参数。受限于
SELinux 的原因,Android 12 版本前后的版本存在区别。
虽然通过命令行传递参数的方式已经足够方便了,但是很多时候需要精细化的配置 Perfetto 的参数,这种情况下就要考虑使用配置文件的方式传递参数。由于 SELinux 的原因,通过配置文件传递参数的方式在 Android 12 版本上有些不同。
Android 12 以前的设备
adb push config.pbtx /data/local/tmp/config.pbtx
adb shell 'cat /data/local/tmp/config.pbtx | perfetto -c - -o /data/misc/perfetto-traces/trace.perfetto-trace'
首先,将本地的 config.pbtx
推送到设备的 /data/local/tmp/
路径下。然后通过 cat
的方式读取设备中的 config.pbtx
文件内容传递给 Perfetto。
Warning
在 User Debug 或者 User Root 的设备上,可以执行 adb shell setenforce 0
关闭 SELinux 权限,从而让 Perfetto
读取配置文件时不会报权限不足的情况。
Android 12 及其以后的设备
从 Android 12 开始,/data/misc/perfetto-configs
可以用于存储 Perfetto 的配置文件,因此可以直接把文件通过 -c
参数传递给
Perfetto。
adb push config.pbtx /data/misc/perfetto-configs/config.pbtx
adb shell perfetto --txt -c /data/misc/perfetto-configs/config.pbtx -o /data/misc/perfetto-traces/trace.perfetto-trace
Note
Perfetto 源码 中也提供了一些开箱即用的配置文件。
record_android_trace
Perfetto 团队推荐使用 tools/record_android_trace 脚本抓取 Perfetto。因为相比命令行的方式,基于 tools/record_android_trace 脚本的方式,简化了推送配置文件到设备、把抓取到的 Perfetto 导出到本地、使用浏览器访问它的过程,因此在使用和操作上更加便捷。
Linux 系统和 Mac:
# 下载脚本到本地
curl -O https://raw.githubusercontent.com/google/perfetto/main/tools/record_android_trace
# 添加可执行权限
chmod u+x record_android_trace
# 执行脚本,可以通过 ./record_android_trace --help 查询帮助信息
./record_android_trace -o trace_file.perfetto-trace -t 30s -b 64mb sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory
Windows:
# 下载脚本到本地
curl -O https://raw.githubusercontent.com/google/perfetto/main/tools/record_android_trace
# 执行脚本
python3 record_android_trace -o trace_file.perfetto-trace -t 30s -b 64mb sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory
Note
执行 curl
时可能无法下载脚本,此时可以访问 工具源码
网页。全选源码,然后复制到本地文件,这个文件取名 record_android_trace.py
即可。或者也可以
此处点击
下载该文件。
Perfetto UI
访问 Perfetto 的官网,然后点击 Record new trace
进入到 Record Settings 界面。

通过 Add ADB Device
的方式选择需要抓取的设备,选择完成后在区域 3 选择配置参数进行配置,详细的配置参数会被显示到区域 5
中。当配置参数选择完成后,点击区域 3 中的 Recording command
命令查看 Perfetto 配置信息,可以选择分享配置信息或者将其复制到本地。
Note
选择将配置信息复制到本地时,若在 Linux 或者 Mac 设备上,可以直接将复制的内容保存为 trace.sh
,然后执行:
sh trace.sh
若在 Windows 设备上,需要通过 adb shell perfetto
的方式运行,此时需要删除蓝色背景行的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Settings
通过设置(Settings)启动系统跟踪的步骤是:Settings > 开发者选项(Developer options)> System Tracing > Record trace。

需要停止抓取 Perfetto 时,再次点击 Record trace 按钮即可停止抓取。也可以下拉状态栏,点击按钮停止。停止完成后,可以通过系统提示找到 Perfetto 的保存位置。或者在 Settings > 开发者选项(Developer options)> System Tracing 中查看是否有提示保存的路径。
配置文件
Perfetto 使用的配置文件(TraceConfig)是一个 protobuf
格式文件,在这个文件中定义了:
- 整个跟踪系统的一般行为。例如:跟踪的最大持续时间、内存缓冲区的数量及其大小和输出跟踪文件的最大大小。
- 启用哪些数据源及其配置。例如:对于内核跟踪数据源启用哪些 ftrace 事件;对于堆分析器,目标进程名称和采样率。
- DataSource 和 Buffer 的映射关系,即每个数据源应该写入哪个缓冲区。
在 Perfetto 中,Trace 的跟踪服务(traced
)充当配置的分发者的角色。traced
接收从 Perfetto 终端窗口或者其他启动 Perfetto
的命令(类似消费者)接收配置,然后将这些配置信息转发给生产者。当消费者启动跟踪会话时,traced
将:
- 读取 TraceConfig 的外部部分(例如:
duration_ms
、buffers
等),并使用它们来确定其自身的行为。 - 读取部分中的数据源列表
data_sources
。对于配置中列出的每个数据源,如果linux.ftrace
已注册相应名称,则服务将要求生产者进程启动该数据源。

Buffers
缓冲区部分定义跟踪服务拥有的内存缓冲区的数量、大小和策略。它如下所示:
# Buffer #0
buffers {
size_kb: 4096
fill_policy: RING_BUFFER
}
# Buffer #1
buffers {
size_kb: 8192
fill_policy: DISCARD
}
每个缓冲区都有一个填充策略,可以是以下任一项:
- RING_BUFFER (默认): 缓冲区的行为类似于环形缓冲区,当缓冲区已满时,写入将覆盖并替换缓冲区中最旧的跟踪数据。
- DISCARD: 缓冲区在数据满后停止接受数据。尝试写入的新数据将被丢弃。
Danger
DISCARD 可能会对在跟踪结束时提交数据的数据源产生意外的副作用。
跟踪配置必须定义至少一个缓冲区才有效。最简单的情况是,所有数据源都会将其跟踪数据写入同一个缓冲区。虽然这对于大多数基本情况来说都没问题, 但当不同数据源的写入速率明显不同时,可能会出现问题。
例如,一个同时启用以下两项的跟踪配置:
- 内核调度程序跟踪器。在 Android 设备上,它每秒记录约 10000 个事件,将约 1 MB/s 的跟踪数据写入缓冲区。
- 内存统计轮询。此数据源将 /proc/meminfo 的内容写入跟踪缓冲区,并配置为每 5 秒轮询一次,每个轮询间隔写入 ~100 KB。
如果两个数据源都配置为写入同一个缓冲区,并且该缓冲区设置为 4MB,则大多数跟踪将仅包含一个内存快照。即使第二个数据源运行正常,大多数跟踪也很可能根本不包含任何内存快照。这是因为在 5 秒的轮询间隔内,调度程序数据源最终可能会填满整个缓冲区,从而将内存快照数据推出缓冲区。
动态缓冲区映射
在 Perfetto 中,数据源 ↔ 缓冲区
映射是动态的。在最简单的情况下,跟踪会话只能定义一个缓冲区。默认情况下,所有数据源都会将数据记录到该缓冲区中。
只定义一个缓存区对于上述提到的例子就可能存在问题。最好将这些数据源分离到不同的缓冲区中。这可以通过 TraceConfig
中的 target_buffer
字段来实现。

可以通过以下的方式实现:
data_sources {
config {
name: "linux.ftrace"
target_buffer: 0 # <-- This goes into buffer 0.
ftrace_config { ... }
}
}
data_sources: {
config {
name: "linux.sys_stats"
target_buffer: 1 # <-- This goes into buffer 1.
sys_stats_config { ... }
}
}
data_sources: {
config {
name: "android.heapprofd"
target_buffer: 1 # <-- This goes into buffer 1 as well.
heapprofd_config { ... }
}
}
注释版配置文件
Note
详细的配置参数可以前往 TraceConfig 查询。
以下是一个较为完整的注解版的配置文件示例。
buffers: {
size_kb: 63488 # 设置第一个缓冲区的大小为 63488 KB
fill_policy: DISCARD # 缓冲区满时丢弃最旧的数据
}
buffers: {
size_kb: 2048 # 设置第二个缓冲区的大小为 2048 KB
fill_policy: DISCARD # 缓冲区满时丢弃最旧的数据
}
data_sources: {
config {
name: "android.packages_list" # 配置数据源以收集 Android 包列表
target_buffer: 1 # 指定该数据源使用的缓冲区
}
}
data_sources: {
config {
name: "android.gpu.memory" # 配置数据源以收集 GPU 内存信息
}
}
data_sources: {
config {
name: "linux.process_stats" # 配置数据源以收集 Linux 进程统计信息
target_buffer: 1 # 指定该数据源使用的缓冲区
process_stats_config {
scan_all_processes_on_start: true # 启动时扫描所有进程
proc_stats_poll_ms: 1000 # 每 1000 毫秒轮询进程统计信息
}
}
}
data_sources: {
config {
# 配置数据源以收集 SurfaceFlinger 帧时间线
name: "android.surfaceflinger.frametimeline"
}
}
data_sources: {
config {
name: "android.game_interventions" # 配置数据源以收集游戏干预信息
}
}
data_sources: {
config {
name: "android.network_packets" # 配置数据源以收集网络数据包
network_packet_trace_config {
poll_ms: 250 # 每 250 毫秒轮询一次网络数据包
}
}
}
data_sources: {
config {
name: "linux.sys_stats" # 配置数据源以收集 Linux 系统统计信息
sys_stats_config {
meminfo_period_ms: 1000 # 每 1000 毫秒收集一次内存信息
meminfo_counters: MEMINFO_ACTIVE_FILE # 收集活动文件计数
meminfo_counters: MEMINFO_ANON_PAGES # 收集匿名页面计数
meminfo_counters: MEMINFO_BUFFERS # 收集缓冲区计数
meminfo_counters: MEMINFO_COMMITED_AS # 收集提交地址空间计数
meminfo_counters: MEMINFO_DIRTY # 收集脏页面计数
vmstat_period_ms: 1000 # 每 1000 毫秒收集一次虚拟内存统计
stat_period_ms: 1000 # 每 1000 毫秒收集一次统计信息
stat_counters: STAT_CPU_TIMES # 收集 CPU 时间统计
stat_counters: STAT_FORK_COUNT # 收集进程生成统计
cpufreq_period_ms: 1000 # 每 1000 毫秒收集一次 CPU 频率
}
}
}
data_sources: {
config {
name: "android.heapprofd" # 配置数据源以收集 Android 堆分析数据
target_buffer: 0 # 指定该数据源使用的缓冲区
heapprofd_config {
sampling_interval_bytes: 4096 # 每 4096 字节采样一次
shmem_size_bytes: 8388608 # 共享内存大小为 8388608 字节
block_client: true # 阻止客户端操作
}
}
}
data_sources: {
config {
name: "android.java_hprof" # 配置数据源以收集 Java HPROF 数据
target_buffer: 0 # 指定该数据源使用的缓冲区
java_hprof_config {
}
}
}
data_sources: {
config {
name: "linux.ftrace" # 配置数据源以收集 Linux ftrace 数据
ftrace_config {
ftrace_events: "clk/*" # 收集所有时钟相关事件
ftrace_events: "ext4/*" # 收集所有 ext4 文件系统事件
ftrace_events: "f2fs/*" # 收集所有 f2fs 文件系统事件
ftrace_events: "fastrpc/*" # 收集所有 fastrpc 事件
ftrace_events: "power/suspend_resume" # 收集电源暂停和恢复事件
ftrace_events: "sched/sched_wakeup" # 收集调度唤醒事件
ftrace_events: "sched/sched_wakeup_new" # 收集新调度唤醒事件
ftrace_events: "sched/sched_waking" # 收集调度唤醒事件
ftrace_events: "oom/oom_score_adj_update" # 收集 OOM 分数调整更新事件
ftrace_events: "sched/sched_blocked_reason" # 收集调度阻塞原因事件
ftrace_events: "ftrace/print" # 收集 ftrace 打印事件
atrace_categories: "aidl" # 收集 AIDL 类别数据
atrace_categories: "dalvik" # 收集 Dalvik 类别数据
atrace_categories: "audio" # 收集音频类别数据
atrace_categories: "binder_lock" # 收集 Binder 锁类别数据
atrace_categories: "binder_driver" # 收集 Binder 驱动类别数据
atrace_categories: "bionic" # 收集 Bionic 类别数据
atrace_categories: "ss" # 收集 SS 类别数据
atrace_categories: "vibrator" # 收集振动器类别数据
atrace_categories: "video" # 收集视频类别数据
atrace_categories: "view" # 收集视图类别数据
atrace_categories: "webview" # 收集 WebView 类别数据
atrace_apps: "*" # 收集所有应用的数据
buffer_size_kb: 1024 # 设置缓冲区大小为 1024 KB
drain_period_ms: 250 # 每 250 毫秒清空缓冲区
symbolize_ksyms: true # 启用符号化内核符号
}
}
}
duration_ms: 10000 # 设置跟踪持续时间为 10000 毫秒
Trace Viewer
Perfetto UI 可以将抓取的 Perfetto Trace 文件通过浏览器的方式查看。它支持多种不同的跟踪格式,包括 Perfetto proto 跟踪记录格式和旧版 Json 跟踪记录格式。

Note
为了方便学习 Perfetto,在 Perfetto 导航栏的左边提供了两种类型的 Trace 案例供初学者学习。
点击左边的 Example Traces
选择 Open Android example
就可以打开一个 Android 的 Perfetto Trace 示例。
导航栏
Perfetto UI 左边显示的是导航栏,功能的作用如下表所示:
标签名 | 含义 | 作用 |
---|---|---|
Open trace file | 打开追踪文件 | 打开一个现有的 trace 文件 |
Open with legacy UI | 使用旧版界面 | 使用旧版用户界面打开当前 trace 文件 |
Record new trace | 记录新追踪 | 开始记录一个新的 trace 文件 |
Show timeline | 显示时间线 | 显示或隐藏时间线视图 |
Download | 下载文件 | 下载当前 trace 文件 |
Query(SQL) | 执行 SQL 查询 | 执行 SQL 查询并查看结果 |
Viz | 数据可视化 | 显示可视化图表和数据 |
Metrics | 性能指标 | 查看性能指标和测量数据 |
Info and stats | 信息和统计 | 查看有关当前 trace 的信息和统计数据 |
Switch to legacy UI | 切换到旧版界面 | 切换到旧版用户界面 |
Convert to .json | 转换为 .json 格式 | 将 trace 文件转换为 .json 格式 |
Convert to .systrace | 转换为 .systrace 格式 | 将 trace 文件转换为 .systrace 格式 |
Open Android example | 打开 Android 示例 | 打开 Android 示例 trace 文件 |
Open Chrome example | 打开 Chrome 示例 | 打开 Chrome 示例 trace 文件 |
Keyboard shortcuts | 键盘快捷键 | 查看和使用键盘快捷键 |
Documentation | 文档 | 查看 Perfetto 的文档 |
Flags | 标志设置 | 查看和修改 Perfetto 的标志设置 |
Report a bug | 报告 bug | 报告 Perfetto 中发现的 bug |
Record metatrace | 记录 metatrace | 记录 metatrace 数据 |
快捷键
在左边的导航栏 Support
选项卡中可以点击 Keyboard shortcuts
或者 Shift + ?
查看 Perfetto 支持的快捷键。

以下是使用频率最高的快捷键:
f
键让内容更聚焦
当鼠标选中任意一个 trace 数据区域时,按 f
键可以快速居中、放大选中的区域。

m
键让内容显示时间
当鼠标选中任意一个 trace 数据区域时,按 m
键可以显示数据区域的时间信息,按 Esc
键退出选择。若已经存在一个区域被 m
键选中,当再次选中另外一个区域时,上一区域的信息会消失。若希望持续保留上个区域的时间信息,可以使用 shift + m
组合键。


q
键弹出底部栏
当选中数据区域时,使用 q
键可以快速打开底部栏查看该数据区域的详细信息。

插旗做标记
将鼠标移动到界面插旗区域后,可以对任意区域进行插旗做标记,方便观察某个时间点的信息。选中任意一面旗帜,然后在底部栏中可以更改旗帜的颜色或者将其移除。


线程运行状态
在 Perfetto 中,通常会有两个轨道标记一个线程的运行情况。例如,渲染线程 RenderThread 的运行轨迹,区域 1 是该线程在 CPU 的运行情况,也就是线程的运行状态。区域 2 是该线程执行的具体方法。区域 3 是该线程运行在哪个 CPU 核上。

线程的运行状态有如下几种情况:
状态 | 颜色 | 含义 |
---|---|---|
Sleep | 线程处于休眠状态(通常是由于调用了 sleep() 或类似的系统调用)。线程不执行任何操作,CPU不会为其分配时间。 |
|
Runnable | 线程处于可运行状态。这意味着线程在调度程序的调度队列中,系统会在未来的某个时刻给予它CPU时间。尽管线程处于可运行状态,但可能因为调度延迟或优先级问题,实际未能立即运行。 | |
Running | 线程正在运行中,实际占用CPU时间并执行代码。这个状态表明线程目前是活动的,正在执行任务。 | |
Uninterruptible Sleep | 线程处于不可中断的睡眠状态。这通常发生在等待I/O操作(如磁盘读取、网络数据接收等)或其他系统资源的情况下。在这种状态下,线程无法被信号或其他事件中断,直到资源变得可用。 |
代码插桩
在应用开发、Framework 开发,甚至是 Kernel 开发过程中,通过在代码插桩添加 trace 点,使其可以被 Perfetto 记录,从而在 Trace Viewer 中显示出来。
应用开发添加 trace
Perfetto 中的 Trace 类用于捕获和记录 Java 应用程序的性能数据。该类提供方法来开始和停止跟踪特定事件,以便分析应用程序的性能瓶颈和资源使用情况。 通过调用这些方法,开发者可以获取线程活动、方法调用和其他相关指标,帮助深入了解应用程序的运行时行为。 使用 Perfetto 进行性能分析,能够有效地优化 Java 应用的性能。
方法跟踪
以 onCreate
方法中添加 Trace 跟踪为例:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Trace.beginSection("perfetto_demo_begin");
// ...
Trace.endSection();
}
在 Java 代码中,通过调用 beginSection()
和 endSection()
方法开始和停止 Trace。在应用启动前,通过如下的命令抓取 trace
信息:
adb shell perfetto -o /data/misc/perfetto-traces/perfetto_demo-1.perfetto-trace -t 10s sched freq idle \
am wm gfx view binder_driver hal dalvik camera input res memory -a com.example.perfettodemo2
在 Perfetto UI 中,可以看到在代码中添加的 Trace 信息:

Warning
使用 beginSection()
和 endSection()
时需要注意以下三点:
beginSection()
和endSection()
遵循就近匹配原则,在使用时应当注意它们的顺序位置;beginSection()
和endSection()
必须在同一个线程中;- 如果
beginSection
被try catch
语句包含,则endSection
必须放到finally
语句块中。
Danger
默认情况下,Perfetto 只会记录和收集系统层面(比如:WMS、AMS、WindowManager、ActivityManager等)的信息。在使用 adb shell perfetto
命令时,需要添加 -a
参数,传递应用的包名信息。只有这样,Perfetto 才会记录该应用相关的信息。
Danger
设备需要开启 Trace 系统属性,否则可能无法抓取(只有在 Android 9 或者 Android 10 的设备上才需要手动开启):
adb shell getprop persist.traced.enable
Counter
Trace 提供了添加计数器的 setCounter()
方法,用于采集一些计数类信息。
public void trackEvent(int eventCount) {
Trace.setCounter(COUNTER_NAME, eventCount);
}
public void someMethod() {
for (int i = 0; i < 10; i++) {
trackEvent(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
someMethod();
}

android.os.Trace
android.os.Trace 类方法 | 描述 |
---|---|
beginSection(String sectionName) | 在一个函数中打印 Trace 开始标记,表示某个操作的开始,显示在特定线程中。可用于分析方法执行时间。 |
endSection() | 在一个函数中打印 Trace 结束标记,表示某个操作的结束,显示在特定线程中。可与 beginSection 配对使用。 |
beginAsyncSection(String methodName, int cookie) | 打印异步 Trace 开始标记,cookie 用来区分相同 methodName 的不同异步 Trace,独立成一行显示。适用于异步任务的性能分析。 |
endAsyncSection(String methodName, int cookie) | 打印异步 Trace 结束标记,cookie 用于区分相同 methodName 的不同异步 Trace,独立成一行显示。 |
setCounter(String counterName, long counterValue) | 以给定计数器的值打印 Trace,便于监控特定事件的频率或状态。可以用于性能调优和资源监测。 |
isEnabled() | 判断 Trace 是否开启,避免在 Trace 关闭时创建无用的临时对象。建议在调用 Trace 方法前进行检查以提高性能。 |
asyncTraceBegin(String name) | 启动一个异步 Trace,适用于较长时间的操作,提供清晰的开始标记。 |
asyncTraceEnd(String name) | 结束一个异步 Trace,记录操作完成的时间点。 |
Framework 添加 trace
通过查询 AOSP 源码,可以看到在 Framework 层,AOSP 使用如下的方式添加标记:
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performCreate:"
+ mComponent.getClassName());
}
// ...
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}

在系统 Framework 层面,Trace 类新提供了一些方法给 Framework 层调用。这些方法会被标记为 SystemApi
。
@UnsupportedAppUsage
@SystemApi(client = MODULE_LIBRARIES)
public static boolean isTagEnabled(long traceTag) {
return nativeIsTagEnabled(traceTag);
}
在 android.os.Trace
的基础上,新增了如下的方法:
android.os.Trace 类方法 | 描述 |
---|---|
traceBegin(long traceTag, String methodName) |
开始一个新的追踪段,记录方法名称。 |
traceEnd(long traceTag) |
结束最近的追踪段。 |
asyncTraceBegin(long traceTag, String methodName, int cookie) |
开始一个异步追踪段,记录方法名称和 cookie。 |
asyncTraceEnd(long traceTag, String methodName, int cookie) |
结束异步追踪段。 |
traceCounter(long traceTag, String counterName, int counterValue) |
设置一个计数器的值,用于监控特定事件的频率。 |
isTagEnabled(long traceTag) |
检查特定追踪标签是否启用。 |
Warning
Trace 类中 traceBegin 和 beginSection 都可以开启 trace 跟踪,两者的区别主要体现在:
traceBegin
方法接收两个参数,而beginSection
方法只接收一个参数;traceBegin
方法添加了@UnsupportedAppUsage
和@SystemApi(client = MODULE_LIBRARIES)
注解,表明该方法只能在系统层面进行调用。 而beginSection
没有任何的注解,表明该方法不仅可以在系统层面也可以在应用层进行调用;
不管是 traceBegin
还是 beginSection
,亦或是 traceEnd
和 endSection
,其逻辑都会调用 Native 方法 nativeTraceBegin
和 nativeTraceEnd
。
Danger
在抓取的 Perfetto trace 中没有看到想要的 trace 信息,可以添加 category 指定需要抓取的 trace 包含的信息:
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 10s sched freq idle \
am wm gfx view binder_driver hal dalvik camera input res memory
这些 category 被声明可以在 Trace.java 和 atrace.cpp 中:
public static final long TRACE_TAG_NEVER = 0;
public static final long TRACE_TAG_ALWAYS = 1L << 0;
...
public static final long TRACE_TAG_AUDIO = 1L << 8;
public static final long TRACE_TAG_VIDEO = 1L << 9;
public static final long TRACE_TAG_CAMERA = 1L << 10;
public static final long TRACE_TAG_HAL = 1L << 11;
...
Kernel 开发添加 trace
ftrace
是 Linux 内核中的一个功能强大的跟踪工具,用于监控和分析内核的运行时行为。它允许开发者和系统管理员跟踪函数调用、上下文切换、调度事件等,
以帮助识别和解决性能问题。通过 ftrace
,用户可以启用不同的跟踪选项,获取有关内核各个部分的详细信息。这些信息通常以文本形式输出,可以通过
/sys/kernel/debug/tracing
文件系统进行访问和配置。ftrace
的灵活性使其适用于多种场景,如性能调优、安全审计和故障排查。
Note
ftrace
和 atrace
是 Android 中用于性能分析和调试的两种工具,但它们适用的层次和用途不同。
ftrace
是 Linux 内核提供的功能,主要用于内核空间的调试和性能分析,能够跟踪各种内核事件,
如系统调用和上下文切换,输出详细的内核级别信息。相对而言,atrace
是针对用户空间的工具,
专注于应用程序的性能监测,支持生成可视化的执行过程和与 Systrace 和 Perfetto 的集成。总的来说,
ftrace
更适合内核层面的分析,而 atrace
则更关注应用程序的性能问题,选择使用哪一个工具取决于具体的分析需求。
在 Kernel 层添加 Trace,可以使用下面的两个库文件:
libcutils.so
:/system/core/libcutils/include/cutils/trace.h
libutils.so
:/system/core/libutils/include/utils/Trace.h
libcutils.so
提供如下的方法:
函数名 | 描述 |
---|---|
ATRACE_BEGIN(name) |
在一个函数中打印 Trace 开始标记,会显示在某个线程中。 |
ATRACE_END() |
在一个函数中打印 Trace 结束标记,会显示在某个线程中。 |
ATRACE_ASYNC_BEGIN(name, cookie) |
打印异步 Trace 开始标记,cookie 用来区分相同名称但不同的异步 Trace,独立成一行显示。 |
ATRACE_ASYNC_END(name, cookie) |
打印异步 Trace 结束标记,cookie 用来区分相同名称但不同的异步 Trace,独立成一行显示。 |
ATRACE_INT(name, value) |
以给定计数器的值打印 Trace。 |
ATRACE_INT64(name, value) |
以给定计数器的值打印 Trace。 |
ATRACE_ENABLED() |
判断是否开启了 Trace。 |
libutils.so
提供如下的方法:
函数名 | 描述 |
---|---|
ATRACE_NAME(name) |
ATRACE_BEGIN(name) 和 ATRACE_END() 的简写形式。 |
ATRACE_CALL() |
ATRACE_BEGIN() 和 ATRACE_END() 的简写形式,name 固定为当前方法名。 |
Trace Processor Shell
trace_processor_shell
是 Perfetto 项目中的一个重要工具,它是一个交互式的 SQL shell,用于分析和查询性能跟踪数据。
通过 trace_processor_shell
可以加载和分析 Perfetto 跟踪文件,尤其是对一些“大”的 trace 文件可以离线打开。
同时,还提供了强大的 SQL 查询能力,允许用户使用 SQL 语句查询跟踪数据。
下载工具
访问 Github 可以下载各个版本的 Trace Processor Shell 工具。 根据系统选择一个可运行的可执行文件:
运行工具
Note
本章节的运行环境系统是 Windows 11。
下载成功后,执行 --help
参数显示工具的帮助信息:
$ trace_processor_shell.exe --help
Usage: trace_processor_shell.exe [FLAGS] trace_file.pb
类别 | 选项 | 描述说明 |
---|---|---|
基础选项 | -h, --help |
打印帮助指南 |
-v, --version |
打印跟踪处理器版本 | |
-d, --debug |
启用虚拟表调试 | |
-W, --wide |
以双倍列宽打印交互式输出 | |
性能分析 | -p, --perf-file 文件 |
将跟踪加载和查询执行耗时写入指定文件(仅配合-q或--run-metrics使用) |
查询相关 | -q, --query-file 文件 |
从文件读取并执行SQL查询 |
-i, --interactive |
即使指定了查询文件也启动交互模式 | |
HTTP服务 | -D, --httpd |
启用HTTP RPC服务器 |
--http-port 端口号 |
指定HTTP RPC服务器端口 | |
数据导出 | -e, --export 文件 |
将跟踪处理器内容导出到SQLite数据库 |
功能开关 | --full-sort |
强制全排序(忽略窗口逻辑) |
--no-ftrace-raw |
阻止将类型化ftrace事件摄入原始表 | |
--analyze-trace-proto-content |
启用跟踪协议内容分析 | |
--crop-track-events |
忽略兴趣范围之外的轨道事件 | |
--dev |
启用仅限开发使用的功能 | |
--dev-flag 键=值 |
设置开发标志值(需配合--dev使用) | |
标准库 | --add-sql-module 模块路径 |
将目录文件视为新SQL模块 |
--override-sql-module 模块路径 |
用指定内容覆盖跟踪处理器模块 | |
--override-stdlib=[标准库路径] |
覆盖trace_processor/stdlib(需配合--dev使用) | |
指标分析 | --run-metrics x,y,z |
运行逗号分隔的指标列表 |
--pre-metrics 文件 |
在执行指标前执行SQL查询(无输出) | |
--metrics-output=[binary\|text\|json] |
指定指标输出格式 | |
--metric-extension 磁盘路径@虚拟路径 |
从磁盘路径加载指标proto/sql文件 | |
元跟踪 | -m, --metatrace 文件 |
启用元跟踪并将结果写入文件 |
--metatrace-buffer-capacity 数量 |
设置元跟踪事件缓冲区容量 | |
--metatrace-categories 类别列表 |
逗号分隔的元跟踪类别列表 |
创建本地服务
trace_processor_shell
可以在本地开启一个 HTTP 服务,用于离线打开一些过大的 Perfetto trace 文件。通过 --httpd
参数后,默认会在本地端口 9001 启动 HTTP 服务:
trace_processor_shell.exe --httpd
启动 HTTP 服务后,浏览器访问 http://localhost:9001/
会提示跳转到 https://ui.perfetto.dev/
:
Perfetto Trace Processor RPC Server
This service can be used in two ways:
1. Open or reload https://ui.perfetto.dev/
It will automatically try to connect and use the server on localhost:9001 when
available. Click YES when prompted to use Trace Processor Native Acceleration
in the UI dialog.
See https://perfetto.dev/docs/visualization/large-traces for more.
...
界面会提示 Incompatible RPC version
,选择 Use Bultin Wasm
。
通过 HTTP 服务功能的优势体现在:
- 原生加速引擎。绕过 WASM 限制直接调用底层 C++ 解析引擎(非浏览器 WASM 模拟),实现10倍以上解析速度提升(实测 1GB trace 文件解析时间从 30s 降至 3s 内)。
- 混合架构优化。Rust/C++ 混合实现的解析器,通过内存布局优化,紧凑型数据结构降低 40% 内存占用,线程分段解析 trace 文件(支持 mmap 流式读取),直接操作原始 trace 数据块,避免序列化开销。
- 交互式分析能力。深度集成 Perfetto UI,与官网提供的 Perfetto UI 功能上和使用方法一样。
- 安全离线调试。数据全程驻留本地内存(memfd 匿名内存文件),同时支持 TLS 1.3 加密(通过
--http-port 443
绑定)。 - 流式解析技术允许超大文件解析,50GB+ trace 文件处理。
下表是使用 trace_processor_shell --httpd
和纯网页版的功能对比:
对比维度 | 命令行本地服务 (trace_processor_shell --httpd ) |
纯网页版 (ui.perfetto.dev) |
---|---|---|
启动方式 | 需本地执行命令启动服务 | 直接浏览器访问 |
核心架构 | C++/Rust 原生进程,多线程优化 | WebAssembly 单线程模拟 |
解析性能 | ▶ 1GB trace 解析约 2-5s ▶ 支持 50GB+ 文件流式处理 |
▶ 1GB trace 解析约 20-30s ▶ 超过2GB易崩溃 |
内存效率 | 内存占用降低 60%(紧凑数据结构+mmap) | 全文件加载,内存占用高 |
功能支持 | ✅ 完整 SQL 查询 ✅ 自定义指标计算 ✅ 跨会话状态保持 |
⚠️ 基础 SQL ❌ 无法保存临时表 |
高级特性 | 🔧 Metatracing 调试 🔧 多标签页协同分析 |
🔗 文件分享链接 📥 修改后下载 |
网络要求 | 纯本地操作,支持内网隔离环境 | 需初始加载 15MB WASM 资源 |
安全控制 | 可绑定 TLS/SSL 支持审计日志 |
依赖浏览器沙箱 |
典型场景 | 🔧 内核/车载长时日志分析 🔬 芯片厂商深度调试 |
🚀 快速查看小文件 🤝 团队协作分享 |
扩展性 | 支持插件式指标扩展(--metric-extension) | 固定功能集 |
调试支持 | 原生崩溃日志、gdb 调试 | 仅限浏览器 DevTools |
在性能方面,根据网上公开的资料显示性能实测数据(i7-1280P/32GB 环境):
测试项 | 本地服务 | WASM 网页版 |
---|---|---|
500MB trace 加载 | 1.2s / 800MB 内存 | 12s / 2.1GB 内存 |
10GB trace 流式处理 | ✅ 峰值 3.2GB 内存 | ❌ 崩溃 |
10万行 SQL 聚合查询 | 0.8s | 6.5s |
Note
Q:命令行本地服务和纯网页版的选型建议?
-
开发调试/企业环境 ➔ 本地服务(支持 Docker 部署)
-
协作分享/快速检查 ➔ 网页版
-
混合方案:用本地服务分析后,导出 SQLite 再上传网页版分享
Trace to SQLite
trace_processor_shell
可以将 Perfetto trace 转换成 SQLite 数据库文件,然后使用 SQLite 数据库工具打开。转换的命令如下:
trace_processor_shell.exe -e trace.db trace_file.perfetto-trace
打开 SQLite 文件的工具有很多,推荐使用 SQLiteStudio。下载 SQLiteStudio 后,打开转换后的
trace.db
文件可以看到 Perfetto trace 的数据表和视图结构。
在 SQLiteStudio 的 Tools 菜单,找到 Open SQL editor。在打开的新窗口中输入 SQL 语句就可以查询 Perfetto trace 中的数据了:
SELECT * FROM cpu;
Note
数据表结构和关系可以在 Perfetto 官网 查询。
Python for Perfetto
在某些情况下,需要使用 Python 自动化的读取和解析 Perfetto trace 文件,核心的步骤如下:
Step 1:安装 Python 解析库
pip install perfetto
# 或安装指定的版本
pip install perfetto==0.11.0
Warning
Perfetto API 只兼容了 Python 3+ 。
Step 2:编写代码
from perfetto.trace_processor import TraceProcessor
# 加载trace文件
tp = TraceProcessor(trace='trace.perfetto-trace')
# 查询trace中的表
tables = tp.tables()
print("Available tables:", tables)
# 查询特定表的数据
processes = tp.query('SELECT * FROM process')
print("Processes:", processes)
# 查询特定进程的线程
threads = tp.query('SELECT * FROM thread WHERE upid = 123')
print("Threads:", threads)
tp.close()
Danger
由于受到国内网络的影响,在解析 trace 的时候可能会出现 Trace processor failed to start.
的错误。解决办法是手动下载
trace_processor_shell 文件:
- Mac 或 Linux:
https://get.perfetto.dev/trace_processor
- Windows:
https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v37.0/windows-amd64/trace_processor_shell.exe
下载完成后,在构造 TraceProcessor
对象时,作为 bin_path
的参数:
from perfetto.trace_processor import TraceProcessor
# 以 Windows 环境为例
tp = TraceProcessor(trace=r'trace_file.perfetto-trace', config=TraceProcessorConfig(
bin_path=r'trace_processor_shell.exe'
))