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 Workerimport {Trace} from '../../public/trace'; 16*6dbdd20aSAndroid Build Coastguard Workerimport {PerfettoPlugin} from '../../public/plugin'; 17*6dbdd20aSAndroid Build Coastguard Workerimport {METRIC_HANDLERS} from './handlers/handlerRegistry'; 18*6dbdd20aSAndroid Build Coastguard Workerimport {MetricData, MetricHandlerMatch} from './handlers/metricUtils'; 19*6dbdd20aSAndroid Build Coastguard Workerimport {PLUGIN_ID} from './pluginId'; 20*6dbdd20aSAndroid Build Coastguard Workerimport AndroidCujsPlugin from '../dev.perfetto.AndroidCujs'; 21*6dbdd20aSAndroid Build Coastguard Worker 22*6dbdd20aSAndroid Build Coastguard Workerconst JANK_CUJ_QUERY_PRECONDITIONS = ` 23*6dbdd20aSAndroid Build Coastguard Worker SELECT RUN_METRIC('android/android_blocking_calls_cuj_metric.sql'); 24*6dbdd20aSAndroid Build Coastguard Worker`; 25*6dbdd20aSAndroid Build Coastguard Worker 26*6dbdd20aSAndroid Build Coastguard Workerfunction getMetricsFromHash(): string[] { 27*6dbdd20aSAndroid Build Coastguard Worker const metricVal = location.hash; 28*6dbdd20aSAndroid Build Coastguard Worker const regex = new RegExp(`${PLUGIN_ID}:metrics=(.*)`); 29*6dbdd20aSAndroid Build Coastguard Worker const match = metricVal.match(regex); 30*6dbdd20aSAndroid Build Coastguard Worker if (match === null) { 31*6dbdd20aSAndroid Build Coastguard Worker return []; 32*6dbdd20aSAndroid Build Coastguard Worker } 33*6dbdd20aSAndroid Build Coastguard Worker const capturedString = match[1]; 34*6dbdd20aSAndroid Build Coastguard Worker let metricList: string[] = []; 35*6dbdd20aSAndroid Build Coastguard Worker if (capturedString.includes('--')) { 36*6dbdd20aSAndroid Build Coastguard Worker metricList = capturedString.split('--'); 37*6dbdd20aSAndroid Build Coastguard Worker } else { 38*6dbdd20aSAndroid Build Coastguard Worker metricList = [capturedString]; 39*6dbdd20aSAndroid Build Coastguard Worker } 40*6dbdd20aSAndroid Build Coastguard Worker return metricList.map((metric) => decodeURIComponent(metric)); 41*6dbdd20aSAndroid Build Coastguard Worker} 42*6dbdd20aSAndroid Build Coastguard Worker 43*6dbdd20aSAndroid Build Coastguard Workerlet metrics: string[]; 44*6dbdd20aSAndroid Build Coastguard Worker 45*6dbdd20aSAndroid Build Coastguard Worker/** 46*6dbdd20aSAndroid Build Coastguard Worker * Plugin that adds and pins the debug track for the metric passed 47*6dbdd20aSAndroid Build Coastguard Worker * For more context - 48*6dbdd20aSAndroid Build Coastguard Worker * This plugin reads the names of regressed metrics from the url upon loading 49*6dbdd20aSAndroid Build Coastguard Worker * It then checks the metric names against some handlers and if they 50*6dbdd20aSAndroid Build Coastguard Worker * match it accordingly adds the debug tracks for them 51*6dbdd20aSAndroid Build Coastguard Worker * This way when comparing two different perfetto traces before and after 52*6dbdd20aSAndroid Build Coastguard Worker * the regression, the user will not have to manually search for the 53*6dbdd20aSAndroid Build Coastguard Worker * slices related to the regressed metric 54*6dbdd20aSAndroid Build Coastguard Worker */ 55*6dbdd20aSAndroid Build Coastguard Workerexport default class implements PerfettoPlugin { 56*6dbdd20aSAndroid Build Coastguard Worker static readonly id = PLUGIN_ID; 57*6dbdd20aSAndroid Build Coastguard Worker static readonly dependencies = [AndroidCujsPlugin]; 58*6dbdd20aSAndroid Build Coastguard Worker 59*6dbdd20aSAndroid Build Coastguard Worker static onActivate(): void { 60*6dbdd20aSAndroid Build Coastguard Worker metrics = getMetricsFromHash(); 61*6dbdd20aSAndroid Build Coastguard Worker } 62*6dbdd20aSAndroid Build Coastguard Worker 63*6dbdd20aSAndroid Build Coastguard Worker async onTraceLoad(ctx: Trace) { 64*6dbdd20aSAndroid Build Coastguard Worker ctx.commands.registerCommand({ 65*6dbdd20aSAndroid Build Coastguard Worker id: 'dev.perfetto.PinAndroidPerfMetrics#PinAndroidPerfMetrics', 66*6dbdd20aSAndroid Build Coastguard Worker name: 'Add and Pin: Jank Metric Slice', 67*6dbdd20aSAndroid Build Coastguard Worker callback: async (metric) => { 68*6dbdd20aSAndroid Build Coastguard Worker metric = prompt('Metrics names (separated by comma)', ''); 69*6dbdd20aSAndroid Build Coastguard Worker if (metric === null) return; 70*6dbdd20aSAndroid Build Coastguard Worker const metricList = metric.split(','); 71*6dbdd20aSAndroid Build Coastguard Worker this.callHandlers(metricList, ctx); 72*6dbdd20aSAndroid Build Coastguard Worker }, 73*6dbdd20aSAndroid Build Coastguard Worker }); 74*6dbdd20aSAndroid Build Coastguard Worker if (metrics.length !== 0) { 75*6dbdd20aSAndroid Build Coastguard Worker this.callHandlers(metrics, ctx); 76*6dbdd20aSAndroid Build Coastguard Worker } 77*6dbdd20aSAndroid Build Coastguard Worker } 78*6dbdd20aSAndroid Build Coastguard Worker 79*6dbdd20aSAndroid Build Coastguard Worker private async callHandlers(metricsList: string[], ctx: Trace) { 80*6dbdd20aSAndroid Build Coastguard Worker // List of metrics that actually match some handler 81*6dbdd20aSAndroid Build Coastguard Worker const metricsToShow: MetricHandlerMatch[] = 82*6dbdd20aSAndroid Build Coastguard Worker this.getMetricsToShow(metricsList); 83*6dbdd20aSAndroid Build Coastguard Worker 84*6dbdd20aSAndroid Build Coastguard Worker if (metricsToShow.length === 0) { 85*6dbdd20aSAndroid Build Coastguard Worker return; 86*6dbdd20aSAndroid Build Coastguard Worker } 87*6dbdd20aSAndroid Build Coastguard Worker 88*6dbdd20aSAndroid Build Coastguard Worker await ctx.engine.query(JANK_CUJ_QUERY_PRECONDITIONS); 89*6dbdd20aSAndroid Build Coastguard Worker for (const {metricData, metricHandler} of metricsToShow) { 90*6dbdd20aSAndroid Build Coastguard Worker metricHandler.addMetricTrack(metricData, ctx); 91*6dbdd20aSAndroid Build Coastguard Worker } 92*6dbdd20aSAndroid Build Coastguard Worker } 93*6dbdd20aSAndroid Build Coastguard Worker 94*6dbdd20aSAndroid Build Coastguard Worker private getMetricsToShow(metricList: string[]): MetricHandlerMatch[] { 95*6dbdd20aSAndroid Build Coastguard Worker const sortedMetricList = [...metricList].sort(); 96*6dbdd20aSAndroid Build Coastguard Worker const validMetrics: MetricHandlerMatch[] = []; 97*6dbdd20aSAndroid Build Coastguard Worker const alreadyMatchedMetricData: Set<string> = new Set(); 98*6dbdd20aSAndroid Build Coastguard Worker for (const metric of sortedMetricList) { 99*6dbdd20aSAndroid Build Coastguard Worker for (const metricHandler of METRIC_HANDLERS) { 100*6dbdd20aSAndroid Build Coastguard Worker const metricData = metricHandler.match(metric); 101*6dbdd20aSAndroid Build Coastguard Worker if (!metricData) continue; 102*6dbdd20aSAndroid Build Coastguard Worker const jsonMetricData = this.metricDataToJson(metricData); 103*6dbdd20aSAndroid Build Coastguard Worker if (!alreadyMatchedMetricData.has(jsonMetricData)) { 104*6dbdd20aSAndroid Build Coastguard Worker alreadyMatchedMetricData.add(jsonMetricData); 105*6dbdd20aSAndroid Build Coastguard Worker validMetrics.push({ 106*6dbdd20aSAndroid Build Coastguard Worker metricData: metricData, 107*6dbdd20aSAndroid Build Coastguard Worker metricHandler: metricHandler, 108*6dbdd20aSAndroid Build Coastguard Worker }); 109*6dbdd20aSAndroid Build Coastguard Worker } 110*6dbdd20aSAndroid Build Coastguard Worker } 111*6dbdd20aSAndroid Build Coastguard Worker } 112*6dbdd20aSAndroid Build Coastguard Worker return validMetrics; 113*6dbdd20aSAndroid Build Coastguard Worker } 114*6dbdd20aSAndroid Build Coastguard Worker 115*6dbdd20aSAndroid Build Coastguard Worker private metricDataToJson(metricData: MetricData): string { 116*6dbdd20aSAndroid Build Coastguard Worker // Used to have a deterministic keys order. 117*6dbdd20aSAndroid Build Coastguard Worker return JSON.stringify(metricData, Object.keys(metricData).sort()); 118*6dbdd20aSAndroid Build Coastguard Worker } 119*6dbdd20aSAndroid Build Coastguard Worker} 120