使用轨迹事件

将 Fuchsia 组件作为跟踪提供程序进行插桩时,您可以创建和观察各种类型的事件。本文档介绍了事件类型之间的区别、每种事件的最佳用例、如何在组件中使用事件,以及您可以在跟踪事件中使用的各种参数。

事件

Fuchsia 事件类型可以是以下任一项:

  • 即时事件:标记具有名称、类别和可选参数的时间点。
  • 时长事件:用于标记两个时间点,并且可以包含名称、类别和可选参数。
  • 计数器事件:捕获通常在 Perfetto 中以堆叠面积图表示的数值样本。
  • 流事件:描述线程之间以及跨进程的流程切换。
  • 异步事件:允许指定 id,用于将不同线程中的事件关联在一起,并将这些事件放置在单个轨道上。

在最基本的形式下,轨迹事件包含一个类别(第一个参数)和一个名称(第二个参数)。

轨迹事件最多可以接受 15 个参数

使用轨迹事件时,请务必了解类别的运作方式。以下是与类别相关的一些关键概念:

  • 借助类别,您可以对事件进行分组,以便一起启用或停用这些事件。
  • 类别通常是 [A-Za-z-_:]+ 的简短 ASCII 字符串字面量,例如 gfxaudio
  • 类别是全球性的。不妨考虑将它们命名为组件。
  • 类别默认处于停用状态。如果要添加类别,请确保在使用 --categories #default,your_category 捕获跟踪记录时指定类别。
  • ffx trace 支持前缀匹配。如果运行 ffx trace start --categories your_component::*,则会启用 your_component::category1your_component::category2 以及任何其他相关类别。

使用事件时,请务必在代码中添加相关库:

C++

#include <lib/trace/event.h>

Rust

use fuchsia_trace::{ArgValue, Scope, ...};

您还可以查看有关使用轨迹提供程序的 C++ 简单示例

即时事件

这是最基本的事件。即时事件可以标记具有名称、类别和可选参数的时间点。

您可以选择注册类别,但大多数跟踪提供程序都不会这样做。

实时轨迹至少应具有名称和类别,以便进行区分:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD);
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD);

Rust

instant!(c"trace_category", c"trace_name", Scope::Thread);

您还可以创建包含其他参数的即时事件:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, argument_1, argument_1_value, argument_2, "argument_2_string");
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, argument_1, argument_1_value, argument_2, "argument_2_string");

Rust

instant!(c"trace_category", c"trace_name", Scope::Thread, argument_1 => argument_1_value, argument_2 => "argument_2_string");

在 Perfetto 中,即时事件由一个小箭头表示,如此屏幕截图所示。

Perfetto 轨迹查看器的屏幕截图,其中显示了在多个 CPU(CPU 0、CPU 1、CPU 2 和 CPU 3)上执行的进程的轨迹。该事件在 00:00:00.194617950 时刻发出,持续时间为 0 秒,因为它是即时事件。该进程称为“example_trace_provider.cm”。

时长事件

时长事件用于标记两个时间点,并且可以包含名称、类别和可选参数。时长事件也可以嵌套在另一个时长事件中,并相互堆叠。

跟踪记录必须至少包含名称和类别来对其进行区分:

  • {C}

    TRACE_DURATION_BEGIN("trace_category", "trace_name");
    // Do some work
    TRACE_DURATION_END("trace_category", "trace_name");
    

C++

TRACE_DURATION_BEGIN("trace_category", "trace_name");
// Do some work
TRACE_DURATION_END("trace_category", "trace_name");

Rust

duration_begin!(c"trace_category", c"trace_name");
// Do some work
duration_end!(c"trace_category", c"trace_name");

或者,您也可以定义一个时长事件,并使用 RAII(资源获取即初始化)在其超出范围时自动关闭它,如下所示:

  • {C}

    {
      TRACE_DURATION("trace_category", "trace_duration_raii");
      // Do some work
      TRACE_DURATION(c"trace_category", c"nested_duration", "argument_3", argument_3_value, "argument_4", argument_4_string);
      // nested_duration closes due to RAII
      // trace_duration_raii closes due to RAII
    }
    

C++

{
  TRACE_DURATION("trace_category", "trace_duration_raii");
  // Do some work
  TRACE_DURATION(c"trace_category", c"nested_duration", "argument_3", argument_3_value, "argument_4", argument_4_string);
  // nested_duration closes due to RAII
  // trace_duration_raii closes due to RAII
}

Rust

{
  duration!(c"trace_category", c"trace_duration_raii");
  // Do some work
  duration!(c"trace_category", c"nested_duration", argument_3 => argument_3_value, argument_4 => argument_4_string);
  // nested_duration closes due to RAII
  // trace_duration_raii closes due to RAII
}

