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  BlockingCallMetricData,
18  MetricHandler,
19} from './metricUtils';
20import {Trace} from '../../../public/trace';
21import {addJankCUJDebugTrack} from '../../dev.perfetto.AndroidCujs';
22import {addDebugSliceTrack} from '../../../components/tracks/debug_tracks';
23
24class BlockingCallMetricHandler implements MetricHandler {
25  /**
26   * Match metric key & return parsed data if successful.
27   *
28   * @param {string} metricKey The metric key to match.
29   * @returns {BlockingCallMetricData | undefined} Parsed data or undefined if no match.
30   */
31  public match(metricKey: string): BlockingCallMetricData | undefined {
32    const matcher =
33      /perfetto_android_blocking_call-cuj-name-(?<process>.*)-name-(?<cujName>.*)-blocking_calls-name-(?<blockingCallName>([^\-]*))-(?<aggregation>.*)/;
34    const match = matcher.exec(metricKey);
35    if (!match?.groups) {
36      return undefined;
37    }
38    const metricData: BlockingCallMetricData = {
39      process: expandProcessName(match.groups.process),
40      cujName: match.groups.cujName,
41      blockingCallName: match.groups.blockingCallName,
42      aggregation: match.groups.aggregation,
43    };
44    return metricData;
45  }
46
47  /**
48   * Adds the debug tracks for Blocking Call metrics
49   *
50   * @param {BlockingCallMetricData} metricData Parsed metric data for the cuj scoped jank
51   * @param {Trace} ctx PluginContextTrace for trace related properties and methods
52   * @returns {void} Adds one track for Jank CUJ slice and one for Janky CUJ frames
53   */
54  public addMetricTrack(metricData: BlockingCallMetricData, ctx: Trace): void {
55    this.pinSingleCuj(ctx, metricData);
56    const config = this.blockingCallTrackConfig(metricData);
57    addDebugSliceTrack({trace: ctx, ...config});
58  }
59
60  private pinSingleCuj(ctx: Trace, metricData: BlockingCallMetricData) {
61    const trackName = `Jank CUJ: ${metricData.cujName}`;
62    addJankCUJDebugTrack(ctx, trackName, metricData.cujName);
63  }
64
65  private blockingCallTrackConfig(metricData: BlockingCallMetricData) {
66    const cuj = metricData.cujName;
67    const processName = metricData.process;
68    const blockingCallName = metricData.blockingCallName;
69
70    // TODO: b/296349525 - Migrate jank tables from run metrics to stdlib
71    const blockingCallDuringCujQuery = `
72  SELECT name, ts, dur
73  FROM main_thread_slices_scoped_to_cujs
74  WHERE process_name = "${processName}"
75      AND cuj_name = "${cuj}"
76      AND name = "${blockingCallName}"
77  `;
78
79    const trackName = 'Blocking calls in ' + processName;
80    return {
81      data: {
82        sqlSource: blockingCallDuringCujQuery,
83        columns: ['name', 'ts', 'dur'],
84      },
85      columns: {ts: 'ts', dur: 'dur', name: 'name'},
86      argColumns: ['name', 'ts', 'dur'],
87      trackName,
88    };
89  }
90}
91
92export const pinBlockingCallHandlerInstance = new BlockingCallMetricHandler();
93