Handler for fullTraceJank
Adds debug track for full trace jank given jank type and process when passed with specific url patterns. Handler used in the PinAndroidPerfMetrics plugin.
URL Examples -
ui.perfetto.dev/#dev.perfetto.PinAndroidPerfMetrics:metrics=perfetto_ft_launcher-missed_sf_frames-mean
Test: pinFullTrace_unittest.ts. Run using ./ui/run-unittests
Bug: b/337774166
Change-Id: I9e0cbe6bd93f39d38cc7c18dd329944adf37c5bc
diff --git a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts
new file mode 100644
index 0000000..afe75a9
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts
@@ -0,0 +1,143 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {
+ expandProcessName,
+ FullTraceMetricData,
+ JankType,
+ MetricHandler,
+} from './metricUtils';
+import {PluginContextTrace} from '../../../public';
+import {
+ addAndPinSliceTrack,
+ TrackType,
+} from '../../dev.perfetto.AndroidCujs/trackUtils';
+import {SimpleSliceTrackConfig} from '../../../frontend/simple_slice_track';
+import {PLUGIN_ID} from '../pluginId';
+
+class FullTraceJankMetricHandler implements MetricHandler {
+ /**
+ * Matches metric key & return parsed data if successful.
+ *
+ * @param {string} metricKey The metric key to match.
+ * @returns {FullTraceMetricData | undefined} Parsed data or undefined if no match.
+ */
+ public match(metricKey: string): FullTraceMetricData | undefined {
+ const matcher =
+ /perfetto_ft_(?<process>.*)-missed_(?<jankType>frames|sf_frames|app_frames)/;
+ const match = matcher.exec(metricKey);
+ if (!match?.groups) {
+ return undefined;
+ }
+ const metricData: FullTraceMetricData = {
+ process: expandProcessName(match.groups.process),
+ jankType: match.groups.jankType as JankType,
+ };
+ return metricData;
+ }
+
+ /**
+ * Adds the debug track for full trace jank metrics
+ * The track contains missed sf/app frames for the process
+ * registerStaticTrack used when plugin adds tracks onTraceload()
+ * addDebugSliceTrack used for adding tracks using the command
+ *
+ * @param {FullTraceMetricData} metricData Parsed metric data for the full trace jank
+ * @param {PluginContextTrace} ctx PluginContextTrace for trace related properties and methods
+ * @param {TrackType} type 'static' for onTraceload and 'debug' for command
+ * @returns {void} Adds one track for Jank slice
+ */
+ public async addMetricTrack(
+ metricData: FullTraceMetricData,
+ ctx: PluginContextTrace,
+ type: TrackType,
+ ) {
+ const INCLUDE_PREQUERY = `
+ INCLUDE PERFETTO MODULE android.frames.jank_type;
+ INCLUDE PERFETTO MODULE slices.slices;
+ `;
+ const uri = `${PLUGIN_ID}#FullTraceJank#${metricData}`;
+ const {config: fullTraceJankConfig, trackName: trackName} =
+ this.fullTraceJankConfig(metricData);
+ await ctx.engine.query(INCLUDE_PREQUERY);
+ addAndPinSliceTrack(ctx, fullTraceJankConfig, trackName, type, uri);
+ }
+
+ private fullTraceJankConfig(metricData: FullTraceMetricData): {
+ config: SimpleSliceTrackConfig;
+ trackName: string;
+ } {
+ let jankTypeFilter;
+ let jankTypeDisplayName = 'all';
+ if (metricData.jankType?.includes('app')) {
+ jankTypeFilter = ' android_is_app_jank_type(display_value)';
+ jankTypeDisplayName = 'app';
+ } else if (metricData.jankType?.includes('sf')) {
+ jankTypeFilter = ' android_is_sf_jank_type(display_value)';
+ jankTypeDisplayName = 'sf';
+ }
+ const processName = metricData.process;
+
+ // TODO: b/324245198 - Refactor when jank_type added to android_frame_stats
+ const fullTraceJankQuery = `
+ WITH filtered_args AS (
+ SELECT DISTINCT arg_set_id
+ FROM args
+ WHERE key = 'Jank type'
+ ${jankTypeFilter ? 'AND ' + jankTypeFilter : ''}
+ )
+ SELECT
+ name,
+ ts as ts,
+ dur as dur,
+ track_id as track_id,
+ id as slice_id,
+ thread_dur as thread_dur,
+ category,
+ thread_name,
+ tid as tid,
+ process_name,
+ pid as pid
+ FROM _slice_with_thread_and_process_info
+ JOIN filtered_args ON filtered_args.arg_set_id = _slice_with_thread_and_process_info.arg_set_id
+ WHERE process_name = '${processName}'`;
+ const fullTraceJankColumns = [
+ 'name',
+ 'ts',
+ 'dur',
+ 'track_id',
+ 'slice_id',
+ 'thread_dur',
+ 'category',
+ 'thread_name',
+ 'tid',
+ 'process_name',
+ 'pid',
+ ];
+ const fullTraceJankConfig: SimpleSliceTrackConfig = {
+ data: {
+ sqlSource: fullTraceJankQuery,
+ columns: fullTraceJankColumns,
+ },
+ columns: {ts: 'ts', dur: 'dur', name: 'name'},
+ argColumns: fullTraceJankColumns,
+ };
+
+ const trackName = jankTypeDisplayName + ' missed frames in ' + processName;
+
+ return {config: fullTraceJankConfig, trackName: trackName};
+ }
+}
+
+export const pinFullTraceJankInstance = new FullTraceJankMetricHandler();