[性能优化]perfetto - Tracing SDK

[性能优化]perfetto - Tracing SDK

编码文章call10242024-12-29 1:46:5843A+A-

Perfetto Tracing SDK 是一个 C++17 库,它允许用户空间应用程序发出跟踪事件,并向 Perfetto 跟踪中添加更多特定于应用程序的上下文。

使用 Tracing SDK 时,需要考虑两个主要方面:

  1. 您是否只对来自您自己应用程序的跟踪事件感兴趣,还是希望收集全栈跟踪,这些跟踪将应用程序跟踪事件与系统跟踪事件(如调度器跟踪、系统调用或任何其他 Perfetto 数据源)叠加在一起。
  2. 对于特定于应用程序的跟踪,您是否需要跟踪简单的时间线事件类型(例如,切片、计数器),还是需要定义具有自定义强类型架构的复杂数据源(例如,将您应用程序子系统的状态转储到跟踪中)。

对于仅针对 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中打开。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4