1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2024 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Worker// When deep linking into Perfetto UI it is possible to pass arguments in the 16*6dbdd20aSAndroid Build Coastguard Worker// query string to automatically select a slice or run a query once the 17*6dbdd20aSAndroid Build Coastguard Worker// trace is loaded. This plugin deals with kicking off the relevant logic 18*6dbdd20aSAndroid Build Coastguard Worker// once the trace has loaded. 19*6dbdd20aSAndroid Build Coastguard Worker 20*6dbdd20aSAndroid Build Coastguard Workerimport {Trace} from '../../public/trace'; 21*6dbdd20aSAndroid Build Coastguard Workerimport {PerfettoPlugin} from '../../public/plugin'; 22*6dbdd20aSAndroid Build Coastguard Workerimport {addQueryResultsTab} from '../../components/query_table/query_result_tab'; 23*6dbdd20aSAndroid Build Coastguard Workerimport {Time} from '../../base/time'; 24*6dbdd20aSAndroid Build Coastguard Workerimport {RouteArgs} from '../../public/route_schema'; 25*6dbdd20aSAndroid Build Coastguard Workerimport {App} from '../../public/app'; 26*6dbdd20aSAndroid Build Coastguard Workerimport {exists} from '../../base/utils'; 27*6dbdd20aSAndroid Build Coastguard Workerimport {NUM} from '../../trace_processor/query_result'; 28*6dbdd20aSAndroid Build Coastguard Worker 29*6dbdd20aSAndroid Build Coastguard Workerlet routeArgsForFirstTrace: RouteArgs | undefined; 30*6dbdd20aSAndroid Build Coastguard Worker 31*6dbdd20aSAndroid Build Coastguard Worker/** 32*6dbdd20aSAndroid Build Coastguard Worker * Uses URL args (table, ts, dur) to select events on trace load. 33*6dbdd20aSAndroid Build Coastguard Worker * 34*6dbdd20aSAndroid Build Coastguard Worker * E.g. ?table=thread_state&ts=39978672284068&dur=18995809 35*6dbdd20aSAndroid Build Coastguard Worker * 36*6dbdd20aSAndroid Build Coastguard Worker * Note: `ts` and `dur` are used rather than id as id is not stable over TP 37*6dbdd20aSAndroid Build Coastguard Worker * versions. 38*6dbdd20aSAndroid Build Coastguard Worker * 39*6dbdd20aSAndroid Build Coastguard Worker * The table passed must have `ts`, `dur` (if a dur value is supplied) and `id` 40*6dbdd20aSAndroid Build Coastguard Worker * columns, and SQL resolvers must be available for those tables (usually from 41*6dbdd20aSAndroid Build Coastguard Worker * plugins). 42*6dbdd20aSAndroid Build Coastguard Worker */ 43*6dbdd20aSAndroid Build Coastguard Workerexport default class implements PerfettoPlugin { 44*6dbdd20aSAndroid Build Coastguard Worker static readonly id = 'dev.perfetto.DeeplinkQuerystring'; 45*6dbdd20aSAndroid Build Coastguard Worker 46*6dbdd20aSAndroid Build Coastguard Worker static onActivate(app: App): void { 47*6dbdd20aSAndroid Build Coastguard Worker routeArgsForFirstTrace = app.initialRouteArgs; 48*6dbdd20aSAndroid Build Coastguard Worker } 49*6dbdd20aSAndroid Build Coastguard Worker 50*6dbdd20aSAndroid Build Coastguard Worker async onTraceLoad(trace: Trace) { 51*6dbdd20aSAndroid Build Coastguard Worker trace.onTraceReady.addListener(async () => { 52*6dbdd20aSAndroid Build Coastguard Worker const initialRouteArgs = routeArgsForFirstTrace; 53*6dbdd20aSAndroid Build Coastguard Worker routeArgsForFirstTrace = undefined; 54*6dbdd20aSAndroid Build Coastguard Worker if (initialRouteArgs === undefined) return; 55*6dbdd20aSAndroid Build Coastguard Worker 56*6dbdd20aSAndroid Build Coastguard Worker await selectInitialRouteArgs(trace, initialRouteArgs); 57*6dbdd20aSAndroid Build Coastguard Worker if ( 58*6dbdd20aSAndroid Build Coastguard Worker initialRouteArgs.visStart !== undefined && 59*6dbdd20aSAndroid Build Coastguard Worker initialRouteArgs.visEnd !== undefined 60*6dbdd20aSAndroid Build Coastguard Worker ) { 61*6dbdd20aSAndroid Build Coastguard Worker zoomPendingDeeplink( 62*6dbdd20aSAndroid Build Coastguard Worker trace, 63*6dbdd20aSAndroid Build Coastguard Worker initialRouteArgs.visStart, 64*6dbdd20aSAndroid Build Coastguard Worker initialRouteArgs.visEnd, 65*6dbdd20aSAndroid Build Coastguard Worker ); 66*6dbdd20aSAndroid Build Coastguard Worker } 67*6dbdd20aSAndroid Build Coastguard Worker if (initialRouteArgs.query !== undefined) { 68*6dbdd20aSAndroid Build Coastguard Worker addQueryResultsTab(trace, { 69*6dbdd20aSAndroid Build Coastguard Worker query: initialRouteArgs.query, 70*6dbdd20aSAndroid Build Coastguard Worker title: 'Deeplink Query', 71*6dbdd20aSAndroid Build Coastguard Worker }); 72*6dbdd20aSAndroid Build Coastguard Worker } 73*6dbdd20aSAndroid Build Coastguard Worker }); 74*6dbdd20aSAndroid Build Coastguard Worker } 75*6dbdd20aSAndroid Build Coastguard Worker} 76*6dbdd20aSAndroid Build Coastguard Worker 77*6dbdd20aSAndroid Build Coastguard Workerfunction zoomPendingDeeplink(trace: Trace, visStart: string, visEnd: string) { 78*6dbdd20aSAndroid Build Coastguard Worker const visualStart = Time.fromRaw(BigInt(visStart)); 79*6dbdd20aSAndroid Build Coastguard Worker const visualEnd = Time.fromRaw(BigInt(visEnd)); 80*6dbdd20aSAndroid Build Coastguard Worker if ( 81*6dbdd20aSAndroid Build Coastguard Worker !( 82*6dbdd20aSAndroid Build Coastguard Worker visualStart < visualEnd && 83*6dbdd20aSAndroid Build Coastguard Worker trace.traceInfo.start <= visualStart && 84*6dbdd20aSAndroid Build Coastguard Worker visualEnd <= trace.traceInfo.end 85*6dbdd20aSAndroid Build Coastguard Worker ) 86*6dbdd20aSAndroid Build Coastguard Worker ) { 87*6dbdd20aSAndroid Build Coastguard Worker return; 88*6dbdd20aSAndroid Build Coastguard Worker } 89*6dbdd20aSAndroid Build Coastguard Worker trace.timeline.setViewportTime(visualStart, visualEnd); 90*6dbdd20aSAndroid Build Coastguard Worker} 91*6dbdd20aSAndroid Build Coastguard Worker 92*6dbdd20aSAndroid Build Coastguard Workerasync function selectInitialRouteArgs(trace: Trace, args: RouteArgs) { 93*6dbdd20aSAndroid Build Coastguard Worker const {table = 'slice', ts, dur} = args; 94*6dbdd20aSAndroid Build Coastguard Worker 95*6dbdd20aSAndroid Build Coastguard Worker // We need at least a ts 96*6dbdd20aSAndroid Build Coastguard Worker if (!exists(ts)) { 97*6dbdd20aSAndroid Build Coastguard Worker return; 98*6dbdd20aSAndroid Build Coastguard Worker } 99*6dbdd20aSAndroid Build Coastguard Worker 100*6dbdd20aSAndroid Build Coastguard Worker const conditions = []; 101*6dbdd20aSAndroid Build Coastguard Worker conditions.push(`ts = ${ts}`); 102*6dbdd20aSAndroid Build Coastguard Worker exists(dur) && conditions.push(`dur = ${dur}`); 103*6dbdd20aSAndroid Build Coastguard Worker 104*6dbdd20aSAndroid Build Coastguard Worker // Find the id of the slice with this ts & dur in the given table 105*6dbdd20aSAndroid Build Coastguard Worker const result = await trace.engine.query(` 106*6dbdd20aSAndroid Build Coastguard Worker select 107*6dbdd20aSAndroid Build Coastguard Worker id 108*6dbdd20aSAndroid Build Coastguard Worker from 109*6dbdd20aSAndroid Build Coastguard Worker ${table} 110*6dbdd20aSAndroid Build Coastguard Worker where ${conditions.join(' AND ')} 111*6dbdd20aSAndroid Build Coastguard Worker `); 112*6dbdd20aSAndroid Build Coastguard Worker 113*6dbdd20aSAndroid Build Coastguard Worker if (result.numRows() === 0) { 114*6dbdd20aSAndroid Build Coastguard Worker return; 115*6dbdd20aSAndroid Build Coastguard Worker } 116*6dbdd20aSAndroid Build Coastguard Worker 117*6dbdd20aSAndroid Build Coastguard Worker const {id} = result.firstRow({ 118*6dbdd20aSAndroid Build Coastguard Worker id: NUM, 119*6dbdd20aSAndroid Build Coastguard Worker }); 120*6dbdd20aSAndroid Build Coastguard Worker 121*6dbdd20aSAndroid Build Coastguard Worker trace.selection.selectSqlEvent(table, id, { 122*6dbdd20aSAndroid Build Coastguard Worker scrollToSelection: true, 123*6dbdd20aSAndroid Build Coastguard Worker switchToCurrentSelectionTab: false, 124*6dbdd20aSAndroid Build Coastguard Worker }); 125*6dbdd20aSAndroid Build Coastguard Worker} 126