For slow camera opens, audio underruns, a stalled WebView first screen, or dropped frames in Flutter and games, the evidence is scattered across the app, system_server, cameraserver/audioserver, HAL, SurfaceFlinger, and scheduling tracks. Simply inspecting a few more tracks in the UI can easily leave critical timestamps unnoticed.
Using Camera and Audio as examples, this article describes a reusable method for domain-specific analysis: capture the right data, identify the objects, calculate stages and blocking, and preserve timestamps in the report so that reviewers can return to the UI. Camera and Audio are examples; the core is a domain schema and collaboration with platform tracing. By the end, you should at least be able to break a slow camera open into reviewable stages and an audio underrun into cycle anomalies, rather than delivering only a table of the longest slices.
Version note (reviewed October 2026): Configuration, SQL tables, and triggers are checked against Perfetto v58.3. Record Android, device-producer, and analysis-tool versions separately. The domain schema, event names, roles, state machine, and field restrictions below are proposed team conventions, not built-in Android interfaces or validated product capabilities.
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
This article applies the preceding methods to domain automation: the SQL methods from Part 11, quality checks from Part 12, application semantics from Part 13, and field capture from Part 15 all come together here in specific domains and platform infrastructure. Rather than explaining those methods again, we reorganize them around domain objects.

Illustration: Camera stages belong to the same operation; Audio periods are measured from actual cycles. Reports retain timestamps for returning to the UI.
Domain Analysis Starts with an Object Dictionary
The difficulty with Camera and Audio is not whether a camera/audio category exists, but which objects are actually present in the trace:
| Domain | Objects | Why identify them? |
|---|---|---|
| Camera | App client, cameraserver, provider, HAL, request/session IDs |
Open, configure, preview, and capture can span different processes |
| Audio | App audio thread, audioserver, MixerThread, FastMixer, audio HAL, Bluetooth/audio route |
Underruns often result from cycle jitter, irregular writes, HAL blocking, or scheduling delays |
The first step in automated analysis is to generate an object dictionary: relevant processes, threads, tracks, slice names, log tags, and request/session IDs. Subsequent SQL does not guess a root cause directly; it calculates stage durations and blocking evidence around this dictionary.
The object dictionary should be script output, rather than a set of keywords that exists only in someone’s head. A Camera case may involve the app process, cameraserver, provider@2.7-service, a vendor camera daemon, a codec, and SurfaceFlinger. An Audio case may involve the app, audioserver, audio.primary, Bluetooth, and a media codec.
An initially confirmed object dictionary looks like this. SQL is only one way to generate and validate it:
These are hypothetical reviewed records, not capture results. upid/utid/track_id are local to one trace; archive its hash, machine/boot identity, schema, and SQL versions with the dictionary. Leave confidence null without a calibrated model; keyword matches are not probabilities. Package, SELinux domain, and vendor ownership each need provenance. The old HIDL provider name is only an example: Camera supports AIDL HALs from Android 13, while Audio HAL development uses AIDL from Android 14. Inspect actual service names and transports instead of inferring them from OS versions or process suffixes.
1 | object_dictionary_version: camera_audio_v1 |
The domain schema is the collaboration interface between the app, platform, SQL, and reports. Version it rather than scattering it across articles, scripts, and verbal agreements:
1 | schema_version: camera_domain_v1 |
The script first lists candidate objects, then the domain owner confirms which belong to this scenario. That confirmation must be recorded in a domain_objects table or equivalently structured YAML/JSON, rather than remaining in someone’s head. Subsequent queries join only against this dictionary.
1 | CREATE TABLE domain_objects AS |
Note that this uses ordinary SQLite CREATE TABLE, not CREATE PERFETTO TABLE: the latter creates a read-only table, so UPDATE cannot write back the results of manual confirmation. At a minimum, the fields must cover role, process/thread, track_id, vendor aliases, confidence, and manual confirmation status. The initial version can still be generated from keywords, but reports may use only objects with owner_confirmed = 1. Otherwise, as soon as a vendor HAL, provider, codec, or Bluetooth route changes, the automation falls back to fuzzy matching.
List candidate objects for the domain owner to review as follows:
1 | SELECT DISTINCT |
A person needs to inspect this output. Automation can narrow the scope, but LIKE cannot reliably establish ownership for every provider, HAL, codec, and Bluetooth route.
Write confirmed objects back to the table or archive equivalent YAML/JSON. The following updates only individually reviewed track_id values. Replace NULL; leaving it unset updates nothing. Check upid/utid and track ownership before filling each row, rather than approving every thread in a process. Confirm Audio roles separately.
1 | WITH confirmed(track_id, role) AS ( |
Subsequent SQL uses only objects with owner_confirmed = 1. The candidate table narrows review scope; the confirmed table records ownership. The fixed role enum includes camera_app_client, camera_service, camera_provider, camera_hal, display_service, audio_app, audio_server, audio_mixer, audio_hal, and bluetooth_audio.
The candidate query covers thread tracks only, and keywords do not guarantee completeness. Add process-level, asynchronous, or custom tracks separately after verifying ownership; shared names do not confer confirmation. Slice queries below match exact tracks, while scheduling queries deduplicate their confirmed utid values. Create, confirm, and query the dictionary in the same Trace Processor instance. Rebuilding it loses confirmations; archive them first.
Start Configuration with a Domain Preset
First make the permission boundaries explicit:
| Capability | Who can use it | Notes |
|---|---|---|
| App markers / metadata | Ordinary apps | android.os.Trace, NDK ATrace, Track Event, application metadata |
| Registered triggers | Ordinary apps or test harnesses | Can activate only trigger names predeclared by the platform |
| App profiling | shell + profileable or debuggable |
Prefer profileable for release performance comparisons; use debuggable only for debugging |
| ftrace/process stats/FrameTimeline/system log | shell, Traceur, trusted platform/OEM consumers | Subject to SELinux and build-type restrictions; ordinary apps cannot submit arbitrary system configurations |
| Uploads, rate limits, privacy, preset selection | Platform/OEM diagnostics workflow | Requires authorization, redaction, retention periods, and auditing |
The following is a lab_domain_debug configuration, not a low-overhead field preset. It retains sched/freq/idle, Camera/Audio/hal/binder atrace, FrameTimeline, process stats, and logs for development devices, userdebug, or laboratory reproduction. Reduce the field version to necessary data sources; enable logs, binder_lock, and high-frequency domain events only for short, focused traces.
Check configuration against the actual Android and Perfetto versions. On Android 12+, prefer /data/misc/perfetto-configs; shell stdin redirection and a direct file open by perfetto follow different permission paths. A visible camera frame still needs target-layer/present evidence; FrameTimeline does not cover every preview path. The 128 MiB central buffer is a laboratory starting point and already consumes the default extra-guardrails total budget. Measure write rates and window coverage. This configuration does not periodically write a file: flush_period_ms requests producer flushes, not a file save every ten seconds.
1 | duration_ms: 30000 |
atrace_categories enables Android system categories; it does not automatically enable android.os.Trace or NDK ATrace in the app. App-side ATrace shares ATRACE_TAG_APP and requires package-specific atrace_apps configuration, with no separate category filtering. When arguments and cross-thread correlation are needed, use the Perfetto SDK Track Event approach from Part 13.
On user builds, release apps can expose supported shell-profiling access with <profileable android:shell="true" />; debuggable changes runtime characteristics. CPU/native profiling is not universally restricted to userdebug: eligibility depends on the tool, app, heap type, and platform policy. Check system-service, HAL, kernel-stack, and other-process permissions separately. Android 15/API 35 ProfilingManager lets apps request system-controlled profiling, including system traces; this does not grant arbitrary TraceConfig, consumer-socket access, or access to other apps’ raw data.
Official documentation lists android.log support for userdebug. A rooted user build may also capture logs if logd/SELinux permissions allow it, but adb root alone does not guarantee this as a general capability. On ordinary user builds or in production, do not assume logs will be in the trace. Include external logcat, bugreports, or platform-redacted logs in the same case package. Field packages should retain only allowlisted tags and window summaries; raw logs require authorization and redaction.
Category names also depend on the target system. Perfetto documentation separates ATrace into system categories and per-app events. System categories come from Android internal processes; app events share ATRACE_TAG_APP and must be enabled by package name. Do not simply copy the category list from someone else’s device into a preset. Check supported categories for the current version in the Perfetto UI Record page or the device-side configuration.
buffers.size_kb configures the Perfetto central buffer, not the ftrace per-CPU kernel ring buffer. For sched/ftrace data loss, inspect ftrace overrun/dropped/data_loss entries in stats. Adjust parameters such as ftrace_config.buffer_size_kb and drain_period_ms only when necessary, first verifying device compatibility and overhead with a short trace. Before adding vendor kernel tracepoints, check /sys/kernel/tracing/events/*/*/format, field stability, and Perfetto parser/stdlib support. Otherwise, they can only serve as candidate evidence in raw ftrace, or require changes to the Perfetto parser or a custom data source.
Do not express the quality gate as only an abstract score. At a minimum, always run this SQL:
1 | SELECT name, idx, severity, source, value |
The meaning of idx varies by statistic: for traced_buf_* it is usually a buffer index, and for ftrace CPU statistics it is a CPU number; check definitions for other entries. Group findings by affected_signal: ftrace/sched, atrace/systrace, track_event, FrameTimeline, android.log, clock, and central_buffer. Explain overwritten data separately under RING_BUFFER: it may simply mean old data outside the window was overwritten, or that insufficient pre-issue evidence remains. Apply separate report downgrades for data loss, packet loss, and overwrites.
This SQL lists anomalies; it does not prove completeness. Inspect detailed configuration/startup metadata, target events, issue-window coverage, clocks, and parser compatibility before assigning data_quality=clean. Setup statistics can have info severity in v58.3, so retain the explicit list. Trace-wide or buffer statistics do not identify a particular affected HAL event. Record unknown impact separately. filter_tags filters tags, not user content inside messages; external logs also need clock alignment.
Analyze Camera by Stage
For a slow camera open, I want to follow the operation in order: when the API returns, when the HAL supplies a buffer, and when the preview actually appears. Break it into at least these stages:
| Stage | Owner | What to examine |
|---|---|---|
| App initiates open | App marker | User action, permissions, whether the UI thread is waiting |
App to cameraserver |
App + camera service | Binder round trips, server thread-pool congestion |
cameraserver to provider/HAL |
Platform event | Provider process, HAL threads, device waits |
| Configure streams | Platform/HAL event | Stream count, resolution, Surface, HAL configure duration |
| First HAL buffer | HAL/provider event | Request submission, buffer returned from HAL |
| First presented frame | SurfaceFlinger layer/present evidence + app marker; FrameTimeline where supported | Verify that the Buffer was actually presented; SurfaceView previews cannot be judged from FrameTimeline alone |
| Capture/result | Platform/HAL event | Request ID, result callback, buffer/metadata return |
These are three different measurement endpoints: API return, the first frame returned from HAL, and the first frame visible to the user. The first two primarily concern the Camera framework/HAL; the third also involves BufferQueue and SurfaceFlinger. FrameTimeline availability depends on the preview path. State which one you measured in the report. Otherwise, “open got faster” might mean only that the API returned earlier, with no improvement to the first preview frame.
Camera2 needs another distinct endpoint: return from CameraManager.openCamera() ends the call, while CameraDevice.StateCallback.onOpened() reports successful opening. Session creation and request submission follow. Below, open_api measures only the call; camera_open_callback measures OpenStart to Opened, including callback-dispatch delay, not pure HAL work. Record success, exceptions, and disconnection separately. FirstHalBuffer is also distinct from capture-result metadata and presentation.
Domain SQL must be tied to the issue window. That window can come from trigger metadata, application markers, user reproduction steps, or an interval selected manually in the UI. If owner events are missing, report missing_owner_event or stage_unavailable; do not promote a LIKE query into a stage conclusion.
1 | WITH target_window(start_ts, end_ts) AS ( |
For stable events, select one operation from one client instance and propagate client_instance_id/camera_id/session_id/operation_id across processes, matching the target stream and surface separately. This SQL assumes zero-duration Track Event markers using the schema names, or equivalent normalized events retaining original IDs. ATrace async slices do not directly satisfy that contract. Replace NULL with integer-nanosecond boundaries and confirmed IDs; the half-open window must contain the full operation. Missing, duplicate, reversed, or explicitly non-successful endpoints do not produce successful-stage durations. Retain original slice/track/argument evidence for review.
1 | WITH target_window(start_ts, end_ts) AS ( |
If stable events have not yet been fully instrumented, fall back to a candidate-slice query. This query only establishes candidate stages. Focus on process_name, thread_name, slice_name, and dur_ms; it cannot directly replace the stage definitions above:
1 | WITH target_window(start_ts, end_ts) AS ( |
If this query does not provide enough stage information, there are usually two options: add android.os.Trace / ATRACE / Track Event instrumentation, or have the platform add stable event names at key stages in CameraService, the provider, and HAL. Validate camera_id/session_id/request_id in event arguments. With Track Event, obtain them from args/debug annotations or flow IDs rather than relying only on event names.
Candidate SQL retains raw nanoseconds, slice/track/upid/utid, and window overlap. Zero-duration markers are selected by timestamp; dur=-1 remains an unclosed candidate without a duration calculation. Inspect other invalid negative values separately. Top-N truncation is for reading, not proof of complete stage coverage.
Analyze Audio by Cycle
Audio issues depend even more on periodic behavior than Camera issues. One long slice does not necessarily cause an audible problem. Jitter across several consecutive mixer cycles, irregular app write intervals, and blocking HAL writes are more directly relevant to underruns or dropouts.
An Audio report should first examine whether the tail of the cycle-duration distribution grows, whether anomalies occur consecutively, and whether they coincide with underrun logs, route changes, or the time of an audible problem. The longest slices are supporting clues only.
Audio candidate slices must also be restricted to the issue window and should preferentially use domain_objects, preventing historical noise from the entire trace from entering the analysis:
1 | WITH target_window(start_ts, end_ts) AS ( |
Inspect scheduling intervals for confirmed audio_mixer threads. Preemption can split one work cycle into multiple sched rows, so adjacent Running starts do not define a mixer period. The query preserves utid/upid/ucpu and raw nanoseconds, calculating gaps only between neighboring completed records in the window. Leave the last interval unset without a next row. Correlate thread_state, HAL, underruns, and routes; missing sched records prevent interpreting surviving neighbors as complete scheduling history.
1 | WITH target_window(start_ts, end_ts) AS ( |
sched_start_interval_ms and off_cpu_gap_ms describe scheduling only. To measure mixer periods, use a confirmed application/platform slice emitted once per cycle, such as the AudioFlinger#MixerCycle event defined below. Restrict to one track and route generation, then calculate adjacent start intervals in time order. Do not compare arbitrary context switches with audio buffer deadlines.
After confirming that AudioFlinger#MixerCycle has been instrumented or mapped, calculate actual cycle start intervals:
This example additionally requires integer cycle_seq and route_generation. Change the generation after route changes, playback restarts, or mixer reconfiguration; leave periods unset for sequence gaps or duplicate timestamps. Select one confirmed track and generation, and output time order to preserve consecutive anomalies. Without those fields, report candidate intervals only. Slice duration is wall-clock work span, potentially including waits, not CPU Running time.
1 | WITH target(start_ts, end_ts, track_id, route_generation) AS ( |
Do not assign period_ms a fixed threshold across devices. It depends on sample rate, buffer size, fast path, Bluetooth route, offload, and how AAudio/OpenSL ES/AudioTrack is used. A more reliable method is to establish a baseline from clean playback on the same device and route, then compare whether the tail grows in the problematic capture, whether it grows across consecutive cycles, and whether that coincides with underrun logs or audible-problem timestamps.
Calculate Running and Runnable Separately
Many domain reports mix thread states together. Parts 09 and 11 already explain Running/Runnable definitions, the meaning of R+, and the prerequisites for scheduling evidence: complete sched data and no ftrace loss in stats. We will not repeat them here. Domain analysis differs in only one respect: the threads being measured come from the domain_objects dictionary with owner_confirmed = 1, rather than guesses based on thread names.
This SQL outputs both Running and Runnable for domain threads within the issue window:
1 | WITH target_window(start_ts, end_ts) AS ( |
This uses Running/Runnable states in thread_state for a consistent calculation. Durations remain blank when records are missing or cover only part of the window. full means only interval coverage and does not replace stats checks. If a thread was alive for only part of the window, narrow the window before calculating again.
Coverage assumes non-overlapping state intervals for each utid. Downgrade overlaps or invalid durations rather than allowing sums to hide gaps. Trace Processor normalizes event clocks; external markers/logs still need same-boot identity and a demonstrated clock conversion. Wall-clock or MONOTONIC timestamps cannot simply be relabeled as the same nanosecond timeline.
If Runnable time is high, first examine CPU contention, thread priority, frequency, and wakeup sources. If Running time is high, consider CPU profiling or function-level hotspots.
Use Logs to Add Stage Semantics
Logs are useful for adding request IDs, session IDs, route changes, underruns, and error codes. When the trace includes android.log, restrict it to the issue window as well:
1 | WITH target_window(start_ts, end_ts) AS ( |
Do not treat logs as the sole evidence. They name the stages; durations and blocking still need to be traced back to slices, sched, thread_state, Binder, and HAL threads.
Filtering by confirmed process still yields candidates: shared services can log other clients or requests, so match session/operation/route next. Logs without process/thread identity are excluded here and should be listed as unattributed evidence. Zero rows do not prove that no underrun occurred.
Reports Must Lead Back to the UI
Automated reports should output more than SQL tables. Use four fixed sections:
| Section | Output |
|---|---|
| Data quality | Trace duration, stats anomalies, presence of key data sources |
| Object dictionary | Relevant processes, threads, tracks, log tags, request/session IDs, confirmation status |
| Stage metrics | Camera open/config/HAL buffer/presented frame; Audio write/mix/output cycles |
| Review points | ts, dur, process, thread, slice/log names |
An illustrative report structure (the numbers below are examples, not measurements from a supplied trace):
1 | Camera open analysis |
Every anomaly in the report should lead back to Perfetto UI. Conclusions without timestamps cannot be reviewed by someone else.
For domain owners, a report must point to reviewable intervals rather than stop at “the script says Camera HAL is slow.” For example: provider configure took 98 ms, the request ID was 17, a Binder thread was Runnable for 41 ms at the same time, and there was no data loss. The script assembles the context; the final conclusion must still be verifiable against the UI and source code paths.
The server and case package also need summary.json. The following retains illustrative durations but leaves unverified quality, attribution, and redaction fields unknown or not_evaluated. Real reports must supply hashes, trace/device versions, object IDs, windows, and evidence locations. The two examples are not output from one measured run:
1 | { |
Add Stable Events in the App and Platform
Name drift is a major problem for Camera/Audio automation. Slice names can vary across Android versions, vendor HALs, camera modules, and audio routes. Add stable events in code under your control:
| Event | Owner | Required args | Purpose | Privacy boundary |
|---|---|---|---|---|
Camera#OpenStart |
App | client_instance_id, camera_id, session_id, operation_id, event_id |
Start of open_api |
No user content |
Camera#OpenEnd |
App | client_instance_id, camera_id, session_id, operation_id, event_id, result |
End of open_api |
Allowlisted error codes |
Camera#Opened |
App | client_instance_id, camera_id, session_id, operation_id, result |
Successful onOpened callback | Allowlisted result |
Camera#ConfigureStreamsEnd |
platform | client_instance_id, camera_id, session_id, operation_id, stream_id, result |
Configure endpoint | Allowlisted result |
Camera#FirstHalBuffer |
platform | client_instance_id, camera_id, session_id, operation_id, stream_id |
First valid target-stream buffer | No image content |
Camera#ConfigureStreamsStart |
Platform | client_instance_id, camera_id, session_id, operation_id, stream_id |
Start of configure stage | Resolution/format may be retained |
Camera#FirstPreviewFrame |
Platform/App | client_instance_id, camera_id, session_id, operation_id, stream_id, surface_id |
Candidate first visible frame | No image content |
AudioFlinger#MixerCycle |
platform | cycle_seq, route_generation |
One loop on a confirmed track | No media content |
Audio#TrackStart |
App/Platform | track_id, route, sample_rate, buffer_frames |
Start of audio cycles | Route represented as an enum |
Audio#WriteBuffer |
App | track_id, frames, buffer_level |
App write cadence | No media content |
Audio#Underrun |
Platform | track_id, route, sample_rate, buffer_frames, underrun_count |
Underrun event | Allowlisted routes/tags |
Audio#RouteChange |
Platform | old_route, new_route, reason |
Route change | No unique device identifiers |
For Camera markers, this example defines result as the string success or an explicit error enum. Success on OpenEnd means only a normal call return. Audio-domain track_id identifies an application audio track, not a Trace Processor track; map them explicitly.
These events can come from android.os.Trace, NDK ATrace, Perfetto SDK Track Event, or platform ATRACE. Keep event names stable; do not concatenate dynamic IDs into them.
With ATrace, focus on stable slice names and async cookies. Prefer Track Event arguments, flow IDs, logs/side tables, or metadata in the same case for ID information. Counters should contain interpretable numeric values such as queue depth, buffer level, and underrun count. For richer arguments and cross-thread correlation, prefer Perfetto SDK Track Event as described in Part 13.
A platform-specific custom data source is a lower priority. Build one only for high-frequency, strongly structured data or information that ordinary slices/counters cannot represent. Its manifest must specify the data source name, producer process, SELinux domain, TraceConfig, parser, and SQL compatibility policy.
Expand Domain Methods into an Issue Checklist
Camera and Audio are only examples. To use Perfetto reliably as a team, translate common symptoms into capture presets, collectors, build types, analysis paths, and output evidence.
| Issue | Base data sources | Additional domain signals | Default output |
|---|---|---|---|
| UI jank | sched, freq, idle, gfx, view, wm, input, process_stats, FrameTimeline | binder_driver, app ATrace/Track Event | Problematic frames, App/HWUI/SurfaceFlinger stages, thread states |
| Slow tap response | input, sched, freq, view, wm, binder_driver, FrameTimeline | App visual-state counters, high-speed camera comparison | Input to first visible change |
| App startup | sched, freq, am, wm, view, binder_driver, FrameTimeline | log, CPU profiling, app ATrace/Track Event | Startup stages, first frame, longest waits |
| ANR | sched, freq, binder_driver, am, wm | lock, IO, CPU profiling; authorized android.log where supported |
Main-thread wait evidence |
| Slow Binder | sched, binder_driver, binder_lock, process_stats | system_server logs, interface events |
Top N slow transactions, server state |
| Native memory | process_stats | heapprofd, log, application triggers | Allocation stacks and growth trends |
| Camera | sched, freq, camera, hal, binder_driver, SurfaceFlinger layer/present; FrameTimeline where supported | log, CPU profiling, platform events | Open/config/HAL buffer/first visible frame |
| Audio | sched, freq, audio, hal, binder_driver | Authorized logs, CPU profiling, platform events | Cycle jitter around underruns |
| Power/thermal management | sched, freq, idle, android.power, thermal | GPU counters, devfreq, power rails; authorized android.log where supported |
Resource trends and anomalous windows |
Every preset needs an owner, version, purpose, default window, collector, build boundaries, data sources, overhead level, privacy level, and quality gate. Without these fields, presets quickly become a pile of configurations that nobody dares to delete:
1 | preset_id: ui_jank_v4 |
Ordinary apps cannot submit arbitrary system TraceConfigs or directly own the consumer socket. They can emit markers/triggers and metadata, and request policy-controlled profiling through ProfilingManager on API 35+. Full platform sessions are normally owned by OEM diagnostics, Traceur, shell, or laboratory tooling. Validate request acceptance and usable results separately.
On user builds, Traceur, shell, and authorized platform/OEM diagnostics are common collection entry points; the exact consumers and permissions depend on the device. Arbitrary ordinary apps or unauthorized diagnostics apps cannot directly control the system-level Perfetto consumer socket. The proposed team policy distributes reviewed binary TraceConfigs or preset IDs. Binary format helps machine compatibility; it is not authorization or redaction. Validate allowed data sources and fields as well.
Before production upload, require appropriate authorization, access control, retention limits, and verified redaction. trace_filter acts at service ReadBuffers for both IPC readback and write_into_file; it does not prove sensitive fields never entered producer or central buffers. The early Android 12 implementation was broken, Android 13 reintroduced it, and Android 14 added a string-filtering bytecode version. Generate and verify rules for the target service; unmatched strings can remain unchanged. Record rule versions and output hashes. A tag allowlist does not redact message content.
Reports also need evidence grades for their conclusions:
| Grade | Wording | Required fields |
|---|---|---|
| Confirmed | X blocked Y within window A |
owner_confirmed=true, complete ordered stage events, required_signals_available=true, data_quality=clean, direct dependency/wait evidence and alternative-cause checks, ui_review_points>=1, source_sql |
| Tentative | The evidence points more toward X |
Primary evidence holds; missing_evidence lists missing source code, logs, or domain state |
| Not supported | The evidence does not currently support X |
signal_present=true and metrics do not support that direction; this cannot be inferred from missing data |
| Downgraded | This trace cannot determine X |
degrade_reason specifies data loss, a missing data source, or insufficient samples |
These grades protect report quality. Many engineering disagreements stem not from the trace itself, but from assigning the wrong conclusion grade: evidence that supports only a tentative direction is presented as confirmed.
Complete stages and clean data establish observations, not automatically that X blocked Y. For example, 41 ms of Runnable time across a window and a 98 ms configure span do not prove that one Binder call was delayed by 41 ms. Recompute within the target dependency interval and establish the wait chain. Attribution to a policy or hardware change also needs matched controls or A/B evidence.
What the Platform and OEM Need to Build
Apps can add application semantics, but many issues occur outside their view: blocked system_server Binder calls, AudioFlinger underruns, Camera HAL blocking, Power HAL policy changes, scheduler CPU migrations, insufficient GPU execution tracks, or evidence scattered through a bugreport. The platform needs to standardize four things: presets, triggers, evidence-package format, and the automated analysis entry point.
Engineers should not have to write an ad hoc TraceConfig every time. Manage TraceConfig versions like an interface, with review, testing, and releases in a repository:
1 | perfetto-presets/ |
A Perfetto infrastructure should have at least three layers:
| Layer | Use case | Collector | Capture characteristics | Typical data sources |
|---|---|---|---|---|
| field | Production/staged-rollout issues on user builds | Platform/OEM diagnostics components or Traceur; apps emit only markers/triggers | Low overhead, ring buffer, stop trigger for a short window | sched, process stats, a few app ATrace events, necessary counters |
| lab | Laboratory reproduction and version comparisons | shell, test frameworks, platform scripts | Controlled devices, repeatable scripts, stable metrics | FrameTimeline, freq, idle, Binder, domain log excerpts |
| deep | Focused investigation | userdebug/root, platform engineering tools | Short duration, high overhead, manual operation | heapprofd, linux.perf, GPU counters, additional HAL events |
The proposed field baseline excludes deep presets to avoid enabling unmeasured laboratory configurations for long periods. This is a team default, not a Perfetto prohibition on production profiling. heapprofd, linux.perf, or system-managed profiling can have controlled field uses, with separate review of target scope, sampling, duration, perturbation, and privacy. They do not inherit a low-overhead label from this example.
Document the boundaries of deep presets separately:
linux.perfis a sampling profiler, not a complete event stream. By default, restrict it to short windows, low sampling rates, and target processes/threads, and document unwind, kernel-frame, root/userdebug/kptr restrictions.- Before using vendor tracepoints, check
/sys/kernel/tracing/events/*/*/format, field stability, andftrace_setup_errors. Unknown events can serve only as raw events or candidate evidence. - This example field policy excludes
linux.perf, heapprofd, GPU counters, and high-frequency syscall/pagefault events. Necessary counters must specify sampling period, units, supported devices, and upload privacy level. - Each preset needs measured write rates and a data-loss baseline. A
cost_levelis no substitute for a real budget.
A ring buffer is useful for retaining a short window around a trigger; it cannot preserve all context indefinitely. Long traces need the background capture, segmented storage, and explicit exit conditions from Part 15.
The trigger registry also needs consolidation. App agents, Framework watchdog, AudioFlinger, CameraService, Thermal/Power, and laboratory tools may send only registered trigger names. The platform then applies rate limits by device, user, version, scenario, network, and storage state. Once a name enters the platform, do not change it casually: doing so breaks server indexes, SQL templates, historical trends, and alert rules.
Registration here is a team protocol: a producer can send an unconfigured name, but no matching session action follows. Acceptance also depends on trigger configuration, timeout, sampling, rate limits, and optional producer_name_regex. A send returning does not prove a trace was captured. Names are not secrets or credentials, and producer-name filtering does not replace platform identity checks.
Integrate Bugreport with Perfetto as well. bugreport_score > 0 marks a running trace session as a Bugreport candidate; sessions with bugreport_score <= 0 are excluded. When Android dumpstate calls perfetto --save-for-bugreport, it selects the highest-scoring candidate trace and saves it to the Bugreport path. Android 12/13 saves the candidate trace and stops the original session early; Android 14 or later creates a read-only snapshot and lets the original session continue. bugreport_filename is an Android 15 / Perfetto v42+ field for output names under --save-all-for-bugreport; save-all differs from selecting only the highest score. A positive score makes the session eligible for cloning by other UIDs; it does not remove same-UID restrictions on detached-session reattachment. Record that access boundary. Saving a trace does not itself package or upload a bugreport.
Build a system instrumentation dictionary before adding code:
1 | camera: |
This platform dictionary and the Camera#* names in the earlier domain schema are two distinct layers. CameraService#Open and CameraHAL#FirstResult are instrumentation implementation names inside individual services; Camera#OpenStart and Camera#FirstHalBuffer are interface names used for analysis and reporting. Match the layers through the owner field in the schema’s stable_events and a mapping table. SQL and reports use only schema names; renaming an implementation event requires updating only the mapping, not the analysis layer.
These implementation names are hypothetical too. The owner field does not execute a mapping: define begin/end semantics, units, argument propagation, success/failure, and original slice/track IDs. FirstResult may be metadata only and cannot simply be renamed FirstHalBuffer. The earlier SQL reads schema markers directly; implementation-only events require a validated normalization step first.
Names must be stable and their meaning suitable for SQL aggregation. Put dynamic information in arguments, counters, or metadata, rather than concatenating it into event names. Plaintext URLs, contacts, message content, search terms, and geographic locations must not enter trace events.
The server needs at least five functions: file acceptance checks, evidence-reliability checks, scenario summaries, aggregation indexes, and a manual review entry point. Before anyone opens the UI, the report should already identify the preset, trigger, trigger window, quality-gate status, relevant processes and threads, and the owner indicated by the machine-generated summary.
Represent processing status as an explicit state machine:
1 | ingested |
This state machine constrains the provenance of evidence: every conclusion can be traced back to the same preset, SQL, and evidence grade. After a version upgrade, renamed fields, missing events, or unavailable data sources can trigger a downgrade directly at quality_checked, preventing reports from continuing to emit apparently certain conclusions.
Common Pitfalls
- Matching only
LIKE '%camera%'misses providers, vendor HALs, codecs, and media processes. - Enabling only
atrace_categoriesmisses app-sideandroid.os.Trace; useatrace_appsor Track Event. - Ordinary apps cannot submit arbitrary system configurations; besides markers/triggers, API 35+ offers system-managed
ProfilingManagerrequests. - Prefer FrameTimeline for ordinary View UI, startup, and tap-response analysis. If the target uses SurfaceView or the device has no corresponding frame data, inspect target layer/present evidence and state that the frame metric is unavailable.
- Design for the documented userdebug support of
android.log. On rooted user devices, verify permissions and actual data; prepare alternative sources for ordinary user builds. - Slow Binder is a symptom. The server may be waiting for HAL, a lock, IO, or downstream hardware.
- Analyze Audio cycle jitter rather than only the longest slices.
- Separate Camera open, configure, preview, and capture rather than combining them into one duration.
- For Running, use actual execution intervals (
schedorRunninginthread_state); for Runnable, useR/R+inthread_state. Calculate them separately. - An agent or script organizes evidence; it cannot replace UI review and domain-owner judgment.
- Distinguish API return, HAL buffer return, and visible presentation when measuring the first camera frame.
- Establish Audio period thresholds against a baseline for the device, route, and buffer configuration.
- Increasingly heavy production presets create power, privacy, and stability problems.
- Without a system instrumentation dictionary, event-name drift invalidates SQL and historical trends.
- Keeping Bugreports and traces separate forces engineers to locate the same time window manually across multiple attachments.
Summary
For domain problems such as Camera and Audio, standardize a preset, build an object dictionary, write SQL around stages and cycles, produce reports with timestamps, and return to the UI to review anomalies.
The same approach applies to WebView, Flutter, games, and vendor-defined system services. Only the object dictionary and metric templates change. The foundation remains trace quality, process/thread ownership, stage durations, scheduling states, and log semantics. Add platform support for presets, triggers, Bugreport, system instrumentation, server summaries, and permission boundaries, and Perfetto can grow from an individual tool into a team capability.
SmartPerfetto combines scenario queries with evidence review. Check its repository for currently supported scenarios and limits; the six-week update review describes the design at the time of writing.
References
- ATrace data source
- Track events
- Android Log data source
- Android Jank detection with FrameTimeline
- Buffers and dataflow
- Trace Processor Stats
- Getting Started with PerfettoSQL
- Trace Processor Python API
- AOSP Camera HAL
- AOSP Audio architecture
- Advanced System Tracing on Android
- TraceConfig reference
- Android
<profileable>manifest element
Source revision checked: v58.3 (2026-10-14).
About Me and the Blog
Follow Android Performance.