• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 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 {slowlyCountRows} from '../../common/query_iterator';
16import {fromNs, toNs} from '../../common/time';
17import {LIMIT} from '../../common/track_data';
18import {
19  TrackController,
20  trackControllerRegistry
21} from '../../controller/track_controller';
22
23import {
24  Config,
25  Data,
26  PROCESS_SUMMARY_TRACK,
27} from './common';
28
29// This is the summary displayed when a process only contains chrome slices
30// and no cpu scheduling.
31
32class ProcessSummaryTrackController extends TrackController<Config, Data> {
33  static readonly kind = PROCESS_SUMMARY_TRACK;
34  private setup = false;
35
36  async onBoundsChange(start: number, end: number, resolution: number):
37      Promise<Data> {
38    const startNs = toNs(start);
39    const endNs = toNs(end);
40
41    if (this.setup === false) {
42      await this.query(
43          `create virtual table ${this.tableName('window')} using window;`);
44
45      let utids = [this.config.utid];
46      if (this.config.upid) {
47        const threadQuery = await this.query(
48            `select utid from thread where upid=${this.config.upid}`);
49        utids = threadQuery.columns[0].longValues!;
50      }
51
52      const trackQuery = await this.query(
53          `select id from thread_track where utid in (${utids.join(',')})`);
54      const tracks = trackQuery.columns[0].longValues!;
55
56      const processSliceView = this.tableName('process_slice_view');
57      await this.query(
58          `create view ${processSliceView} as ` +
59          // 0 as cpu is a dummy column to perform span join on.
60          `select ts, dur/${utids.length} as dur ` +
61          `from slice s ` +
62          `where depth = 0 and track_id in ` +
63          `(${tracks.join(',')})`);
64      await this.query(`create virtual table ${this.tableName('span')}
65          using span_join(${processSliceView},
66                          ${this.tableName('window')});`);
67      this.setup = true;
68    }
69
70    // |resolution| is in s/px we want # ns for 10px window:
71    // Max value with 1 so we don't end up with resolution 0.
72    const bucketSizeNs = Math.max(1, Math.round(resolution * 10 * 1e9));
73    const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
74    const windowDurNs = Math.max(1, endNs - windowStartNs);
75
76    this.query(`update ${this.tableName('window')} set
77      window_start=${windowStartNs},
78      window_dur=${windowDurNs},
79      quantum=${bucketSizeNs}
80      where rowid = 0;`);
81
82    return this.computeSummary(
83        fromNs(windowStartNs), end, resolution, bucketSizeNs);
84  }
85
86  private async computeSummary(
87      start: number, end: number, resolution: number,
88      bucketSizeNs: number): Promise<Data> {
89    const startNs = toNs(start);
90    const endNs = toNs(end);
91    const numBuckets =
92        Math.min(Math.ceil((endNs - startNs) / bucketSizeNs), LIMIT);
93
94    const query = `select
95      quantum_ts as bucket,
96      sum(dur)/cast(${bucketSizeNs} as float) as utilization
97      from ${this.tableName('span')}
98      group by quantum_ts
99      limit ${LIMIT}`;
100
101    const rawResult = await this.query(query);
102    const numRows = slowlyCountRows(rawResult);
103
104    const summary: Data = {
105      start,
106      end,
107      resolution,
108      length: numBuckets,
109      bucketSizeSeconds: fromNs(bucketSizeNs),
110      utilizations: new Float64Array(numBuckets),
111    };
112    const cols = rawResult.columns;
113    for (let row = 0; row < numRows; row++) {
114      const bucket = +cols[0].longValues![row];
115      if (bucket > numBuckets) {
116        continue;
117      }
118      summary.utilizations[bucket] = +cols[1].doubleValues![row];
119    }
120    return summary;
121  }
122
123  onDestroy(): void {
124    if (this.setup) {
125      this.query(`drop table ${this.tableName('window')}`);
126      this.query(`drop table ${this.tableName('span')}`);
127      this.setup = false;
128    }
129  }
130}
131
132trackControllerRegistry.register(ProcessSummaryTrackController);
133