perf-analyze)perf-analyze is a host-side tool designed to perform standalone, automated, and human-interactive performance analysis on Fuchsia traces. The tool acts as a CLI orchestrator that delegates trace processing to specialized analysis plugins.
query: Executes SQL queries to inspect and extract structured data from traces. Backed by Perfetto's Trace Processor.analyze: Executes specialized analysis plugins to identify performance anomalies (e.g., binder delays, jank, CPU starvation).visualize (Planned): Generates HTML reports, flamegraphs, SVGs, or deep-link trampoline URLs for the Perfetto UI.--format <json|markdown|text>: Specifies the output format (default: text).text: Unformatted tab-separated values (TSV), ideal for shell scripting.markdown: Formatted Markdown tables, ideal for doc insertion.json: Structured JSON, ideal for automated tool ingestion.query SubcommandThe query subcommand executes SQL queries against a trace file using Perfetto's Trace Processor.
--trace <path_or_url> (Required): File path to a local .fxt trace file or a URL to a remote trace.--sql <query_string>: Executes a single raw SQL query. Mutually exclusive with --batch.--batch <json_string_or_@filepath>: Executes a JSON array of queries in the format [{"name": "query_name", "sql": "select ..."}, ...]. Use @filepath to read the array from a local file. Mutually exclusive with --sql.fx perf-analyze query --help
Output:
usage: perf-analyze query [-h] --trace TRACE (--sql SQL | --batch BATCH) options: -h, --help show this help message and exit --trace TRACE Trace file path or URL --sql SQL SQL query to run --batch BATCH Batch JSON or @file
fx perf-analyze query \ --trace src/performance/perf-analyze/test-data/sample_fxt.fxt \ --sql "select count(*) as cnt from slice"
Output:
cnt 520
fx perf-analyze --format markdown query \ --trace src/performance/perf-analyze/test-data/sample_fxt.fxt \ --sql "select count(*) as cnt from slice"
Output:
| cnt | | --- | | 520 |
fx perf-analyze --format json query \ --trace src/performance/perf-analyze/test-data/sample_fxt.fxt \ --batch @src/performance/perf-analyze/test-data/sample_queries.json
Output:
[ { "name": "slice_count", "results": [ { "cnt": 520 } ] }, { "name": "process_count", "results": [ { "cnt": 2 } ] } ]
analyze SubcommandThe analyze subcommand runs specialized analysis plugins against a trace file to identify specific performance anomalies.
--trace <path_or_url> (Required): File path to a local .fxt trace file or a URL to a remote trace.--plugin <plugin_name> (Required): Name of the analysis plugin to execute (e.g., binder).--list-plugins: Lists all available analysis plugins.app-launch (App Launch Critical Path Breakdown)Decomposes the critical path of Android/Starnix application launches into contiguous chronological phase segments, intra-segment sub-operations, per-segment kernel thread states, and Fuchsia subsystem bottlenecks.
The plugin executes several diagnostic sections in order:
total_launch_ms (the launchingActivity slice) and displayed_ms (from ActivityTaskManager's START to the launch end, which matches logcat Displayed and AtmDisplayTime), and divides each launch into pre_start_ms (intent receipt to START, e.g. intent resolution or lock waits; 0 when the trace has no START marker, as for some hot launches), pre_fork_ms (START to Start proc), process_fork_ms (Zygote fork & specialization), attach_app_ms (ART post-fork init & attachApplication IPC), bind_app_ms (bindApplication), activity_lifecycle_ms (activityStart + activityResume), first_frame_ui_ms (first Choreographer#doFrame UI traversal), first_frame_render_ms (first RenderThread DrawFrame to TTID), and display_present_tail_ms (post-TTID Fuchsia Display::Client::CommitConfig). Warm and hot launches have no fork or bindApplication, so those columns are empty and pre_activity_ms covers the launch start to the first activityStart/activityResume. unattributed_ms is launch time that no segment covers because a milestone is missing, so the segments up to first_frame_render_ms plus unattributed_ms sum to total_launch_ms.init, draw, render, traversal, animation, and scenic). Nested slices (e.g. traversal > draw-VRI[...] > Record View#draw()) count only their self time, so no time on a thread is counted twice. Sub-stages on different threads can overlap in time. Reported as raw milliseconds (--scaling raw) or as each sub-stage's share of the attributed work, scaled to total_launch_ms (--scaling proportional).open_dex_ms, dlopen_ms, verify_class_ms, class_init_ms, get_resources_ms, inflate_ms, relayout_window_ms, dequeue_buffer_ms, shader_compile_ms, and queue_present_ms.running_cpu_ms (Running), runnable_starved_ms (R/R+/W), and sleeping_blocked_ms (S/D).BinderIoctl on Starnix), and Java monitor and native lock contention on the main thread and RenderThread. These count only outermost slices, clipped to the launch. It also reports system-wide Fxfs storage page-ins (page_in_chunk), time sysmem was busy (sysmem_busy_ms, not necessarily on the app's critical path), and Starnix SELinux hook overhead in the app process.sched.thread_executing_span (critical_path.py) to rank exact critical-path milliseconds (critical_path_ms, pct_of_total, running_ms, runnable_ms, waking_ms, unattributed_sleep_ms, unknown_ms) across the app, system_server, zygote, sysmem, fxfs, and GPU drivers.<uninstrumented> when executing without trace slices) along each launch's critical path.handoff_from whenever execution shifts between threads via wakeup or blocked wait.--package <str>: Optional substring filter for package or process name.--limit <int>: Maximum rows returned in sections 1-5 (default: 20); in critical-path sections 6-8, caps analyzed launches to limit and returns up to limit threads/slices and max(limit * 3, 50) timeline spans per launch.--scaling <raw|proportional>: Report CUJ sub-stage durations as raw milliseconds (raw, default) or as each sub-stage's share of the attributed work, scaled to total_launch_ms (proportional).fx perf-analyze --format markdown analyze \ --trace out/e2e-testout/trace.fxt \ --plugin app-launch \ --package settings
critical-path (Thread-Execution Critical Path Analyzer)Computes the scheduler wakeup and trace flow critical path between any two thread-bound points in a trace using Perfetto's sched.thread_executing_span and intervals.intersect standard library modules.
Because scheduler wakeup chains (thread_state / sched_slice) are anchored to OS threads (utid), both --start-* and --end-* selectors must resolve to a thread-bound (utid, ts) point:
--start-slice-id <id>, --end-slice-id <id>): Selects a specific row in slice whose track is a thread_track.--start-slice <glob>, --end-slice <glob>): Matches slice.name GLOB <glob> on thread_track, optionally narrowed with --*-process <glob>, --*-thread <glob>, --*-index <N> (0 for first, -1 for last), and --*-edge <start|end> (defaults: start for the start point, end for the end point).--start-ts <ns> with --start-tid <tid> or --start-utid <utid>, and matching --end-* flags).Process-scoped or async-track slices (for example launchingActivity#<id> on process_track, where utid IS NULL) are explicitly rejected with an error explaining how to anchor on a thread-track slice (such as Start proc:* or the instant launchingActivity#<id>:completed*:* slice) or pass --*-ts + --*-tid.
When the start and end points lie on different threads, critical-path discovers a contiguous thread spine across [start_ts, end_ts] by walking deterministic scheduler wakeups (COALESCE(waker_id, prev_id)) first, falling back to bounded flow + wakeup graph reachability when handoffs cross already-running event-loop threads.
The plugin outputs 3 sections:
critical_path_ms (pct_of_total, running_ms, runnable_ms, waking_ms, unattributed_sleep_ms, unknown_ms, spans).<uninstrumented> when executing without trace slices) along the critical path.rel_start_ms, ts, dur_ms, process_name, thread_name, tid, state, cpu, slice_name, handoff_from), coalescing adjacent spans with identical thread/state/slice by default.--start-slice-id <int> / --end-slice-id <int>: Slice ID on a thread_track.--start-slice <glob> / --end-slice <glob>: Glob pattern matching a thread_track slice name.--start-process <glob> / --end-process <glob>: Optional process name glob filter for slice or tid resolution.--start-thread <glob> / --end-thread <glob>: Optional thread name glob filter for slice or tid resolution.--start-index <int> / --end-index <int>: 0-based (or negative) match index when multiple slices match --*-slice.--start-edge <start|end> / --end-edge <start|end>: Slice boundary to anchor on (start default for start point; end default for end point).--start-ts <int> / --end-ts <int>: Explicit timestamp in nanoseconds (requires --*-tid or --*-utid).--start-tid <int> / --end-tid <int>: OS thread ID (tid) for --*-ts.--start-utid <int> / --end-utid <int>: Perfetto unique thread ID (utid) for --*-ts.--limit <int>: Maximum summary/slice rows to return (default: 20).--timeline-limit <int>: Maximum timeline spans to return (default: max(limit * 5, 100)).--no-coalesce: Disable coalescing of adjacent timeline spans with identical thread, state, and slice.Analyze the cross-thread critical path between Start proc and launch completion:
fx perf-analyze --format markdown analyze \ --trace out/e2e-testout/trace.fxt \ --plugin critical-path \ --start-slice 'Start proc:*calculator*' \ --end-slice 'launchingActivity#*:completed*:*calculator*' \ --limit 15
Analyze the critical path between two explicit timestamps and thread IDs:
fx perf-analyze --format markdown analyze \ --trace out/e2e-testout/trace.fxt \ --plugin critical-path \ --start-ts 54120000000 --start-tid 1842 \ --end-ts 54680000000 --end-tid 2109
binder (Starnix Binder Analysis)Analyzes Starnix binder delays, missed wakeups (scheduling delays), late-spawned wakers (SpawnLooper instant events), thread pool exhaustion (no_available_threads instant events), and process queue depth backlog (process_queue_depth counters).
[!TIP] Trace Category Recommendation: Including the
starnix:bindertrace category significantly improves the fidelity and depth of binder analysis. Withstarnix:binder, the plugin reconstructs full round-trip IPC transaction flows, driver queue wait times, thread pool exhaustion signals, and backlog counters. Ifstarnix:binderis not found in the trace, the plugin issues an explicit notification and falls back to best-effort heuristics usingbinder_ioctlslices andbinder:%thread naming patterns.
The plugin executes 5 diagnostic queries:
Runnable state duration) for binder threads exceeding the threshold.SpawnLooper thread-scoped instant events (dur = 0) and their enclosing syscall/ioctl parent slice context when the binder thread pool exhausts available threads.no_available_threads process-scoped instant events recorded when an incoming command cannot find an idle thread and queues to the process queue.process_queue_depth counter track peaks, averages, and sample counts per process.starnix:binderWhen the trace does not contain slices under the starnix:binder category:
starnix:binder.binder_ioctl slices and binder:% / binder_% thread names.--threshold-ms <float>: Threshold for scheduling delay and queue latency in milliseconds (default: 10.0). Does not filter instant events or queue depth counters.--complete-only: Only return complete transactions (default: False, includes incomplete transactions).fx perf-analyze --format markdown analyze \ --trace "https://ui.perfetto.dev/#!/?s=b3615b084da54e9a0742dd8f6280de355df4f513" \ --plugin binder \ --threshold-ms 15.0
cpu (CPU Utilization & Idle Power Diagnostics)Provides a multi-dimensional diagnostic view of system activity during an idle trace, identifying timer thrashing, excessive wakeups, per-core utilization and frequency scaling, async executor overhead, binder IPC chatter, and suspend/wake lease blockers.
The plugin executes 6 diagnostic queries in prioritized order:
Rate:CPU:N counters.--limit <int>: Maximum number of rows returned for ranked queries (default: 15).fx perf-analyze --format markdown analyze \ --trace src/performance/perf-analyze/test-data/sample_fxt.fxt \ --plugin cpu \ --limit 10
Unit tests are written using standard Python unittest. To build and run all unit tests, execute:
fx test perf_analyze_test app_launch_test critical_path_test binder_test result_formatter_test cpu_test plugins_test