Querying ProfilingManager profiles is similar to querying regular Perfetto
profiles. Therefore, review Getting Started with PerfettoSQL for a guide on
how to query profiles.
An important distinction between regular Perfetto traces and ProfilingManager
traces is that ProfilingManager traces pass through a trace redactor. This
redactor removes information about other processes unrelated to your app for
privacy reasons.
Some queries from the Perfetto standard library are not usable on redacted
traces. This is because ProfilingManager only collects profiling data for your
app, not other processes. As a result, the queries you can use with
ProfilingManager are a smaller set than those for full system profiles
recorded using local Perfetto.
Even though the querying space is reduced, you can still use many PerfettoSQL queries and tables from the Perfetto Standard Library as-is, so we encourage you to try them.
We also recommend that you review Analyzing Android Traces to find ready-to-use queries that provide useful performance data without modification.
ProfilingManager sample queries
To simplify the querying journey, this section provides a list of queries that
work with ProfilingManager. You can use these queries directly or as examples
to build other queries.
Find the most duplicated slices
This query finds repeated slices in a trace and sorts them by how often they appear, showing the most duplicated ones first.
Finding duplicated work is a common way to find unnecessary work in a trace.
-- You only need to call this once in the session to create the function
DROP TABLE IF EXISTS find_duplicates;
CREATE PERFETTO FUNCTION find_duplicates(pattern STRING) RETURNS
TABLE(name STRING, count_slice LONG) AS SELECT name, COUNT(dur) as count_slice FROM slice WHERE name GLOB $pattern GROUP BY name HAVING COUNT(name) >= 2 ORDER BY count_slice DESC;
-- Subsequent calls can just use the function to find dupes
SELECT * FROM find_duplicates('*Text*')
Jank queries
Find slow frames
This query finds frames where your app takes too long to generate a frame,
assuming an expected frame rate of 60 Hz (16.6 ms). The dur is set to
16,660,000 because slice durations in Perfetto tables are stored in nanoseconds.
INCLUDE PERFETTO module android.frames.timeline;
SELECT * FROM android_frames WHERE dur > 16660000;
Find jank-causing frames
INCLUDE PERFETTO module android.frames.timeline;
SELECT * FROM actual_frame_timeline_slice WHERE jank_type = 'App Deadline Missed';
This query is useful to find locations where jank occurs in the trace because the app takes too long to generate a frame. This means that the UI thread failed to generate a frame. Under extreme circumstances, this could precede an ANR.
Find most duplicated objects
You can also query memory-related profiles, such as heap dumps, to perform more complex memory analyses.
INCLUDE PERFETTO MODULE android.memory.heap_graph.heap_graph_class_aggregation;
SELECT * FROM android_heap_graph_class_aggregation WHERE obj_count >= 2
ORDER BY obj_count DESC LIMIT 100
This query returns the top 100 duplicated objects. This can help you find objects that are instantiated multiple times, which might reveal opportunities for caching them or identify unintended duplicates.
Cold startup latency
You can also query for startups. This section provides a more elaborate query to estimate cold startup time in a trace.
-- This function finds slices that match the given GLOB $pattern
CREATE OR REPLACE FUNCTION find_slices(pattern STRING) RETURNS
TABLE (name STRING, ts LONG, dur LONG) AS
SELECT name,ts,dur FROM slice WHERE name GLOB $pattern;
-- This function generates a slice that starts at $startSlicePattern and finishes at the slice matched by $endSlicePattern. If $inclusive is true, then the end slice dur will be added, otherwise, the end slice start time will be used.
CREATE OR REPLACE PERFETTO FUNCTION generate_start_to_end_slices(startSlicePattern STRING, endSlicePattern STRING, inclusive BOOL) RETURNS
TABLE(name STRING, ts LONG, dur LONG) AS
SELECT name, ts, MIN(startToEndDur) as dur
FROM
(SELECT S.name as name, S.ts as ts, E.ts + IIF($inclusive, E.dur, 0) - S.ts as startToEndDur
FROM find_slices($startSlicePattern) as S CROSS JOIN find_slices($endSlicePattern) as E
WHERE startToEndDur > 0)
GROUP BY name, ts;
-- Using these functions we can estimate cold startup time by generating a slice between bindApplication and first frame.
SELECT * from generate_start_to_end_slices('bindApplication','*Choreographer#doFrame [0-9]*', true)
This query generates a slice that represents the time between two slices that
define the startup time: bindApplication (typically found at the start of a
cold app launch) and the first Choreographer#doFrame slice (the first
generated frame). This metric effectively estimates cold startup TTFF (time to
first frame).