• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 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 {fromNs, toNs} from '../../common/time';
16import {LIMIT} from '../../common/track_data';
17
18import {
19  TrackController,
20  trackControllerRegistry
21} from '../../controller/track_controller';
22
23import {
24  Config,
25  CPU_FREQ_TRACK_KIND,
26  Data,
27} from './common';
28
29class CpuFreqTrackController extends TrackController<Config, Data> {
30  static readonly kind = CPU_FREQ_TRACK_KIND;
31  private setup = false;
32  private maximumValueSeen = 0;
33
34  async onBoundsChange(start: number, end: number, resolution: number):
35      Promise<Data> {
36    const startNs = toNs(start);
37    const endNs = toNs(end);
38
39    if (!this.setup) {
40      const result = await this.query(`
41        select max(value)
42        from counter
43        where track_id = ${this.config.freqTrackId}`);
44      this.maximumValueSeen = +result.columns[0].doubleValues![0];
45
46      await this.query(
47        `create virtual table ${this.tableName('window')} using window;`);
48
49      await this.query(`create view ${this.tableName('freq')}
50          as select
51            ts,
52            dur,
53            value as freq_value
54          from experimental_counter_dur c
55          where track_id = ${this.config.freqTrackId};
56      `);
57
58      // If there is no idle track, just make the idle track a single row
59      // which spans the entire time range.
60      if (this.config.idleTrackId === undefined) {
61        await this.query(`create view ${this.tableName('idle')} as
62           select
63             0 as ts,
64             ${Number.MAX_SAFE_INTEGER} as dur,
65             -1 as idle_value;
66          `);
67      } else {
68        await this.query(`create view ${this.tableName('idle')}
69          as select
70            ts,
71            dur,
72            value as idle_value
73          from experimental_counter_dur c
74          where track_id = ${this.config.idleTrackId};
75        `);
76      }
77
78      await this.query(`create virtual table ${this.tableName('freq_idle')}
79              using span_join(${this.tableName('freq')},
80                              ${this.tableName('idle')});`);
81
82      await this.query(`create virtual table ${this.tableName('span_activity')}
83              using span_join(${this.tableName('freq_idle')},
84                              ${this.tableName('window')});`);
85
86      // TODO(taylori): Move the idle value processing to the TP.
87      await this.query(`create view ${this.tableName('activity')}
88      as select
89        ts,
90        dur,
91        quantum_ts,
92        case idle_value
93          when 4294967295 then -1
94          else idle_value
95        end as idle,
96        freq_value as freq
97        from ${this.tableName('span_activity')};
98      `);
99
100      this.setup = true;
101    }
102
103    this.query(`update ${this.tableName('window')} set
104    window_start = ${startNs},
105    window_dur = ${Math.max(1, endNs - startNs)},
106    quantum = 0`);
107
108    const result = await this.engine.queryOneRow(`select count(*)
109      from ${this.tableName('activity')}`);
110    const isQuantized = result[0] > LIMIT;
111
112    // Cast as double to avoid problem where values are sometimes
113    // doubles, sometimes longs.
114    let query = `select ts, dur, cast(idle as DOUBLE), freq
115      from ${this.tableName('activity')} limit ${LIMIT}`;
116
117    if (isQuantized) {
118      // |resolution| is in s/px we want # ns for 10px window:
119      const bucketSizeNs = Math.round(resolution * 10 * 1e9);
120      const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
121      const windowDurNs = Math.max(1, endNs - windowStartNs);
122
123      this.query(`update ${this.tableName('window')} set
124      window_start = ${startNs},
125      window_dur = ${windowDurNs},
126      quantum = ${isQuantized ? bucketSizeNs : 0}`);
127
128      query = `select
129        min(ts) as ts,
130        sum(dur) as dur,
131        case
132          when min(idle) = -1 then cast(-1 as DOUBLE)
133          else cast(0 as DOUBLE)
134        end as idle,
135        sum(weighted_freq)/sum(dur) as freq_avg,
136        quantum_ts
137        from (
138          select
139          ts,
140          dur,
141          quantum_ts,
142          freq*dur as weighted_freq,
143          idle
144          from ${this.tableName('activity')})
145        group by quantum_ts limit ${LIMIT}`;
146    }
147
148    const freqResult = await this.query(query);
149
150    const numRows = +freqResult.numRecords;
151    const data: Data = {
152      start,
153      end,
154      resolution,
155      length: numRows,
156      maximumValue: this.maximumValue(),
157      isQuantized,
158      tsStarts: new Float64Array(numRows),
159      tsEnds: new Float64Array(numRows),
160      idles: new Int8Array(numRows),
161      freqKHz: new Uint32Array(numRows),
162    };
163
164    const cols = freqResult.columns;
165    for (let row = 0; row < numRows; row++) {
166      const startSec = fromNs(+cols[0].longValues![row]);
167      data.tsStarts[row] = startSec;
168      data.tsEnds[row] = startSec + fromNs(+cols[1].longValues![row]);
169      data.idles[row] = +cols[2].doubleValues![row];
170      data.freqKHz[row] = +cols[3].doubleValues![row];
171    }
172
173    return data;
174  }
175
176  private maximumValue() {
177    return Math.max(this.config.maximumValue || 0, this.maximumValueSeen);
178  }
179
180}
181
182
183trackControllerRegistry.register(CpuFreqTrackController);
184