• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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