xref: /aosp_15_r20/external/perfetto/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_jank_cause_map.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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