学习记录:Perfetto工具_5 CPU Profiling了解

30 阅读11分钟

原文

Recording performance counters and CPU profiling with Perfetto - Perfetto Tracing Docs

📘 Tutorials(教程)

原文中文解释
Full-Stack Perfetto全栈 Perfetto —— 涵盖从系统底层到应用层的完整跟踪教程。
├─ System Tracing系统跟踪 —— 如何记录整个系统的活动(内核、CPU、进程等)。
└─ In-App Tracing应用内跟踪 —— 如何在 Android 应用中插入自定义跟踪点。
Memory Profiling内存性能剖析 —— 使用 Perfetto 分析内存使用情况(如 malloc 调用、Java 堆等)。
CPU ProfilingCPU 性能剖析 —— 使用 Perfetto 分析 CPU 使用率、函数耗时等。

下半部分是该文档翻译,主要描述 CPU Profiling 内存性能剖析等等

翻译

使用 Perfetto 记录性能计数器与 CPU 性能剖析

在本指南中,您将学习如何:

  • 使用 Perfetto 记录 CPU 性能剖析与性能计数器。
  • 收集调用栈性能剖析以识别性能瓶颈。
  • 在 Perfetto UI 中可视化和分析 CPU 性能剖析。

在 Linux 和 Android 上,Perfetto 可以记录每 CPU 的性能计数器,例如已执行指令数或缓存未命中次数等硬件事件。此外,Perfetto 还可配置为基于这些性能计数器对正在运行的进程的调用栈进行采样。这两种模式均类似于 perf 工具中的 perf record 命令,并使用相同的系统调用(perf_event_open)。

如果您仅对性能剖析(即火焰图)感兴趣,可直接跳转到“收集调用栈性能剖析”一节。

我来为您翻译这段关于使用性能计数器收集追踪的内容:

使用性能计数器收集追踪

性能计数器(perf counters)的录制使用标准的 Perfetto 配置 protobuf 进行定义,并且可以与其他数据源(如 ftrace)自由组合。这允许生成混合追踪,在单一时间线上同时显示采样的计数器值和其他追踪数据(例如进程调度)。

数据源配置定义了以下内容:

配置项说明
时基(Timebase)或组领导者(group leader)主要计数的事件。该事件在每个 CPU 上单独计数。
采样周期/频率计数器的采样频率。可以是固定周期(例如每 1000 个事件)或频率(例如每秒 100 次)。
跟随者(Followers)需要记录的任何额外计数器。这些计数器与时基事件同时被快照采样。

一个追踪配置可以为单独的采样组定义多个 "linux.perf" 数据源。

⚠️ 注意:需要小心不要超过平台的 PMU(性能监控单元)容量,尤其是在计数硬件事件时。否则内核会进行多路复用(反复切换进/出事件组),导致计数不足(underestimation)。更多信息请参阅 perfwiki 页面

1.配置示例

此配置为每个 CPU 定义了一个包含三个计数器的组。使用定时器事件(SW_CPU_CLOCK)作为组主导事件,以提供稳定的采样速率。每个采样还额外包含自跟踪开始以来的 CPU 周期数(HW_CPU_CYCLES)和已执行指令数(HW_INSTRUCTIONS)。

duration_ms: 10000          # 追踪持续时间:10 秒

buffers: {
  size_kb: 40960            # 缓冲区大小:40 MB(40960 KB)
  fill_policy: DISCARD      # 缓冲区满时的策略:丢弃旧数据(而非阻塞)
}

# 性能计数器采样:每 CPU 的指令数和周期数
data_sources {
  config {
    name: "linux.perf"                    # 数据源名称:Linux perf 事件
    perf_event_config {
      timebase {                          # 时基事件(主事件/组领导者)
        frequency: 1000                   # 采样频率:每秒 1000 次(即每 1ms)
        counter: SW_CPU_CLOCK             # 计数器类型:软件 CPU 时钟
        timestamp_clock: PERF_CLOCK_MONOTONIC  # 时间戳时钟:单调时钟(不受系统时间调整影响)
      }
      followers { counter: HW_CPU_CYCLES }     # 跟随者计数器 1:硬件 CPU 周期
      followers { counter: HW_INSTRUCTIONS }   # 跟随者计数器 2:硬件指令数
    }
  }
}

# 通过 ftrace 包含调度数据
data_sources: {
  config: {
    name: "linux.ftrace"                  # 数据源名称:Linux ftrace
    ftrace_config: {
      ftrace_events: "sched/sched_switch"    # 调度事件:进程/线程切换事件(谁在运行)
      ftrace_events: "sched/sched_waking"    # 调度事件:进程/线程唤醒事件(谁被唤醒)
    }
  }
}

