1// Copyright (C) 2022 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 {v4 as uuidv4} from 'uuid'; 16 17import {Actions, AddTrackArgs} from '../common/actions'; 18import {Engine} from '../common/engine'; 19import {NUM} from '../common/query_result'; 20import {InThreadTrackSortKey} from '../common/state'; 21import {globals} from '../frontend/globals'; 22import { 23 VISUALISED_ARGS_SLICE_TRACK_KIND, 24} from '../tracks/visualised_args/index'; 25 26import {Controller} from './controller'; 27 28export interface VisualisedArgControllerArgs { 29 argName: string; 30 engine: Engine; 31} 32 33export class VisualisedArgController extends Controller<'init'|'running'> { 34 private engine: Engine; 35 private argName: string; 36 private escapedArgName: string; 37 private tableName: string; 38 private addedTrackIds: string[]; 39 40 constructor(args: VisualisedArgControllerArgs) { 41 super('init'); 42 this.argName = args.argName; 43 this.engine = args.engine; 44 this.escapedArgName = this.argName.replace(/[^a-zA-Z]/g, '_'); 45 this.tableName = `__arg_visualisation_helper_${this.escapedArgName}_slice`; 46 this.addedTrackIds = []; 47 } 48 49 onDestroy() { 50 this.engine.query(`drop table if exists ${this.tableName}`); 51 globals.dispatch( 52 Actions.removeVisualisedArgTracks({trackIds: this.addedTrackIds})); 53 } 54 55 async createTracks() { 56 const result = await this.engine.query(` 57 drop table if exists ${this.tableName}; 58 59 create table ${this.tableName} as 60 with slice_with_arg as ( 61 select 62 slice.id, 63 slice.track_id, 64 slice.ts, 65 slice.dur, 66 slice.thread_dur, 67 NULL as cat, 68 args.display_value as name 69 from slice 70 join args using (arg_set_id) 71 where args.key='${this.argName}' 72 ) 73 select 74 *, 75 (select count() 76 from ancestor_slice(s1.id) s2 77 join slice_with_arg s3 on s2.id=s3.id 78 ) as depth 79 from slice_with_arg s1 80 order by id; 81 82 select 83 track_id as trackId, 84 max(depth) as maxDepth 85 from ${this.tableName} 86 group by track_id; 87 `); 88 89 const tracksToAdd: AddTrackArgs[] = []; 90 const it = result.iter({'trackId': NUM, 'maxDepth': NUM}); 91 for (; it.valid(); it.next()) { 92 const track = 93 globals.state 94 .tracks[globals.state.uiTrackIdByTraceTrackId[it.trackId]]; 95 const utid = (track.trackSortKey as {utid?: number}).utid; 96 const id = uuidv4(); 97 this.addedTrackIds.push(id); 98 tracksToAdd.push({ 99 id, 100 trackGroup: track.trackGroup, 101 engineId: this.engine.id, 102 kind: VISUALISED_ARGS_SLICE_TRACK_KIND, 103 name: this.argName, 104 trackSortKey: utid === undefined ? 105 track.trackSortKey : 106 {utid, priority: InThreadTrackSortKey.VISUALISED_ARGS_TRACK}, 107 config: { 108 maxDepth: it.maxDepth, 109 namespace: `__arg_visualisation_helper_${this.escapedArgName}`, 110 trackId: it.trackId, 111 argName: this.argName, 112 tid: (track.config as {tid?: number}).tid, 113 }, 114 }); 115 } 116 globals.dispatch(Actions.addTracks({tracks: tracksToAdd})); 117 globals.dispatch(Actions.sortThreadTracks({})); 118 } 119 120 run() { 121 switch (this.state) { 122 case 'init': 123 this.createTracks(); 124 this.setState('running'); 125 break; 126 case 'running': 127 // Nothing to do here. 128 break; 129 default: 130 throw new Error(`Unexpected state ${this.state}`); 131 } 132 } 133} 134