diff options
author | Harkiran Bolaria <hbolaria@google.com> | 2023-07-05 09:03:37 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2023-07-05 09:03:37 +0000 |
commit | 09c5009206a83f6015904b7dda8e111681ec4da9 (patch) | |
tree | 3a5fc9c682ec5f4d2633e7d401a25a110cf49c93 | |
parent | 5d69a9365ef1755ec0cd393df2e41e8e5ff45385 (diff) | |
parent | e817b0258b75e3a73a90c2bd1a008c0601e97c8b (diff) | |
download | perfetto-09c5009206a83f6015904b7dda8e111681ec4da9.tar.gz |
Merge "[Part 1] Add slice linking to scroll jank plugin slices."
7 files changed, 564 insertions, 32 deletions
diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_slice.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_slice.ts new file mode 100644 index 000000000..046ef23bc --- /dev/null +++ b/ui/src/tracks/chrome_scroll_jank/event_latency_slice.ts @@ -0,0 +1,189 @@ +// 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 {assertExists} from '../../base/logging'; +import {Actions} from '../../common/actions'; +import {EngineProxy} from '../../common/engine'; +import {LONG, NUM} from '../../common/query_result'; +import {TPDuration} from '../../common/time'; +import {Anchor} from '../../frontend/anchor'; +import {globals} from '../../frontend/globals'; +import {scrollToTrackAndTs} from '../../frontend/scroll_helper'; +import {Icons} from '../../frontend/semantic_icons'; +import {asTPTimestamp, SliceSqlId, TPTimestamp} from '../../frontend/sql_types'; + +import { + EventLatencyTrack, +} from './event_latency_track'; +import {ScrollJankPluginState} from './index'; + +export interface EventLatencySlice { + // Chrome slice id for an EventLatency slice. + sliceId: SliceSqlId; + // Timestamp of the beginning of this slice in nanoseconds. + ts: TPTimestamp; + // Duration of this slice in nanoseconds. + dur: TPDuration; +} + +export async function getEventLatencySlice( + engine: EngineProxy, id: number): Promise<EventLatencySlice|undefined> { + const eventLatencyTrack = + ScrollJankPluginState.getInstance().getTrack(EventLatencyTrack.kind); + if (eventLatencyTrack == undefined) { + throw new Error(`${EventLatencyTrack.kind} track is not registered.`); + } + + const query = await engine.query(` + SELECT + id as sliceId, + ts, + dur as dur + FROM ${eventLatencyTrack.sqlTableName} + WHERE id=${id}`); + const it = query.iter({ + sliceId: NUM, + ts: LONG, + dur: LONG, + }); + + const result: EventLatencySlice[] = []; + + for (; it.valid(); it.next()) { + result.push({ + sliceId: it.sliceId as SliceSqlId, + ts: asTPTimestamp(it.ts), + dur: it.dur, + }); + } + + if (result.length > 1) { + throw new Error(`${ + eventLatencyTrack.sqlTableName} table has more than one row with id ${ + id}`); + } + if (result.length === 0) { + return undefined; + } + return result[0]; +} + +export async function getEventLatencyDescendantSlice( + engine: EngineProxy, id: number, descendant: string|undefined): + Promise<EventLatencySlice|undefined> { + const query = await engine.query(` + SELECT + id as sliceId, + ts, + dur as dur + FROM descendant_slice(${id}) + WHERE name='${descendant}'`); + const it = query.iter({ + sliceId: NUM, + ts: LONG, + dur: LONG, + }); + + const result: EventLatencySlice[] = []; + + for (; it.valid(); it.next()) { + result.push({ + sliceId: it.sliceId as SliceSqlId, + ts: asTPTimestamp(it.ts), + dur: it.dur, + }); + } + + const eventLatencyTrack = + ScrollJankPluginState.getInstance().getTrack(EventLatencyTrack.kind); + if (eventLatencyTrack == undefined) { + throw new Error(`${EventLatencyTrack.kind} track is not registered.`); + } + + if (result.length > 1) { + throw new Error(` + Slice table and track view ${ + eventLatencyTrack + .sqlTableName} has more than one descendant of slice id ${ + id} with name ${descendant}`); + } + if (result.length === 0) { + return undefined; + } + return result[0]; +} + +interface EventLatencySliceRefAttrs { + id: SliceSqlId; + ts: TPTimestamp; + // If not present, a placeholder name will be used. + name: string; + chromeSliceTrackId?: number; +} + +export class EventLatencySliceRef implements + m.ClassComponent<EventLatencySliceRefAttrs> { + view(vnode: m.Vnode<EventLatencySliceRefAttrs>) { + return m( + Anchor, + { + icon: Icons.UpdateSelection, + onclick: () => { + const eventLatencyTrack = + ScrollJankPluginState.getInstance().getTrack( + EventLatencyTrack.kind); + if (eventLatencyTrack == undefined) { + throw new Error( + `${EventLatencyTrack.kind} track is not registered.`); + } + + const trackIdx = vnode.attrs.chromeSliceTrackId as number; + assertExists(trackIdx); + const uiTrackId = globals.state.uiTrackIdByTraceTrackId[trackIdx]; + if (uiTrackId === undefined) return; + globals.makeSelection(Actions.selectChromeSlice( + {id: vnode.attrs.id, trackId: uiTrackId, table: 'slice'})); + + let trackId = ''; + for (const track of Object.values(globals.state.tracks)) { + if (track.kind === EventLatencyTrack.kind) { + trackId = track.id; + } + } + + if (trackId === '') { + throw new Error( + `Track id for ${EventLatencyTrack.kind} track not found.`); + } + + scrollToTrackAndTs(trackId, vnode.attrs.ts, true); + }, + }, + vnode.attrs.name, + ); + } +} + +export function eventLatencySlice( + state: EventLatencySlice, name: string, chromeSliceTrackId?: number): + m.Child { + return m(EventLatencySliceRef, { + id: state.sliceId, + ts: state.ts, + name: name, + chromeSliceTrackId: chromeSliceTrackId, + }); +} diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts index c784527fc..7250819d8 100644 --- a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts +++ b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts @@ -19,12 +19,14 @@ import { generateSqlWithInternalLayout, } from '../../common/internal_layout_utils'; import {PrimaryTrackSortKey, SCROLLING_TRACK_GROUP} from '../../common/state'; +import {ChromeSliceDetailsTab} from '../../frontend/chrome_slice_details_tab'; import { NamedSliceTrack, NamedSliceTrackTypes, } from '../../frontend/named_slice_track'; import {NewTrackArgs, Track} from '../../frontend/track'; import {ScrollJankTracks as DecideTracksResult} from './index'; +import {ScrollJankPluginState} from './index'; export interface EventLatencyTrackTypes extends NamedSliceTrackTypes { config: {baseTable: string;} @@ -39,6 +41,23 @@ export class EventLatencyTrack extends NamedSliceTrack<EventLatencyTrackTypes> { constructor(args: NewTrackArgs) { super(args); + ScrollJankPluginState.getInstance().registerTrack({ + kind: EventLatencyTrack.kind, + trackId: this.trackId, + tableName: this.tableName, + detailsPanelConfig: { + kind: ChromeSliceDetailsTab.kind, + config: { + title: 'Input Event Latency Slice', + sqlTableName: this.tableName, + }, + }, + }); + } + + onDestroy() { + super.onDestroy(); + ScrollJankPluginState.getInstance().unregisterTrack(EventLatencyTrack.kind); } async initSqlTable(tableName: string) { @@ -126,7 +145,7 @@ export async function addLatencyTracks(engine: Engine): engineId: engine.id, kind: EventLatencyTrack.kind, trackSortKey: PrimaryTrackSortKey.NULL_TRACK, - name: 'Chrome Input Event Latencies', + name: 'Chrome Scroll Input Latencies', config: {baseTable: baseTable}, trackGroup: SCROLLING_TRACK_GROUP, }); diff --git a/ui/src/tracks/chrome_scroll_jank/index.ts b/ui/src/tracks/chrome_scroll_jank/index.ts index b6c816d82..c72bb698e 100644 --- a/ui/src/tracks/chrome_scroll_jank/index.ts +++ b/ui/src/tracks/chrome_scroll_jank/index.ts @@ -18,6 +18,8 @@ import {featureFlags} from '../../common/feature_flags'; import { PluginContext, } from '../../public'; +import {ObjectById} from '../../common/state'; +import {CustomSqlDetailsPanelConfig} from '../custom_sql_table_slices'; import {ChromeTasksScrollJankTrack} from './chrome_tasks_scroll_jank_track'; import {addLatencyTracks, EventLatencyTrack} from './event_latency_track'; @@ -50,6 +52,51 @@ export type ScrollJankTracks = { tracksToAdd: AddTrackArgs[], }; +export interface ScrollJankTrackSpec { + id: string; + sqlTableName: string; + detailsPanelConfig: CustomSqlDetailsPanelConfig; +} + +// Global state for the scroll jank plugin. +export class ScrollJankPluginState { + private static instance: ScrollJankPluginState; + private tracks: ObjectById<ScrollJankTrackSpec>; + + private constructor() { + this.tracks = {}; + } + + public static getInstance(): ScrollJankPluginState { + if (!ScrollJankPluginState.instance) { + ScrollJankPluginState.instance = new ScrollJankPluginState(); + } + + return ScrollJankPluginState.instance; + } + + public registerTrack(args: { + kind: string, + trackId: string, + tableName: string, + detailsPanelConfig: CustomSqlDetailsPanelConfig, + }): void { + this.tracks[args.kind] = { + id: args.trackId, + sqlTableName: args.tableName, + detailsPanelConfig: args.detailsPanelConfig, + }; + } + + public unregisterTrack(kind: string): void { + delete this.tracks[kind]; + } + + public getTrack(kind: string): ScrollJankTrackSpec|undefined { + return this.tracks[kind]; + } +} + export async function getScrollJankTracks(engine: Engine): Promise<ScrollJankTracks> { const result: ScrollJankTracks = { diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_track.ts index e63dfa100..be3d6f6ad 100644 --- a/ui/src/tracks/chrome_scroll_jank/scroll_track.ts +++ b/ui/src/tracks/chrome_scroll_jank/scroll_track.ts @@ -30,6 +30,7 @@ import { CustomSqlTableDefConfig, CustomSqlTableSliceTrack, } from '../custom_sql_table_slices'; +import {ScrollJankPluginState} from './index'; import {ScrollJankTracks as DecideTracksResult} from './index'; @@ -65,13 +66,22 @@ export class TopLevelScrollTrack extends constructor(args: NewTrackArgs) { super(args); + ScrollJankPluginState.getInstance().registerTrack({ + kind: TopLevelScrollTrack.kind, + trackId: this.trackId, + tableName: this.tableName, + detailsPanelConfig: this.getDetailsPanel(), + }); + this.displayColumns['id'] = {displayName: 'Scroll Id (gesture_scroll_id)'}; this.displayColumns['ts'] = {displayName: 'Start time'}; this.displayColumns['dur'] = {displayName: 'Duration'}; } - async initSqlTable(tableName: string) { - await super.initSqlTable(tableName); + onDestroy() { + super.onDestroy(); + ScrollJankPluginState.getInstance().unregisterTrack( + TopLevelScrollTrack.kind); } } diff --git a/ui/src/tracks/chrome_scroll_jank/top_level_jank_track.ts b/ui/src/tracks/chrome_scroll_jank/top_level_jank_track.ts index 848f1d381..050f08d24 100644 --- a/ui/src/tracks/chrome_scroll_jank/top_level_jank_track.ts +++ b/ui/src/tracks/chrome_scroll_jank/top_level_jank_track.ts @@ -27,6 +27,7 @@ import { CustomSqlTableDefConfig, CustomSqlTableSliceTrack, } from '../custom_sql_table_slices'; +import {ScrollJankPluginState} from './index'; import {ScrollJankTracks as DecideTracksResult} from './index'; @@ -46,6 +47,13 @@ export class TopLevelJankTrack extends constructor(args: NewTrackArgs) { super(args); + ScrollJankPluginState.getInstance().registerTrack({ + kind: TopLevelJankTrack.kind, + trackId: this.trackId, + tableName: this.tableName, + detailsPanelConfig: this.getDetailsPanel(), + }); + this.displayColumns['name'] = {}; this.displayColumns['id'] = {displayName: 'Interval ID'}; this.displayColumns['ts'] = {displayName: 'Start time'}; @@ -69,8 +77,9 @@ export class TopLevelJankTrack extends }; } - async initSqlTable(tableName: string) { - await super.initSqlTable(tableName); + onDestroy() { + super.onDestroy(); + ScrollJankPluginState.getInstance().unregisterTrack(TopLevelJankTrack.kind); } } @@ -91,7 +100,7 @@ export async function addTopLevelJankTrack(engine: Engine): FROM chrome_scrolling_intervals UNION ALL SELECT - "Janky Scrolling Time" AS name, + "Janky Frame Visible" AS name, ts, dur FROM chrome_scroll_jank_intervals_v3 diff --git a/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_details_panel.ts new file mode 100644 index 000000000..e6a895f19 --- /dev/null +++ b/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_details_panel.ts @@ -0,0 +1,257 @@ +// 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 {exists} from '../../base/utils'; +import {EngineProxy} from '../../common/engine'; +import {LONG, NUM, STR, STR_NULL} from '../../common/query_result'; +import { + TPDuration, + tpDurationFromSql, + TPTime, + tpTimeFromSql, +} from '../../common/time'; +import { + BottomTab, + bottomTabRegistry, + NewBottomTabArgs, +} from '../../frontend/bottom_tab'; +import { + GenericSliceDetailsTabConfig, +} from '../../frontend/generic_slice_details_tab'; +import {getSlice, SliceDetails, sliceRef} from '../../frontend/sql/slice'; +import {asSliceSqlId, asTPTimestamp} from '../../frontend/sql_types'; +import {sqlValueToString} from '../../frontend/sql_utils'; +import {DetailsShell} from '../../frontend/widgets/details_shell'; +import {Duration} from '../../frontend/widgets/duration'; +import {GridLayout} from '../../frontend/widgets/grid_layout'; +import {Section} from '../../frontend/widgets/section'; +import {SqlRef} from '../../frontend/widgets/sql_ref'; +import {Timestamp} from '../../frontend/widgets/timestamp'; +import {dictToTreeNodes, Tree, TreeNode} from '../../frontend/widgets/tree'; + +import { + eventLatencySlice, + EventLatencySlice, + getEventLatencyDescendantSlice, + getEventLatencySlice, +} from './event_latency_slice'; +import {ScrollJankPluginState} from './index'; +import { + TopLevelEventLatencyTrack, +} from './top_level_janky_event_latencies_track'; +import {raf} from '../../core/raf_scheduler'; + +interface Data { + id: number; + // The slice display name - the cause + subcause of jank. + name: string; + // The stage of EventLatency that is the cause of jank. + jankCause: string; + // Where possible, the subcause of jank. + jankSubcause: string; + // The slice type - e.g. EventLatency + type: string; + // Timestamp of the beginning of this slice in nanoseconds. + ts: TPTime; + // Duration of this slice in nanoseconds. + dur: TPDuration; +} + +async function getSliceDetails( + engine: EngineProxy, id: number): Promise<SliceDetails|undefined> { + return getSlice(engine, asSliceSqlId(id)); +} + +export class JankyEventLatenciesDetailsPanel extends + BottomTab<GenericSliceDetailsTabConfig> { + static readonly kind = 'org.perfetto.JankyEventLatenciesDetailsPanel'; + static title = 'Chrome Scroll Jank Causes'; + private loaded = false; + private sliceDetails?: SliceDetails; + private eventLatencySliceDetails?: EventLatencySlice; + private causeSliceDetails?: EventLatencySlice; + private subcauseSliceDetails?: EventLatencySlice; + + data: Data|undefined; + + static create(args: NewBottomTabArgs): JankyEventLatenciesDetailsPanel { + return new JankyEventLatenciesDetailsPanel(args); + } + + constructor(args: NewBottomTabArgs) { + super(args); + this.loadData(); + } + + private async loadData() { + const trackDetails = ScrollJankPluginState.getInstance().getTrack( + TopLevelEventLatencyTrack.kind); + + const queryResult = await this.engine.query(` + SELECT + id, + name, + jank_cause AS jankCause, + jank_subcause AS jankSubcause, + type, + ts, + dur + FROM ${trackDetails?.sqlTableName} where id = ${this.config.id}`); + + const iter = queryResult.firstRow({ + id: NUM, + name: STR, + jankCause: STR, + jankSubcause: STR_NULL, + type: STR, + ts: LONG, + dur: LONG, + }); + this.data = { + id: iter.id, + name: iter.name, + jankCause: iter.jankCause, + jankSubcause: iter.jankSubcause, + type: iter.type, + ts: iter.ts, + dur: iter.dur, + } as Data; + + await this.loadSlices(); + this.loaded = true; + + raf.scheduleFullRedraw(); + } + + private hasCause(): boolean { + if (this.data === undefined) { + return false; + } + return this.data.jankCause !== 'UNKNOWN'; + } + + private hasSubcause(): boolean { + return this.hasCause() && this.data?.jankSubcause !== undefined; + } + + private async loadSlices() { + this.sliceDetails = await getSliceDetails(this.engine, this.config.id); + this.eventLatencySliceDetails = + await getEventLatencySlice(this.engine, this.config.id); + + if (this.hasCause()) { + this.causeSliceDetails = await getEventLatencyDescendantSlice( + this.engine, this.config.id, this.data?.jankCause); + } + + if (this.hasSubcause()) { + this.subcauseSliceDetails = await getEventLatencyDescendantSlice( + this.engine, this.config.id, this.data?.jankSubcause); + } + } + + viewTab() { + if (this.data === undefined) { + return m('h2', 'Loading'); + } + + const detailsDict: {[key: string]: m.Child} = { + 'Janked Event Latency stage': + exists(this.sliceDetails) && exists(this.causeSliceDetails) ? + eventLatencySlice( + this.causeSliceDetails, + this.data.jankCause, + this.sliceDetails.sqlTrackId) : + sqlValueToString(this.data.jankCause), + }; + + if (sqlValueToString(this.data.jankSubcause) != 'NULL') { + detailsDict['Sub-cause of Jank'] = + exists(this.sliceDetails) && exists(this.subcauseSliceDetails) ? + eventLatencySlice( + this.subcauseSliceDetails, + this.data.jankSubcause, + this.sliceDetails.sqlTrackId) : + sqlValueToString(this.data.jankSubcause); + } + + detailsDict['Start time'] = + m(Timestamp, {ts: asTPTimestamp(tpTimeFromSql(this.data.ts))}); + detailsDict['Duration'] = + m(Duration, {dur: tpDurationFromSql(this.data.dur)}); + detailsDict['Slice Type'] = sqlValueToString(this.data.type as string); + + const details = dictToTreeNodes(detailsDict); + + if (exists(this.sliceDetails)) { + details.push(m(TreeNode, { + left: sliceRef(this.sliceDetails, 'Original EventLatency'), + right: '', + })); + if (exists(this.eventLatencySliceDetails)) { + details.push(m(TreeNode, { + left: eventLatencySlice( + this.eventLatencySliceDetails, + 'Chrome Input Event Latencies', + this.sliceDetails.sqlTrackId), + right: '', + })); + } + } + + // TODO(b/278844325): add links to the correct process/track for cause. + + return m( + DetailsShell, + { + title: JankyEventLatenciesDetailsPanel.title, + }, + m( + GridLayout, + m( + Section, + {title: 'Details'}, + m(Tree, details), + ), + m( + Section, + {title: 'Metadata'}, + m(Tree, [m(TreeNode, { + left: 'SQL ID', + right: m(SqlRef, { + table: 'chrome_janky_event_latencies_v3', + id: this.config.id, + }), + })]), + ), + ), + ); + } + + getTitle(): string { + return `Current Selection`; + } + + isLoading() { + return this.loaded; + } + + renderTabCanvas() { + return; + } +} + +bottomTabRegistry.register(JankyEventLatenciesDetailsPanel); diff --git a/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_track.ts b/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_track.ts index 8577fd195..45bbae8e9 100644 --- a/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_track.ts +++ b/ui/src/tracks/chrome_scroll_jank/top_level_janky_event_latencies_track.ts @@ -19,10 +19,6 @@ import { PrimaryTrackSortKey, SCROLLING_TRACK_GROUP, } from '../../common/state'; -import { - Columns, - GenericSliceDetailsTab, -} from '../../frontend/generic_slice_details_tab'; import {NamedSliceTrackTypes} from '../../frontend/named_slice_track'; import {NewTrackArgs, Track} from '../../frontend/track'; import { @@ -31,12 +27,15 @@ import { CustomSqlTableSliceTrack, } from '../custom_sql_table_slices'; +import {ScrollJankPluginState} from './index'; import {ScrollJankTracks as DecideTracksResult} from './index'; +import { + JankyEventLatenciesDetailsPanel, +} from './top_level_janky_event_latencies_details_panel'; export class TopLevelEventLatencyTrack extends CustomSqlTableSliceTrack<NamedSliceTrackTypes> { static readonly kind = 'org.chromium.ScrollJank.top_level_event_latencies'; - displayColumns: Columns = {}; static create(args: NewTrackArgs): Track { return new TopLevelEventLatencyTrack(args); @@ -44,27 +43,29 @@ export class TopLevelEventLatencyTrack extends constructor(args: NewTrackArgs) { super(args); - this.displayColumns['cause_of_jank'] = {displayName: 'Cause of Jank'}; - this.displayColumns['sub_cause_of_jank'] = { - displayName: 'Sub-cause of Jank', - }; - this.displayColumns['id'] = {displayName: 'Slice ID'}; - this.displayColumns['ts'] = {displayName: 'Start time'}; - this.displayColumns['dur'] = {displayName: 'Duration'}; - this.displayColumns['type'] = {displayName: 'Slice Type'}; + ScrollJankPluginState.getInstance().registerTrack({ + kind: TopLevelEventLatencyTrack.kind, + trackId: this.trackId, + tableName: this.tableName, + detailsPanelConfig: this.getDetailsPanel(), + }); } getSqlDataSource(): CustomSqlTableDefConfig { return { columns: [ - 'id', - 'ts', - 'dur', - 'track_id', - 'cause_of_jank || IIF(sub_cause_of_jank IS NOT NULL, "::" || sub_cause_of_jank, "") AS name', - 'cause_of_jank', - 'name AS type', - 'sub_cause_of_jank', + `id`, + `ts`, + `dur`, + `track_id`, + `IIF( + cause_of_jank IS NOT NULL, + cause_of_jank || IIF( + sub_cause_of_jank IS NOT NULL, "::" || sub_cause_of_jank, "" + ), "UNKNOWN") AS name`, + `IFNULL(cause_of_jank, "UNKNOWN") AS jank_cause`, + `name AS type`, + `sub_cause_of_jank AS jank_subcause`, ], sqlTableName: 'chrome_janky_event_latencies_v3', }; @@ -72,17 +73,18 @@ export class TopLevelEventLatencyTrack extends getDetailsPanel(): CustomSqlDetailsPanelConfig { return { - kind: GenericSliceDetailsTab.kind, + kind: JankyEventLatenciesDetailsPanel.kind, config: { sqlTableName: this.tableName, title: 'Chrome Scroll Jank Event Latency: Cause', - columns: this.displayColumns, }, }; } - async initSqlTable(tableName: string) { - super.initSqlTable(tableName); + onDestroy() { + super.onDestroy(); + ScrollJankPluginState.getInstance().unregisterTrack( + TopLevelEventLatencyTrack.kind); } } @@ -94,7 +96,6 @@ export async function addJankyLatenciesTrack(engine: Engine): await engine.query(`SELECT IMPORT('chrome.chrome_scroll_janks');`); - result.tracksToAdd.push({ id: uuidv4(), engineId: engine.id, |