1// Copyright (C) 2023 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 {exists} from '../../base/utils'; 16import {Engine} from '../../trace_processor/engine'; 17import {STR} from '../../trace_processor/query_result'; 18 19export enum CauseProcess { 20 UNKNOWN, 21 BROWSER = 'Browser', 22 RENDERER = 'Renderer', 23 GPU = 'GPU', 24} 25 26export enum CauseThread { 27 UNKNOWN, 28 BROWSER_MAIN = 'CrBrowserMain', 29 RENDERER_MAIN = 'CrRendererMain', 30 COMPOSITOR = 'Compositor', 31 CHROME_CHILD_IO_THREAD = 'Chrome_ChildIOThread', 32 VIZ_COMPOSITOR = 'VizCompositorThread', 33 SURFACE_FLINGER = 'surfaceflinger', 34} 35 36export interface ScrollJankCause { 37 description: string; 38 process: CauseProcess; 39 thread: CauseThread; 40} 41 42export interface EventLatencyStageDetails { 43 description: string; 44 jankCauses: ScrollJankCause[]; 45} 46 47export interface ScrollJankCauseMapInternal { 48 // Key corresponds with the EventLatency stage. 49 [key: string]: EventLatencyStageDetails; 50} 51 52function getScrollJankProcess(process: string): CauseProcess { 53 switch (process) { 54 case CauseProcess.BROWSER: 55 return CauseProcess.BROWSER; 56 case CauseProcess.RENDERER: 57 return CauseProcess.RENDERER; 58 case CauseProcess.GPU: 59 return CauseProcess.GPU; 60 default: 61 return CauseProcess.UNKNOWN; 62 } 63} 64 65function getScrollJankThread(thread: string): CauseThread { 66 switch (thread) { 67 case CauseThread.BROWSER_MAIN: 68 return CauseThread.BROWSER_MAIN; 69 case CauseThread.RENDERER_MAIN: 70 return CauseThread.RENDERER_MAIN; 71 case CauseThread.CHROME_CHILD_IO_THREAD: 72 return CauseThread.CHROME_CHILD_IO_THREAD; 73 case CauseThread.COMPOSITOR: 74 return CauseThread.COMPOSITOR; 75 case CauseThread.VIZ_COMPOSITOR: 76 return CauseThread.VIZ_COMPOSITOR; 77 case CauseThread.SURFACE_FLINGER: 78 return CauseThread.SURFACE_FLINGER; 79 default: 80 return CauseThread.UNKNOWN; 81 } 82} 83 84export class ScrollJankCauseMap { 85 private static instance: ScrollJankCauseMap; 86 private causes: ScrollJankCauseMapInternal; 87 88 private constructor() { 89 this.causes = {}; 90 } 91 92 private async initializeCauseMap(engine: Engine) { 93 const queryResult = await engine.query(` 94 INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_cause_map; 95 96 SELECT 97 IFNULL(name, '') AS name, 98 IFNULL(description, '') AS description, 99 IFNULL(cause_process, '') AS causeProcess, 100 IFNULL(cause_thread, '') AS causeThread, 101 IFNULL(cause_description, '') AS causeDescription 102 FROM chrome_scroll_jank_causes_with_event_latencies; 103 `); 104 105 const iter = queryResult.iter({ 106 name: STR, 107 description: STR, 108 causeProcess: STR, 109 causeThread: STR, 110 causeDescription: STR, 111 }); 112 113 for (; iter.valid(); iter.next()) { 114 const eventLatencyStage = iter.name; 115 if (!(eventLatencyStage in this.causes)) { 116 this.causes[eventLatencyStage] = { 117 description: iter.description, 118 jankCauses: [] as ScrollJankCause[], 119 }; 120 } 121 122 const causeProcess = getScrollJankProcess(iter.causeProcess); 123 const causeThread = getScrollJankThread(iter.causeThread); 124 125 this.causes[eventLatencyStage].jankCauses.push({ 126 description: iter.causeDescription, 127 process: causeProcess, 128 thread: causeThread, 129 }); 130 } 131 } 132 133 // Must be called before this item is accessed, as the object is populated 134 // from SQL data. 135 public static async initialize(engine: Engine) { 136 if (!exists(ScrollJankCauseMap.instance)) { 137 ScrollJankCauseMap.instance = new ScrollJankCauseMap(); 138 await ScrollJankCauseMap.instance.initializeCauseMap(engine); 139 } 140 } 141 142 public static getEventLatencyDetails( 143 eventLatency: string, 144 ): EventLatencyStageDetails | undefined { 145 if (eventLatency in ScrollJankCauseMap.instance.causes) { 146 return ScrollJankCauseMap.instance.causes[eventLatency]; 147 } 148 return undefined; 149 } 150} 151