• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, addLatencyCUJDebugTrack} from '../../dev.perfetto.AndroidCujs';
22import {addDebugSliceTrack} from '../../../components/tracks/debug_tracks';
23
24class BlockingCallMetricHandler implements MetricHandler {
25  /**
26   * Matches metric key for blocking call and per-frame blocking call metrics & return parsed data
27   * if successful.
28   *
29   * @param {string} metricKey The metric key to match.
30   * @returns {BlockingCallMetricData | undefined} Parsed data or undefined if no match.
31   */
32  public match(metricKey: string): BlockingCallMetricData | undefined {
33    const matcher =
34      /perfetto_android_blocking_call(?:_per_frame)?-cuj-name-(?<process>.*)-name-(?<cujName>.*)-blocking_calls-name-(?<blockingCallName>([^\-]*))-(?<aggregation>.*)/;
35    const match = matcher.exec(metricKey);
36    if (!match?.groups) {
37      return undefined;
38    }
39    const metricData: BlockingCallMetricData = {
40      process: expandProcessName(match.groups.process),
41      cujName: match.groups.cujName,
42      blockingCallName: match.groups.blockingCallName,
43      aggregation: match.groups.aggregation,
44    };
45    return metricData;
46  }
47
48  /**
49   * Adds the debug tracks for Blocking Call metrics
50   *
51   * @param {BlockingCallMetricData} metricData Parsed metric data for the cuj scoped jank
52   * @param {Trace} ctx PluginContextTrace for trace related properties and methods
53   * @returns {void} Adds one track for Jank CUJ slice and one for Janky CUJ frames
54   */
55  public addMetricTrack(metricData: BlockingCallMetricData, ctx: Trace): void {
56    this.pinSingleCuj(ctx, metricData);
57    const config = this.blockingCallTrackConfig(metricData);
58    addDebugSliceTrack({trace: ctx, ...config});
59  }
60
61  private async pinSingleCuj(ctx: Trace, metricData: BlockingCallMetricData) {
62    const jankTrackName = `Jank CUJ: ${metricData.cujName}`;
63    const latencyTrackName = `Latency CUJ: ${metricData.cujName}`;
64    // TODO: b/296349525 - Refactor once CUJ tables are migrated to stdlib
65    // Currently, we try to pin a Jank CUJ track and if that fails we add
66    // a Latency CUJ track. We can instead look up a single CUJ table to
67    // better determine what to query and pin.
68    const jankCujPinned = await addJankCUJDebugTrack(ctx, jankTrackName, metricData.cujName);
69    if (!jankCujPinned) {
70      addLatencyCUJDebugTrack(ctx, latencyTrackName, metricData.cujName);
71    }
72  }
73
74  private blockingCallTrackConfig(metricData: BlockingCallMetricData) {
75    const cuj = metricData.cujName;
76    const processName = metricData.process;
77    const blockingCallName = metricData.blockingCallName;
78
79    // TODO: b/296349525 - Migrate jank tables from run metrics to stdlib
80    const blockingCallDuringCujQuery = `
81  SELECT name, ts, dur
82  FROM main_thread_slices_scoped_to_cujs
83  WHERE process_name = "${processName}"
84      AND cuj_name = "${cuj}"
85      AND name = "${blockingCallName}"
86  `;
87
88    const trackName = 'Blocking calls in ' + processName;
89    return {
90      data: {
91        sqlSource: blockingCallDuringCujQuery,
92        columns: ['name', 'ts', 'dur'],
93      },
94      columns: {ts: 'ts', dur: 'dur', name: 'name'},
95      argColumns: ['name', 'ts', 'dur'],
96      trackName,
97    };
98  }
99}
100
101export const pinBlockingCallHandlerInstance = new BlockingCallMetricHandler();
102