使用追蹤事件

將 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>

C++

#include <lib/trace/event.h>

荒漠油廠

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);

荒漠油廠

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");

荒漠油廠

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", "example_duration");
// Do some work
TRACE_DURATION_END("trace_category", "example_duration");

C++

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

荒漠油廠

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

或者,您也可以定義時間長度事件,並在超出範圍時使用 RAII (資源取得即初始化) 自動關閉,如下所示:

C

{
  TRACE_DURATION("trace_category", "example_duration_raii");
  // Do some work
  TRACE_DURATION(c"trace_category", "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", "example_duration_raii");
  // Do some work
  TRACE_DURATION(c"trace_category", "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
}

荒漠油廠

{
  duration!(c"trace_category", c"example_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-執行緒 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);

荒漠油廠

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

在 Perfetto 中,計數器事件如下所示:

Perfetto 追蹤記錄檢視器螢幕截圖,顯示 `example_counter:somedataseries` 計數器事件。在計數器事件名稱的右側,有一個隨時間上升的圖表,顯示計數器值增加。

流程事件

流程事件說明執行緒之間和程序之間的流程交接。這些事件必須封裝在時間長度事件中,代表流程交接發生的位置。每個流程事件也可以附加引數。流程開始 (TRACE_FLOW_BEGINflow_begin) 事件之後,可能會接著發生流程步驟 (TRACE_FLOW_STEPflow_step) 事件。

Perfetto 需要追蹤記錄中的全域流程事件 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);

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);

荒漠油廠

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);

荒漠油廠

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

在 Perfetto 中,非同步事件會放在自己的命名軌上,不會放在發出事件的執行緒軌上。非同步事件如下所示:

螢幕截圖:Perfetto 追蹤檢視器顯示兩個非同步事件:「example_async」和「example_async2」。其中「example_async」事件下方顯示「example_nested_async」。螢幕截圖也顯示 `example_async` 事件中有 2 個藍色箭頭,`example_async2` 事件中有 2 個藍色箭頭,表示這些是即時事件。

引數

您可以在 Fuchsia 追蹤記錄中使用下列類型的事件:

  • 空值引數
  • 數值類型
  • 字串類型
  • 指標
  • KOID
  • 布林值

空值引數

空值引數只是名稱,沒有資料。

例如:

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);

荒漠油廠

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);

荒漠油廠

// 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");

荒漠油廠

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);

荒漠油廠

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));

荒漠油廠

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);

荒漠油廠

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