# Trace Event Macro [perfetto](https://perfetto.dev/)というOpen Sourceツールがあって、これはイベントのトレースを取ってパフォーマンスチェックをできたりする。 Chromiumの中では、perfettoが提供しているライブラリに定義されたTRACE_EVENTマクロを使って各イベントの継続時間やいつ呼ばれたかなどを計測するために使われている。 今日はそのイベントのトレースで使われるマクロについて確認する。使い方や、各マクロの意味を確認するのがメインテーマで、中身の実装の深追いは今日はしない。 ## Overview Track Eventsを追加するためにはまずcategoriesを定義してストレージをつくる。 例えばChromeでは[base::trace_event::TraceEvent](https://source.chromium.org/chromium/chromium/src/+/main:base/trace_event/trace_event_impl.h;l=52;drc=51d26a40f145af6de565b22a600a39e6e39184e9)がそれらを定義している。 ```cpp= PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS( base, BASE_EXPORT, INTERNAL_TRACE_LIST_BUILTIN_CATEGORIES(INTERNAL_CATEGORY) INTERNAL_TRACE_LIST_BUILTIN_CATEGORY_GROUPS(INTERNAL_CATEGORY_GROUP)); PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(base); PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS(base, BASE_EXPORT); ``` このあとに[`TRACE_EVENT`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/track_event.h;l=366;drc=6f3f85b321146cfc0f9eb81a74c7c2257821461e)でcategoryに対してeventを作ることができる。 TRACE_EVENTはカテゴリー、イベント名は必須で、そのあとはいろんなパラメータをいろんなフォーマットで追加できる。 ```cpp= // Requiredな値だけ TRACE_EVENT("category", "MyEvent"); // 引数名、その値の順で任意のパラメータを指定できる TRACE_EVENT("category", "MyEvent", "parameter1", 42, "parameter2", 3); // lambda式で各filedを定義 TRACE_EVENT("category", "Name", [&](perfetto::EventContext ctx) { ctx.event()->set_custom_value(...); }); ``` これで作られたイベントはScoped eventとなっており、TRACE_EVENTが呼ばれたタイミングからそれがスコープを抜けるまでの間をイベント期間とみなしている。 ```cpp= #define TRACE_EVENT(category, name, ...) \ PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ##__VA_ARGS__) ``` [PERFETTO_INTERNAL_SCOPED_TRACK_EVENT](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/internal/track_event_macros.h;l=184;drc=bcb9d223ac43ffbf81b773575070304d4cf1f32a)の中を見ると、TRACE_EVENT_BEGINとそれを出た跡にTRACE_EVENT_ENDが呼ばれている。これらについては次の節で。 ### Begin/End Scopeで定義されていたTRACE_EVENTとは異なり、明示的にイベントの始まりと終わりを決められるメソッドもある。それがTRACE_EVENT_BEGIN/ENDだ。 [TRACE_EVENT_BEGIN](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/track_event.h;l=354;drc=6f3f85b321146cfc0f9eb81a74c7c2257821461e)と[TRACE_EVENT_END](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/track_event.h;l=360;drc=6f3f85b321146cfc0f9eb81a74c7c2257821461e)がEventの始まりと終わりを定義している。 ```cpp= #define TRACE_EVENT_BEGIN(category, name, ...) \ PERFETTO_INTERNAL_TRACK_EVENT_WITH_METHOD( \ TraceForCategory, category, name, \ ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN, ##__VA_ARGS__) ``` 内部実装で使われている[PERFETTO_INTERNAL_TRACK_EVENT_WITH_METHOD](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/internal/track_event_macros.h;l=115;drc=bcb9d223ac43ffbf81b773575070304d4cf1f32a)はそのcategoryでトレースがenableされてるかをefficientlyに確認するらしいが、とにかくやることはTYPE_SLICE_BEGINを追加する。 ENDも同様にTYPE_SLICE_ENDを追加する。 ### Instant [TRACE_EVENT_INSTANT](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/track_event.h;l=370;drc=6f3f85b321146cfc0f9eb81a74c7c2257821461e)はゼロdurationのinstance eventを挟み込む。 これは主に今の状態に関するログを差し込んだりする際に使われるっぽい。 ## Legacy trace event 上記のTRACE_EVENTの他にも[trace_event_legacy.h](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/include/perfetto/tracing/track_event_legacy.h;drc=3cc59c3eb018c01b1524d2a4b851019bcb03fb30)の中で定義されたlegacyマクロがいくつかある。 Chromiumの中にはまだまだこいつを使っているoutdated codeがたくさんあるのでlegacy版も確認する。 ### 例:DrawAndSwap [Display::DrawAndSwap](https://source.chromium.org/chromium/chromium/src/+/main:components/viz/service/display/display.cc;l=668;drc=d795f90f4f782f1dba26a02d69cad3f799f05bf8)は描画してそのバッファを今表示しているやつと交換する機能を持った、グラフィックスタックで頻出の関数。これもperfettoでメトリクスが取れる。 おおまかな流れはこんな感じだった。 ```cpp= TRACE_EVENT0("viz", "Display::DrawAndSwap"); // こんな感じのEVENT_INSTANCEがいくつか TRACE_EVENT_INSTANT0("viz", "No root surface.", TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_ASYNC_BEGIN0("viz,benchmark", "Graphics.Pipeline.DrawAndSwap", swapped_trace_id_); // ここでもこんな感じのEVENT_INSTANCE TRACE_EVENT_INSTANT0("viz", "Skip draw", TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_ASYNC_STEP_INTO0("viz,benchmark", "Graphics.Pipeline.DrawAndSwap", swapped_trace_id_, "Draw"); TRACE_EVENT( "viz,benchmark,graphics.pipeline", "Graphics.Pipeline", perfetto::Flow::Global(swap_frame_data.swap_trace_id), [swap_trace_id = swap_frame_data.swap_trace_id](perfetto::EventContext ctx) { auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>(); auto* data = event->set_chrome_graphics_pipeline(); data->set_step(perfetto::protos::pbzero::ChromeGraphicsPipeline:: StepName::STEP_SEND_BUFFER_SWAP); data->set_display_trace_id(swap_trace_id); }); TRACE_EVENT_ASYNC_END1("viz,benchmark", "Graphics.Pipeline.DrawAndSwap", swapped_trace_id_, "status", "canceled"); ``` これからわかるとおり、イベントはnested構造を許されている。