tp: Stub of frames stdlib support
Implements basic frames timeline (Choreographer#doFrame, DrawFrames, actual and expected frame timeline) support in a nicely aggregated tables and implements basic per frame metrics as defined by go/android-performance-metrics-glossary docs.
Change-Id: I358dacdcb967a72538c351f110d2ed2392edc73e
diff --git a/Android.bp b/Android.bp
index 722da60..7f9dcb1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12319,6 +12319,8 @@
"src/trace_processor/perfetto_sql/stdlib/android/broadcasts.sql",
"src/trace_processor/perfetto_sql/stdlib/android/critical_blocking_calls.sql",
"src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql",
"src/trace_processor/perfetto_sql/stdlib/android/freezer.sql",
"src/trace_processor/perfetto_sql/stdlib/android/garbage_collection.sql",
"src/trace_processor/perfetto_sql/stdlib/android/input.sql",
diff --git a/BUILD b/BUILD
index d678720..3b56b52 100644
--- a/BUILD
+++ b/BUILD
@@ -2366,6 +2366,15 @@
],
)
+# GN target: //src/trace_processor/perfetto_sql/stdlib/android/frames:frames
+perfetto_filegroup(
+ name = "src_trace_processor_perfetto_sql_stdlib_android_frames_frames",
+ srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql",
+ ],
+)
+
# GN target: //src/trace_processor/perfetto_sql/stdlib/android/startup:startup
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_android_startup_startup",
@@ -2576,6 +2585,7 @@
name = "src_trace_processor_perfetto_sql_stdlib_stdlib",
deps = [
":src_trace_processor_perfetto_sql_stdlib_android_android",
+ ":src_trace_processor_perfetto_sql_stdlib_android_frames_frames",
":src_trace_processor_perfetto_sql_stdlib_android_startup_startup",
":src_trace_processor_perfetto_sql_stdlib_chrome_chrome_sql",
":src_trace_processor_perfetto_sql_stdlib_common_common",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index 87f8dec..6eda0bc 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -15,7 +15,10 @@
import("../../../../../gn/perfetto_sql.gni")
perfetto_sql_source_set("android") {
- deps = [ "startup" ]
+ deps = [
+ "frames",
+ "startup",
+ ]
sources = [
"anrs.sql",
"app_process_starts.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn
new file mode 100644
index 0000000..bc434d5
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("frames") {
+ sources = [
+ "per_frame_metrics.sql",
+ "timeline.sql",
+ ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql b/src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql
new file mode 100644
index 0000000..cda1361
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql
@@ -0,0 +1,197 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE time.conversion;
+INCLUDE PERFETTO MODULE android.frames.timeline;
+
+-- The amount by which each frame missed of hit its deadline. Positive if the
+-- deadline was not missed. Frames are considered janky if `overrun` is
+-- negative.
+-- Calculated as the difference between the end of the
+-- `expected_frame_timeline_slice` and `actual_frame_timeline_slice` for the
+-- frame.
+-- Availability: from S (API 31).
+-- For Googlers: more details in go/android-performance-metrics-glossary.
+CREATE PERFETTO TABLE android_frames_overrun(
+ -- Frame id.
+ frame_id INT,
+ -- Difference between `expected` and `actual` frame ends. Positive if frame
+ -- didn't miss deadline.
+ overrun INT
+) AS
+SELECT
+ frame_id,
+ (exp_slice.ts + exp_slice.dur) - (act_slice.ts + act_slice.dur) AS overrun
+FROM _distinct_from_actual_timeline_slice act
+JOIN _distinct_from_expected_timeline_slice exp USING (frame_id)
+JOIN slice act_slice ON (act.id = act_slice.id)
+JOIN slice exp_slice ON (exp.id = exp_slice.id);
+
+-- How much time did the frame's Choreographer callbacks take.
+CREATE PERFETTO TABLE android_frames_ui_time(
+ -- Frame id
+ frame_id INT,
+ -- UI time duration
+ ui_time INT
+) AS
+SELECT
+ frame_id,
+ dur AS ui_time
+FROM android_frames_choreographer_do_frame f
+JOIN slice USING (id);
+
+-- App Vsync delay for a frame. The time between the VSYNC-app signal and the
+-- start of Choreographer work.
+-- Calculated as time difference between the actual frame start (from
+-- `actual_frame_timeline_slice`) and start of the `Choreographer#doFrame`
+-- slice.
+-- NOTE: Sometimes because of data losses `app_vsync_delay` can be negative.
+-- The frames where it happens are filtered out.
+-- For Googlers: more details in go/android-performance-metrics-glossary.
+CREATE PERFETTO TABLE android_app_vsync_delay_per_frame(
+ -- Frame id
+ frame_id INT,
+ -- App VSYNC delay.
+ app_vsync_delay INT
+) AS
+-- As there can be multiple `DrawFrame` slices, the `frames_surface_slices`
+-- table contains multiple rows for the same `frame_id` which only differ on
+-- `draw_frame_id`. As we don't care about `draw_frame_id` we can just collapse
+-- them.
+WITH distinct_frames AS (
+ SELECT
+ frame_id,
+ do_frame_id,
+ actual_frame_timeline_id
+ FROM android_frames
+ GROUP BY 1
+)
+SELECT
+ frame_id,
+ act.ts - do_frame.ts AS app_vsync_delay
+FROM distinct_frames f
+JOIN slice act ON (f.actual_frame_timeline_id = act.id)
+JOIN slice do_frame ON (f.do_frame_id = do_frame.id)
+WHERE act.ts >= do_frame.ts;
+
+-- How much time did the frame take across the UI Thread + RenderThread.
+-- Calculated as sum of `app VSYNC delay` `Choreographer#doFrame` slice
+-- duration and summed durations of all `DrawFrame` slices associated with this
+-- frame.
+-- Availability: from N (API 24).
+-- For Googlers: more details in go/android-performance-metrics-glossary.
+CREATE PERFETTO TABLE android_cpu_time_per_frame(
+ -- Frame id
+ frame_id INT,
+ -- Difference between actual timeline of the frame and
+ -- `Choreographer#doFrame`. See `android_app_vsync_delay_per_frame` table for more details.
+ app_vsync_delay INT,
+ -- Duration of `Choreographer#doFrame` slice.
+ do_frame_dur INT,
+ -- Duration of `DrawFrame` slice. Summed duration of all `DrawFrame`
+ -- slices, if more than one. See `android_frames_draw_frame` for more details.
+ draw_frame_dur INT,
+ -- CPU time across the UI Thread + RenderThread.
+ cpu_time INT
+) AS
+WITH all_draw_frames AS (
+SELECT
+ frame_id,
+ SUM(dur) as draw_frame_dur
+FROM android_frames_draw_frame
+JOIN slice USING (id)
+GROUP BY frame_id
+),
+distinct_frames AS (
+ SELECT
+ frame_id,
+ do_frame_id,
+ actual_frame_timeline_id
+ FROM android_frames
+ GROUP BY 1
+)
+SELECT
+ frame_id,
+ app_vsync_delay,
+ do_frame.dur AS do_frame_dur,
+ draw_frame_dur,
+ app_vsync_delay + do_frame.dur + draw_frame_dur AS cpu_time
+FROM android_app_vsync_delay_per_frame
+JOIN all_draw_frames USING (frame_id)
+JOIN distinct_frames f USING (frame_id)
+JOIN slice do_frame ON (f.do_frame_id = do_frame.id);
+
+-- CPU time of frames which don't have `android_cpu_time_per_frame` available.
+-- Calculated as UI time of the frame + 5ms.
+-- For Googlers: more details in go/android-performance-metrics-glossary.
+CREATE PERFETTO TABLE _cpu_time_per_frame_fallback(
+ -- Frame id.
+ frame_id INT,
+ -- Estimated cpu time.
+ estimated_cpu_time INT
+) AS
+SELECT
+ frame_id,
+ ui_time + time_from_ms(5) AS estimated_cpu_time
+FROM android_frames_ui_time;
+
+CREATE PERFETTO TABLE _estimated_cpu_time_per_frame(
+ frame_id INT,
+ cpu_time INT
+) AS
+SELECT
+ frame_id,
+ IIF(r.cpu_time IS NULL, f.estimated_cpu_time, r.cpu_time) AS cpu_time
+FROM _cpu_time_per_frame_fallback f
+LEFT JOIN android_cpu_time_per_frame r USING (frame_id);
+
+-- Aggregated stats of the frame.
+--
+-- For Googlers: more details in go/android-performance-metrics-glossary.
+CREATE PERFETTO TABLE android_frame_stats(
+ -- Frame id.
+ frame_id INT,
+ -- The amount by which each frame missed of hit its deadline. See
+ -- `android_frames_overrun` for details.
+ overrun INT,
+ -- How much time did the frame take across the UI Thread + RenderThread.
+ cpu_time INT,
+ -- How much time did the frame's Choreographer callbacks take.
+ ui_time INT,
+ -- Was frame janky.
+ was_jank BOOL,
+ -- CPU time of the frame took over 20ms.
+ was_slow_frame BOOL,
+ -- CPU time of the frame took over 50ms.
+ was_big_jank BOOL,
+ -- CPU time of the frame took over 200ms.
+ was_huge_jank BOOL
+) AS
+SELECT
+ frame_id,
+ overrun,
+ cpu_time,
+ ui_time,
+ IIF(overrun < 0, 1, NULL) AS was_jank,
+ IIF(cpu_time > time_from_ms(20), 1, NULL) AS was_slow_frame,
+ IIF(cpu_time > time_from_ms(50), 1, NULL) AS was_big_jank,
+ IIF(cpu_time > time_from_ms(200), 1, NULL) AS was_huge_jank
+FROM android_frames_overrun
+JOIN android_frames_ui_time USING (frame_id)
+-- Because some frames might not have CPU time calculated properly (data loss
+-- or too old API), we will use fallback cpu time from
+-- `_cpu_time_per_frame_fallback`.
+JOIN _estimated_cpu_time_per_frame USING (frame_id);
+
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql b/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql
new file mode 100644
index 0000000..e6963b2
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql
@@ -0,0 +1,104 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- Parses the slice name to fetch `frame_id` from `slice` table.
+-- Use with caution. Slice names are a flaky source of ids and the resulting
+-- table might require some further operations.
+CREATE PERFETTO FUNCTION _get_frame_table_with_id(
+ -- String just before id.
+ glob_str STRING
+) RETURNS TABLE (
+ -- `slice.id` of the frame slice.
+ id INT,
+ -- Parsed frame id.
+ frame_id INT
+) AS
+WITH all_found AS (
+ SELECT
+ id,
+ cast_int!(STR_SPLIT(name, ' ', 1)) AS frame_id
+ FROM slice
+ WHERE name GLOB $glob_str
+)
+SELECT *
+FROM all_found
+-- Casting string to int returns 0 if the string can't be cast.
+WHERE frame_id != 0;
+
+-- All of the `Choreographer#doFrame` slices with their frame id.
+CREATE PERFETTO TABLE android_frames_choreographer_do_frame(
+ -- `slice.id`
+ id INT,
+ -- Frame id
+ frame_id INT
+) AS
+SELECT * FROM _get_frame_table_with_id('Choreographer#doFrame*');
+
+-- All of the `DrawFrame` slices with their frame id.
+-- There might be multiple DrawFrames slices for a single vsync (frame id).
+-- This happens when we are drawing multiple layers (e.g. status bar and
+-- notifications).
+CREATE PERFETTO TABLE android_frames_draw_frame(
+ -- `slice.id`
+ id INT,
+ -- Frame id
+ frame_id INT
+) AS
+SELECT * FROM _get_frame_table_with_id('DrawFrame*');
+
+-- `actual_frame_timeline_slice` returns the same slice on different tracks.
+-- We are getting the first slice with one frame id.
+CREATE PERFETTO TABLE _distinct_from_actual_timeline_slice AS
+SELECT
+ id,
+ cast_int!(name) AS frame_id
+FROM actual_frame_timeline_slice
+GROUP BY 2;
+
+-- `expected_frame_timeline_slice` returns the same slice on different tracks.
+-- We are getting the first slice with one frame id.
+CREATE PERFETTO TABLE _distinct_from_expected_timeline_slice AS
+SELECT
+ id,
+ cast_int!(name) AS frame_id
+FROM expected_frame_timeline_slice
+GROUP BY 2;
+
+-- All slices related to one frame. Aggregates `Choreographer#doFrame`,
+-- `DrawFrame`, `actual_frame_timeline_slice` and
+-- `expected_frame_timeline_slice` slices.
+CREATE PERFETTO TABLE android_frames(
+ -- Frame id.
+ frame_id INT,
+ -- `slice.id` of "Choreographer#doFrame" slice.
+ do_frame_id INT,
+ -- `slice.id` of "DrawFrame" slice.
+ draw_frame_id INT,
+ -- `slice.id` from `actual_frame_timeline_slice`
+ actual_frame_timeline_id INT,
+ -- `slice.id` from `expected_frame_timeline_slice`
+ expected_frame_timeline_id INT
+) AS
+SELECT
+ frame_id,
+ do_frame.id AS do_frame_id,
+ draw_frame.id AS draw_frame_id,
+ act.id AS actual_frame_timeline_id,
+ exp.id AS expected_frame_timeline_id
+FROM android_frames_choreographer_do_frame do_frame
+JOIN android_frames_draw_frame draw_frame USING (frame_id)
+JOIN _distinct_from_actual_timeline_slice act USING (frame_id)
+JOIN _distinct_from_expected_timeline_slice exp USING (frame_id)
+ORDER BY frame_id;
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index c3cde34..36872ed 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -46,21 +46,22 @@
from diff_tests.metrics.startup.tests_lock_contention import StartupLockContention
from diff_tests.metrics.startup.tests_metrics import StartupMetrics
from diff_tests.metrics.webview.tests import WebView
+from diff_tests.parser.android_fs.tests import AndroidFs
from diff_tests.parser.android.tests import AndroidParser
from diff_tests.parser.android.tests_bugreport import AndroidBugreport
from diff_tests.parser.android.tests_games import AndroidGames
+from diff_tests.parser.android.tests_protolog import ProtoLog
+from diff_tests.parser.android.tests_shell_transitions import ShellTransitions
from diff_tests.parser.android.tests_surfaceflinger_layers import SurfaceFlingerLayers
from diff_tests.parser.android.tests_surfaceflinger_transactions import SurfaceFlingerTransactions
-from diff_tests.parser.android.tests_shell_transitions import ShellTransitions
-from diff_tests.parser.android.tests_protolog import ProtoLog
-from diff_tests.parser.android_fs.tests import AndroidFs
from diff_tests.parser.atrace.tests import Atrace
from diff_tests.parser.atrace.tests_error_handling import AtraceErrorHandling
from diff_tests.parser.chrome.tests import ChromeParser
-from diff_tests.parser.chrome.tests_v8 import ChromeV8Parser
from diff_tests.parser.chrome.tests_memory_snapshots import ChromeMemorySnapshots
+from diff_tests.parser.chrome.tests_v8 import ChromeV8Parser
from diff_tests.parser.cros.tests import Cros
from diff_tests.parser.fs.tests import Fs
+from diff_tests.parser.ftrace.ftrace_crop_tests import FtraceCrop
from diff_tests.parser.fuchsia.tests import Fuchsia
from diff_tests.parser.graphics.tests import GraphicsParser
from diff_tests.parser.graphics.tests_drm_related_ftrace_events import GraphicsDrmRelatedFtraceEvents
@@ -91,22 +92,23 @@
from diff_tests.parser.translated_args.tests import TranslatedArgs
from diff_tests.parser.ufs.tests import Ufs
from diff_tests.stdlib.android.tests import AndroidStdlib
+from diff_tests.stdlib.android.frames_tests import Frames
from diff_tests.stdlib.chrome.chrome_stdlib_testsuites import CHROME_STDLIB_TESTSUITES
from diff_tests.stdlib.common.tests import StdlibCommon
from diff_tests.stdlib.common.tests import StdlibCommon
from diff_tests.stdlib.counters.tests import StdlibCounterIntervals
from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
-from diff_tests.stdlib.intervals.tests import StdlibIntervals
-from diff_tests.stdlib.intervals.intersect_tests import IntervalsIntersect
from diff_tests.stdlib.graphs.dominator_tree_tests import DominatorTree
from diff_tests.stdlib.graphs.search_tests import GraphSearchTests
+from diff_tests.stdlib.intervals.intersect_tests import IntervalsIntersect
+from diff_tests.stdlib.intervals.tests import StdlibIntervals
from diff_tests.stdlib.linux.tests import LinuxStdlib
from diff_tests.stdlib.memory.heap_graph_dominator_tree_tests import HeapGraphDominatorTree
from diff_tests.stdlib.pkvm.tests import Pkvm
from diff_tests.stdlib.prelude.math_functions_tests import PreludeMathFunctions
from diff_tests.stdlib.prelude.pprof_functions_tests import PreludePprofFunctions
-from diff_tests.stdlib.prelude.window_functions_tests import PreludeWindowFunctions
from diff_tests.stdlib.prelude.slices_tests import PreludeSlices
+from diff_tests.stdlib.prelude.window_functions_tests import PreludeWindowFunctions
from diff_tests.stdlib.sched.tests import StdlibSched
from diff_tests.stdlib.slices.tests import Slices
from diff_tests.stdlib.span_join.tests_left_join import SpanJoinLeftJoin
@@ -125,7 +127,6 @@
from diff_tests.tables.tests import Tables
from diff_tests.tables.tests_counters import TablesCounters
from diff_tests.tables.tests_sched import TablesSched
-from diff_tests.parser.ftrace.ftrace_crop_tests import FtraceCrop
sys.path.pop()
@@ -242,6 +243,7 @@
stdlib_tests = [
*AndroidStdlib(index_path, 'stdlib/android', 'AndroidStdlib').fetch(),
*DominatorTree(index_path, 'stdlib/graphs', 'DominatorTree').fetch(),
+ *Frames(index_path, 'stdlib/android', 'Frames').fetch(),
*GraphSearchTests(index_path, 'stdlib/graphs',
'GraphSearchTests').fetch(),
*StdlibCounterIntervals(index_path, 'stdlib/counters',
diff --git a/test/trace_processor/diff_tests/stdlib/android/frames_tests.py b/test/trace_processor/diff_tests/stdlib/android/frames_tests.py
new file mode 100644
index 0000000..687afde
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/android/frames_tests.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path
+from python.generators.diff_tests.testing import Csv, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Frames(TestSuite):
+
+ def test_android_frames_choreographer_do_frame(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.timeline;
+
+ SELECT * FROM android_frames_choreographer_do_frame;
+ """,
+ out=Csv("""
+ "id","frame_id"
+ 2,10
+ 15,20
+ 22,30
+ 35,40
+ 46,60
+ 55,90
+ 63,100
+ 73,110
+ 79,120
+ 87,130
+ 93,140
+ 99,145
+ 102,150
+ 108,160
+ 140,1000
+ """))
+
+ def test_android_frames_draw_frame(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.timeline;
+
+ SELECT * FROM android_frames_draw_frame;
+ """,
+ out=Csv("""
+ "id","frame_id"
+ 8,10
+ 16,20
+ 23,30
+ 41,40
+ 50,60
+ 57,90
+ 60,90
+ 66,100
+ 69,100
+ 74,110
+ 80,120
+ 89,130
+ 95,140
+ 100,145
+ 105,150
+ 109,160
+ 146,1000
+ """))
+
+ def test_android_frames(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.timeline;
+
+ SELECT * FROM android_frames;
+ """,
+ out=Csv("""
+ "frame_id","do_frame_id","draw_frame_id","actual_frame_timeline_id","expected_frame_timeline_id"
+ 10,2,8,1,0
+ 20,15,16,12,11
+ 30,22,23,21,20
+ 40,35,41,37,36
+ 60,46,50,48,47
+ 90,55,57,54,53
+ 90,55,60,54,53
+ 100,63,66,65,64
+ 100,63,69,65,64
+ 110,73,74,71,70
+ 120,79,80,78,77
+ 130,87,89,85,84
+ 140,93,95,94,91
+ 145,99,100,98,97
+ 150,102,105,104,103
+ 160,108,109,132,107
+ 1000,140,146,138,137
+ """))
+
+ def test_android_frames_overrun(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.per_frame_metrics;
+
+ SELECT * FROM android_frames_overrun;
+ """,
+ out=Csv("""
+ "frame_id","overrun"
+ 10,0
+ 20,-8000000
+ 30,-5000000
+ 40,-20000000
+ 60,10000000
+ 90,-3000000
+ 100,-2000000
+ 110,-41000000
+ 120,-41000000
+ 130,18000000
+ 140,-5600000
+ 145,0
+ 150,5000000
+ 160,-266000000
+ 190,0
+ 200,-16000000
+ 1000,-480000000
+ """))
+
+ def test_android_app_vsync_delay_per_frame(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.per_frame_metrics;
+
+ SELECT * FROM android_app_vsync_delay_per_frame;
+ """,
+ out=Csv("""
+ "frame_id","app_vsync_delay"
+ 10,0
+ 30,0
+ 40,0
+ 60,0
+ 90,0
+ 100,0
+ 110,0
+ 120,0
+ 140,100000
+ 150,500000
+ 160,270000000
+ 1000,0
+ """))
+
+ def test_android_cpu_time_per_frame(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.per_frame_metrics;
+
+ SELECT * FROM android_cpu_time_per_frame;
+ """,
+ out=Csv("""
+ "frame_id","app_vsync_delay","do_frame_dur","draw_frame_dur","cpu_time"
+ 10,0,5000000,1000000,6000000
+ 30,0,3000000,19000000,22000000
+ 40,0,13000000,7000000,20000000
+ 60,0,10000000,9000000,19000000
+ 90,0,15000000,8000000,23000000
+ 100,0,15000000,8000000,23000000
+ 110,0,15000000,2000000,17000000
+ 120,0,15000000,2000000,17000000
+ 140,100000,1500000,17000000,18600000
+ 150,500000,2000000,13800000,16300000
+ 160,270000000,2000000,1000000,273000000
+ 1000,0,100000000,150000000,250000000
+ """))
+
+ def test_android_frame_stats(self):
+ return DiffTestBlueprint(
+ trace=Path('../../metrics/graphics/android_jank_cuj.py'),
+ query="""
+ INCLUDE PERFETTO MODULE android.frames.per_frame_metrics;
+
+ SELECT * FROM android_frame_stats;
+ """,
+ out=Csv("""
+ "frame_id","overrun","cpu_time","ui_time","was_jank","was_slow_frame","was_big_jank","was_huge_jank"
+ 10,0,6000000,5000000,"[NULL]","[NULL]","[NULL]","[NULL]"
+ 20,-8000000,8000000,3000000,1,"[NULL]","[NULL]","[NULL]"
+ 30,-5000000,22000000,3000000,1,1,"[NULL]","[NULL]"
+ 40,-20000000,20000000,13000000,1,"[NULL]","[NULL]","[NULL]"
+ 60,10000000,19000000,10000000,"[NULL]","[NULL]","[NULL]","[NULL]"
+ 90,-3000000,23000000,15000000,1,1,"[NULL]","[NULL]"
+ 100,-2000000,23000000,15000000,1,1,"[NULL]","[NULL]"
+ 110,-41000000,17000000,15000000,1,"[NULL]","[NULL]","[NULL]"
+ 120,-41000000,17000000,15000000,1,"[NULL]","[NULL]","[NULL]"
+ 130,18000000,10000000,5000000,"[NULL]","[NULL]","[NULL]","[NULL]"
+ 140,-5600000,18600000,1500000,1,"[NULL]","[NULL]","[NULL]"
+ 145,0,25000000,20000000,"[NULL]",1,"[NULL]","[NULL]"
+ 150,5000000,16300000,2000000,"[NULL]","[NULL]","[NULL]","[NULL]"
+ 160,-266000000,273000000,2000000,1,1,1,1
+ 1000,-480000000,250000000,100000000,1,1,1,1
+ """))