• 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 {assertFalse} from '../../base/logging';
16import {NUM, NUM_NULL, STR_NULL} from '../../common/query_result';
17import {translateState} from '../../common/thread_state';
18import {fromNs, toNs} from '../../common/time';
19import {
20  TrackController,
21  trackControllerRegistry
22} from '../../controller/track_controller';
23
24import {
25  Config,
26  Data,
27  THREAD_STATE_TRACK_KIND,
28} from './common';
29
30class ThreadStateTrackController extends TrackController<Config, Data> {
31  static readonly kind = THREAD_STATE_TRACK_KIND;
32
33  private maxDurNs = 0;
34
35  async onSetup() {
36    await this.query(`
37      create view ${this.tableName('thread_state')} as
38      select
39        id,
40        ts,
41        dur,
42        cpu,
43        state,
44        io_wait as ioWait
45      from thread_state
46      where utid = ${this.config.utid} and utid != 0
47    `);
48
49    const queryRes = await this.query(`
50      select ifnull(max(dur), 0) as maxDur
51      from ${this.tableName('thread_state')}
52    `);
53    this.maxDurNs = queryRes.firstRow({maxDur: NUM}).maxDur;
54  }
55
56  async onBoundsChange(start: number, end: number, resolution: number):
57      Promise<Data> {
58    const resolutionNs = toNs(resolution);
59    const startNs = toNs(start);
60    const endNs = toNs(end);
61
62    // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
63    // be an even number, so we can snap in the middle.
64    const bucketNs =
65        Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
66
67    const query = `
68      select
69        (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
70        ts,
71        state = 'S' as is_sleep,
72        max(dur) as dur,
73        ifnull(cast(cpu as integer), -1) as cpu,
74        state,
75        ioWait,
76        ifnull(id, -1) as id
77      from ${this.tableName('thread_state')}
78      where
79        ts >= ${startNs - this.maxDurNs} and
80        ts <= ${endNs}
81      group by tsq, is_sleep
82      order by tsq
83    `;
84
85    const queryRes = await this.query(query);
86    const numRows = queryRes.numRows();
87
88    const data: Data = {
89      start,
90      end,
91      resolution,
92      length: numRows,
93      ids: new Float64Array(numRows),
94      starts: new Float64Array(numRows),
95      ends: new Float64Array(numRows),
96      strings: [],
97      state: new Uint16Array(numRows),
98      cpu: new Int8Array(numRows),
99    };
100
101    const stringIndexes = new Map<
102        {shortState: string | undefined; ioWait: boolean | undefined},
103        number>();
104    function internState(
105        shortState: string|undefined, ioWait: boolean|undefined) {
106      let idx = stringIndexes.get({shortState, ioWait});
107      if (idx !== undefined) return idx;
108      idx = data.strings.length;
109      data.strings.push(translateState(shortState, ioWait));
110      stringIndexes.set({shortState, ioWait}, idx);
111      return idx;
112    }
113    const it = queryRes.iter({
114      'tsq': NUM,
115      'ts': NUM,
116      'dur': NUM,
117      'cpu': NUM,
118      'state': STR_NULL,
119      'ioWait': NUM_NULL,
120      'id': NUM,
121    });
122    for (let row = 0; it.valid(); it.next(), row++) {
123      const startNsQ = it.tsq;
124      const startNs = it.ts;
125      const durNs = it.dur;
126      const endNs = startNs + durNs;
127
128      let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
129      endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
130
131      const cpu = it.cpu;
132      const state = it.state || undefined;
133      const ioWait = it.ioWait === null ? undefined : !!it.ioWait;
134      const id = it.id;
135
136      // We should never have the end timestamp being the same as the bucket
137      // start.
138      assertFalse(startNsQ === endNsQ);
139
140      data.starts[row] = fromNs(startNsQ);
141      data.ends[row] = fromNs(endNsQ);
142      data.state[row] = internState(state, ioWait);
143      data.ids[row] = id;
144      data.cpu[row] = cpu;
145    }
146    return data;
147  }
148
149  async onDestroy() {
150    await this.query(`drop view if exists ${this.tableName('thread_state')}`);
151  }
152}
153
154trackControllerRegistry.register(ThreadStateTrackController);
155