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