1// Copyright (C) 2021 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 ACTUAL_FRAMES_SLICE_TRACK_KIND, 17 EXPECTED_FRAMES_SLICE_TRACK_KIND, 18} from '../../public/track_kinds'; 19import {Trace} from '../../public/trace'; 20import {PerfettoPlugin} from '../../public/plugin'; 21import {getTrackName} from '../../public/utils'; 22import {TrackNode} from '../../public/workspace'; 23import {NUM, NUM_NULL, STR, STR_NULL} from '../../trace_processor/query_result'; 24import {ActualFramesTrack} from './actual_frames_track'; 25import {ExpectedFramesTrack} from './expected_frames_track'; 26import {FrameSelectionAggregator} from './frame_selection_aggregator'; 27import ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups'; 28 29export default class implements PerfettoPlugin { 30 static readonly id = 'dev.perfetto.Frames'; 31 static readonly dependencies = [ProcessThreadGroupsPlugin]; 32 33 async onTraceLoad(ctx: Trace): Promise<void> { 34 this.addExpectedFrames(ctx); 35 this.addActualFrames(ctx); 36 ctx.selection.registerAreaSelectionAggregator( 37 new FrameSelectionAggregator(), 38 ); 39 } 40 41 async addExpectedFrames(ctx: Trace): Promise<void> { 42 const {engine} = ctx; 43 const result = await engine.query(` 44 select 45 upid, 46 t.name as trackName, 47 t.track_ids as trackIds, 48 process.name as processName, 49 process.pid as pid, 50 __max_layout_depth(t.track_count, t.track_ids) as maxDepth 51 from _process_track_summary_by_upid_and_parent_id_and_name t 52 join process using(upid) 53 where t.name = "Expected Timeline" 54 `); 55 56 const it = result.iter({ 57 upid: NUM, 58 trackName: STR_NULL, 59 trackIds: STR, 60 processName: STR_NULL, 61 pid: NUM_NULL, 62 maxDepth: NUM, 63 }); 64 65 for (; it.valid(); it.next()) { 66 const upid = it.upid; 67 const trackName = it.trackName; 68 const rawTrackIds = it.trackIds; 69 const trackIds = rawTrackIds.split(',').map((v) => Number(v)); 70 const processName = it.processName; 71 const pid = it.pid; 72 const maxDepth = it.maxDepth; 73 74 const title = getTrackName({ 75 name: trackName, 76 upid, 77 pid, 78 processName, 79 kind: 'ExpectedFrames', 80 }); 81 82 const uri = `/process_${upid}/expected_frames`; 83 ctx.tracks.registerTrack({ 84 uri, 85 title, 86 track: new ExpectedFramesTrack(ctx, maxDepth, uri, trackIds), 87 tags: { 88 trackIds, 89 upid, 90 kind: EXPECTED_FRAMES_SLICE_TRACK_KIND, 91 }, 92 }); 93 const group = ctx.plugins 94 .getPlugin(ProcessThreadGroupsPlugin) 95 .getGroupForProcess(upid); 96 const track = new TrackNode({uri, title, sortOrder: -50}); 97 group?.addChildInOrder(track); 98 } 99 } 100 101 async addActualFrames(ctx: Trace): Promise<void> { 102 const {engine} = ctx; 103 const result = await engine.query(` 104 select 105 upid, 106 t.name as trackName, 107 t.track_ids as trackIds, 108 process.name as processName, 109 process.pid as pid, 110 __max_layout_depth(t.track_count, t.track_ids) as maxDepth 111 from _process_track_summary_by_upid_and_parent_id_and_name t 112 join process using(upid) 113 where t.name = "Actual Timeline" 114 `); 115 116 const it = result.iter({ 117 upid: NUM, 118 trackName: STR_NULL, 119 trackIds: STR, 120 processName: STR_NULL, 121 pid: NUM_NULL, 122 maxDepth: NUM_NULL, 123 }); 124 for (; it.valid(); it.next()) { 125 const upid = it.upid; 126 const trackName = it.trackName; 127 const rawTrackIds = it.trackIds; 128 const trackIds = rawTrackIds.split(',').map((v) => Number(v)); 129 const processName = it.processName; 130 const pid = it.pid; 131 const maxDepth = it.maxDepth; 132 133 if (maxDepth === null) { 134 // If there are no slices in this track, skip it. 135 continue; 136 } 137 138 const kind = 'ActualFrames'; 139 const title = getTrackName({ 140 name: trackName, 141 upid, 142 pid, 143 processName, 144 kind, 145 }); 146 147 const uri = `/process_${upid}/actual_frames`; 148 ctx.tracks.registerTrack({ 149 uri, 150 title, 151 track: new ActualFramesTrack(ctx, maxDepth, uri, trackIds), 152 tags: { 153 upid, 154 trackIds, 155 kind: ACTUAL_FRAMES_SLICE_TRACK_KIND, 156 }, 157 }); 158 const group = ctx.plugins 159 .getPlugin(ProcessThreadGroupsPlugin) 160 .getGroupForProcess(upid); 161 const track = new TrackNode({uri, title, sortOrder: -50}); 162 group?.addChildInOrder(track); 163 } 164 } 165} 166