Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 1 | # Track events (Tracing SDK) |
| 2 | |
| 3 | Track 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 |
| 7 | with a *track*, which is a timeline of monotonically increasing time. A track |
| 8 | corresponds to an independent sequence of execution, such as a single thread |
| 9 | in a process. |
| 10 | |
| 11 |  |
| 13 | |
| 14 | See the [Getting started](/docs/instrumentation/tracing-sdk#getting-started) |
| 15 | section of the Tracing SDK page for instructions on how to check out and |
| 16 | build the SDK. |
| 17 | |
Sami Kyostila | 2778ac8 | 2020-12-21 16:12:27 +0000 | [diff] [blame] | 18 | TIP: The code from these examples is also available [in the |
| 19 | repository](/examples/sdk/README.md). |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 20 | |
| 21 | There 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. (Not fully implemented yet). |
| 36 | |
| 37 | The [Perfetto UI](https://ui.perfetto.dev) has built in support for track |
| 38 | events, which provides a useful way to quickly visualize the internal |
| 39 | processing of an app. For example, the [Chrome |
| 40 | browser](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool) |
| 41 | is deeply instrumented with track events to assist in debugging, development |
| 42 | and performance analysis. |
| 43 | |
| 44 | To start using track events, first define the set of categories that your events |
| 45 | will fall into. Each category can be separately enabled or disabled for tracing |
| 46 | (see [Category configuration](#category-configuration)). |
| 47 | |
| 48 | Add 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 | |
| 54 | PERFETTO_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 | |
| 61 | Then, 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 | |
| 67 | PERFETTO_TRACK_EVENT_STATIC_STORAGE(); |
| 68 | ``` |
| 69 | |
| 70 | Finally, initialize track events after the client library is brought up: |
| 71 | |
| 72 | ```C++ |
| 73 | int main(int argv, char** argc) { |
| 74 | ... |
| 75 | perfetto::Tracing::Initialize(args); |
| 76 | perfetto::TrackEvent::Register(); // Add this. |
| 77 | } |
| 78 | ``` |
| 79 | |
| 80 | Now you can add track events to existing functions like this: |
| 81 | |
| 82 | ```C++ |
| 83 | #include "my_app_tracing_categories.h" |
| 84 | |
| 85 | void DrawPlayer() { |
Sami Kyostila | 3d60943 | 2020-06-25 15:59:29 +0100 | [diff] [blame] | 86 | TRACE_EVENT("rendering", "DrawPlayer"); // Begin "DrawPlayer" slice. |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 87 | ... |
Sami Kyostila | 3d60943 | 2020-06-25 15:59:29 +0100 | [diff] [blame] | 88 | // End "DrawPlayer" slice. |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 89 | } |
| 90 | ``` |
| 91 | |
| 92 | This type of trace event is scoped, under the hood it uses C++ [RAII]. The |
| 93 | event will cover the time from when the `TRACE_EVENT` annotation is encountered |
| 94 | to the end of the block (in the example above, until the function returns). |
| 95 | |
Sami Kyostila | 3d60943 | 2020-06-25 15:59:29 +0100 | [diff] [blame] | 96 | For events that don't follow function scoping, use `TRACE_EVENT_BEGIN` and |
| 97 | `TRACE_EVENT_END`: |
| 98 | |
| 99 | ```C++ |
| 100 | void 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 | |
| 113 | Note that you don't need to give a name for `TRACE_EVENT_END`, since it |
| 114 | automatically closes the most recent event that began on the same thread. In |
| 115 | other words, all events on a given thread share the same stack. This means |
| 116 | that 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 |
| 118 | might terminate the original event unexpectedly; for events that cross |
| 119 | function boundaries it's usually best to emit them on a [separate |
| 120 | track](#tracks). |
| 121 | |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 122 | You can also supply (up to two) debug annotations together with the event. |
| 123 | |
| 124 | ```C++ |
| 125 | int player_number = 1; |
| 126 | TRACE_EVENT("rendering", "DrawPlayer", "player_number", player_number); |
| 127 | ``` |
| 128 | |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 129 | See [below](#track-event-arguments) for the other types of supported track |
| 130 | event arguments. For more complex arguments, you can define [your own |
| 131 | protobuf messages](/protos/perfetto/trace/track_event/track_event.proto) and |
| 132 | emit them as a parameter for the event. |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 133 | |
| 134 | NOTE: 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 | |
| 139 | As 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 |
| 143 | message PlayerInfo { |
| 144 | optional string name = 1; |
| 145 | optional uint64 score = 2; |
| 146 | } |
| 147 | ``` |
| 148 | |
| 149 | This new file should also be added to |
| 150 | `protos/perfetto/trace/track_event/BUILD.gn`: |
| 151 | |
| 152 | ```json |
| 153 | sources = [ |
| 154 | ... |
| 155 | "player_info.proto" |
| 156 | ] |
| 157 | ``` |
| 158 | |
| 159 | Also, a matching argument should be added to the track event message |
| 160 | definition in |
| 161 | `protos/perfetto/trace/track_event/track_event.proto`: |
| 162 | |
| 163 | ```protobuf |
| 164 | import "protos/perfetto/trace/track_event/player_info.proto"; |
| 165 | |
| 166 | ... |
| 167 | |
| 168 | message TrackEvent { |
| 169 | ... |
| 170 | // New argument types go here. |
| 171 | optional PlayerInfo player_info = 1000; |
| 172 | } |
| 173 | ``` |
| 174 | |
| 175 | The corresponding trace point could look like this: |
| 176 | |
| 177 | ```C++ |
| 178 | Player my_player; |
| 179 | TRACE_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 | |
| 186 | The lambda function passed to the macro is only called if tracing is enabled for |
| 187 | the given category. It is always called synchronously and possibly multiple |
| 188 | times if multiple concurrent tracing sessions are active. |
| 189 | |
| 190 | Now that you have instrumented your app with track events, you are ready to |
| 191 | start [recording traces](tracing-sdk.md#recording). |
| 192 | |
| 193 | ## Category configuration |
| 194 | |
| 195 | All track events are assigned to one more trace categories. For example: |
| 196 | |
| 197 | ```C++ |
| 198 | TRACE_EVENT("rendering", ...); // Event in the "rendering" category. |
| 199 | ``` |
| 200 | |
| 201 | By default, all non-debug and non-slow track event categories are enabled for |
| 202 | tracing. *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 | |
| 209 | Category tags can be can be defined like this: |
| 210 | |
| 211 | ```C++ |
| 212 | perfetto::Category("rendering.debug") |
| 213 | .SetDescription("Debug events from the graphics subsystem") |
| 214 | .SetTags("debug", "my_custom_tag") |
| 215 | ``` |
| 216 | |
| 217 | A single trace event can also belong to multiple categories: |
| 218 | |
| 219 | ```C++ |
| 220 | // Event in the "rendering" and "benchmark" categories. |
| 221 | TRACE_EVENT("rendering,benchmark", ...); |
| 222 | ``` |
| 223 | |
| 224 | A corresponding category group entry must be added to the category registry: |
| 225 | |
| 226 | ```C++ |
| 227 | perfetto::Category::Group("rendering,benchmark") |
| 228 | ``` |
| 229 | |
| 230 | It's also possible to efficiently query whether a given category is enabled |
| 231 | for tracing: |
| 232 | |
| 233 | ```C++ |
| 234 | if (TRACE_EVENT_CATEGORY_ENABLED("rendering")) { |
| 235 | // ... |
| 236 | } |
| 237 | ``` |
| 238 | |
| 239 | The `TrackEventConfig` field in Perfetto's `TraceConfig` can be used to |
| 240 | select which categories are enabled for tracing: |
| 241 | |
| 242 | ```protobuf |
| 243 | message 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 | |
| 253 | To determine if a category is enabled, it is checked against the filters in the |
| 254 | following order: |
| 255 | |
| 256 | 1. Exact matches in enabled categories. |
| 257 | 2. Exact matches in enabled tags. |
| 258 | 3. Exact matches in disabled categories. |
| 259 | 4. Exact matches in disabled tags. |
| 260 | 5. Pattern matches in enabled categories. |
| 261 | 6. Pattern matches in enabled tags. |
| 262 | 7. Pattern matches in disabled categories. |
| 263 | 8. Pattern matches in disabled tags. |
| 264 | |
| 265 | If none of the steps produced a match, the category is enabled by default. In |
| 266 | other words, every category is implicitly enabled unless specifically disabled. |
| 267 | For example: |
| 268 | |
| 269 | | Setting | Needed configuration | |
| 270 | | ------------------------------- | -------------------------------------------- | |
| 271 | | Enable just specific categories | `enabled_categories = [“foo”, “bar”, “baz”]` | |
| 272 | | | `disabled_categories = [“*”]` | |
| 273 | | Enable all non-slow categories | (Happens by default.) | |
| 274 | | Enable specific tags | `disabled_tags = [“*”]` | |
| 275 | | | `enabled_tags = [“foo”, “bar”]` | |
| 276 | |
| 277 | ## Dynamic and test-only categories |
| 278 | |
| 279 | Ideally all trace categories should be defined at compile time as shown |
| 280 | above, as this ensures trace points will have minimal runtime and binary size |
| 281 | overhead. However, in some cases trace categories can only be determined at |
| 282 | runtime (e.g., they come from instrumentation in a dynamically loaded JavaScript |
| 283 | running in a WebView or in a NodeJS engine). These can be used by trace points |
| 284 | as follows: |
| 285 | |
| 286 | ```C++ |
| 287 | perfetto::DynamicCategory dynamic_category{"nodejs.something"}; |
| 288 | TRACE_EVENT(dynamic_category, "SomeEvent", ...); |
| 289 | ``` |
| 290 | |
| 291 | TIP: It's also possible to use dynamic event names by passing `nullptr` as |
| 292 | the name and filling in the `TrackEvent::name` field manually. |
| 293 | |
| 294 | Some trace categories are only useful for testing, and they should not make |
| 295 | it into a production binary. These types of categories can be defined with a |
| 296 | list of prefix strings: |
| 297 | |
| 298 | ```C++ |
| 299 | PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES( |
| 300 | "test", // Applies to test.* |
| 301 | "dontship" // Applies to dontship.*. |
| 302 | ); |
| 303 | ``` |
| 304 | |
| 305 | ## Performance |
| 306 | |
| 307 | Perfetto's trace points are designed to have minimal overhead when tracing is |
| 308 | disabled while providing high throughput for data intensive tracing use |
| 309 | cases. While exact timings will depend on your system, there is a |
| 310 | [microbenchmark](/src/tracing/api_benchmark.cc) which gives some ballpark |
| 311 | figures: |
| 312 | |
| 313 | | Scenario | Runtime on Pixel 3 XL | Runtime on ThinkStation P920 | |
| 314 | | -------- | --------------------- | ---------------------------- | |
| 315 | | `TRACE_EVENT(...)` (disabled) | 2 ns | 1 ns | |
| 316 | | `TRACE_EVENT("cat", "name")` | 285 ns | 630 ns | |
| 317 | | `TRACE_EVENT("cat", "name", <lambda>)` | 304 ns | 663 ns | |
| 318 | | `TRACE_EVENT("cat", "name", "key", value)` | 354 ns | 664 ns | |
| 319 | | `DataSource::Trace(<lambda>)` (disabled) | 2 ns | 1 ns | |
| 320 | | `DataSource::Trace(<lambda>)` | 133 ns | 58 ns | |
| 321 | |
| 322 | ## Advanced topics |
| 323 | |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 324 | ### Track event arguments |
| 325 | |
| 326 | The following optional arguments can be passed to `TRACE_EVENT` to add extra |
| 327 | information to events: |
| 328 | |
| 329 | ```C++ |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 330 | TRACE_EVENT("cat", "name"[, track][, timestamp] |
| 331 | [, "debug_name1", debug_value1] |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 332 | [, "debug_name2", debug_value2] |
| 333 | ... |
| 334 | [, "debug_nameN", debug_valueN] |
| 335 | [, lambda]); |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 336 | ``` |
| 337 | |
| 338 | Some examples of valid combinations: |
| 339 | |
| 340 | 1. A lambda for writing custom TrackEvent fields: |
| 341 | |
| 342 | ```C++ |
| 343 | TRACE_EVENT("category", "Name", [&](perfetto::EventContext ctx) { |
| 344 | ctx.event()->set_custom_value(...); |
| 345 | }); |
| 346 | ``` |
| 347 | |
| 348 | 2. A timestamp and a lambda: |
| 349 | |
| 350 | ```C++ |
| 351 | TRACE_EVENT("category", "Name", time_in_nanoseconds, |
| 352 | [&](perfetto::EventContext ctx) { |
| 353 | ctx.event()->set_custom_value(...); |
| 354 | }); |
| 355 | ``` |
| 356 | |
Alexander Timin | 37c8afd | 2021-04-06 18:42:18 +0000 | [diff] [blame] | 357 | |time_in_nanoseconds| should be an uint64_t by default. To support custom |
| 358 | timestamp types, |
| 359 | |perfetto::TraceTimestampTraits<MyTimestamp>::ConvertTimestampToTraceTimeNs| |
| 360 | should be defined. See |ConvertTimestampToTraceTimeNs| for more details. |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 361 | |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 362 | 3. Arbitrary number of debug annotations: |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 363 | |
| 364 | ```C++ |
| 365 | TRACE_EVENT("category", "Name", "arg", value); |
| 366 | TRACE_EVENT("category", "Name", "arg", value, "arg2", value2); |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 367 | TRACE_EVENT("category", "Name", "arg", value, "arg2", value2, |
| 368 | "arg3", value3); |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 369 | ``` |
| 370 | |
| 371 | See |TracedValue| for recording custom types as debug annotations. |
| 372 | |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 373 | 4. Arbitrary number of debug annotations and a lambda: |
| 374 | |
| 375 | ```C++ |
| 376 | TRACE_EVENT("category", "Name", "arg", value, |
| 377 | [&](perfetto::EventContext ctx) { |
| 378 | ctx.event()->set_custom_value(...); |
| 379 | }); |
| 380 | ``` |
| 381 | |
| 382 | 5. An overridden track: |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 383 | |
| 384 | ```C++ |
| 385 | TRACE_EVENT("category", "Name", perfetto::Track(1234)); |
| 386 | ``` |
| 387 | |
| 388 | See |Track| for other types of tracks which may be used. |
| 389 | |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 390 | 6. A track and a lambda: |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 391 | |
| 392 | ```C++ |
| 393 | TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| 394 | [&](perfetto::EventContext ctx) { |
| 395 | ctx.event()->set_custom_value(...); |
| 396 | }); |
| 397 | ``` |
| 398 | |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 399 | 7. A track and a timestamp: |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 400 | |
| 401 | ```C++ |
| 402 | TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| 403 | time_in_nanoseconds); |
| 404 | ``` |
| 405 | |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 406 | 8. A track, a timestamp and a lambda: |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 407 | |
| 408 | ```C++ |
| 409 | TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| 410 | time_in_nanoseconds, [&](perfetto::EventContext ctx) { |
| 411 | ctx.event()->set_custom_value(...); |
| 412 | }); |
| 413 | ``` |
| 414 | |
Alexander Timin | 129c37c | 2021-04-08 19:17:59 +0000 | [diff] [blame^] | 415 | 9. A track and an arbitrary number of debug annotions: |
Sami Kyostila | 655fa3d | 2021-03-05 13:11:08 +0000 | [diff] [blame] | 416 | |
| 417 | ```C++ |
| 418 | TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| 419 | "arg", value); |
| 420 | TRACE_EVENT("category", "Name", perfetto::Track(1234), |
| 421 | "arg", value, "arg2", value2); |
| 422 | ``` |
| 423 | |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 424 | ### Tracks |
| 425 | |
| 426 | Every track event is associated with a track, which specifies the timeline |
| 427 | the event belongs to. In most cases, a track corresponds to a visual |
| 428 | horizontal track in the Perfetto UI like this: |
| 429 | |
| 430 |  |
| 432 | |
| 433 | Events that describe parallel sequences (e.g., separate |
| 434 | threads) should use separate tracks, while sequential events (e.g., nested |
| 435 | function calls) generally belong on the same track. |
| 436 | |
| 437 | Perfetto supports three kinds of tracks: |
| 438 | |
| 439 | - `Track` – a basic timeline. |
| 440 | |
| 441 | - `ProcessTrack` – a timeline that represents a single process in the system. |
| 442 | |
| 443 | - `ThreadTrack` – a timeline that represents a single thread in the system. |
| 444 | |
| 445 | Tracks can have a parent track, which is used to group related tracks |
| 446 | together. For example, the parent of a `ThreadTrack` is the `ProcessTrack` of |
| 447 | the process the thread belongs to. By default, tracks are grouped under the |
| 448 | current process's `ProcessTrack`. |
| 449 | |
| 450 | A track is identified by a uuid, which must be unique across the entire |
| 451 | recorded trace. To minimize the chances of accidental collisions, the uuids |
| 452 | of child tracks are combined with those of their parents, with each |
| 453 | `ProcessTrack` having a random, per-process uuid. |
| 454 | |
| 455 | By default, track events (e.g., `TRACE_EVENT`) use the `ThreadTrack` for the |
| 456 | calling thread. This can be overridden, for example, to mark events that |
| 457 | begin and end on a different thread: |
| 458 | |
| 459 | ```C++ |
| 460 | void OnNewRequest(size_t request_id) { |
| 461 | // Open a slice when the request came in. |
| 462 | TRACE_EVENT_BEGIN("category", "HandleRequest", perfetto::Track(request_id)); |
| 463 | |
| 464 | // Start a thread to handle the request. |
| 465 | std::thread worker_thread([=] { |
| 466 | // ... produce response ... |
| 467 | |
| 468 | // Close the slice for the request now that we finished handling it. |
| 469 | TRACE_EVENT_END("category", perfetto::Track(request_id)); |
| 470 | }); |
| 471 | ``` |
| 472 | Tracks can also optionally be annotated with metadata: |
| 473 | |
| 474 | ```C++ |
| 475 | auto desc = track.Serialize(); |
| 476 | desc.set_name("MyTrack"); |
| 477 | perfetto::TrackEvent::SetTrackDescriptor(track, desc); |
| 478 | ``` |
| 479 | |
| 480 | Threads and processes can also be named in a similar way, e.g.: |
| 481 | |
| 482 | ```C++ |
| 483 | auto desc = perfetto::ProcessTrack::Current().Serialize(); |
| 484 | desc.mutable_process()->set_process_name("MyProcess"); |
| 485 | perfetto::TrackEvent::SetTrackDescriptor( |
| 486 | perfetto::ProcessTrack::Current(), desc); |
| 487 | ``` |
| 488 | |
| 489 | The metadata remains valid between tracing sessions. To free up data for a |
| 490 | track, call EraseTrackDescriptor: |
| 491 | |
| 492 | ```C++ |
| 493 | perfetto::TrackEvent::EraseTrackDescriptor(track); |
| 494 | ``` |
| 495 | |
Sami Kyostila | edf7c86 | 2021-03-11 13:33:35 +0000 | [diff] [blame] | 496 | ### Counters |
| 497 | |
| 498 | Time-varying numeric data can be recorded with the `TRACE_COUNTER` macro: |
| 499 | |
| 500 | ```C++ |
| 501 | TRACE_COUNTER("category", "MyCounter", 1234.5); |
| 502 | ``` |
| 503 | |
| 504 | This data is displayed as a counter track in the Perfetto UI: |
| 505 | |
| 506 |  |
| 508 | |
| 509 | Both integer and floating point counter values are supported. Counters can |
| 510 | also be annotated with additional information such as units, for example, for |
| 511 | tracking the rendering framerate in terms of frames per second or "fps": |
| 512 | |
| 513 | ```C++ |
| 514 | TRACE_COUNTER("category", perfetto::CounterTrack("Framerate", "fps"), 120); |
| 515 | ``` |
| 516 | |
| 517 | As another example, a memory counter that records bytes but accepts samples |
| 518 | as kilobytes (to reduce trace binary size) can be defined like this: |
| 519 | |
| 520 | ```C++ |
| 521 | perfetto::CounterTrack memory_track = perfetto::CounterTrack("Memory") |
| 522 | .set_unit("bytes") |
| 523 | .set_multiplier(1024); |
| 524 | TRACE_COUNTER("category", memory_track, 4 /* = 4096 bytes */); |
| 525 | ``` |
| 526 | |
| 527 | See |
| 528 | [counter_descriptor.proto]( |
| 529 | /protos/perfetto/trace/track_event/counter_descriptor.proto) for the full set |
| 530 | of attributes for a counter track. |
| 531 | |
| 532 | To record a counter value at a specific point in time (instead of the current |
| 533 | time), you can pass in a custom timestamp: |
| 534 | |
| 535 | ```C++ |
| 536 | // First record the current time and counter value. |
| 537 | uint64_t timestamp = perfetto::TrackEvent::GetTraceTimeNs(); |
| 538 | int64_t value = 1234; |
| 539 | |
| 540 | // Later, emit a sample at that point in time. |
| 541 | TRACE_COUNTER("category", "MyCounter", timestamp, value); |
| 542 | ``` |
| 543 | |
Primiano Tucci | a662485 | 2020-05-21 19:12:50 +0100 | [diff] [blame] | 544 | ### Interning |
| 545 | |
| 546 | Interning can be used to avoid repeating the same constant data (e.g., event |
| 547 | names) throughout the trace. Perfetto automatically performs interning for |
| 548 | most strings passed to `TRACE_EVENT`, but it's also possible to also define |
| 549 | your own types of interned data. |
| 550 | |
| 551 | First, define an interning index for your type. It should map to a specific |
| 552 | field of |
| 553 | [interned_data.proto](/protos/perfetto/trace/interned_data/interned_data.proto) |
| 554 | and specify how the interned data is written into that message when seen for |
| 555 | the first time. |
| 556 | |
| 557 | ```C++ |
| 558 | struct MyInternedData |
| 559 | : public perfetto::TrackEventInternedDataIndex< |
| 560 | MyInternedData, |
| 561 | perfetto::protos::pbzero::InternedData::kMyInternedDataFieldNumber, |
| 562 | const char*> { |
| 563 | static void Add(perfetto::protos::pbzero::InternedData* interned_data, |
| 564 | size_t iid, |
| 565 | const char* value) { |
| 566 | auto my_data = interned_data->add_my_interned_data(); |
| 567 | my_data->set_iid(iid); |
| 568 | my_data->set_value(value); |
| 569 | } |
| 570 | }; |
| 571 | ``` |
| 572 | |
| 573 | Next, use your interned data in a trace point as shown below. The interned |
| 574 | string will only be emitted the first time the trace point is hit (unless the |
| 575 | trace buffer has wrapped around). |
| 576 | |
| 577 | ```C++ |
| 578 | TRACE_EVENT( |
| 579 | "category", "Event", [&](perfetto::EventContext ctx) { |
| 580 | auto my_message = ctx.event()->set_my_message(); |
| 581 | size_t iid = MyInternedData::Get(&ctx, "Repeated data to be interned"); |
| 582 | my_message->set_iid(iid); |
| 583 | }); |
| 584 | ``` |
| 585 | |
| 586 | Note that interned data is strongly typed, i.e., each class of interned data |
| 587 | uses a separate namespace for identifiers. |
| 588 | |
Sami Kyostila | 2778ac8 | 2020-12-21 16:12:27 +0000 | [diff] [blame] | 589 | ### Tracing session observers |
| 590 | |
| 591 | The session observer interface allows applications to be notified when track |
| 592 | event tracing starts and stops: |
| 593 | |
| 594 | ```C++ |
| 595 | class Observer : public perfetto::TrackEventSessionObserver { |
| 596 | public: |
| 597 | ~Observer() override = default; |
| 598 | |
| 599 | void OnSetup(const perfetto::DataSourceBase::SetupArgs&) override { |
| 600 | // Called when tracing session is configured. Note tracing isn't active yet, |
| 601 | // so track events emitted here won't be recorded. |
| 602 | } |
| 603 | |
| 604 | void OnStart(const DataSourceBase::SetupArgs&) override { |
| 605 | // Called when a tracing session is started. It is possible to emit track |
| 606 | // events from this callback. |
| 607 | } |
| 608 | |
| 609 | void OnStop(const DataSourceBase::StartArgs&) override { |
| 610 | // Called when a tracing session is stopped. It is still possible to emit |
| 611 | // track events from this callback. |
| 612 | } |
| 613 | }; |
| 614 | ``` |
| 615 | |
| 616 | Note that all methods of the interface are called on an internal Perfetto |
| 617 | thread. |
| 618 | |
| 619 | For example, here's how to wait for any tracing session to start: |
| 620 | |
| 621 | ```C++ |
| 622 | class Observer : public perfetto::TrackEventSessionObserver { |
| 623 | public: |
| 624 | Observer() { perfetto::TrackEvent::AddSessionObserver(this); } |
| 625 | ~Observer() { perfetto::TrackEvent::RemoveSessionObserver(this); } |
| 626 | |
| 627 | void OnStart(const perfetto::DataSourceBase::StartArgs&) override { |
| 628 | std::unique_lock<std::mutex> lock(mutex); |
| 629 | cv.notify_one(); |
| 630 | } |
| 631 | |
| 632 | void WaitForTracingStart() { |
| 633 | printf("Waiting for tracing to start...\n"); |
| 634 | std::unique_lock<std::mutex> lock(mutex); |
| 635 | cv.wait(lock, [] { return perfetto::TrackEvent::IsEnabled(); }); |
| 636 | printf("Tracing started\n"); |
| 637 | } |
| 638 | |
| 639 | std::mutex mutex; |
| 640 | std::condition_variable cv; |
| 641 | }; |
| 642 | |
| 643 | Observer observer; |
| 644 | observer.WaitForTracingToStart(); |
| 645 | ``` |
| 646 | |
Alexander Timin | 37c8afd | 2021-04-06 18:42:18 +0000 | [diff] [blame] | 647 | [RAII]: https://en.cppreference.com/w/cpp/language/raii |