mirror of
https://github.com/android/skills.git
synced 2026-09-14 20:07:09 +08:00
Updates skills (2026-08-17 17:17)
This commit is contained in:
@@ -28,8 +28,7 @@
|
||||
"./play/engage-sdk-integration",
|
||||
"./play/play-billing-library-version-upgrade",
|
||||
"./play/play-policy-insights",
|
||||
"./profilers/perfetto-sql",
|
||||
"./profilers/perfetto-trace-analysis",
|
||||
"./profilers/android-profiler",
|
||||
"./security/android-intent-security",
|
||||
"./system/edge-to-edge",
|
||||
"./testing/testing-setup",
|
||||
|
||||
@@ -87,12 +87,7 @@
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"path": "./profilers/perfetto-sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"path": "./profilers/perfetto-trace-analysis"
|
||||
"path": "./profilers/android-profiler"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: android-profiler
|
||||
description: >
|
||||
Manages Android performance profiling and debugging. Triggers when the user
|
||||
asks to record or analyze Android performance data, such as system traces,
|
||||
heap dumps, method recordings, callstack samples, memory allocations, or
|
||||
investigate bottlenecks, jank, memory leaks, and app startup issues on
|
||||
Android, or when the user asks to write, debug, or execute ad-hoc SQL
|
||||
queries. Applies to both user and system apps or services.
|
||||
license: Complete terms in LICENSE.txt
|
||||
metadata:
|
||||
author: Google LLC
|
||||
last-updated: '2026-08-06'
|
||||
keywords:
|
||||
- Android performance
|
||||
- debugging
|
||||
- profiling
|
||||
- recording
|
||||
- trace analysis
|
||||
- memory leaks
|
||||
- bottleneck
|
||||
- jank
|
||||
- SQL
|
||||
---
|
||||
|
||||
# Android Profiler Orchestrator
|
||||
|
||||
Your primary role is **Intent Disambiguation and Routing**; route the user to
|
||||
the correct workflow or prepare an execution plan for the user. Work with the
|
||||
user to finalize the plan and then proceed with the plan execution, addressing
|
||||
singular as well as composite needs.
|
||||
|
||||
## Prerequisites and Setup
|
||||
|
||||
Before executing any workflows, read
|
||||
[`references/env_setup.md`](references/env_setup.md) (it sits next to this file
|
||||
in the skill root). It defines what to set `$SKILL_ROOT` to - the anchor every
|
||||
other path in this skill is written against.
|
||||
|
||||
## Intent Disambiguation
|
||||
|
||||
Do not guess the user's intent. If the user request is not clear, **ask the
|
||||
user** what they want to do before proceeding.
|
||||
|
||||
## Recording
|
||||
|
||||
Route all recording requests through
|
||||
`$SKILL_ROOT/recording/recording_orchestrator.md`. This defines guidelines and
|
||||
pre-flight checks or dependency checks that apply to all recording workflows,
|
||||
and ensures you have the necessary setup to proceed. Read the orchestrator and
|
||||
execute the plan it describes based on what the user wants to record (for
|
||||
example, a system trace or a heap dump).
|
||||
|
||||
## Analysis
|
||||
|
||||
Route all analysis requests through
|
||||
`$SKILL_ROOT/analysis/analysis_orchestrator.md`.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Analysis Orchestrator
|
||||
|
||||
Use the guidelines below to find the right analysis workflow to execute based
|
||||
on the user intent (for example, analyzing a system trace or a heap dump).
|
||||
|
||||
Ensure you can answer the following: "User intends to
|
||||
[analyze/query/investigate] ______". For example,
|
||||
"User intends to analyze a heap dump to investigate a memory leak".
|
||||
|
||||
## Handling Composite Requests
|
||||
|
||||
If the user's request involves multiple distinct analysis goals (for example,
|
||||
analyzing a trace for jank AND checking for memory leaks or GPU issues),
|
||||
do not execute them simultaneously.
|
||||
|
||||
1. Break down the request and propose a sequential execution plan to the user.
|
||||
2. Ask the user for confirmation to start the first step.
|
||||
3. Do not proceed to the next workflow until the current one is completed.
|
||||
|
||||
## Workflow Discovery and Routing
|
||||
|
||||
This skill supports multiple specialized analysis workflows. To determine the
|
||||
right workflows to use:
|
||||
|
||||
1. Use your file search tools (for example, `grep_search`) to recursively scan
|
||||
the `$SKILL_ROOT/analysis/workflows/` directory for workflow entrypoints
|
||||
(markdown files defining a top-level `name:` key in their frontmatter,
|
||||
ignoring internal `references/` subdirectories).
|
||||
2. Compare the user's request and intent against the `name:`, `description:`,
|
||||
and `keywords:` fields to identify matching workflows. If multiple are
|
||||
found, present them as options to the user and proceed with the
|
||||
user's selection.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: perfetto-sql
|
||||
description: >
|
||||
Translates natural language data intents into syntactically valid PerfettoSQL
|
||||
queries and/or executes them against a trace file, if provided. Use this
|
||||
workflow to draft, debug, or execute queries extracting slice, thread, or
|
||||
memory data from Android Perfetto traces using trace_processor.
|
||||
keywords:
|
||||
- Perfetto SQL
|
||||
- SQL Guidelines
|
||||
- SQL Best Practices
|
||||
- Ad-hoc Query
|
||||
- Trace Processor
|
||||
- SPAN_JOIN
|
||||
- Idempotency
|
||||
---
|
||||
|
||||
# Ad-Hoc PerfettoSQL Querying
|
||||
|
||||
Use this workflow to write, debug, or execute PerfettoSQL queries:
|
||||
|
||||
- **Executing against a trace:** If a trace file path or URL is provided, verify
|
||||
environment prerequisites in `$SKILL_ROOT/references/perfetto/setup.md` and
|
||||
follow the session execution workflow in
|
||||
`$SKILL_ROOT/references/perfetto/sql.md`.
|
||||
- **Static query authoring or debugging:** If no trace is attached, draft,
|
||||
optimize, or debug the queries using the syntax, schema guidelines, and
|
||||
standard library best practices in `$SKILL_ROOT/references/perfetto/sql.md`.
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
---
|
||||
name: perfetto-trace-analysis
|
||||
description: >
|
||||
Analyzes Perfetto traces to find the root cause of performance issues in
|
||||
user or system Android apps (for example, janks, app startup, memory, or
|
||||
latency stalls).
|
||||
keywords:
|
||||
- perfetto
|
||||
- trace
|
||||
- jank
|
||||
- startup
|
||||
- latency
|
||||
- stall
|
||||
- thread
|
||||
- bottleneck
|
||||
---
|
||||
|
||||
# Perfetto Trace Analysis
|
||||
|
||||
Use this workflow to diagnose general Android performance issues: jank, app
|
||||
startup, latency, stall, or a thread-blocking problem.
|
||||
|
||||
Follow these steps in order:
|
||||
|
||||
## Step 1: Identify what to investigate first
|
||||
|
||||
Work with the user (for example, ask questions, present options) to
|
||||
understand the following:
|
||||
|
||||
- _What is the symptom_: Do they want to investigate frame drops, jank,
|
||||
startup issues, app crash, system crash, ANR?
|
||||
- _Who is the victim:_ Where did they observe the symptom? For example, "a
|
||||
frame drop in `com.example.sample`".
|
||||
|
||||
> **Input Trace Required:** Confirm that the user has provided a trace file
|
||||
> path or URL (for example, `.pftrace`, `.perfetto-trace`, or a Perfetto UI
|
||||
> link). If none is provided, pause and ask the user to provide one before
|
||||
> proceeding.
|
||||
|
||||
> If you do not know the symptom, pause and ask the user for clarification.
|
||||
|
||||
> If you know the symptom but not the victim, proceed to Step 2 (Triage) to
|
||||
> discover candidates. Present the 3-5 most severe instances to the user and
|
||||
> proceed with the user selection.
|
||||
|
||||
> **A/B Trace Comparison:** If the user provides multiple traces (for
|
||||
> example, a baseline and an issue trace), explicitly clarify which is the
|
||||
> baseline. You will pass both paths to the subagents in Step 3 so they can
|
||||
> use the baseline trace to establish expected behavior.
|
||||
|
||||
## Step 2: Run a system-wide triage
|
||||
|
||||
Spin off a subagent/task and instruct it to follow instructions from
|
||||
`$SKILL_ROOT/analysis/workflows/perfetto-trace-analysis/references/triage.md`
|
||||
to run a system-wide triage. (Do not read this file yourself. Pass this path
|
||||
in the sub-agent prompt so it loads the file and performs the triage steps).
|
||||
|
||||
Note the output produced by the subagent to identify the filtered list of
|
||||
**candidates** that we need to investigate.
|
||||
|
||||
> **Note:**
|
||||
> - If no candidates are found, ask the user for symptom clarification and
|
||||
> specific timestamps.
|
||||
> - If the triage reveals multiple candidates, select the top 2 or 3 most
|
||||
> severe, representative candidates and ask the user before proceeding
|
||||
> which ones to investigate. The user may choose one or more.
|
||||
|
||||
**Expected outcome:** Confirmation on which candidate(s) to investigate
|
||||
before proceeding.
|
||||
|
||||
## Step 3: Investigate each branch in parallel
|
||||
|
||||
For **every** candidate (one or more) identified in the previous step, spin
|
||||
off a task/subagent to investigate these tracks _in parallel_.
|
||||
|
||||
Construct a prompt for each candidate containing:
|
||||
```markdown
|
||||
Trace path: [path]
|
||||
Baseline Trace Path: [path, if provided]
|
||||
Candidate Info: [`utid`, `upid`, thread_name, process_name, `render_thread_utid` (if applicable)]
|
||||
Symptom Window: [start_ts, end_ts, duration]
|
||||
System Vitals: [payload from Step 2]
|
||||
Budget: [expected duration, if applicable]
|
||||
Execution Protocol: Read
|
||||
`$SKILL_ROOT/analysis/workflows/perfetto-trace-analysis/references/per_candidate_analysis.md`
|
||||
and follow its instructions end-to-end.
|
||||
```
|
||||
|
||||
SPAWN the subagent(s) in parallel and await completion of all subagents before
|
||||
proceeding to Step 4.
|
||||
|
||||
## Step 4: Final report and consolidation
|
||||
|
||||
Read the output from every spawned subagent and generate a consolidated "Trace
|
||||
Analysis Report" adhering to these instructions:
|
||||
|
||||
1. **Summary and Root Cause:** State the findings in simple, clear language
|
||||
and classify root causes (hardware, software/code, scheduling policy,
|
||||
system exhaustion, or external dependency).
|
||||
2. **Dependency Chain:** Map the full path from symptom to root cause.
|
||||
Include thread names, `UTID`s, blocking states, and exact timestamps at
|
||||
every hop.
|
||||
3. **Evidence Tags:** Tag every single claim with `[SQL]` (backed by data),
|
||||
`[INFERRED]` (logical deduction), or `[GAP]` (partial or missing data),
|
||||
and briefly define these tags at the start.
|
||||
4. **Platform Context:** Explain Android system behaviors _only_ by citing
|
||||
specific retrieved slice names, values, and timestamps.
|
||||
**What's bad:** "SurfaceFlinger does X around vsync"
|
||||
**What's a good explanation:** "SurfaceFlinger's composition slice at
|
||||
`ts=142.3ms` ran `4ms` after the vsync signal at `ts=138.1ms`, consistent
|
||||
with X".
|
||||
5. **Partial Suspects:** List all branches that were investigated but did not
|
||||
reach a terminal root cause, ranked by evidence strength. Include what
|
||||
was found and where verification fell short. A partial suspect can be the
|
||||
real contributor - report it even if a terminal root cause was found
|
||||
elsewhere.
|
||||
|
||||
> If evidence is evenly split between multiple potential causes, report each
|
||||
> suspicion with its supporting data and evidence so that engineers can
|
||||
> evaluate probabilities without false certainty. Do not arbitrarily pick
|
||||
> a winner.
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# Guiding Principles for Trace Analysis
|
||||
|
||||
Whether you are augmented with domain-specific knowledge or doing a standard
|
||||
workflow analysis, follow these rules.
|
||||
|
||||
**Why:** Performance analysis is complex. It is entirely possible to end up
|
||||
identifying a root cause while the reality is different (for example, a hardware
|
||||
bottleneck causing a cascade of failures). The principles below keep your
|
||||
analysis grounded in truth and ensure that you investigate the entire causal
|
||||
chain to discover the true bottleneck.
|
||||
|
||||
1. **Schema Validation via Intrinsic Discovery:**
|
||||
Read `$SKILL_ROOT/references/perfetto/sql.md` and query table schemas
|
||||
using `LIMIT 0` before drafting queries.
|
||||
- **Why:** Trace processor schemas evolve across versions; discovering
|
||||
schema directly prevents invalid assumptions and syntax failures.
|
||||
2. **Empirical Data Grounding:** Support every claim with tool output or
|
||||
queried timestamps, slice durations or counter values (`[SQL]`).
|
||||
- **Why:** General Android heuristics cannot substitute for ground-truth
|
||||
trace metrics. When queries return empty results, broaden search
|
||||
constraints using fuzzy matching or wider time windows.
|
||||
3. **Causation vs. Correlation:** Verify that the blocker's active execution
|
||||
overlaps with the victim's wait interval.
|
||||
- **Why:** Concurrent anomalies are only causally linked if their
|
||||
execution lifetimes intersect. For example, just because thread A was busy
|
||||
while thread B was waiting does not necessarily mean thread A caused the
|
||||
wait.
|
||||
4. **Follow Evidence:** Follow dependency chains across thread, process and
|
||||
kernel boundaries to the terminal root cause. Keep going as long as
|
||||
**each hop meaningfully explains the symptom**.
|
||||
- **Why:** Halting blocker traversal prematurely reports intermediate
|
||||
symptoms rather than the true origin.
|
||||
5. **Explicit Uncertainty Reporting:** Categorize unverified execution paths
|
||||
as `[GAP]` or partial suspects.
|
||||
- **Why:** Transparent reporting of missing data enables engineers to evaluate
|
||||
probabilities without false certainty.
|
||||
6. **Evidence-First Explanation:** Cite concrete retrieved metrics and slice
|
||||
timestamps before asserting platform behavioral context.
|
||||
- **Why:** Contextual explanations are only reliable when anchored in
|
||||
empirical trace observations.
|
||||
7. **Systemic Confound Sweeps:** Before attributing a bottleneck to
|
||||
application software, verify that thermal throttling, CPU capping
|
||||
(`cpufreq`), scheduling (`sched_slice`), or LMKD pressure isn't uniformly
|
||||
degrading the system. Report such confounds as root cause modifiers.
|
||||
> To uncover short-lived anomalies that get mathematically missed by simple
|
||||
> averages or aggregate queries, isolate and look around the symptom window
|
||||
> and query for individual event spikes or percentiles.
|
||||
8. **Systematic Step Adherence:** At every step of your investigation, strictly
|
||||
follow the defined investigation steps.
|
||||
- **Why:** Structured verification prevents analytical blind spots and
|
||||
premature conclusions on obvious but non-critical anomalies.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
domain: The specific domain or product.
|
||||
description: Description of the domain/subsystem for effective routing
|
||||
keywords:
|
||||
- List of symptoms and keywords where these hints are applicable,
|
||||
for example, 'jank', 'surfaceflinger', 'vulkan'.
|
||||
---
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
---
|
||||
subsystem: cpu
|
||||
description: >
|
||||
Expert hints for CPU scheduling, thread states, CPU contention, and
|
||||
frequency scaling.
|
||||
keywords:
|
||||
- cpu
|
||||
- scheduling
|
||||
- latency
|
||||
- runnable
|
||||
- running
|
||||
- thread state
|
||||
- governor
|
||||
- core
|
||||
- frequency
|
||||
---
|
||||
|
||||
# CPU & Scheduling Expert Hints
|
||||
|
||||
- When debugging a long slice: Examine its thread states to understand what the
|
||||
thread was doing (running, sleeping, blocked on I/O).
|
||||
- When debugging a long slice for latency issues: Check if its duration is
|
||||
caused by one or more long-running child slices. Apply recursively.
|
||||
- When a thread is woken up but there is a delay before it runs, check the
|
||||
"IRQ" track for the corresponding CPU to see if an interrupt is the cause.
|
||||
- Check if kernel threads associated with hardware are running with real-time
|
||||
priority; if not, they can be preempted.
|
||||
- When analysis of the primary application package does not reveal root cause,
|
||||
expand to all threads and processes. Search for other runnable threads on the
|
||||
same CPU or high-priority kernel threads.
|
||||
- When investigating app startup, use an SQL query to aggregate reasons for
|
||||
uninterruptible sleep on the main thread.
|
||||
- Check the "cpu_frequency" counter for CPU cores that ran the main process.
|
||||
Missing frequency data or stuck frequencies indicate a kernel-level bug in
|
||||
the governor.
|
||||
- Query raw "ftrace" events for logs related to the governor thread (for
|
||||
example, "su_gov").
|
||||
- To find concurrency issues, search for critical threads (for example,
|
||||
'RenderThread') in a blocked state (`thread_state.state = 'D'`) and join with
|
||||
scheduling data to find the waker.
|
||||
- Compare time spent in userspace functions vs kernel (slices with [k] prefix).
|
||||
- To detect a 'catch-up storm', look for threads with long gaps in
|
||||
thread_state/cpu_slice activity immediately followed by a high-density burst.
|
||||
- Quantify scheduler contention by calculating scheduling latency (measure
|
||||
duration of preceding 'Runnable' state using
|
||||
`preceding_sched_slice_for_thread`). Search for maximums and high percentiles
|
||||
(p95/p99).
|
||||
- If a task exhibits high scheduling latency, check if other CPUs were idle
|
||||
(running swapper or idle thread).
|
||||
- Check the `cpu_id` for key threads; if consistently scheduled on slower
|
||||
cores, it signals a potential performance gain by allowing them on big cores.
|
||||
- For a struggling thread, analyze 'Runnable' vs 'Running' state time. A large
|
||||
'Runnable' time indicates CPU contention.
|
||||
- If a slice's wall duration increases but the percentage of 'Running' time is
|
||||
unchanged, it strongly suggests a lower CPU frequency. Check `sched_switch`
|
||||
to focus on the correct cores.
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
---
|
||||
subsystem: graphics
|
||||
description: >
|
||||
Expert hints for graphics performance, UI jank, GPU memory, and frame
|
||||
rendering.
|
||||
keywords:
|
||||
- graphics
|
||||
- ui jank
|
||||
- gpu
|
||||
- frame
|
||||
- vsync
|
||||
- render
|
||||
- surfaceflinger
|
||||
- texture
|
||||
- bitmap
|
||||
---
|
||||
|
||||
# Graphics & UI Jank Expert Hints
|
||||
|
||||
- When investigating UI jank, check for long-running slices on the main
|
||||
thread; if a slice like "ConstraintLayout.onMeasure" is taking a long time
|
||||
(for example, >8ms), it is a likely cause of the jank.
|
||||
- When a `bitmap_write_to_parcel` slice takes milliseconds instead of
|
||||
microseconds, investigate its children slices to see how time is spent. Long
|
||||
durations often point to suboptimal kernel operations like memory mapping
|
||||
(`mmap`) or unnecessary data zeroing.
|
||||
- For a high-level overview of graphics memory usage, track `gpu_mem_total`
|
||||
counter for a specific process (`upid`).
|
||||
- To find the largest graphics allocations, query the `android_graphics_allocs`
|
||||
table and sort by `size_bytes` in descending order. Compare width and height
|
||||
against the device's display resolution.
|
||||
- To detect the "double memory cost" of an image existing on both CPU and GPU,
|
||||
look for a large buffer in `android_graphics_allocs` and a simultaneous CPU
|
||||
memory allocation of a similar size (`rss_anon_bytes` or `heap_graph`).
|
||||
- To find potentially costly intermediate render targets, look for large
|
||||
buffers in `android_graphics_allocs` where `usage_bits` lack a
|
||||
`COMPOSER_OVERLAY` flag.
|
||||
- To check if an allocation is actually presented on screen, correlate
|
||||
`buffer_id` with SurfaceFlinger events.
|
||||
- To see if high graphics memory is causing performance issues, check the frame
|
||||
duration in `actual_frame_timeline_slice`. Durations over vsync (for
|
||||
example, 16.6ms) correlating with `gpu_mem_total` spikes suggest memory
|
||||
pressure jank.
|
||||
- Within the main thread of a janky graphics application, look for frequent or
|
||||
long-running `texture_upload` slices.
|
||||
- Analyze the duration of `eglSwapBuffersWithDamageKHR` or similar buffer-swap
|
||||
slices in the graphics rendering thread. Consistently long durations suggest
|
||||
a large "damage area" being redrawn every frame.
|
||||
- To identify UI jank, compare `actual_frame_timeline` against the
|
||||
`expected_frame_timeline` on the main process; a significant deviation
|
||||
indicates missed frames.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
---
|
||||
subsystem: io
|
||||
description: >
|
||||
Expert hints for I/O and storage bottlenecks, uninterruptible sleep, and
|
||||
page cache.
|
||||
keywords:
|
||||
- i/o
|
||||
- storage
|
||||
- disk
|
||||
- uninterruptible sleep
|
||||
- d state
|
||||
- page fault
|
||||
- dm-verity
|
||||
- file
|
||||
- syscall
|
||||
---
|
||||
|
||||
# I/O & Storage Expert Hints
|
||||
|
||||
- When debugging a long, uninterruptible sleep state related to I/O: Check for
|
||||
overlapping slices with "verity" in their name (dm-verity).
|
||||
- When a thread is stuck in an uninterruptible sleep with no `blocked_function`,
|
||||
look for other threads that might be holding the memory lock (for example,
|
||||
"jit-thread-pool", memory mapping ops).
|
||||
- When analyzing a long uninterruptible sleep, check the `blocked_function` in
|
||||
the thread state details (from `sched_blocked_reason` ftrace event).
|
||||
- A lot of time spent in `do_page_fault` during app startup is a strong
|
||||
indicator of I/O contention.
|
||||
- For file integrity mechanisms like DM-Verity, search for events like
|
||||
`dm_verity_fec_prefetch`.
|
||||
- To find the specific kernel dependency of a stalled app thread, locate the
|
||||
thread in state 'D', then look for kworker or kernel threads that become
|
||||
runnable immediately after.
|
||||
- For app stalls caused by I/O, analyze the scheduling latency of the relevant
|
||||
kworker threads handling the request.
|
||||
- To find inefficient file I/O, query the syscall table for a high frequency
|
||||
of small, sequential `read()` or `pread()` syscalls on a single fd.
|
||||
- If a thread spends significant time in 'Uninterruptible Sleep', check if
|
||||
`blocked_function` is `page_cache_readahead`. Correlate waking timestamps
|
||||
with `filemap_add_to_page_cache` ftrace events.
|
||||
- Aggregate counts of `filemap_add_to_page_cache` grouping by `inode` to find
|
||||
the specific file causing I/O pressure.
|
||||
- Inspect `nr_sector` in `block_rq_issue` ftrace events to understand file
|
||||
read-ahead size.
|
||||
- If an I/O issue disappears on subsequent launches, it is a 'cold start'
|
||||
problem (populating page cache).
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
---
|
||||
subsystem: ipc
|
||||
description: >
|
||||
Expert hints for IPC, binder transactions, and cross-process dependencies.
|
||||
keywords:
|
||||
- ipc
|
||||
- binder
|
||||
- transaction
|
||||
- cross-process
|
||||
- bottleneck
|
||||
- service
|
||||
- system_server
|
||||
---
|
||||
|
||||
# IPC & Binder Expert Hints
|
||||
|
||||
- Look for multiple outbound binder transactions from the same process
|
||||
(`system_server`) that carry similar data to different destinations in a
|
||||
short time frame. This "binder storm" indicates a lack of multiplexing.
|
||||
- To trace data across processes, correlate slices using flow events by
|
||||
linking a slice's ID to `flow.source_slice_id` or `flow.dest_slice_id`.
|
||||
- To detect binder spam, query the `binder_transaction` table and group by
|
||||
thread ID (`tid`), `service_name`, and `method_name` to find high numbers of
|
||||
identical calls.
|
||||
- When high binder concurrency is found, identify the bottleneck server
|
||||
process by grouping transactions by `server_upid`.
|
||||
- To analyze latency of a slow binder transaction, calculate time spent
|
||||
outside the server by subtracting `server_dur` from total `dur` in the
|
||||
`binder_transaction` table.
|
||||
- When a thread is suspected of binder spam, correlate its `tid` with the
|
||||
`cpu_slice` table to check for high CPU consumption.
|
||||
- To find code responsible for binder spam, get the `utid` of the problematic
|
||||
thread and use it to query `stack_profile_callsite`.
|
||||
- To find callers of a problematic function, filter `stack_profile_callsite`
|
||||
for frames mapping to it, then trace upwards using `parent_id`.
|
||||
- A long-running slice on one thread causally linked to a slice on another
|
||||
thread (for example, binder from `system_server` to SystemUI) indicates a
|
||||
scheduling dependency bottleneck.
|
||||
- To find asynchronous operations that might cause UI jank, look for a binder
|
||||
transaction from a controlling process that returns quickly, followed by a
|
||||
long-running slice in the receiving process.
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
---
|
||||
subsystem: memory
|
||||
description: >
|
||||
Expert hints for memory pressure, Low Memory Killer (LMK), and allocations.
|
||||
keywords:
|
||||
- memory
|
||||
- lmk
|
||||
- lmkd
|
||||
- oom
|
||||
- rss
|
||||
- swap
|
||||
- kswapd
|
||||
- thrashing
|
||||
- pressure
|
||||
- allocation
|
||||
---
|
||||
|
||||
# Memory & LMK Expert Hints
|
||||
|
||||
- To investigate low memory kills, look for the `lmk_kill_occurred` ftrace
|
||||
event; a high count indicates severe memory pressure.
|
||||
- When you see a burst of `lmk_kill_occurred` events, query for processes with
|
||||
high CPU `wall_duration` in the `sched_slice` table during the same time
|
||||
window. Runaway processes consuming CPU exacerbate memory pressure.
|
||||
- To understand a process's memory impact, inspect its `anon_rss` (Anonymous
|
||||
Resident Set Size) from memory counters like `mem.info`.
|
||||
- Check system-wide memory stats for a high or rapidly increasing `swap_used`
|
||||
value.
|
||||
- To confirm memory thrashing, look for high CPU usage by the `kswapd` kernel
|
||||
thread.
|
||||
- To find the direct trigger for LMKD, look for `memory_pressure` trace events
|
||||
from Pressure Stall Information (PSI).
|
||||
- To detect 'lost' memory for processes using hardware accelerators (like a
|
||||
TPU), query the `counter` table for `RssFile` values. If it drops
|
||||
significantly while hardware is active, check for `kswapd0` scheduling
|
||||
slices.
|
||||
- When analyzing memory shared with hardware, query `dma_heap_stat` and
|
||||
`dmabuf_total_size` counters (or `ion_total_size` for older devices) for a
|
||||
more accurate picture than RSS.
|
||||
- Be aware that `RssFile` can over-report memory if the same physical page is
|
||||
mapped multiple times.
|
||||
- When investigating OOMs, establish a baseline by querying process memory
|
||||
counters (for example, `mem.rss`) and compare the median and 95th percentile
|
||||
against its history.
|
||||
- If bitmaps are a major memory consumer, check for outliers in bitmap count by
|
||||
querying `android.graphics.Bitmap` instances across traces.
|
||||
- To find what is holding onto an object (like a bitmap), trace its retainer
|
||||
path back to a GC root by querying `heap_graph_reference`. Pay close
|
||||
attention to custom application classes.
|
||||
- Query the `android_bitmaps` table to check the width and height properties of
|
||||
bitmaps.
|
||||
- Check for the presence of software bitmaps, which consume memory on both the
|
||||
app heap and in graphics memory.
|
||||
- When analyzing bitmaps, look for duplicates by checking for multiple
|
||||
`android.graphics.Bitmap` objects with identical properties.
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
---
|
||||
subsystem: power
|
||||
description: >
|
||||
Expert hints for battery drain, power rails, network traffic, and wake locks.
|
||||
keywords:
|
||||
- power
|
||||
- battery
|
||||
- wake lock
|
||||
- suspend
|
||||
- network
|
||||
- modem
|
||||
- bluetooth
|
||||
- drain
|
||||
---
|
||||
|
||||
# Power & Network Expert Hints
|
||||
|
||||
- When investigating overall battery drain, start by querying `power_rails`
|
||||
track and summing energy consumed (`power_ma * duration`) for each rail.
|
||||
- To check if a device is sleeping correctly during screen-off periods, query
|
||||
the `suspend_state` track; a lack of time in "suspended" state indicates a
|
||||
wakefulness problem.
|
||||
- To find the root cause of the device failing to suspend, query the
|
||||
`kernel_wakelock` track and aggregate total duration for each wake lock.
|
||||
- If a top kernel wake lock name contains "bt_" or "bcm" (for example,
|
||||
`bt_host_wake`), cross-reference its timing with events in the
|
||||
`bluetooth_scan_results` track.
|
||||
- If the modem rail in `power_rails` shows high consumption, query the
|
||||
`network_packets` table and aggregate traffic volume by UID to identify app.
|
||||
- When analyzing network traffic from a shared UID, use the `socket_tag`
|
||||
associated with network packets for granular attribution.
|
||||
- Convert impact into a common energy unit like milliwatt-hours (mWh) to
|
||||
compare the severity of different issues.
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
# Candidate Investigation Protocol
|
||||
|
||||
This is the execution protocol for a deep-dive subagent during trace analysis.
|
||||
|
||||
## Context
|
||||
|
||||
Execute the protocol below using the candidate details (Trace Paths, Symptom
|
||||
Window, UTID, UPID, System Vitals) provided in your initial prompt.
|
||||
|
||||
## Investigation Protocol
|
||||
|
||||
**High-Level Algorithm:**
|
||||
|
||||
1. Prerequisites (Step 1)
|
||||
2. Calculate time distribution and identify state buckets (Step 2)
|
||||
3. Domain and System hints discovery and selection (Step 3)
|
||||
4. Run exhaustive investigation for each significant state bucket sequentially
|
||||
(Steps 4 and 5), starting with the largest bucket. Output total time
|
||||
explained so far after investigating each.
|
||||
5. Output the result (Step 6)
|
||||
|
||||
### Step 1: Prerequisites
|
||||
|
||||
- Follow `$SKILL_ROOT/references/perfetto/sql.md` for session-based query execution.
|
||||
- Read
|
||||
`$SKILL_ROOT/analysis/workflows/perfetto-trace-analysis/references/guiding_principles.md`
|
||||
to identify best practices, ensure data-driven analysis, and avoid pitfalls.
|
||||
|
||||
### Step 2: Calculate the time distribution
|
||||
|
||||
1. **Establish a baseline if possible:** For example, if investigating a jank
|
||||
candidate, find a _non-janky_ instance of the **same** operation first to
|
||||
establish expected behavior and rule out red herrings. _If no healthy
|
||||
instance exists in the trace, note this as a `[GAP]` and proceed with the
|
||||
next steps_.
|
||||
2. **Calculate time spent in each state:** Sum the time spent in `Running`,
|
||||
`R`, `S` and `D` states. Investigate all substantial buckets. Also look for
|
||||
composite bottlenecks (for example, "40% CPU-starved + 35% IO-blocked").
|
||||
3. **Run checks to rule out red herrings:** For example:
|
||||
- A thread in `S` state could be normal behavior -> investigate only if
|
||||
the sleep actually overlaps with a pending obligation (for example, a
|
||||
pending binder reply).
|
||||
- A thread in `Running/Runnable`: When a thread spends high duration in
|
||||
`Running/Runnable`, inspect CPU frequency, throttling counters, and core
|
||||
migrations before attributing latency to code inefficiency, because
|
||||
hardware throttling inflates wall time without increasing instruction
|
||||
overhead:
|
||||
- Verify first that an identified code path is actually doing
|
||||
disproportionate work.
|
||||
- Query the trace to find what the thread was doing during this window.
|
||||
Query the `slice` table for the longest duration slices during this
|
||||
window.
|
||||
- Beyond blocking slices, query and identify repetitive micro-operations
|
||||
or gaps that collectively exhaust the budget to prevent tunnel vision.
|
||||
- Perform a system-wide check to identify if there was CPU throttling or
|
||||
core migrations around the symptom window (see Principle 7).
|
||||
4. **Drill down:** For every significant bucket revealed from the time
|
||||
distribution analysis during the symptom window, identify what the thread
|
||||
was doing at the transition point. Investigate every significant bucket (for
|
||||
example, >= 20% of the time window) to avoid missing real bottlenecks or
|
||||
composite issues.
|
||||
> **Action:** Tag missing data as `[GAP]`. Do not guess.
|
||||
|
||||
### Step 3: Domain and Hints Discovery
|
||||
|
||||
Even if you find a major bottleneck, continue searching for other bottlenecks
|
||||
using expert-vetted domain and system hints and techniques. Follow these steps
|
||||
in order:
|
||||
|
||||
- **Domain discovery:** Use your file search tools (for example, `grep_search`)
|
||||
to scan frontmatter (`domain:`, `description:`, `keywords:`) in
|
||||
`$SKILL_ROOT/analysis/workflows/perfetto-trace-analysis/references/hints/domains/`
|
||||
(if it exists) and identify matching domain hint files based on collective
|
||||
investigation state (victim, intermediate processes).
|
||||
- **Subsystem hints:** Use your file search tools to scan frontmatter
|
||||
(`subsystem:`, `description:`, `keywords:`) in
|
||||
`$SKILL_ROOT/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/`
|
||||
for relevant tracks (for example, keywords for sleep states, memory, IPC).
|
||||
- **Hint Selection and Application:** Apply all the matched subsystem and
|
||||
domain hint files during the Step 4 investigation, and list them under
|
||||
`Applied Hints` in your Step 6 output.
|
||||
|
||||
### Step 4: Exhaustive Investigation (Do Not Give Up Early)
|
||||
|
||||
- **Follow the dependency chain:**
|
||||
- If the victim thread was waiting or blocked by another thread, follow the
|
||||
chain to the leaf. Find out what it is waiting _for_. Cross process
|
||||
boundaries if necessary. Do not conclude without following the entire
|
||||
blocker chain (See Principle 4 in `guiding_principles.md`).
|
||||
- **Dynamic hint injection:** If your dependency chain leads to a new
|
||||
subsystem or process you haven't researched yet, do a single search in
|
||||
`$SKILL_ROOT/analysis/workflows/perfetto-trace-analysis/references/hints/`
|
||||
for relevant hints before proceeding. Do not fall into infinite loops.
|
||||
- Every state bucket investigation should end in one of three states:
|
||||
**Terminal root cause**, **Blocked by another thread**, or
|
||||
**Partial suspect**.
|
||||
|
||||
> A finding is a "terminal root cause" if:
|
||||
>
|
||||
> - You can trace it down to a physical bottleneck (thermal throttle, GPU,
|
||||
> storage). **Require Specificity:** Do not conclude with generic labels.
|
||||
> Specify the _what_ clearly.
|
||||
> - A specific function or code path is identified as doing disproportionate
|
||||
> work relative to its purpose (for example, synchronous disk IO on main
|
||||
> thread, unnecessary object allocation triggering GC).
|
||||
> - A scheduling policy or resource limit is identified as artificially
|
||||
> constraining the thread (for example, background CPU cap, foreground
|
||||
> service restriction).
|
||||
> - The bottleneck is identified in a different process/service that the
|
||||
> investigated process cannot control (for example, `system_server` lock
|
||||
> contention, `SurfaceFlinger` throttling).
|
||||
|
||||
- **Systemic sweep before concluding:** Discovering an application-layer
|
||||
bottleneck (software root cause) does not terminate the investigation of a
|
||||
state bucket. Before concluding any state bucket as a terminal root cause,
|
||||
check relevant system hints for that state (`CPU`, `IO`, `Memory`, `IPC`)
|
||||
and verify whether platform-level confounds - such as CPU scaling, memory
|
||||
pressure, thermal throttling, or I/O saturation - simultaneously degraded
|
||||
performance **around the symptom window.** Report discovered systemic
|
||||
confounds as **co-root causes** or duration modifiers (see Principle 7 in
|
||||
`guiding_principles.md`).
|
||||
|
||||
**Why:** Software execution duration is not an absolute constant; it is
|
||||
modulated by platform state (frequency scaling, thermals, memory reclamation).
|
||||
Always check system-wide confounds before reporting an inefficient code path as
|
||||
the sole root cause - to avoid concluding inflated software duration as the
|
||||
sole root cause while missing the underlying kernel or hardware anomaly that
|
||||
magnified it. Back findings with empirical proof (`[SQL]`) or mark as `[GAP]`
|
||||
if inconclusive.
|
||||
|
||||
### Step 5: Contextualize the Workload
|
||||
|
||||
Once the mechanical bottleneck is identified in Step 4, query the trace to
|
||||
identify the high-level user feature, UI operation, or exact workload that
|
||||
triggered it. Explain the _why_, not just the _what_. This workload context
|
||||
ensures that we can provide actionable next steps for the user instead of
|
||||
leaving them confused.
|
||||
|
||||
### Step 6: Output
|
||||
|
||||
Output the following investigation result:
|
||||
|
||||
```markdown
|
||||
# Investigation Result
|
||||
|
||||
Candidate:
|
||||
Symptom Window: [Start TS] and [End TS]
|
||||
Symptom Duration: [Duration]
|
||||
Budget (Expected Duration): [If applicable]
|
||||
Applied Hints: [List of matched subsystem and domain hint files used]
|
||||
|
||||
## Primary Finding
|
||||
|
||||
- **Classification:** [Hardware | Software/Code | System Exhaustion | Scheduling Policy | External Dependency]
|
||||
- **Details and Reasoning:** `___`
|
||||
- **Explained Duration:** `___`
|
||||
- **Evidence SQL or backing data:** `___`
|
||||
- **Dependency chain:** [Root to Leaf Dependency Chain]
|
||||
- **Root cause conclusion:** `___`
|
||||
|
||||
## Other Findings
|
||||
|
||||
- **Status:** [Partial Suspect | Low Confidence Suspicion | Terminal Root Cause]
|
||||
- _(same fields)_
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- All claims tagged (`[SQL]`/`[GAP]`/`[INFERRED]`): [yes/no]
|
||||
- Total explained time accounts for most of the symptom window: [yes/no]
|
||||
- Unexplored state buckets: [none/list with reason why]
|
||||
- Workload contextualized (for example, specific slices/layers involved): [yes/no]
|
||||
```
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
# System-Wide Triage
|
||||
|
||||
Follow these steps in order:
|
||||
|
||||
1. **Verify data integrity:** Query `stats` for CPU and data loss indicators.
|
||||
If found, warn the user that findings may be incomplete and proceed with
|
||||
`[GAP]` awareness.
|
||||
|
||||
2. **Run metrics:** Systemic issues (like thermal throttling, LMKs, binder
|
||||
contention) affect all threads.
|
||||
|
||||
> **Prerequisite:** Verify that `trace_processor` is available in your
|
||||
> environment before executing commands (see
|
||||
> `$SKILL_ROOT/references/perfetto/setup.md`).
|
||||
|
||||
Run the following query based on the user's intent to understand the
|
||||
high-level picture:
|
||||
|
||||
```sh
|
||||
trace_processor [trace_file] --run-metrics [comma_separated_metrics]
|
||||
```
|
||||
|
||||
3. **Define target and symptom window:** Find the specific issue's start
|
||||
(`ts`), duration (`dur`) and end timestamp (`ts + dur`). Verify the trace
|
||||
actually covers the full window.
|
||||
|
||||
> After identifying the specific instance to investigate and the symptom
|
||||
> window (for example, janky frame at `ts = 5.1s`), check whether the first
|
||||
> blocking event was caused by a stall that _started before_ the symptom
|
||||
> window. Expanding the window upstream is essential because stalls (for
|
||||
> example, in binder servers, memory reclamation or kernel locks) often
|
||||
> originate hundreds of milliseconds before the user-visible frame drop or
|
||||
> latency spike occurs.
|
||||
>
|
||||
> - Find the victim's first non-Running state transition within the symptom
|
||||
> window.
|
||||
> - Follow the waker chain for that transition. Check waker timestamps -
|
||||
> if the blocking event began significantly before the symptom window,
|
||||
> expand the investigation window upstream to include that origin.
|
||||
> - If it cascades down to an origin in another process (for example, a
|
||||
> binder server that stalled `500ms` before the jank), **note** the
|
||||
> expanded window and include the upstream stall as a co-candidate to
|
||||
> investigate further.
|
||||
|
||||
4. **Output** the triage summary using the format defined below.
|
||||
|
||||
## Quick reference for triaging
|
||||
|
||||
Key available metrics for `--run-metrics`:
|
||||
`android_startup`, `android_cpu`, `android_mem`, `android_lmk`,
|
||||
`android_binder`, `android_surfaceflinger`, `android_gpu`
|
||||
|
||||
Quick lookup table based on symptom:
|
||||
|
||||
| Symptom/Issue | What to check | Useful Perfetto tables |
|
||||
| :--------------- | :------------------------------------ | :---------------------------------------------- |
|
||||
| App startup | Main thread | `android.startup.startups` (`android_startups`) |
|
||||
| App jank | Main, render threads | `actual_frame_timeline_slice` |
|
||||
| System jank | SurfaceFlinger | `actual_frame_timeline_slice` |
|
||||
| App/system crash | `Process crashed` or `tombstoned` | `slice` |
|
||||
| ANR | Main thread, `system_server` watchdog | `thread_state`, `slice` |
|
||||
| Frame issues | `DrawFrame` or `doFrame` slices | `slice` |
|
||||
|
||||
## Final Output Format
|
||||
|
||||
Output the triage summary in the following format:
|
||||
|
||||
```markdown
|
||||
## Trace Metadata
|
||||
|
||||
- **Device Model:** `[String, e.g., Pixel 7 Pro]`
|
||||
- **Android Build:** `[String, e.g., TQ2A.230505.002]`
|
||||
|
||||
## System Vitals Summary
|
||||
|
||||
- **Status:** `[Nominal | Degraded | Critical]`
|
||||
- **Flags Raised:** _(Only list systemic issues that are actually detected)_
|
||||
- **Metric:** `[String, e.g., thermal_throttling, lmkd, binder_contention]`
|
||||
- **Description:** `[e.g., 'Thermal throttling during symptom window']`
|
||||
|
||||
## Candidate
|
||||
|
||||
- **Issue Classification:** `[String, e.g., Startup, Jank, ANR_Input, Crash]`
|
||||
- **Package Name:** `[String]`
|
||||
- **Process Name:** `[String]`
|
||||
- **Thread Name:** `[String]`
|
||||
- **UPID:** `[Integer]`
|
||||
- **Target UTID:** `[Integer]` _(The primary thread)_
|
||||
- **Render Thread UTID:** `[Integer or null]` _(If issue is jank)_
|
||||
- **Start TS:** `[Integer Timestamp]`
|
||||
- **End TS:** `[Integer Timestamp]`
|
||||
- **Duration (ms):** `[Float]`
|
||||
- **Severity Note:** `[String, e.g., '150ms missed frame - worst instance']`
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
# Recording Orchestrator
|
||||
|
||||
Use the guidelines below to orchestrate between recording workflows after
|
||||
disambiguating the user intent (for example, recording a system trace or a
|
||||
heap dump). The guidelines below ensure pre-flight checks are always performed
|
||||
and domain-specific profiling flags are applied accurately.
|
||||
|
||||
## Workflow Selection
|
||||
|
||||
At this point, you should have an answer to the following question: "User
|
||||
intends to record a _____". For example, "User intends to record a system
|
||||
trace".
|
||||
|
||||
## Handling Composite Requests
|
||||
|
||||
If the user's request involves multiple distinct recording goals (for example,
|
||||
setting up a specialized environment AND recording a generic trace), do not
|
||||
execute them simultaneously.
|
||||
|
||||
1. Break down the request and propose a sequential execution plan to the user.
|
||||
2. Ask the user for confirmation to start the first step.
|
||||
3. Do not proceed to the next workflow until the current one is completed.
|
||||
|
||||
## Workflow Discovery and Routing
|
||||
|
||||
This skill supports multiple specialized recording workflows. To determine the
|
||||
right workflows to use:
|
||||
|
||||
1. Use your file search tools (for example, `grep_search`) to recursively scan
|
||||
the `$SKILL_ROOT/recording/workflows/` directory for workflow entrypoints
|
||||
(markdown files defining a top-level `name:` key in their frontmatter,
|
||||
ignoring internal `references/` subdirectories).
|
||||
2. Compare the user's request and intent against the `name:`, `description:`,
|
||||
and `keywords:` fields to identify matching workflows. If multiple are
|
||||
found, present them as options to the user and proceed with the
|
||||
user's selection.
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
---
|
||||
name: perfetto-trace-recording
|
||||
description: >
|
||||
Records Java/native heap dumps, system traces, or custom configs via
|
||||
Perfetto helper scripts on Android.
|
||||
keywords:
|
||||
- perfetto
|
||||
- record
|
||||
- system trace
|
||||
- heap dump
|
||||
- memory leak
|
||||
- custom config
|
||||
---
|
||||
|
||||
# Recording Perfetto Traces on Android (Helper Scripts)
|
||||
|
||||
> [!IMPORTANT] **Scope:** This guide is **strictly for recording traces on
|
||||
> Android devices**. For other platforms (Linux, macOS, or Chrome), refer to
|
||||
> the platform documentation on [perfetto.dev/docs](https://perfetto.dev/docs/).
|
||||
|
||||
Rather than running raw `adb` commands, use the official Perfetto helper
|
||||
scripts. They automatically handle pushing configurations, starting tracing
|
||||
daemons, pulling the trace file, and optionally opening it in the browser.
|
||||
|
||||
Ensure **Developer options** and **USB debugging** are enabled, and your
|
||||
device is connected via USB before starting.
|
||||
|
||||
---
|
||||
|
||||
## 0. Download the Helper Scripts
|
||||
|
||||
Download the helper scripts from the official Perfetto repository:
|
||||
|
||||
```bash
|
||||
TOOLS_URL="https://raw.githubusercontent.com/google/perfetto/main/tools"
|
||||
|
||||
# Java Heap Dump (ART)
|
||||
curl -O "$TOOLS_URL/java_heap_dump" && chmod +x java_heap_dump
|
||||
|
||||
# Native Heap Profiling (heapprofd)
|
||||
curl -O "$TOOLS_URL/heap_profile" && chmod +x heap_profile
|
||||
|
||||
# CPU Stack Sampling (traced_perf)
|
||||
curl -O "$TOOLS_URL/cpu_profile" && chmod +x cpu_profile
|
||||
|
||||
# General Tracing (Ftrace, ATrace, custom configs)
|
||||
curl -O "$TOOLS_URL/record_android_trace" && chmod +x record_android_trace
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Memory Tracing
|
||||
|
||||
Use these tools to analyze memory leaks, object retention, C/C++ allocations,
|
||||
or system-wide memory counters.
|
||||
|
||||
### A. Java Heap Dump (ART)
|
||||
|
||||
Capture a snapshot of all Java objects in a process to investigate memory
|
||||
leaks. Reference docs:
|
||||
<https://perfetto.dev/docs/data-sources/java-heap-profiler>.
|
||||
|
||||
```bash
|
||||
# Trigger a Java heap dump for a specific app
|
||||
./java_heap_dump -n YOUR_APP_PACKAGE_NAME -o ./heap_dump.perfetto-trace
|
||||
```
|
||||
|
||||
### B. Native C/C++ Heap Profiling (heapprofd)
|
||||
|
||||
Track C/C++ memory allocations (malloc/free) to find native leaks. Reference
|
||||
docs: <https://perfetto.dev/docs/data-sources/native-heap-profiler>.
|
||||
|
||||
```bash
|
||||
# Profile native allocations for a specific app
|
||||
./heap_profile -n YOUR_APP_PACKAGE_NAME
|
||||
|
||||
# Profile with custom sampling interval (default is 4096 bytes)
|
||||
./heap_profile -n YOUR_APP_PACKAGE_NAME -i 2048
|
||||
```
|
||||
|
||||
### C. System-wide Memory Counters
|
||||
|
||||
Track RSS, Swap, and process stats over time. Reference docs:
|
||||
<https://perfetto.dev/docs/data-sources/memory-counters-sys-stats>.
|
||||
|
||||
**Note:** To record memory counters, you must use a custom config via
|
||||
`record_android_trace` (see Section 4).
|
||||
|
||||
---
|
||||
|
||||
## 2. Stack Sampling / Callstack Profiling (traced_perf)
|
||||
|
||||
Identify CPU hotspots in C/C++ or Rust code by periodically sampling
|
||||
callstacks. Reference docs:
|
||||
<https://perfetto.dev/docs/data-sources/cpu-profiler>.
|
||||
|
||||
```bash
|
||||
# Profile CPU usage by sampling callstacks at 100Hz (default) for 10 seconds
|
||||
./cpu_profile -n YOUR_APP_PACKAGE_NAME -d 10000
|
||||
|
||||
# Profile at a custom frequency (e.g., 200Hz)
|
||||
./cpu_profile -n YOUR_APP_PACKAGE_NAME -f 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. System Tracing (CPU, Scheduling, & ATrace)
|
||||
|
||||
Investigate jank, slow transitions, CPU scheduling, and system calls.
|
||||
Reference docs: <https://perfetto.dev/docs/data-sources/ftrace>.
|
||||
|
||||
You can specify duration, buffer size, and categories directly on the command
|
||||
line:
|
||||
|
||||
```bash
|
||||
# Record scheduling, frequency, and window manager events for 5 seconds
|
||||
./record_android_trace -t 5s -b 32mb sched gfx wm -a YOUR_APP_PACKAGE_NAME
|
||||
```
|
||||
|
||||
Common categories: `sched` (CPU scheduling), `freq` (CPU frequency), `gfx`
|
||||
(Graphics), `am` (Activity Manager), `wm` (Window Manager), `view` (View
|
||||
System).
|
||||
|
||||
---
|
||||
|
||||
## 4. Custom Configs (Mix & Match Data Sources)
|
||||
|
||||
If you need a custom mixture of data sources (e.g., combining Java heap dumps
|
||||
with ftrace), or control that command-line flags do not offer (ring buffers,
|
||||
long traces, per-counter polling), synthesize a config:
|
||||
|
||||
1. **Read the config reference:**
|
||||
`$SKILL_ROOT/recording/workflows/perfetto-trace-recording/references/trace_config_reference.md`
|
||||
explains config structure and data sources, pointing to exemplar configs in
|
||||
`$SKILL_ROOT/recording/workflows/perfetto-trace-recording/references/example-configs/`
|
||||
that you can start from and merge. For other sources, consult the
|
||||
official guide at <https://perfetto.dev/docs/data-sources/>.
|
||||
2. **Save the Config:** Write the synthesized text configuration to a local
|
||||
file (e.g., `config.pftxt`).
|
||||
3. **Execute the Trace:** Run the trace using the general recorder script
|
||||
`record_android_trace` (specialized scripts do not accept custom configs):
|
||||
|
||||
```bash
|
||||
./record_android_trace -c config.pftxt -o ./my_trace.perfetto-trace
|
||||
```
|
||||
|
||||
If the config has a typo, this fails fast with a parse error naming the bad
|
||||
field - fix the config and retry.
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# App jank / slow UI: framework + app atrace events, frame deadlines from
|
||||
# SurfaceFlinger, and the scheduling context underneath.
|
||||
# Replace com.example.myapp with the app's package name.
|
||||
buffers {
|
||||
size_kb: 65536
|
||||
fill_policy: RING_BUFFER
|
||||
}
|
||||
duration_ms: 10000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.ftrace"
|
||||
ftrace_config {
|
||||
ftrace_events: "sched/sched_switch"
|
||||
ftrace_events: "sched/sched_wakeup"
|
||||
ftrace_events: "sched/sched_waking"
|
||||
ftrace_events: "power/cpu_frequency"
|
||||
ftrace_events: "power/cpu_idle"
|
||||
atrace_categories: "gfx"
|
||||
atrace_categories: "view"
|
||||
atrace_categories: "wm"
|
||||
atrace_categories: "am"
|
||||
atrace_categories: "input"
|
||||
atrace_apps: "com.example.myapp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Per-frame expected vs actual timelines (actual jank classification).
|
||||
data_sources {
|
||||
config {
|
||||
name: "android.surfaceflinger.frametimeline"
|
||||
}
|
||||
}
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.process_stats"
|
||||
process_stats_config {
|
||||
scan_all_processes_on_start: true
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# CPU callstack sampling via traced_perf: periodic stack samples to find hot
|
||||
# functions. Prefer the cpu_profile helper script for a standalone profile;
|
||||
# use this config when combining with other data sources.
|
||||
# Replace com.example.myapp with the app's package name, or drop the scope
|
||||
# block to sample all processes.
|
||||
buffers {
|
||||
size_kb: 65536
|
||||
fill_policy: RING_BUFFER
|
||||
}
|
||||
duration_ms: 10000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.perf"
|
||||
perf_event_config {
|
||||
timebase {
|
||||
frequency: 100
|
||||
}
|
||||
callstack_sampling {
|
||||
scope {
|
||||
target_cmdline: "com.example.myapp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.process_stats"
|
||||
process_stats_config {
|
||||
scan_all_processes_on_start: true
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Java heap dump (retention graph) of one app. Prefer the java_heap_dump
|
||||
# helper script for a standalone dump; use this config when combining a heap
|
||||
# dump with other data sources in one trace.
|
||||
# Replace com.example.myapp with the app's package name.
|
||||
buffers {
|
||||
size_kb: 102400
|
||||
}
|
||||
duration_ms: 30000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "android.java_hprof"
|
||||
java_hprof_config {
|
||||
process_cmdline: "com.example.myapp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Heap dumps can be large; stream to the output file instead of relying on
|
||||
# the in-memory buffer alone.
|
||||
write_into_file: true
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Long / field trace: runs until stopped, keeps the most recent data in a
|
||||
# ring buffer, and periodically flushes to the output file so the trace
|
||||
# survives longer than RAM allows.
|
||||
buffers {
|
||||
size_kb: 65536
|
||||
fill_policy: RING_BUFFER
|
||||
}
|
||||
|
||||
# No duration_ms: stop manually (Ctrl-C on record_android_trace), or add one.
|
||||
# Move data to the output file periodically instead of only at the end.
|
||||
write_into_file: true
|
||||
file_write_period_ms: 2500
|
||||
# Commit app shared-memory buffers periodically so events stay ordered.
|
||||
flush_period_ms: 30000
|
||||
# Stop when the output file reaches this size (bytes).
|
||||
max_file_size_bytes: 1000000000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.ftrace"
|
||||
ftrace_config {
|
||||
ftrace_events: "sched/sched_switch"
|
||||
ftrace_events: "sched/sched_wakeup"
|
||||
ftrace_events: "power/cpu_frequency"
|
||||
ftrace_events: "power/cpu_idle"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.process_stats"
|
||||
process_stats_config {
|
||||
scan_all_processes_on_start: true
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# System and per-process memory over time: meminfo/vmstat counters polled
|
||||
# periodically, per-process RSS via both polling and kernel rss_stat events,
|
||||
# and low-memory-killer activity.
|
||||
buffers {
|
||||
size_kb: 32768
|
||||
}
|
||||
duration_ms: 30000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.sys_stats"
|
||||
sys_stats_config {
|
||||
meminfo_period_ms: 1000
|
||||
meminfo_counters: MEMINFO_MEM_TOTAL
|
||||
meminfo_counters: MEMINFO_MEM_FREE
|
||||
meminfo_counters: MEMINFO_MEM_AVAILABLE
|
||||
meminfo_counters: MEMINFO_SWAP_FREE
|
||||
vmstat_period_ms: 1000
|
||||
vmstat_counters: VMSTAT_NR_FREE_PAGES
|
||||
vmstat_counters: VMSTAT_PGFAULT
|
||||
vmstat_counters: VMSTAT_PGMAJFAULT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.process_stats"
|
||||
process_stats_config {
|
||||
proc_stats_poll_ms: 10000
|
||||
scan_all_processes_on_start: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.ftrace"
|
||||
ftrace_config {
|
||||
ftrace_events: "kmem/rss_stat"
|
||||
ftrace_events: "lowmemorykiller/lowmemory_kill"
|
||||
ftrace_events: "oom/oom_score_adj_update"
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Native (C/C++) heap profiling via heapprofd: sampled malloc/free callstacks
|
||||
# for one app. Prefer the heap_profile helper script for a standalone profile;
|
||||
# use this config when combining with other data sources.
|
||||
# Replace com.example.myapp with the app's package name.
|
||||
buffers {
|
||||
size_kb: 65536
|
||||
}
|
||||
duration_ms: 30000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "android.heapprofd"
|
||||
heapprofd_config {
|
||||
sampling_interval_bytes: 4096
|
||||
process_cmdline: "com.example.myapp"
|
||||
# To profile ART/JNI and custom allocators too, add: all_heaps: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_into_file: true
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# CPU scheduling: who ran when, on which core, at what clock speed.
|
||||
# The base layer for almost any performance investigation.
|
||||
buffers {
|
||||
size_kb: 32768
|
||||
fill_policy: RING_BUFFER
|
||||
}
|
||||
duration_ms: 10000
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.ftrace"
|
||||
ftrace_config {
|
||||
ftrace_events: "sched/sched_switch"
|
||||
ftrace_events: "sched/sched_wakeup"
|
||||
ftrace_events: "sched/sched_waking"
|
||||
ftrace_events: "sched/sched_process_exit"
|
||||
ftrace_events: "sched/sched_process_free"
|
||||
ftrace_events: "power/cpu_frequency"
|
||||
ftrace_events: "power/cpu_idle"
|
||||
ftrace_events: "power/suspend_resume"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Resolves thread/process ids in sched events to readable names.
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.process_stats"
|
||||
process_stats_config {
|
||||
scan_all_processes_on_start: true
|
||||
}
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
# Synthesizing Perfetto Trace Configs (Mix & Match)
|
||||
|
||||
Read this when you need a custom trace config - a mixture of data sources the
|
||||
specialized helper scripts do not cover. Start from the exemplar configs in
|
||||
`$SKILL_ROOT/recording/workflows/perfetto-trace-recording/references/example-configs/`,
|
||||
then add, remove, or merge the pieces described below.
|
||||
|
||||
## Shape of a config
|
||||
|
||||
A config is a `TraceConfig` protobuf - authoritative schema:
|
||||
<https://raw.githubusercontent.com/google/perfetto/main/protos/perfetto/config/trace_config.proto>
|
||||
(per-source option messages live under `protos/perfetto/config/` in the same
|
||||
repo, e.g. `.../ftrace/ftrace_config.proto`) - written in protobuf **text
|
||||
format** (conventionally saved as `config.pftxt`):
|
||||
|
||||
```protobuf
|
||||
buffers {
|
||||
size_kb: 32768 # sizes are in KB, not bytes
|
||||
fill_policy: RING_BUFFER # keep newest data; DISCARD keeps oldest
|
||||
}
|
||||
duration_ms: 10000 # omit to trace until stopped manually
|
||||
|
||||
data_sources {
|
||||
config {
|
||||
name: "linux.ftrace" # which producer to enable
|
||||
ftrace_config { ... } # that producer's own options
|
||||
}
|
||||
}
|
||||
# ...more data_sources blocks, one per source...
|
||||
```
|
||||
|
||||
Merging two configs = keep one `buffers` section and concatenate their
|
||||
`data_sources` blocks. Each data source may appear at most once (the
|
||||
`linux.ftrace` config especially: merge all `ftrace_events`,
|
||||
`atrace_categories`, and `atrace_apps` entries into a single block).
|
||||
|
||||
## Exemplar configs
|
||||
|
||||
Location:
|
||||
`$SKILL_ROOT/recording/workflows/perfetto-trace-recording/references/example-configs/`
|
||||
|
||||
| File | Use case |
|
||||
| :--- | :--- |
|
||||
| `sched_cpu.pftxt` | CPU scheduling + frequency/idle (base layer). |
|
||||
| `app_jank.pftxt` | Slow UI / dropped frames: atrace + frame timeline. |
|
||||
| `memory_counters.pftxt` | System + per-process memory, LMK activity. |
|
||||
| `java_heap_dump.pftxt` | Java heap retention graph for one app. |
|
||||
| `native_heap.pftxt` | Sampled native malloc/free callstacks (heapprofd). |
|
||||
| `cpu_profile.pftxt` | Periodic CPU callstack samples (traced_perf). |
|
||||
| `long_background.pftxt` | Long/field traces: ring buffer + periodic writes. |
|
||||
|
||||
For a standalone heap dump, native heap profile, or CPU profile, prefer the
|
||||
dedicated helper scripts in:
|
||||
`$SKILL_ROOT/recording/workflows/perfetto-trace-recording/perfetto_trace_recording.md`.
|
||||
Use exemplars when one trace must combine several sources.
|
||||
|
||||
## Data sources at a glance
|
||||
|
||||
| `name` | What it records | Key options |
|
||||
| :--- | :--- | :--- |
|
||||
| `linux.ftrace` | Kernel events and atrace | `ftrace_events`, `atrace_categories`, `atrace_apps` |
|
||||
| `linux.process_stats` | Process/thread names & stats | `scan_all_processes_on_start`, `proc_stats_poll_ms` |
|
||||
| `linux.sys_stats` | Periodic `/proc` counters | `meminfo_period_ms`, `vmstat_period_ms`, `stat_period_ms` |
|
||||
| `android.log` | Logcat | `android_log_config { log_ids: ... }` |
|
||||
| `android.surfaceflinger.frametimeline` | Frame timelines (jank) | None needed |
|
||||
| `android.java_hprof` | Java heap dump | `java_hprof_config { process_cmdline: ... }` |
|
||||
| `android.heapprofd` | Native heap profiling | `heapprofd_config { sampling_interval_bytes, ... }` |
|
||||
| `linux.perf` | CPU callstack sampling | `perf_event_config { timebase, callstack_sampling }` |
|
||||
| `android.packages_list` | Package mapping | None needed |
|
||||
| `android.power` | Battery counters | `android_power_config { battery_poll_ms, ... }` |
|
||||
| `track_event` | Custom app trace events | `track_event_config { enabled_categories }` |
|
||||
|
||||
The full, authoritative field list for every data source is the generated
|
||||
[TraceConfig reference](https://perfetto.dev/docs/reference/trace-config-proto);
|
||||
per-source guides live under
|
||||
[perfetto.dev/docs/data-sources](https://perfetto.dev/docs/data-sources/).
|
||||
|
||||
## Top-level knobs
|
||||
|
||||
- `duration_ms` - trace length. Omit it to trace until the recording command
|
||||
is stopped (Ctrl-C on `record_android_trace`).
|
||||
- `buffers.fill_policy` - `RING_BUFFER` keeps newest data (right choice when
|
||||
the interesting moment is at the end); `DISCARD` keeps the oldest.
|
||||
- Long traces: `write_into_file: true` + `file_write_period_ms` stream buffer
|
||||
to disk periodically so the trace can exceed RAM; `flush_period_ms: 30000`
|
||||
keeps app-emitted events ordered; `max_file_size_bytes` bounds output.
|
||||
- Buffer sizing rule of thumb: 32–64 MB (`size_kb: 32768`–`65536`) is plenty
|
||||
for most 10–30s traces; heap dumps need ~100 MB or `write_into_file`.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- All buffer sizes are **KB** (`size_kb: 32768` = 32 MB); durations are
|
||||
**ms**.
|
||||
- atrace data (categories and app events) only flows through the
|
||||
`linux.ftrace` data source - there is no separate "atrace" source, and an
|
||||
app's custom trace events appear only if its package is listed in
|
||||
`atrace_apps` (or `atrace_apps: "*"`).
|
||||
- Field-name typos are only caught when the config is parsed at record time:
|
||||
`record_android_trace -c config.pftxt` fails fast with a parse error naming
|
||||
the bad field, so treat that as your validator, fix, and retry.
|
||||
- Text-format enum values are bare identifiers (`fill_policy: RING_BUFFER`),
|
||||
and strings are quoted.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Environment setup
|
||||
|
||||
## Set `$SKILL_ROOT`
|
||||
|
||||
Set up `$SKILL_ROOT` once per session.
|
||||
|
||||
Every file this skill references - workflow markdown, reference docs, helper
|
||||
scripts, and downloaded dependencies - is written in the form `$SKILL_ROOT/...`,
|
||||
relative to the **skill root** (the directory holding this skill's `SKILL.md`).
|
||||
Set it once to the absolute path of the directory you loaded `SKILL.md` from:
|
||||
|
||||
```sh
|
||||
# Substitute with the directory the SKILL.md lives in.
|
||||
export SKILL_ROOT="/absolute/path/to/skills/profilers/android-profiler"
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# Getting `trace_processor` working
|
||||
|
||||
> **Prerequisite:** Ensure `$SKILL_ROOT` is initialized per
|
||||
> `$SKILL_ROOT/references/env_setup.md`.
|
||||
|
||||
## Put `trace_processor` on the `PATH`
|
||||
|
||||
If the workspace does not already have a pre-downloaded `trace_processor`
|
||||
wrapper, download it from <https://get.perfetto.dev/trace_processor> to
|
||||
`$SKILL_ROOT/bin/trace_processor` and make it invocable for this session:
|
||||
|
||||
```sh
|
||||
chmod +x "$SKILL_ROOT/bin/trace_processor" # ensure exec bit is set
|
||||
export PATH="$SKILL_ROOT/bin:$PATH"
|
||||
trace_processor --version # smoke test (ensure > v57.0, e.g. v57.1+)
|
||||
```
|
||||
|
||||
After this, every bare `trace_processor ...` command in this skill works
|
||||
verbatim. On Windows, skip the `PATH` setup and invoke it as
|
||||
`python "$SKILL_ROOT/bin/trace_processor" ...` instead.
|
||||
|
||||
Notes:
|
||||
|
||||
- The first invocation downloads the prebuilt native binary (picking the
|
||||
right one for the host platform) into `~/.local/share/perfetto/prebuilts/`
|
||||
and caches it; only the first call pays the download cost.
|
||||
- If the user's environment has its own mandatory `trace_processor`
|
||||
(Google-internal, OEM build environments, CI images), prefer that
|
||||
team-specific setup instead.
|
||||
@@ -0,0 +1,327 @@
|
||||
# Querying Perfetto traces
|
||||
|
||||
This reference explains how to extract data from a Perfetto trace file
|
||||
(`.pftrace`, `.perfetto-trace`, `.pb`) using `trace_processor` and
|
||||
PerfettoSQL. Read it for ad-hoc querying outside a guided workflow; the
|
||||
workflows under `$SKILL_ROOT/analysis/workflows` and
|
||||
`$SKILL_ROOT/recording/workflows` carry their own queries.
|
||||
|
||||
The `trace_processor` binary is what every other Perfetto analysis tool
|
||||
runs on top of, including the Perfetto UI. Reference docs:
|
||||
<https://perfetto.dev/docs/analysis/trace-processor>.
|
||||
|
||||
> **Prerequisite - `trace_processor` must be invokable.** Before
|
||||
> running any of the shell commands below, read
|
||||
> `$SKILL_ROOT/references/perfetto/setup.md`. It defines how to make
|
||||
> the bare `trace_processor` commands below work in this environment.
|
||||
|
||||
## Querying a trace: sessions
|
||||
|
||||
Querying goes through a **session**: load the trace once into a named
|
||||
background session, then run every query against it with `--remote`.
|
||||
Parsing a trace is the expensive part (tens of seconds for a multi-GB
|
||||
trace); the session pays it once, and every real analysis runs more than
|
||||
one query.
|
||||
|
||||
```sh
|
||||
# 1. Load the trace into a background session - once per trace.
|
||||
# Pick a descriptive session name (e.g. derived from the trace file).
|
||||
trace_processor server unix --name mysession --daemonize TRACE_FILE
|
||||
|
||||
# 2. Run queries against the warm session: instant, no reparse.
|
||||
trace_processor query --remote mysession \
|
||||
"SELECT ts, dur, name FROM slice WHERE dur > 5e8 LIMIT 5"
|
||||
|
||||
# 3. When you are completely done with the trace:
|
||||
trace_processor server kill mysession
|
||||
```
|
||||
|
||||
Multiple statements separated by `;` are supported in one invocation.
|
||||
|
||||
Session rules:
|
||||
|
||||
- Session names are managed by trace_processor in a per-user session
|
||||
directory - there are no ports to choose and no collisions with other
|
||||
agents or the Perfetto UI.
|
||||
- **Session state persists across `query --remote` calls.** A
|
||||
`CREATE PERFETTO TABLE` or `INCLUDE PERFETTO MODULE` run in one call is
|
||||
visible in the next, so materializing intermediate results pays off
|
||||
across invocations.
|
||||
- Flags that configure trace loading (`--full-sort`, `--add-sql-package`, ...)
|
||||
belong on the `server unix` invocation, not on `query --remote` - the client
|
||||
rejects them with an explanatory error.
|
||||
- `--remote` also accepts an absolute `*.sock` path or `host:port`;
|
||||
names are the common case.
|
||||
- Forgotten sessions are reaped automatically after 30 minutes idle
|
||||
(`--idle-timeout`), but kill your session when the analysis is done.
|
||||
|
||||
`TRACE_FILE` can be a local path, an `http(s)://` URL, or a Perfetto UI
|
||||
share link (`https://ui.perfetto.dev/#!/?s=<hash>`) - in the last two
|
||||
cases trace_processor downloads the trace for you (cached under
|
||||
`~/.cache/perfetto/` or the platform equivalent), resolving the share
|
||||
link to its underlying trace first.
|
||||
|
||||
For a single throwaway query on a small trace you _can_ skip the session
|
||||
(`trace_processor query TRACE_FILE "..."` parses, queries, and exits),
|
||||
but treat that as the exception: it re-parses the trace on every
|
||||
invocation and forgets created tables and included modules between
|
||||
calls. Default to a session.
|
||||
|
||||
## Discovering what's in the trace
|
||||
|
||||
PerfettoSQL ships with **intrinsic table-functions** for browsing the
|
||||
loaded standard library - modules, tables/views, functions, macros. Use
|
||||
these to find what's available and to verify if a Standard Library module
|
||||
already provides the needed abstraction before drafting custom logic.
|
||||
|
||||
**Mandatory Schema Check:** Do not guess column names or join keys. Always
|
||||
use a plain `LIMIT 0` query to read the exact column schema of any specific
|
||||
table, view, or query result before drafting your query.
|
||||
|
||||
> **Intrinsic surface - not stable API.** The `__intrinsic_*` names below
|
||||
> are an implementation detail of trace processor. They're fair game for
|
||||
> an agent to use during a session because this reference is loaded, but
|
||||
> **don't bake `__intrinsic_*` names into committed scripts, dashboards,
|
||||
> or stdlib modules** - they can change without notice.
|
||||
|
||||
```sql
|
||||
-- 1. List every stdlib module currently available.
|
||||
SELECT package, module FROM __intrinsic_stdlib_modules ORDER BY 1, 2;
|
||||
|
||||
-- 2. List the tables/views a specific module exposes
|
||||
-- (after INCLUDE PERFETTO MODULE).
|
||||
INCLUDE PERFETTO MODULE slices.with_context;
|
||||
SELECT name, type, exposed, description
|
||||
FROM __intrinsic_stdlib_tables('slices.with_context');
|
||||
|
||||
-- 3. List functions / macros a module exposes.
|
||||
SELECT name, return_type, args
|
||||
FROM __intrinsic_stdlib_functions('slices.with_context');
|
||||
SELECT name, return_type, args
|
||||
FROM __intrinsic_stdlib_macros('android.memory.heap_graph.helpers');
|
||||
|
||||
-- 4. Read the column schema of any table, view, or query.
|
||||
-- LIMIT 0 returns the result header with no row scan; trace_processor
|
||||
-- prints "column N = <name>" lines for each column.
|
||||
SELECT * FROM slice LIMIT 0;
|
||||
SELECT * FROM thread_or_process_slice LIMIT 0;
|
||||
SELECT * FROM (SELECT ts, dur, name FROM slice WHERE dur > 0) LIMIT 0;
|
||||
```
|
||||
|
||||
Useful starting points for any trace:
|
||||
|
||||
| View | What's in it |
|
||||
| :------------- | :--------------------------------------------------------------- |
|
||||
| `slice` | Atrace slices, async slices, anything with a duration on a track |
|
||||
| `thread` | One row per thread |
|
||||
| `process` | One row per process |
|
||||
| `thread_state` | State transitions (Running, Runnable, Sleeping, ...) |
|
||||
| `sched_slice` | When threads were on-CPU |
|
||||
| `counter` | Time-series counter samples |
|
||||
| `track` | Every track in the trace; join on `track_id` to other tables |
|
||||
|
||||
Static reference for the public surface (does not require a running
|
||||
trace_processor): <https://perfetto.dev/docs/analysis/sql-tables>.
|
||||
|
||||
## Using the standard library
|
||||
|
||||
Most useful queries are _much_ shorter when you build on stdlib modules
|
||||
instead of joining raw tables yourself. Generated stdlib reference:
|
||||
<https://perfetto.dev/docs/analysis/stdlib-docs>.
|
||||
|
||||
Include a module before referencing the views, tables or macros it
|
||||
defines:
|
||||
|
||||
```sql
|
||||
INCLUDE PERFETTO MODULE slices.with_context;
|
||||
|
||||
SELECT name, dur, thread_name, process_name
|
||||
FROM thread_or_process_slice
|
||||
WHERE dur > 1e9 -- slices longer than 1s
|
||||
ORDER BY dur DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
A few commonly used modules to know:
|
||||
|
||||
- `slices.with_context` - slice rows joined with their thread / process.
|
||||
- `sched.with_context` - `sched_slice` joined with thread / process.
|
||||
- `android.startup.startups` - one row per app startup.
|
||||
- `stacks.cpu_profiling` - flat samples and call-graph helpers.
|
||||
- `android.memory.heap_graph.dominator_tree` - retained-size analysis for
|
||||
Java heap dumps.
|
||||
|
||||
The module name maps directly to the file path under the stdlib root:
|
||||
`foo.bar` lives at `foo/bar.sql`. Browse the full list at the stdlib
|
||||
reference linked above.
|
||||
|
||||
## Tips for writing good PerfettoSQL
|
||||
|
||||
- **Reach for stdlib first.** If you find yourself joining `slice` to
|
||||
`thread_track` to `thread` to `process`, there is almost certainly a
|
||||
stdlib module that already does it. Check the stdlib reference before
|
||||
writing the join.
|
||||
- **Filter on `dur > 0` and Trace Boundaries carefully.** Some slices have
|
||||
`dur = -1` (still open at trace end) and some have `dur = 0` (instant
|
||||
events). Be explicit about which you mean. When calculating a bounding box
|
||||
(for example, `ts + dur`) or summing durations (`SUM(dur)`), handle
|
||||
incomplete durations using: `IIF(dur = -1, trace_end() - ts, dur)`.
|
||||
- **Robust State Transitions.** Avoid manual timestamp arithmetic (for
|
||||
example, `ts + dur = next.ts`) to join adjacent events. Rely on standard
|
||||
library modules (for example, `sched.runnable`, `linux.perf.counters`,
|
||||
`intervals.overlap`) which safely handle trace gaps and preemptions.
|
||||
- **Working with Identifiers:**
|
||||
- **Use Unique Identifiers for Joins:** When writing SQL queries in
|
||||
Perfetto, you must join tables using `utid` (unique thread ID) or `upid`
|
||||
(unique process ID) instead of regular `tid` or `pid`. **Why it's
|
||||
useful**: The operating system recycles `TIDs` and `PIDs`, while `UTIDs`
|
||||
and `UPIDs` remain unique for the lifetime of the trace, which prevents
|
||||
incorrect joins.
|
||||
- Columns like `id`, `utid`, `upid`, `track_id` are not stable across traces
|
||||
or even runs of trace_processor on the same trace. You can use them
|
||||
**inside** a query as join keys, but alongside IDs, always join out to
|
||||
a stable name (`thread.name`, `process.name`, `slice.name`) when reporting
|
||||
results to the user.
|
||||
- **Materialize expensive intermediate results.** `CREATE PERFETTO TABLE foo
|
||||
AS SELECT ...` caches the result so subsequent queries don't redo the work.
|
||||
- _Note for `SPAN_JOIN`:_ Intermediate tables fed into a `SPAN_JOIN` must
|
||||
be materialized using `CREATE PERFETTO TABLE`, not `CREATE VIEW`.
|
||||
- **Idempotency.** Ensure queries are idempotent to prevent "already exists"
|
||||
errors during multiple executions.
|
||||
- For Perfetto objects, always use `CREATE OR REPLACE`:
|
||||
`CREATE OR REPLACE PERFETTO {TABLE|VIEW|MACRO|FUNCTION}`.
|
||||
- For SQLite Virtual Tables (such as `SPAN_JOIN`), `CREATE OR REPLACE` is
|
||||
not supported. Explicitly drop them first:
|
||||
`DROP TABLE IF EXISTS my_table;`
|
||||
`CREATE VIRTUAL TABLE my_table USING SPAN_JOIN(...);`
|
||||
- For standard SQLite indexes, prepend `DROP INDEX IF EXISTS index_name;`.
|
||||
- **`SPAN_JOIN` safety.** `SPAN_JOIN` will crash if intervals **within the
|
||||
same input table** overlap. Always use the `PARTITIONED {column}` (for
|
||||
example, `PARTITIONED track_id`) clause to isolate intervals.
|
||||
- **Avoid `SELECT *` in saved queries.** Trace processor table schemas can
|
||||
gain columns; pin the columns you actually use.
|
||||
- **Use `EXPLAIN QUERY PLAN` if a query is slow.** It shows whether SQLite is
|
||||
using indexes. Counter and slice tables have built-in indexes on `ts` and
|
||||
`track_id`; queries that don't filter on either will scan the whole table.
|
||||
- **Argument Extraction:** Use `EXTRACT_ARG(arg_set_id, 'key')` to fetch event
|
||||
properties instead of manually joining the `args` table.
|
||||
- **JSON Parsing:** When dealing with JSON text, use standard SQLite JSON
|
||||
functions (for example, `json_extract()`) to extract values.
|
||||
- **String Matching (Always use GLOB).** Use `GLOB` instead of `LIKE`. `LIKE`
|
||||
causes performance bottlenecks and treats underscores (`_`) as wildcards,
|
||||
leading to bugs.
|
||||
- **Exact matches:** Use `=`.
|
||||
- **Substring matches:** Use `GLOB` with `*` (for example,
|
||||
`name GLOB '*RenderThread*'`).
|
||||
- **Case-insensitive matches:** Use `LOWER(name) GLOB` and make sure the
|
||||
search string is fully lowercase (for example,
|
||||
`LOWER(name) GLOB '*renderthread*'`). Use this when dealing with
|
||||
inconsistent trace capitalization (for example, `WakeLock` vs `wakelock`).
|
||||
- **Alias Precision.** Always prefix column names with table or view alias,
|
||||
that is: `{alias}.{column_name}`.
|
||||
|
||||
## Common Analysis Patterns
|
||||
|
||||
- **Calculating Time Overlaps & CPU Time:**
|
||||
1. **Primary Method (MANDATORY):** Always search the standard library first
|
||||
before writing custom interval logic. For example, to find the exact CPU
|
||||
execution time of a slice, do not calculate it manually; instead, search
|
||||
the docs and use the `slices.cpu_time` module.
|
||||
2. **Fallback Method (Use ONLY if you have verified no stdlib module or
|
||||
`SPAN_JOIN` applies):** If you must calculate custom overlap durations
|
||||
between two different sets of time intervals `[start1, end1]` and
|
||||
`[start2, end2]`:
|
||||
- **Condition:** The intervals overlap if `start1 < end2` and
|
||||
`start2 < end1`.
|
||||
- **Duration:** The overlap duration is calculated as
|
||||
`MIN(end1, end2) - MAX(start1, start2)`.
|
||||
- **Important:** Incomplete Perfetto slices have a duration of -1
|
||||
(`dur = -1`). Always calculate the effective end time using
|
||||
`ts + IIF(dur = -1, trace_end() - ts, dur)` before applying this logic.
|
||||
- Include the `android.startup.startups` module and query
|
||||
`android_thread_slices_for_all_startups` (or `android_startups`) for
|
||||
app startup requests.
|
||||
- Join `counter_track` with `counter` to get values of counter with a
|
||||
specific name.
|
||||
- When querying for a CPU frequency counter, include the `linux.cpu.frequency`
|
||||
module and use the `cpu_frequency_counters` table.
|
||||
- **Window Size:** When looking for events around a specific timestamp, start
|
||||
with 100ms as the window size.
|
||||
- **Total Duration:** To calculate the total time spent in slices matching a
|
||||
specific name pattern (for example, `*{name_pattern}*`), you must sum their
|
||||
durations. **Why it's useful**: This helps quantify the total impact of a
|
||||
specific function or feature on performance across multiple calls. Here is
|
||||
an example query (note the safe handling of incomplete slices):
|
||||
```sql
|
||||
SELECT
|
||||
count(*) as total_count,
|
||||
sum(IIF(slice.dur = -1, trace_end() - slice.ts, slice.dur)) / 1e6
|
||||
as total_dur_ms
|
||||
FROM slice
|
||||
WHERE slice.name GLOB '*{name_pattern}*';
|
||||
```
|
||||
|
||||
## Analytical Workflow (Standard Operating Procedure)
|
||||
|
||||
To ensure accuracy and efficiency, follow these steps:
|
||||
|
||||
1. **Research & Dissection:** Identify the core question and required data
|
||||
points.
|
||||
2. **Mandatory Schema Validation:** Locate relevant modules via
|
||||
`__intrinsic_stdlib_modules` and their tables via
|
||||
`__intrinsic_stdlib_tables('module_name')`. Verify column names and types.
|
||||
- **Intent Check:** You must verify if a stdlib module already provides
|
||||
the needed abstraction before drafting manual arithmetic or custom joins.
|
||||
|
||||
- **IMPORTANT:** If your query requires calculating overlaps, intersections,
|
||||
or boundaries between intervals, you MUST search `__intrinsic_stdlib_modules`
|
||||
globally (for example, `WHERE module GLOB '*overlap*'`) before writing
|
||||
`MIN()/MAX()` or `IIF(dur = -1...)` logic.
|
||||
|
||||
3. **Draft & Validate Loop (Max 3 Iterations):**
|
||||
|
||||
- [ ] **Draft:** Use only verified schemas. Ensure `INCLUDE PERFETTO MODULE`
|
||||
is present for non-prelude modules.
|
||||
- [ ] **Verify Idempotency:** Use `CREATE OR REPLACE` or `DROP TABLE IF EXISTS`
|
||||
for virtual tables.
|
||||
- [ ] **Check Precision:** Are ALL columns prefixed with aliases (e.g.,
|
||||
`s.name`)? Are you joining on `utid`/`upid`?
|
||||
- [ ] **String Matching:** Did you use `GLOB` or `=` instead of `LIKE`?
|
||||
- [ ] **Span Join Check:** If using `SPAN_JOIN`, are tables `PARTITIONED`
|
||||
and materialized?
|
||||
- [ ] **Execute:** Run against the session:
|
||||
`trace_processor query --remote SESSION "QUERY"`.
|
||||
|
||||
**Execution Rules:**
|
||||
|
||||
- **File Usage:** If you must create a SQL file to execute queries (for
|
||||
example, due to query length or escaping issues), you must create them
|
||||
in the `/tmp/` directory.
|
||||
- **Failure Resilience:** Debug and fix SQL syntax and logic errors when
|
||||
query fails. Don't simplify the analytical intent to pass validation.
|
||||
For example, if requested to calculate an overlap or intersection, you
|
||||
must fix the intersection math. Don't substitute with disjoint queries
|
||||
(for example, returning independent total durations) as a workaround.
|
||||
|
||||
4. **Cleanup & Finalize:**
|
||||
|
||||
- Explicitly return and state the final validated SQL and explain the
|
||||
results to the user.
|
||||
- **Save an analysis report.** Write a markdown file in the working
|
||||
directory (default `perfetto_analysis_report.md`) containing: the
|
||||
question investigated, the trace file(s) analyzed, the findings with
|
||||
concrete numbers, the final validated queries (so the analysis can be
|
||||
re-run), and open questions / next steps. Point the user at it in
|
||||
your final message.
|
||||
- Before finishing, delete any temporary SQL files created in `/tmp/`.
|
||||
|
||||
## Where to look for more
|
||||
|
||||
- Language tour:
|
||||
<https://perfetto.dev/docs/analysis/perfetto-sql-getting-started>
|
||||
- Trace processor reference:
|
||||
<https://perfetto.dev/docs/analysis/trace-processor>
|
||||
- Generated table reference:
|
||||
<https://perfetto.dev/docs/analysis/sql-tables>
|
||||
- Generated stdlib reference:
|
||||
<https://perfetto.dev/docs/analysis/stdlib-docs>
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
name: perfetto-sql
|
||||
description: Translates natural language data intents into syntactically valid Perfetto
|
||||
SQL queries and executes them against a local trace file. Use this skill to extract
|
||||
slice, thread, or memory data from Android Perfetto traces using trace_processor.
|
||||
license: Complete terms in LICENSE.txt
|
||||
metadata:
|
||||
author: Google LLC
|
||||
last-updated: '2026-05-14'
|
||||
keywords:
|
||||
- Android
|
||||
- Perfetto SQL
|
||||
- Query Guidelines
|
||||
- Performance Profiling
|
||||
- Trace Analysis
|
||||
- SQL Best Practices
|
||||
- SPAN_JOIN
|
||||
- Idempotency
|
||||
---
|
||||
|
||||
## Guidelines and Hints
|
||||
|
||||
- **Idempotency:** Ensure queries are idempotent to prevent "already exists" errors during multiple executions.
|
||||
- For Perfetto objects, always use `CREATE OR REPLACE`: `CREATE OR REPLACE
|
||||
PERFETTO TABLE`, `CREATE OR REPLACE PERFETTO VIEW`, `CREATE OR REPLACE
|
||||
PERFETTO FUNCTION`, `CREATE OR REPLACE PERFETTO MACRO`.
|
||||
- For SQLite Virtual Tables (such as `SPAN_JOIN`), `CREATE OR REPLACE` is not supported. Explicitly drop them first: `DROP TABLE IF EXISTS
|
||||
my_table; CREATE VIRTUAL TABLE my_table USING SPAN_JOIN(...);`
|
||||
- For standard SQLite indexes, prepend `DROP INDEX IF EXISTS index_name;`.
|
||||
- `SPAN_JOIN` will crash if intervals **within the same input table** overlap. Always use the `PARTITIONED {column}` (for example, `PARTITIONED upid`) clause to isolate intervals.
|
||||
- Intermediate tables fed into a `SPAN_JOIN` must be materialized using `CREATE PERFETTO TABLE`, not `CREATE VIEW`.
|
||||
- **Trace Boundaries (`dur = -1`):** Slices or thread states that don't finish before the trace ends are recorded with `dur = -1`. When calculating a bounding box (for example, `ts + dur`) or summing durations (`SUM(dur)`), handle incomplete durations using: `IIF(dur = -1, trace_end() - ts, dur)`.
|
||||
- **Robust State Transitions:** Avoid manual timestamp arithmetic (for example, `ts + dur = next.ts`) to join adjacent events. Rely on standard library modules (for example, `sched.runnable`, `linux.perf.counters`, `intervals.overlap`) which safely handle trace gaps and preemptions.
|
||||
- **Unique Identifiers:** When writing SQL queries in Perfetto, you must join tables using `utid` (unique thread ID) or `upid` (unique process ID) instead of the regular `tid` or `pid`. **Why it's useful** : The operating system recycles `TIDs` and `PIDs`, while `UTIDs` and `UPIDs` remain unique for the lifetime of the trace, which prevents incorrect joins.
|
||||
- **Safe Argument Extraction:** Use `EXTRACT_ARG(arg_set_id, 'key')` to extract dictionary or JSON-like properties from slices or tracks. Don't attempt string parsing.
|
||||
- **String Matching (Always use GLOB):** Use `GLOB` instead of `LIKE`. `LIKE` causes performance bottlenecks and treats underscores (`_`) as wildcards, leading to bugs.
|
||||
- **Exact matches:** Use `=`.
|
||||
- **Substring matches:** Use `GLOB` with `*` (for example, `name GLOB
|
||||
'*RenderThread*'`).
|
||||
- **Case-insensitive matches:** Use `LOWER(name) GLOB` and make sure the search string is fully lowercase (for example, `LOWER(name) GLOB
|
||||
'*renderthread*'`). Use this when dealing with inconsistent trace capitalization (for example, `WakeLock` versus `wakelock`).
|
||||
- **Calculating Time Overlaps:** To calculate the overlap duration between two
|
||||
time intervals `[start1, end1]` and `[start2, end2]`:
|
||||
|
||||
> **Precedence Rule:** Always prefer using `SPAN_JOIN` or standard library
|
||||
> functions (for example, `intervals.overlap`) to calculate overlaps
|
||||
> **between two different sets of intervals** . Avoid manual arithmetic if a
|
||||
> standard library feature or `SPAN_JOIN` can achieve the same result. Use
|
||||
> the following logic if no built-in alternative exists.
|
||||
1. **Condition:** The intervals overlap if `start1 < end2` and `start2 <
|
||||
end1`.
|
||||
2. **Duration:** The overlap duration is calculated as `MIN(end1, end2) -
|
||||
MAX(start1, start2)`
|
||||
|
||||
> **Important:** Incomplete Perfetto slices have a duration of -1
|
||||
> (`dur = -1`). Always calculate the effective end time using `ts +
|
||||
> IIF(dur = -1, trace_end() - ts, dur)` before applying this logic.
|
||||
- Query `android_thread_slices_for_all_startups` for app startup requests.
|
||||
|
||||
- Join `counter_track` with `counter` to get values of counter with a specific
|
||||
name.
|
||||
|
||||
- When querying for a CPU frequency counter, include the `linux.cpu.frequency`
|
||||
module and use the `cpu_frequency_counters` table.
|
||||
|
||||
- When looking for events around a specific timestamp, start with 100ms as the
|
||||
window size.
|
||||
|
||||
- Always prefix column names with table or view alias, that is:
|
||||
`{alias}.{column_name}`.
|
||||
|
||||
- To calculate the total time spent in slices matching a specific name pattern
|
||||
(for example, `*{name_pattern}*`), you must sum their durations. **Why it's
|
||||
useful** : This helps quantify the total impact of a specific function or
|
||||
feature on performance across multiple calls. Here is an example query (note
|
||||
the safe handling of incomplete slices): `sql SELECT count(*) as
|
||||
total_count, sum(IIF(slice.dur = -1, trace_end() - slice.ts, slice.dur)) /
|
||||
1000000.0 as total_dur_ms FROM slice WHERE slice.name GLOB
|
||||
'*{name_pattern}*';`
|
||||
|
||||
## Resources
|
||||
|
||||
- **Documentation:** The Perfetto Standard Library documentation is in [`perfetto-stdlib.md`](references/perfetto-stdlib.md). Use this file as a reference to discover available modules, find schemas (columns and types) for specific tables or views, or determine the `INCLUDE PERFETTO MODULE` statements required before drafting SQL query.
|
||||
- **Execution Tool:** Queries are executed using the official `trace_processor` wrapper script downloaded directly from Perfetto. Output is returned in pure CSV format.
|
||||
|
||||
## Execution Protocol
|
||||
|
||||
You must follow these steps sequentially, mirroring a multi-agent pipeline:
|
||||
|
||||
### Step 0: Tool Setup
|
||||
|
||||
**Fetch the Wrapper:** You must use the top level of the current project workspace (`./trace_processor`).
|
||||
|
||||
> **CRITICAL GUARDRAIL:** NEVER use filesystem search tools (`find`, `find_by_name`, `grep`, `dir /s`, `Get-ChildItem`) across the home directory or workspace to locate `trace_processor` — unconstrained searches across entire workspaces will stop responding or time out.
|
||||
|
||||
Perform a direct file check at the top level of your workspace (e.g., `ls trace_processor`). If missing, download `https://get.perfetto.dev/trace_processor` directly into the root workspace (`curl -LO`), make it executable on macOS/Linux (`chmod +x`), and ensure `trace_processor` is added to `.gitignore`. Execute queries directly via `./trace_processor` (on Windows, explicitly invoke `python trace_processor`).
|
||||
|
||||
> **Important:** The file served at this URL is a `~10KB` Python wrapper script. Don't assume the download failed because it is human-readable text. This is the intended behavior. This script handles lazy-loading the precompiled binary automatically on its first run. Use it directly.
|
||||
|
||||
### Step 1: Dissection and Schema Research
|
||||
|
||||
1. Identify the core question, required data points, and filtering conditions.
|
||||
2. **Precedence Rule:** If the user's request contains a SQL query, use it **without modification** and skip to Step 2 for validation.
|
||||
3. **Mandatory Schema and Module Search:** For every table or view you plan to use, you MUST find its schema in [`perfetto-stdlib.md`](references/perfetto-stdlib.md). **Don't read the entire documentation file** --- it consumes the context window. Follow this precise workflow:
|
||||
- **Discovery and Search:** Use available search tools (`grep`, `read_file` or file search) with line limits to discover relevant views, tables or modules based on your problem domain and high-level intents (for example, 'CPU time', 'running time', 'overlap', 'jank').
|
||||
- **Why:** Searching solely for exact table names misses comprehensive, pre-computed views built for these analyses.
|
||||
- **Note:** You must verify if a Standard Library module already provides the needed abstraction before drafting manual arithmetic or custom functions.
|
||||
- **Targeted Bounded Reads:** Once you identify the relevant modules, efficiently read the tables and views within that module section.
|
||||
- **Extract:** Extract only the schema, columns, and the exact `INCLUDE
|
||||
PERFETTO MODULE` statements for the required object from the documentation.
|
||||
- **Verify:** Review the columns, types, and descriptions to ensure the table matches your needs.
|
||||
4. Print the research results before drafting the query:
|
||||
5. *Tables/Views:* `Schema for {name}:` listing columns and types.
|
||||
|
||||
### Step 2: Draft and Validate Loop (Max 3 Iterations)
|
||||
|
||||
Draft the SQL query in SQLite syntax using **only** the schemas retrieved in
|
||||
Step 1. After drafting, you must validate against this checklist:
|
||||
|
||||
- \[ \] **SQLite Syntax:** Does the query parse successfully without syntax errors?
|
||||
- \[ \] **Idempotency:** Are all object creations safe to re-run? (Did you use `CREATE OR REPLACE PERFETTO` and `DROP TABLE IF EXISTS` for virtual tables?)
|
||||
- \[ \] **Existence:** Were all tables found in the documentation?
|
||||
- \[ \] **Intent Check:** Is there a pre-existing standard library table or view that will fulfill this intent before instead of writing manual arithmetic?
|
||||
- \[ \] **Column Accuracy:** Do columns match the retrieved schemas?
|
||||
- \[ \] **Alias Check:** Are ALL column names prefixed with their table or view alias (for example, `alias.column_name`)?
|
||||
- \[ \] **Module Check:** Are `INCLUDE PERFETTO MODULE` statements included for all non-prelude modules? **You must use the exact module names provided in
|
||||
the documentation.**
|
||||
- \[ \] **Span Join Check:** If using `SPAN_JOIN`, are tables safely `PARTITIONED` to prevent overlapping interval crashes? Are intermediate tables materialized with `CREATE PERFETTO TABLE`?
|
||||
- \[ \] **No LIKE Constraint:** Did you map string matches using `GLOB` or `=` instead of prohibited `LIKE`?
|
||||
- \[ \] **Execution Check:** You MUST run queries using the standalone
|
||||
`./trace_processor` wrapper with the `--query-string` flag:
|
||||
`./trace_processor --query-string "QUERY" {trace_file}`.
|
||||
|
||||
**Execution Rules:**
|
||||
- **File Usage** : If you must create a SQL file to execute queries (for example, due to query length or escaping issues), you must create them in the `/tmp/` directory.
|
||||
- **State:** The execution is purely ephemeral. Database state does not persist across turns. You **cannot** share state (like views or tables) across queries in different turns. Every query must be standalone and fully self-contained.
|
||||
- **Failure Resilience:** Debug and fix SQL syntax and logic errors when query fails.Don't simplify the analytical intent to pass validation. For example, if requested to calculate an overlap or intersection, you must fix the intersection math. Don't substitute with disjoint queries (for example, returning independent total durations) as a workaround.
|
||||
|
||||
### Step 3: Final Output
|
||||
|
||||
1. Explicitly return and state the final validated SQL and explain the results to the user.
|
||||
2. Before finishing your response, delete all temporary SQL files you created in `/tmp/` directory.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
||||
---
|
||||
name: perfetto-trace-analysis
|
||||
description: Analyzes Perfetto traces to find the root cause of latency, memory, or
|
||||
jank issues in Android apps. Use when the user provides a Perfetto trace file and
|
||||
asks any question, ongoing investigation, or open-ended request to analyze its contents.
|
||||
license: Complete terms in LICENSE.txt
|
||||
metadata:
|
||||
author: Google LLC
|
||||
last-updated: '2026-05-14'
|
||||
keywords:
|
||||
- Perfetto
|
||||
- trace analysis
|
||||
- Android performance
|
||||
- debugging
|
||||
- profiling
|
||||
- jank
|
||||
- bottleneck
|
||||
- SQL
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- **Domain Hints:** Reference files for specific performance areas: [`CPU`](references/hints_cpu.md), [`Graphics`](references/hints_graphics.md), [`I/O`](references/hints_io.md), [`IPC`](references/hints_ipc.md), [`Memory`](references/hints_memory.md), [`Power`](references/hints_power.md). These files each contain multiple expert-vetted, powerful trace analysis techniques to steer and aid in the analysis.
|
||||
- **Perfetto SQL Reference:** Reference guidelines for translating intents into valid queries are located in [the SQL reference](references/sql.md). You must read this reference and follow its Execution Protocol for all SQL generation.
|
||||
|
||||
## Setup Phase (Mandatory)
|
||||
|
||||
1. **Initialize Scratchpad (Chain of Evidence):**
|
||||
- Maintain your working memory in a local scratchpad file located in the exact same directory as the target trace file.
|
||||
- Name the file using the trace's filename appended with `_analysis.md` (e.g., `[trace_filename]_analysis.md`). Before creating it, check if a file with that name already exists by listing the directory's contents---to avoid biasing your investigation, DO NOT read the file's contents to check for its existence. If it does, append an incrementing version number (e.g., `_v2.md`, `_v3.md`) until you find an available filename. You MUST hardcode this exact filename in all subsequent tool calls.
|
||||
- Use this scratchpad STRICTLY to log verified facts: timestamps, slice names, thread IDs (utid/tid), and thread states.
|
||||
- DO NOT write preliminary hypotheses or premature conclusions in the scratchpad. It is a strict Chain of Evidence.
|
||||
2. **Review Domain Hints:** Read the Domain Hints in each file to get a high-level overview of what techniques are possible. Make sure to use this baseline knowledge when researching and retrieving hints during the ongoing investigation.
|
||||
3. **Review SQL Reference:** Read the SQL reference in [`references/sql.md`](references/sql.md) and follow its Execution Protocol for all SQL generation. Do not guess schemas.
|
||||
4. **Target Resolution:** If the user's request is broad (e.g., "why is the app slow?") and doesn't specify a package name:
|
||||
- Execute a query to identify the active application: `sql INCLUDE
|
||||
PERFETTO MODULE android.startup.startups; SELECT package FROM
|
||||
android_startups;`
|
||||
- If multiple packages are returned, ask the user to choose one. Save the chosen `package_name` to your scratchpad.
|
||||
|
||||
## Investigation Protocol
|
||||
|
||||
Follow this iterative loop until you have isolated the definitive root cause(s):
|
||||
|
||||
### 1. Formulate Hypothesis
|
||||
|
||||
- **Prioritization:** Form hypotheses using information from: user prompt \> "Domain Hints" ([`CPU`](references/hints_cpu.md), [`Graphics`](references/hints_graphics.md), [`I/O`](references/hints_io.md), [`IPC`](references/hints_ipc.md), [`Memory`](references/hints_memory.md), [`Power`](references/hints_power.md)) \> general knowledge. Be sure to leverage these "Domain Hints" as they are expert-vetted analysis techniques.
|
||||
- **Source Attribution:** Explicitly mention the source of your hypothesis (e.g., "Based on hints_io.md...").
|
||||
- **Focus Constraint:** Focus on the primary bottleneck. Avoid investigating deep into binder transactions unless the user explicitly asks for it or there is no other obvious bottleneck.
|
||||
- **State Reasoning:** Briefly state your reasoning based on previous findings *before* generating a new query.
|
||||
|
||||
### 2. Plan and Collect Data
|
||||
|
||||
- **Metrics First:** Start with a high-level view using trace metrics before diving into custom SQL (e.g., `./trace_processor --run-metrics
|
||||
android_startup`).
|
||||
- **Broad to Narrow:** Begin with broad queries using minimal filters. Favor fuzzy matching (e.g., `GLOB '*abc*'`) over exact matching.
|
||||
- **Overlapping Time:** When filtering by time, you MUST check for events that overlap with the target time range (e.g., `start1 < end2 AND start2 < end1`) to ensure you don't miss slices that span across the boundaries.
|
||||
|
||||
### 3. Analyze and Drill Down (Depth-First)
|
||||
|
||||
- **Evidentiary Rigor:** Do not draw conclusions without explicit data.
|
||||
- **Wall Time vs. CPU Time:** Do not assume a long-running slice is actively computing. You MUST query the `thread_state` table for the exact timestamp window of suspicious slices to verify if the thread was `Running`, `Runnable` (waiting for CPU), or `Sleeping`/`Uninterruptible Sleep` (blocked).
|
||||
- **Follow Dependencies:** If a thread is blocked/waiting, you MUST find what it is waiting *for* (Binder, Lock, I/O, etc.). Cross process boundaries if necessary. You cannot conclude an investigation on a waiting thread without identifying the blocker.
|
||||
|
||||
### 4. Exhaustive Investigation (Do Not Give Up Early)
|
||||
|
||||
- **Multiple Bottlenecks:** Complex performance issues rarely have a single cause. Do NOT stop your investigation after finding the first anomaly. Even if you find a major bottleneck (e.g., emulator graphics lag), you MUST continue searching for other independent system-wide issues (e.g., lock contention, I/O stalls). To find other bottlenecks, search through the content of the "Domain Hints" files ([`CPU`](references/hints_cpu.md), [`Graphics`](references/hints_graphics.md), [`I/O`](references/hints_io.md), [`IPC`](references/hints_ipc.md), [`Memory`](references/hints_memory.md), [`Power`](references/hints_power.md)) to retrieve and leverage expert-vetted, powerful trace analysis techniques. Investigate each relevant hint with depth.
|
||||
- **Global Verification:** Periodically perform a system-wide query for the longest running slices (`ORDER BY slice.dur DESC`) and most frequent D-states to ensure your local investigation hasn't missed a massive, unrelated system stall.
|
||||
- **Persist Through Dead Ends:** If a hypothesis is disproven or a query returns empty, do not conclude. Pivot your focus, broaden your search constraints (fuzzy matching, wider time windows), and continue the mission.
|
||||
|
||||
## Final Report
|
||||
|
||||
Only when you have followed the entire chain of dependencies to the root
|
||||
cause(s) AND confirmed through exhaustive search that no other major bottlenecks
|
||||
exist: 1. Summarize your findings detailing the verified chain of evidence. 2.
|
||||
Conclude with: "This concludes the trace analysis. You can review the full chain
|
||||
of evidence in \[scratchpad_filename\]. Let me know if you would like me to drill
|
||||
down into any of these specific threads, or if you'd like help drafting a bug
|
||||
report."
|
||||
@@ -1,16 +0,0 @@
|
||||
- When debugging a long slice: Examine its thread states to understand what the thread was doing (running, sleeping, blocked on I/O).
|
||||
- When debugging a long slice for latency issues: Check if its duration is caused by one or more long-running child slices. Apply recursively.
|
||||
- When a thread is woken up but there's a delay before it runs, check the "IRQ" track for the corresponding CPU to see if an interrupt is the cause.
|
||||
- Check if kernel threads associated with hardware are running with real-time priority; if not, they can be preempted.
|
||||
- When analysis of the primary application package does not reveal the root cause, expand to all threads and processes. Search for other runnable threads on the same CPU or high-priority kernel threads.
|
||||
- When investigating app startup, use an SQL query to aggregate the reasons for uninterruptible sleep on the main thread.
|
||||
- Check the "cpu_frequency" counter for the CPU cores that ran the main process. Missing frequency data or stuck frequencies indicate a kernel-level bug in the governor.
|
||||
- Query raw "ftrace" events for logs related to the governor thread (e.g., "su_gov").
|
||||
- To find concurrency issues, search for critical threads (e.g., 'RenderThread') in a blocked state (thread_state.state = 'D') and join with scheduling data to find the waker.
|
||||
- Compare time spent in userspace functions vs kernel (slices with \[k\] prefix).
|
||||
- To detect a 'catch-up storm', look for threads with long gaps in thread_state/cpu_slice activity immediately followed by a high-density burst.
|
||||
- Quantify scheduler contention by calculating scheduling latency (measure duration of preceding 'Runnable' state using preceding_sched_slice_for_thread). Search for maximums and high percentiles (p95/p99).
|
||||
- If a task exhibits high scheduling latency, check if other CPUs were idle (running swapper or idle thread).
|
||||
- Check the cpu_id for key threads; if consistently scheduled on slower cores, it signals a potential performance gain by allowing them on big cores.
|
||||
- For a struggling thread, analyze 'Runnable' vs 'Running' state time. A large 'Runnable' time indicates CPU contention.
|
||||
- If a slice's wall duration increases but the percentage of 'Running' time is unchanged, it strongly suggests a lower CPU frequency. Check sched_switch to focus on the correct cores.
|
||||
@@ -1,11 +0,0 @@
|
||||
- When investigating UI jank, check for long-running slices on the main thread; if a slice like "ConstraintLayout.onMeasure" is taking a long time (e.g., \>8ms), it is a likely cause of the jank.
|
||||
- When a bitmap_write_to_parcel slice takes milliseconds instead of microseconds, investigate its children slices to see how time is spent. Long durations often point to suboptimal kernel operations like memory mapping (mmap) or unnecessary data zeroing.
|
||||
- For a high-level overview of graphics memory usage, track the 'gpu_mem_total' counter for a specific process (upid).
|
||||
- To find the largest graphics allocations, query the android_graphics_allocs table and sort by size_bytes in descending order. Compare its 'width' and 'height' against the device's display resolution.
|
||||
- To detect the "double memory cost" of an image existing on both CPU and GPU, look for a large buffer in android_graphics_allocs and a simultaneous CPU memory allocation of a similar size (rss_anon_bytes or heap_graph).
|
||||
- To find potentially costly intermediate render targets, look for large buffers in android_graphics_allocs where 'usage_bits' lack a 'COMPOSER_OVERLAY' flag.
|
||||
- To check if an allocation is actually presented on screen, correlate 'buffer_id' with SurfaceFlinger events.
|
||||
- To see if high graphics memory is causing performance issues, check the frame duration in 'actual_frame_timeline_frame'. Durations over vsync (e.g., 16.6ms) correlating with 'gpu_mem_total' spikes suggest memory pressure jank.
|
||||
- Within the main thread of a janky graphics application, look for frequent or long-running 'texture_upload' slices.
|
||||
- Analyze the duration of 'eglSwapBuffersWithDamageKHR' or similar buffer-swapping slices in the graphics rendering thread. Consistently long durations suggest a large "damage area" being redrawn every frame.
|
||||
- To identify UI jank, compare the 'actual_frame_timeline' against the 'expected_frame_timeline' on the main process; a significant deviation indicates missed frames.
|
||||
@@ -1,12 +0,0 @@
|
||||
- When debugging a long, uninterruptible sleep state related to I/O: Check for overlapping slices with "verity" in their name (dm-verity).
|
||||
- When a thread is stuck in an uninterruptible sleep with no blocked_function, look for other threads that might be holding the memory lock (e.g., "jit-thread-pool", memory mapping ops).
|
||||
- When analyzing a long uninterruptible sleep, check the "blocked_function" in the thread state details (from sched_blocked_reason ftrace event).
|
||||
- A lot of time spent in "do_page_fault" during app startup is a strong indicator of I/O contention.
|
||||
- For file integrity mechanisms like DM-Verity, search for events like dm_verity_fec_prefetch.
|
||||
- To find the specific kernel dependency of a stalled app thread, locate the thread in state 'D', then look for kworker or kernel threads that become runnable immediately after.
|
||||
- For app stalls caused by I/O, analyze the scheduling latency of the relevant kworker threads handling the request.
|
||||
- To find inefficient file I/O, query the syscall table for a high frequency of small, sequential read() or pread() syscalls on a single fd.
|
||||
- If a thread spends significant time in 'Uninterruptible Sleep', check if 'blocked_function' is 'page_cache_readahead'. Correlate waking timestamps with 'filemap_add_to_page_cache' ftrace events.
|
||||
- Aggregate counts of 'filemap_add_to_page_cache' grouping by 'inode' to find the specific file causing I/O pressure.
|
||||
- Inspect 'nr_sector' in 'block_rq_issue' ftrace events to understand file read-ahead size.
|
||||
- If an I/O issue disappears on subsequent launches, it's a 'cold start' problem (populating page cache).
|
||||
@@ -1,10 +0,0 @@
|
||||
- Look for multiple outbound binder transactions from the same process (system_server) that carry similar data to different destinations in a short time frame. This "binder storm" indicates a lack of multiplexing.
|
||||
- To trace data across processes, correlate slices using flow events by linking a slice's ID to flow.source_slice_id or flow.dest_slice_id.
|
||||
- To detect binder spam, query the binder_transaction table and group by thread ID (tid), service_name, and method_name to find high numbers of identical calls.
|
||||
- When high binder concurrency is found, identify the bottleneck server process by grouping transactions by server_upid.
|
||||
- To analyze the latency of a slow binder transaction, calculate the time spent outside the server by subtracting server_dur from the total dur in the binder_transaction table.
|
||||
- When a thread is suspected of binder spam, correlate its tid with the cpu_slice table to check for high CPU consumption.
|
||||
- To find the code responsible for binder spam, get the utid of the problematic thread and use it to query stack_profile_callsite.
|
||||
- To find the callers of a problematic function, filter stack_profile_callsite for frames mapping to it, then trace upwards using parent_id.
|
||||
- A long-running slice on one thread causally linked to a slice on another thread (e.g., binder from system_server to SystemUI) indicates a scheduling dependency bottleneck.
|
||||
- To find asynchronous operations that might cause UI jank, look for a binder transaction from a controlling process that returns quickly, followed by a long-running slice in the receiving process.
|
||||
@@ -1,15 +0,0 @@
|
||||
- To investigate low memory kills, look for the "lmk_kill_occurred" ftrace event; a high count indicates severe memory pressure.
|
||||
- When you see a burst of "lmk_kill_occurred" events, query for processes with high CPU wall_duration in the "sched_slice" table during the same time window. Runaway processes consuming CPU exacerbate memory pressure.
|
||||
- To understand a process's memory impact, inspect its "anon_rss" (Anonymous Resident Set Size) from memory counters like "mem.info".
|
||||
- Check system-wide memory stats for a high or rapidly increasing "swap_used" value.
|
||||
- To confirm memory thrashing, look for high CPU usage by the "kswapd" kernel thread.
|
||||
- To find the direct trigger for LMKD, look for "memory_pressure" trace events from Pressure Stall Information (PSI).
|
||||
- To detect 'lost' memory for processes using hardware accelerators (like a TPU), query the counter table for RssFile values. If it drops significantly while hardware is active, check for kswapd0 scheduling slices.
|
||||
- When analyzing memory shared with hardware, query the dma_heap_stat and dmabuf_total_size counters (or ion_total_size for older devices) for a more accurate picture than RSS.
|
||||
- Be aware that RssFile can over-report memory if the same physical page is mapped multiple times.
|
||||
- When investigating OOMs, establish a baseline by querying process memory counters (e.g., mem.rss) and compare the median and 95th percentile against its history.
|
||||
- If bitmaps are a major memory consumer, check for outliers in bitmap count by querying android.graphics.Bitmap instances across traces.
|
||||
- To find what is holding onto an object (like a bitmap), trace its retainer path back to a GC root by querying heap_graph_reference. Pay close attention to custom application classes.
|
||||
- Query the android_bitmaps table to check the width and height properties of bitmaps.
|
||||
- Check for the presence of software bitmaps, which consume memory on both the app heap and in graphics memory.
|
||||
- When analyzing bitmaps, look for duplicates by checking for multiple android.graphics.Bitmap objects with identical properties.
|
||||
@@ -1,7 +0,0 @@
|
||||
- When investigating overall battery drain, start by querying the power_rails track and summing the energy consumed (power_ma \* duration) for each rail name.
|
||||
- To check if a device is sleeping correctly during screen-off periods, query the suspend_state track; a lack of time in "suspended" state indicates a wakefulness problem.
|
||||
- To find the root cause of the device failing to suspend, query the kernel_wakelock track and aggregate the total duration for each wake lock name.
|
||||
- If a top kernel wake lock name contains "bt_" or "bcm" (e.g., bt_host_wake), cross-reference its timing with events in the bluetooth_scan_results track.
|
||||
- If the modem rail in power_rails shows high consumption, query the network_packets table and aggregate traffic volume by uid to identify the app.
|
||||
- When analyzing network traffic from a shared uid, use the socket_tag associated with network packets for granular attribution.
|
||||
- Convert impact into a common energy unit like milliwatt-hours (mWh) to compare the severity of different issues.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,122 +0,0 @@
|
||||
## Guidelines and Hints
|
||||
|
||||
- **Idempotency:** Ensure queries are idempotent to prevent "already exists" errors during multiple executions.
|
||||
- For Perfetto objects, always use `CREATE OR REPLACE`: `CREATE OR REPLACE
|
||||
PERFETTO TABLE`, `CREATE OR REPLACE PERFETTO VIEW`, `CREATE OR REPLACE
|
||||
PERFETTO FUNCTION`, `CREATE OR REPLACE PERFETTO MACRO`.
|
||||
- For SQLite Virtual Tables (such as `SPAN_JOIN`), `CREATE OR REPLACE` is not supported. Explicitly drop them first: `DROP TABLE IF EXISTS
|
||||
my_table; CREATE VIRTUAL TABLE my_table USING SPAN_JOIN(...);`
|
||||
- For standard SQLite indexes, prepend `DROP INDEX IF EXISTS index_name;`.
|
||||
- `SPAN_JOIN` will crash if intervals **within the same input table** overlap. Always use the `PARTITIONED {column}` (for example, `PARTITIONED upid`) clause to isolate intervals.
|
||||
- Intermediate tables fed into a `SPAN_JOIN` must be materialized using `CREATE PERFETTO TABLE`, not `CREATE VIEW`.
|
||||
- **Trace Boundaries (`dur = -1`):** Slices or thread states that don't finish before the trace ends are recorded with `dur = -1`. When calculating a bounding box (for example, `ts + dur`) or summing durations (`SUM(dur)`), handle incomplete durations using: `IIF(dur = -1, trace_end() - ts, dur)`.
|
||||
- **Robust State Transitions:** Avoid manual timestamp arithmetic (for example, `ts + dur = next.ts`) to join adjacent events. Rely on standard library modules (for example, `sched.runnable`, `linux.perf.counters`, `intervals.overlap`) which safely handle trace gaps and preemptions.
|
||||
- **Unique Identifiers:** When writing SQL queries in Perfetto, you must join tables using `utid` (unique thread ID) or `upid` (unique process ID) instead of the regular `tid` or `pid`. **Why it's useful** : The operating system recycles `TIDs` and `PIDs`, while `UTIDs` and `UPIDs` remain unique for the lifetime of the trace, which prevents incorrect joins.
|
||||
- **Safe Argument Extraction:** Use `EXTRACT_ARG(arg_set_id, 'key')` to extract dictionary or JSON-like properties from slices or tracks. Don't attempt string parsing.
|
||||
- **String Matching (Always use GLOB):** Use `GLOB` instead of `LIKE`. `LIKE` causes performance bottlenecks and treats underscores (`_`) as wildcards, leading to bugs.
|
||||
- **Exact matches:** Use `=`.
|
||||
- **Substring matches:** Use `GLOB` with `*` (for example, `name GLOB
|
||||
'*RenderThread*'`).
|
||||
- **Case-insensitive matches:** Use `LOWER(name) GLOB` and make sure the search string is fully lowercase (for example, `LOWER(name) GLOB
|
||||
'*renderthread*'`). Use this when dealing with inconsistent trace capitalization (for example, `WakeLock` versus `wakelock`).
|
||||
- **Calculating Time Overlaps:** To calculate the overlap duration between two
|
||||
time intervals `[start1, end1]` and `[start2, end2]`:
|
||||
|
||||
> **Precedence Rule:** Always prefer using `SPAN_JOIN` or standard library
|
||||
> functions (for example, `intervals.overlap`) to calculate overlaps
|
||||
> **between two different sets of intervals** . Avoid manual arithmetic if a
|
||||
> standard library feature or `SPAN_JOIN` can achieve the same result. Use
|
||||
> the following logic if no built-in alternative exists.
|
||||
1. **Condition:** The intervals overlap if `start1 < end2` and `start2 <
|
||||
end1`.
|
||||
2. **Duration:** The overlap duration is calculated as `MIN(end1, end2) -
|
||||
MAX(start1, start2)`
|
||||
|
||||
> **Important:** Incomplete Perfetto slices have a duration of -1
|
||||
> (`dur = -1`). Always calculate the effective end time using `ts +
|
||||
> IIF(dur = -1, trace_end() - ts, dur)` before applying this logic.
|
||||
- Query `android_thread_slices_for_all_startups` for app startup requests.
|
||||
|
||||
- Join `counter_track` with `counter` to get values of counter with a specific
|
||||
name.
|
||||
|
||||
- When querying for a CPU frequency counter, include the `linux.cpu.frequency`
|
||||
module and use the `cpu_frequency_counters` table.
|
||||
|
||||
- When looking for events around a specific timestamp, start with 100ms as the
|
||||
window size.
|
||||
|
||||
- Always prefix column names with table or view alias, that is:
|
||||
`{alias}.{column_name}`.
|
||||
|
||||
- To calculate the total time spent in slices matching a specific name pattern
|
||||
(for example, `*{name_pattern}*`), you must sum their durations. **Why it's
|
||||
useful** : This helps quantify the total impact of a specific function or
|
||||
feature on performance across multiple calls. Here is an example query (note
|
||||
the safe handling of incomplete slices): `sql SELECT count(*) as
|
||||
total_count, sum(IIF(slice.dur = -1, trace_end() - slice.ts, slice.dur)) /
|
||||
1000000.0 as total_dur_ms FROM slice WHERE slice.name GLOB
|
||||
'*{name_pattern}*';`
|
||||
|
||||
## Resources
|
||||
|
||||
- **Documentation:** The Perfetto Standard Library documentation is in [`perfetto-stdlib.md`](perfetto-stdlib.md). Use this file as a reference to discover available modules, find schemas (columns and types) for specific tables or views, or determine the `INCLUDE PERFETTO MODULE` statements required before drafting SQL query.
|
||||
- **Execution Tool:** Queries are executed using the official `trace_processor` wrapper script downloaded directly from Perfetto. Output is returned in pure CSV format.
|
||||
|
||||
## Execution Protocol
|
||||
|
||||
You must follow these steps sequentially, mirroring a multi-agent pipeline:
|
||||
|
||||
### Step 0: Tool Setup
|
||||
|
||||
**Fetch the Wrapper:** You must use the top level of the current project workspace (`./trace_processor`).
|
||||
|
||||
> **CRITICAL GUARDRAIL:** NEVER use filesystem search tools (`find`, `find_by_name`, `grep`, `dir /s`, `Get-ChildItem`) across the home directory or workspace to locate `trace_processor` — unconstrained searches across entire workspaces will stop responding or time out.
|
||||
|
||||
Perform a direct file check at the top level of your workspace (e.g., `ls trace_processor`). If missing, download `https://get.perfetto.dev/trace_processor` directly into the root workspace (`curl -LO`), make it executable on macOS/Linux (`chmod +x`), and ensure `trace_processor` is added to `.gitignore`. Execute queries directly via `./trace_processor` (on Windows, explicitly invoke `python trace_processor`).
|
||||
|
||||
> **Important:** The file served at this URL is a `~10KB` Python wrapper script. Don't assume the download failed because it is human-readable text. This is the intended behavior. This script handles lazy-loading the precompiled binary automatically on its first run. Use it directly.
|
||||
|
||||
### Step 1: Dissection and Schema Research
|
||||
|
||||
1. Identify the core question, required data points, and filtering conditions.
|
||||
2. **Precedence Rule:** If the user's request contains a SQL query, use it **without modification** and skip to Step 2 for validation.
|
||||
3. **Mandatory Schema and Module Search:** For every table or view you plan to use, you MUST find its schema in [`perfetto-stdlib.md`](perfetto-stdlib.md). **Don't read the entire documentation file** --- it consumes the context window. Follow this precise workflow:
|
||||
- **Discovery and Search:** Use available search tools (`grep`, `read_file` or file search) with line limits to discover relevant views, tables or modules based on your problem domain and high-level intents (for example, 'CPU time', 'running time', 'overlap', 'jank').
|
||||
- **Why:** Searching solely for exact table names misses comprehensive, pre-computed views built for these analyses.
|
||||
- **Note:** You must verify if a Standard Library module already provides the needed abstraction before drafting manual arithmetic or custom functions.
|
||||
- **Targeted Bounded Reads:** Once you identify the relevant modules, efficiently read the tables and views within that module section.
|
||||
- **Extract:** Extract only the schema, columns, and the exact `INCLUDE
|
||||
PERFETTO MODULE` statements for the required object from the documentation.
|
||||
- **Verify:** Review the columns, types, and descriptions to ensure the table matches your needs.
|
||||
4. Print the research results before drafting the query:
|
||||
5. *Tables/Views:* `Schema for {name}:` listing columns and types.
|
||||
|
||||
### Step 2: Draft and Validate Loop (Max 3 Iterations)
|
||||
|
||||
Draft the SQL query in SQLite syntax using **only** the schemas retrieved in
|
||||
Step 1. After drafting, you must validate against this checklist:
|
||||
|
||||
- \[ \] **SQLite Syntax:** Does the query parse successfully without syntax errors?
|
||||
- \[ \] **Idempotency:** Are all object creations safe to re-run? (Did you use `CREATE OR REPLACE PERFETTO` and `DROP TABLE IF EXISTS` for virtual tables?)
|
||||
- \[ \] **Existence:** Were all tables found in the documentation?
|
||||
- \[ \] **Intent Check:** Is there a pre-existing standard library table or view that will fulfill this intent before instead of writing manual arithmetic?
|
||||
- \[ \] **Column Accuracy:** Do columns match the retrieved schemas?
|
||||
- \[ \] **Alias Check:** Are ALL column names prefixed with their table or view alias (for example, `alias.column_name`)?
|
||||
- \[ \] **Module Check:** Are `INCLUDE PERFETTO MODULE` statements included for all non-prelude modules? **You must use the exact module names provided in
|
||||
the documentation.**
|
||||
- \[ \] **Span Join Check:** If using `SPAN_JOIN`, are tables safely `PARTITIONED` to prevent overlapping interval crashes? Are intermediate tables materialized with `CREATE PERFETTO TABLE`?
|
||||
- \[ \] **No LIKE Constraint:** Did you map string matches using `GLOB` or `=` instead of prohibited `LIKE`?
|
||||
- \[ \] **Execution Check:** You MUST run queries using the standalone
|
||||
`./trace_processor` wrapper with the `--query-string` flag:
|
||||
`./trace_processor --query-string "QUERY" {trace_file}`.
|
||||
|
||||
**Execution Rules:**
|
||||
- **File Usage** : If you must create a SQL file to execute queries (for example, due to query length or escaping issues), you must create them in the `/tmp/` directory.
|
||||
- **State:** The execution is purely ephemeral. Database state does not persist across turns. You **cannot** share state (like views or tables) across queries in different turns. Every query must be standalone and fully self-contained.
|
||||
- **Failure Resilience:** Debug and fix SQL syntax and logic errors when query fails.Don't simplify the analytical intent to pass validation. For example, if requested to calculate an overlap or intersection, you must fix the intersection math. Don't substitute with disjoint queries (for example, returning independent total durations) as a workaround.
|
||||
|
||||
### Step 3: Final Output
|
||||
|
||||
1. Explicitly return and state the final validated SQL and explain the results to the user.
|
||||
2. Before finishing your response, delete all temporary SQL files you created in `/tmp/` directory.
|
||||
Reference in New Issue
Block a user