|
|
# Track events (Tracing SDK)
|
|
|
|
|
|
Track events are part of the [Perfetto Tracing SDK](tracing-sdk.md).
|
|
|
|
|
|
*Track events* are application specific, time bounded events recorded into a
|
|
|
*trace* while the application is running. Track events are always associated
|
|
|
with a *track*, which is a timeline of monotonically increasing time. A track
|
|
|
corresponds to an independent sequence of execution, such as a single thread
|
|
|
in a process.
|
|
|
|
|
|

|
|
|
|
|
|
See the [Getting started](/docs/instrumentation/tracing-sdk#getting-started)
|
|
|
section of the Tracing SDK page for instructions on how to check out and
|
|
|
build the SDK.
|
|
|
|
|
|
TIP: The code from these examples is also available [in the
|
|
|
repository](/examples/sdk/README.md).
|
|
|
|
|
|
There are a few main types of track events:
|
|
|
|
|
|
- **Slices**, which represent nested, time bounded operations. For example,
|
|
|
a slice could cover the time period from when a function begins executing
|
|
|
to when it returns, the time spent loading a file from the network or the
|
|
|
time to complete a user journey.
|
|
|
|
|
|
- **Counters**, which are snapshots of time-varying numeric values. For
|
|
|
example, a track event can record instantaneous the memory usage of a
|
|
|
process during its execution.
|
|
|
|
|
|
- **Flows**, which are used to connect related slices that span different
|
|
|
tracks together. For example, if an image file is first loaded from
|
|
|
the network and then decoded on a thread pool, a flow event can be used to
|
|
|
highlight its path through the system. (Not fully implemented yet).
|
|
|
|
|
|
The [Perfetto UI](https://ui.perfetto.dev) has built in support for track
|
|
|
events, which provides a useful way to quickly visualize the internal
|
|
|
processing of an app. For example, the [Chrome
|
|
|
browser](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)
|
|
|
is deeply instrumented with track events to assist in debugging, development
|
|
|
and performance analysis.
|
|
|
|
|
|
To start using track events, first define the set of categories that your events
|
|
|
will fall into. Each category can be separately enabled or disabled for tracing
|
|
|
(see [Category configuration](#category-configuration)).
|
|
|
|
|
|
Add the list of categories into a header file (e.g.,
|
|
|
`my_app_tracing_categories.h`) like this:
|
|
|
|
|
|
```C++
|
|
|
#include <perfetto.h>
|
|
|
|
|
|
PERFETTO_DEFINE_CATEGORIES(
|
|
|
perfetto::Category("rendering")
|
|
|
.SetDescription("Events from the graphics subsystem"),
|
|
|
perfetto::Category("network")
|
|
|
.SetDescription("Network upload and download statistics"));
|
|
|
```
|
|
|
|
|
|
Then, declare static storage for the categories in a cc file (e.g.,
|
|
|
`my_app_tracing_categories.cc`):
|
|
|
|
|
|
```C++
|
|
|
#include "my_app_tracing_categories.h"
|
|
|
|
|
|
PERFETTO_TRACK_EVENT_STATIC_STORAGE();
|
|
|
```
|
|
|
|
|
|
Finally, initialize track events after the client library is brought up:
|
|
|
|
|
|
```C++
|
|
|
int main(int argv, char** argc) {
|
|
|
...
|
|
|
perfetto::Tracing::Initialize(args);
|
|
|
perfetto::TrackEvent::Register(); // Add this.
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Now you can add track events to existing functions like this:
|
|
|
|
|
|
```C++
|
|
|
#include "my_app_tracing_categories.h"
|
|
|
|
|
|
void DrawPlayer() {
|
|
|
TRACE_EVENT("rendering", "DrawPlayer"); // Begin "DrawPlayer" slice.
|
|
|
...
|
|
|
// End "DrawPlayer" slice.
|
|
|
}
|
|
|
```
|
|
|
|
|
|
This type of trace event is scoped, under the hood it uses C++ [RAII]. The
|
|
|
event will cover the time from when the `TRACE_EVENT` annotation is encountered
|
|
|
to the end of the block (in the example above, until the function returns).
|
|
|
|
|
|
For events that don't follow function scoping, use `TRACE_EVENT_BEGIN` and
|
|
|
`TRACE_EVENT_END`:
|
|
|
|
|
|
```C++
|
|
|
void LoadGame() {
|
|
|
DisplayLoadingScreen();
|
|
|
|
|
|
TRACE_EVENT_BEGIN("io", "Loading"); // Begin "Loading" slice.
|
|
|
LoadCollectibles();
|
|
|
LoadVehicles();
|
|
|
LoadPlayers();
|
|
|
TRACE_EVENT_END("io"); // End "Loading" slice.
|
|
|
|
|
|
StartGame();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Note that you don't need to give a name for `TRACE_EVENT_END`, since it
|
|
|
automatically closes the most recent event that began on the same thread. In
|
|
|
other words, all events on a given thread share the same stack. This means
|
|
|
that it's not recommended to have a matching pair of `TRACE_EVENT_BEGIN` and
|
|
|
`TRACE_EVENT_END` markers in separate functions, since an unrelated event
|
|
|
might terminate the original event unexpectedly; for events that cross
|
|
|
function boundaries it's usually best to emit them on a [separate
|
|
|
track](#tracks).
|
|
|
|
|
|
You can also supply (up to two) debug annotations together with the event.
|
|
|
|
|
|
```C++
|
|
|
int player_number = 1;
|
|
|
TRACE_EVENT("rendering", "DrawPlayer", "player_number", player_number);
|
|
|
```
|
|
|
|
|
|
See [below](#track-event-arguments) for the other types of supported track
|
|
|
event arguments. For more complex arguments, you can define [your own
|
|
|
protobuf messages](/protos/perfetto/trace/track_event/track_event.proto) and
|
|
|
emit them as a parameter for the event.
|
|
|
|
|
|
NOTE: Currently custom protobuf messages need to be added directly to the
|
|
|
Perfetto repository under `protos/perfetto/trace`, and Perfetto itself
|
|
|
must also be rebuilt. We are working
|
|
|
[to lift this limitation](https://github.com/google/perfetto/issues/11).
|
|
|
|
|
|
As an example of a custom track event argument type, save the following as
|
|
|
`protos/perfetto/trace/track_event/player_info.proto`:
|
|
|
|
|
|
```protobuf
|
|
|
message PlayerInfo {
|
|
|
optional string name = 1;
|
|
|
optional uint64 score = 2;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
This new file should also be added to
|
|
|
`protos/perfetto/trace/track_event/BUILD.gn`:
|
|
|
|
|
|
```json
|
|
|
sources = [
|
|
|
...
|
|
|
"player_info.proto"
|
|
|
]
|
|
|
```
|
|
|
|
|
|
Also, a matching argument should be added to the track event message
|
|
|
definition in
|
|
|
`protos/perfetto/trace/track_event/track_event.proto`:
|
|
|
|
|
|
```protobuf
|
|
|
import "protos/perfetto/trace/track_event/player_info.proto";
|
|
|
|
|
|
...
|
|
|
|
|
|
message TrackEvent {
|
|
|
...
|
|
|
// New argument types go here.
|
|
|
optional PlayerInfo player_info = 1000;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The corresponding trace point could look like this:
|
|
|
|
|
|
```C++
|
|
|
Player my_player;
|
|
|
TRACE_EVENT("category", "MyEvent", [&](perfetto::EventContext ctx) {
|
|
|
auto player = ctx.event()->set_player_info();
|
|
|
player->set_name(my_player.name());
|
|
|
player->set_player_score(my_player.score());
|
|
|
});
|
|
|
```
|
|
|
|
|
|
The lambda function passed to the macro is only called if tracing is enabled for
|
|
|
the given category. It is always called synchronously and possibly multiple
|
|
|
times if multiple concurrent tracing sessions are active.
|
|
|
|
|
|
Now that you have instrumented your app with track events, you are ready to
|
|
|
start [recording traces](tracing-sdk.md#recording).
|
|
|
|
|
|
## Category configuration
|
|
|
|
|
|
All track events are assigned to one more trace categories. For example:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("rendering", ...); // Event in the "rendering" category.
|
|
|
```
|
|
|
|
|
|
By default, all non-debug and non-slow track event categories are enabled for
|
|
|
tracing. *Debug* and *slow* categories are categories with special tags:
|
|
|
|
|
|
- `"debug"` categories can give more verbose debugging output for a particular
|
|
|
subsystem.
|
|
|
- `"slow"` categories record enough data that they can affect the interactive
|
|
|
performance of your app.
|
|
|
|
|
|
Category tags can be can be defined like this:
|
|
|
|
|
|
```C++
|
|
|
perfetto::Category("rendering.debug")
|
|
|
.SetDescription("Debug events from the graphics subsystem")
|
|
|
.SetTags("debug", "my_custom_tag")
|
|
|
```
|
|
|
|
|
|
A single trace event can also belong to multiple categories:
|
|
|
|
|
|
```C++
|
|
|
// Event in the "rendering" and "benchmark" categories.
|
|
|
TRACE_EVENT("rendering,benchmark", ...);
|
|
|
```
|
|
|
|
|
|
A corresponding category group entry must be added to the category registry:
|
|
|
|
|
|
```C++
|
|
|
perfetto::Category::Group("rendering,benchmark")
|
|
|
```
|
|
|
|
|
|
It's also possible to efficiently query whether a given category is enabled
|
|
|
for tracing:
|
|
|
|
|
|
```C++
|
|
|
if (TRACE_EVENT_CATEGORY_ENABLED("rendering")) {
|
|
|
// ...
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The `TrackEventConfig` field in Perfetto's `TraceConfig` can be used to
|
|
|
select which categories are enabled for tracing:
|
|
|
|
|
|
```protobuf
|
|
|
message TrackEventConfig {
|
|
|
// Each list item is a glob. Each category is matched against the lists
|
|
|
// as explained below.
|
|
|
repeated string disabled_categories = 1; // Default: []
|
|
|
repeated string enabled_categories = 2; // Default: []
|
|
|
repeated string disabled_tags = 3; // Default: [“slow”, “debug”]
|
|
|
repeated string enabled_tags = 4; // Default: []
|
|
|
}
|
|
|
```
|
|
|
|
|
|
To determine if a category is enabled, it is checked against the filters in the
|
|
|
following order:
|
|
|
|
|
|
1. Exact matches in enabled categories.
|
|
|
2. Exact matches in enabled tags.
|
|
|
3. Exact matches in disabled categories.
|
|
|
4. Exact matches in disabled tags.
|
|
|
5. Pattern matches in enabled categories.
|
|
|
6. Pattern matches in enabled tags.
|
|
|
7. Pattern matches in disabled categories.
|
|
|
8. Pattern matches in disabled tags.
|
|
|
|
|
|
If none of the steps produced a match, the category is enabled by default. In
|
|
|
other words, every category is implicitly enabled unless specifically disabled.
|
|
|
For example:
|
|
|
|
|
|
| Setting | Needed configuration |
|
|
|
| ------------------------------- | -------------------------------------------- |
|
|
|
| Enable just specific categories | `enabled_categories = [“foo”, “bar”, “baz”]` |
|
|
|
| | `disabled_categories = [“*”]` |
|
|
|
| Enable all non-slow categories | (Happens by default.) |
|
|
|
| Enable specific tags | `disabled_tags = [“*”]` |
|
|
|
| | `enabled_tags = [“foo”, “bar”]` |
|
|
|
|
|
|
## Dynamic and test-only categories
|
|
|
|
|
|
Ideally all trace categories should be defined at compile time as shown
|
|
|
above, as this ensures trace points will have minimal runtime and binary size
|
|
|
overhead. However, in some cases trace categories can only be determined at
|
|
|
runtime (e.g., they come from instrumentation in a dynamically loaded JavaScript
|
|
|
running in a WebView or in a NodeJS engine). These can be used by trace points
|
|
|
as follows:
|
|
|
|
|
|
```C++
|
|
|
perfetto::DynamicCategory dynamic_category{"nodejs.something"};
|
|
|
TRACE_EVENT(dynamic_category, "SomeEvent", ...);
|
|
|
```
|
|
|
|
|
|
TIP: It's also possible to use dynamic event names by passing `nullptr` as
|
|
|
the name and filling in the `TrackEvent::name` field manually.
|
|
|
|
|
|
Some trace categories are only useful for testing, and they should not make
|
|
|
it into a production binary. These types of categories can be defined with a
|
|
|
list of prefix strings:
|
|
|
|
|
|
```C++
|
|
|
PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES(
|
|
|
"test", // Applies to test.*
|
|
|
"dontship" // Applies to dontship.*.
|
|
|
);
|
|
|
```
|
|
|
|
|
|
## Performance
|
|
|
|
|
|
Perfetto's trace points are designed to have minimal overhead when tracing is
|
|
|
disabled while providing high throughput for data intensive tracing use
|
|
|
cases. While exact timings will depend on your system, there is a
|
|
|
[microbenchmark](/src/tracing/api_benchmark.cc) which gives some ballpark
|
|
|
figures:
|
|
|
|
|
|
| Scenario | Runtime on Pixel 3 XL | Runtime on ThinkStation P920 |
|
|
|
| -------- | --------------------- | ---------------------------- |
|
|
|
| `TRACE_EVENT(...)` (disabled) | 2 ns | 1 ns |
|
|
|
| `TRACE_EVENT("cat", "name")` | 285 ns | 630 ns |
|
|
|
| `TRACE_EVENT("cat", "name", <lambda>)` | 304 ns | 663 ns |
|
|
|
| `TRACE_EVENT("cat", "name", "key", value)` | 354 ns | 664 ns |
|
|
|
| `DataSource::Trace(<lambda>)` (disabled) | 2 ns | 1 ns |
|
|
|
| `DataSource::Trace(<lambda>)` | 133 ns | 58 ns |
|
|
|
|
|
|
## Advanced topics
|
|
|
|
|
|
### Track event arguments
|
|
|
|
|
|
The following optional arguments can be passed to `TRACE_EVENT` to add extra
|
|
|
information to events:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("cat", "name"[, track][, timestamp]
|
|
|
(, "debug_name", debug_value |, TrackEvent::kFieldName, value)*
|
|
|
[, lambda]);
|
|
|
```
|
|
|
|
|
|
Some examples of valid combinations:
|
|
|
|
|
|
1. A lambda for writing custom TrackEvent fields:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", [&](perfetto::EventContext ctx) {
|
|
|
ctx.event()->set_custom_value(...);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
2. A timestamp and a lambda:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", time_in_nanoseconds,
|
|
|
[&](perfetto::EventContext ctx) {
|
|
|
ctx.event()->set_custom_value(...);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
|time_in_nanoseconds| should be an uint64_t by default. To support custom
|
|
|
timestamp types,
|
|
|
|perfetto::TraceTimestampTraits<MyTimestamp>::ConvertTimestampToTraceTimeNs|
|
|
|
should be defined. See |ConvertTimestampToTraceTimeNs| for more details.
|
|
|
|
|
|
3. Arbitrary number of debug annotations:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", "arg", value);
|
|
|
TRACE_EVENT("category", "Name", "arg", value, "arg2", value2);
|
|
|
TRACE_EVENT("category", "Name", "arg", value, "arg2", value2,
|
|
|
"arg3", value3);
|
|
|
```
|
|
|
|
|
|
See |TracedValue| for recording custom types as debug annotations.
|
|
|
|
|
|
4. Arbitrary number of TrackEvent fields (including extensions):
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name",
|
|
|
perfetto::protos::pbzero::TrackEvent::kFieldName, value);
|
|
|
```
|
|
|
|
|
|
5. Arbitrary combination of debug annotations and TrackEvent fields:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name",
|
|
|
perfetto::protos::pbzero::TrackEvent::kFieldName, value1,
|
|
|
"arg", value2);
|
|
|
```
|
|
|
|
|
|
6. Arbitrary combination of debug annotations / TrackEvent fields and a lambda:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", "arg", value1,
|
|
|
pbzero::TrackEvent::kFieldName, value2,
|
|
|
[&](perfetto::EventContext ctx) {
|
|
|
ctx.event()->set_custom_value(...);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
7. An overridden track:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234));
|
|
|
```
|
|
|
|
|
|
See |Track| for other types of tracks which may be used.
|
|
|
|
|
|
8. A track and a lambda:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234),
|
|
|
[&](perfetto::EventContext ctx) {
|
|
|
ctx.event()->set_custom_value(...);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
9. A track and a timestamp:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234),
|
|
|
time_in_nanoseconds);
|
|
|
```
|
|
|
|
|
|
10. A track, a timestamp and a lambda:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234),
|
|
|
time_in_nanoseconds, [&](perfetto::EventContext ctx) {
|
|
|
ctx.event()->set_custom_value(...);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
11. A track and any combination of debug annotions and TrackEvent fields:
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234),
|
|
|
"arg", value);
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234),
|
|
|
"arg", value, "arg2", value2);
|
|
|
TRACE_EVENT("category", "Name", perfetto::Track(1234),
|
|
|
"arg", value, "arg2", value2,
|
|
|
pbzero::TrackEvent::kFieldName, value3);
|
|
|
```
|
|
|
|
|
|
### Tracks
|
|
|
|
|
|
Every track event is associated with a track, which specifies the timeline
|
|
|
the event belongs to. In most cases, a track corresponds to a visual
|
|
|
horizontal track in the Perfetto UI like this:
|
|
|
|
|
|

|
|
|
|
|
|
Events that describe parallel sequences (e.g., separate
|
|
|
threads) should use separate tracks, while sequential events (e.g., nested
|
|
|
function calls) generally belong on the same track.
|
|
|
|
|
|
Perfetto supports three kinds of tracks:
|
|
|
|
|
|
- `Track` – a basic timeline.
|
|
|
|
|
|
- `ProcessTrack` – a timeline that represents a single process in the system.
|
|
|
|
|
|
- `ThreadTrack` – a timeline that represents a single thread in the system.
|
|
|
|
|
|
Tracks can have a parent track, which is used to group related tracks
|
|
|
together. For example, the parent of a `ThreadTrack` is the `ProcessTrack` of
|
|
|
the process the thread belongs to. By default, tracks are grouped under the
|
|
|
current process's `ProcessTrack`.
|
|
|
|
|
|
A track is identified by a uuid, which must be unique across the entire
|
|
|
recorded trace. To minimize the chances of accidental collisions, the uuids
|
|
|
of child tracks are combined with those of their parents, with each
|
|
|
`ProcessTrack` having a random, per-process uuid.
|
|
|
|
|
|
By default, track events (e.g., `TRACE_EVENT`) use the `ThreadTrack` for the
|
|
|
calling thread. This can be overridden, for example, to mark events that
|
|
|
begin and end on a different thread:
|
|
|
|
|
|
```C++
|
|
|
void OnNewRequest(size_t request_id) {
|
|
|
// Open a slice when the request came in.
|
|
|
TRACE_EVENT_BEGIN("category", "HandleRequest", perfetto::Track(request_id));
|
|
|
|
|
|
// Start a thread to handle the request.
|
|
|
std::thread worker_thread([=] {
|
|
|
// ... produce response ...
|
|
|
|
|
|
// Close the slice for the request now that we finished handling it.
|
|
|
TRACE_EVENT_END("category", perfetto::Track(request_id));
|
|
|
});
|
|
|
```
|
|
|
Tracks can also optionally be annotated with metadata:
|
|
|
|
|
|
```C++
|
|
|
auto desc = track.Serialize();
|
|
|
desc.set_name("MyTrack");
|
|
|
perfetto::TrackEvent::SetTrackDescriptor(track, desc);
|
|
|
```
|
|
|
|
|
|
Threads and processes can also be named in a similar way, e.g.:
|
|
|
|
|
|
```C++
|
|
|
auto desc = perfetto::ProcessTrack::Current().Serialize();
|
|
|
desc.mutable_process()->set_process_name("MyProcess");
|
|
|
perfetto::TrackEvent::SetTrackDescriptor(
|
|
|
perfetto::ProcessTrack::Current(), desc);
|
|
|
```
|
|
|
|
|
|
The metadata remains valid between tracing sessions. To free up data for a
|
|
|
track, call EraseTrackDescriptor:
|
|
|
|
|
|
```C++
|
|
|
perfetto::TrackEvent::EraseTrackDescriptor(track);
|
|
|
```
|
|
|
|
|
|
### Counters
|
|
|
|
|
|
Time-varying numeric data can be recorded with the `TRACE_COUNTER` macro:
|
|
|
|
|
|
```C++
|
|
|
TRACE_COUNTER("category", "MyCounter", 1234.5);
|
|
|
```
|
|
|
|
|
|
This data is displayed as a counter track in the Perfetto UI:
|
|
|
|
|
|

|
|
|
|
|
|
Both integer and floating point counter values are supported. Counters can
|
|
|
also be annotated with additional information such as units, for example, for
|
|
|
tracking the rendering framerate in terms of frames per second or "fps":
|
|
|
|
|
|
```C++
|
|
|
TRACE_COUNTER("category", perfetto::CounterTrack("Framerate", "fps"), 120);
|
|
|
```
|
|
|
|
|
|
As another example, a memory counter that records bytes but accepts samples
|
|
|
as kilobytes (to reduce trace binary size) can be defined like this:
|
|
|
|
|
|
```C++
|
|
|
perfetto::CounterTrack memory_track = perfetto::CounterTrack("Memory")
|
|
|
.set_unit("bytes")
|
|
|
.set_multiplier(1024);
|
|
|
TRACE_COUNTER("category", memory_track, 4 /* = 4096 bytes */);
|
|
|
```
|
|
|
|
|
|
See
|
|
|
[counter_descriptor.proto](
|
|
|
/protos/perfetto/trace/track_event/counter_descriptor.proto) for the full set
|
|
|
of attributes for a counter track.
|
|
|
|
|
|
To record a counter value at a specific point in time (instead of the current
|
|
|
time), you can pass in a custom timestamp:
|
|
|
|
|
|
```C++
|
|
|
// First record the current time and counter value.
|
|
|
uint64_t timestamp = perfetto::TrackEvent::GetTraceTimeNs();
|
|
|
int64_t value = 1234;
|
|
|
|
|
|
// Later, emit a sample at that point in time.
|
|
|
TRACE_COUNTER("category", "MyCounter", timestamp, value);
|
|
|
```
|
|
|
|
|
|
### Interning
|
|
|
|
|
|
Interning can be used to avoid repeating the same constant data (e.g., event
|
|
|
names) throughout the trace. Perfetto automatically performs interning for
|
|
|
most strings passed to `TRACE_EVENT`, but it's also possible to also define
|
|
|
your own types of interned data.
|
|
|
|
|
|
First, define an interning index for your type. It should map to a specific
|
|
|
field of
|
|
|
[interned_data.proto](/protos/perfetto/trace/interned_data/interned_data.proto)
|
|
|
and specify how the interned data is written into that message when seen for
|
|
|
the first time.
|
|
|
|
|
|
```C++
|
|
|
struct MyInternedData
|
|
|
: public perfetto::TrackEventInternedDataIndex<
|
|
|
MyInternedData,
|
|
|
perfetto::protos::pbzero::InternedData::kMyInternedDataFieldNumber,
|
|
|
const char*> {
|
|
|
static void Add(perfetto::protos::pbzero::InternedData* interned_data,
|
|
|
size_t iid,
|
|
|
const char* value) {
|
|
|
auto my_data = interned_data->add_my_interned_data();
|
|
|
my_data->set_iid(iid);
|
|
|
my_data->set_value(value);
|
|
|
}
|
|
|
};
|
|
|
```
|
|
|
|
|
|
Next, use your interned data in a trace point as shown below. The interned
|
|
|
string will only be emitted the first time the trace point is hit (unless the
|
|
|
trace buffer has wrapped around).
|
|
|
|
|
|
```C++
|
|
|
TRACE_EVENT(
|
|
|
"category", "Event", [&](perfetto::EventContext ctx) {
|
|
|
auto my_message = ctx.event()->set_my_message();
|
|
|
size_t iid = MyInternedData::Get(&ctx, "Repeated data to be interned");
|
|
|
my_message->set_iid(iid);
|
|
|
});
|
|
|
```
|
|
|
|
|
|
Note that interned data is strongly typed, i.e., each class of interned data
|
|
|
uses a separate namespace for identifiers.
|
|
|
|
|
|
### Tracing session observers
|
|
|
|
|
|
The session observer interface allows applications to be notified when track
|
|
|
event tracing starts and stops:
|
|
|
|
|
|
```C++
|
|
|
class Observer : public perfetto::TrackEventSessionObserver {
|
|
|
public:
|
|
|
~Observer() override = default;
|
|
|
|
|
|
void OnSetup(const perfetto::DataSourceBase::SetupArgs&) override {
|
|
|
// Called when tracing session is configured. Note tracing isn't active yet,
|
|
|
// so track events emitted here won't be recorded.
|
|
|
}
|
|
|
|
|
|
void OnStart(const perfetto::DataSourceBase::StartArgs&) override {
|
|
|
// Called when a tracing session is started. It is possible to emit track
|
|
|
// events from this callback.
|
|
|
}
|
|
|
|
|
|
void OnStop(const perfetto::DataSourceBase::StopArgs&) override {
|
|
|
// Called when a tracing session is stopped. It is still possible to emit
|
|
|
// track events from this callback.
|
|
|
}
|
|
|
};
|
|
|
```
|
|
|
|
|
|
Note that all methods of the interface are called on an internal Perfetto
|
|
|
thread.
|
|
|
|
|
|
For example, here's how to wait for any tracing session to start:
|
|
|
|
|
|
```C++
|
|
|
class Observer : public perfetto::TrackEventSessionObserver {
|
|
|
public:
|
|
|
Observer() { perfetto::TrackEvent::AddSessionObserver(this); }
|
|
|
~Observer() { perfetto::TrackEvent::RemoveSessionObserver(this); }
|
|
|
|
|
|
void OnStart(const perfetto::DataSourceBase::StartArgs&) override {
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
|
|
cv.notify_one();
|
|
|
}
|
|
|
|
|
|
void WaitForTracingStart() {
|
|
|
printf("Waiting for tracing to start...\n");
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
|
|
cv.wait(lock, [] { return perfetto::TrackEvent::IsEnabled(); });
|
|
|
printf("Tracing started\n");
|
|
|
}
|
|
|
|
|
|
std::mutex mutex;
|
|
|
std::condition_variable cv;
|
|
|
};
|
|
|
|
|
|
Observer observer;
|
|
|
observer.WaitForTracingToStart();
|
|
|
```
|
|
|
|
|
|
[RAII]: https://en.cppreference.com/w/cpp/language/raii
|