[性能优化]perfetto - Tracing SDK
Perfetto Tracing SDK 是一个 C++17 库,它允许用户空间应用程序发出跟踪事件,并向 Perfetto 跟踪中添加更多特定于应用程序的上下文。
使用 Tracing SDK 时,需要考虑两个主要方面:
- 您是否只对来自您自己应用程序的跟踪事件感兴趣,还是希望收集全栈跟踪,这些跟踪将应用程序跟踪事件与系统跟踪事件(如调度器跟踪、系统调用或任何其他 Perfetto 数据源)叠加在一起。
- 对于特定于应用程序的跟踪,您是否需要跟踪简单的时间线事件类型(例如,切片、计数器),还是需要定义具有自定义强类型架构的复杂数据源(例如,将您应用程序子系统的状态转储到跟踪中)。
对于仅针对 Android 的检测,如果android.os.Trace(SDK)/ATrace_*(NDK)足以满足您的用例,建议继续使用它们。基于atrace的检测在Perfetto中得到完全支持。
Getting started
要使用 Client API,请首先检查最新的 SDK 发行版:
git clone https://android.googlesource.com/platform/external/perfetto -b v47.0
SDK 包含两个文件,sdk/perfetto.h 和 sdk/perfetto.cc。这两个文件是 Client API 的集合,旨在轻松集成到现有的构建系统中。源代码是自包含的,并且仅需要一个符合 C++17 标准的库。
例如,若要将 SDK 添加到 CMake 项目中,请编辑您的 CMakeLists.txt 文件:
cmake_minimum_required(VERSION 3.13)
project(PerfettoExample)
find_package(Threads)
# Define a static library for Perfetto.
include_directories(perfetto/sdk)
add_library(perfetto STATIC perfetto/sdk/perfetto.cc)
# Link the library to your main executable.
add_executable(example example.cc)
target_link_libraries(example perfetto ${CMAKE_THREAD_LIBS_INIT})
if (WIN32)
# The perfetto library contains many symbols, so it needs the big object
# format.
target_compile_options(perfetto PRIVATE "/bigobj")
# Disable legacy features in windows.h.
add_definitions(-DWIN32_LEAN_AND_MEAN -DNOMINMAX)
# On Windows we should link to WinSock2.
target_link_libraries(example ws2_32)
endif (WIN32)
# Enable standards-compliant mode when using the Visual Studio compiler.
if (MSVC)
target_compile_options(example PRIVATE "/permissive-")
endif (MSVC)
接下来,在您的程序中初始化 Perfetto:
#include <perfetto.h>
int main(int argc, char** argv) {
perfetto::TracingInitArgs args;
// The backends determine where trace events are recorded. You may select one
// or more of:
// 1) The in-process backend only records within the app itself.
args.backends |= perfetto::kInProcessBackend;
// 2) The system backend writes events into a system Perfetto daemon,
// allowing merging app and system events (e.g., ftrace) on the same
// timeline. Requires the Perfetto `traced` daemon to be running (e.g.,
// on Android Pie and newer).
args.backends |= perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
}
现在,您已经准备好使用跟踪事件来检测您的应用程序了。
自定义数据源与跟踪事件
SDK 提供了两种抽象层来注入跟踪数据,它们建立在彼此之上,并在代码复杂性和表达能力之间做出权衡:跟踪事件和自定义数据源。
1)跟踪事件(track events)
当处理特定于应用程序的跟踪时,建议使用跟踪事件,因为它们处理了许多细微差别(例如,线程安全性 - thread safety、刷新 - flushing、字符串内联 - string interning)。跟踪事件(track events)是基于代码库中的简单 TRACE_EVENT 注释标签的时间限定事件(例如,切片、计数器),如下所示:
#include <perfetto.h>
PERFETTO_DEFINE_CATEGORIES(
perfetto::Category("rendering")
.SetDescription("Events from the graphics subsystem"),
perfetto::Category("network")
.SetDescription("Network upload and download statistics"));
PERFETTO_TRACK_EVENT_STATIC_STORAGE();
...
int main(int argc, char** argv) {
...
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();
}
...
void LayerTreeHost::DoUpdateLayers() {
TRACE_EVENT("rendering", "LayerTreeHost::DoUpdateLayers");
...
for (PictureLayer& pl : layers) {
TRACE_EVENT("rendering", "PictureLayer::Update");
pl.Update();
}
}
在用户界面中的呈现方式如下:
跟踪事件是最佳的默认选项,并且以极低的复杂性满足大多数跟踪用例。
要将您的新跟踪事件包含在跟踪中,请确保在跟踪配置中包含 track_event 数据源,并列出启用和禁用的类别。
data_sources {
config {
name: "track_event"
track_event_config {
enabled_categories: "rendering"
disabled_categories: "*"
}
}
}
2) 自定义数据源(Custom data sources)
对于大多数用途,跟踪事件是跟踪应用程序的最直接方式。然而,在某些罕见情况下,它们可能不够灵活,例如,当数据不符合跟踪的概念,或者数据量非常大,需要强类型架构来最小化每个事件的大小时。在这种情况下,您可以为 Perfetto 实现一个自定义数据源。
与跟踪事件不同,当使用自定义数据源时,您还需要在跟踪处理器中进行相应的更改,以便能够导入您的数据格式。
自定义数据源是 perfetto::DataSource 的子类。Perfetto 会自动为每个处于活动状态的跟踪会话(通常只有一个)创建该类的一个实例。
class CustomDataSource : public perfetto::DataSource<CustomDataSource> {
public:
void OnSetup(const SetupArgs&) override {
// Use this callback to apply any custom configuration to your data source
// based on the TraceConfig in SetupArgs.
}
void OnStart(const StartArgs&) override {
// This notification can be used to initialize the GPU driver, enable
// counters, etc. StartArgs will contains the DataSourceDescriptor,
// which can be extended.
}
void OnStop(const StopArgs&) override {
// Undo any initialization done in OnStart.
}
// Data sources can also have per-instance state.
int my_custom_state = 0;
};
PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource);
数据源的静态数据应该像这样在一个源文件中定义:
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource);
自定义数据源需要与 Perfetto 进行注册:
int main(int argc, char** argv) {
...
perfetto::Tracing::Initialize(args);
// Add the following:
perfetto::DataSourceDescriptor dsd;
dsd.set_name("com.example.custom_data_source");
CustomDataSource::Register(dsd);
}
In-process vs System mode
这两种模式并不互斥。一个应用程序可以被配置为在这两种模式下工作,并同时响应进程内跟踪请求和系统跟踪请求。两种模式都会生成相同格式的跟踪文件。
// The backends determine where trace events are recorded. You may select one
// or more of:
// 1) The in-process backend only records within the app itself.
args.backends |= perfetto::kInProcessBackend;
// 2) The system backend writes events into a system Perfetto daemon,
// allowing merging app and system events (e.g., ftrace) on the same
// timeline. Requires the Perfetto `traced` daemon to be running (e.g.,
// on Android Pie and newer).
args.backends |= perfetto::kSystemBackend;
进程内模式(In-process mode)
在此模式下,Perfetto服务和应用程序定义的数据源完全托管在进程内,即在被分析应用程序的同一进程中。不会尝试与系统 traced 建立连接。
通过在初始化SDK时设置TracingInitArgs.backends = perfetto::kInProcessBackend来启用进程内模式,示例如下。
In-process模式用于生成仅包含应用程序发出的事件的跟踪,但不包含其他类型的事件(如调度器跟踪)。
主要优势在于,由于完全在进程内运行,它不需要任何特殊的操作系统权限,并且被分析进程可以控制跟踪会话的生命周期。
此模式在Android、Linux、MacOS和Windows上均受支持。
系统模式(System mode)
在系统模式下,应用程序定义的数据源将通过UNIX套接字上的IPC连接到外部 traced服务。
通过在初始化SDK时设置TracingInitArgs.backends = perfetto::kSystemBackend来启用系统模式,示例如下。
此模式的主要优势在于,可以创建融合跟踪,其中应用程序事件与操作系统事件的同一时间线相叠加。这使得能够进行全栈性能调查,查看系统调用和内核调度事件。
此模式的主要限制在于,它需要外部跟踪守护进程正在运行并可通过UNIX套接字连接访问。
这适用于本地调试或实验室测试场景,其中用户(或测试框架)可以控制操作系统的部署(例如,在Android上旁加载二进制文件)。
在使用系统模式时,跟踪会话必须从外部使用Perfetto命令行客户端进行控制。这是因为,在收集系统跟踪时,不允许跟踪数据生产者读取回跟踪数据,因为这可能会泄露其他进程的信息并允许旁路攻击。
- 在Android 9(Pie)及以上版本中,traced作为平台的一部分提供。
- 在较旧版本的Android上,可以使用基于NDK的独立工作流从源代码构建traced,并通过adb shell侧载。
- 在Linux、MacOS和Windows上,必须分别构建和运行 traced。请参阅Linux快速入门指南以获取说明。
- 在Windows上,跟踪协议通过TCP/IP(127.0.0.1:32278)+命名共享内存工作。
通过API记录跟踪
目前,通过API进行跟踪仅支持进程内模式。当使用系统模式时,请使用Perfetto命令行客户端(请参阅快速入门指南)。
首先,初始化一个TraceConfig消息,指定要记录的数据类型。
如果你的应用程序包含跟踪事件(即TRACE_EVENT),你通常希望选择已启用跟踪的类别。
默认情况下,所有非调试类别都已启用,但你可以像这样启用特定的类别:
perfetto::protos::gen::TrackEventConfig track_event_cfg;
track_event_cfg.add_disabled_categories("*");
track_event_cfg.add_enabled_categories("rendering");
接下来,将主要的跟踪配置与跟踪事件部分一起构建:
perfetto::TraceConfig cfg;
cfg.add_buffers()->set_size_kb(1024); // 记录最多1 MiB。
auto* ds_cfg = cfg.add_data_sources()->mutable_config();
ds_cfg->set_name("track_event");
ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
如果你的应用程序包含自定义数据源,你也可以在这里启用它:
ds_cfg = cfg.add_data_sources()->mutable_config();
ds_cfg->set_name("my_data_source");
构建跟踪配置后,你可以开始跟踪:
std::unique_ptr<perfetto::TracingSession> tracing_session(
perfetto::Tracing::NewTrace());
tracing_session->Setup(cfg);
tracing_session->StartBlocking();
提示:名称中包含Blocking的API方法会挂起调用线程,直到相应的操作完成。还有不受此限制的异步变体。
现在跟踪已激活,指示你的应用程序执行你想要记录的操作。之后,停止跟踪并收集protobuf格式的跟踪数据:
tracing_session->StopBlocking();
std::vector<char> trace_data(tracing_session->ReadTraceBlocking());
// 将跟踪写入文件。
std::ofstream output;
output.open("example.perfetto-trace", std::ios::out | std::ios::binary);
output.write(&trace_data[0], trace_data.size());
output.close();
为了节省较长跟踪的内存,你还可以告诉Perfetto直接将数据写入文件,通过在Setup()中传递文件描述符,并在跟踪完成后记得关闭文件:
int fd = open("example.perfetto-trace", O_RDWR | O_CREAT | O_TRUNC, 0600);
tracing_session->Setup(cfg, fd);
tracing_session->StartBlocking();
// ...
tracing_session->StopBlocking();
close(fd);
生成的跟踪文件可以直接在Perfetto UI或Trace Processor中打开。