首次采集缓存相关的 Trace:选择 categories、buffer 大小、时长、adb 命令示例
1. 现象识别:何时需要采集缓存 Trace
在以下场景中,缓存命中率下降是核心嫌疑之一,应当立即启动首次 Trace 采集:
| 现象 | 典型表现 | 可疑缓存层级 |
|---|---|---|
| App 启动/页面切换卡顿 | 帧率抖动 < 30fps,CPU 使用率突高但 I/O 低 | L1/L2 指令缓存、页缓存 |
| 多线程竞争场景性能劣化 | sched_switch 频繁,线程迁移导致缓存污染 | L2/L3 缓存一致性 |
| 内存访问延迟突增 | perf 采样显示大量 stall 周期 | 所有层级缓存未命中 |
| 后台任务导致前台抖动 | mm_vmscan 事件密集,页缓存被大量回收 | 页缓存 (page cache) |
判断规则:如果设备上出现“CPU 繁忙但 IPC 很低(< 0.5)”或“内存带宽高但利用率低”,说明缓存未命中开销已占主导,
必须通过 Trace 量化缓存事件。
2. 数据采集:首次缓存 Trace 参数配置
首次采集的目标是“低成本、全覆盖” —— 用最少的 categories 捕获缓存关键事件,同时保证 buffer 不丢失数据。下面从四个维度逐一展开。
2.1 选择 Categories
下表列出与缓存命中率直接相关的 Perfetto 数据源和推荐类别:
| 数据源 (DataSource) | 推荐类别 / 事件 | 覆盖的缓存信息 | 首次是否必选 |
|---|---|---|---|
| linux.perf_event | PERF_COUNT_HW_CACHE_REFERENCES (0x2) PERF_COUNT_HW_CACHE_MISSES (0x3) | CPU 缓存引用/未命中次数 | 必选 |
| linux.ftrace | ftrace_events: mm_page_alloc, mm_vmscan, sched_switch | 页缓存分配/回收、调度与缓存热度 | 必选 |
| linux.process_stats | (默认开启) | 进程内存态,辅助判断页缓存归属 | 推荐 |
| android.simpleperf | (备选) | 更细粒度的 PMU 事件矩阵 | 可选(首次暂不加) |
首次简化原则:只启用
linux.perf_event(hardware cache 事件)和 linux.ftrace(mm + sched 事件),
避免开启过多 category 导致 buffer 快速写满。
2.2 配置 Buffer 大小
缓存事件产生频率极高(尤其是 PMU 事件),buffer 过小会提前截断。参考以下阈值:
| 采集场景 | 建议 Buffer 大小 | 说明 |
|---|---|---|
| 首次快速采集(30s) | 4 MB(4096 KB) | 平衡丢失风险与资源占用 |
| 复测 / 深度分析(60s) | 8 MB(8192 KB) | 保证不丢弃 PMU 事件 |
| 长时间后台采集(120s+) | 16 MB(16384 KB)以上 | 需结合频率降低采样周期(period) |
2.3 设置采集时长
首次建议控制在 30 秒,覆盖 App 冷启动+首屏渲染+一次用户交互即可。后续可根据问题复现窗口调整:
- 30 秒:首次基线采集,风险最低。
- 60 秒:复测验证修复效果,覆盖更多执行路径。
- 120 秒:排查偶发性抖动,需要配合触发脚本。
2.4 adb 命令执行示例
下方命令使用 Perfetto 文本配置格式,同时启用 PMU 缓存事件和关键 ftrace 事件,输出到设备 /data/local/tmp/:
adb shell perfetto \
-c - -o /data/local/tmp/cache_trace.perfetto \
--txt << 'EOF'
buffers: {
size_kb: 4096
}
data_sources: [
{
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "mm_page_alloc"
ftrace_events: "mm_vmscan/mm_vmscan_lru_isolate"
ftrace_events: "sched/sched_switch"
}
}
},
{
config {
name: "linux.perf_event"
perf_event_config {
timebase: {
period: 1000
}
events: [
{
type: 0
config: 0x2 # PERF_COUNT_HW_CACHE_REFERENCES
},
{
type: 0
config: 0x3 # PERF_COUNT_HW_CACHE_MISSES
},
{
type: 0
config: 0x0 # PERF_COUNT_HW_CPU_CYCLES (用于计算miss ratio)
}
]
}
}
}
]
duration_ms: 30000
flush_period_ms: 5000
EOF
权限提醒:PMU 事件采集需要内核
perf_event_open 权限。如果设备非 root,确保目标 App 有
profileable 或 debuggable 标识。否则会看到 permission denied 错误,
此时可改用 android.simpleperf 数据源(需 API level 29+)。
3. 原因定位:首次采集后的数据有效性验证
采集完成后,将 Trace 文件 pull 到本地并用 Perfetto UI 打开,按以下规则快速判断数据是否可用:
| 验证项 | 正常标志 | 异常标志 | 动作 |
|---|---|---|---|
| PMU 事件存在 | 在 UI 中搜索“perf_event”能看到 cache-misses 计数 | 完全无 PMU 数据 | 检查权限或改用 simpleperf |
| buffer 未溢出 | trace 开头无“buffer overwritten”警告 | 出现“lost events”标记 | 增大 buffer 或缩短时长 |
| ftrace 事件完整 | mm_page_alloc、sched_switch 可展开 | 事件稀疏或集中在最后几秒 | 增加 buffer 或减少其他 category |
首次采集最常见的问题是 buffer 过小导致 PMU 事件丢失。如果发现“lost events”标记,优先将 buffer 翻倍至 8 MB,而不是减少 category。
4. 修复动作:调整采集参数
根据验证结果,对照下表进行针对性调整:
| 问题 | 调整方案 | 预期效果 |
|---|---|---|
| buffer 溢出 | size_kb 翻倍(4096 → 8192) | 减少 80% 以上丢失事件 |
| PMU 事件无数据 | 改用 android.simpleperf 数据源,配置相同 event id | 获取等效 PMU 计数 |
| ftrace 事件过多 | 移除 mm_vmscan 子事件,只保留 mm_page_alloc | 降低 ftrace 写入压力 |
| 时长不足以复现问题 | duration_ms 延长至 60000,同时 period 增大到 2000 | 覆盖完整问题窗口 |
5. 复测:验证新配置
使用调整后的参数重新执行同一条 adb 命令,再次执行 原因定位 中的验证项。重点确认:
- PMU 事件覆盖整个时长(而非仅开头几秒)。
- 缓存未命中率(misses / references)在问题时段有明显波动。
- ftrace 事件包含 2~3 次完整的 mm 操作序列。
6. 预防:建立标准采集模板
将已验证的配置保存为 cache_first_trace.cfg,纳入团队工具库。以后每次缓存问题排查都以此模板为起点,避免重复调参:
# cache_first_trace.cfg —— 首次缓存 Trace 标准模板
buffers: {
size_kb: 4096
}
data_sources: [
{
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "mm_page_alloc"
ftrace_events: "sched/sched_switch"
}
}
},
{
config {
name: "linux.perf_event"
perf_event_config {
timebase: { period: 1000 }
events: [
{ type: 0 config: 0x2 }
{ type: 0 config: 0x3 }
{ type: 0 config: 0x0 }
]
}
}
}
]
duration_ms: 30000
flush_period_ms: 5000
每次复用模板时,只需修改 duration_ms 和 size_kb 即可适配不同场景。
动手建议:将上方 adb 命令直接复制到终端执行,即可获得第一份缓存 Trace。然后用 Perfetto UI (ui.perfetto.dev) 打开,
搜索
perf_event 和 mm_page_alloc 验证数据是否完整。这是后续所有缓存分析的起点。