# 通过 procfs 包含进程名称和分组信息
data_sources: {
  config: {
    name: "linux.process_stats"           # 数据源名称:Linux 进程统计
    process_stats_config {
      scan_all_processes_on_start: true   # 启动时扫描所有进程:获取完整的进程列表和名称
    }
  }
}

在 UI 中展开 "Perf Counters" 轨道组后,应该看起来类似于以下内容。计数器轨道默认以计数速率的形式显示数值。

image.png 计数器数据可以通过以下方式查询:

select ts, cpu, name, value 
from counter c join perf_counter_track pct on (c.track_id = pct.id) 
order by 1, 2 asc

2.录制说明

Android(命令行)前置条件:

主机上已安装 ADB。
运行 Android 15+ 的设备,通过 USB 连接到主机,且已授权 ADB 调试。
从 perfetto 仓库下载 tools/record_android_trace Python 脚本。该脚本会自动将配置推送到设备、调用 perfetto、从设备拉取生成的追踪文件,并在 UI 中打开它。

curl -LO https://raw.githubusercontent.com/google/perfetto/main/tools/record_android_trace
假设上面的示例配置已保存为 /tmp/config.txtpb,

开始录制:

python3 record_android_trace -c /tmp/config.txtpb -o /tmp/trace.pb

录制将在 10 秒后自动停止(由配置中的 duration_ms 设置),也可以按 Ctrl+C 提前停止。停止后,脚本应会自动在 perfetto UI 中打开该追踪文件。

收集调用栈配置文件

计数器录制也可以配置为包含在计数器采样时刻被中断的进程的调用栈(相互调用的函数帧列表)。这是通过要求内核在每个样本中记录额外状态(用户空间寄存器状态、栈顶内存)来实现的,然后在分析器中对调用栈进行展开(unwinding)和符号化(symbolising)。展开操作在进程外部进行,无需对被分析的进程进行任何插桩或注入库。

要启用调用栈分析,请在数据源配置中设置 callstack_sampling 字段。注意,采样仍将按 CPU 进行,但您可以设置 scope 字段,使分析器仅对匹配的进程展开调用栈(这反过来有助于防止分析器因展开运行时开销而过载)。

1.配置示例

以下是一个基于时间进行周期性采样的配置示例(即每 CPU 定时器领导者),仅当具有给定名称的进程正在运行时才展开调用栈。

通过更改时基(timebase),您可以改为在其他事件上捕获调用栈,例如,您可以通过将 "sched/sched_waking" 设置为跟踪点时基,来查看进程唤醒其他线程时的调用栈。

Android 注意:该示例使用 "com.android.settings" 作为示例,但要成功进行调用栈采样,应用必须在清单中声明为 profileabledebuggable(或者您必须使用 Android 操作系统的可调试版本)。

duration_ms: 10000          # 追踪持续时间:10 秒

buffers: {
  size_kb: 40960            # 缓冲区大小:40 MB(40960 KB)
  fill_policy: DISCARD      # 缓冲区满时的策略:丢弃旧数据(而非阻塞)
}

# 每 CPU 周期性采样,仅在 "com.android.settings" 运行时展开调用栈
data_sources {
  config {
    name: "linux.perf"                    # 数据源名称:Linux perf 事件
    perf_event_config {
      timebase {                          # 时基事件(主事件/组领导者)
        counter: SW_CPU_CLOCK             # 计数器类型:软件 CPU 时钟
        frequency: 100                    # 采样频率:每秒 100 次(即每 10ms)
        timestamp_clock: PERF_CLOCK_MONOTONIC  # 时间戳时钟:单调时钟(不受系统时间调整影响)
      }
      callstack_sampling {                # 调用栈采样配置
        scope {                           # 作用域:限制哪些进程进行调用栈展开
          target_cmdline: "com.android.settings"  # 目标进程:仅匹配 "com.android.settings" 包名
        }
        kernel_frames: true               # 是否包含内核帧:true 表示同时采集内核态调用栈
      }
    }
  }
}

# 通过 ftrace 包含调度数据
data_sources: {
  config: {
    name: "linux.ftrace"                  # 数据源名称:Linux ftrace
    ftrace_config: {
      ftrace_events: "sched/sched_switch"    # 调度事件:进程/线程切换事件(谁在运行)
      ftrace_events: "sched/sched_waking"    # 调度事件:进程/线程唤醒事件(谁被唤醒)
    }
  }
}

# 通过 procfs 包含进程名称和分组信息
data_sources: {
  config: {
    name: "linux.process_stats"           # 数据源名称:Linux 进程统计
    process_stats_config {
      scan_all_processes_on_start: true   # 启动时扫描所有进程:获取完整的进程列表和名称
    }
  }
}