在 Perfetto 中,时长嵌套堆栈如下所示:

Perfetto 轨迹查看器的屏幕截图,显示了事件的时间轴,时间从左到右。顶栏会显示“example_trace_provider.com 1617970”字样。在该时间轴下方,时间轴会拆分为两个部分,即“initial-thread 1617972”。其中每个线程都有一个条状标签,显示与每个线程关联的事件的持续时间。名称不同的时长事件会以不同的颜色显示:橄榄色、绿色、蓝色和深绿色。时长事件会向下堆叠,因此时长较短的事件包含在其上方时长较长的事件中。

计数器事件

计数器事件会捕获数字样本,这些样本通常在 Perfetto 中表示为堆叠面积图。使用计数器事件时,您可以使用 id 来区分系统进程中共享相同类别和名称的多个计数器实例。您可以将 1-15 个数字参数与事件相关联,每个参数都会被解读为一个不同的时间序列。

以下示例显示了传递给计数器事件的 counter_id 值和数据序列:

  • {C}

    trace_counter_id_t counter_id = 555;
    uint32_t data = get_some_data();
    TRACE_COUNTER("trace_category", "trace_name", counter_id, "data_series", data);
    

C++

trace_counter_id_t counter_id = 555;
uint32_t data = get_some_data();
TRACE_COUNTER("trace_category", "trace_name", counter_id, "data_series", data);

Rust

let counter_id = 555;
let data = get_some_data();
counter!("trace_category", "trace_name", counter_id, "data_series" => data);

在 Perfetto 中,计数器事件如下所示:

Perfetto 轨迹查看器的屏幕截图,其中显示了“example_counter:somedataseries”计数器事件。计数器事件名称的右侧有一个图表,该图表会随时间推移而上升,显示计数器值的增加情况。

用户流事件

流事件描述了线程之间和进程之间的流交接。这些事件必须包含在表示流程移交发生的时长事件中。每个 flow 事件还可以附加参数。流程开始(TRACE_FLOW_BEGINflow_begin)事件可以后跟流程步骤(TRACE_FLOW_STEPflow_step)事件。

例如:

  • {C}

    trace_flow_id_t flow_id = 555;
    TRACE_FLOW_BEGIN("trace_category", "trace_flow_name", flow_id, "argument_1", argument_1_value);
    TRACE_FLOW_STEP("trace_category", "trace_flow_step_name", flow_id);
    TRACE_FLOW_END("trace_category", "trace_flow_name", flow_id);
    

C++

trace_flow_id_t flow_id = 555;
TRACE_FLOW_BEGIN("trace_category", "trace_flow_name", flow_id, "argument_1", argument_1_value);
TRACE_FLOW_STEP("trace_category", "trace_flow_step_name", flow_id);
TRACE_FLOW_END("trace_category", "trace_flow_name", flow_id);

Rust

let flow_id = 555;
flow_begin!(c"trace_category", c"trace_flow_name", flow_id, "argument_1" => argument_1_value);
flow_step!(c"trace_category", c"trace_flow_step_name", flow_id);
flow_end!(c"trace_category", c"trace_flow_name", flow_id);

流事件在 Perfetto 中表示为箭头,例如:

Perfetto 轨迹查看器的屏幕截图,显示了不同线程上的事件流事件。水平条表示事件,其长度表示时长。每个线程都用弯曲箭头连接,表示这些线程中的事件序列。

异步事件

在轨迹中,即时事件和时长事件会放置在生成它们的线程轨道上。使用基于多线程异步或线程池的代码时,事件可能会在不同的线程上启动和完成。异步事件允许指定 id,用于将不同线程中的事件关联在一起,并将这些事件放置在单个轨道上。

异步开始 (TRACE_ASYNC_BEGINasync_begin) 事件后面可以跟随异步即时 (TRACE_ASYNC_INSTANTasync_instant) 事件,并且必须与具有相同类别、名称和 id 的异步结束 (TRACE_ASYNC_ENDasync_end) 事件匹配。

异步事件描述了异步发生并且可能跨越多个线程的工作。id 有助于关联同一进程中具有相同类别和名称的不同异步操作的进度。同一进程中具有匹配类别和 ID 的异步事件会嵌套。

您可以将 0-15 个实参与异步事件相关联,每个实参都用于使用其他信息为异步操作添加注解。您为匹配的异步开始事件、异步即时事件和异步结束事件提供的参数会在跟踪记录中合并在一起,您无需重复这些参数。

  • {C}

    trace_async_id_t async_id = 555;
    TRACE_ASYNC_BEGIN("trace_category", "trace_async_name", async_id, "argument_1", argument_1_value);
    TRACE_ASYNC_INSTANT("trace_category", "trace_async_instant_name", async_id);
    TRACE_ASYNC_END("trace_category", "trace_async_name", async_id);
    

