Cookbook: Local Android Trace Recording
This page collects end-to-end recipes for recording Perfetto traces on Android in situations that the standard interactive workflow does not cover.
- Tracing Android boot: record a trace covering the boot sequence, which you cannot start by hand while the device is booting.
- Capturing a heap dump on OutOfMemoryError: automatically
dump the Java heap when an app crashes with an
OutOfMemoryError.
The recipes assume a host with adb access to the device. Each recipe is
self-contained: copy the config and commands as they are, then adjust the
highlighted parameters. If you have never recorded a trace before, start with
the system tracing tutorial. For the
full reference on each topic, follow the links into the deeper guides:
Recipe: Tracing Android boot
Goal: record a trace covering the Android boot sequence, to profile process startup, scheduling and everything else that happens while the device boots.
You cannot start a trace by hand while the device is still booting. Instead, since Android 13 (T), perfetto can be armed to start recording automatically on the next boot.
1. Write a config. The boot trace config must be in text format (not
binary). Save the following as boottrace.pbtxt. It records process scheduling
and lifecycle events, but any
trace configuration works here (more examples in
/test/configs/):
# One buffer allocated within the central tracing binary for the entire trace,
# shared by the two data sources below.
buffers {
size_kb: 32768
fill_policy: DISCARD
}
# Ftrace data from the kernel, mainly the process scheduling events.
data_sources {
config {
name: "linux.ftrace"
target_buffer: 0
ftrace_config {
ftrace_events: "sched_switch"
ftrace_events: "sched_waking"
ftrace_events: "sched_wakeup_new"
ftrace_events: "task_newtask"
ftrace_events: "task_rename"
ftrace_events: "sched_process_exec"
ftrace_events: "sched_process_exit"
ftrace_events: "sched_process_fork"
ftrace_events: "sched_process_free"
ftrace_events: "sched_process_hang"
ftrace_events: "sched_process_wait"
}
}
}
# Resolve process commandlines and parent/child relationships, to better
# interpret the ftrace events, which are in terms of pids.
data_sources {
config {
name: "linux.process_stats"
target_buffer: 0
}
}
# 10s trace, but can be stopped prematurely via `adb shell pkill perfetto`.
duration_ms: 100002. Push the config to the device. The path is fixed; perfetto only looks
for /data/misc/perfetto-configs/boottrace.pbtxt:
adb push boottrace.pbtxt /data/misc/perfetto-configs/boottrace.pbtxt3. Arm tracing for the next boot:
adb shell setprop persist.debug.perfetto.boottrace 1The property is reset during boot, so each boot trace is one-shot: to trace another boot, set the property again.
4. Reboot the device:
adb reboot5. Pull the trace. The trace is written to
/data/misc/perfetto-traces/boottrace.perfetto-trace. The file appears only
after the recording has stopped, once duration_ms has elapsed, so keep it to
a reasonable value. (If your config sets write_into_file: true, the file is
instead written incrementally, every file_write_period_ms.)
adb pull /data/misc/perfetto-traces/boottrace.perfetto-traceThe file is removed before a new boot trace starts, so pull it before arming the next one.
6. View it. Open boottrace.perfetto-trace in the
Perfetto UI. To dig into the data with SQL, see the
Android trace analysis cookbook.
How early in boot does the trace start?
The trace is started by the perfetto_trace_on_boot oneshot init service,
defined in perfetto.rc. Init starts it once three conditions
hold: persistent properties have been loaded (which happens only after /data
has been mounted), the traced daemon is up, and boot has not completed yet.
The last condition is why setting the property on a booted device arms the
next boot instead of starting a trace immediately. The earliest boot stages
(kernel init, mounting filesystems) are therefore not covered by the trace.
Recipe: Capturing a heap dump on OutOfMemoryError
Goal: automatically capture an ART (Java/Kotlin) heap dump at the moment a
process crashes with a java.lang.OutOfMemoryError, so you can see exactly
what was keeping memory alive when allocations started failing.
Since Android 14 (U), ART notifies perfetto when a Java process is about to
crash with an OutOfMemoryError, and perfetto can use that notification as a
trigger to dump the Java heap of the crashing process.
Option A: using the helper script
If you have a perfetto checkout, tools/java_heap_dump drives this end to end.
Pass --wait-for-oom together with the process to watch (-n '*' matches all
processes):
tools/java_heap_dump --wait-for-oom --oom-wait-seconds 3600 \
-n 'com.example.myapp' -o oome.pftraceThe script starts a tracing session, waits up to --oom-wait-seconds for an
OutOfMemoryError to be thrown, then pulls the heap dump to oome.pftrace.
Option B: using only adb
If you don't have a checkout, the following command does the same with nothing
but adb access. It is safe to copy-paste as-is:
cat << EOF | adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/oome.pftrace
buffers: {
size_kb: 524288
fill_policy: DISCARD
}
data_sources: {
config {
name: "android.java_hprof.oom"
java_hprof_config {
process_cmdline: "*"
}
}
}
data_source_stop_timeout_ms: 100000
trigger_config {
trigger_mode: START_TRACING
trigger_timeout_ms: 3600000
triggers {
name: "com.android.telemetry.art-outofmemory"
stop_delay_ms: 500
}
}
data_sources {
config {
name: "android.packages_list"
}
}
EOFThis starts a tracing session that waits for up to one hour
(trigger_timeout_ms) for any ART runtime instance to hit an
OutOfMemoryError. To watch only your own app, replace the "*" in
process_cmdline with its process name (e.g. "com.example.myapp").
Once an error is hit, the heap is dumped and tracing stops:
[862.335] perfetto_cmd.cc:1047 Connected to the Perfetto traced service, TTL: 3601s
[871.335] perfetto_cmd.cc:1210 Wrote 19487866 bytes into /data/misc/perfetto-traces/oome.pftraceThen pull the heap dump:
adb pull /data/misc/perfetto-traces/oome.pftraceAnalysing the heap dump
Open oome.pftrace in the Perfetto UI and click the
diamond marker in the "Heap Profile" track to get a flamegraph of what
retained the memory. For a guided investigation, see:
- Heap Dump Explorer, interactive dominator-tree and class-level analysis of heap dumps.
- Debugging memory usage, an end-to-end guide to investigating Android memory issues.
- ART heap dumps, the full reference for the underlying data source.