perfetto-ui: Update CPU track to use span join
Change-Id: I8aec6ee181680ed1352de0ee3532880008ab4a8e
diff --git a/ui/src/tracks/cpu_slices/controller.ts b/ui/src/tracks/cpu_slices/controller.ts
index 17a1b0f..77fbdf0 100644
--- a/ui/src/tracks/cpu_slices/controller.ts
+++ b/ui/src/tracks/cpu_slices/controller.ts
@@ -23,49 +23,82 @@
class CpuSliceTrackController extends TrackController<Config, Data> {
static readonly kind = CPU_SLICE_TRACK_KIND;
private busy = false;
+ private setup = false;
- onBoundsChange(start: number, end: number, resolution: number) {
+ onBoundsChange(start: number, end: number, resolution: number): void {
+ this.update(start, end, resolution);
+ }
+
+ private async update(start: number, end: number, resolution: number):
+ Promise<void> {
// TODO: we should really call TraceProcessor.Interrupt() at this point.
if (this.busy) return;
- const LIMIT = 10000;
- const query = 'select ts,dur,utid from sched ' +
- `where cpu = ${this.config.cpu} ` +
- `and ts_lower_bound = ${Math.round(start * 1e9)} ` +
- `and ts <= ${Math.round(end * 1e9)} ` +
- `and dur >= ${Math.round(resolution * 1e9)} ` +
- `and utid != 0 ` +
- `order by ts ` +
- `limit ${LIMIT};`;
+
+ const startNs = Math.round(start * 1e9);
+ const endNs = Math.round(end * 1e9);
+ const resolutionNs = Math.round(resolution * 1e9);
this.busy = true;
- this.engine.query(query).then(rawResult => {
- this.busy = false;
- if (rawResult.error) {
- throw new Error(`Query error "${query}": ${rawResult.error}`);
- }
- const numRows = +rawResult.numRecords;
+ if (this.setup === false) {
+ await this.query(
+ `create virtual table window_${this.trackState.id} using window;`);
+ await this.query(`create virtual table span_${this.trackState.id}
+ using span(sched, window_${this.trackState.id}, cpu);`);
+ this.setup = true;
+ }
- const slices: Data = {
- start,
- end,
- resolution,
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
- utids: new Uint32Array(numRows),
- };
+ this.query(`update window_${this.trackState.id} set
+ window_start=${startNs},
+ window_dur=${endNs - startNs}
+ where rowid = 0;`);
- for (let row = 0; row < numRows; row++) {
- const cols = rawResult.columns;
- const startSec = fromNs(+cols[0].longValues![row]);
- slices.starts[row] = startSec;
- slices.ends[row] = startSec + fromNs(+cols[1].longValues![row]);
- slices.utids[row] = +cols[2].longValues![row];
- }
- if (numRows === LIMIT) {
- slices.end = slices.ends[slices.ends.length - 1];
- }
- this.publish(slices);
- });
+ const LIMIT = 10000;
+ const query = `select ts,dur,utid from span_${this.trackState.id}
+ where cpu = ${this.config.cpu}
+ and utid != 0
+ and dur >= ${resolutionNs}
+ limit ${LIMIT};`;
+ const rawResult = await this.query(query);
+
+ const numRows = +rawResult.numRecords;
+
+ const slices: Data = {
+ start,
+ end,
+ resolution,
+ starts: new Float64Array(numRows),
+ ends: new Float64Array(numRows),
+ utids: new Uint32Array(numRows),
+ };
+
+ for (let row = 0; row < numRows; row++) {
+ const cols = rawResult.columns;
+ const startSec = fromNs(+cols[0].longValues![row]);
+ slices.starts[row] = startSec;
+ slices.ends[row] = startSec + fromNs(+cols[1].longValues![row]);
+ slices.utids[row] = +cols[2].longValues![row];
+ }
+ if (numRows === LIMIT) {
+ slices.end = slices.ends[slices.ends.length - 1];
+ }
+ this.publish(slices);
+ this.busy = false;
+ }
+
+ private async query(query: string) {
+ const result = await this.engine.query(query);
+ if (result.error) {
+ throw new Error(`Query error "${query}": ${result.error}`);
+ }
+ return result;
+ }
+
+ onDestroy(): void {
+ if (this.setup) {
+ this.query(`drop table window_${this.trackState.id}`);
+ this.query(`drop table span_${this.trackState.id}`);
+ this.setup = false;
+ }
}
}