在<性能工具-Perfetto-调度轨迹抓取(3)>介绍了通过perfetto轻松的抓取系统的调度轨迹,本文介绍一个更产品和的办法,通过perfetto sdk来抓取你想要的信息
perfetto分为C和C++版本,我们可以直接下载即可
C版本的
https://github.com/google/perfetto/releases/download/v54.0/perfetto-c-sdk-src.zip
C++版本的
https://github.com/google/perfetto/releases/download/v54.0/perfetto-cpp-sdk-src.zip
本文基于C版本介绍
当然我们也可以直接基于perfetto 的github仓库来抽取sdk
https://github.com/google/perfetto/tree/main/include
我们直接可以编译perfetto给我的示例程序
首先将sdk链接到example处
ln -s $(pwd)/../../../c-sdk sdk
然后修改一下CMakeLists.txt的配置
diff --git a/examples/sdk/CMakeLists.txt b/examples/sdk/CMakeLists.txt index 00508dc34e..1e90607c25 100644 --- a/examples/sdk/CMakeLists.txt +++ b/examples/sdk/CMakeLists.txt @@ -22,8 +22,8 @@ find_package(Threads) # Define a static library for Perfetto. In this example, we expect the SDK # (perfetto.cc and perfetto.h) to live in the top level sdk/ directory. -include_directories(../../sdk) -add_library(perfetto STATIC ../../sdk/perfetto.cc) +include_directories(sdk) +add_library(perfetto STATIC sdk/perfetto.cc) # Link the library to the main executables. add_executable(example example.cc trace_categories.cc)
直接编译
# cmake -B build && cmake --build build -- Configuring done (0.0s) -- Generating done (0.0s) -- Build files have been written to: /root/claude/perfetto/examples/sdk/build [ 6%] Building CXX object CMakeFiles/perfetto.dir/sdk/perfetto.cc.o /root/claude/perfetto/examples/sdk/sdk/perfetto.cc:49545:13: warning: #pragma system_header ignored outside include file 49545 | #pragma GCC system_header | ^~~~~~~~~~~~~ [ 13%] Linking CXX static library libperfetto.a [ 13%] Built target perfetto [ 20%] Building CXX object CMakeFiles/example.dir/example.cc.o [ 26%] Building CXX object CMakeFiles/example.dir/trace_categories.cc.o [ 33%] Linking CXX executable example [ 33%] Built target example [ 40%] Building CXX object CMakeFiles/example_system_wide.dir/example_system_wide.cc.o [ 46%] Building CXX object CMakeFiles/example_system_wide.dir/trace_categories.cc.o [ 53%] Linking CXX executable example_system_wide [ 53%] Built target example_system_wide [ 60%] Building CXX object CMakeFiles/example_custom_data_source.dir/example_custom_data_source.cc.o [ 66%] Linking CXX executable example_custom_data_source [ 66%] Built target example_custom_data_source [ 73%] Building CXX object CMakeFiles/example_console.dir/example_console.cc.o [ 80%] Building CXX object CMakeFiles/example_console.dir/trace_categories.cc.o [ 86%] Linking CXX executable example_console [ 86%] Built target example_console [ 93%] Building CXX object CMakeFiles/example_startup_trace.dir/example_startup_trace.cc.o [100%] Linking CXX executable example_startup_trace [100%] Built target example_startup_trace
编译完成了有四个产物,如下
example example_console example_custom_data_source example_startup_trace example_system_wide
解释如下
| 二进制 | 后端 | 输出文件 | 适用场景 |
|---|---|---|---|
example | kInProcess | example.pftrace | 基础示例,学习 SDK 基本用法 |
example_console | kInProcess | 无(输出到终端) | 调试,直接在终端看事件流 |
example_custom_data_source | kInProcess | example_custom_data_source.pftrace | 自定义数据源格式 |
example_startup_trace | kSystem | example_startup_trace.pftrace | 采集进程启动早期事件 |
example_system_wide | kSystem | 无(由外部 perfetto CLI 写入) | 结合 ftrace/heap 做系统级分析 |
下面逐一介绍这些examples的功能
这是最基础的示例,直接在代码中嵌入 trace 并输出到 pftrace文件上,其核心代码如下
#include "trace_categories.h" void InitializePerfetto() { perfetto::TracingInitArgs args; args.backends = perfetto::kInProcessBackend; // 进程内 tracing perfetto::Tracing::Initialize(args); perfetto::TrackEvent::Register(); } std::unique_ptr<perfetto::TracingSession> StartTracing() { perfetto::TraceConfig cfg; cfg.add_buffers()->set_size_kb(1024); auto* ds_cfg = cfg.add_data_sources()->mutable_config(); ds_cfg->set_name("track_event"); auto session = perfetto::Tracing::NewTrace(); session->Setup(cfg); session->StartBlocking(); return session; } void StopTracing(std::unique_ptr<perfetto::TracingSession> session) { perfetto::TrackEvent::Flush(); session->StopBlocking(); std::vector<char> data = session->ReadTraceBlocking(); std::ofstream("example.pftrace", std::ios::binary).write(data.data(), data.size()); } int main() { InitializePerfetto(); auto session = StartTracing(); TRACE_EVENT("rendering", "DrawPlayer", "player_number", 1); std::this_thread::sleep_for(std::chrono::milliseconds(100)); TRACE_COUNTER("rendering", "Framerate", 120); StopTracing(std::move(session)); }
首先通过 perfetto::Tracing::Initialize和 perfetto::TrackEvent::Register 注册,其通过TRACE_EVENT和TRACE_COUNTER获取了trace,然后输出为example.pftrace。下面演示一下
# ./example [923.099] perfetto.cc:64891 Configured tracing session 1, #sources:1, duration:0 ms, #buffers:1, total buffer size:1024 KB, total sessions:1, uid:0 session name: "" [925.102] example.cc:80 Trace written in example.pftrace file. To read this trace in text form, run `./tools/traceconv text example.pftrace`
此时我们打开example.pftrace文件