2.录制说明

Android(命令行)前置条件:

主机上已安装 ADB。 运行 Android 15+ 的设备,通过 USB 连接到主机,且已授权 ADB 调试。 可分析(Profileable)或可调试(Debuggable)的应用。如果您运行的是 Android 的 "user" 版本(而非 "userdebug" 或 "eng"),则您的应用需要在其清单中标记为 profileable 或 debuggable。 对于 Android,tools/cpu_profile 辅助 Python 脚本简化了追踪配置的构建,并具有额外的选项用于配置文件的后符号化(针对没有符号信息的库)以及转换为更适合纯火焰图可视化的 pprof 格式。可以通过以下方式下载:

curl -LO raw.githubusercontent.com/google/perf… 使用基于时间的周期性采样开始录制(即每 CPU 定时器领导者),仅当具有给定名称的进程正在运行时才展开调用栈。注意,非原生调用栈的展开开销较大,因此我们建议将采样频率保持在每 CPU 200 Hz 以下。

python3 cpu_profile -n com.android.example -f 100 可以通过按 Ctrl+C 停止录制。然后脚本会打印出输出文件所在的 /tmp/ 路径,该目录中的 raw-trace 文件可以在 Perfetto UI 中打开,而 profile.*.pb 是按进程聚合的配置文件,采用 "pprof" 文件格式。

更多选项请参阅 cpu_profile --help,特别是 -c 选项允许您提供自己的 textproto 配置,同时利用脚本化的录制和输出转换功能。

缺失的符号和反混淆 如果您的配置文件缺少原生库的函数名,但您可以访问带有符号数据的调试版本库,您可以按照这些说明指示 cpu_profile 脚本在主机上对配置文件进行符号化,同时替换脚本名称。

3.在 Perfetto UI 中可视化配置文件

在 UI 中,调用栈样本将显示为时间轴上的瞬时事件,位于被采样进程的进程轨道组内。每个被采样的线程都有一个轨道,以及一个合并该进程所有样本的单一轨道。通过选择带有 perf 样本的时间区域,底部窗格将显示所选调用栈的动态火焰图视图。

image.png 样本数据也可以通过 SQL 从 perf_sample 表中查询。

4.查询追踪

除了在时间轴上可视化追踪之外,Perfetto 还支持使用 SQL 查询追踪。最简单的方法是使用 UI 中直接提供的查询引擎。

a. 在 Perfetto UI 中,点击左侧菜单中的 "Query (SQL)" 标签页。

image.png

b. 这将打开一个分为两部分的窗口。您可以在上半部分编写 PerfettoSQL 查询,在下半部分查看结果。

image.png

c. 然后您可以使用 Ctrl/Cmd + Enter 执行查询:

例如,通过运行:

INCLUDE PERFETTO MODULE linux.perf.samples;

SELECT
  -- 调用栈的 ID。在此上下文中,调用栈是指
  -- 从当前帧到根帧的唯一帧集合。
  id,
  
  -- 此调用栈的父调用栈 ID。
  parent_id,
  
  -- 此调用栈帧的函数名称。
  name,
  
  -- 包含该帧的映射名称。这可以是
  -- 原生二进制文件、库、JAR 或 APK。
  mapping_name,
  
  -- 包含该函数的文件名称。
  source_file,
  
  -- 函数在文件中的行号。
  line_number,
  
  -- 以此函数作为叶子帧(最底层调用)的样本数量。
  self_count,
  
  -- 此函数出现在调用栈任何位置的样本数量。
  cumulative_count
  
FROM linux_perf_samples_summary_tree;

5.替代方案

Perfetto 的性能分析实现专为连续(流式)采集而构建,因此在短时长、高频率分析场景下的优化较少。如果您只需要聚合的火焰图,可以考虑 Android 上的 simpleperf 和 Linux 上的 perf。这些工具更加成熟,并且针对这种使用场景提供了更简单的用户界面。

下一步

现在您已经录制了第一个 CPU 配置文件,可以探索更多高级主题:

更多关于追踪分析的内容

  • Perfetto UI:了解追踪查看器的所有功能。
  • 使用 SQL 进行追踪分析:了解如何使用 Trace Processor 和 PerfettoSQL 分析追踪。

与其他数据源结合 您还可以在 CPU 采样的同一时间线上包含其他数据源,以获得系统性能的更完整视图。

  • 调度事件:获取关于哪些线程在哪些 CPU 上运行的详细信息。
  • CPU 频率:查看 CPU 频率随时间的变化情况。
  • 系统调用:追踪系统调用的进入和退出。