|  | // Copyright (C) 2022 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 {v4 as uuidv4} from 'uuid'; | 
|  |  | 
|  | import {Actions, AddTrackArgs} from '../common/actions'; | 
|  | import {Engine} from '../common/engine'; | 
|  | import {NUM} from '../common/query_result'; | 
|  | import {InThreadTrackSortKey} from '../common/state'; | 
|  | import {globals} from '../frontend/globals'; | 
|  | import { | 
|  | VISUALISED_ARGS_SLICE_TRACK_KIND, | 
|  | } from '../tracks/visualised_args/index'; | 
|  |  | 
|  | import {Controller} from './controller'; | 
|  |  | 
|  | export interface VisualisedArgControllerArgs { | 
|  | argName: string; | 
|  | engine: Engine; | 
|  | } | 
|  |  | 
|  | export class VisualisedArgController extends Controller<'init'|'running'> { | 
|  | private engine: Engine; | 
|  | private argName: string; | 
|  | private escapedArgName: string; | 
|  | private tableName: string; | 
|  | private addedTrackIds: string[]; | 
|  |  | 
|  | constructor(args: VisualisedArgControllerArgs) { | 
|  | super('init'); | 
|  | this.argName = args.argName; | 
|  | this.engine = args.engine; | 
|  | this.escapedArgName = this.argName.replace(/[^a-zA-Z]/g, '_'); | 
|  | this.tableName = `__arg_visualisation_helper_${this.escapedArgName}_slice`; | 
|  | this.addedTrackIds = []; | 
|  | } | 
|  |  | 
|  | onDestroy() { | 
|  | this.engine.query(`drop table if exists ${this.tableName}`); | 
|  | globals.dispatch( | 
|  | Actions.removeVisualisedArgTracks({trackIds: this.addedTrackIds})); | 
|  | } | 
|  |  | 
|  | async createTracks() { | 
|  | const result = await this.engine.query(` | 
|  | drop table if exists ${this.tableName}; | 
|  |  | 
|  | create table ${this.tableName} as | 
|  | with slice_with_arg as ( | 
|  | select | 
|  | slice.id, | 
|  | slice.track_id, | 
|  | slice.ts, | 
|  | slice.dur, | 
|  | slice.thread_dur, | 
|  | NULL as cat, | 
|  | args.display_value as name | 
|  | from slice | 
|  | join args using (arg_set_id) | 
|  | where args.key='${this.argName}' | 
|  | ) | 
|  | select | 
|  | *, | 
|  | (select count() | 
|  | from ancestor_slice(s1.id) s2 | 
|  | join slice_with_arg s3 on s2.id=s3.id | 
|  | ) as depth | 
|  | from slice_with_arg s1 | 
|  | order by id; | 
|  |  | 
|  | select | 
|  | track_id as trackId, | 
|  | max(depth) as maxDepth | 
|  | from ${this.tableName} | 
|  | group by track_id; | 
|  | `); | 
|  |  | 
|  | const tracksToAdd: AddTrackArgs[] = []; | 
|  | const it = result.iter({'trackId': NUM, 'maxDepth': NUM}); | 
|  | for (; it.valid(); it.next()) { | 
|  | const track = | 
|  | globals.state | 
|  | .tracks[globals.state.uiTrackIdByTraceTrackId[it.trackId]]; | 
|  | const utid = (track.trackSortKey as {utid?: number}).utid; | 
|  | const id = uuidv4(); | 
|  | this.addedTrackIds.push(id); | 
|  | tracksToAdd.push({ | 
|  | id, | 
|  | trackGroup: track.trackGroup, | 
|  | engineId: this.engine.id, | 
|  | kind: VISUALISED_ARGS_SLICE_TRACK_KIND, | 
|  | name: this.argName, | 
|  | trackSortKey: utid === undefined ? | 
|  | track.trackSortKey : | 
|  | {utid, priority: InThreadTrackSortKey.VISUALISED_ARGS_TRACK}, | 
|  | config: { | 
|  | maxDepth: it.maxDepth, | 
|  | namespace: `__arg_visualisation_helper_${this.escapedArgName}`, | 
|  | trackId: it.trackId, | 
|  | argName: this.argName, | 
|  | tid: (track.config as {tid?: number}).tid, | 
|  | }, | 
|  | }); | 
|  | } | 
|  | globals.dispatch(Actions.addTracks({tracks: tracksToAdd})); | 
|  | globals.dispatch(Actions.sortThreadTracks({})); | 
|  | } | 
|  |  | 
|  | run() { | 
|  | switch (this.state) { | 
|  | case 'init': | 
|  | this.createTracks(); | 
|  | this.setState('running'); | 
|  | break; | 
|  | case 'running': | 
|  | // Nothing to do here. | 
|  | break; | 
|  | default: | 
|  | throw new Error(`Unexpected state ${this.state}`); | 
|  | } | 
|  | } | 
|  | } |