编辑
2026-04-19
记录知识
0

目录

下载sdk
编译构建
example程序
example_console程序
examplecustomdata_source 程序
examplestartuptrace程序
examplesystemwide程序
个人技巧分享
1. 缓冲区大小估算
2. 多缓冲区策略
3. 长时间追踪
总结

在<性能工具-Perfetto-调度轨迹抓取(3)>介绍了通过perfetto轻松的抓取系统的调度轨迹,本文介绍一个更产品和的办法,通过perfetto sdk来抓取你想要的信息

下载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

解释如下

二进制后端输出文件适用场景
examplekInProcessexample.pftrace基础示例,学习 SDK 基本用法
example_consolekInProcess无(输出到终端)调试,直接在终端看事件流
example_custom_data_sourcekInProcessexample_custom_data_source.pftrace自定义数据源格式
example_startup_tracekSystemexample_startup_trace.pftrace采集进程启动早期事件
example_system_widekSystem无(由外部 perfetto CLI 写入)结合 ftrace/heap 做系统级分析

下面逐一介绍这些examples的功能

example程序

这是最基础的示例,直接在代码中嵌入 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::Initializeperfetto::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文件

example_console程序

这个程序不会生成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

example_custom_data_source 程序

如果我们在定制开发的时候,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的格式包

example_startup_trace程序

这个程序用来采集进程启动早期的事件(在 TracingSession 建立之前),特别适合分析 init 阶段性能问题。

我们看看其核心代码

cpp
void 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 Event — 在 TracingSession 建立前写入
  • Main Event — 在 TracingSession 建立后写入

两个事件都正确记录,证明 startup tracing 功能正常。

example_system_wide程序

对于perfetto的分析,我们通常需要按照事件触发的方式,这也就意味着并不需要对程序的event进行常开,那example_system_wide程序的作用就是默认不采集,但是一直监听等待traced守护进程的事件,如果traced的事件触发,那么就会随着系统级的ftrace一同采集。这样的好处如下

  • 降低cpu开销
  • 按照特定事件触发
  • 减少采集数据的大小
  • 更精准的提供数据

下面开始演示

我们需要提供一个支持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 混合的日志信息

image.png

同时,我们也可以通过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)

个人技巧分享

1. 缓冲区大小估算

  • 经验法则:每秒约 1-10 MB 的 trace 数据(取决于开启的数据源)
  • 开启 ftrace 调度事件:约 2-5 MB/s
  • 加上 ATrace:额外 0.5-2 MB/s
  • 建议:至少设置 64 MB(size_kb: 65536),复杂场景使用 128-256 MB

2. 多缓冲区策略

将高频和低频数据源分配到不同缓冲区:

protobuf
# 高频:ftrace 事件 buffers { size_kb: 131072 fill_policy: RING_BUFFER } # 低频:进程统计 buffers { size_kb: 8192 fill_policy: RING_BUFFER }

3. 长时间追踪

使用 write_into_file 配合较小的缓冲区可以追踪数小时:

protobuf
write_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灵活可变的采集方式,接下来我们聚焦在可视化上的分析角度继续演示