这个程序不会生成pftrace文件,而是直接打印在console上,其核心代码如下
void InitializePerfetto() { perfetto::TracingInitArgs args; args.backends = perfetto::kInProcessBackend; perfetto::Tracing::Initialize(args); perfetto::TrackEvent::Register(); perfetto::ConsoleInterceptor::Register(); // 注册控制台拦截器 }
现在运行如下
# ./example_console [018.957] perfetto.cc:64891 Configured tracing session 1, #sources:1, duration:0 ms, #buffers:1, total buffer size:1024 KB, total sessions:1, uid:0 session name: "" [ 0.000] example_console rende DrawGame { [ 0.000] example_console rende - DrawPlayer {(player_number:1) [ 0.501] example_console rende - } DrawPlayer +500ms [ 0.501] example_console rende - DrawPlayer {(player_number:2) [ 1.001] example_console rende - } DrawPlayer +500ms [ 1.001] example_console rende } DrawGame +1000ms
如果我们在定制开发的时候,perfetto提供的track_event数据不满足要求,那么可以自定义数据,自定义数据源可以发任意 protobuf 数据,只要你自己定义 schema。 然后自行由第三方程序解析。
开始演示
运行测试程序
# ./example_custom_data_source [076.202] perfetto.cc:64891 Configured tracing session 1, #sources:1, duration:0 ms, #buffers:1, total buffer size:1024 KB, total sessions:1, uid:0 session name: "" [076.203] ustom_data_source.cc:99 Trace written in example_custom_data_source.pftrace file. To read this trace in text form, run `./tools/traceconv text example_custom_data_source.pftrace`
注意,此时example_custom_data_source.pftrace并不能被perfetto解析,这个用作第三方程序自定义解析
我们可以将其转换成格式化的txt文件显示,如下
# ./traceconv text example_custom_data_source.pftrace packet { timestamp: 2394076202757473 trusted_uid: 0 trusted_packet_sequence_id: 1 service_event { tracing_disabled: true } } ......
这是protobuf的格式包
这个程序用来采集进程启动早期的事件(在 TracingSession 建立之前),特别适合分析 init 阶段性能问题。
我们看看其核心代码
cppvoid InitializePerfetto() {
perfetto::TracingInitArgs args;
args.backends = perfetto::kSystemBackend; // 必须用系统后端
perfetto::Tracing::Initialize(args);
perfetto::DataSourceDescriptor dsd;
dsd.set_name("com.example.startup_trace");
MyDataSource::Register(dsd);
}
// 在 TracingSession 创建前写入事件
void StartStartupTracing() {
perfetto::Tracing::SetupStartupTracingOpts args;
args.backend = perfetto::kSystemBackend;
perfetto::Tracing::SetupStartupTracingBlocking(GetTraceConfig(), args);
}
int main() {
InitializePerfetto();
StartStartupTracing(); // 在 session 创建前调用
// 这些事件在 session 建立前就写入
CustomDataSource::Trace([](auto& ctx) {
ctx.NewTracePacket()->set_timestamp(41);
});
auto session = StartTracing();
// session 建立后的事件
CustomDataSource::Trace([](auto& ctx) {
ctx.NewTracePacket()->set_timestamp(42);
});
}
这里StartStartupTracing会在程序启动前写入事件
下面开始演示
# mkdir -p /run/perfetto # traced --background # ./example_startup_trace [409.984] le_startup_trace.cc:102 Trace written in example_startup_trace.pftrace file. To read this trace in text form, run `./tools/traceconv text example_startup_trace.pftrace` # traceconv text example_startup_trace.pftrace
我们看到startup事件
packet { for_testing { str: "Startup Event" } trusted_packet_sequence_id: 2 trusted_pid: 1137362 } --- packet { for_testing { str: "Main Event" } trusted_packet_sequence_id: 2 trusted_pid: 1137362 }
两个事件都正确记录,证明 startup tracing 功能正常。
对于perfetto的分析,我们通常需要按照事件触发的方式,这也就意味着并不需要对程序的event进行常开,那example_system_wide程序的作用就是默认不采集,但是一直监听等待traced守护进程的事件,如果traced的事件触发,那么就会随着系统级的ftrace一同采集。这样的好处如下
下面开始演示
我们需要提供一个支持track_event的data source,如下
buffers { size_kb: 63488 } # ============================================================ # Data Source 1: Ftrace CPU 调度事件 # ============================================================ data_sources { config { name: "linux.ftrace" ftrace_config { ftrace_events: "sched/sched_switch" ftrace_events: "sched/sched_wakeup" buffer_size_kb: 16384 } } } # ============================================================ # Data Source 2: Track Event(接收 example_system_wide 的事件) # ============================================================ data_sources { config { name: "track_event" } } # ============================================================ # Trace 配置 # ============================================================ duration_ms: 10000 write_into_file: true flush_timeout_ms: 30000
然后我们通过tracebox触发一次调度轨迹的trace事件,如下
./perfetto --txt -c /tmp/config_with_track_event.pbtx -o /tmp/example.pftrace
根据上面的分析,example_system_wide会默认阻塞,等待系统级的trace进行触发,我们运行它
# ./example_system_wide [499.783] ample_system_wide.cc:78 Waiting for tracing to start...
此时我们可以看到 ftrace 和 track_event 混合的日志信息

同时,我们也可以通过perfetto命令查看到启动了哪些data source,很明显trace_event是打开的
# ./perfetto --query --long PRODUCER PROCESSES CONNECTED: ID PID UID FLAGS NAME SDK == === === ===== ==== === 1 1156509 0 perfetto.traced_probes Perfetto v54.0-7616314b3 (7616314b391aa5fe69aa57705216c40727eb0160)
size_kb: 65536),复杂场景使用 128-256 MB将高频和低频数据源分配到不同缓冲区:
protobuf# 高频:ftrace 事件 buffers { size_kb: 131072 fill_policy: RING_BUFFER } # 低频:进程统计 buffers { size_kb: 8192 fill_policy: RING_BUFFER }
使用 write_into_file 配合较小的缓冲区可以追踪数小时:
protobufwrite_into_file: true file_write_period_ms: 2500 max_file_size_bytes: 2147483648 # 2GB 上限 buffers { size_kb: 32768 fill_policy: RING_BUFFER }
本文详细的演示了perfetto的sdk的使用办法,在高性能的程序调试过程中,经常需要支持在perfetto上埋点然后触发采集,这样能够准确有效的定位核心性能问题。
perfetto的使用非常简单,只要心里记住他们是生产者消费者模型即可。我们配好自己的data sourc(生产者),配好自己的输出方式(消费者)就能高效的使用perfetto了
现在我们知道了perfetto灵活可变的采集方式,接下来我们聚焦在可视化上的分析角度继续演示