使用追蹤事件

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

荒漠油廠

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

或者,您也可以定義一段時間長度事件,並在該事件超出範圍時,使用 RAII (Resource acquisition is initialization) 自動關閉該事件,如下所示:

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
}

荒漠油廠

{
  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-執行緒 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` 計數器事件。計數器事件名稱右側的圖表會隨時間上升,顯示計數器值的增加情形。

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

荒漠油廠

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!(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`。螢幕截圖中也顯示了 2 個藍色箭頭,分別位於 `example_async` 事件和 `example_async2` 事件中,代表即時事件。

引數

您可以在 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);