Many performance problems cannot be reproduced with a single tap. Slow boot, intermittent jank, problems after turning the screen on or off, audio interruptions after several hours, periodic stutters in an automotive system, and performance degradation as temperature rises are all poor fits for a manually recorded 10-second trace.
This article covers long-running field traces: defining the observation window in advance, using triggers to preserve the incident, ensuring the file finishes writing safely, and determining whether the collected data is trustworthy. Boot tracing is covered below as a special observation window. Part 02 already covered basic interactive capture methods—the command line, official scripts, Developer options, and the web interface. Here, we focus on what changes in the field and during long-running recording.
Version note (reviewed October 2026): Configurations and commands are checked against Perfetto v58.3. Check Android, device-side perfetto CLI, traced service, and host analysis-tool versions separately. Android 14 clone support does not imply the v49+
--clone-by-nameflag. write_flush_mode requires v54+; remove unsupported fields on older devices and verify their manual-flush behavior.
Perfetto Series Catalog
- Android Perfetto Series Catalog
- Android Perfetto Series 1: Introduction to Perfetto
- Android Perfetto Series 2: Capturing Perfetto Traces
- Android Perfetto Series 3: Familiarizing with the Perfetto View
- Android Perfetto Series 4: Opening Large Traces via Command Line
- Android Perfetto Series 5: Choreographer-based Rendering Flow
- Android Perfetto Series 6: Why 120Hz? Advantages and Challenges
- Android Perfetto Series 7: MainThread and RenderThread Deep Dive
- Android Perfetto Series 8: Understanding Vsync and Performance Analysis
- Android Perfetto Series 9: Interpreting CPU Information
- Android Perfetto Series 10: Binder Scheduling and Lock Contention
- Android Perfetto Series 11: PerfettoSQL, Trace Processor and Regression Detection
- Android Perfetto Series 12: Trace Dataflow and Data Loss
- Android Perfetto Series 13: Perfetto SDK, Track Event and App Field Traces
- Android Perfetto Series 14: heapprofd and Memory Profiling
- Android Perfetto Series 15: Boot Traces and Long-running Field Tracing
- Android Perfetto Series 16: GPU, Power Counters and Hardware Bottlenecks
- Android Perfetto Series 17: Scenario Automation and Platform Tracing
- Android Perfetto Series 18: Input Response Latency
- Video (Bilibili) - Android Perfetto Basics and Case Studies
- Video (Bilibili) - Android Perfetto: Trace Graph Types - AOSP, WebView, Flutter + OEM System Optimization
The purpose of a long-running field trace is to retain enough data before and after a problem to explain its root cause. Enabling every option and recording for an hour only brings file-size, overhead, and data-loss problems sooner. A good field capture strategy produces reviewable evidence at a controlled cost.

