18 KiB
Perfetto看Trace技巧
一、Perfetto基础操作
1. 录制Trace
1.1 命令行录制
# 基础录制命令
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace
# 指定时长(10秒)
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 10s
# 指定应用包名
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -a com.example.app
# 拉取Trace文件
adb pull /data/misc/perfetto-traces/trace trace.pb
1.2 使用配置文件录制
启动分析配置文件(startup_config.pb):
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_waking"
ftrace_events: "sched/sched_process_exit"
ftrace_events: "sched/sched_process_free"
ftrace_events: "task/task_newtask"
ftrace_events: "task/task_rename"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "power/suspend_resume"
atrace_categories: "am"
atrace_categories: "wm"
atrace_categories: "gfx"
atrace_categories: "view"
atrace_categories: "binder_driver"
atrace_categories: "binder_lock"
atrace_categories: "input"
atrace_categories: "res"
atrace_categories: "dalvik"
buffer_size_kb: 4096
drain_period_ms: 250
}
}
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
duration_ms: 10000
游戏丢帧分析配置文件(jank_config.pb):
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_waking"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "gfx/mali_gpu_total"
ftrace_events: "gpu_mem_total/gpu_mem_total"
atrace_categories: "gfx"
atrace_categories: "view"
atrace_categories: "sched"
atrace_categories: "freq"
atrace_categories: "idle"
buffer_size_kb: 8192
drain_period_ms: 250
}
}
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
duration_ms: 30000
1.3 使用配置文件录制
# 将配置文件推送到设备
adb push startup_config.pb /data/local/tmp/
# 使用配置文件录制
adb shell perfetto -c /data/local/tmp/startup_config.pb -o /data/local/tmp/trace.pb
# 拉取Trace文件
adb pull /data/local/tmp/trace.pb trace.pb
2. 打开Trace文件
2.1 Web UI打开
- 访问 https://ui.perfetto.dev/
- 点击 "Open trace file"
- 选择 trace.pb 文件
- 等待加载完成
2.2 Android Studio打开
- 打开 Android Studio
- 连接设备
- 打开 Profiler
- 选择 CPU Profiler
- 点击 "Record" 开始录制
- 执行操作后停止录制
二、应用冷启动分析技巧
1. 启动流程关键节点
1.1 启动阶段划分
阶段1:进程创建(Process Creation)
- 查找点:
ActivityManager: startProcess - 关键指标:进程fork时间、进程初始化时间
- 查看位置:SystemServer进程的ActivityManagerService
阶段2:Application初始化(Application Init)
- 查找点:
ActivityThread: handleBindApplication - 关键指标:Application.onCreate()耗时
- 查看位置:目标应用主线程
阶段3:Activity创建(Activity Creation)
- 查找点:
ActivityThread: performLaunchActivity - 关键指标:Activity.onCreate()耗时
- 查看位置:目标应用主线程
阶段4:首帧渲染(First Frame)
- 查找点:
Choreographer#doFrame或SurfaceFlinger: onMessageReceived - 关键指标:首帧输出时间
- 查看位置:应用主线程、RenderThread、SurfaceFlinger
1.2 在Perfetto中定位启动节点
步骤1:找到应用进程
1. 在左侧进程列表中找到目标应用进程
2. 展开进程,找到主线程(通常是包名)
3. 点击主线程,查看时间线
步骤2:查找启动关键事件
1. 在搜索框输入 "ActivityManager: startProcess"
2. 找到进程启动事件
3. 标记为起点(按M键)
步骤3:查找Application初始化
1. 在主线程时间线中查找 "handleBindApplication"
2. 展开查看 Application.onCreate()
3. 分析初始化耗时
步骤4:查找Activity创建
1. 在主线程时间线中查找 "performLaunchActivity"
2. 展开查看 Activity.onCreate()
3. 分析创建耗时
步骤5:查找首帧
1. 在SurfaceFlinger进程中查找 "onMessageReceived"
2. 或查找应用的 "Choreographer#doFrame"
3. 标记为终点(按M键)
4. 计算总耗时
2. 启动性能瓶颈分析
2.1 主线程阻塞分析
查找主线程Sleep状态:
1. 选中应用主线程
2. 查看线程状态(Running/Sleep/Runnable)
3. 查找长时间Sleep的片段
4. 分析Sleep原因(等待Binder、等待锁、等待IO等)
常见阻塞场景:
-
等待Binder调用
- 特征:主线程处于Sleep状态,等待Binder返回
- 查找:查看wakeup信息,找到对应的Binder调用
- 分析:检查SystemServer中的Binder处理耗时
-
等待锁
- 特征:主线程处于Sleep状态,等待锁释放
- 查找:查看wakeup信息,找到锁持有者
- 分析:检查锁持有时间是否过长
-
等待IO
- 特征:主线程处于UninterruptibleSleep-IO状态
- 查找:查看IO操作(文件读写、数据库操作)
- 分析:检查IO操作是否在主线程执行
2.2 CPU调度分析
查看CPU频率:
1. 在顶部选择 "CPU frequency"
2. 查看启动期间CPU频率变化
3. 检查是否降频或频率不足
查看CPU使用率:
1. 在顶部选择 "CPU usage"
2. 查看启动期间CPU使用情况
3. 检查是否有CPU瓶颈
查看任务调度:
1. 在CPU核心时间线中查看任务调度
2. 检查关键任务是否跑在小核
3. 检查是否有调度延迟
2.3 内存压力分析
查看GC活动:
1. 在主线程中查找 "HeapTaskDaemon"
2. 查看GC频率和耗时
3. 检查是否频繁GC影响启动
查看内存分配:
1. 在进程信息中查看内存使用
2. 检查内存分配速度
3. 识别大对象分配
2.4 系统服务分析
SystemServer分析:
1. 找到SystemServer进程
2. 查看ActivityManagerService处理耗时
3. 查看WindowManagerService处理耗时
4. 检查是否有锁竞争或处理延迟
SurfaceFlinger分析:
1. 找到SurfaceFlinger进程
2. 查看Buffer分配和合成耗时
3. 检查是否有合成延迟
3. 启动优化验证
3.1 对比分析
优化前后对比:
1. 录制优化前的Trace
2. 录制优化后的Trace
3. 在Perfetto中同时打开两个Trace
4. 对比关键节点耗时
5. 验证优化效果
关键指标对比:
- 进程创建时间
- Application初始化时间
- Activity创建时间
- 首帧输出时间
- 总启动时间
3.2 SQL查询分析
查询启动总耗时:
-- 查询进程启动到首帧的时间
SELECT
(SELECT ts FROM slice WHERE name = 'Choreographer#doFrame' LIMIT 1) -
(SELECT ts FROM slice WHERE name LIKE '%startProcess%' LIMIT 1)
AS startup_time_ns;
查询Application初始化耗时:
-- 查询Application.onCreate耗时
SELECT
name,
dur / 1000000.0 AS duration_ms
FROM slice
WHERE name LIKE '%Application.onCreate%'
ORDER BY dur DESC
LIMIT 10;
查询主线程阻塞时间:
-- 查询主线程Sleep时间
SELECT
SUM(dur) / 1000000.0 AS total_sleep_ms
FROM sched
WHERE utid = (SELECT utid FROM thread WHERE name LIKE '%main%' LIMIT 1)
AND state = 'S';
三、游戏丢帧问题分析技巧
1. 帧率分析
1.1 查看帧率信息
方法1:查看Frame信息
1. 在顶部选择 "Frame Timeline"
2. 查看每一帧的状态(绿色/黄色/红色)
3. 绿色:正常帧(< 16.67ms)
4. 黄色:轻微掉帧(16.67-33.33ms)
5. 红色:严重掉帧(> 33.33ms)
方法2:查看VSync信息
1. 在SurfaceFlinger进程中查找 "VSync"
2. 查看VSync周期是否正常(60Hz = 16.67ms)
3. 检查是否有VSync丢失
方法3:查看Choreographer
1. 在应用主线程中查找 "Choreographer#doFrame"
2. 查看每一帧的处理时间
3. 识别耗时较长的帧
1.2 丢帧统计
使用SQL查询丢帧:
-- 查询所有掉帧(> 16.67ms)
SELECT
COUNT(*) AS jank_count,
AVG(dur) / 1000000.0 AS avg_duration_ms,
MAX(dur) / 1000000.0 AS max_duration_ms
FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16666667;
查询连续丢帧:
-- 查询连续丢帧的情况
SELECT
ts,
dur / 1000000.0 AS duration_ms
FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16666667
ORDER BY ts;
2. 渲染性能分析
2.1 RenderThread分析
查看RenderThread状态:
1. 找到应用的RenderThread线程
2. 查看线程状态和执行情况
3. 检查是否有阻塞或延迟
分析渲染耗时:
1. 在RenderThread中查找关键操作:
- drawFrame:绘制帧
- dequeueBuffer:获取Buffer
- queueBuffer:提交Buffer
2. 分析每个操作的耗时
3. 识别渲染瓶颈
常见渲染问题:
-
dequeueBuffer耗时
- 特征:RenderThread等待Buffer分配
- 原因:SurfaceFlinger繁忙或Buffer不足
- 分析:查看SurfaceFlinger进程状态
-
queueBuffer耗时
- 特征:RenderThread等待Buffer提交
- 原因:SurfaceFlinger处理延迟
- 分析:查看SurfaceFlinger合成耗时
-
drawFrame耗时
- 特征:RenderThread绘制耗时过长
- 原因:绘制内容复杂、GPU性能不足
- 分析:查看GPU使用情况和绘制命令
2.2 GPU性能分析
查看GPU使用率:
1. 在顶部选择 "GPU frequency" 或 "GPU usage"
2. 查看GPU频率和使用率
3. 检查是否有GPU瓶颈
查看GPU渲染时间:
1. 在RenderThread中查找GPU相关操作
2. 查看GPU渲染耗时
3. 检查是否有GPU过载
分析GPU瓶颈:
1. 查看GPU频率是否达到最大值
2. 查看GPU使用率是否过高
3. 检查是否有GPU调度问题
2.3 SurfaceFlinger分析
查看合成耗时:
1. 找到SurfaceFlinger进程
2. 查看合成操作耗时
3. 检查是否有合成延迟
查看Buffer管理:
1. 查看Buffer分配和释放
2. 检查Buffer队列状态
3. 识别Buffer泄漏或不足
3. 主线程性能分析
3.1 主线程耗时分析
查找耗时操作:
1. 选中应用主线程
2. 按耗时排序(点击Duration列)
3. 查看耗时最长的操作
4. 分析是否可以优化
使用SQL查询耗时操作:
-- 查询主线程耗时最长的操作
SELECT
name,
dur / 1000000.0 AS duration_ms,
ts
FROM slice
WHERE utid = (SELECT utid FROM thread WHERE name LIKE '%main%' LIMIT 1)
ORDER BY dur DESC
LIMIT 20;
3.2 主线程阻塞分析
查找阻塞点:
1. 查看主线程状态变化
2. 查找长时间Sleep的片段
3. 分析阻塞原因
常见阻塞场景:
-
等待网络请求
- 特征:主线程等待网络响应
- 解决:使用异步网络请求
-
等待数据库操作
- 特征:主线程等待数据库读写
- 解决:使用异步数据库操作
-
等待文件IO
- 特征:主线程等待文件读写
- 解决:使用异步IO操作
-
等待锁
- 特征:主线程等待锁释放
- 解决:优化锁的使用
4. CPU性能分析
4.1 CPU频率分析
查看CPU频率:
1. 在顶部选择 "CPU frequency"
2. 查看游戏运行期间CPU频率
3. 检查是否降频或频率不足
分析频率问题:
1. 检查CPU频率是否达到最大值
2. 查看是否有温控降频
3. 检查频率调度是否合理
4.2 CPU使用率分析
查看CPU使用率:
1. 在顶部选择 "CPU usage"
2. 查看各核心使用情况
3. 检查是否有CPU瓶颈
分析CPU负载:
1. 检查CPU使用率是否过高
2. 查看是否有核心过载
3. 检查负载是否均衡
4.3 CPU调度分析
查看任务调度:
1. 在CPU核心时间线中查看任务调度
2. 检查关键任务是否跑在小核
3. 查看调度延迟
分析调度问题:
1. 检查关键线程是否绑定到高性能核心
2. 查看线程优先级是否合理
3. 检查是否有调度延迟
5. 内存性能分析
5.1 GC压力分析
查看GC活动:
1. 在主线程中查找 "HeapTaskDaemon"
2. 查看GC频率和耗时
3. 检查是否频繁GC影响帧率
分析GC影响:
1. 检查GC是否在关键帧期间发生
2. 查看GC耗时是否过长
3. 分析GC对帧率的影响
5.2 内存分配分析
查看内存分配:
1. 在进程信息中查看内存使用
2. 检查内存分配速度
3. 识别大对象分配
分析内存问题:
1. 检查是否有内存泄漏
2. 查看内存碎片化情况
3. 分析内存分配模式
四、Perfetto高级技巧
1. 自定义Trace标记
1.1 在代码中添加Trace
Java/Kotlin代码:
import android.os.Trace;
// 开始标记
Trace.beginSection("my_custom_section");
// 执行代码
doSomething();
// 结束标记
Trace.endSection();
C++代码:
#include <utils/Trace.h>
// 开始标记
ATRACE_BEGIN("my_custom_section");
// 执行代码
doSomething();
// 结束标记
ATRACE_END();
1.2 在Perfetto中查看自定义标记
1. 在搜索框输入自定义标记名称
2. 找到对应的Trace事件
3. 分析该代码段的性能
2. SQL查询技巧
2.1 常用查询
查询帧率统计:
-- 计算平均帧率
SELECT
COUNT(*) * 1000000000.0 / (MAX(ts) - MIN(ts)) AS fps
FROM slice
WHERE name = 'Choreographer#doFrame';
查询CPU使用率:
-- 查询CPU使用率
SELECT
cpu,
COUNT(*) * 100.0 / (SELECT COUNT(*) FROM sched WHERE cpu = s.cpu) AS usage_percent
FROM sched s
GROUP BY cpu;
查询内存使用:
-- 查询内存使用情况
SELECT
name,
value / 1024.0 / 1024.0 AS size_mb
FROM counter
WHERE name LIKE '%mem%'
ORDER BY ts DESC
LIMIT 10;
2.2 复杂查询
查询启动时间分布:
-- 查询不同阶段的启动时间
SELECT
'Process Creation' AS stage,
(SELECT ts FROM slice WHERE name LIKE '%startProcess%' LIMIT 1) AS start_ts,
(SELECT ts FROM slice WHERE name LIKE '%handleBindApplication%' LIMIT 1) AS end_ts,
((SELECT ts FROM slice WHERE name LIKE '%handleBindApplication%' LIMIT 1) -
(SELECT ts FROM slice WHERE name LIKE '%startProcess%' LIMIT 1)) / 1000000.0 AS duration_ms
UNION ALL
SELECT
'Application Init' AS stage,
(SELECT ts FROM slice WHERE name LIKE '%handleBindApplication%' LIMIT 1) AS start_ts,
(SELECT ts FROM slice WHERE name LIKE '%performLaunchActivity%' LIMIT 1) AS end_ts,
((SELECT ts FROM slice WHERE name LIKE '%performLaunchActivity%' LIMIT 1) -
(SELECT ts FROM slice WHERE name LIKE '%handleBindApplication%' LIMIT 1)) / 1000000.0 AS duration_ms;
3. 对比分析技巧
3.1 多Trace对比
方法1:时间对齐
1. 打开多个Trace文件
2. 找到相同的起始事件
3. 对齐时间线
4. 对比关键指标
方法2:指标对比
1. 分别计算各Trace的关键指标
2. 制作对比表格
3. 分析差异原因
3.2 版本对比
优化前后对比:
1. 录制优化前的Trace(基准版本)
2. 录制优化后的Trace(优化版本)
3. 对比关键指标:
- 启动时间
- 帧率
- CPU使用率
- 内存使用
4. 验证优化效果
4. 性能监控技巧
4.1 长时间监控
录制长时间Trace:
# 录制30秒的Trace
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 30s
# 录制5分钟的Trace
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 5m
分析长时间Trace:
1. 使用时间轴缩放功能
2. 查找性能问题的时间点
3. 分析性能趋势
4.2 自动化分析
导出数据:
1. 使用SQL查询导出数据
2. 使用脚本自动化分析
3. 生成性能报告
五、常见问题排查
1. Trace文件过大
问题: Trace文件太大,打开缓慢
解决方案:
- 减少追踪时间
- 减少追踪类别
- 使用更小的缓冲区
- 分段录制和分析
2. 找不到关键事件
问题: 在Trace中找不到关键事件
解决方案:
- 确认追踪类别包含相关事件
- 检查应用是否添加了Trace标记
- 使用搜索功能查找
- 查看相关进程和线程
3. 性能数据不准确
问题: Trace中的性能数据与实际不符
解决方案:
- 确认追踪配置正确
- 检查是否有其他进程干扰
- 多次录制取平均值
- 对比其他性能工具
4. 分析效率低
问题: 分析Trace耗时过长
解决方案:
- 使用SQL查询快速定位问题
- 使用书签标记关键位置
- 使用筛选功能缩小范围
- 建立分析模板和流程
六、最佳实践
1. 录制Trace
- 明确目标:在录制前明确要分析的问题
- 合适时长:选择适当的追踪时间(启动5-10秒,游戏30-60秒)
- 关键类别:只追踪相关的类别,减少文件大小
- 多次录制:多次录制确保结果一致
- 环境一致:保持测试环境一致(温度、电量、后台应用等)
2. 分析Trace
- 系统化分析:按照固定流程分析(启动流程、渲染流程等)
- 关键指标:关注关键性能指标(启动时间、帧率等)
- 对比分析:与基准版本或竞品对比
- 深入分析:找到根本原因,不只是表面现象
- 验证优化:优化后再次录制验证效果
3. 优化验证
- 量化指标:使用具体数值衡量优化效果
- 多维度验证:从多个角度验证优化效果
- 回归测试:确保优化没有引入新问题
- 持续监控:建立性能监控体系
最后更新:2024年