[CUI] Add SQL model and custom track for CUI page loads.
* Introduce two new data models
- chrome_page_loads - stores fcp/lcp stats
- chrome_critical_user_actions - stores all CUIs in a
single table.
* Adds new diff tests for the new data model (fcl/lcp only)
This will expand as more CUI metrics are added.
* Add a new CUI track with details panel to the UI, hidden
behind a flag. This track summarizes the navigation duration
based on fcp and lcp timings only.
https://screenshot.googleplex.com/C6BLruh9P6vkCPz
https://screenshot.googleplex.com/4WTpwsDhrd9ZNJr
Bug: b/303579550
Change-Id: I38bc341f2f5306060366b71dd23c8820fa805b65
diff --git a/Android.bp b/Android.bp
index bfa3740..1b8103f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11864,7 +11864,9 @@
"src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scrolls.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/histograms.sql",
+ "src/trace_processor/perfetto_sql/stdlib/chrome/interactions.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/metadata.sql",
+ "src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_intervals.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_v3.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_v3_cause.sql",
diff --git a/BUILD b/BUILD
index 1ba49a5..1315915 100644
--- a/BUILD
+++ b/BUILD
@@ -2242,7 +2242,9 @@
"src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scrolls.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/histograms.sql",
+ "src/trace_processor/perfetto_sql/stdlib/chrome/interactions.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/metadata.sql",
+ "src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/speedometer.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/tasks.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/vsync_intervals.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/chrome/BUILD.gn
index cac258f..04a1571 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/BUILD.gn
@@ -20,7 +20,9 @@
"chrome_scrolls.sql",
"cpu_powerups.sql",
"histograms.sql",
+ "interactions.sql",
"metadata.sql",
+ "page_loads.sql",
"speedometer.sql",
"tasks.sql",
"vsync_intervals.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/interactions.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/interactions.sql
new file mode 100644
index 0000000..49ce0c8
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/interactions.sql
@@ -0,0 +1,46 @@
+-- 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.
+
+-- This file specifies common metrics/tables for critical user interactions. It
+-- is expected to be in flux as metrics are added across different CUI types.
+-- Currently we only track Chrome page loads and their associated metrics.
+
+INCLUDE PERFETTO MODULE chrome.page_loads;
+
+-- All critical user interaction events, including type and table with
+-- associated metrics.
+--
+-- @column scoped_id Identifier of the interaction; this is not
+-- guaranteed to be unique to the table -
+-- rather, it is unique within an individual
+-- interaction type. Combine with type to get
+-- a unique identifier in this table.
+-- @column type Type of this interaction, which together
+-- with scoped_id uniquely identifies this
+-- interaction. Also corresponds to a SQL
+-- table name containing more details specific
+-- to this type of interaction.
+-- @column name Interaction name - e.g. 'PageLoad', 'Tap',
+-- etc. Interactions will have unique metrics
+-- stored in other tables.
+-- @column ts Timestamp of the CUI event.
+-- @column dur Duration of the CUI event.
+CREATE PERFETTO TABLE chrome_interactions AS
+SELECT
+ navigation_id AS scoped_id,
+ 'chrome_page_loads' AS type,
+ 'PageLoad' AS name,
+ navigation_start_ts AS ts,
+ IFNULL(lcp, fcp) AS dur
+FROM chrome_page_loads;
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql
new file mode 100644
index 0000000..fcc5874
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql
@@ -0,0 +1,73 @@
+-- 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.
+
+-- TODO(b/306300843): The recorded navigation ids are not guaranteed to be
+-- unique within a trace; they are only guaranteed to be unique within a single
+-- chrome instance. Chrome instance id needs to be recorded, and used here in
+-- combination with navigation id to uniquely identify page load metrics.
+
+INCLUDE PERFETTO MODULE common.slices;
+
+-- Chrome page loads, including associated high-level metrics and properties.
+--
+-- @column navigation_id ID of the navigation associated with the
+-- page load (i.e. the cross-document
+-- navigation in primary main frame which
+-- created this page's main document). Also
+-- note that navigation_id is specific to a
+-- given Chrome browser process, and not
+-- globally unique.
+-- @column navigation_start_ts Timestamp of the start of navigation.
+-- @column fcp Duration between the navigation start and
+-- the first contentful paint event
+-- (web.dev/fcp).
+-- @column fcp_ts Timestamp of the first contentful paint.
+-- @column lcp Duration between the navigation start and
+-- the largest contentful paint event
+-- (web.dev/lcp).
+-- @column lcp_ts Timestamp of the largest contentful paint.
+-- @column url URL at the page load event.
+-- @column browser_upid The unique process id (upid) of the browser
+-- process where the page load occurred.
+CREATE PERFETTO TABLE chrome_page_loads AS
+WITH fcp AS (
+ SELECT
+ ts,
+ dur,
+ EXTRACT_ARG(arg_set_id, 'page_load.navigation_id') AS navigation_id,
+ EXTRACT_ARG(arg_set_id, 'page_load.url') AS url,
+ upid AS browser_upid
+ FROM process_slice
+ WHERE name = 'PageLoadMetrics.NavigationToFirstContentfulPaint'
+),
+lcp AS (
+ SELECT
+ ts,
+ dur,
+ EXTRACT_ARG(arg_set_id, 'page_load.navigation_id')
+ AS navigation_id
+ FROM slice
+ WHERE name = 'PageLoadMetrics.NavigationToLargestContentfulPaint'
+)
+SELECT
+ fcp.navigation_id,
+ fcp.ts AS navigation_start_ts,
+ fcp.dur AS fcp,
+ fcp.ts + fcp.dur AS fcp_ts,
+ lcp.dur AS lcp,
+ IFNULL(lcp.dur, 0) + IFNULL(lcp.ts, 0) AS lcp_ts,
+ fcp.url,
+ fcp.browser_upid
+FROM fcp
+LEFT JOIN lcp USING (navigation_id);
diff --git a/test/data/chrome_fcp_lcp_navigations.pftrace.sha256 b/test/data/chrome_fcp_lcp_navigations.pftrace.sha256
new file mode 100644
index 0000000..e4274e2
--- /dev/null
+++ b/test/data/chrome_fcp_lcp_navigations.pftrace.sha256
@@ -0,0 +1 @@
+ae01d849fbd75a98be1b7ddd5a8873217c377b393a1d5bbd788ed3364f7fefc3
\ 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 0918d10..d8669d6 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -88,6 +88,7 @@
from diff_tests.parser.ufs.tests import Ufs
from diff_tests.stdlib.android.tests import AndroidStdlib
from diff_tests.stdlib.chrome.tests import ChromeStdlib
+from diff_tests.stdlib.chrome.tests_chrome_interactions import ChromeInteractions
from diff_tests.stdlib.chrome.tests_scroll_jank import ChromeScrollJankStdlib
from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
from diff_tests.stdlib.pkvm.tests import Pkvm
@@ -202,6 +203,8 @@
stdlib_tests = [
*AndroidStdlib(index_path, 'stdlib/android', 'AndroidStdlib').fetch(),
+ *ChromeInteractions(index_path, 'stdlib/chrome',
+ 'ChromeInteractions').fetch(),
*ChromeScrollJankStdlib(index_path, 'stdlib/chrome',
'ChromeScrollJankStdlib').fetch(),
*ChromeStdlib(index_path, 'stdlib/chrome', 'ChromeStdlib').fetch(),
diff --git a/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py b/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py
new file mode 100644
index 0000000..5019e54
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py
@@ -0,0 +1,50 @@
+#!/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 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 ChromeInteractions(TestSuite):
+ def test_chrome_fcp_lcp_navigations(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_fcp_lcp_navigations.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE chrome.page_loads;
+
+ SELECT
+ navigation_id,
+ navigation_start_ts,
+ fcp,
+ fcp_ts,
+ lcp,
+ lcp_ts,
+ browser_upid
+ FROM chrome_page_loads
+ ORDER by navigation_start_ts;
+ """,
+ out=Csv("""
+ "navigation_id","navigation_start_ts","fcp","fcp_ts","lcp","lcp_ts","browser_upid"
+ 6,687425601436243,950000000,687426551436243,950000000,687426551436243,1
+ 7,687427799068243,888000000,687428687068243,888000000,687428687068243,1
+ 8,687429970749243,1031000000,687431001749243,1132000000,687431102749243,1
+ 9,687432344113243,539000000,687432883113243,539000000,687432883113243,1
+ 10,687434796215243,475000000,687435271215243,475000000,687435271215243,1
+ 11,687435970742243,763000000,687436733742243,852000000,687436822742243,1
+ 13,687438343638243,1005000000,687439348638243,1005000000,687439348638243,1
+ 14,687440258111243,900000000,687441158111243,"[NULL]",0,1
+ """))
diff --git a/ui/src/tracks/chrome_critical_user_interactions/details_panel.ts b/ui/src/tracks/chrome_critical_user_interactions/details_panel.ts
new file mode 100644
index 0000000..9eea78f
--- /dev/null
+++ b/ui/src/tracks/chrome_critical_user_interactions/details_panel.ts
@@ -0,0 +1,236 @@
+// 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.
+
+import m from 'mithril';
+
+import {duration, Time, time} from '../../base/time';
+import {exists} from '../../base/utils';
+import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
+import {raf} from '../../core/raf_scheduler';
+import {
+ BottomTab,
+ bottomTabRegistry,
+ NewBottomTabArgs,
+} from '../../frontend/bottom_tab';
+import {
+ GenericSliceDetailsTabConfig,
+} from '../../frontend/generic_slice_details_tab';
+import {sqlValueToString} from '../../frontend/sql_utils';
+import {Timestamp} from '../../frontend/widgets/timestamp';
+import {Anchor} from '../../widgets/anchor';
+import {DetailsShell} from '../../widgets/details_shell';
+import {DurationWidget} from '../../widgets/duration';
+import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
+import {SqlRef} from '../../widgets/sql_ref';
+import {dictToTreeNodes, Tree} from '../../widgets/tree';
+
+interface PageLoadMetrics {
+ url: string;
+ navigationId: number;
+ fcpDuration?: duration;
+ lcpDuration?: duration;
+ fcpTs: time, lcpTs?: time,
+}
+
+enum CriticalUserJourneyType {
+ UNKNOWN = 'Unknown',
+ PAGE_LOAD = 'PageLoad',
+}
+
+function convertToCriticalUserJourneyType(cujType: string):
+ CriticalUserJourneyType {
+ switch (cujType) {
+ case CriticalUserJourneyType.PAGE_LOAD:
+ return CriticalUserJourneyType.PAGE_LOAD;
+ default:
+ return CriticalUserJourneyType.UNKNOWN;
+ }
+}
+
+interface Data {
+ name: string;
+ // Timestamp of the beginning of this slice in nanoseconds.
+ ts: time;
+ // Duration of this slice in nanoseconds.
+ dur: duration;
+ type: CriticalUserJourneyType;
+ tableName: string;
+ // Metrics for |type| = CriticalUserJourney.PAGE_LOAD
+ pageLoadMetrics?: PageLoadMetrics;
+}
+
+export class CriticalUserInteractionDetailsPanel extends
+ BottomTab<GenericSliceDetailsTabConfig> {
+ static readonly kind = 'org.perfetto.CriticalUserInteractionDetailsPanel';
+ data: Data|undefined;
+ loaded = false;
+
+ static create(args: NewBottomTabArgs): CriticalUserInteractionDetailsPanel {
+ return new CriticalUserInteractionDetailsPanel(args);
+ }
+
+ constructor(args: NewBottomTabArgs) {
+ super(args);
+ this.loadData();
+ }
+
+ private async loadData() {
+ const queryResult = await this.engine.query(`
+ SELECT
+ name,
+ ts,
+ dur,
+ type AS tableName
+ FROM chrome_interactions
+ WHERE scoped_id = ${this.config.id}`);
+
+ const iter = queryResult.firstRow({
+ name: STR,
+ ts: LONG,
+ dur: LONG,
+ tableName: STR,
+ });
+
+ this.data = {
+ name: iter.name,
+ ts: Time.fromRaw(iter.ts),
+ dur: iter.dur,
+ type: convertToCriticalUserJourneyType(iter.name),
+ tableName: iter.tableName,
+ };
+
+ await this.loadMetrics();
+
+ this.loaded = true;
+ raf.scheduleFullRedraw();
+ }
+
+ private async loadMetrics() {
+ if (exists(this.data)) {
+ switch (this.data.type) {
+ case CriticalUserJourneyType.PAGE_LOAD:
+ await this.loadPageLoadMetrics();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private async loadPageLoadMetrics() {
+ if (exists(this.data)) {
+ const queryResult = await this.engine.query(`
+ SELECT
+ navigation_id AS navigationId,
+ url,
+ fcp AS fcpDuration,
+ lcp AS lcpDuration,
+ fcp_ts AS fcpTs,
+ lcp_ts AS lcpTs
+ FROM chrome_page_loads
+ WHERE navigation_id = ${this.config.id}`);
+
+ const iter = queryResult.firstRow({
+ navigationId: NUM,
+ url: STR,
+ fcpDuration: LONG_NULL,
+ lcpDuration: LONG_NULL,
+ fcpTs: LONG,
+ lcpTs: LONG,
+ });
+
+ this.data.pageLoadMetrics = {
+ navigationId: iter.navigationId,
+ url: iter.url,
+ fcpTs: Time.fromRaw(iter.fcpTs),
+ };
+
+ if (exists(iter.fcpDuration)) {
+ this.data.pageLoadMetrics.fcpDuration = iter.fcpDuration;
+ }
+
+ if (exists(iter.lcpDuration)) {
+ this.data.pageLoadMetrics.lcpDuration = iter.lcpDuration;
+ }
+
+ if (Number(iter.lcpTs) != 0) {
+ this.data.pageLoadMetrics.lcpTs = Time.fromRaw(iter.lcpTs);
+ }
+ }
+ }
+
+ private renderDetailsDictionary(): m.Child[] {
+ const details: {[key: string]: m.Child} = {};
+ if (exists(this.data)) {
+ details['Name'] = sqlValueToString(this.data.name);
+ details['Timestamp'] = m(Timestamp, {ts: this.data.ts});
+ if (exists(this.data.pageLoadMetrics)) {
+ details['FCP Timestamp'] =
+ m(Timestamp, {ts: this.data.pageLoadMetrics.fcpTs});
+ if (exists(this.data.pageLoadMetrics.fcpDuration)) {
+ details['FCP Duration'] =
+ m(DurationWidget, {dur: this.data.pageLoadMetrics.fcpDuration});
+ }
+ if (exists(this.data.pageLoadMetrics.lcpTs)) {
+ details['LCP Timestamp'] =
+ m(Timestamp, {ts: this.data.pageLoadMetrics.lcpTs});
+ }
+ if (exists(this.data.pageLoadMetrics.lcpDuration)) {
+ details['LCP Duration'] =
+ m(DurationWidget, {dur: this.data.pageLoadMetrics.lcpDuration});
+ }
+ details['Navigation ID'] = this.data.pageLoadMetrics.navigationId;
+ const url = this.data.pageLoadMetrics.url;
+ details['URL'] =
+ m(Anchor, {href: url, target: '_blank', icon: 'open_in_new'}, url);
+ }
+ details['SQL ID'] =
+ m(SqlRef, {table: 'chrome_interactions', id: this.config.id});
+ }
+
+ return dictToTreeNodes(details);
+ }
+
+ viewTab() {
+ if (this.data === undefined) {
+ return m('h2', 'Loading');
+ }
+
+ return m(
+ DetailsShell,
+ {
+ title: this.getTitle(),
+ },
+ m(GridLayout,
+ m(
+ GridLayoutColumn,
+ m(
+ Section,
+ {title: 'Details'},
+ m(Tree, this.renderDetailsDictionary()),
+ ),
+ )));
+ }
+
+ getTitle(): string {
+ return this.config.title;
+ }
+
+ isLoading() {
+ return !this.loaded;
+ }
+}
+
+bottomTabRegistry.register(CriticalUserInteractionDetailsPanel);
diff --git a/ui/src/tracks/chrome_critical_user_interactions/index.ts b/ui/src/tracks/chrome_critical_user_interactions/index.ts
new file mode 100644
index 0000000..298368e
--- /dev/null
+++ b/ui/src/tracks/chrome_critical_user_interactions/index.ts
@@ -0,0 +1,110 @@
+// 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.
+
+import {v4 as uuidv4} from 'uuid';
+
+import {Actions} from '../../common/actions';
+import {SCROLLING_TRACK_GROUP} from '../../common/state';
+import {globals} from '../../frontend/globals';
+import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
+import {NewTrackArgs, TrackBase} from '../../frontend/track';
+import {
+ Plugin,
+ PluginContext,
+ PluginContextTrace,
+ PluginDescriptor,
+ PrimaryTrackSortKey,
+} from '../../public';
+import {
+ CustomSqlDetailsPanelConfig,
+ CustomSqlImportConfig,
+ CustomSqlTableDefConfig,
+ CustomSqlTableSliceTrack,
+} from '../custom_sql_table_slices';
+
+import {CriticalUserInteractionDetailsPanel} from './details_panel';
+
+export const CRITICAL_USER_INTERACTIONS_KIND =
+ 'org.chromium.TopLevelScrolls.scrolls';
+
+export class CriticalUserInteractionTrack extends
+ CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
+ static readonly kind = CRITICAL_USER_INTERACTIONS_KIND;
+
+ static create(args: NewTrackArgs): TrackBase {
+ return new CriticalUserInteractionTrack(args);
+ }
+
+ getSqlDataSource(): CustomSqlTableDefConfig {
+ return {
+ columns: ['scoped_id AS id', 'name', 'ts', 'dur', 'type'],
+ sqlTableName: 'chrome_interactions',
+ };
+ }
+
+ getDetailsPanel(): CustomSqlDetailsPanelConfig {
+ return {
+ kind: CriticalUserInteractionDetailsPanel.kind,
+ config: {
+ sqlTableName: this.tableName,
+ title: 'Chrome Critical User Interaction',
+ },
+ };
+ }
+
+ getSqlImports(): CustomSqlImportConfig {
+ return {
+ modules: ['chrome.interactions'],
+ };
+ }
+}
+
+export function addCriticalUserInteractionTrack() {
+ const trackId = uuidv4();
+ globals.dispatchMultiple([
+ Actions.addTrack({
+ id: trackId,
+ uri: CriticalUserInteractionTrack.kind,
+ name: `Chrome Interactions`,
+ trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
+ trackGroup: SCROLLING_TRACK_GROUP,
+ }),
+ Actions.toggleTrackPinned({trackId}),
+ ]);
+}
+
+class CriticalUserInteractionPlugin implements Plugin {
+ async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+ ctx.addTrack({
+ uri: CriticalUserInteractionTrack.kind,
+ kind: CriticalUserInteractionTrack.kind,
+ displayName: 'Chrome Interactions',
+ track: (trackCtx) => new CriticalUserInteractionTrack(
+ {engine: ctx.engine, trackId: trackCtx.trackInstanceId}),
+ });
+ }
+
+ onActivate(ctx: PluginContext): void {
+ ctx.addCommand({
+ id: 'perfetto.CriticalUserInteraction.AddInteractionTrack',
+ name: 'Add Chrome Interactions track',
+ callback: () => addCriticalUserInteractionTrack(),
+ });
+ }
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: 'perfetto.CriticalUserInteraction',
+ plugin: CriticalUserInteractionPlugin,
+};
diff --git a/ui/src/tracks/custom_sql_table_slices/index.ts b/ui/src/tracks/custom_sql_table_slices/index.ts
index 696bec8..3e63418 100644
--- a/ui/src/tracks/custom_sql_table_slices/index.ts
+++ b/ui/src/tracks/custom_sql_table_slices/index.ts
@@ -32,6 +32,10 @@
import {NewTrackArgs} from '../../frontend/track';
import {Plugin, PluginContext, PluginDescriptor} from '../../public';
+export interface CustomSqlImportConfig {
+ modules: string[];
+}
+
export interface CustomSqlTableDefConfig {
// Table name
sqlTableName: string;
@@ -62,8 +66,14 @@
// Override by subclasses.
abstract getDetailsPanel(): CustomSqlDetailsPanelConfig;
+ getSqlImports(): CustomSqlImportConfig {
+ return {
+ modules: [] as string[],
+ };
+ }
async onInit(): Promise<Disposable> {
+ await this.loadImports();
const config = this.getSqlDataSource();
let columns = ['*'];
if (config.columns !== undefined) {
@@ -113,6 +123,12 @@
},
}));
}
+
+ async loadImports() {
+ for (const importModule of this.getSqlImports().modules) {
+ await this.engine.query(`INCLUDE PERFETTO MODULE ${importModule};`);
+ }
+ }
}
class CustomSqlTrackPlugin implements Plugin {