Add metric for Android blocking calls during CUJ
The metric aggregates a set of blocking calls from processes main thread, scoped for each CUJ.
Bug: 255974834
Test: tools/diff_test_trace_processor.py out/linux_clang_release/trace_processor_shell --name-filter Graphics:android_blocking_calls_cuj --keep-input
Change-Id: I53a565b327367f83b477086185fb8f94b5b88efc
diff --git a/Android.bp b/Android.bp
index 4d69c9a..a4ae406 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4250,6 +4250,7 @@
genrule {
name: "perfetto_protos_perfetto_metrics_chrome_descriptor",
srcs: [
+ "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
"protos/perfetto/metrics/android/android_frame_timeline_metric.proto",
"protos/perfetto/metrics/android/android_trusty_workqueues.proto",
"protos/perfetto/metrics/android/batt_metric.proto",
@@ -4321,6 +4322,7 @@
genrule {
name: "perfetto_protos_perfetto_metrics_descriptor",
srcs: [
+ "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
"protos/perfetto/metrics/android/android_frame_timeline_metric.proto",
"protos/perfetto/metrics/android/android_trusty_workqueues.proto",
"protos/perfetto/metrics/android/batt_metric.proto",
@@ -9693,6 +9695,7 @@
srcs: [
"src/trace_processor/metrics/sql/android/android_batt.sql",
"src/trace_processor/metrics/sql/android/android_binder.sql",
+ "src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql",
"src/trace_processor/metrics/sql/android/android_camera.sql",
"src/trace_processor/metrics/sql/android/android_camera_unagg.sql",
"src/trace_processor/metrics/sql/android/android_cpu.sql",
@@ -9989,6 +9992,7 @@
"src/trace_processor/stdlib/android/battery.sql",
"src/trace_processor/stdlib/android/binder.sql",
"src/trace_processor/stdlib/android/process_metadata.sql",
+ "src/trace_processor/stdlib/android/slices.sql",
"src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql",
"src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql",
"src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql",
diff --git a/BUILD b/BUILD
index 49778a3..916fcd8 100644
--- a/BUILD
+++ b/BUILD
@@ -1524,6 +1524,7 @@
srcs = [
"src/trace_processor/metrics/sql/android/android_batt.sql",
"src/trace_processor/metrics/sql/android/android_binder.sql",
+ "src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql",
"src/trace_processor/metrics/sql/android/android_camera.sql",
"src/trace_processor/metrics/sql/android/android_camera_unagg.sql",
"src/trace_processor/metrics/sql/android/android_cpu.sql",
@@ -1882,6 +1883,7 @@
"src/trace_processor/stdlib/android/battery.sql",
"src/trace_processor/stdlib/android/binder.sql",
"src/trace_processor/stdlib/android/process_metadata.sql",
+ "src/trace_processor/stdlib/android/slices.sql",
],
)
@@ -3495,6 +3497,7 @@
perfetto_proto_library(
name = "protos_perfetto_metrics_android_protos",
srcs = [
+ "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
"protos/perfetto/metrics/android/android_frame_timeline_metric.proto",
"protos/perfetto/metrics/android/android_trusty_workqueues.proto",
"protos/perfetto/metrics/android/batt_metric.proto",
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 4fef3c0..cdce406 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -20,6 +20,7 @@
"source_set",
]
sources = [
+ "android_blocking_calls_cuj_metric.proto",
"android_frame_timeline_metric.proto",
"android_trusty_workqueues.proto",
"batt_metric.proto",
diff --git a/protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto b/protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
new file mode 100644
index 0000000..86a720b
--- /dev/null
+++ b/protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
+// Blocking calls inside Android CUJs. Shows count and total duration for each.
+message AndroidBlockingCallsCujMetric {
+ repeated Cuj cuj = 1;
+
+ // Next id: 7
+ message Cuj {
+ // ID of the CUJ that is unique within the trace.
+ optional int32 id = 1;
+
+ // Name of the CUJ, extracted from the CUJ jank or latency trace marker.
+ // For example SHADE_EXPAND_COLLAPSE from J<SHADE_EXPAND_COLLAPSE>.
+ optional string name = 2;
+
+ // Details about the process (uid, version, etc)
+ optional AndroidProcessMetadata process = 3;
+
+ // ts of the CUJ trace marker slice, in ns.
+ optional int64 ts = 4;
+
+ // dur of the CUJ trace marker slice, in ns.
+ optional int64 dur = 5;
+
+ // List of blocking calls on the process main thread.
+ repeated BlockingCall blocking_calls = 6;
+ }
+
+ // Blocking call on the main thread.
+ message BlockingCall {
+ // Name of the blocking call
+ optional string name = 1;
+ // Number of times it happened within the CUJ
+ optional int64 cnt = 2;
+ // Total duration within the CUJ
+ optional int64 total_dur_ms = 3;
+ }
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index db94258..ede0e0a 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -20,6 +20,7 @@
import "protos/perfetto/metrics/android/android_frame_timeline_metric.proto";
import "protos/perfetto/metrics/android/batt_metric.proto";
+import "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto";
import "protos/perfetto/metrics/android/cpu_metric.proto";
import "protos/perfetto/metrics/android/camera_metric.proto";
import "protos/perfetto/metrics/android/camera_unagg_metric.proto";
@@ -100,7 +101,7 @@
// Root message for all Perfetto-based metrics.
//
-// Next id: 49
+// Next id: 50
message TraceMetrics {
reserved 4, 10, 13, 14, 16, 19;
@@ -232,6 +233,9 @@
// Metrics for app deadline missed.
optional AndroidFrameTimelineMetric android_frame_timeline_metric = 47;
+ // Blocking calls (e.g. binder calls) for CUJs (important UI transitions).
+ optional AndroidBlockingCallsCujMetric android_blocking_calls_cuj_metric = 49;
+
// Demo extensions.
extensions 450 to 499;
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index b4956f8..cff1e81 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -47,6 +47,47 @@
// End of protos/perfetto/metrics/android/process_metadata.proto
+// Begin of protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
+
+// Blocking calls inside Android CUJs. Shows count and total duration for each.
+message AndroidBlockingCallsCujMetric {
+ repeated Cuj cuj = 1;
+
+ // Next id: 7
+ message Cuj {
+ // ID of the CUJ that is unique within the trace.
+ optional int32 id = 1;
+
+ // Name of the CUJ, extracted from the CUJ jank or latency trace marker.
+ // For example SHADE_EXPAND_COLLAPSE from J<SHADE_EXPAND_COLLAPSE>.
+ optional string name = 2;
+
+ // Details about the process (uid, version, etc)
+ optional AndroidProcessMetadata process = 3;
+
+ // ts of the CUJ trace marker slice, in ns.
+ optional int64 ts = 4;
+
+ // dur of the CUJ trace marker slice, in ns.
+ optional int64 dur = 5;
+
+ // List of blocking calls on the process main thread.
+ repeated BlockingCall blocking_calls = 6;
+ }
+
+ // Blocking call on the main thread.
+ message BlockingCall {
+ // Name of the blocking call
+ optional string name = 1;
+ // Number of times it happened within the CUJ
+ optional int64 cnt = 2;
+ // Total duration within the CUJ
+ optional int64 total_dur_ms = 3;
+ }
+}
+
+// End of protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
+
// Begin of protos/perfetto/metrics/android/android_frame_timeline_metric.proto
message AndroidFrameTimelineMetric {
@@ -1806,7 +1847,7 @@
// Root message for all Perfetto-based metrics.
//
-// Next id: 49
+// Next id: 50
message TraceMetrics {
reserved 4, 10, 13, 14, 16, 19;
@@ -1938,6 +1979,9 @@
// Metrics for app deadline missed.
optional AndroidFrameTimelineMetric android_frame_timeline_metric = 47;
+ // Blocking calls (e.g. binder calls) for CUJs (important UI transitions).
+ optional AndroidBlockingCallsCujMetric android_blocking_calls_cuj_metric = 49;
+
// Demo extensions.
extensions 450 to 499;
diff --git a/python/perfetto/experimental/slice_breakdown/breakdown.py b/python/perfetto/experimental/slice_breakdown/breakdown.py
index 13dd665..cd1404f 100644
--- a/python/perfetto/experimental/slice_breakdown/breakdown.py
+++ b/python/perfetto/experimental/slice_breakdown/breakdown.py
@@ -48,24 +48,13 @@
""")
tp.query("""
+ SELECT IMPORT('android.slices');
CREATE VIEW modded_names AS
SELECT
slice.id,
slice.depth,
slice.stack_id,
- CASE
- WHEN slice.name LIKE 'Choreographer#doFrame%'
- THEN 'Choreographer#doFrame'
- WHEN slice.name LIKE 'DrawFrames%'
- THEN 'DrawFrames'
- WHEN slice.name LIKE '/data/app%.apk'
- THEN 'APK load'
- WHEN slice.name LIKE 'OpenDexFilesFromOat%'
- THEN 'OpenDexFilesFromOat'
- WHEN slice.name LIKE 'Open oat file%'
- THEN 'Open oat file'
- ELSE slice.name
- END AS modded_name
+ ANDROID_STANDARDIZE_SLICE_NAME(slice.name) AS modded_name
FROM slice
""")
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index ffc882c..9b3d2e7 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/metrics.descriptor.sha1 b/python/perfetto/trace_processor/metrics.descriptor.sha1
index aff5d76..3d2e0ce 100644
--- a/python/perfetto/trace_processor/metrics.descriptor.sha1
+++ b/python/perfetto/trace_processor/metrics.descriptor.sha1
@@ -2,5 +2,5 @@
// SHA1(tools/gen_binary_descriptors)
// 6886b319e65925c037179e71a803b8473d06dc7d
// SHA1(protos/perfetto/metrics/metrics.proto)
-// 493e9c04a68437008b69b5b78cae53802b33c624
+// 5bc40b3771af22a3e705d03fa050f582935480b8
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index aff43fd..5fe8985 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -21,6 +21,7 @@
sources = [
"android_batt.sql",
"android_binder.sql",
+ "android_blocking_calls_cuj_metric.sql",
"android_camera.sql",
"android_camera_unagg.sql",
"android_cpu.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
new file mode 100644
index 0000000..a0aba28
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
@@ -0,0 +1,181 @@
+--
+-- Copyright 2023 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.
+
+-- Create the base table (`android_jank_cuj`) containing all completed CUJs
+-- found in the trace.
+-- This script will use the `android_jank_cuj_main_thread_cuj_boundary`,
+-- containing bounds of jank CUJs.
+SELECT RUN_METRIC('android/android_jank_cuj.sql');
+
+SELECT IMPORT('android.slices');
+
+-- Jank "J<*>" and latency "L<*>" cujs are put together in android_cujs table.
+-- They are computed separately as latency ones are slightly different, don't
+-- currently have the same way to be cancelled, and are not anchored to vsyncs.
+DROP TABLE IF EXISTS android_cujs;
+CREATE TABLE android_cujs AS
+WITH latency_cujs AS (
+ SELECT
+ ROW_NUMBER() OVER (ORDER BY ts) AS cuj_id,
+ process.upid AS upid,
+ process.name AS process_name,
+ process_metadata.metadata AS process_metadata,
+ -- Extracts "CUJ_NAME" from "L<CUJ_NAME>"
+ SUBSTR(slice.name, 3, LENGTH(slice.name) - 3) AS cuj_name,
+ ts,
+ dur,
+ ts + dur AS ts_end,
+ 'completed' AS state
+ FROM slice
+ JOIN process_track
+ ON slice.track_id = process_track.id
+ JOIN process USING (upid)
+ JOIN process_metadata USING (upid)
+ WHERE
+ slice.name GLOB 'L<*>'
+ AND dur > 0
+),
+all_cujs AS (
+ SELECT
+ cuj_id,
+ upid,
+ process_name,
+ process_metadata,
+ cuj_name,
+ tb.ts,
+ tb.dur,
+ tb.ts_end
+ FROM android_jank_cuj_main_thread_cuj_boundary tb
+ JOIN android_jank_cuj using (cuj_id)
+UNION
+ SELECT
+ cuj_id,
+ upid,
+ process_name,
+ process_metadata,
+ cuj_name,
+ ts,
+ dur,
+ ts_end
+ FROM latency_cujs
+)
+SELECT ROW_NUMBER() OVER (ORDER BY ts) AS cuj_id, *
+FROM all_cujs;
+
+
+DROP TABLE IF EXISTS android_blocking_calls_cuj_calls;
+CREATE TABLE android_blocking_calls_cuj_calls AS
+WITH all_main_thread_relevant_slices AS (
+ SELECT DISTINCT
+ ANDROID_STANDARDIZE_SLICE_NAME(s.name) AS name,
+ s.ts,
+ s.track_id,
+ s.dur,
+ s.id,
+ process.name AS process_name,
+ thread.utid,
+ process.upid
+ FROM slice s
+ JOIN thread_track ON s.track_id = thread_track.id
+ JOIN thread USING (utid)
+ JOIN process USING (upid)
+ JOIN android_cujs USING (upid) -- Keeps only slices in cuj processes.
+ WHERE
+ thread.is_main_thread AND (
+ s.name = 'measure'
+ OR s.name = 'layout'
+ OR s.name = 'configChanged'
+ OR s.name = 'Contending for pthread mutex'
+ OR s.name GLOB 'monitor contention with*'
+ OR s.name GLOB 'SuspendThreadByThreadId*'
+ OR s.name GLOB 'LoadApkAssetsFd*'
+ OR s.name GLOB '*binder transaction*'
+ OR s.name GLOB 'inflate*'
+ OR s.name GLOB 'Lock contention on*'
+ OR s.name GLOB '*CancellableContinuationImpl*'
+ OR s.name GLOB 'relayoutWindow*'
+ OR s.name GLOB 'ImageDecoder#decode*'
+ )
+),
+-- Now we have:
+-- (1) a list of slices from the main thread of each process
+-- (2) a list of android cuj with beginning, end, and process
+-- It's needed to:
+-- (1) assign a cuj to each slice. If there are multiple cujs going on during a
+-- slice, there needs to be 2 entries for that slice, one for each cuj id.
+-- (2) each slice needs to be trimmed to be fully inside the cuj associated
+-- (as we don't care about what's outside cujs)
+main_thread_slices_scoped_to_cujs AS (
+SELECT
+ s.id,
+ s.id AS slice_id,
+ s.track_id,
+ s.name,
+ max(s.ts, cuj.ts) AS ts,
+ min(s.ts + s.dur, cuj.ts_end) as ts_end,
+ min(s.ts + s.dur, cuj.ts_end) - max(s.ts, cuj.ts) AS dur,
+ cuj.cuj_id,
+ cuj.cuj_name,
+ s.process_name,
+ s.upid,
+ s.utid
+FROM all_main_thread_relevant_slices s
+ JOIN android_cujs cuj
+ -- only when there is an overlap
+ ON s.ts + s.dur > cuj.ts AND s.ts < cuj.ts_end
+ -- and are from the same process
+ AND s.upid = cuj.upid
+)
+SELECT
+ name,
+ COUNT(*) AS occurrences,
+ CAST(SUM(dur) / 1e6 AS INT) AS total_dur_ms,
+ upid,
+ cuj_id,
+ cuj_name,
+ process_name
+FROM
+ main_thread_slices_scoped_to_cujs
+GROUP BY name, upid, cuj_id, cuj_name, process_name
+ORDER BY cuj_id;
+
+
+DROP VIEW IF EXISTS android_blocking_calls_cuj_metric_output;
+CREATE VIEW android_blocking_calls_cuj_metric_output AS
+SELECT AndroidBlockingCallsCujMetric('cuj', (
+ SELECT RepeatedField(
+ AndroidBlockingCallsCujMetric_Cuj(
+ 'id', cuj_id,
+ 'name', cuj_name,
+ 'process', process_metadata,
+ 'ts', cuj.ts,
+ 'dur', cuj.dur,
+ 'blocking_calls', (
+ SELECT RepeatedField(
+ AndroidBlockingCallsCujMetric_BlockingCall(
+ 'name', b.name,
+ 'cnt', b.occurrences,
+ 'total_dur_ms', b.total_dur_ms
+ )
+ )
+ FROM android_blocking_calls_cuj_calls b
+ WHERE b.cuj_id = cuj.cuj_id and b.upid = cuj.upid
+ ORDER BY total_dur_ms DESC
+ )
+ )
+ )
+ FROM android_cujs cuj
+ ORDER BY cuj.cuj_id ASC
+));
diff --git a/src/trace_processor/stdlib/android/BUILD.gn b/src/trace_processor/stdlib/android/BUILD.gn
index ea6659e..844e4d7 100644
--- a/src/trace_processor/stdlib/android/BUILD.gn
+++ b/src/trace_processor/stdlib/android/BUILD.gn
@@ -20,5 +20,6 @@
"battery.sql",
"binder.sql",
"process_metadata.sql",
+ "slices.sql",
]
}
diff --git a/src/trace_processor/stdlib/android/slices.sql b/src/trace_processor/stdlib/android/slices.sql
new file mode 100644
index 0000000..7cd80e7
--- /dev/null
+++ b/src/trace_processor/stdlib/android/slices.sql
@@ -0,0 +1,45 @@
+--
+-- Copyright 2023 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.
+
+-- Some slice names have params in them. This functions removes them to make it
+-- possible to aggregate by name.
+-- Some examples are:
+-- - Lock/monitor contention slices. The name includes where the lock
+-- contention is in the code. That part is removed.
+-- - DrawFrames/ooFrame. The name also includes the frame number.
+-- - Apk/oat/dex loading: The name of the apk is removed
+--
+-- @arg name STRING Raw name of the slice
+-- @ret STRING Simplified name.
+SELECT CREATE_FUNCTION(
+ 'ANDROID_STANDARDIZE_SLICE_NAME(name STRING)',
+ 'STRING',
+ '
+ SELECT
+ CASE
+ WHEN $name GLOB "Lock contention on*" THEN "Lock contention on <...>"
+ WHEN $name GLOB "monitor contention with*" THEN "monitor contention with <...>"
+ WHEN $name GLOB "SuspendThreadByThreadId*" THEN "SuspendThreadByThreadId <...>"
+ WHEN $name GLOB "LoadApkAssetsFd*" THEN "LoadApkAssetsFd <...>"
+ WHEN $name GLOB "relayoutWindow*" THEN "relayoutWindow <...>"
+ WHEN $name GLOB "*CancellableContinuationImpl*" THEN "CoroutineContinuation"
+ WHEN $name GLOB "Choreographer#doFrame*" THEN "Choreographer#doFrame"
+ WHEN $name GLOB "DrawFrames*" THEN "DrawFrames"
+ WHEN $name GLOB "/data/app*.apk" THEN "APK load"
+ WHEN $name GLOB "OpenDexFilesFromOat*" THEN "OpenDexFilesFromOat"
+ WHEN $name GLOB "Open oat file*" THEN "Open oat file"
+ ELSE $name
+ END
+');
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
new file mode 100644
index 0000000..81e1700
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
@@ -0,0 +1,142 @@
+android_blocking_calls_cuj_metric {
+ cuj {
+ id: 1
+ name: "TEST_SYSUI_LATENCY_EVENT"
+ process {
+ name: "com.android.systemui"
+ uid: 10001
+ }
+ ts: 2000000
+ dur: 15000000
+ blocking_calls {
+ name: "binder transaction"
+ cnt: 4
+ total_dur_ms: 6
+ }
+ }
+ cuj {
+ id: 2
+ name: "TEST_LAUNCHER_LATENCY_EVENT"
+ process {
+ name: "com.google.android.apps.nexuslauncher"
+ uid: 10002
+ }
+ ts: 2000000
+ dur: 15000000
+ blocking_calls {
+ name: "binder transaction"
+ cnt: 4
+ total_dur_ms: 6
+ }
+ }
+ cuj {
+ id: 3
+ name: "CUJ_WITH_MANY_BLOCKING_CALLS"
+ process {
+ name: "com.google.android.third.process"
+ uid: 10003
+ }
+ ts: 2000000
+ dur: 150000000
+ blocking_calls {
+ name: "Contending for pthread mutex"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "CoroutineContinuation"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "ImageDecoder#decodeBitmap"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "ImageDecoder#decodeDrawable"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "LoadApkAssetsFd <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "Lock contention on <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "SuspendThreadByThreadId <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "binder transaction"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "configChanged"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "inflate"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "layout"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "measure"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "monitor contention with <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ blocking_calls {
+ name: "relayoutWindow <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ }
+ cuj {
+ id: 4
+ name: "OVERLAPPING_CUJ_1"
+ process {
+ name: "com.android.systemui"
+ uid: 10001
+ }
+ ts: 20000000
+ dur: 10000000
+ blocking_calls {
+ name: "monitor contention with <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ }
+ cuj {
+ id: 5
+ name: "OVERLAPPING_CUJ_2"
+ process {
+ name: "com.android.systemui"
+ uid: 10001
+ }
+ ts: 22000000
+ dur: 10000000
+ blocking_calls {
+ name: "monitor contention with <...>"
+ cnt: 1
+ total_dur_ms: 10
+ }
+ }
+}
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
new file mode 100755
index 0000000..7573b76
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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.
+
+from os import sys, path
+import synth_common
+
+# com.android.systemui
+SYSUI_PID = 1000
+# com.google.android.apps.nexuslauncher
+LAUNCHER_PID = 2000
+
+THIRD_PROCESS_PID = 3000
+
+# List of blocking calls
+blocking_call_names = [
+ 'monitor contention with something else', 'SuspendThreadByThreadId 123',
+ 'LoadApkAssetsFd 123', 'binder transaction',
+ 'inflate', 'Lock contention on thread list lock (owner tid: 1665)',
+ 'CancellableContinuationImpl#123', 'relayoutWindow*', 'measure', 'layout',
+ 'configChanged', 'Contending for pthread mutex',
+ 'ImageDecoder#decodeBitmap', 'ImageDecoder#decodeDrawable',
+ 'Should not be in the metric'
+]
+
+
+def add_main_thread_atrace(trace, ts, ts_end, buf, pid):
+ trace.add_atrace_begin(ts=ts, tid=pid, pid=pid, buf=buf)
+ trace.add_atrace_end(ts=ts_end, tid=pid, pid=pid)
+
+
+def add_async_trace(trace, ts, ts_end, buf, pid):
+ trace.add_atrace_async_begin(ts=ts, tid=pid, pid=pid, buf=buf)
+ trace.add_atrace_async_end(ts=ts_end, tid=pid, pid=pid, buf=buf)
+
+
+# Adds a set of predefined blocking calls in places near the cuj boundaries to
+# verify that only the portion inside the cuj is counted in the metric.
+def add_cuj_with_blocking_calls(trace, cuj_name, pid):
+ cuj_begin = 2_000_000
+ cuj_dur = 15_000_000
+ cuj_end = cuj_begin + cuj_dur
+ blocking_call_name = "binder transaction"
+
+ add_async_trace(trace, ts=cuj_begin, ts_end=cuj_end, buf=cuj_name, pid=pid)
+
+ # all outside, before cuj, shouldn't be counted
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_begin - 2_000_000,
+ ts_end=cuj_begin - 1_000_000,
+ buf=blocking_call_name,
+ pid=pid)
+
+ # mid inside, mid outside. Should account for half the time.
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_begin - 1_000_000,
+ ts_end=cuj_begin + 1_000_000,
+ buf=blocking_call_name,
+ pid=pid)
+
+ # completely inside
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_begin + 2_000_000,
+ ts_end=cuj_begin + 3_000_000,
+ buf=blocking_call_name,
+ pid=pid)
+
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_begin + 4_000_000,
+ ts_end=cuj_begin + 7_000_000,
+ buf=blocking_call_name,
+ pid=pid)
+
+ # mid inside, mid outside
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_end - 1_000_000,
+ ts_end=cuj_end + 1_000_000,
+ buf=blocking_call_name,
+ pid=pid)
+
+ # all outside, after cuj, shouldn't be counted/
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_end + 2_000_000,
+ ts_end=cuj_end + 3_000_000,
+ buf=blocking_call_name,
+ pid=pid)
+
+
+# Creates a cuj that contains one of each blocking call.
+def add_all_blocking_calls_in_cuj(trace, pid):
+ blocking_call_dur = 10_000_000
+ blocking_call_ts = 2_000_000
+
+ cuj_dur = len(blocking_call_names) * blocking_call_dur
+ add_async_trace(
+ trace,
+ ts=blocking_call_ts,
+ ts_end=blocking_call_ts + cuj_dur,
+ buf="L<CUJ_WITH_MANY_BLOCKING_CALLS>",
+ pid=pid)
+
+ for blocking_call in blocking_call_names:
+ add_main_thread_atrace(
+ trace,
+ ts=blocking_call_ts,
+ ts_end=blocking_call_ts + blocking_call_dur,
+ buf=blocking_call,
+ pid=pid)
+ blocking_call_ts += blocking_call_dur
+
+# Creates 2 overlapping cuj, and a blocking call that lasts for both of them.
+def add_overlapping_cujs_with_blocking_calls(trace, start_ts, pid):
+ add_async_trace(
+ trace,
+ ts=start_ts,
+ ts_end=start_ts + 10_000_000,
+ buf="L<OVERLAPPING_CUJ_1>",
+ pid=pid)
+ add_async_trace(
+ trace,
+ ts=start_ts + 2_000_000,
+ ts_end=start_ts + 12_000_000,
+ buf="L<OVERLAPPING_CUJ_2>",
+ pid=pid)
+
+ add_main_thread_atrace(
+ trace,
+ ts=start_ts,
+ ts_end=start_ts + 12_000_000,
+ buf=blocking_call_names[0],
+ pid=pid)
+
+
+def add_process(trace, package_name, uid, pid):
+ trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
+ trace.add_process(
+ pid=pid, ppid=0, cmdline=package_name, uid=uid)
+ trace.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")
+
+
+def setup_trace():
+ trace = synth_common.create_trace()
+ trace.add_packet()
+ add_process(trace, package_name="com.android.systemui", uid=10001,
+ pid=SYSUI_PID)
+ add_process(trace, package_name="com.google.android.apps.nexuslauncher",
+ uid=10002, pid=LAUNCHER_PID)
+ add_process(trace, package_name="com.google.android.third.process",
+ uid=10003, pid=THIRD_PROCESS_PID)
+ trace.add_ftrace_packet(cpu=0)
+ add_async_trace(trace, ts=0, ts_end=5, buf="J<IGNORED>", pid=SYSUI_PID)
+ return trace
+
+
+trace = setup_trace()
+
+add_cuj_with_blocking_calls(trace, "L<TEST_SYSUI_LATENCY_EVENT>", pid=SYSUI_PID)
+add_cuj_with_blocking_calls(trace, "L<TEST_LAUNCHER_LATENCY_EVENT>",
+ pid=LAUNCHER_PID)
+
+add_all_blocking_calls_in_cuj(trace, pid=THIRD_PROCESS_PID)
+
+add_overlapping_cujs_with_blocking_calls(trace, pid=SYSUI_PID,
+ start_ts=20_000_000)
+
+# Note that J<*> events are not tested here.
+# See test_android_blocking_calls_on_jank_cujs.
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
new file mode 100644
index 0000000..babc4da
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
@@ -0,0 +1,52 @@
+android_blocking_calls_cuj_metric {
+ cuj {
+ id: 1
+ name: "SHOULD_BE_IGNORED"
+ process {
+ name: "com.android.systemui"
+ uid: 10001
+ package {
+ package_name: "com.android.systemui"
+ apk_version_code: 1
+ debuggable: false
+ }
+ packages_for_uid {
+ package_name: "com.android.systemui"
+ apk_version_code: 1
+ debuggable: false
+ }
+ }
+ ts: 0
+ dur: 115000000
+ blocking_calls {
+ name: "binder transaction"
+ cnt: 11
+ total_dur_ms: 10
+ }
+ }
+ cuj {
+ id: 2
+ name: "SHADE_ROW_EXPAND"
+ process {
+ name: "com.android.systemui"
+ uid: 10001
+ package {
+ package_name: "com.android.systemui"
+ apk_version_code: 1
+ debuggable: false
+ }
+ packages_for_uid {
+ package_name: "com.android.systemui"
+ apk_version_code: 1
+ debuggable: false
+ }
+ }
+ ts: 0
+ dur: 802000000
+ blocking_calls {
+ name: "binder transaction"
+ cnt: 13
+ total_dur_ms: 68
+ }
+ }
+}
diff --git a/test/trace_processor/diff_tests/android/android_slice_standardization.out b/test/trace_processor/diff_tests/android/android_slice_standardization.out
new file mode 100644
index 0000000..2dd4186
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_slice_standardization.out
@@ -0,0 +1,12 @@
+"name"
+"APK load"
+"Choreographer#doFrame"
+"CoroutineContinuation"
+"DrawFrames"
+"LoadApkAssetsFd <...>"
+"Lock contention on <...>"
+"Open oat file"
+"OpenDexFilesFromOat"
+"SuspendThreadByThreadId <...>"
+"monitor contention with <...>"
+"relayoutWindow <...>"
diff --git a/test/trace_processor/diff_tests/android/android_slice_standardization.py b/test/trace_processor/diff_tests/android/android_slice_standardization.py
new file mode 100644
index 0000000..a0556df
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_slice_standardization.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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.
+
+from os import sys, path
+import synth_common
+
+SYSUI_PID = 1000
+
+trace = synth_common.create_trace()
+trace.add_packet()
+
+trace.add_package_list(ts=0, name="com.android.systemui", uid=SYSUI_PID,
+ version_code=1)
+trace.add_process(pid=SYSUI_PID, ppid=0, cmdline="com.android.systemui",
+ uid=SYSUI_PID)
+trace.add_ftrace_packet(cpu=0)
+
+
+slices_to_standardize = [
+ "Lock contention on thread list lock (owner tid: 1665)",
+ "monitor contention with owner BG Thread #1 (30) at",
+ "SuspendThreadByThreadId suspended Primes-1 id=19",
+ "LoadApkAssetsFd({ParcelFileDescriptor: java.io.FileDescriptor@340019d})",
+ "relayoutWindow#first=false/resize=false/vis=true/params=true/force=false",
+ "android.os.Handler: kotlinx.coroutines.CancellableContinuationImpl",
+ "Choreographer#doFrame 122932914",
+ "DrawFrames 122921845",
+ "/data/app/.../base.apk",
+ "OpenDexFilesFromOat(/data/app/.../base.apk)",
+ "Open oat file /data/misc/apexdata/com.android.art/dalvik-cache/boot.oat",
+]
+
+for name in slices_to_standardize:
+ trace.add_atrace_async_begin(ts=1_000_000, tid=SYSUI_PID, pid=SYSUI_PID,
+ buf=name)
+ trace.add_atrace_async_end(ts=2_000_000, tid=SYSUI_PID, pid=SYSUI_PID,
+ buf=name)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py
index 4127021..02c5664 100644
--- a/test/trace_processor/diff_tests/android/tests.py
+++ b/test/trace_processor/diff_tests/android/tests.py
@@ -196,3 +196,26 @@
trace=DataPath('android_binder_metric_trace.atr'),
query=Metric('android_binder'),
out=Path('android_binder_metric.out'))
+
+ def test_android_blocking_calls_cuj(self):
+ return DiffTestBlueprint(
+ trace=Path('android_blocking_calls_cuj_metric.py'),
+ query=Metric('android_blocking_calls_cuj_metric'),
+ out=Path('android_blocking_calls_cuj_metric.out'))
+
+ def test_android_blocking_calls_on_jank_cujs(self):
+ return DiffTestBlueprint(
+ trace=Path('../graphics/android_jank_cuj.py'),
+ query=Metric('android_blocking_calls_cuj_metric'),
+ out=Path('android_blocking_calls_on_jank_cuj_metric.out'))
+
+ def test_android_slices_standardization_for_aggregation(self):
+ return DiffTestBlueprint(
+ trace=Path('android_slice_standardization.py'),
+ query="""
+ SELECT IMPORT('android.slices');
+ SELECT ANDROID_STANDARDIZE_SLICE_NAME(slice.name) name
+ FROM slice
+ ORDER BY name;
+ """,
+ out=Path('android_slice_standardization.out'))