tree: 67f65655701ce214eb9698de7e941844a086c37b [search]
  1. test-data/
  2. app_launch.py
  3. app_launch_test.py
  4. binder.py
  5. binder_test.py
  6. BUILD.gn
  7. cpu.py
  8. cpu_test.py
  9. perf_analyze.py
  10. perf_analyze_test.py
  11. plugins.py
  12. plugins_test.py
  13. query.py
  14. README.md
  15. result_formatter.py
  16. result_formatter_test.py
src/performance/perf-analyze/README.md

Fuchsia Standalone Performance Analysis Tool (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.


Subcommands

  1. query: Executes SQL queries to inspect and extract structured data from traces. Backed by Perfetto's Trace Processor.
  2. analyze: Executes specialized analysis plugins to identify performance anomalies (e.g., binder delays, jank, CPU starvation).
  3. visualize (Planned): Generates HTML reports, flamegraphs, SVGs, or deep-link trampoline URLs for the Perfetto UI.

Global Options

  • --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.

The query Subcommand

The query subcommand executes SQL queries against a trace file using Perfetto's Trace Processor.

Arguments

  • --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.

Examples

1. Print query help

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

2. Run a single SQL query (default TSV format)

fx perf-analyze query \
  --trace src/performance/perf-analyze/test-data/sample_fxt.fxt \
  --sql "select count(*) as cnt from slice"

Output:

cnt
520

3. Run a single SQL query (Markdown format)

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 |

4. Run a batch of queries from a file (JSON format)

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
      }
    ]
  }
]

The analyze Subcommand

The analyze subcommand runs specialized analysis plugins against a trace file to identify specific performance anomalies.

Arguments

  • --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.

Available 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 5 diagnostic sections in order:

  1. Critical Path Phase Breakdown (Sequential Segments): Reports 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.
  2. CUJ Sub-Stage Breakdown: Quantifies the CUJ sub-stage slices (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).
  3. Intra-Segment Sub-Operation Breakdown: Quantifies 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.
  4. Critical Path Thread State Breakdown: Breaks down each critical-path segment into running_cpu_ms (Running), runnable_starved_ms (R/R+/W), and sleeping_blocked_ms (S/D).
  5. Cross-Layer Contention & Fuchsia Subsystem Attribution: Quantifies main thread Binder calls (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.

Plugin-Specific Arguments

  • --package <str>: Optional substring filter for package or process name.
  • --limit <int>: Maximum number of rows returned per section (default: 20).
  • --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).

Example

fx perf-analyze --format markdown analyze \
  --trace out/e2e-testout/trace.fxt \
  --plugin app-launch \
  --package settings

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:binder trace category significantly improves the fidelity and depth of binder analysis. With starnix:binder, the plugin reconstructs full round-trip IPC transaction flows, driver queue wait times, thread pool exhaustion signals, and backlog counters. If starnix:binder is not found in the trace, the plugin issues an explicit notification and falls back to best-effort heuristics using binder_ioctl slices and binder:% thread naming patterns.

The plugin executes 5 diagnostic queries:

  1. Missed Wakeups (Wakeup Latencies): Identifies scheduling delays (Runnable state duration) for binder threads exceeding the threshold.
  2. Binder Delays (Transaction Queue Latencies): Identifies completed and incomplete transaction queue latencies exceeding the threshold.
  3. Spawn Looper Events (Late-Spawned Wakers): Identifies SpawnLooper thread-scoped instant events (dur = 0) and their enclosing syscall/ioctl parent slice context when the binder thread pool exhausts available threads.
  4. No Available Threads Events (Thread Pool Exhaustion): Identifies no_available_threads process-scoped instant events recorded when an incoming command cannot find an idle thread and queues to the process queue.
  5. Binder Process Queue Depth (Counter Summary): Summarizes process_queue_depth counter track peaks, averages, and sample counts per process.

Fallback Behavior Without starnix:binder

When the trace does not contain slices under the starnix:binder category:

  • An initial Trace Category Status section is reported recommending the inclusion of starnix:binder.
  • Missed Wakeups and Binder Delays fall back to matching binder_ioctl slices and binder:% / binder_% thread names.
  • Spawn Looper Events, No Available Threads, and Process Queue Depth return empty result sets with a note indicating the category was not found and that enabling it will strengthen the analysis.

Plugin-Specific Arguments

  • --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).

Example

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:

  1. Restless Sleepers (Wakeup Counts): Identifies threads with high context switch / wakeup counts preventing deep sleep (VddMin).
  2. Per-Core Utilization & Processing Rate: Analyzes core duty cycles (% active non-idle time) and CPU frequency scaling via Fuchsia kernel Rate:CPU:N counters.
  3. Top CPU Consumers (Usual Suspects): Ranks threads by total accumulated CPU runtime.
  4. Fuchsia Async Executor Overhead: Measures runtime and slice counts for async executors managing futures.
  5. Binder IPC Breakdown: Quantifies Starnix/Android IPC transaction traffic and latencies broken down by thread and process.
  6. Suspend and Wake Lease Tracking: Verifies SAG suspend attempts and attributes active wake leases to components.

Plugin-Specific Arguments

  • --limit <int>: Maximum number of rows returned for ranked queries (default: 15).

Example

fx perf-analyze --format markdown analyze \
  --trace src/performance/perf-analyze/test-data/sample_fxt.fxt \
  --plugin cpu \
  --limit 10

Running Tests

Unit tests are written using standard Python unittest. To build and run all unit tests, execute:

fx test perf_analyze_test app_launch_test binder_test result_formatter_test cpu_test plugins_test