xref: /aosp_15_r20/external/perfetto/docs/instrumentation/track-events.md (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1# Track events (Tracing SDK)
2
3Track events are part of the [Perfetto Tracing SDK](tracing-sdk.md).
4
5*Track events* are application specific, time bounded events recorded into a
6*trace* while the application is running. Track events are always associated
7with a *track*, which is a timeline of monotonically increasing time. A track
8corresponds to an independent sequence of execution, such as a single thread
9in a process.
10
11![Track events shown in the Perfetto UI](
12  /docs/images/track-events.png "Track events in the Perfetto UI")
13
14See the [Getting started](/docs/instrumentation/tracing-sdk#getting-started)
15section of the Tracing SDK page for instructions on how to check out and
16build the SDK.
17
18TIP: The code from these examples is also available [in the
19repository](/examples/sdk/README.md).
20
21There are a few main types of track events:
22
23- **Slices**, which represent nested, time bounded operations. For example,
24    a slice could cover the time period from when a function begins executing
25    to when it returns, the time spent loading a file from the network or the
26    time to complete a user journey.
27
28- **Counters**, which are snapshots of time-varying numeric values. For
29    example, a track event can record instantaneous the memory usage of a
30    process during its execution.
31
32- **Flows**, which are used to connect related slices that span different
33    tracks together. For example, if an image file is first loaded from
34    the network and then decoded on a thread pool, a flow event can be used to
35    highlight its path through the system.
36
37The [Perfetto UI](https://ui.perfetto.dev) has built in support for track
38events, which provides a useful way to quickly visualize the internal
39processing of an app. For example, the [Chrome
40browser](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)
41is deeply instrumented with track events to assist in debugging, development
42and performance analysis.
43
44To start using track events, first define the set of categories that your events
45will fall into. Each category can be separately enabled or disabled for tracing
46(see [Category configuration](#category-configuration)).
47
48Add the list of categories into a header file (e.g.,
49`my_app_tracing_categories.h`) like this:
50
51```C++
52#include <perfetto.h>
53
54PERFETTO_DEFINE_CATEGORIES(
55    perfetto::Category("rendering")
56        .SetDescription("Events from the graphics subsystem"),
57    perfetto::Category("network")
58        .SetDescription("Network upload and download statistics"));
59```
60
61Then, declare static storage for the categories in a cc file (e.g.,
62`my_app_tracing_categories.cc`):
63
64```C++
65#include "my_app_tracing_categories.h"
66
67PERFETTO_TRACK_EVENT_STATIC_STORAGE();
68```
69
70Finally, initialize track events after the client library is brought up:
71
72```C++
73int main(int argc, char** argv) {
74  ...
75  perfetto::Tracing::Initialize(args);
76  perfetto::TrackEvent::Register();  // Add this.
77}
78```
79
80Now you can add track events to existing functions like this:
81
82```C++
83#include "my_app_tracing_categories.h"
84
85void DrawPlayer() {
86  TRACE_EVENT("rendering", "DrawPlayer");  // Begin "DrawPlayer" slice.
87  ...
88  // End "DrawPlayer" slice.
89}
90```
91
92This type of trace event is scoped, under the hood it uses C++ [RAII]. The
93event will cover the time from when the `TRACE_EVENT` annotation is encountered
94to the end of the block (in the example above, until the function returns).
95
96For events that don't follow function scoping, use `TRACE_EVENT_BEGIN` and
97`TRACE_EVENT_END`:
98
99```C++
100void LoadGame() {
101  DisplayLoadingScreen();
102
103  TRACE_EVENT_BEGIN("io", "Loading");  // Begin "Loading" slice.
104  LoadCollectibles();
105  LoadVehicles();
106  LoadPlayers();
107  TRACE_EVENT_END("io");               // End "Loading" slice.
108
109  StartGame();
110}
111```
112
113Note that you don't need to give a name for `TRACE_EVENT_END`, since it
114automatically closes the most recent event that began on the same thread. In
115other words, all events on a given thread share the same stack. This means
116that it's not recommended to have a matching pair of `TRACE_EVENT_BEGIN` and
117`TRACE_EVENT_END` markers in separate functions, since an unrelated event
118might terminate the original event unexpectedly; for events that cross
119function boundaries it's usually best to emit them on a [separate
120track](#tracks).
121
122You can also supply (up to two) debug annotations together with the event.
123
124```C++
125int player_number = 1;
126TRACE_EVENT("rendering", "DrawPlayer", "player_number", player_number);
127```
128
129See [below](#track-event-arguments) for the other types of supported track
130event arguments. For more complex arguments, you can define [your own
131protobuf messages](/protos/perfetto/trace/track_event/track_event.proto) and
132emit them as a parameter for the event.
133
134NOTE: Currently custom protobuf messages need to be added directly to the
135      Perfetto repository under `protos/perfetto/trace`, and Perfetto itself
136      must also be rebuilt. We are working
137      [to lift this limitation](https://github.com/google/perfetto/issues/11).
138
139As an example of a custom track event argument type, save the following as
140`protos/perfetto/trace/track_event/player_info.proto`:
141
142```protobuf
143message PlayerInfo {
144  optional string name = 1;
145  optional uint64 score = 2;
146}
147```
148
149This new file should also be added to
150`protos/perfetto/trace/track_event/BUILD.gn`:
151
152```json
153sources = [
154  ...
155  "player_info.proto"
156]
157```
158
159Also, a matching argument should be added to the track event message
160definition in
161`protos/perfetto/trace/track_event/track_event.proto`:
162
163```protobuf
164import "protos/perfetto/trace/track_event/player_info.proto";
165
166...
167
168message TrackEvent {
169  ...
170  // New argument types go here.
171  optional PlayerInfo player_info = 1000;
172}
173```
174
175The corresponding trace point could look like this:
176
177```C++
178Player my_player;
179TRACE_EVENT("category", "MyEvent", [&](perfetto::EventContext ctx) {
180  auto player = ctx.event()->set_player_info();
181  player->set_name(my_player.name());
182  player->set_player_score(my_player.score());
183});
184```
185
186The lambda function passed to the macro is only called if tracing is enabled for
187the given category. It is always called synchronously and possibly multiple
188times if multiple concurrent tracing sessions are active.
189
190Now that you have instrumented your app with track events, you are ready to
191start [recording traces](tracing-sdk.md#recording).
192
193## Category configuration
194
195All track events are assigned to one more trace categories. For example:
196
197```C++
198TRACE_EVENT("rendering", ...);  // Event in the "rendering" category.
199```
200
201By default, all non-debug and non-slow track event categories are enabled for
202tracing. *Debug* and *slow* categories are categories with special tags:
203
204  - `"debug"` categories can give more verbose debugging output for a particular
205    subsystem.
206  - `"slow"` categories record enough data that they can affect the interactive
207    performance of your app.
208
209Category tags can be defined like this:
210
211```C++
212perfetto::Category("rendering.debug")
213    .SetDescription("Debug events from the graphics subsystem")
214    .SetTags("debug", "my_custom_tag")
215```
216
217A single trace event can also belong to multiple categories:
218
219```C++
220// Event in the "rendering" and "benchmark" categories.
221TRACE_EVENT("rendering,benchmark", ...);
222```
223
224A corresponding category group entry must be added to the category registry:
225
226```C++
227perfetto::Category::Group("rendering,benchmark")
228```
229
230It's also possible to efficiently query whether a given category is enabled
231for tracing:
232
233```C++
234if (TRACE_EVENT_CATEGORY_ENABLED("rendering")) {
235  // ...
236}
237```
238
239The `TrackEventConfig` field in Perfetto's `TraceConfig` can be used to
240select which categories are enabled for tracing:
241
242```protobuf
243message TrackEventConfig {
244  // Each list item is a glob. Each category is matched against the lists
245  // as explained below.
246  repeated string disabled_categories = 1;  // Default: []
247  repeated string enabled_categories = 2;   // Default: []
248  repeated string disabled_tags = 3;        // Default: [“slow”, “debug”]
249  repeated string enabled_tags = 4;         // Default: []
250}
251```
252
253To determine if a category is enabled, it is checked against the filters in the
254following order:
255
2561. Exact matches in enabled categories.
2572. Exact matches in enabled tags.
2583. Exact matches in disabled categories.
2594. Exact matches in disabled tags.
2605. Pattern matches in enabled categories.
2616. Pattern matches in enabled tags.
2627. Pattern matches in disabled categories.
2638. Pattern matches in disabled tags.
264
265If none of the steps produced a match, the category:
266* is enabled by default in the C++ API
267* is disabled by default in the C API.
268
269Specifying an `enabled_categories: "*"` or `disabled_categories: "*"` helps
270achieving a consistent behavior explicitly.
271
272For example:
273
274| Setting                         | Needed configuration                         |
275| ------------------------------- | -------------------------------------------- |
276| Enable just specific categories | `enabled_categories = ["foo", "bar", "baz"]` |
277|                                 | `disabled_categories = ["*"]`                |
278| Enable all non-slow categories  | `enabled_categories = ["*"] `                |
279| Enable specific tags            | `disabled_tags = ["*"]`                      |
280|                                 | `enabled_tags = ["foo", "bar"]`              |
281
282## Dynamic and test-only categories
283
284Ideally all trace categories should be defined at compile time as shown
285above, as this ensures trace points will have minimal runtime and binary size
286overhead. However, in some cases trace categories can only be determined at
287runtime (e.g., they come from instrumentation in a dynamically loaded JavaScript
288running in a WebView or in a NodeJS engine). These can be used by trace points
289as follows:
290
291```C++
292perfetto::DynamicCategory dynamic_category{"nodejs.something"};
293TRACE_EVENT_BEGIN(dynamic_category, "SomeEvent", ...);
294```
295
296TIP: It's also possible to use dynamic event names by passing `nullptr` as
297    the name and filling in the `TrackEvent::name` field manually.
298
299Some trace categories are only useful for testing, and they should not make
300it into a production binary. These types of categories can be defined with a
301list of prefix strings:
302
303```C++
304PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES(
305   "test",      // Applies to test.*
306   "dontship"   // Applies to dontship.*.
307);
308```
309
310## Dynamic event names
311
312Ideally all event name should be compile time string constants. For example:
313
314```C++
315TRACE_EVENT_BEGIN("rendering", "DrawGame");
316```
317
318Here `"DrawGame"` is a compile time string. If we pass a dynamic string here,
319we will get compile time static_assert failure. For example :
320
321```C++
322const char* name = "DrawGame";
323TRACE_EVENT_BEGIN("rendering", name);  // Error. Event name is not static.
324```
325
326There are two ways to use dynamic event name:
327
3281) If the event name is actually dynamic (e.g., std::string), write it using
329   `perfetto::DynamicString`:
330
331```C++
332  TRACE_EVENT("category", perfetto::DynamicString{dynamic_name});
333```
334
335Note: Below is the old way of using dynamic event names. It's not recommended
336      anymore.
337
338```C++
339TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) {
340  ctx.event()->set_name(dynamic_name);
341});
342```
343
3442) If the name is static, but the pointer is computed at runtime, wrap it
345   with perfetto::StaticString:
346
347```C++
348TRACE_EVENT("category", perfetto::StaticString{name});
349TRACE_EVENT("category", perfetto::StaticString{i % 2 == 0 ? "A" : "B"});
350```
351
352DANGER: Using perfetto::StaticString with strings whose contents change
353        dynamically can cause silent trace data corruption.
354
355## Performance
356
357Perfetto's trace points are designed to have minimal overhead when tracing is
358disabled while providing high throughput for data intensive tracing use
359cases. While exact timings will depend on your system, there is a
360[microbenchmark](/src/tracing/api_benchmark.cc) which gives some ballpark
361figures:
362
363| Scenario | Runtime on Pixel 3 XL | Runtime on ThinkStation P920 |
364| -------- | --------------------- | ---------------------------- |
365| `TRACE_EVENT(...)` (disabled)              | 2 ns   | 1 ns   |
366| `TRACE_EVENT("cat", "name")`               | 285 ns | 630 ns |
367| `TRACE_EVENT("cat", "name", <lambda>)`     | 304 ns | 663 ns |
368| `TRACE_EVENT("cat", "name", "key", value)` | 354 ns | 664 ns |
369| `DataSource::Trace(<lambda>)` (disabled)   | 2 ns   | 1 ns   |
370| `DataSource::Trace(<lambda>)`              | 133 ns | 58 ns  |
371
372## Advanced topics
373
374### Track event arguments
375
376The following optional arguments can be passed to `TRACE_EVENT` to add extra
377information to events:
378
379```C++
380TRACE_EVENT("cat", "name"[, track][, timestamp]
381    (, "debug_name", debug_value |, TrackEvent::kFieldName, value)*
382    [, lambda]);
383```
384
385Some examples of valid combinations:
386
3871. A lambda for writing custom TrackEvent fields:
388
389   ```C++
390     TRACE_EVENT("category", "Name", [&](perfetto::EventContext ctx) {
391       ctx.event()->set_custom_value(...);
392     });
393   ```
394
3952. A timestamp and a lambda:
396
397   ```C++
398     TRACE_EVENT("category", "Name", time_in_nanoseconds,
399         [&](perfetto::EventContext ctx) {
400       ctx.event()->set_custom_value(...);
401     });
402   ```
403
404   |time_in_nanoseconds| should be an uint64_t by default. To support custom
405   timestamp types,
406   |perfetto::TraceTimestampTraits<MyTimestamp>::ConvertTimestampToTraceTimeNs|
407   should be defined. See |ConvertTimestampToTraceTimeNs| for more details.
408
4093. Arbitrary number of debug annotations:
410
411   ```C++
412     TRACE_EVENT("category", "Name", "arg", value);
413     TRACE_EVENT("category", "Name", "arg", value, "arg2", value2);
414     TRACE_EVENT("category", "Name", "arg", value, "arg2", value2,
415                 "arg3", value3);
416   ```
417
418   See |TracedValue| for recording custom types as debug annotations.
419
4204. Arbitrary number of TrackEvent fields (including extensions):
421
422  ```C++
423    TRACE_EVENT("category", "Name",
424                perfetto::protos::pbzero::TrackEvent::kFieldName, value);
425  ```
426
4275. Arbitrary combination of debug annotations and TrackEvent fields:
428
429  ```C++
430    TRACE_EVENT("category", "Name",
431                perfetto::protos::pbzero::TrackEvent::kFieldName, value1,
432                "arg", value2);
433  ```
434
4356. Arbitrary combination of debug annotations / TrackEvent fields and a lambda:
436
437   ```C++
438     TRACE_EVENT("category", "Name", "arg", value1,
439                 pbzero::TrackEvent::kFieldName, value2,
440                 [&](perfetto::EventContext ctx) {
441                     ctx.event()->set_custom_value(...);
442                 });
443   ```
444
4457. An overridden track:
446
447   ```C++
448     TRACE_EVENT("category", "Name", perfetto::Track(1234));
449   ```
450
451   See |Track| for other types of tracks which may be used.
452
4538. A track and a lambda:
454
455   ```C++
456     TRACE_EVENT("category", "Name", perfetto::Track(1234),
457                 [&](perfetto::EventContext ctx) {
458                     ctx.event()->set_custom_value(...);
459                 });
460   ```
461
4629. A track and a timestamp:
463
464   ```C++
465     TRACE_EVENT("category", "Name", perfetto::Track(1234),
466                 time_in_nanoseconds);
467   ```
468
46910. A track, a timestamp and a lambda:
470
471   ```C++
472     TRACE_EVENT("category", "Name", perfetto::Track(1234),
473                 time_in_nanoseconds, [&](perfetto::EventContext ctx) {
474                     ctx.event()->set_custom_value(...);
475                 });
476   ```
477
47811. A track and any combination of debug annotions and TrackEvent fields:
479
480   ```C++
481     TRACE_EVENT("category", "Name", perfetto::Track(1234),
482                 "arg", value);
483     TRACE_EVENT("category", "Name", perfetto::Track(1234),
484                 "arg", value, "arg2", value2);
485     TRACE_EVENT("category", "Name", perfetto::Track(1234),
486                 "arg", value, "arg2", value2,
487                 pbzero::TrackEvent::kFieldName, value3);
488   ```
489
490### Tracks
491
492Every track event is associated with a track, which specifies the timeline
493the event belongs to. In most cases, a track corresponds to a visual
494horizontal track in the Perfetto UI like this:
495
496![Track timelines shown in the Perfetto UI](
497  /docs/images/track-timeline.png "Track timelines in the Perfetto UI")
498
499Events that describe parallel sequences (e.g., separate
500threads) should use separate tracks, while sequential events (e.g., nested
501function calls) generally belong on the same track.
502
503Perfetto supports three kinds of tracks:
504
505- `Track` – a basic timeline.
506
507- `ProcessTrack` – a timeline that represents a single process in the system.
508
509- `ThreadTrack` – a timeline that represents a single thread in the system.
510
511Tracks can have a parent track, which is used to group related tracks
512together. For example, the parent of a `ThreadTrack` is the `ProcessTrack` of
513the process the thread belongs to. By default, tracks are grouped under the
514current process's `ProcessTrack`.
515
516A track is identified by a uuid, which must be unique across the entire
517recorded trace. To minimize the chances of accidental collisions, the uuids
518of child tracks are combined with those of their parents, with each
519`ProcessTrack` having a random, per-process uuid.
520
521By default, track events (e.g., `TRACE_EVENT`) use the `ThreadTrack` for the
522calling thread. This can be overridden, for example, to mark events that
523begin and end on a different thread:
524
525```C++
526void OnNewRequest(size_t request_id) {
527  // Open a slice when the request came in.
528  TRACE_EVENT_BEGIN("category", "HandleRequest", perfetto::Track(request_id));
529
530  // Start a thread to handle the request.
531  std::thread worker_thread([=] {
532    // ... produce response ...
533
534    // Close the slice for the request now that we finished handling it.
535    TRACE_EVENT_END("category", perfetto::Track(request_id));
536  });
537```
538Tracks can also optionally be annotated with metadata:
539
540```C++
541auto desc = track.Serialize();
542desc.set_name("MyTrack");
543perfetto::TrackEvent::SetTrackDescriptor(track, desc);
544```
545
546Threads and processes can also be named in a similar way, e.g.:
547
548```C++
549auto desc = perfetto::ProcessTrack::Current().Serialize();
550desc.mutable_process()->set_process_name("MyProcess");
551perfetto::TrackEvent::SetTrackDescriptor(
552    perfetto::ProcessTrack::Current(), desc);
553```
554
555The metadata remains valid between tracing sessions. To free up data for a
556track, call EraseTrackDescriptor:
557
558```C++
559perfetto::TrackEvent::EraseTrackDescriptor(track);
560```
561
562### Flows
563
564Flows can be used to link two (or more) events (slices or instants), to mark
565them as related.
566
567The link is displayed as an arrow in the UI, when one of the events is selected:
568
569![A flow between two slices in the Perfetto UI](
570  /docs/images/flows.png "A flow between two slices in the Perfetto UI")
571
572```C++
573// The same identifier is used in both the related slices.
574uint64_t request_id = GetRequestId();
575
576{
577  TRACE_EVENT("rendering", "HandleRequestPhase1",
578              perfetto::Flow::ProcessScoped(request_id));
579  //...
580}
581
582std::thread t1([&] {
583  TRACE_EVENT("rendering", "HandleRequestPhase2",
584              perfetto::TerminatingFlow::ProcessScoped(request_id));
585  //...
586});
587```
588
589### Counters
590
591Time-varying numeric data can be recorded with the `TRACE_COUNTER` macro:
592
593```C++
594TRACE_COUNTER("category", "MyCounter", 1234.5);
595```
596
597This data is displayed as a counter track in the Perfetto UI:
598
599![A counter track shown in the Perfetto UI](
600  /docs/images/counter-events.png "A counter track shown in the Perfetto UI")
601
602Both integer and floating point counter values are supported. Counters can
603also be annotated with additional information such as units, for example, for
604tracking the rendering framerate in terms of frames per second or "fps":
605
606```C++
607TRACE_COUNTER("category", perfetto::CounterTrack("Framerate", "fps"), 120);
608```
609
610As another example, a memory counter that records bytes but accepts samples
611as kilobytes (to reduce trace binary size) can be defined like this:
612
613```C++
614perfetto::CounterTrack memory_track = perfetto::CounterTrack("Memory")
615    .set_unit("bytes")
616    .set_multiplier(1024);
617TRACE_COUNTER("category", memory_track, 4 /* = 4096 bytes */);
618```
619
620See
621[counter_descriptor.proto](
622/protos/perfetto/trace/track_event/counter_descriptor.proto) for the full set
623of attributes for a counter track.
624
625To record a counter value at a specific point in time (instead of the current
626time), you can pass in a custom timestamp:
627
628```C++
629// First record the current time and counter value.
630uint64_t timestamp = perfetto::TrackEvent::GetTraceTimeNs();
631int64_t value = 1234;
632
633// Later, emit a sample at that point in time.
634TRACE_COUNTER("category", "MyCounter", timestamp, value);
635```
636
637### Interning
638
639Interning can be used to avoid repeating the same constant data (e.g., event
640names) throughout the trace. Perfetto automatically performs interning for
641most strings passed to `TRACE_EVENT`, but it's also possible to also define
642your own types of interned data.
643
644First, define an interning index for your type. It should map to a specific
645field of
646[interned_data.proto](/protos/perfetto/trace/interned_data/interned_data.proto)
647and specify how the interned data is written into that message when seen for
648the first time.
649
650```C++
651struct MyInternedData
652    : public perfetto::TrackEventInternedDataIndex<
653        MyInternedData,
654        perfetto::protos::pbzero::InternedData::kMyInternedDataFieldNumber,
655        const char*> {
656  static void Add(perfetto::protos::pbzero::InternedData* interned_data,
657                   size_t iid,
658                   const char* value) {
659    auto my_data = interned_data->add_my_interned_data();
660    my_data->set_iid(iid);
661    my_data->set_value(value);
662  }
663};
664```
665
666Next, use your interned data in a trace point as shown below. The interned
667string will only be emitted the first time the trace point is hit (unless the
668trace buffer has wrapped around).
669
670```C++
671TRACE_EVENT(
672   "category", "Event", [&](perfetto::EventContext ctx) {
673     auto my_message = ctx.event()->set_my_message();
674     size_t iid = MyInternedData::Get(&ctx, "Repeated data to be interned");
675     my_message->set_iid(iid);
676   });
677```
678
679Note that interned data is strongly typed, i.e., each class of interned data
680uses a separate namespace for identifiers.
681
682### Tracing session observers
683
684The session observer interface allows applications to be notified when track
685event tracing starts and stops:
686
687```C++
688class Observer : public perfetto::TrackEventSessionObserver {
689  public:
690  ~Observer() override = default;
691
692  void OnSetup(const perfetto::DataSourceBase::SetupArgs&) override {
693    // Called when tracing session is configured. Note tracing isn't active yet,
694    // so track events emitted here won't be recorded.
695  }
696
697  void OnStart(const perfetto::DataSourceBase::StartArgs&) override {
698    // Called when a tracing session is started. It is possible to emit track
699    // events from this callback.
700  }
701
702  void OnStop(const perfetto::DataSourceBase::StopArgs&) override {
703    // Called when a tracing session is stopped. It is still possible to emit
704    // track events from this callback.
705  }
706};
707```
708
709Note that all methods of the interface are called on an internal Perfetto
710thread.
711
712For example, here's how to wait for any tracing session to start:
713
714```C++
715class Observer : public perfetto::TrackEventSessionObserver {
716 public:
717  Observer() { perfetto::TrackEvent::AddSessionObserver(this); }
718  ~Observer() { perfetto::TrackEvent::RemoveSessionObserver(this); }
719
720  void OnStart(const perfetto::DataSourceBase::StartArgs&) override {
721    std::unique_lock<std::mutex> lock(mutex);
722    cv.notify_one();
723  }
724
725  void WaitForTracingStart() {
726    printf("Waiting for tracing to start...\n");
727    std::unique_lock<std::mutex> lock(mutex);
728    cv.wait(lock, [] { return perfetto::TrackEvent::IsEnabled(); });
729    printf("Tracing started\n");
730  }
731
732  std::mutex mutex;
733  std::condition_variable cv;
734};
735
736Observer observer;
737observer.WaitForTracingToStart();
738```
739
740[RAII]: https://en.cppreference.com/w/cpp/language/raii
741