# Perfetto看Trace技巧

## 一、Perfetto基础操作

### 1. 录制Trace

#### 1.1 命令行录制

```bash
# 基础录制命令
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）：**
```protobuf
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）：**
```protobuf
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 使用配置文件录制

```bash
# 将配置文件推送到设备
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

**阶段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等）
```

**常见阻塞场景：**

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查询分析

**查询启动总耗时：**
```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初始化耗时：**
```sql
-- 查询Application.onCreate耗时
SELECT 
    name,
    dur / 1000000.0 AS duration_ms
FROM slice 
WHERE name LIKE '%Application.onCreate%'
ORDER BY dur DESC
LIMIT 10;
```

**查询主线程阻塞时间：**
```sql
-- 查询主线程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查询丢帧：**
```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;
```

**查询连续丢帧：**
```sql
-- 查询连续丢帧的情况
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查询耗时操作：**
```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代码：**
```java
import android.os.Trace;

// 开始标记
Trace.beginSection("my_custom_section");

// 执行代码
doSomething();

// 结束标记
Trace.endSection();
```

**C++代码：**
```cpp
#include <utils/Trace.h>

// 开始标记
ATRACE_BEGIN("my_custom_section");

// 执行代码
doSomething();

// 结束标记
ATRACE_END();
```

#### 1.2 在Perfetto中查看自定义标记

```
1. 在搜索框输入自定义标记名称
2. 找到对应的Trace事件
3. 分析该代码段的性能
```

### 2. SQL查询技巧

#### 2.1 常用查询

**查询帧率统计：**
```sql
-- 计算平均帧率
SELECT 
    COUNT(*) * 1000000000.0 / (MAX(ts) - MIN(ts)) AS fps
FROM slice 
WHERE name = 'Choreographer#doFrame';
```

**查询CPU使用率：**
```sql
-- 查询CPU使用率
SELECT 
    cpu,
    COUNT(*) * 100.0 / (SELECT COUNT(*) FROM sched WHERE cpu = s.cpu) AS usage_percent
FROM sched s
GROUP BY cpu;
```

**查询内存使用：**
```sql
-- 查询内存使用情况
SELECT 
    name,
    value / 1024.0 / 1024.0 AS size_mb
FROM counter 
WHERE name LIKE '%mem%'
ORDER BY ts DESC
LIMIT 10;
```

#### 2.2 复杂查询

**查询启动时间分布：**
```sql
-- 查询不同阶段的启动时间
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：**
```bash
# 录制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年*
