This document outlines best practices for emitting trace events in Chrome, focusing on clarity, performance, and effective debugging. It draws from the current state of Chrome's tracing infrastructure and the Perfetto library.
Trace events are grouped into categories, which are essential for filtering and organizing trace data. Proper categorization is crucial for effective trace analysis.
base/trace_event/builtin_categories.h
for a list of predefined categories.namespace.category(.sub_category)(.debug)
naming convention for new categories.base.scheduling
, renderer.compositor
.toplevel
. These become “junk drawers” and are too noisy..debug
suffix for debug categories instead of TRACE_DISABLED_BY_DEFAULT()
..SetDescription()
to clarify their purpose."debug"
tag for debug categories.#include "base/trace_event/track_event.h"
perfetto/include/perfetto/tracing/track_event.h
.TRACE_EVENT()
TRACE_EVENT_BEGIN()
TRACE_EVENT_END()
third_party/perfetto/docs/instrumentation/track-events.md
for more details.third_party/perfetto/include/perfetto/tracing/track_event_legacy.h
, such as TRACE_EVENT0/1/2
, TRACE_EVENT_ASYNC_BEGIN0/1/2
or any other macro that has 0/1/2 suffix.perfetto::StaticString
to mark strings that are not known to be static at compile time but are in fact static.Asynchronous events are emitted outside of the current thread‘s execution flow; they are not bound to the thread where the TRACE_EVENT_BEGIN
or TRACE_EVENT_END
macros are invoked. Use the perfetto::Track
API from third_party/perfetto/include/perfetto/tracing/track.h
to specify which track they belong to, which can span across threads or even processes. Asynchronous events are best suited for representing high-level, long-lived states or operations that are conceptually independent of a single thread’s execution.
.debug
suffix (e.g., mycomponent.debug
). This allows for easy filtering when collecting traces.TRACE_EVENT_INSTANT("my_component.debug.lifetime", "MyObject::Constructor", perfetto::Flow::FromPointer(this)); TRACE_EVENT_INSTANT("my_component.debug.lifetime", "MyObject::Destructor", perfetto::Flow::TerminatingFlow(this));
NamedTrack
. To organize events logically, you can create a track hierarchy.#include "base/trace_event/track_event.h" #include "third_party/perfetto/include/perfetto/tracing/track.h" const perfetto::NamedTrack CreateFrameParentTrack() { perfetto::NamedTrack track("Frames", 0, perfetto::Track()); if (perfetto::Tracing::IsInitialized()) { // Because the track doesn't get any events of its own it must manually // emit the track descriptor. This is done conditionally to avoid // crashing in unit tests where tracing isn't initialized. base::TrackEvent::SetTrackDescriptor(track, track.Serialize()); } return track; } perfetto::NamedTrack GetFrameVisibleTrack(int64_t frame_id) { static const perfetto::NamedTrack parent_track = CreateFrameParentTrack(); return perfetto::NamedTrack("Frame Visibility", frame_id, parent_track); } void MyFunction(int64_t frame_id, int64_t start_ts, int64_t end_ts) { TRACE_EVENT_BEGIN("renderer", "Visible", GetFrameVisibleTrack(frame_id), start_ts); TRACE_EVENT_END("renderer", "Visible", GetFrameVisibleTrack(frame_id), end_ts); }
Use base::test::TracingEnvironment
to enable tracing in unittests and base::test::TestTraceProcessor
to control a tracing session and read events. Avoid using legacy base::trace_event::TraceLog in new code.
#include "base/test/test_trace_processor.h" #include "base/test/trace_test_utils.h" #include "base/trace_event/track_event.h" #include "testing/gtest/include/gtest/gtest.h" TEST(MyComponentTest, TestTracing) { base::test::TracingEnvironment tracing_env; base::test::TestTraceProcessor ttp; ttp.StartTrace("my_category"); // Code that emits trace events TRACE_EVENT("my_category", "MyScopedEvent"); TRACE_EVENT_BEGIN("my_category", "MyScopedEvent"); // ... some work ... TRACE_EVENT_END("my_category", "MyScopedEvent"); absl::Status status = ttp.StopAndParseTrace(); ASSERT_TRUE(status.ok()) << status.message(); auto result = ttp.RunQuery(R"sql( SELECT count(*) as cnt from slice where name = 'MyScopedEvent' )sql"); ASSERT_TRUE(result.has_value()) << result.error(); // verify that the expected events are present in result. EXPECT_THAT(result.value(), ::testing::ElementsAre(std::vector<std::string>{"cnt"}, std::vector<std::string>{"2"})); }