You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
500 lines
17 KiB
500 lines
17 KiB
# Trace configuration
|
|
|
|
Unlike many always-on logging systems (e.g. Linux's rsyslog, Android's logcat),
|
|
in Perfetto all tracing data sources are idle by default and record data only
|
|
when instructed to do so.
|
|
|
|
Data sources record data only when one (or more) tracing sessions are active.
|
|
A tracing session is started by invoking the `perfetto` cmdline client and
|
|
passing a config (see QuickStart guide for
|
|
[Android](/docs/quickstart/android-tracing.md) or
|
|
[Linux](/docs/quickstart/linux-tracing.md)).
|
|
|
|
A simple trace config looks like this:
|
|
|
|
```protobuf
|
|
duration_ms: 10000
|
|
|
|
buffers {
|
|
size_kb: 65536
|
|
fill_policy: RING_BUFFER
|
|
}
|
|
|
|
data_sources {
|
|
config {
|
|
name: "linux.ftrace"
|
|
target_buffer: 0
|
|
ftrace_config {
|
|
ftrace_events: "sched_switch"
|
|
ftrace_events: "sched_wakeup"
|
|
}
|
|
}
|
|
}
|
|
|
|
````
|
|
|
|
And is used as follows:
|
|
|
|
```bash
|
|
perfetto --txt -c config.pbtx -o trace_file.perfetto-trace
|
|
```
|
|
|
|
TIP: Some more complete examples of trace configs can be found in the repo in
|
|
[`/test/configs/`](/test/configs/).
|
|
|
|
NOTE: If you are tracing on Android using adb and experiencing problems, see
|
|
[the Android section](#android) below.
|
|
|
|
## TraceConfig
|
|
|
|
The TraceConfig is a protobuf message
|
|
([reference docs](/docs/reference/trace-config-proto.autogen)) that defines:
|
|
|
|
1. The general behavior of the whole tracing system, e.g.:
|
|
* The max duration of the trace.
|
|
* The number of in-memory buffers and their size.
|
|
* The max size of the output trace file.
|
|
|
|
2. Which data sources to enable and their configuration, e.g.:
|
|
* For the [kernel tracing data source](/docs/data-sources/cpu-scheduling.md)
|
|
, which ftrace events to enable.
|
|
* For the [heap profiler](/docs/data-sources/native-heap-profiler.md), the
|
|
target process name and sampling rate.
|
|
|
|
See the _data sources_ section of the docs for details on how to
|
|
configure the data sources bundled with Perfetto.
|
|
|
|
3. The `{data source} x {buffer}` mappings: which buffer each data
|
|
source should write into (see [buffers section](#buffers) below).
|
|
|
|
The tracing service (`traced`) acts as a configuration dispatcher: it receives
|
|
a config from the `perfetto` cmdline client (or any other
|
|
[Consumer](/docs/concepts/service-model.md#consumer)) and forwards parts of the
|
|
config to the various [Producers](/docs/concepts/service-model.md#producer)
|
|
connected.
|
|
|
|
When a tracing session is started by a consumer, the tracing service will:
|
|
|
|
* Read the outer section of the TraceConfig (e.g. `duration_ms`, `buffers`) and
|
|
use that to determine its own behavior.
|
|
* Read the list of data sources in the `data_sources` section. For each data
|
|
source listed in the config, if a corresponding name (`"linux.ftrace"` in the
|
|
example below) was registered, the service will ask the producer process to
|
|
start that data source, passing it the raw bytes of the
|
|
[`DataSourceConfig` subsection][dss] verbatim to the data source (See
|
|
backward/forward compat section below).
|
|
|
|
![TraceConfig diagram](/docs/images/trace_config.png)
|
|
|
|
[dss]: /docs/reference/trace-config-proto.autogen#DataSourceConfig
|
|
|
|
## Buffers
|
|
|
|
The buffer sections define the number, size and policy of the in-memory buffers
|
|
owned by the tracing service. It looks as follows:
|
|
|
|
```protobuf
|
|
// Buffer #0
|
|
buffers {
|
|
size_kb: 4096
|
|
fill_policy: RING_BUFFER
|
|
}
|
|
|
|
// Buffer #1
|
|
buffers {
|
|
size_kb: 8192
|
|
fill_policy: DISCARD
|
|
}
|
|
```
|
|
|
|
Each buffer has a fill policy which is either:
|
|
|
|
* RING_BUFFER (default): the buffer behaves like a ring buffer and writes when
|
|
full will wrap over and replace the oldest trace data in the buffer.
|
|
|
|
* DISCARD: the buffer stops accepting data once full. Further write attempts are
|
|
dropped.
|
|
|
|
WARNING: DISCARD can have unexpected side-effect with data sources that commit
|
|
data at the end of the trace.
|
|
|
|
A trace config must define at least one buffer to be valid. In the simplest case
|
|
all data sources will write their trace data into the same buffer.
|
|
|
|
While this is
|
|
fine for most basic cases, it can be problematic in cases where different data
|
|
sources write at significantly different rates.
|
|
|
|
For instance, imagine a trace config that enables both:
|
|
|
|
1. The kernel scheduler tracer. On a typical Android phone this records
|
|
~10000 events/second, writing ~1 MB/s of trace data into the buffer.
|
|
|
|
2. Memory stat polling. This data source writes the contents of /proc/meminfo
|
|
into the trace buffer and is configured to poll every 5 seconds, writing
|
|
~100 KB per poll interval.
|
|
|
|
If both data sources are configured to write into the same buffer and such
|
|
buffer is set to 4MB, most traces will contain only one memory snapshot. There
|
|
are very good chances that most traces won't contain any memory snapshot at all,
|
|
even if the 2nd data sources was working perfectly.
|
|
This is because during the 5 s. polling interval, the scheduler data source can
|
|
end up filling the whole buffer, pushing the memory snapshot data out of the
|
|
buffer.
|
|
|
|
## Dynamic buffer mapping
|
|
|
|
Data-source <> buffer mappings are dynamic in Perfetto.
|
|
In the simplest case a tracing session can define only one buffer. By default,
|
|
all data sources will record data into that one buffer.
|
|
|
|
In cases like the example above, it might be preferable separating these data
|
|
sources into different buffers.
|
|
This can be achieved with the `target_buffer` field of the TraceConfig.
|
|
|
|
![Buffer mapping](/docs/images/trace_config_buffer_mapping.png)
|
|
|
|
Can be achieved with:
|
|
|
|
```protobuf
|
|
data_sources {
|
|
config {
|
|
name: "linux.ftrace"
|
|
target_buffer: 0 // <-- This goes into buffer 0.
|
|
ftrace_config { ... }
|
|
}
|
|
}
|
|
|
|
data_sources: {
|
|
config {
|
|
name: "linux.sys_stats"
|
|
target_buffer: 1 // <-- This goes into buffer 1.
|
|
sys_stats_config { ... }
|
|
}
|
|
}
|
|
|
|
data_sources: {
|
|
config {
|
|
name: "android.heapprofd"
|
|
target_buffer: 1 // <-- This goes into buffer 1 as well.
|
|
heapprofd_config { ... }
|
|
}
|
|
}
|
|
```
|
|
|
|
## PBTX vs binary format
|
|
|
|
There are two ways to pass the trace config when using the `perfetto` cmdline
|
|
client format:
|
|
|
|
#### Text format
|
|
|
|
It is the preferred format for human-driven workflows and exploration. It
|
|
allows to pass directly the text file in the PBTX (ProtoBuf TeXtual
|
|
representation) syntax, for the schema defined in the
|
|
[trace_config.proto](/protos/perfetto/config/trace_config.proto)
|
|
(see [reference docs](/docs/reference/trace-config-proto.autogen))
|
|
|
|
When using this mode pass the `--txt` flag to `perfetto` to indicate the config
|
|
should be interpreted as a PBTX file:
|
|
|
|
```bash
|
|
perfetto -c /path/to/config.pbtx --txt -o trace_file.perfetto-trace
|
|
```
|
|
|
|
NOTE: The `--txt` option has been introduced only in Android 10 (Q). Older
|
|
versions support only the binary format.
|
|
|
|
WARNING: Do not use the text format for machine-to-machine interaction
|
|
benchmark, scripts and tools) as it's more prone to breakages (e.g. if a field
|
|
is renamed or an enum is turned into an integer)
|
|
|
|
#### Binary format
|
|
|
|
It is the preferred format for machine-to-machine (M2M) interaction. It involves
|
|
passing the protobuf-encoded binary of the TraceConfig message.
|
|
This can be obtained passing the PBTX in input to the protobuf's `protoc`
|
|
compiler (which can be downloaded
|
|
[here](https://github.com/protocolbuffers/protobuf/releases)).
|
|
|
|
```bash
|
|
cd ~/code/perfetto # external/perfetto in the Android tree.
|
|
|
|
protoc --encode=perfetto.protos.TraceConfig \
|
|
-I. protos/perfetto/config/perfetto_config.proto \
|
|
< config.txpb \
|
|
> config.bin
|
|
```
|
|
|
|
and then passing it to perfetto as follows, without the `--txt` argument:
|
|
|
|
```bash
|
|
perfetto -c config.bin -o trace_file.perfetto-trace
|
|
```
|
|
|
|
## {#long-traces} Streaming long traces
|
|
|
|
By default Perfetto keeps the full trace buffer(s) in memory and writes it into
|
|
the destination file (the `-o` cmdline argument) only at the end of the tracing
|
|
session. This is to reduce the perf-intrusiveness of the tracing system.
|
|
This, however, limits the max size of the trace to the physical memory size of
|
|
the device, which is often too limiting.
|
|
|
|
In some cases (e.g., benchmarks, hard to repro cases) it is desirable to capture
|
|
traces that are way larger than that, at the cost of extra I/O overhead.
|
|
|
|
To achieve that, Perfetto allows to periodically write the trace buffers into
|
|
the target file (or stdout) using the following TraceConfig fields:
|
|
|
|
* `write_into_file (bool)`:
|
|
When true periodically drains the trace buffers into the output
|
|
file. When this option is enabled, the userspace buffers need to be just
|
|
big enough to hold tracing data between two write periods.
|
|
The buffer sizing depends on the activity of the device.
|
|
The data rate of a typical trace is ~1-4 MB/s. So a 16MB in-memory buffer can
|
|
hold for up write periods of ~4 seconds before starting to lose data.
|
|
|
|
* `file_write_period_ms (uint32)`:
|
|
Overrides the default drain period (5s). Shorter periods require a smaller
|
|
userspace buffer but increase the performance intrusiveness of tracing. If
|
|
the period given is less than 100ms, the tracing service will use a period
|
|
of 100ms.
|
|
|
|
* `max_file_size_bytes (uint64)`:
|
|
If set, stops the tracing session after N bytes have been written. Used to
|
|
cap the size of the trace.
|
|
|
|
For a complete example of a working trace config in long-tracing mode see
|
|
[`/test/configs/long_trace.cfg`](/test/configs/long_trace.cfg).
|
|
|
|
Summary: to capture a long trace just set `write_into_file:true`, set a long
|
|
`duration_ms` and use an in-memory buffer size of 32MB or more.
|
|
|
|
## Data-source specific config
|
|
|
|
Alongside the trace-wide configuration parameters, the trace config also defines
|
|
data-source-specific behaviors. At the proto schema level, this is defined in
|
|
the `DataSourceConfig` section of `TraceConfig`:
|
|
|
|
From [data_source_config.proto](/protos/perfetto/config/data_source_config.proto):
|
|
|
|
```protobuf
|
|
message TraceConfig {
|
|
...
|
|
repeated DataSource data_sources = 2; // See below.
|
|
}
|
|
|
|
message DataSource {
|
|
optional protos.DataSourceConfig config = 1; // See below.
|
|
...
|
|
}
|
|
|
|
message DataSourceConfig {
|
|
optional string name = 1;
|
|
...
|
|
optional FtraceConfig ftrace_config = 100 [lazy = true];
|
|
...
|
|
optional AndroidPowerConfig android_power_config = 106 [lazy = true];
|
|
}
|
|
```
|
|
|
|
Fields like `ftrace_config`, `android_power_config` are examples of data-source
|
|
specific configs. The tracing service will completely ignore the contents of
|
|
those fields and route the whole DataSourceConfig object to any data source
|
|
registered with the same name.
|
|
|
|
The `[lazy=true]` marker has a special implication in the
|
|
[protozero](/docs/design-docs/protozero.md) code generator. Unlike standard
|
|
nested messages, it generates raw accessors (e.g.,
|
|
`const std::string& ftrace_config_raw()` instead of
|
|
`const protos::FtraceConfig& ftrace_config()`). This is to avoid injecting too
|
|
many `#include` dependencies and avoiding binary size bloat in the code that
|
|
implements data sources.
|
|
|
|
#### A note on backwards/forward compatibility
|
|
The tracing service will route the raw binary blob of the `DataSourceConfig`
|
|
message to the data sources with a matching name, without attempting to decode
|
|
and re-encode it. If the `DataSourceConfig` section of the trace config contains
|
|
a new field that didn't exist at the time when the service was built, the
|
|
service will still pass the `DataSourceConfig` through to the data source.
|
|
This allows to introduced new data sources without needing the service to
|
|
know anything about them upfront.
|
|
|
|
TODO: we are aware of the fact that today extending the `DataSourceConfig` with
|
|
a custom proto requires changing the `data_source_config.proto` in the Perfetto
|
|
repo, which is unideal for external projects. The long-term plan is to reserve
|
|
a range of fields for non-upstream extensions and provide generic templated
|
|
accessors for client code. Until then, we accept patches upstream to introduce
|
|
ad-hoc configurations for your own data sources.
|
|
|
|
## Multi-process data sources
|
|
|
|
Some data sources are singletons. E.g., in the case of scheduler tracing that
|
|
Perfetto ships on Android, there is only data source for the whole system,
|
|
owned by the `traced_probes` service.
|
|
|
|
However, in the general case multiple processes can advertise the same data
|
|
source. This is the case, for instance, when using the
|
|
[Perfetto SDK](/docs/instrumentation/tracing-sdk.md) for userspace
|
|
instrumentation.
|
|
|
|
If this happens, when starting a tracing session that specifies that data
|
|
source in the trace config, Perfetto by default will ask all processes that
|
|
advertise that data source to start it.
|
|
|
|
In some cases it might be desirable to further limit the enabling of the data
|
|
source to a specific process (or set of processes). That is possible through the
|
|
`producer_name_filter` and `producer_name_regex_filter`.
|
|
|
|
NOTE: the typical Perfetto run-time model is: one process == one Perfetto
|
|
Producer; one Producer typically hosts multiple data sources.
|
|
|
|
When those filters are set, the Perfetto tracing service will activate the data
|
|
source only in the subset of producers matching the filter.
|
|
|
|
Example:
|
|
|
|
```protobuf
|
|
buffers {
|
|
size_kb: 4096
|
|
}
|
|
|
|
data_sources {
|
|
config {
|
|
name: "track_event"
|
|
}
|
|
# Enable the data source only on Chrome and Chrome canary.
|
|
producer_name_filter: "com.android.chrome"
|
|
producer_name_filter: "com.google.chrome.canary"
|
|
}
|
|
```
|
|
|
|
## Triggers
|
|
|
|
In nominal conditions, a tracing session has a lifecycle that simply matches the
|
|
invocation of the `perfetto` cmdline client: trace data recording starts when
|
|
the TraceConfig is passed to `perfetto` and ends when either the
|
|
`TraceConfig.duration_ms` has elapsed, or when the cmdline client terminates.
|
|
|
|
Perfetto supports an alternative mode of either starting or stopping the trace
|
|
which is based on triggers. The overall idea is to declare in the trace config
|
|
itself:
|
|
|
|
* A set of triggers, which are just free-form strings.
|
|
* Whether a given trigger should cause the trace to be started or stopped, and
|
|
the start/stop delay.
|
|
|
|
Why using triggers? Why can't one just start perfetto or kill(SIGTERM) it when
|
|
needed? The rationale of all this is the security model: in most Perfetto
|
|
deployments (e.g., on Android) only privileged entities (e.g., adb shell) can
|
|
configure/start/stop tracing. Apps are unprivileged in this sense and they
|
|
cannot control tracing.
|
|
|
|
Triggers offer a way to unprivileged apps to control, in a limited fashion, the
|
|
lifecycle of a tracing session. The conceptual model is:
|
|
|
|
* The privileged Consumer (see
|
|
[_Service model_](/docs/concepts/service-model.md)), i.e. the entity
|
|
that is normally authorized to start tracing (e.g., adb shell in Android),
|
|
declares upfront what are the possible trigger names for the trace and what
|
|
they will do.
|
|
* Unprivileged entities (any random app process) can activate those triggers.
|
|
Unprivileged entities don't get a say on what the triggers will do, they only
|
|
communicate that an event happened.
|
|
|
|
Triggers can be signaled via the cmdline util
|
|
|
|
```bash
|
|
/system/bin/trigger_perfetto "trigger_name"
|
|
```
|
|
|
|
(or also by starting an independent trace session which uses only the
|
|
`activate_triggers: "trigger_name"` field in the config)
|
|
|
|
There are two types of triggers:
|
|
|
|
#### Start triggers
|
|
|
|
Start triggers allow activating a tracing session only after some significant
|
|
event has happened. Passing a trace config that has `START_TRACING` trigger
|
|
causes the tracing session to stay idle (i.e. not recording any data) until either
|
|
the trigger is hit or the `trigger_timeout_ms` timeout is hit.
|
|
|
|
`trace_duration_ms` and triggered traces can not be used at the same time.
|
|
|
|
Example config:
|
|
```protobuf
|
|
# If no trigger is hit, the trace will end without having recorded any data
|
|
# after 30s.
|
|
trigger_timeout_ms: 30000
|
|
|
|
# If the "myapp_is_slow" is hit, the trace starts recording data and will be
|
|
# stopped after 5s.
|
|
trigger_config {
|
|
trigger_mode: START_TRACING
|
|
triggers {
|
|
name: "myapp_is_slow"
|
|
stop_delay_ms: 5000
|
|
}
|
|
}
|
|
|
|
# The rest of the config is as usual.
|
|
buffers { ... }
|
|
data_sources { ... }
|
|
```
|
|
|
|
#### Stop triggers
|
|
|
|
STOP_TRACING triggers allow to prematurely finalize a trace when the trigger is
|
|
hit. In this mode the trace starts immediately when the `perfetto` client is
|
|
invoked (like in nominal cases). The trigger acts as a premature finalization
|
|
signal.
|
|
|
|
This can be used to use perfetto in flight-recorder mode. By starting a trace
|
|
with buffers configured in `RING_BUFFER` mode and `STOP_TRACING` triggers,
|
|
the trace will be recorded in a loop and finalized when the culprit event is
|
|
detected. This is key for events where the root cause is in the recent past
|
|
(e.g., the app detects a slow scroll or a missing frame).
|
|
|
|
Example config:
|
|
```protobuf
|
|
# If no trigger is hit, the trace will end after 30s.
|
|
trigger_timeout_ms: 30000
|
|
|
|
# If the "missed_frame" is hit, the trace is stopped after 1s.
|
|
trigger_config {
|
|
trigger_mode: STOP_TRACING
|
|
triggers {
|
|
name: "missed_frame"
|
|
stop_delay_ms: 1000
|
|
}
|
|
}
|
|
|
|
# The rest of the config is as usual.
|
|
buffers { ... }
|
|
data_sources { ... }
|
|
```
|
|
|
|
## Android
|
|
|
|
On Android, there are some caveats around using `adb shell`
|
|
|
|
* Ctrl+C, which normally causes a graceful termination of the trace, is not
|
|
propagated by ADB when using `adb shell perfetto` but only when using an
|
|
interactive PTY-based session via `adb shell`.
|
|
* On non-rooted devices before Android 12, the config can only be passed as
|
|
`cat config | adb shell perfetto -c -` (-: stdin) because of over-restrictive
|
|
SELinux rules. Since Android 12 `/data/misc/perfetto-configs` can be used for
|
|
storing configs.
|
|
* On devices before Android 10, adb cannot directly pull
|
|
`/data/misc/perfetto-traces`. Use
|
|
`adb shell cat /data/misc/perfetto-traces/trace > trace` to work around.
|
|
* When capturing longer traces, e.g. in the context of benchmarks or CI, use
|
|
`PID=$(perfetto --background)` and then `kill $PID` to stop.
|
|
|
|
|
|
## Other resources
|
|
|
|
* [TraceConfig Reference](/docs/reference/trace-config-proto.autogen)
|
|
* [Buffers and dataflow](/docs/concepts/buffers.md)
|