Diagram: a STOP trigger preserves the trigger window, periodic file writes persist data in batches, and cloning leaves the original session running.
Design the Evidence Window First
Long-running traces require tradeoffs: how much to retain before and after the problem, and which data sources are worth keeping. Before writing the configuration, establish the observation window:
| Question | Configuration implications |
|---|---|
| How often does the problem recur? | duration_ms, whether background execution is needed, whether periodic snapshots are needed |
| Does the root cause precede or follow the observed problem? | Choose a STOP or START trigger |
| How many seconds before the problem must be retained? | Ring buffer size, breadth of data sources, file-write interval |
| Can the user stop recording manually? | Whether automatic triggers or bugreport integration are needed |
| Is ADB unavailable during the early phase? | Boot tracing, platform provisioning, init/service activation, or a vendor solution |
| Might USB disconnect after recording starts? | --background-wait, detached mode, snapshots, or a background service |
| What is the maximum file size? | max_file_size_bytes, upload policy, local TTL |
| Is the data sensitive? | Log tag allowlist, whether to disable atrace_apps: "*" |
Intermittent UI jank usually requires tens of seconds before the problem; thermal throttling may require several minutes of trends; long-term automotive stability problems may require hours of execution. Such scenarios often use periodic snapshot retrieval or upload only a summary.
Map common scenarios to presets before writing the TraceConfig:
| Scenario | Recommended mode | Trigger and acceptance checks |
|---|---|---|
| Intermittent UI jank | Ring buffer + STOP trigger | App markers, FrameTimeline coverage of the target window, sufficient pre-trigger history |
| Signs of an impending ANR | Ring buffer + STOP trigger | Main thread, Binder, lock waits, I/O, and critical business phases are all present |
| Thermal throttling | write_into_file or snapshots |
Preserve minute-scale trends for CPU frequency, idle, thermal state, and critical app phases |
| Business-operation timeout | App marker + STOP/START trigger | Use STOP with sufficient stop_delay for before/after evidence; use START only when post-event data alone is sufficient |
| Audio underrun | Ring buffer + STOP trigger | Align audio/server events, sched, Binder, wakeups, and the underrun marker |
Device Capabilities and Permissions Constrain Field Capture
A field capture strategy depends on more than TraceConfig: device version, build type, and the trigger emitter’s permissions also matter.
| Environment | What you can rely on | What you should not assume |
|---|---|---|
| userdebug / rooted development device | Use config directories, logs, and additional events as actual permissions allow | Root alone does not guarantee adb root; check adbd/SELinux, log allowlists, and resource budgets |
| user build + adb shell | Ordinary system traces, package-scoped atrace, reviewed presets | Do not rely on android.log by default; do not assume arbitrary config paths are readable; prefer /data/misc/perfetto-configs |
| Ordinary app | Record app-side ATrace / Track Event and activate predeclared triggers | This trigger contract grants no system-consumer privileges; constrained APIs such as ProfilingManager have their own version/policy rules |
| CPU / native / Java profiling | Check debuggable, profileable, and Android version as required by the data source |
Do not treat <profileable> as a universal permission switch for system tracing in the field |
Text proto configurations may use either .pbtxt or .pbtx; parsing is determined by perfetto --txt or an encoding step in the toolchain. This article consistently uses .pbtxt filenames below.
Do not concatenate the sections blindly. The background-stop example needs a complete field.pbtxt with sources, duration, and write_into_file; STOP mode follows trigger lifecycle and SIGINT cannot recover an unmatched incident. Use unique output paths per run. Fixed names here denote a single laboratory run; archive earlier outputs first.
Boot Tracing Is a Special Field Observation Window
App cold starts and system boot are different problems. The older Systrace response-latency walkthrough covered app startup paths, and Part 05 covered rendering after startup. Here, we focus only on device-level boot: when ADB is not yet available but Android has reached the boot phase after /data is mounted, how do we capture a complete system trace, interpret it correctly, and produce reviewable conclusions?
Common scenarios requiring boot tracing include:
| Symptom | Why an ordinary trace is insufficient |
|---|---|
| Slow boot to the home screen | The problem occurs before ADB is available |
| Slow first boot after an OTA | init, zygote, dexopt, and system_server all need examination |
| Late Launcher first frame | system_server, SurfaceFlinger, and Launcher need to be examined together |
| A service blocks during boot | init/service manager/Binder context is needed |
| A user reports “it takes a long time to become usable after reboot” | The readiness criterion must be defined first |
Boot tracing is a specialized platform or system-startup observation window; app cold-start analysis takes a different route. Establish these boundaries before analysis:
- Android boot tracing is available on Android 13+. Earlier versions require a vendor-specific boot capture solution.
- The v58.3 init rule additionally requires persist.traced.enable=1, traced listening, and sys.boot_completed not yet set. Persistent properties load after /data is mounted. It does not cover PMIC power-on, the bootloader, early kernel boot, or first-stage init. Earlier phases require pstore, kernel cmdline/ftrace persistent buffers, or vendor init instrumentation.
- This workflow generally assumes
adb rootis available on a development or userdebug device. Production user builds require platform provisioning, an engineering build, or a vendor diagnostic entry point. - The start of the UI’s relative timeline is not the moment the power button is pressed or the PMIC powers on. SQL
tsretains timestamps in Trace Processor’s unified clock domain and does not automatically reset to zero when tracing starts. Compare relative phases within the same trace in reports; do not directly compare absolutetsvalues across devices. - There is no single definition of “boot complete.” Launcher first frame, accepting input, network availability, navigation service readiness, and background-task completion may be different teams’ metrics.
- Without streaming, output normally becomes available after recording stops. With write_into_file, a partial file can appear earlier; file existence does not establish completion.
persist.debug.perfetto.boottraceis one-shot and must be set again before the next boot.
boottrace.pbtxt is the fixed text-config filename for this boot entry point; do not replace it with a binary proto. duration_ms sets the endpoint. This example uses WRITE_FLUSH_AUTO: with five-second writes, v58.3 requests flushes every five seconds, so an additional flush_period_ms:10000 would not remain effective. Keep the first version modest: start with scheduling, process lifecycle, and limited ATrace, then add signals required by the readiness criterion.
DISCARD retains unread early data but drops new events if it fills before the consumer drains it; RING overwrites unread old events. With periodic writes enabled here, size buffers for peak traffic and delays between actual drains, not automatically for all 90 seconds. The example’s 128 MiB already consumes the default total central-buffer limit under extra guardrails; adding more buffers can exceed it.
1 | duration_ms: 90000 |
The following workflow is for a development device with root access. Archive the previous boottrace first, because init deletes the old file when starting a new capture. Check the changed boot ID, consumed one-shot property, stopped dedicated init service, and nonempty output. These checks still do not replace content validation.
1 | adb root || exit 1 |
Validate the boot trace before analyzing it:
This preset does not enable FrameTimeline or a specific readiness marker. Seeing Launcher, sched, or a first-draw marker does not prove presentation or input readiness. Add the relevant sources, application markers, or external observations when those are acceptance criteria.
- Does the trace cover the Android boot window after
/datais mounted through the target readiness milestone? - Does
statsreport data loss, packet loss, or ftrace setup errors? - Are processes such as init, system_server, surfaceflinger, and launcher visible?
- Do process stats, sched, and frequency all contain data? Check logs only when both the configuration and build type support them.
- Was this a cold boot, an
adb reboot, or the first boot after an OTA? - Does
metadata.jsonrecord the button/reboot method, boot ID, readiness criterion, trace start/end, device build, and capture operator’s permissions?
Use a consistent analysis order: init/service startup, zygote, system_server, PackageManager/ActivityTaskManager, SurfaceFlinger, Launcher, and the first-frame/input-ready milestone. State the readiness criterion explicitly in the report; otherwise, “boot is three seconds slower” can easily become a cross-team dispute.
To read the final portion of ftrace before a crash-induced reboot, see linux.frozen_ftrace in Tracing across reboot. It relies on a persistent ring buffer provisioned in the kernel and is a different approach from the boottrace workflow here, which automatically records during the next Android boot.
Three Modes for Long-Running Field Traces
Long-running Perfetto field traces can use these three modes:
| Mode | Behavior | Suitable scenarios | Risks |
|---|---|---|---|
| Ring buffer + STOP trigger | Continuously records the most recent window and stops after an anomaly | Dropped frames, impending ANRs, audio underruns | A small buffer overwrites the root cause; without a matching trigger, no incident trace is returned |
Long recording with write_into_file |
Periodically writes the buffer to a file | Trends spanning tens of minutes to hours | I/O overhead and oversized files |
--clone-by-name snapshot |
Clones the current window on demand while the original session continues | Parameter tuning, thermal management, power-policy experiments | The device’s perfetto must support --clone-by-name; check perfetto --help / --version. Android service-side cloning typically starts at Android 14, while cloning by name additionally needs a v49+ CLI or corresponding vendor integration |
Do not combine all three into one all-purpose configuration. Choose the mode for the problem first, then choose the data sources.
I choose the mode by how far before the incident I need to look. If the cause is in the preceding few seconds, a measured ring window is more useful than a long trace whose volume nobody has checked. If the trend itself lasts minutes, periodic file writes deserve their overhead assessment.
A Baseline Ring Buffer Configuration
The following configuration targets intermittent UI jank. Recording starts immediately, and the ring buffer retains only the recent window. When the app, a platform service, or a test harness detects a slow frame and activates the app_ui_jank trigger, Perfetto records for one more second and stops.
This configuration assumes that app-side business markers and a trigger emitter, discussed in Part 13, are already integrated. trigger_config does not automatically trigger based on FrameTimeline. Without an emitter, the session simply waits for cleanup at trigger_timeout_ms; it does not provide a fallback capture of the final window.
1 | unique_session_name: "ui_jank_flight_recorder" |
There are two buffers: high-frequency ftrace/atrace goes into the large buffer, while FrameTimeline, process stats, and app Track Events go into the smaller semantic buffer. For graphics problems, retaining sched events is not enough if FrameTimeline, app markers, or process descriptions have been overwritten. You would still be unable to determine whether the jank came from the app, SurfaceFlinger, BufferQueue, or system scheduling.
producer_name_regex restricts which producers may activate the trigger. The regular expression must match the actual emitter’s producer name. If a test harness triggers on its behalf, add the harness producer to the allowlist or give the harness a separate trigger name.
buffer_size_kb=4096 is a per-CPU budget: on eight CPUs this layer alone may take roughly 32 MiB, in addition to shared memory and the 72 MiB central buffers. buffer_size_lower_bound constrains downward adjustment but does not guarantee the exact requested size. Shorter drain_period_ms generally means more wakeups; 250 ms is not universally better than the default. Check actual settings, setup errors, and overhead before tuning.
FrameTimeline requires Android 12 or later. It is not a trigger emitter: it provides facts in the trace, including app frames, SurfaceFlinger frames, jank_type, present_type, on_time_finish, and app-to-SF flows. Restrict atrace_apps to the target package; do not use atrace_apps: "*" as a field default.
Ring buffer retention is determined by the write rate and buffer size, not by trigger_timeout_ms. The estimate remains the one from Part 12: retained seconds per buffer ≈ capacity MiB / rate into that buffer in MiB/s.
For ftrace, examine three buffer layers: kernel per-CPU ftrace buffers, producer shared memory, and the Perfetto central buffer. Enlarging the central buffer only extends the final retained window. It cannot recover events lost earlier because traced_probes could not drain the kernel buffer in time.
Consider ftrace_config.buffer_size_kb, buffer_size_lower_bound, drain_period_ms, and drain_buffer_percent when tuning long-running field traces, after checking overhead and device compatibility in short traces. The current semantics of drain_buffer_percent were introduced in Perfetto v48, and the official proto comments state that it has no effect on Linux kernels older than 6.9. Do not treat it as a universal replacement for drain_period_ms on older kernels.
For a 64 MiB buffer receiving 2 MiB/s, the ideal steady-state window is about 32 seconds. Additional events routed to that buffer shorten it; calculate other buffers independently. Recording the one-second post-trigger tail also consumes ring capacity and overwrites earlier data, so budget both that tail and stop/flush delays. An unmatched STOP returns no data; manual fallback needs an allowed manual trigger or a separate, verified snapshot session.
After recording with the UI jank preset, first confirm that FrameTimeline data actually exists:
1 | SELECT 'expected' AS table_name, COUNT(*) AS rows |
Find the app and SurfaceFlinger upids in process, then replace NULL below with those process instances and the incident window’s integer-nanosecond bounds. The query retains zero counts and unclosed frames that might overlap the window rather than combining same-named processes or fuzzy cmdline matches. Presence is not proof of the target layer, causality, or continuous coverage:
1 | WITH target_window(start_ns, end_ns) AS ( |
If you suspect hardirq, softirq, or kworker activity is taking CPU time, do not put these events into the default long-running trace. Start with a focused short-window preset, then decide from the results whether to include them in field capture:
This is a data-source fragment to merge into a complete config with an explicit duration_ms; it is not a standalone capture command.
1 | data_sources { |
Long Recordings Need Periodic File Writes
Problems spanning hours cannot rely only on memory buffers. Save this as field.pbtxt for the background/stop examples below. It records low-rate CPU-frequency, thermal-node, and system-memory trends, without sched or frame events. Remeasure file growth and perturbation after adding high-rate sources:
1 | unique_session_name: "field_trends" |
By default, duration_ms excludes system suspend time. For field problems measured by wall-clock time, such as screen on/off, standby, or automotive sleep, consider prefer_suspend_clock_for_duration: true. It also changes the time semantics of a trigger’s stop_delay_ms.
Use consistent binary units: 2147483648 bytes is 2 GiB. At measured file growth of 1–2 MiB/s, the cap is reached in about 17–34 minutes, before the one-hour duration. Published scheduler rates are only reference points; budget using the actual preset’s peak/average traffic. Shorter write intervals do not reduce total file volume.
Logs, syscalls, page faults, and broad application events can sharply increase volume. With write_into_file + AUTO in v58.3, write periods over five seconds flush before each write. This ten-second preset therefore would not also follow flush_period_ms:30000. A supporting version can use WRITE_FLUSH_DISABLED plus flush_period_ms for an explicit manual period; historical 10–30-second guidance does not describe current AUTO behavior.
Background Recording and Safe Shutdown
USB often disconnects during field tests. In v58.3, --background-wait waits up to 30 seconds for startup acknowledgement and exits zero on success. A printed PID alone is not that acknowledgement. This does not guarantee every expected event or late-registering producer is ready. Begin the test action after success and validate sources/window afterward. A background child may remain after timeout; inspect session state before retrying.
On Android 12 or later, place the configuration in /data/misc/perfetto-configs, then start the background trace; check write access on the target device:
1 | adb push field.pbtxt /data/misc/perfetto-configs/field.pbtxt || exit 1 |
If the Android 12–17 target device cannot use the configuration path above, pass the config through stdin. Production M2M toolchains should use reviewed presets or binary TraceConfig rather than allowing a server to send arbitrary text configurations:
1 | PID=$(adb shell perfetto --background-wait --txt \ |
Choose one startup method and retain this run’s PID, boot ID, and process-start ticks in the same host shell. Across terminals, persist and restore those fields through case metadata; never kill using only a stale PID. Do not run adb pull immediately after signaling; Perfetto still needs to write the tail. Verify identity before SIGINT, then wait for that process to exit and the file to stabilize before pulling. This example is for non-detached recording.
1 | case "$PID" in |
Run the preceding identity capture immediately after successful startup. After exercising the scenario, use the following stop/pull steps. If the process ended automatically, distinguish normal completion from failure using logs and stats.
1 | CURRENT_BOOT_ID=$(adb shell cat /proc/sys/kernel/random/boot_id) || exit 1 |
When inotifyd is available, install the watch before signaling to avoid missing close_write. Stable size is not proof of close/fsync, and identity-check plus kill is not atomic. Long-running automation should let the session owner manage its lifecycle. Identity changes, timeouts, or inaccessible process state require investigation, not an assumption of completeness or killall.
Using Triggers
The value of Perfetto triggers is permission separation. A privileged party declares trigger names and behavior in advance. An ordinary app or system component only reports that an event occurred; it cannot decide what to capture, how much to capture, or where to upload it.
A field capture strategy must identify the trigger emitter explicitly. For UI jank, app-side JankStats, FrameMetrics, or a custom threshold detector can activate app_ui_jank; a platform service or test harness can also activate it on the app’s behalf. Either path requires deduplication and rate limiting, with the trigger source, activation time, and decision threshold recorded in metadata.
A useful app-emitter contract is a fixed JSON event accompanied by a Track Event with the same event_id:
1 | { |
The server or analysis script uses event_id and the time window to align external trigger records, Track Events inside the trace, and slow FrameTimeline frames. If they do not align, the trace provides only system-side context; it cannot directly attribute the problem to a page or business action.
sample_decision=record is the app’s choice, not service acceptance. event_id identifies a business event, not a FrameTimeline frame token; establish the target process, layer, and mapping rule. CLOCK_BOOTTIME here assumes conversion has already been performed, not that raw JankStats timestamps can be placed into elapsed fields unchanged.
In a test environment, these shell methods can activate a trigger, but this example also sets producer_name_regex. A shell emitter may not match that filter. Use an emitter with a matching producer name, or adjust the filter in a laboratory-only preset; a successful shell command alone does not prove that the session matched the trigger:
1 | adb shell /system/bin/trigger_perfetto app_ui_jank |
Alternatively, activate the same trigger using a configuration containing only activate_triggers:
1 | echo 'activate_triggers: "app_ui_jank"' | \ |
STOP triggers suit problems whose root cause precedes activation, such as slow frames, impending ANRs, and audio underruns. START triggers suit cases that need observation after the event, such as recording for another 10 seconds after temperature enters the hot state.
When using a START trigger, do not also configure an ordinary duration_ms; trigger_config manages the trigger window.
Set trigger_matched=false only after confirming successful session startup and expiry at trigger_timeout_ms without an accepted activation. START/STOP then return no trace data; a file may be empty or absent. An empty file can also reflect permission, configuration, process, or I/O failures. Keep the cause unknown until established. Accepted triggers end the session after stop_delay_ms; restart with a new case ID/file and account for the coverage gap between sessions.
Snapshots for Parameter Tuning
Some scenarios call for a snapshot of the current window every few seconds without stopping the original trace—for example, tuning CPU policies, thermal parameters, or power policies. The --clone-by-name command-line entry point was added in Perfetto v49.0. Session cloning itself also requires Android 14 (U)+ platform support or a vendor integration. Ultimately, check adb shell perfetto --help / --version.
Save the following complete ten-minute example as thermal.pbtxt. It polls CPU frequency and thermal nodes once per second, subject to field/device support and access. It enables neither a STOP trigger nor periodic file writes, so snapshots read the current ring window:
1 | unique_session_name: "thermal_snapshot" |
First confirm no session already uses the name, then start it and clone on demand after success. The original continues until its ten-minute duration expires. Give each clone a new filename. These host commands require --clone-by-name support:
1 | adb shell perfetto --query |
After startup succeeds, exercise the scenario, then capture the desired window with the following commands.
1 | SNAPSHOT_FILE="thermal-$(date +%Y%m%d-%H%M%S).pftrace" |
Cloning requests a flush and consumes extra buffer/read resources; it is not free. Frequent snapshots can overlap and do not automatically form a continuous history, nor recover overwritten events. Check overlap and gaps before treating them as a trend.
Use Detached Mode Sparingly
--detach separates the tracing session from the lifetime of the perfetto command, leaving the traced service holding the session. The official documentation explicitly discourages routine use because it can leak sessions and leave devices tracing for long periods.
If detached mode is necessary, enable write_into_file in the configuration and prepare commands to inspect and stop the session:
1 | adb shell perfetto --detach=field_session --txt \ |
Automation scripts need fallback cleanup: check for old sessions before starting, stop sessions after testing, and stop them automatically when their TTL expires.
A detach key is not unique_session_name. Reattachment requires the same Unix UID; changing root/shell identity can matter. --is_detached returns 0 for an attachable session, 2 when none is found, and 1 for an error; communication failure is not absence. field.pbtxt must contain actual sources, write_into_file, and duration_ms.
Bugreport as a Field Fallback
A user, tester, or operator often encounters the field problem first and triggers a bugreport. Bugreport capture selects eligible sessions that still exist at that time. These are fields to merge into a complete config, not a standalone recording. For thermal.pbtxt above, replace its unique_session_name, keep sources/duration, and start a new session with the revised config:
1 | unique_session_name: "field_thermal_bugreport" |
Only sessions with bugreport_score > 0 become bugreport candidates; higher scores receive higher priority for saving. bugreport_filename supplies a readable filename for --save-all-for-bugreport starting with Android 15 / Perfetto v42; older versions use a default name. This is distinct from ordinary -o /path/trace.perfetto-trace and --upload: the former serves bugreports, while the latter two serve the current command’s output or framework reporting.
Development or platform scripts can verify bugreport behavior with these commands:
1 | adb shell perfetto --save-for-bugreport |
On Android 12/13, saving for a bugreport takes the trace contents and stops the session early. On Android 14 or later, the system creates a read-only snapshot and the original session can continue. Record the Android version and verification-command output in the report. Do not treat bugreport packaging as equivalent to ordinary background tracing.
Choose a save entry point supported by the device rather than running both as a required sequence: --save-for-bugreport selects the highest score, while supported --save-all-for-bugreport saves all eligible sessions. Device-side saving does not itself generate or upload a bugreport ZIP. A positive bugreport_score also makes the session eligible for cloning by other UIDs; scope its data accordingly.
What a Field Evidence Package Should Contain
A .perfetto-trace file alone is not enough. At minimum, a field evidence package must explain the scenario, when it was triggered, and whether the file is complete.
1 | case-20260504-153012/ |
Recommended fields for metadata.json include:
1 | { |
This metadata does not replace the trace. It helps investigators locate the problem window, filter versions, and assess environmental differences. Part 13 covered adding app-side page, business-phase, and user-action information. Metadata in the field evidence package must align those business markers.
summary.json is the entry point for automation, with fields that directly drive triage, upload, and retention:
1 | { |
These JSON objects illustrate a hypothetically validated incident with a twenty-second pre-window and roughly one-second tail; they are not defaults or results inferred from file size. trigger_matched, file_complete, usable_metrics, and upload_allowed need separate evidence; retain null/unknown until verified. Enclosing earliest/latest events do not prove continuity. A/B/C/D are reporting conventions, and a usable core judgment for B needs independent support, not merely a small loss count.
Record clock domains explicitly. Android ftrace commonly uses CLOCK_BOOTTIME, while original log wall time may already be normalized by the parser; do not apply an offset twice. JankStats/FrameMetrics timestamps cannot become elapsed realtime merely by renaming fields. External JSON requires same-boot clock pairs and in-trace markers; wall time can also change due to clock correction.
External metadata should record at least elapsed realtime, wall time, and boot ID together. A more robust approach also records the trigger itself as a Track Event inside the trace. If analysis reveals clock synchronization failures or invalid clock snapshots, reduce confidence in alignment across data sources.
Trim at Capture Time First
Perfetto traces contain incremental state, track descriptors, stats, and metadata. Cutting a binary file after capture can easily break parsing. The recommended order is:
- Use a ring buffer to retain only the recent window.
- Use
stop_delay_msto control how much to retain after the trigger. - Use max_file_size_bytes with write_into_file to stop file growth; reaching the cap ends recording, potentially before the incident. It is not offline trimming.
- Upload the original trace covering the complete incident window.
- Generate summaries, screenshots, and SQL results on the server as lightweight indexes.
If offline trimming is unavoidable, the tool must rewrite TracePackets and retain the necessary descriptors and stats. Do not use dd, text truncation, or arbitrary cuts through a protobuf byte stream.
Upload and Cleanup Policies
Long-running field traces are collected close to real user environments, so upload policies must consider more than engineering convenience:
- Upload by default only under conditions such as Wi-Fi, charging, or user authorization.
- When a file exceeds the threshold, upload the summary first and retain the original trace locally for manual export.
- Collect logs using a tag allowlist and redact them before upload.
- Give trace files a TTL, such as automatic cleanup after three days.
- Apply both TraceConfig’s
max_per_24_hand client-side rate limiting to triggers. - Run
statsbefore upload and record data loss in metadata. - Do not allow servers to send arbitrary text proto configurations to user devices; allow selection only from reviewed presets.
These constraints reduce ad hoc tuning flexibility, but control upload, privacy, and stability risks.
Standard Checks Before Analysis
Long-running field traces are more susceptible to data loss. Before analysis, check two acceptance criteria: whether the time covered by the trace meets expectations, and whether stats supports trusting the key data sources.
1 | SELECT trace_start() AS trace_start_ns, trace_end() AS trace_end_ns, |
If the duration is unexpected, first check trigger_timeout_ms, max_file_size_bytes, whether the background process was killed, whether the file is still open, and whether duration_ms accounts for suspend as intended. When data is incomplete, explicitly state which conclusions must be withheld.
Then run the global quality check. Part 12 already provides the complete stats health-check query and a mapping from common stats items to corrective actions. Reuse that SQL at the beginning of your analysis script; it is not repeated here. Long-running field traces need two additional layers of checking.
First, check each buffer. For traced_buf_*, idx is the buffer index. The global flush/config items listed alongside them below must not be interpreted as buffer-specific indices:
1 | SELECT name, idx, severity, source, value |
With RING_BUFFER, traced_buf_chunks_overwritten does not necessarily indicate an error: it usually means old data outside the retained window was overwritten. Check separately whether it overwrote the root cause before the trigger:
1 | SELECT name, idx, severity, source, value |
If ftrace is enabled in the field, also record the ftrace clock, available events, and setup errors. When comparing traces, differences in clocks or event sets directly affect the credibility of sched/frequency conclusions:
1 | adb shell cat /sys/kernel/tracing/trace_clock |
Turn Intermittent Problems into Evidence
Long-running field traces require observation windows, triggers, completeness criteria, and upload boundaries to be defined in advance. A ring buffer retains the recent window; triggers preserve the incident; write_into_file controls long recordings; --background-wait and file-close checks help ensure the data reaches disk; and stats plus metadata establish how much of that evidence remains trustworthy.
Part 13 covered app-side Track Events, while this article covers field observation windows. Together, they provide the foundation for bringing slow frames, impending ANRs, and business-operation timeouts into a single incident package.
References
- Trace configuration
- Buffers and dataflow
- Tracing in Background
- Running perfetto in detached mode
- Perfetto command-line reference
- TraceConfig proto reference
- Android boot tracing
- FrameTimeline
- ATrace
- Android Log
- Clock synchronization
- Trace Processor Stats
Original source baseline: b23c67be (September 19, 2026), retained below. This review also checks v58.3 init rules, the CLI implementation, TraceConfig, service write/flush logic, and the v49 changelog.
About Me and the Blog
Follow Android Performance.