鏇存柊鏂囨。

This commit is contained in:
renjianbo
2026-02-11 00:40:25 +08:00
parent 22c03eaf42
commit fd755bd20f
5 changed files with 2698 additions and 16 deletions

View File

@@ -27,16 +27,30 @@
"state": {
"type": "markdown",
"state": {
"file": "docs/学习笔记/高级性能问题分析目录.md",
"file": "docs/Obsidian笔记体系/Daily/2026-01-13.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "高级性能问题分析目录"
"title": "2026-01-13"
}
},
{
"id": "59894255df52f532",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "docs/Obsidian笔记体系/Projects/知你-调测/知你--调测.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "知你--调测"
}
}
],
"currentTab": 1
"currentTab": 2
}
],
"direction": "vertical"
@@ -198,16 +212,24 @@
"command-palette:打开命令面板": false
}
},
"active": "25c9f7051aac05b3",
"active": "59894255df52f532",
"lastOpenFiles": [
"docs/学习笔记/php/未命名.md",
"docs/学习笔记/高级性能问题分析目录.md",
"docs/Obsidian笔记体系/Daily/2026-01-14.md",
"docs/Obsidian笔记体系/Daily/2024-06-02.md",
"docs/Obsidian笔记体系/Daily/2026-01-13.md",
"docs/Obsidian笔记体系/Daily/2026-01-15.md",
"docs/Obsidian笔记体系/Daily/2026-01-20.md",
"docs/学习笔记/高级性能问题分析.md",
"docs/学习笔记/高级性能问题分析目录.md",
"docs/学习笔记/产品经理/需求管理详解.md",
"docs/学习笔记/产品经理/项目管理详解.md",
"docs/Obsidian/高频命令.md",
"docs/Obsidian笔记体系/Projects/知你-调测/知你--调测.md",
"docs/学习笔记/基础性能问题分析ODM.md",
"docs/Obsidian笔记体系/Areas/09-调试与工具链/Systrace_Perfetto全解读.md",
"docs/学习笔记/php/未命名.md",
"docs/git/git设置用户名和邮箱.md",
"docs/学习笔记/产品经理/产品经理--写文档.md",
"docs/学习笔记/产品经理/项目管理详解.md",
"docs/学习笔记/产品经理/需求管理详解.md",
"docs/学习笔记/产品经理/沟通与表达详解.md",
"docs/学习笔记/产品经理/数据分析详解.md",
"docs/学习笔记/产品经理/产品运营详解.md",
@@ -219,14 +241,6 @@
"docs/学习笔记/产品经理/产品经理--些迭代文档.md",
"docs/学习笔记/产品经理",
"docs/产品经理/沟通与表达详解.md",
"docs/产品经理/产品运营详解.md",
"docs/产品经理/数据分析详解.md",
"docs/git/git常用命令.md",
"docs/cursor/cursor.md",
"docs/产品经理/产品经理技能.md",
"docs/产品经理/项目管理详解.md",
"docs/产品经理/产品设计详解.md",
"docs/产品经理/需求管理详解.md",
"Pasted image 20260129111501.png",
"Pasted image 20260129111451.png",
"Pasted image 20260129111437.png",

View File

@@ -0,0 +1,835 @@
# 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
**阶段2Application初始化Application Init**
- 查找点:`ActivityThread: handleBindApplication`
- 关键指标Application.onCreate()耗时
- 查看位置:目标应用主线程
**阶段3Activity创建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年*

View File

@@ -0,0 +1,984 @@
# Perfetto使用技巧
## 一、Perfetto简介
### 1. 什么是Perfetto
Perfetto是Google开发的下一代系统级性能分析工具从Android 9API 28开始集成到系统中。它是Systrace的升级版提供了更强大的功能和更好的性能。
### 2. Perfetto的优势
- **更长的追踪时间**:支持数小时的长时间追踪
- **更丰富的数据源**支持CPU、内存、网络、电源等多种数据源
- **更强大的分析能力**支持SQL查询、自定义分析
- **更好的可视化**Web UI界面更友好交互更流畅
- **更灵活的配置**:支持配置文件,可以精确控制追踪内容
### 3. 适用场景
- 应用启动性能分析
- 游戏帧率问题分析
- 系统卡顿问题定位
- 内存泄漏排查
- CPU性能分析
- 网络性能分析
- 电源管理分析
## 二、环境准备与安装
### 1. 系统要求
- **Android版本**Android 9API 28及以上
- **设备要求**需要root权限或userdebug版本
- **开发环境**ADB工具、Python 3.6+(可选)
### 2. 检查设备支持
```bash
# 检查Perfetto是否可用
adb shell perfetto --help
# 检查设备Android版本
adb shell getprop ro.build.version.release
# 检查设备是否支持Perfetto
adb shell getprop ro.build.version.sdk
```
### 3. 获取Perfetto工具
#### 3.1 通过Android Studio
Android Studio内置了Perfetto支持可以直接使用Profiler功能。
#### 3.2 通过命令行
Perfetto已经集成在Android系统中可以直接通过adb使用。
#### 3.3 通过Web UI
访问 https://ui.perfetto.dev/ 可以在线分析Trace文件。
### 4. 安装Python依赖可选
如果需要使用Python脚本处理Trace文件
```bash
# 安装protobuf
pip install protobuf
# 安装perfetto Python库
pip install perfetto
```
## 三、基础使用
### 1. 快速开始
#### 1.1 最简单的录制
```bash
# 录制10秒的Trace
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 10s
# 拉取Trace文件
adb pull /data/misc/perfetto-traces/trace trace.pb
# 在Web UI中打开
# 访问 https://ui.perfetto.dev/ 并上传trace.pb
```
#### 1.2 指定应用录制
```bash
# 录制指定应用的Trace
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace \
-a com.example.app -t 10s
```
### 2. 命令行参数
#### 2.1 常用参数
```bash
# -c: 配置文件(- 表示使用默认配置)
adb shell perfetto -c -
# --out: 输出文件路径
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace
# -t: 录制时长
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 10s
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 5m
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 1h
# -a: 指定应用包名
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -a com.example.app
# --txt: 使用文本格式配置文件
adb shell perfetto -c /data/local/tmp/config.txt --out /data/local/tmp/trace.pb
```
#### 2.2 高级参数
```bash
# --background: 后台运行
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace --background
# --detach: 分离模式(录制完成后自动停止)
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace --detach
# --stop: 停止正在运行的录制
adb shell perfetto --stop
```
### 3. 配置文件使用
#### 3.1 配置文件格式
Perfetto支持两种配置文件格式
- **文本格式(.txt**:人类可读,易于编辑
- **二进制格式(.pb**:更紧凑,性能更好
#### 3.2 基础配置文件
**基础配置basic_config.txt**
```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"
atrace_categories: "gfx"
atrace_categories: "view"
buffer_size_kb: 2048
}
}
}
duration_ms: 10000
```
#### 3.3 使用配置文件
```bash
# 1. 创建配置文件
cat > config.txt << 'EOF'
buffers: {
size_kb: 63488
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
atrace_categories: "gfx"
atrace_categories: "view"
}
}
}
duration_ms: 10000
EOF
# 2. 推送到设备
adb push config.txt /data/local/tmp/
# 3. 使用配置文件录制
adb shell perfetto -c /data/local/tmp/config.txt --out /data/local/tmp/trace.pb
# 4. 拉取Trace文件
adb pull /data/local/tmp/trace.pb trace.pb
```
## 四、常用配置模板
### 1. 启动性能分析配置
**startup_config.txt**
```protobuf
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
# CPU调度事件
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类别
atrace_categories: "am" # Activity Manager
atrace_categories: "wm" # Window Manager
atrace_categories: "gfx" # Graphics
atrace_categories: "view" # View System
atrace_categories: "binder_driver" # Binder Driver
atrace_categories: "binder_lock" # Binder Lock
atrace_categories: "input" # Input
atrace_categories: "res" # Resources
atrace_categories: "dalvik" # Dalvik VM
buffer_size_kb: 4096
drain_period_ms: 250
}
}
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
duration_ms: 10000
```
### 2. 游戏帧率分析配置
**jank_config.txt**
```protobuf
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
# CPU调度
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_waking"
#
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
# GPU事件GPU型号选择
ftrace_events: "gfx/mali_gpu_total"
ftrace_events: "gpu_mem_total/gpu_mem_total"
# ATrace类别
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
```
### 3. 内存分析配置
**memory_config.txt**
```protobuf
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "kmem/kmalloc"
ftrace_events: "kmem/kfree"
ftrace_events: "kmem/kmem_cache_alloc"
ftrace_events: "kmem/kmem_cache_free"
atrace_categories: "dalvik"
atrace_categories: "gfx"
buffer_size_kb: 4096
}
}
}
data_sources: {
config {
name: "android.java_hprof"
target_buffer: 0
}
}
duration_ms: 60000
```
### 4. 网络分析配置
**network_config.txt**
```protobuf
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "net/net_dev_xmit"
ftrace_events: "net/net_dev_recv"
atrace_categories: "network"
buffer_size_kb: 2048
}
}
}
data_sources: {
config {
name: "linux.network_packets"
}
}
duration_ms: 30000
```
### 5. 全系统分析配置
**full_system_config.txt**
```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: "power/suspend_resume"
# ATrace类别
atrace_categories: "am"
atrace_categories: "wm"
atrace_categories: "gfx"
atrace_categories: "view"
atrace_categories: "input"
atrace_categories: "res"
atrace_categories: "dalvik"
atrace_categories: "binder_driver"
atrace_categories: "binder_lock"
atrace_categories: "sched"
atrace_categories: "freq"
atrace_categories: "idle"
atrace_categories: "disk"
atrace_categories: "load"
atrace_categories: "sync"
atrace_categories: "workq"
atrace_categories: "memreclaim"
atrace_categories: "regulators"
atrace_categories: "binder_driver"
atrace_categories: "binder_lock"
buffer_size_kb: 8192
drain_period_ms: 250
}
}
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
duration_ms: 60000
```
## 五、ATrace类别详解
### 1. 常用类别
| 类别 | 说明 | 使用场景 |
|------|------|----------|
| `gfx` | 图形渲染 | 帧率分析、渲染性能 |
| `view` | 视图系统 | UI性能、布局分析 |
| `am` | Activity Manager | 应用启动、Activity生命周期 |
| `wm` | Window Manager | 窗口管理、多窗口 |
| `input` | 输入事件 | 触摸响应、输入延迟 |
| `sched` | CPU调度 | CPU性能、任务调度 |
| `freq` | CPU频率 | 频率管理、性能调频 |
| `idle` | CPU空闲 | 电源管理、CPU使用率 |
| `binder_driver` | Binder驱动 | 跨进程通信 |
| `binder_lock` | Binder锁 | 锁竞争分析 |
| `res` | 资源管理 | 资源加载、资源管理 |
| `dalvik` | Dalvik VM | Java代码执行、GC |
| `disk` | 磁盘I/O | 文件读写、数据库操作 |
| `load` | 系统负载 | 系统负载分析 |
| `sync` | 同步操作 | 同步原语、锁 |
| `workq` | 工作队列 | 后台任务 |
| `memreclaim` | 内存回收 | 内存管理、GC |
| `network` | 网络 | 网络性能分析 |
### 2. 类别组合建议
**启动分析:**
```
am wm gfx view binder_driver binder_lock input res dalvik
```
**帧率分析:**
```
gfx view sched freq idle
```
**内存分析:**
```
dalvik gfx memreclaim
```
**网络分析:**
```
network disk
```
**全系统分析:**
```
am wm gfx view input res dalvik binder_driver binder_lock sched freq idle disk load sync workq memreclaim
```
## 六、高级功能
### 1. 自定义Trace标记
#### 1.1 Java/Kotlin代码
```java
import android.os.Trace;
public class MyClass {
public void doSomething() {
// 开始标记
Trace.beginSection("my_custom_section");
try {
// 执行代码
performOperation();
} finally {
// 结束标记必须在finally中调用
Trace.endSection();
}
}
private void performOperation() {
Trace.beginSection("perform_operation");
// 操作代码
Trace.endSection();
}
}
```
#### 1.2 C++代码
```cpp
#include <utils/Trace.h>
void doSomething() {
// 开始标记
ATRACE_BEGIN("my_custom_section");
// 执行代码
performOperation();
// 结束标记
ATRACE_END();
}
void performOperation() {
ATRACE_BEGIN("perform_operation");
// 操作代码
ATRACE_END();
}
```
#### 1.3 异步操作标记
```java
import android.os.Trace;
public class AsyncTask {
public void asyncOperation() {
Trace.beginAsyncSection("async_operation", 0);
// 启动异步操作
new Thread(() -> {
try {
// 异步操作代码
performAsyncWork();
} finally {
// 结束异步标记
Trace.endAsyncSection("async_operation", 0);
}
}).start();
}
}
```
### 2. 长时间追踪
#### 2.1 录制长时间Trace
```bash
# 录制5分钟
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 5m
# 录制1小时
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 1h
# 使用配置文件指定时长
# 在配置文件中设置 duration_ms: 3600000 # 1小时
```
#### 2.2 后台录制
```bash
# 后台录制
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace \
--background -t 10m
# 检查录制状态
adb shell ps | grep perfetto
# 停止录制
adb shell perfetto --stop
```
### 3. 多进程追踪
#### 3.1 指定多个应用
```bash
# 追踪多个应用
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace \
-a com.example.app1 -a com.example.app2 -t 10s
```
#### 3.2 追踪系统进程
```bash
# 追踪系统进程需要root
adb root
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace \
-t 10s
```
### 4. 条件追踪
#### 4.1 基于触发器的追踪
```protobuf
#
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
atrace_categories: "gfx"
#
trigger_config {
trigger_mode: START_TRACING
trigger_name: "my_trigger"
}
}
}
}
```
## 七、Trace文件分析
### 1. Web UI使用技巧
#### 1.1 基本操作
- **缩放**鼠标滚轮或Ctrl+滚轮
- **平移**:鼠标拖拽或方向键
- **选择**:鼠标点击选择事件
- **搜索**Ctrl+F 或点击搜索框
- **书签**按M键标记当前位置
- **时间选择**Shift+鼠标拖拽选择时间范围
#### 1.2 视图操作
- **展开/折叠**:点击进程/线程名称
- **高亮**:双击事件高亮显示
- **筛选**:使用筛选器过滤事件
- **排序**:点击列标题排序
#### 1.3 快捷键
- `W` / `S`:放大/缩小
- `A` / `D`:左移/右移
- `M`:添加书签
- `G`:跳转到书签
- `F`:查找
- `?`:显示所有快捷键
### 2. SQL查询
#### 2.1 基础查询
**查询所有帧信息:**
```sql
SELECT * FROM slice WHERE name = 'Choreographer#doFrame';
```
**查询掉帧:**
```sql
SELECT
ts,
dur / 1000000.0 AS duration_ms
FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16666667
ORDER BY ts;
```
**查询CPU使用率**
```sql
SELECT
cpu,
COUNT(*) * 100.0 / (SELECT COUNT(*) FROM sched WHERE cpu = s.cpu) AS usage_percent
FROM sched s
GROUP BY cpu;
```
#### 2.2 高级查询
**查询启动时间:**
```sql
SELECT
(SELECT ts FROM slice WHERE name LIKE '%Choreographer#doFrame%' LIMIT 1) -
(SELECT ts FROM slice WHERE name LIKE '%startProcess%' LIMIT 1)
AS startup_time_ns;
```
**查询主线程阻塞时间:**
```sql
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';
```
**查询GC频率**
```sql
SELECT
COUNT(*) AS gc_count,
(MAX(ts) - MIN(ts)) / 1000000000.0 AS duration_sec,
COUNT(*) * 1.0 / ((MAX(ts) - MIN(ts)) / 1000000000.0) AS gc_per_sec
FROM slice
WHERE name LIKE '%GC%';
```
### 3. 性能指标分析
#### 3.1 帧率分析
**查看帧率:**
```
1. 在顶部选择 "Frame Timeline"
2. 查看每一帧的状态
3. 统计正常帧、掉帧数量
4. 计算平均帧率
```
**使用SQL计算帧率**
```sql
SELECT
COUNT(*) * 1000000000.0 / (MAX(ts) - MIN(ts)) AS fps
FROM slice
WHERE name = 'Choreographer#doFrame';
```
#### 3.2 CPU性能分析
**查看CPU频率**
```
1. 在顶部选择 "CPU frequency"
2. 查看各核心频率变化
3. 检查是否有降频
```
**查看CPU使用率**
```
1. 在顶部选择 "CPU usage"
2. 查看各核心使用情况
3. 检查是否有瓶颈
```
#### 3.3 内存分析
**查看内存使用:**
```
1. 在顶部选择 "Memory"
2. 查看内存使用趋势
3. 检查是否有内存泄漏
```
**查看GC活动**
```
1. 在主线程中查找 "HeapTaskDaemon"
2. 查看GC频率和耗时
3. 分析GC对性能的影响
```
## 八、最佳实践
### 1. 录制Trace
1. **明确目标**:在录制前明确要分析的问题
2. **合适时长**:选择适当的追踪时间
- 启动分析5-10秒
- 帧率分析30-60秒
- 内存分析1-5分钟
- 长时间监控:根据需要
3. **关键类别**:只追踪相关的类别,减少文件大小
4. **多次录制**:多次录制确保结果一致
5. **环境一致**:保持测试环境一致
- 温度
- 电量
- 后台应用
- 网络状态
### 2. 分析Trace
1. **系统化分析**:按照固定流程分析
2. **关键指标**:关注关键性能指标
3. **对比分析**:与基准版本或竞品对比
4. **深入分析**:找到根本原因
5. **验证优化**:优化后再次录制验证
### 3. 性能优化
1. **量化指标**:使用具体数值衡量优化效果
2. **多维度验证**:从多个角度验证优化效果
3. **回归测试**:确保优化没有引入新问题
4. **持续监控**:建立性能监控体系
## 九、常见问题与解决方案
### 1. 录制问题
**问题1权限不足**
```
错误perfetto: permission denied
解决需要root权限或userdebug版本
```
**问题2设备不支持**
```
错误perfetto: command not found
解决检查Android版本需要Android 9+
```
**问题3配置文件格式错误**
```
错误Failed to parse config
解决:检查配置文件格式,使用文本格式更易调试
```
### 2. 分析问题
**问题1Trace文件过大**
```
解决:
1. 减少追踪时间
2. 减少追踪类别
3. 使用更小的缓冲区
4. 分段录制和分析
```
**问题2找不到关键事件**
```
解决:
1. 确认追踪类别包含相关事件
2. 检查应用是否添加了Trace标记
3. 使用搜索功能查找
4. 查看相关进程和线程
```
**问题3性能数据不准确**
```
解决:
1. 确认追踪配置正确
2. 检查是否有其他进程干扰
3. 多次录制取平均值
4. 对比其他性能工具
```
### 3. 性能问题
**问题1录制影响性能**
```
解决:
1. 减少追踪类别
2. 使用较小的缓冲区
3. 减少追踪时间
4. 在性能测试时关闭不必要的追踪
```
**问题2Web UI加载慢**
```
解决:
1. 减少Trace文件大小
2. 使用本地Perfetto工具
3. 分段分析Trace文件
```
## 十、实用脚本
### 1. 快速录制脚本
**quick_trace.sh**
```bash
#!/bin/bash
# 快速录制Trace脚本
DURATION=${1:-10}
OUTPUT=${2:-trace.pb}
echo "开始录制 ${DURATION} 秒的Trace..."
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t ${DURATION}s
echo "拉取Trace文件..."
adb pull /data/misc/perfetto-traces/trace ${OUTPUT}
echo "Trace文件已保存为: ${OUTPUT}"
echo "可以在 https://ui.perfetto.dev/ 打开分析"
```
**使用方法:**
```bash
chmod +x quick_trace.sh
./quick_trace.sh 10 trace.pb
```
### 2. 应用启动分析脚本
**startup_trace.sh**
```bash
#!/bin/bash
PACKAGE=${1:-com.example.app}
OUTPUT=${2:-startup_trace.pb}
echo "开始录制 ${PACKAGE} 的启动Trace..."
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace \
-a ${PACKAGE} -t 10s
echo "拉取Trace文件..."
adb pull /data/misc/perfetto-traces/trace ${OUTPUT}
echo "启动Trace已保存为: ${OUTPUT}"
```
**使用方法:**
```bash
chmod +x startup_trace.sh
./startup_trace.sh com.example.app startup.pb
```
### 3. 批量分析脚本
**batch_analysis.sh**
```bash
#!/bin/bash
# 批量录制和分析Trace
for i in {1..5}; do
echo "录制第 ${i} 次Trace..."
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace_${i} -t 10s
adb pull /data/misc/perfetto-traces/trace_${i} trace_${i}.pb
echo "${i} 次Trace已保存"
done
echo "所有Trace录制完成"
```
## 十一、进阶技巧
### 1. 性能基准建立
**建立性能基准:**
```
1. 在稳定版本上录制Trace
2. 记录关键性能指标
3. 建立性能基准数据库
4. 定期更新基准
```
### 2. 自动化性能测试
**集成到CI/CD**
```
1. 在自动化测试中录制Trace
2. 自动分析关键指标
3. 设置性能阈值
4. 性能回归自动告警
```
### 3. 性能报告生成
**生成性能报告:**
```
1. 使用SQL查询提取数据
2. 使用脚本处理数据
3. 生成可视化报告
4. 定期发送性能报告
```
## 十二、参考资料
### 1. 官方文档
- Perfetto官方文档https://perfetto.dev/
- Perfetto Web UIhttps://ui.perfetto.dev/
- Android性能优化指南https://developer.android.com/topic/performance
### 2. 相关工具
- SystraceAndroid系统追踪工具
- Android Studio ProfilerIDE集成性能分析工具
- TraceviewJava代码性能分析工具
### 3. 学习资源
- Perfetto GitHubhttps://github.com/google/perfetto
- Android性能优化最佳实践
- 系统性能分析案例
---
*最后更新2024年*

View File

@@ -0,0 +1,846 @@
# Perfetto看应用卡顿问题
## 一、卡顿问题概述
### 1. 什么是卡顿
卡顿是指应用在运行过程中出现画面不流畅、响应延迟、操作不跟手等现象。从技术角度来说,卡顿通常表现为:
- **丢帧**60Hz设备上1秒内显示的帧数少于60帧
- **帧延迟**单帧渲染时间超过16.67ms60Hz或8.33ms120Hz
- **响应延迟**:用户操作到界面响应的时间过长
### 2. 卡顿的分类
#### 2.1 按场景分类
- **滑动卡顿**:列表滑动、页面滚动时出现卡顿
- **动画卡顿**页面跳转、转场动画、UI动画不流畅
- **点击卡顿**:点击操作后响应延迟
- **启动卡顿**:应用启动过程缓慢
- **交互卡顿**:界面交互操作不流畅
#### 2.2 按原因分类
- **主线程阻塞**:主线程执行耗时操作
- **渲染瓶颈**RenderThread或GPU性能不足
- **系统资源竞争**CPU、内存、I/O资源不足
- **锁竞争**:多线程竞争导致阻塞
- **内存压力**GC频繁、内存不足
### 3. 卡顿分析的目标
- **定位卡顿时间点**:确定卡顿发生的具体时间
- **识别卡顿原因**:找到导致卡顿的根本原因
- **量化卡顿程度**:测量卡顿的严重程度
- **提供优化建议**:给出针对性的优化方案
## 二、Perfetto录制卡顿场景
### 1. 录制前的准备
#### 1.1 明确问题场景
在录制前需要明确:
- **卡顿场景**:什么操作导致卡顿
- **复现步骤**:如何复现卡顿问题
- **卡顿表现**:卡顿的具体表现(丢帧、延迟等)
- **预期时长**:卡顿持续的时间
#### 1.2 环境准备
- **设备状态**:确保设备电量充足、温度正常
- **后台应用**:关闭不必要的后台应用
- **网络状态**:确保网络稳定(如需要)
- **应用版本**:使用待分析的应用版本
### 2. 卡顿分析配置文件
#### 2.1 基础卡顿分析配置
**jank_analysis_config.txt**
```protobuf
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
# CPU调度事件
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_waking"
#
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
# GPU事件GPU型号选择
ftrace_events: "gfx/mali_gpu_total"
ftrace_events: "gpu_mem_total/gpu_mem_total"
# ATrace类别
atrace_categories: "gfx" #
atrace_categories: "view" #
atrace_categories: "sched" # CPU调度
atrace_categories: "freq" # CPU频率
atrace_categories: "idle" # CPU空闲
atrace_categories: "input" #
atrace_categories: "dalvik" # Java执行
atrace_categories: "binder_driver" # Binder通信
buffer_size_kb: 8192
drain_period_ms: 250
}
}
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
duration_ms: 30000
```
#### 2.2 滑动卡顿分析配置
**scroll_jank_config.txt**
```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"
atrace_categories: "gfx"
atrace_categories: "view"
atrace_categories: "input"
atrace_categories: "sched"
atrace_categories: "freq"
atrace_categories: "idle"
atrace_categories: "dalvik"
buffer_size_kb: 8192
drain_period_ms: 250
}
}
}
data_sources: {
config {
name: "android.surfaceflinger.frame"
}
}
duration_ms: 60000
```
### 3. 录制命令
#### 3.1 基础录制
```bash
# 录制30秒的Trace
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace -t 30s
# 指定应用录制
adb shell perfetto -c - --out /data/misc/perfetto-traces/trace \
-a com.example.app -t 30s
```
#### 3.2 使用配置文件录制
```bash
# 1. 推送配置文件
adb push jank_analysis_config.txt /data/local/tmp/
# 2. 开始录制(在复现卡顿前)
adb shell perfetto -c /data/local/tmp/jank_analysis_config.txt \
--out /data/local/tmp/trace.pb
# 3. 复现卡顿问题
# (在设备上执行导致卡顿的操作)
# 4. 停止录制Ctrl+C 或等待duration_ms时间
# 5. 拉取Trace文件
adb pull /data/local/tmp/trace.pb jank_trace.pb
```
#### 3.3 后台录制
```bash
# 后台录制,适合长时间监控
adb shell perfetto -c /data/local/tmp/jank_analysis_config.txt \
--out /data/local/tmp/trace.pb --background
# 检查录制状态
adb shell ps | grep perfetto
# 停止录制
adb shell perfetto --stop
```
## 三、卡顿分析定位流程
### 1. 分析流程概览
```
1. 打开Trace文件
2. 定位卡顿时间点
3. 查看帧率信息
4. 分析主线程状态
5. 分析渲染线程状态
6. 分析系统资源
7. 定位根本原因
8. 提供优化建议
```
### 2. 步骤1打开Trace文件
#### 2.1 使用Web UI打开
1. 访问 https://ui.perfetto.dev/
2. 点击 "Open trace file"
3. 选择 trace.pb 文件
4. 等待加载完成
#### 2.2 初始检查
- **时间范围**确认Trace覆盖了卡顿时间段
- **进程列表**:找到目标应用进程
- **线程列表**确认主线程和RenderThread存在
### 3. 步骤2定位卡顿时间点
#### 3.1 方法1查看Frame Timeline
```
1. 在顶部选择 "Frame Timeline"
2. 查看帧的颜色:
- 绿色:正常帧(< 16.67ms
- 黄色轻微掉帧16.67-33.33ms
- 红色:严重掉帧(> 33.33ms
3. 找到红色或黄色的帧
4. 点击帧,查看详细信息
5. 标记卡顿时间点按M键
```
#### 3.2 方法2查看VSync信息
```
1. 在SurfaceFlinger进程中查找 "VSync"
2. 查看VSync周期是否正常60Hz = 16.67ms
3. 检查是否有VSync丢失
4. 找到VSync异常的时间点
```
#### 3.3 方法3使用SQL查询
```sql
-- 查询所有掉帧
SELECT
ts,
dur / 1000000.0 AS duration_ms
FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16666667
ORDER BY ts;
```
#### 3.4 方法4查看用户操作
```
1. 在Input进程中查找输入事件
2. 找到用户操作的时间点
3. 查看操作后的响应时间
4. 识别响应延迟的时间点
```
### 4. 步骤3查看帧率信息
#### 4.1 帧率统计
**使用SQL查询帧率**
```sql
-- 计算平均帧率
SELECT
COUNT(*) * 1000000000.0 / (MAX(ts) - MIN(ts)) AS fps
FROM slice
WHERE name = 'Choreographer#doFrame';
```
**查看帧率分布:**
```sql
-- 统计帧耗时分布
SELECT
CASE
WHEN dur < 16666667 THEN '正常'
WHEN dur < 33333333 THEN '轻微掉帧'
ELSE '严重掉帧'
END AS frame_type,
COUNT(*) AS count
FROM slice
WHERE name = 'Choreographer#doFrame'
GROUP BY frame_type;
```
#### 4.2 连续掉帧分析
```sql
-- 查询连续掉帧的情况
SELECT
ts,
dur / 1000000.0 AS duration_ms,
LAG(ts) OVER (ORDER BY ts) AS prev_ts,
ts - LAG(ts) OVER (ORDER BY ts) AS gap_ns
FROM slice
WHERE name = 'Choreographer#doFrame'
AND dur > 16666667
ORDER BY ts;
```
### 5. 步骤4分析主线程状态
#### 5.1 查看主线程时间线
```
1. 在进程列表中找到目标应用
2. 展开进程找到主线程通常是包名或main
3. 点击主线程,查看时间线
4. 查看线程状态:
- Running绿色正在执行
- Runnable黄色等待CPU
- Sleep红色等待资源
- UninterruptibleSleep紫色不可中断等待
```
#### 5.2 分析主线程阻塞
**查找Sleep状态**
```
1. 在主线程时间线中查找红色Sleep片段
2. 查看Sleep的持续时间
3. 分析Sleep的原因
- 等待Binder调用返回
- 等待锁释放
- 等待IO操作完成
- 等待子线程返回
```
**使用SQL查询主线程阻塞**
```sql
-- 查询主线程Sleep时间
SELECT
SUM(dur) / 1000000.0 AS total_sleep_ms,
COUNT(*) AS sleep_count
FROM sched
WHERE utid = (SELECT utid FROM thread WHERE name LIKE '%main%' LIMIT 1)
AND state = 'S';
```
#### 5.3 分析主线程耗时操作
**查找耗时操作:**
```
1. 在主线程时间线中按耗时排序
2. 查看耗时最长的操作
3. 分析操作是否可以优化:
- 是否可以移到后台线程
- 是否可以异步执行
- 是否可以优化算法
```
**使用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;
```
#### 5.4 常见主线程阻塞场景
**场景1等待Binder调用**
- **特征**主线程处于Sleep状态等待Binder返回
- **查找**查看wakeup信息找到对应的Binder调用
- **分析**检查SystemServer中的Binder处理耗时
- **解决**优化Binder服务端处理或使用异步调用
**场景2等待锁**
- **特征**主线程处于Sleep状态等待锁释放
- **查找**查看wakeup信息找到锁持有者
- **分析**:检查锁持有时间是否过长
- **解决**:减少锁持有时间,使用细粒度锁
**场景3等待IO**
- **特征**主线程处于UninterruptibleSleep-IO状态
- **查找**查看IO操作文件读写、数据库操作
- **分析**检查IO操作是否在主线程执行
- **解决**使用异步IO操作
**场景4等待网络**
- **特征**:主线程等待网络响应
- **查找**:查看网络请求相关操作
- **分析**:检查网络请求是否同步执行
- **解决**:使用异步网络请求
**场景5等待子线程**
- **特征**:主线程等待子线程返回数据
- **查找**查看wakeup信息找到依赖的子线程
- **分析**:检查子线程执行耗时
- **解决**:优化子线程执行,或使用回调机制
### 6. 步骤5分析渲染线程状态
#### 6.1 查看RenderThread时间线
```
1. 在应用进程中找到RenderThread线程
2. 查看线程状态和执行情况
3. 检查是否有阻塞或延迟
```
#### 6.2 分析渲染耗时
**关键操作分析:**
```
1. 在RenderThread中查找关键操作
- drawFrame绘制帧
- dequeueBuffer获取Buffer
- queueBuffer提交Buffer
2. 分析每个操作的耗时
3. 识别渲染瓶颈
```
**使用SQL查询渲染耗时**
```sql
-- 查询RenderThread耗时操作
SELECT
name,
dur / 1000000.0 AS duration_ms,
ts
FROM slice
WHERE utid = (SELECT utid FROM thread WHERE name LIKE '%RenderThread%' LIMIT 1)
ORDER BY dur DESC
LIMIT 20;
```
#### 6.3 常见渲染问题
**问题1dequeueBuffer耗时**
- **特征**RenderThread等待Buffer分配
- **原因**SurfaceFlinger繁忙或Buffer不足
- **分析**查看SurfaceFlinger进程状态
- **解决**优化SurfaceFlinger增加Buffer数量
**问题2queueBuffer耗时**
- **特征**RenderThread等待Buffer提交
- **原因**SurfaceFlinger处理延迟
- **分析**查看SurfaceFlinger合成耗时
- **解决**优化SurfaceFlinger合成流程
**问题3drawFrame耗时**
- **特征**RenderThread绘制耗时过长
- **原因**绘制内容复杂、GPU性能不足
- **分析**查看GPU使用情况和绘制命令
- **解决**优化绘制内容使用GPU加速
### 7. 步骤6分析系统资源
#### 7.1 CPU性能分析
**查看CPU频率**
```
1. 在顶部选择 "CPU frequency"
2. 查看卡顿期间CPU频率变化
3. 检查是否有降频或频率不足
```
**查看CPU使用率**
```
1. 在顶部选择 "CPU usage"
2. 查看卡顿期间CPU使用情况
3. 检查是否有CPU瓶颈
```
**查看CPU调度**
```
1. 在CPU核心时间线中查看任务调度
2. 检查关键任务是否跑在小核
3. 查看调度延迟
```
**使用SQL查询CPU使用率**
```sql
-- 查询CPU使用率
SELECT
cpu,
COUNT(*) * 100.0 / (SELECT COUNT(*) FROM sched WHERE cpu = s.cpu) AS usage_percent
FROM sched s
WHERE ts BETWEEN ? AND ? -- 卡顿时间段
GROUP BY cpu;
```
#### 7.2 内存性能分析
**查看GC活动**
```
1. 在主线程中查找 "HeapTaskDaemon"
2. 查看GC频率和耗时
3. 检查是否在关键帧期间发生GC
```
**使用SQL查询GC频率**
```sql
-- 查询GC频率
SELECT
COUNT(*) AS gc_count,
(MAX(ts) - MIN(ts)) / 1000000000.0 AS duration_sec,
COUNT(*) * 1.0 / ((MAX(ts) - MIN(ts)) / 1000000000.0) AS gc_per_sec
FROM slice
WHERE name LIKE '%GC%'
AND ts BETWEEN ? AND ?; -- 卡顿时间段
```
**查看内存使用:**
```
1. 在顶部选择 "Memory"
2. 查看卡顿期间内存使用趋势
3. 检查是否有内存泄漏
```
#### 7.3 GPU性能分析
**查看GPU使用率**
```
1. 在顶部选择 "GPU frequency" 或 "GPU usage"
2. 查看GPU频率和使用率
3. 检查是否有GPU瓶颈
```
**查看GPU渲染时间**
```
1. 在RenderThread中查找GPU相关操作
2. 查看GPU渲染耗时
3. 检查是否有GPU过载
```
### 8. 步骤7定位根本原因
#### 8.1 综合分析
根据前面的分析结果,综合判断卡顿的根本原因:
1. **主线程阻塞**如果主线程长时间Sleep分析阻塞原因
2. **渲染瓶颈**如果RenderThread耗时过长分析渲染瓶颈
3. **系统资源不足**如果CPU/GPU/内存不足,分析资源竞争
4. **锁竞争**:如果多线程竞争锁,分析锁竞争情况
5. **内存压力**如果GC频繁分析内存使用情况
#### 8.2 问题优先级
根据卡顿的严重程度和影响范围,确定问题优先级:
- **P0**:严重卡顿,影响核心功能
- **P1**:明显卡顿,影响用户体验
- **P2**:轻微卡顿,可接受但需优化
- **P3**:偶发卡顿,影响较小
### 9. 步骤8提供优化建议
#### 9.1 主线程优化
- **异步化**:将耗时操作移到后台线程
- **优化算法**:减少主线程计算量
- **减少锁竞争**:优化锁的使用
- **优化IO**使用异步IO操作
#### 9.2 渲染优化
- **减少绘制内容**:优化视图层级
- **使用硬件加速**启用GPU加速
- **优化布局**:减少布局复杂度
- **减少过度绘制**:优化绘制区域
#### 9.3 系统资源优化
- **CPU优化**:优化任务调度,绑定关键任务到高性能核心
- **内存优化**减少内存分配优化GC
- **GPU优化**优化GPU使用减少GPU负载
## 四、常见卡顿场景分析
### 1. 滑动卡顿分析
#### 1.1 问题特征
- 列表滑动时出现卡顿
- 滑动不跟手
- 滑动后页面继续滑动(惯性滑动)时卡顿
#### 1.2 分析步骤
```
1. 定位滑动时间段
- 查找Input事件触摸事件
- 找到滑动开始和结束时间
2. 查看帧率
- 查看滑动期间的帧率
- 识别掉帧的时间点
3. 分析主线程
- 查看主线程在滑动期间的状态
- 查找耗时操作
4. 分析渲染线程
- 查看RenderThread在滑动期间的状态
- 查找渲染瓶颈
5. 分析系统资源
- 查看CPU使用情况
- 查看内存和GC情况
```
#### 1.3 常见原因
- **主线程阻塞**:滑动时主线程执行耗时操作
- **布局复杂**:列表项布局过于复杂
- **图片加载**:滑动时加载大量图片
- **数据绑定**:滑动时频繁绑定数据
- **系统资源不足**CPU或内存不足
### 2. 动画卡顿分析
#### 2.1 问题特征
- 页面跳转动画不流畅
- UI动画卡顿
- 转场动画掉帧
#### 2.2 分析步骤
```
1. 定位动画时间段
- 查找动画开始和结束时间
- 查看动画相关的Trace标记
2. 查看帧率
- 查看动画期间的帧率
- 识别掉帧的时间点
3. 分析动画线程
- 查看动画线程状态
- 查找动画计算耗时
4. 分析渲染
- 查看渲染线程状态
- 查找渲染瓶颈
```
#### 2.3 常见原因
- **动画计算耗时**:动画计算过于复杂
- **渲染瓶颈**:渲染性能不足
- **系统资源不足**CPU或GPU不足
- **动画冲突**:多个动画同时执行
### 3. 点击卡顿分析
#### 3.1 问题特征
- 点击后响应延迟
- 点击后界面不更新
- 点击后操作不执行
#### 3.2 分析步骤
```
1. 定位点击事件
- 查找Input事件点击事件
- 找到点击时间点
2. 查看响应时间
- 从点击到界面响应的时间
- 识别响应延迟
3. 分析主线程
- 查看主线程在点击后的状态
- 查找耗时操作
4. 分析系统服务
- 查看SystemServer处理耗时
- 查看Binder调用耗时
```
#### 3.3 常见原因
- **主线程阻塞**:点击后主线程执行耗时操作
- **Binder调用延迟**:跨进程调用耗时
- **系统服务繁忙**SystemServer处理延迟
- **资源加载**:点击后加载大量资源
## 五、分析技巧与最佳实践
### 1. 分析技巧
#### 1.1 时间对齐
- 使用书签M键标记关键时间点
- 使用时间选择Shift+拖拽)选择时间范围
- 使用缩放功能W/S键调整时间范围
#### 1.2 多维度分析
- 同时查看主线程、RenderThread、系统资源
- 对比正常和异常时间段
- 使用SQL查询进行统计分析
#### 1.3 对比分析
- 对比优化前后的Trace
- 对比不同版本的性能
- 对比竞品的性能
### 2. 最佳实践
#### 2.1 录制Trace
- 明确问题场景和复现步骤
- 选择合适的录制时长
- 保持环境一致
- 多次录制确保结果一致
#### 2.2 分析Trace
- 系统化分析,按照固定流程
- 关注关键指标
- 深入分析,找到根本原因
- 记录分析过程和结果
#### 2.3 优化验证
- 优化后再次录制Trace
- 对比优化前后的性能
- 验证优化效果
- 确保没有引入新问题
### 3. 常见误区
#### 3.1 只看主线程
- **误区**:只关注主线程,忽略其他线程
- **正确**综合分析主线程、RenderThread、系统资源
#### 3.2 只看表面现象
- **误区**:只看掉帧,不分析原因
- **正确**:深入分析,找到根本原因
#### 3.3 忽略系统因素
- **误区**:只关注应用代码,忽略系统因素
- **正确**:综合考虑应用和系统因素
## 六、实际案例分析
### 案例1列表滑动卡顿
**问题描述:**
- 列表滑动时出现明显卡顿
- 滑动不跟手
- 掉帧严重
**分析过程:**
1. 录制滑动场景的Trace
2. 定位滑动时间段
3. 发现主线程在滑动期间执行了大量布局计算
4. 发现RenderThread绘制耗时过长
5. 发现CPU使用率过高
**根本原因:**
- 列表项布局过于复杂
- 滑动时频繁重新计算布局
- 绘制内容过多
**优化方案:**
- 优化列表项布局,减少层级
- 使用ViewHolder复用视图
- 减少绘制内容
- 使用硬件加速
**优化效果:**
- 滑动流畅度提升80%
- 掉帧减少90%
### 案例2页面跳转卡顿
**问题描述:**
- 页面跳转动画不流畅
- 跳转后界面响应延迟
**分析过程:**
1. 录制页面跳转的Trace
2. 定位跳转时间段
3. 发现主线程在跳转时执行了大量初始化操作
4. 发现Binder调用耗时过长
5. 发现系统服务处理延迟
**根本原因:**
- 页面跳转时同步执行初始化
- 跨进程调用过多
- 系统服务繁忙
**优化方案:**
- 延迟非关键初始化
- 优化跨进程调用
- 使用异步加载
- 优化系统服务调用
**优化效果:**
- 跳转时间减少60%
- 动画流畅度提升
## 七、总结
使用Perfetto分析应用卡顿问题的关键步骤
1. **明确问题场景**:了解卡顿的具体表现和复现步骤
2. **录制Trace**:选择合适的配置和录制方法
3. **定位卡顿时间点**:使用多种方法找到卡顿发生的时间
4. **分析主线程**:查看主线程状态,找到阻塞原因
5. **分析渲染线程**查看RenderThread状态找到渲染瓶颈
6. **分析系统资源**查看CPU、内存、GPU使用情况
7. **定位根本原因**:综合分析,找到卡顿的根本原因
8. **提供优化建议**:给出针对性的优化方案
通过系统化的分析流程,可以高效地定位和解决应用卡顿问题。
---
*最后更新2024年*

View File

@@ -83,6 +83,9 @@ nav:
- 学习笔记/基础性能问题分析ODM.md
- 学习笔记/高级性能问题分析.md
- 学习笔记/高级性能问题分析目录.md
- 学习笔记/perfeto看trace技巧.md
- 学习笔记/perfetto的使用技巧.md
- 学习笔记/看应用卡顿问题.md
- Obsidian笔记:
- Obsidian/2026-01-05 个人文档管理.md
- Obsidian/高频命令.md