Add a plugin for Desktop Windowing statsd atoms.
Parse desktop window events into a stdlib table, and
create a plugin that registers a command that will
create a timeline track and pin it.
Bug: 370730027
Test: tools/diff_test_trace_processor.py out/linux/trace_processor_shell --name-filter '.*desktop_mode.*'
Change-Id: Ic8e20529b802c3a25fb0c6858800048f75beb2fc
diff --git a/Android.bp b/Android.bp
index f724ada..861d8ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13532,6 +13532,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/broadcasts.sql",
"src/trace_processor/perfetto_sql/stdlib/android/cpu/cluster_type.sql",
"src/trace_processor/perfetto_sql/stdlib/android/critical_blocking_calls.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql",
"src/trace_processor/perfetto_sql/stdlib/android/device.sql",
"src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql",
"src/trace_processor/perfetto_sql/stdlib/android/frames/jank_type.sql",
diff --git a/BUILD b/BUILD
index efe83c8..19d13b1 100644
--- a/BUILD
+++ b/BUILD
@@ -2814,6 +2814,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/binder_breakdown.sql",
"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/desktop_mode.sql",
"src/trace_processor/perfetto_sql/stdlib/android/device.sql",
"src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql",
"src/trace_processor/perfetto_sql/stdlib/android/freezer.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index 40320fa..14a1560 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -33,6 +33,7 @@
"binder_breakdown.sql",
"broadcasts.sql",
"critical_blocking_calls.sql",
+ "desktop_mode.sql",
"device.sql",
"dvfs.sql",
"freezer.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql b/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql
new file mode 100644
index 0000000..de4729b
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql
@@ -0,0 +1,72 @@
+--
+-- 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 android.statsd;
+
+-- Desktop Windows with durations they were open.
+CREATE PERFETTO TABLE android_desktop_mode_windows (
+-- Window add timestamp; NULL if no add event in the trace.
+raw_add_ts INT,
+-- Window remove timestamp; NULL if no remove event in the trace.
+raw_remove_ts INT,
+-- timestamp that the window was added; or trace_start() if no add event in the trace.
+ts INT,
+-- duration the window was open; or until trace_end() if no remove event in the trace.
+dur INT,
+-- Desktop Window instance ID - unique per window.
+instance_id INT,
+-- UID of the app running in the window.
+uid INT
+) AS
+WITH
+ atoms AS (
+ SELECT
+ ts,
+ extract_arg(arg_set_id, 'field_1') AS type,
+ extract_arg(arg_set_id, 'field_2') AS instance_id,
+ extract_arg(arg_set_id, 'field_3') AS uid
+ FROM android_statsd_atoms
+ WHERE name = 'atom_819'),
+ dw_statsd_events_add AS (
+ SELECT *
+ FROM atoms
+ WHERE type = 1),
+ dw_statsd_events_remove AS (
+ SELECT * FROM atoms
+ WHERE type = 2),
+ dw_statsd_events_update_by_instance AS (
+ SELECT instance_id, min(uid) AS uid FROM atoms
+ WHERE type = 3 GROUP BY instance_id),
+ dw_windows AS (
+ SELECT
+ a.ts AS raw_add_ts,
+ r.ts AS raw_remove_ts,
+ ifnull(a.ts, trace_start()) AS ts, -- Assume trace_start() if no add event found.
+ ifnull(r.ts, trace_end()) - ifnull(a.ts, trace_start()) AS dur, -- Assume trace_end() if no remove event found.
+ ifnull(a.instance_id, r.instance_id) AS instance_id,
+ ifnull(a.uid, r.uid) AS uid
+ FROM dw_statsd_events_add a
+ FULL JOIN dw_statsd_events_remove r ON a.instance_id = r.instance_id),
+ -- Assume window was open for the entire trace if we only see change events for the instance ID.
+ dw_windows_with_update_events AS (
+ SELECT * FROM dw_windows
+ UNION
+ SELECT NULL, NULL, trace_start(), trace_end() - trace_start(), instance_id, uid
+ FROM dw_statsd_events_update_by_instance
+ WHERE
+ instance_id NOT IN (SELECT instance_id FROM dw_windows))
+SELECT * FROM dw_windows_with_update_events;
+
diff --git a/test/data/android_desktop_mode/multiple_window_only_update.pb.sha256 b/test/data/android_desktop_mode/multiple_window_only_update.pb.sha256
new file mode 100644
index 0000000..939c986
--- /dev/null
+++ b/test/data/android_desktop_mode/multiple_window_only_update.pb.sha256
@@ -0,0 +1 @@
+de2c99d57ebec6ab843bc56047eb12dbedc3d08a8a1d989d9b3302ed30057286
\ No newline at end of file
diff --git a/test/data/android_desktop_mode/multiple_windows_add_update_remove.pb.sha256 b/test/data/android_desktop_mode/multiple_windows_add_update_remove.pb.sha256
new file mode 100644
index 0000000..5866d07
--- /dev/null
+++ b/test/data/android_desktop_mode/multiple_windows_add_update_remove.pb.sha256
@@ -0,0 +1 @@
+0967fdf494167fe47e5ddf4c540fcc7cc0b22e36ddafa4b4c1172fe3c171b3d6
\ No newline at end of file
diff --git a/test/data/android_desktop_mode/single_window_add_update_no_remove.pb.sha256 b/test/data/android_desktop_mode/single_window_add_update_no_remove.pb.sha256
new file mode 100644
index 0000000..f4656e4
--- /dev/null
+++ b/test/data/android_desktop_mode/single_window_add_update_no_remove.pb.sha256
@@ -0,0 +1 @@
+0bb09b4c68a125a09b6856879af92cd47dd980c2dcaa1513c370b9b6a7b575d1
\ No newline at end of file
diff --git a/test/data/android_desktop_mode/single_window_add_update_remove.pb.sha256 b/test/data/android_desktop_mode/single_window_add_update_remove.pb.sha256
new file mode 100644
index 0000000..3496183
--- /dev/null
+++ b/test/data/android_desktop_mode/single_window_add_update_remove.pb.sha256
@@ -0,0 +1 @@
+bab0c50523ac903872cd11f3fdd4a9ecda98e937573d17011eb877636e82c3c3
\ No newline at end of file
diff --git a/test/data/android_desktop_mode/single_window_no_add_update_remove.pb.sha256 b/test/data/android_desktop_mode/single_window_no_add_update_remove.pb.sha256
new file mode 100644
index 0000000..3c01fec
--- /dev/null
+++ b/test/data/android_desktop_mode/single_window_no_add_update_remove.pb.sha256
@@ -0,0 +1 @@
+7f63f17b65a0fca4bc5ccd75c9a7eb854779585b20cb91fcf371e7892642327e
\ No newline at end of file
diff --git a/test/data/android_desktop_mode/single_window_only_update.pb.sha256 b/test/data/android_desktop_mode/single_window_only_update.pb.sha256
new file mode 100644
index 0000000..20f7be3
--- /dev/null
+++ b/test/data/android_desktop_mode/single_window_only_update.pb.sha256
@@ -0,0 +1 @@
+ee1172b8ad0eaf856e2776575b861f44663bb98f1d40d7d4e66ee0f532220568
\ 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 f13fbb6..9062b8e 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -108,6 +108,7 @@
from diff_tests.parser.ufs.tests import Ufs
from diff_tests.parser.zip.tests import Zip
from diff_tests.stdlib.android.cpu_cluster_tests import CpuClusters
+from diff_tests.stdlib.android.desktop_mode_tests import DesktopMode
from diff_tests.stdlib.android.frames_tests import Frames
from diff_tests.stdlib.android.gpu import AndroidGpu
from diff_tests.stdlib.android.heap_graph_tests import HeapGraph
@@ -295,6 +296,7 @@
*AndroidGpu(index_path, 'stdlib/android', 'AndroidGpu').fetch(),
*AndroidStdlib(index_path, 'stdlib/android', 'AndroidStdlib').fetch(),
*CpuClusters(index_path, 'stdlib/android', 'CpuClusters').fetch(),
+ *DesktopMode(index_path, 'stdlib/android', 'DesktopMode').fetch(),
*LinuxCpu(index_path, 'stdlib/linux/cpu', 'LinuxCpu').fetch(),
*LinuxTests(index_path, 'stdlib/linux', 'LinuxTests').fetch(),
*DominatorTree(index_path, 'stdlib/graphs', 'DominatorTree').fetch(),
diff --git a/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py b/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py
new file mode 100644
index 0000000..0ef52a4
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py
@@ -0,0 +1,96 @@
+#!/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 DataPath
+from python.generators.diff_tests.testing import Csv
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+class DesktopMode(TestSuite):
+
+ def test_android_desktop_mode_windows_statsd_events(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/single_window_add_update_remove.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ 1112172132337,1115098491388,1112172132337,2926359051,22,10211
+ """))
+
+ def test_android_desktop_mode_windows_statsd_events_multiple_windows(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/multiple_windows_add_update_remove.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ 1340951146935,1347096280320,1340951146935,6145133385,24,10211
+ 1342507511641,1345461733688,1342507511641,2954222047,26,10183
+ """))
+
+ def test_android_desktop_mode_windows_statsd_events_add_no_remove(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/single_window_add_update_no_remove.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ 1552558346094,"[NULL]",1552558346094,1620521485,27,10211
+ """))
+
+ def test_android_desktop_mode_windows_statsd_events_no_add_update_remove(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/single_window_no_add_update_remove.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ "[NULL]",1696520389866,1695387563286,1132826580,29,10211
+ """))
+
+ def test_android_desktop_mode_windows_statsd_events_only_update(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/single_window_only_update.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ "[NULL]","[NULL]",1852548597746,3663403770,31,10211
+ """))
+
+ def test_android_desktop_mode_windows_statsd_events_multiple_windows_update_only(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/multiple_window_only_update.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ "[NULL]","[NULL]",2137135290268,4737314089,33,10211
+ "[NULL]","[NULL]",2137135290268,4737314089,35,10183
+ """))
+
diff --git a/ui/src/plugins/dev.perfetto.AndroidDesktopMode/OWNERS b/ui/src/plugins/dev.perfetto.AndroidDesktopMode/OWNERS
new file mode 100644
index 0000000..d0be0cc
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.AndroidDesktopMode/OWNERS
@@ -0,0 +1,2 @@
+benm@google.com
+
diff --git a/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts b/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts
new file mode 100644
index 0000000..c4fead2
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts
@@ -0,0 +1,81 @@
+// 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 {
+ SimpleSliceTrack,
+ SimpleSliceTrackConfig,
+} from '../../frontend/simple_slice_track';
+import {PerfettoPlugin, PluginDescriptor} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {TrackNode} from '../../public/workspace';
+
+const INCLUDE_DESKTOP_MODULE_QUERY = `INCLUDE PERFETTO MODULE android.desktop_mode`;
+
+const QUERY = `
+SELECT
+ ROW_NUMBER() OVER (ORDER BY ts) AS id,
+ ts,
+ dur,
+ ifnull(p.package_name, 'uid=' || dw.uid) AS name
+FROM android_desktop_mode_windows dw
+LEFT JOIN package_list p ON CAST (dw.uid AS INT) % 100000 = p.uid AND p.uid != 1000
+`;
+
+const COLUMNS = ['id', 'ts', 'dur', 'name'];
+const TRACK_NAME = 'Desktop Mode Windows';
+const TRACK_URI = '/desktop_windows';
+
+class AndroidDesktopMode implements PerfettoPlugin {
+ async onTraceReady(_ctx: Trace): Promise<void> {
+ await _ctx.engine.query(INCLUDE_DESKTOP_MODULE_QUERY);
+ this.registerTrack(
+ _ctx,
+ QUERY,
+ );
+ _ctx.commands.registerCommand({
+ id: 'dev.perfetto.DesktopMode#AddTrackDesktopWindowss',
+ name: 'Add Track: ' + TRACK_NAME,
+ callback: () => this.addSimpleTrack(_ctx),
+ });
+ }
+
+ registerTrack(_ctx: Trace, sql: string) {
+ const config: SimpleSliceTrackConfig = {
+ data: {
+ sqlSource: sql,
+ columns: COLUMNS,
+ },
+ columns: {ts: 'ts', dur: 'dur', name: 'name'},
+ argColumns: [],
+ };
+ const track = new SimpleSliceTrack(_ctx, {trackUri: TRACK_URI}, config);
+ _ctx.tracks.registerTrack({
+ uri: TRACK_URI,
+ title: TRACK_NAME,
+ track,
+ });
+ }
+
+ addSimpleTrack(_ctx: Trace) {
+ const trackNode = new TrackNode({uri: TRACK_URI, title: TRACK_NAME});
+ _ctx.workspace.addChildInOrder(trackNode);
+ trackNode.pin();
+ }
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: 'dev.perfetto.AndroidDesktopMode',
+ plugin: AndroidDesktopMode,
+};
+