C++

trace_async_id_t async_id = 555;
TRACE_ASYNC_BEGIN("trace_category", "trace_async_name", async_id, "argument_1", argument_1_value);
TRACE_ASYNC_INSTANT("trace_category", "trace_async_instant_name", async_id);
TRACE_ASYNC_END("trace_category", "trace_async_name", async_id);

Rust

let async_id = 555;
async_begin!(c"trace_category", c"trace_async_name", async_id, "argument_1" => argument_1_value);
async_instant!(c"trace_category", c"trace_async_instant_name", async_id);
async_end!(c"trace_category", c"trace_async_name", async_id);

在 Perfetto 中,异步事件会放置在其自己的命名轨道上,而不会放置在其发出线程的轨道上。异步事件如下所示:

Perfetto 轨迹查看器的屏幕截图,其中显示了两个异步事件:“example_async”和“example_async2”。“example_async”事件下方会显示一个“example_nested_async”。该屏幕截图还显示了 `example_async` 事件中有 2 个蓝色箭头,`example_async2` 事件中有 2 个蓝色箭头,这表示这两个事件是即时事件。

参数

您可以在 Fuchsia 轨迹中使用以下类型的事件:

  • Null 实参
  • 数值类型
  • 字符串类型
  • 指针
  • KOID
  • 布尔值

Null 实参

null 参数只是没有数据的名称。

例如:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "SomeNullArg", TA_NULL());
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "SomeNullArg", nullptr);

Rust

instant!(c"trace_category", c"trace_name", Scope::Thread, c"SomeNullArg" => ());

数值类型

数值类型是指任何 intfloat 数据类型。

例如:

  • {C}

    // Standard int types
    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someuint32", TA_UINT32(2145));
    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someuint64", TA_UINT64(423621626134123415));
    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someint32", TA_INT32(-7));
    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someint64", TA_INT64(-234516543631231));
    // Doubles
    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Somedouble", TA_DOUBLE(3.1415));
    

C++

// Standard int types
TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someuint32", 2145);
TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someuint64", 423621626134123415);
TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someint32", -7);
TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Someint64", -234516543631231);
// Doubles
TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "Somedouble", 3.1415);

Rust

// Standard int types
instant!(c"trace_category", c"trace_name", Scope::Thread, c"Someuint32" => 2145);
instant!(c"trace_category", c"trace_name", Scope::Thread, c"Someuint64" => 423621626134123415);
instant!(c"trace_category", c"trace_name", Scope::Thread, c"Someint32" => -7);
instant!(c"trace_category", c"trace_name", Scope::Thread, c"Someint64" => -234516543631231);
// Doubles
instant!(c"trace_category", c"trace_name", Scope::Thread, c"Somedouble" => 3.1415);

字符串类型

字符串类型是指任何 string 数据类型。

例如:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "ping", "pong");
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "ping", "pong");

Rust

instant!(c"trace_category", c"trace_name", Scope::Thread, "ping" => "pong");

指针

在 Perfetto 查看器中,指针会采用十六进制格式。如果类型推断会解析为错误的数据类型,您也可以明确指定跟踪参数类型。

例如:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "somepointer", &i, "someotherpointer", &i, TA_POINTER(0xABCD));
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "somepointer", &i, "someotherpointer", &i, 0xABCD);

Rust

let x: u64 = 123;
instant!(c"trace_category", c"trace_name", Scope::Thread, "SomeOtherPointer" => &x as *const u64);

KOID

KOID 是内核对象的 ID,具有自己的数据类型,以便将其与常规 u64 数据类型区分开来。

例如:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "somekoid", TA_KOID(0x0012));
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "somekoid", TA_KOID(0x0012));

Rust

let koid: zx::Koid = vmo.get_koid()?;
instant!(c"trace_category", c"trace_name", Scope::Thread, "somekoid" => koid);

布尔值

布尔值显示为 truefalse

例如:

  • {C}

    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "somebool", TA_BOOL(true));
    TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "someotherbool", TA_BOOL(false));
    

C++

TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "somebool", true);
TRACE_INSTANT("trace_category", "trace_name", TRACE_SCOPE_THREAD, "someotherbool", false);

Rust

instant!(c"trace_category", c"trace_name", Scope::Thread, "somebool" => true);
instant!(c"trace_category", c"trace_name", Scope::Thread, "someotherbool" => false);