Files
mkdocs/docs/学习笔记/perfeto看trace技巧.md
2026-02-11 00:40:25 +08:00

18 KiB
Raw Permalink Blame History

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打开

  1. 访问 https://ui.perfetto.dev/
  2. 点击 "Open trace file"
  3. 选择 trace.pb 文件
  4. 等待加载完成

2.2 Android Studio打开

  1. 打开 Android Studio
  2. 连接设备
  3. 打开 Profiler
  4. 选择 CPU Profiler
  5. 点击 "Record" 开始录制
  6. 执行操作后停止录制

二、应用冷启动分析技巧

1. 启动流程关键节点

1.1 启动阶段划分

阶段1进程创建Process Creation

  • 查找点:ActivityManager: startProcess
  • 关键指标进程fork时间、进程初始化时间
  • 查看位置SystemServer进程的ActivityManagerService

阶段2Application初始化Application Init

  • 查找点:ActivityThread: handleBindApplication
  • 关键指标Application.onCreate()耗时
  • 查看位置:目标应用主线程

阶段3Activity创建Activity Creation

  • 查找点:ActivityThread: performLaunchActivity
  • 关键指标Activity.onCreate()耗时
  • 查看位置:目标应用主线程

阶段4首帧渲染First Frame

  • 查找点:Choreographer#doFrameSurfaceFlinger: 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等

常见阻塞场景:

  1. 等待Binder调用

    • 特征主线程处于Sleep状态等待Binder返回
    • 查找查看wakeup信息找到对应的Binder调用
    • 分析检查SystemServer中的Binder处理耗时
  2. 等待锁

    • 特征主线程处于Sleep状态等待锁释放
    • 查找查看wakeup信息找到锁持有者
    • 分析:检查锁持有时间是否过长
  3. 等待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. 识别渲染瓶颈

常见渲染问题:

  1. dequeueBuffer耗时

    • 特征RenderThread等待Buffer分配
    • 原因SurfaceFlinger繁忙或Buffer不足
    • 分析查看SurfaceFlinger进程状态
  2. queueBuffer耗时

    • 特征RenderThread等待Buffer提交
    • 原因SurfaceFlinger处理延迟
    • 分析查看SurfaceFlinger合成耗时
  3. 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. 分析阻塞原因

常见阻塞场景:

  1. 等待网络请求

    • 特征:主线程等待网络响应
    • 解决:使用异步网络请求
  2. 等待数据库操作

    • 特征:主线程等待数据库读写
    • 解决:使用异步数据库操作
  3. 等待文件IO

    • 特征:主线程等待文件读写
    • 解决使用异步IO操作
  4. 等待锁

    • 特征:主线程等待锁释放
    • 解决:优化锁的使用

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文件太大打开缓慢

解决方案:

  1. 减少追踪时间
  2. 减少追踪类别
  3. 使用更小的缓冲区
  4. 分段录制和分析

2. 找不到关键事件

问题: 在Trace中找不到关键事件

解决方案:

  1. 确认追踪类别包含相关事件
  2. 检查应用是否添加了Trace标记
  3. 使用搜索功能查找
  4. 查看相关进程和线程

3. 性能数据不准确

问题: Trace中的性能数据与实际不符

解决方案:

  1. 确认追踪配置正确
  2. 检查是否有其他进程干扰
  3. 多次录制取平均值
  4. 对比其他性能工具

4. 分析效率低

问题: 分析Trace耗时过长

解决方案:

  1. 使用SQL查询快速定位问题
  2. 使用书签标记关键位置
  3. 使用筛选功能缩小范围
  4. 建立分析模板和流程

六、最佳实践

1. 录制Trace

  1. 明确目标:在录制前明确要分析的问题
  2. 合适时长选择适当的追踪时间启动5-10秒游戏30-60秒
  3. 关键类别:只追踪相关的类别,减少文件大小
  4. 多次录制:多次录制确保结果一致
  5. 环境一致:保持测试环境一致(温度、电量、后台应用等)

2. 分析Trace

  1. 系统化分析:按照固定流程分析(启动流程、渲染流程等)
  2. 关键指标:关注关键性能指标(启动时间、帧率等)
  3. 对比分析:与基准版本或竞品对比
  4. 深入分析:找到根本原因,不只是表面现象
  5. 验证优化:优化后再次录制验证效果

3. 优化验证

  1. 量化指标:使用具体数值衡量优化效果
  2. 多维度验证:从多个角度验证优化效果
  3. 回归测试:确保优化没有引入新问题
  4. 持续监控:建立性能监控体系

最后更新2024年