• 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 {NUM} from '../../common/query_result';
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 = [];
50        for (const it = threadQuery.iter({utid: NUM}); it.valid(); it.next()) {
51          utids.push(it.utid);
52        }
53      }
54
55      const trackQuery = await this.query(
56          `select id from thread_track where utid in (${utids.join(',')})`);
57      const tracks = [];
58      for (const it = trackQuery.iter({id: NUM}); it.valid(); it.next()) {
59        tracks.push(it.id);
60      }
61
62      const processSliceView = this.tableName('process_slice_view');
63      await this.query(
64          `create view ${processSliceView} as ` +
65          // 0 as cpu is a dummy column to perform span join on.
66          `select ts, dur/${utids.length} as dur ` +
67          `from slice s ` +
68          `where depth = 0 and track_id in ` +
69          `(${tracks.join(',')})`);
70      await this.query(`create virtual table ${this.tableName('span')}
71          using span_join(${processSliceView},
72                          ${this.tableName('window')});`);
73      this.setup = true;
74    }
75
76    // |resolution| is in s/px we want # ns for 10px window:
77    // Max value with 1 so we don't end up with resolution 0.
78    const bucketSizeNs = Math.max(1, Math.round(resolution * 10 * 1e9));
79    const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
80    const windowDurNs = Math.max(1, endNs - windowStartNs);
81
82    await this.query(`update ${this.tableName('window')} set
83      window_start=${windowStartNs},
84      window_dur=${windowDurNs},
85      quantum=${bucketSizeNs}
86      where rowid = 0;`);
87
88    return this.computeSummary(
89        fromNs(windowStartNs), end, resolution, bucketSizeNs);
90  }
91
92  private async computeSummary(
93      start: number, end: number, resolution: number,
94      bucketSizeNs: number): Promise<Data> {
95    const startNs = toNs(start);
96    const endNs = toNs(end);
97    const numBuckets =
98        Math.min(Math.ceil((endNs - startNs) / bucketSizeNs), LIMIT);
99
100    const query = `select
101      quantum_ts as bucket,
102      sum(dur)/cast(${bucketSizeNs} as float) as utilization
103      from ${this.tableName('span')}
104      group by quantum_ts
105      limit ${LIMIT}`;
106
107    const summary: Data = {
108      start,
109      end,
110      resolution,
111      length: numBuckets,
112      bucketSizeSeconds: fromNs(bucketSizeNs),
113      utilizations: new Float64Array(numBuckets),
114    };
115
116    const queryRes = await this.query(query);
117    const it = queryRes.iter({bucket: NUM, utilization: NUM});
118    for (; it.valid(); it.next()) {
119      const bucket = it.bucket;
120      if (bucket > numBuckets) {
121        continue;
122      }
123      summary.utilizations[bucket] = it.utilization;
124    }
125
126    return summary;
127  }
128
129  onDestroy(): void {
130    if (this.setup) {
131      this.query(`drop table ${this.tableName('window')}`);
132      this.query(`drop table ${this.tableName('span')}`);
133      this.setup = false;
134    }
135  }
136}
137
138trackControllerRegistry.register(ProcessSummaryTrackController);
139