1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import { 16 expandProcessName, 17 FullTraceMetricData, 18 JankType, 19 MetricHandler, 20} from './metricUtils'; 21import {Trace} from '../../../public/trace'; 22import {addDebugSliceTrack} from '../../../components/tracks/debug_tracks'; 23 24class FullTraceJankMetricHandler implements MetricHandler { 25 /** 26 * Matches metric key & return parsed data if successful. 27 * 28 * @param {string} metricKey The metric key to match. 29 * @returns {FullTraceMetricData | undefined} Parsed data or undefined if no match. 30 */ 31 public match(metricKey: string): FullTraceMetricData | undefined { 32 const matcher = 33 /perfetto_ft_(?<process>.*)-missed_(?<jankType>frames|sf_frames|app_frames)/; 34 const match = matcher.exec(metricKey); 35 if (!match?.groups) { 36 return undefined; 37 } 38 const metricData: FullTraceMetricData = { 39 process: expandProcessName(match.groups.process), 40 jankType: match.groups.jankType as JankType, 41 }; 42 return metricData; 43 } 44 45 /** 46 * Adds the debug track for full trace jank metrics 47 * 48 * @param {FullTraceMetricData} metricData Parsed metric data for the full trace jank 49 * @param {Trace} ctx PluginContextTrace for trace related properties and methods 50 * @returns {void} Adds one track for Jank slice 51 */ 52 public async addMetricTrack(metricData: FullTraceMetricData, ctx: Trace) { 53 const INCLUDE_PREQUERY = ` 54 INCLUDE PERFETTO MODULE android.frames.jank_type; 55 INCLUDE PERFETTO MODULE slices.slices; 56 `; 57 const config = this.fullTraceJankConfig(metricData); 58 await ctx.engine.query(INCLUDE_PREQUERY); 59 addDebugSliceTrack({trace: ctx, ...config}); 60 } 61 62 private fullTraceJankConfig(metricData: FullTraceMetricData) { 63 let jankTypeFilter; 64 let jankTypeDisplayName; 65 if (metricData.jankType?.includes('app')) { 66 jankTypeFilter = ' android_is_app_jank_type(display_value)'; 67 jankTypeDisplayName = 'app'; 68 } else if (metricData.jankType?.includes('sf')) { 69 jankTypeFilter = ' android_is_sf_jank_type(display_value)'; 70 jankTypeDisplayName = 'sf'; 71 } else { 72 jankTypeFilter = " display_value != 'None'"; 73 jankTypeDisplayName = 'all'; 74 } 75 const processName = metricData.process; 76 77 // TODO: b/324245198 - Refactor when jank_type added to android_frame_stats 78 const fullTraceJankQuery = ` 79 WITH filtered_args AS ( 80 SELECT DISTINCT arg_set_id 81 FROM args 82 WHERE key = 'Jank type' 83 ${jankTypeFilter ? 'AND ' + jankTypeFilter : ''} 84 ) 85 SELECT 86 name, 87 ts as ts, 88 dur as dur, 89 track_id as track_id, 90 id as slice_id, 91 thread_dur as thread_dur, 92 category, 93 thread_name, 94 tid as tid, 95 process_name, 96 pid as pid 97 FROM _slice_with_thread_and_process_info 98 JOIN filtered_args ON filtered_args.arg_set_id = _slice_with_thread_and_process_info.arg_set_id 99 WHERE process_name = '${processName}'`; 100 const fullTraceJankColumns = [ 101 'name', 102 'ts', 103 'dur', 104 'track_id', 105 'slice_id', 106 'thread_dur', 107 'category', 108 'thread_name', 109 'tid', 110 'process_name', 111 'pid', 112 ]; 113 114 const trackName = jankTypeDisplayName + ' missed frames in ' + processName; 115 116 return { 117 data: { 118 sqlSource: fullTraceJankQuery, 119 columns: fullTraceJankColumns, 120 }, 121 columns: {ts: 'ts', dur: 'dur', name: 'name'}, 122 argColumns: fullTraceJankColumns, 123 tableName: trackName, 124 }; 125 } 126} 127 128export const pinFullTraceJankInstance = new FullTraceJankMetricHandler(); 129