UI: move tracks to queryV2
Use the new streaming query operator for all
tracks. Some controllers still need update.
Change-Id: Iec3dcdf4d9432636aec0a9a941fee986587172c7
Bug: 159142289
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index f56c8aa..6d77b0f 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -24,7 +24,7 @@
RawQueryArgs,
RawQueryResult
} from './protos';
-import {iter, NUM_NULL, slowlyCountRows, STR} from './query_iterator';
+import {NUM, NUM_NULL, slowlyCountRows, STR} from './query_iterator';
import {
createQueryResult,
QueryResult,
@@ -365,10 +365,13 @@
// TODO(hjd): When streaming must invalidate this somehow.
async getCpus(): Promise<number[]> {
if (!this._cpus) {
- const result =
- await this.query('select distinct(cpu) from sched order by cpu;');
- if (slowlyCountRows(result) === 0) return [];
- this._cpus = result.columns[0].longValues!.map(n => +n);
+ const cpus = [];
+ const queryRes = await this.queryV2(
+ 'select distinct(cpu) as cpu from sched order by cpu;');
+ for (const it = queryRes.iter({cpu: NUM}); it.valid(); it.next()) {
+ cpus.push(it.cpu);
+ }
+ this._cpus = cpus;
}
return this._cpus;
}
@@ -388,8 +391,8 @@
// TODO: This should live in code that's more specific to chrome, instead of
// in engine.
async getNumberOfProcesses(): Promise<number> {
- const result = await this.query('select count(*) from process;');
- return +result.columns[0].longValues![0];
+ const result = await this.queryV2('select count(*) as cnt from process;');
+ return result.firstRow({cnt: NUM}).cnt;
}
async getTraceTimeBounds(): Promise<TimeSpan> {
@@ -399,15 +402,15 @@
}
async getTracingMetadataTimeBounds(): Promise<TimeSpan> {
- const query = await this.query(`select name, int_value from metadata
+ const queryRes = await this.queryV2(`select name, int_value from metadata
where name = 'tracing_started_ns' or name = 'tracing_disabled_ns'
or name = 'all_data_source_started_ns'`);
let startBound = -Infinity;
let endBound = Infinity;
- const it = iter({'name': STR, 'int_value': NUM_NULL}, query);
+ const it = queryRes.iter({'name': STR, 'int_value': NUM_NULL});
for (; it.valid(); it.next()) {
- const columnName = it.row.name;
- const timestamp = it.row.int_value;
+ const columnName = it.name;
+ const timestamp = it.int_value;
if (timestamp === null) continue;
if (columnName === 'tracing_disabled_ns') {
endBound = Math.min(endBound, timestamp / 1e9);
diff --git a/ui/src/common/query_result.ts b/ui/src/common/query_result.ts
index 20c9424..82afc75 100644
--- a/ui/src/common/query_result.ts
+++ b/ui/src/common/query_result.ts
@@ -100,6 +100,11 @@
// iter<T extends Row>(spec: T): RowIterator<T>;
iter<T extends Row>(spec: T): RowIterator<T>;
+ // Like iter() for queries that expect only one row. It embeds the valid()
+ // check (i.e. throws if no rows are available) and returns directly the
+ // first result.
+ firstRow<T extends Row>(spec: T): T;
+
// If != undefined the query errored out and error() contains the message.
error(): string|undefined;
@@ -179,6 +184,12 @@
return impl as {} as RowIterator<T>;
}
+ firstRow<T extends Row>(spec: T): T {
+ const impl = new RowIteratorImplWithRowData(spec, this);
+ assertTrue(impl.valid());
+ return impl as {} as RowIterator<T>as T;
+ }
+
// Can be called only once.
waitAllRows(): Promise<QueryResult> {
assertTrue(this.allRowsPromise === undefined);
@@ -510,6 +521,7 @@
if (nextBatchIdx >= this.resultObj.batches.length) {
return false;
}
+
this.columnNames = this.resultObj.columnNames;
this.numColumns = this.columnNames.length;
@@ -582,7 +594,8 @@
// This is the object ultimately returned to the client when calling
// QueryResult.iter(...).
// The only reason why this is disjoint from RowIteratorImpl is to avoid
-// polluting the class members with the state variables required by
+// naming collisions between the members variables required by RowIteratorImpl
+// and the column names returned by the iterator.
class RowIteratorImplWithRowData implements RowIteratorBase {
private _impl: RowIteratorImpl;
@@ -613,6 +626,9 @@
iter<T extends Row>(spec: T) {
return this.impl.iter(spec);
}
+ firstRow<T extends Row>(spec: T) {
+ return this.impl.firstRow(spec);
+ }
waitAllRows() {
return this.impl.waitAllRows();
}
diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts
index 06f2d7a..8f71701 100644
--- a/ui/src/controller/search_controller.ts
+++ b/ui/src/controller/search_controller.ts
@@ -14,7 +14,7 @@
import {TRACE_MARGIN_TIME_S} from '../common/constants';
import {Engine} from '../common/engine';
-import {slowlyCountRows} from '../common/query_iterator';
+import {NUM, STR} from '../common/query_iterator';
import {CurrentSearchResults, SearchSummary} from '../common/search_data';
import {TimeSpan} from '../common/time';
@@ -59,11 +59,11 @@
}
private async setup() {
- await this.query(`create virtual table search_summary_window
+ await this.queryV2(`create virtual table search_summary_window
using window;`);
- await this.query(`create virtual table search_summary_sched_span using
+ await this.queryV2(`create virtual table search_summary_sched_span using
span_join(sched PARTITIONED cpu, search_summary_window);`);
- await this.query(`create virtual table search_summary_slice_span using
+ await this.queryV2(`create virtual table search_summary_slice_span using
span_join(slice PARTITIONED track_id, search_summary_window);`);
}
@@ -151,22 +151,25 @@
startNs = Math.floor(startNs / quantumNs) * quantumNs;
- await this.query(`update search_summary_window set
+ await this.queryV2(`update search_summary_window set
window_start=${startNs},
window_dur=${endNs - startNs},
quantum=${quantumNs}
where rowid = 0;`);
- const rawUtidResult = await this.query(`select utid from thread join process
+ const utidRes = await this.queryV2(`select utid from thread join process
using(upid) where thread.name like ${searchLiteral}
or process.name like ${searchLiteral}`);
- const utids = [...rawUtidResult.columns[0].longValues!];
+ const utids = [];
+ for (const it = utidRes.iter({utid: NUM}); it.valid(); it.next()) {
+ utids.push(it.utid);
+ }
const cpus = await this.engine.getCpus();
const maxCpu = Math.max(...cpus, -1);
- const rawResult = await this.query(`
+ const res = await this.queryV2(`
select
(quantum_ts * ${quantumNs} + ${startNs})/1e9 as tsStart,
((quantum_ts+1) * ${quantumNs} + ${startNs})/1e9 as tsEnd,
@@ -185,18 +188,18 @@
group by quantum_ts
order by quantum_ts;`);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = res.numRows();
const summary = {
tsStarts: new Float64Array(numRows),
tsEnds: new Float64Array(numRows),
count: new Uint8Array(numRows)
};
- const columns = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- summary.tsStarts[row] = +columns[0].doubleValues![row];
- summary.tsEnds[row] = +columns[1].doubleValues![row];
- summary.count[row] = +columns[2].longValues![row];
+ const it = res.iter({tsStart: NUM, tsEnd: NUM, count: NUM});
+ for (let row = 0; it.valid(); it.next(), ++row) {
+ summary.tsStarts[row] = it.tsStart;
+ summary.tsEnds[row] = it.tsEnd;
+ summary.count[row] = it.count;
}
return summary;
}
@@ -226,80 +229,78 @@
}
}
- const rawUtidResult = await this.query(`select utid from thread join process
+ const utidRes = await this.queryV2(`select utid from thread join process
using(upid) where
thread.name like ${searchLiteral} or
process.name like ${searchLiteral}`);
- const utids = [...rawUtidResult.columns[0].longValues!];
+ const utids = [];
+ for (const it = utidRes.iter({utid: NUM}); it.valid(); it.next()) {
+ utids.push(it.utid);
+ }
- const rawResult = await this.query(`
+ const queryRes = await this.queryV2(`
select
- id as slice_id,
+ id as sliceId,
ts,
'cpu' as source,
- cpu as source_id,
+ cpu as sourceId,
utid
from sched where utid in (${utids.join(',')})
union
select
- slice_id,
+ slice_id as sliceId,
ts,
'track' as source,
- track_id as source_id,
+ track_id as sourceId,
0 as utid
from slice
where slice.name like ${searchLiteral}
union
select
- slice_id,
+ slice_id as sliceId,
ts,
'track' as source,
- track_id as source_id,
+ track_id as sourceId,
0 as utid
from slice
join args using(arg_set_id)
where string_value like ${searchLiteral}
order by ts`);
- const numRows = slowlyCountRows(rawResult);
-
const searchResults: CurrentSearchResults = {
sliceIds: [],
tsStarts: [],
utids: [],
trackIds: [],
sources: [],
- totalResults: +numRows,
+ totalResults: queryRes.numRows(),
};
- const columns = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- const source = columns[2].stringValues![row];
- const sourceId = +columns[3].longValues![row];
+ const spec = {sliceId: NUM, ts: NUM, source: STR, sourceId: NUM, utid: NUM};
+ for (const it = queryRes.iter(spec); it.valid(); it.next()) {
let trackId = undefined;
- if (source === 'cpu') {
- trackId = cpuToTrackId.get(sourceId);
- } else if (source === 'track') {
- trackId = engineTrackIdToTrackId.get(sourceId);
+ if (it.source === 'cpu') {
+ trackId = cpuToTrackId.get(it.sourceId);
+ } else if (it.source === 'track') {
+ trackId = engineTrackIdToTrackId.get(it.sourceId);
}
+ // The .get() calls above could return undefined, this isn't just an else.
if (trackId === undefined) {
searchResults.totalResults--;
continue;
}
-
searchResults.trackIds.push(trackId);
- searchResults.sources.push(source);
- searchResults.sliceIds.push(+columns[0].longValues![row]);
- searchResults.tsStarts.push(+columns[1].longValues![row]);
- searchResults.utids.push(+columns[4].longValues![row]);
+ searchResults.sources.push(it.source);
+ searchResults.sliceIds.push(it.sliceId);
+ searchResults.tsStarts.push(it.ts);
+ searchResults.utids.push(it.utid);
}
return searchResults;
}
-
- private async query(query: string) {
- const result = await this.engine.query(query);
+ private async queryV2(query: string) {
+ const result = await this.engine.queryV2(query);
return result;
}
}
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index b3742b3..b2a82fb 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -118,6 +118,11 @@
return result;
}
+ protected async queryV2(query: string) {
+ const result = await this.engine.queryV2(query);
+ return result;
+ }
+
private shouldReload(): boolean {
const {lastTrackReloadRequest} = globals.state;
return !!lastTrackReloadRequest &&
diff --git a/ui/src/tracks/actual_frames/controller.ts b/ui/src/tracks/actual_frames/controller.ts
index 58140f7..95c9201 100644
--- a/ui/src/tracks/actual_frames/controller.ts
+++ b/ui/src/tracks/actual_frames/controller.ts
@@ -12,14 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists, assertTrue} from '../../base/logging';
-import {
- iter,
- NUM,
- singleRow,
- slowlyCountRows,
- STR
-} from '../../common/query_iterator';
+import {assertTrue} from '../../base/logging';
+import {NUM, NUM_NULL, STR} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -51,18 +45,17 @@
const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
if (this.maxDurNs === 0) {
- const maxDurResult = await this.query(`
+ const maxDurResult = await this.queryV2(`
select
max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
as maxDur
from experimental_slice_layout
where filter_track_ids = '${this.config.trackIds.join(',')}'
`);
- const row = singleRow({maxDur: NUM}, maxDurResult);
- this.maxDurNs = assertExists(row).maxDur;
+ this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
}
- const rawResult = await this.query(`
+ const rawResult = await this.queryV2(`
SELECT
(s.ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
s.ts as ts,
@@ -92,7 +85,7 @@
order by tsq, s.layout_depth
`);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = rawResult.numRows();
const slices: Data = {
start,
end,
@@ -119,23 +112,21 @@
return idx;
}
- const it = iter(
- {
- 'tsq': NUM,
- 'ts': NUM,
- 'dur': NUM,
- 'layoutDepth': NUM,
- 'id': NUM,
- 'name': STR,
- 'isInstant': NUM,
- 'isIncomplete': NUM,
- 'color': STR,
- },
- rawResult);
+ const it = rawResult.iter({
+ 'tsq': NUM,
+ 'ts': NUM,
+ 'dur': NUM,
+ 'layoutDepth': NUM,
+ 'id': NUM,
+ 'name': STR,
+ 'isInstant': NUM,
+ 'isIncomplete': NUM,
+ 'color': STR,
+ });
for (let i = 0; it.valid(); i++, it.next()) {
- const startNsQ = it.row.tsq;
- const startNs = it.row.ts;
- const durNs = it.row.dur;
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
@@ -145,12 +136,12 @@
slices.starts[i] = fromNs(startNsQ);
slices.ends[i] = fromNs(endNsQ);
- slices.depths[i] = it.row.layoutDepth;
- slices.titles[i] = internString(it.row.name);
- slices.colors![i] = internString(it.row.color);
- slices.sliceIds[i] = it.row.id;
- slices.isInstant[i] = it.row.isInstant;
- slices.isIncomplete[i] = it.row.isIncomplete;
+ slices.depths[i] = it.layoutDepth;
+ slices.titles[i] = internString(it.name);
+ slices.colors![i] = internString(it.color);
+ slices.sliceIds[i] = it.id;
+ slices.isInstant[i] = it.isInstant;
+ slices.isIncomplete[i] = it.isIncomplete;
}
return slices;
}
diff --git a/ui/src/tracks/android_log/controller.ts b/ui/src/tracks/android_log/controller.ts
index afd235c..3f1401a 100644
--- a/ui/src/tracks/android_log/controller.ts
+++ b/ui/src/tracks/android_log/controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM} from '../../common/query_iterator';
import {fromNs, toNsCeil, toNsFloor} from '../../common/time';
import {LIMIT} from '../../common/track_data';
import {
@@ -33,17 +33,17 @@
// |resolution| is in s/px the frontend wants.
const quantNs = toNsCeil(resolution);
- const rawResult = await this.query(`
+ const queryRes = await this.queryV2(`
select
- cast(ts / ${quantNs} as integer) * ${quantNs} as ts_quant,
+ cast(ts / ${quantNs} as integer) * ${quantNs} as tsQuant,
prio,
- count(prio)
+ count(prio) as numEvents
from android_logs
where ts >= ${startNs} and ts <= ${endNs}
- group by ts_quant, prio
- order by ts_quant, prio limit ${LIMIT};`);
+ group by tsQuant, prio
+ order by tsQuant, prio limit ${LIMIT};`);
- const rowCount = slowlyCountRows(rawResult);
+ const rowCount = queryRes.numRows();
const result = {
start,
end,
@@ -53,12 +53,14 @@
timestamps: new Float64Array(rowCount),
priorities: new Uint8Array(rowCount),
};
- const cols = rawResult.columns;
- for (let i = 0; i < rowCount; i++) {
- result.timestamps[i] = fromNs(+cols[0].longValues![i]);
- const prio = Math.min(+cols[1].longValues![i], 7);
- result.priorities[i] |= (1 << prio);
- result.numEvents += +cols[2].longValues![i];
+
+
+ const it = queryRes.iter({tsQuant: NUM, prio: NUM, numEvents: NUM});
+ for (let row = 0; it.valid(); it.next(), row++) {
+ result.timestamps[row] = fromNs(it.tsQuant);
+ const prio = Math.min(it.prio, 7);
+ result.priorities[row] |= (1 << prio);
+ result.numEvents += it.numEvents;
}
return result;
}
diff --git a/ui/src/tracks/async_slices/controller.ts b/ui/src/tracks/async_slices/controller.ts
index 50a074a..0cfc609 100644
--- a/ui/src/tracks/async_slices/controller.ts
+++ b/ui/src/tracks/async_slices/controller.ts
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {slowlyCountRows} from '../../common/query_iterator';
+import {assertTrue} from '../../base/logging';
+import {NUM, NUM_NULL, STR} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -37,36 +38,34 @@
const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
if (this.maxDurNs === 0) {
- const maxDurResult = await this.query(`
+ const maxDurResult = await this.queryV2(`
select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
- from experimental_slice_layout
+ as maxDur from experimental_slice_layout
where filter_track_ids = '${this.config.trackIds.join(',')}'
`);
- if (slowlyCountRows(maxDurResult) === 1) {
- this.maxDurNs = maxDurResult.columns[0].longValues![0];
- }
+ this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
}
- const rawResult = await this.query(`
+ const queryRes = await this.queryV2(`
SELECT
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
ts,
max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as dur,
- layout_depth,
+ layout_depth as depth,
name,
id,
- dur = 0 as is_instant,
- dur = -1 as is_incomplete
+ dur = 0 as isInstant,
+ dur = -1 as isIncomplete
from experimental_slice_layout
where
filter_track_ids = '${this.config.trackIds.join(',')}' and
ts >= ${startNs - this.maxDurNs} and
ts <= ${endNs}
- group by tsq, layout_depth
- order by tsq, layout_depth
+ group by tsq, depth
+ order by tsq, depth
`);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = queryRes.numRows();
const slices: Data = {
start,
end,
@@ -92,31 +91,37 @@
return idx;
}
- const cols = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- const startNsQ = +cols[0].longValues![row];
- const startNs = +cols[1].longValues![row];
- const durNs = +cols[2].longValues![row];
+ const it = queryRes.iter({
+ tsq: NUM,
+ ts: NUM,
+ dur: NUM,
+ depth: NUM,
+ name: STR,
+ id: NUM,
+ isInstant: NUM,
+ isIncomplete: NUM
+ });
+ for (let row = 0; it.valid(); it.next(), row++) {
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- if (startNsQ === endNsQ) {
- throw new Error('Should never happen');
- }
+ assertTrue(startNsQ !== endNsQ);
slices.starts[row] = fromNs(startNsQ);
slices.ends[row] = fromNs(endNsQ);
- slices.depths[row] = +cols[3].longValues![row];
- slices.titles[row] = internString(cols[4].stringValues![row]);
- slices.sliceIds[row] = +cols[5].longValues![row];
- slices.isInstant[row] = +cols[6].longValues![row];
- slices.isIncomplete[row] = +cols[7].longValues![row];
+ slices.depths[row] = it.depth;
+ slices.titles[row] = internString(it.name);
+ slices.sliceIds[row] = it.id;
+ slices.isInstant[row] = it.isInstant;
+ slices.isIncomplete[row] = it.isIncomplete;
}
return slices;
}
}
-
trackControllerRegistry.register(AsyncSliceTrackController);
diff --git a/ui/src/tracks/chrome_slices/controller.ts b/ui/src/tracks/chrome_slices/controller.ts
index 2f29388..e702fd6 100644
--- a/ui/src/tracks/chrome_slices/controller.ts
+++ b/ui/src/tracks/chrome_slices/controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM, NUM_NULL, STR} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -41,31 +41,29 @@
if (this.maxDurNs === 0) {
const query = `
SELECT max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
- FROM ${tableName} WHERE track_id = ${this.config.trackId}`;
- const rawResult = await this.query(query);
- if (slowlyCountRows(rawResult) === 1) {
- this.maxDurNs = rawResult.columns[0].longValues![0];
- }
+ AS maxDur FROM ${tableName} WHERE track_id = ${this.config.trackId}`;
+ const queryRes = await this.queryV2(query);
+ this.maxDurNs = queryRes.firstRow({maxDur: NUM_NULL}).maxDur || 0;
}
const query = `
SELECT
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
ts,
- max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)),
+ max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as dur,
depth,
- id as slice_id,
+ id as sliceId,
name,
- dur = 0 as is_instant,
- dur = -1 as is_incomplete
+ dur = 0 as isInstant,
+ dur = -1 as isIncomplete
FROM ${tableName}
WHERE track_id = ${this.config.trackId} AND
ts >= (${startNs - this.maxDurNs}) AND
ts <= ${endNs}
GROUP BY depth, tsq`;
- const rawResult = await this.query(query);
+ const queryRes = await this.queryV2(query);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = queryRes.numRows();
const slices: Data = {
start,
end,
@@ -91,19 +89,26 @@
return idx;
}
- const cols = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- const startNsQ = +cols[0].longValues![row];
- const startNs = +cols[1].longValues![row];
- const durNs = +cols[2].longValues![row];
+ const it = queryRes.iter({
+ tsq: NUM,
+ ts: NUM,
+ dur: NUM,
+ depth: NUM,
+ sliceId: NUM,
+ name: STR,
+ isInstant: NUM,
+ isIncomplete: NUM
+ });
+ for (let row = 0; it.valid(); it.next(), row++) {
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
- const isInstant = +cols[6].longValues![row];
- const isIncomplete = +cols[7].longValues![row];
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- if (!isInstant && startNsQ === endNsQ) {
+ if (!it.isInstant && startNsQ === endNsQ) {
throw new Error(
'Expected startNsQ and endNsQ to differ (' +
`startNsQ: ${startNsQ}, startNs: ${startNs},` +
@@ -113,11 +118,11 @@
slices.starts[row] = fromNs(startNsQ);
slices.ends[row] = fromNs(endNsQ);
- slices.depths[row] = +cols[3].longValues![row];
- slices.sliceIds[row] = +cols[4].longValues![row];
- slices.titles[row] = internString(cols[5].stringValues![row]);
- slices.isInstant[row] = isInstant;
- slices.isIncomplete[row] = isIncomplete;
+ slices.depths[row] = it.depth;
+ slices.sliceIds[row] = it.sliceId;
+ slices.titles[row] = internString(it.name);
+ slices.isInstant[row] = it.isInstant;
+ slices.isIncomplete[row] = it.isIncomplete;
}
return slices;
}
diff --git a/ui/src/tracks/counter/controller.ts b/ui/src/tracks/counter/controller.ts
index 22dffe2..5b230f3 100644
--- a/ui/src/tracks/counter/controller.ts
+++ b/ui/src/tracks/counter/controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {iter, NUM, slowlyCountRows} from '../../common/query_iterator';
+import {NUM, NUM_NULL} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -47,7 +47,7 @@
if (!this.setup) {
if (this.config.namespace === undefined) {
- await this.query(`
+ await this.queryV2(`
create view ${this.tableName('counter_view')} as
select
id,
@@ -59,7 +59,7 @@
where track_id = ${this.config.trackId};
`);
} else {
- await this.query(`
+ await this.queryV2(`
create view ${this.tableName('counter_view')} as
select
id,
@@ -72,33 +72,33 @@
`);
}
- const maxDurResult = await this.query(`
+ const maxDurResult = await this.queryV2(`
select
max(
iif(dur != -1, dur, (select end_ts from trace_bounds) - ts)
- )
+ ) as maxDur
from ${this.tableName('counter_view')}
`);
- if (slowlyCountRows(maxDurResult) === 1) {
- this.maxDurNs = maxDurResult.columns[0].longValues![0];
- }
+ this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
- const result = await this.query(`
+ const queryRes = await this.queryV2(`
select
- max(value) as maxValue,
- min(value) as minValue,
- max(delta) as maxDelta,
- min(delta) as minDelta
+ ifnull(max(value), 0) as maxValue,
+ ifnull(min(value), 0) as minValue,
+ ifnull(max(delta), 0) as maxDelta,
+ ifnull(min(delta), 0) as minDelta
from ${this.tableName('counter_view')}`);
- this.maximumValueSeen = +result.columns[0].doubleValues![0];
- this.minimumValueSeen = +result.columns[1].doubleValues![0];
- this.maximumDeltaSeen = +result.columns[2].doubleValues![0];
- this.minimumDeltaSeen = +result.columns[3].doubleValues![0];
+ const row = queryRes.firstRow(
+ {maxValue: NUM, minValue: NUM, maxDelta: NUM, minDelta: NUM});
+ this.maximumValueSeen = row.maxValue;
+ this.minimumValueSeen = row.minValue;
+ this.maximumDeltaSeen = row.maxDelta;
+ this.minimumDeltaSeen = row.minDelta;
this.setup = true;
}
- const rawResult = await this.query(`
+ const queryRes = await this.queryV2(`
select
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
min(value) as minValue,
@@ -112,7 +112,7 @@
order by tsq
`);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = queryRes.numRows();
const data: Data = {
start,
@@ -131,25 +131,22 @@
totalDeltas: new Float64Array(numRows),
};
- const it = iter(
- {
- 'tsq': NUM,
- 'lastId': NUM,
- 'minValue': NUM,
- 'maxValue': NUM,
- 'lastValue': NUM,
- 'totalDelta': NUM,
- },
- rawResult);
- for (let i = 0; it.valid(); ++i, it.next()) {
- data.timestamps[i] = fromNs(it.row.tsq);
- data.lastIds[i] = it.row.lastId;
- data.minValues[i] = it.row.minValue;
- data.maxValues[i] = it.row.maxValue;
- data.lastValues[i] = it.row.lastValue;
- data.totalDeltas[i] = it.row.totalDelta;
+ const it = queryRes.iter({
+ 'tsq': NUM,
+ 'lastId': NUM,
+ 'minValue': NUM,
+ 'maxValue': NUM,
+ 'lastValue': NUM,
+ 'totalDelta': NUM,
+ });
+ for (let row = 0; it.valid(); it.next(), row++) {
+ data.timestamps[row] = fromNs(it.tsq);
+ data.lastIds[row] = it.lastId;
+ data.minValues[row] = it.minValue;
+ data.maxValues[row] = it.maxValue;
+ data.lastValues[row] = it.lastValue;
+ data.totalDeltas[row] = it.totalDelta;
}
-
return data;
}
diff --git a/ui/src/tracks/cpu_freq/controller.ts b/ui/src/tracks/cpu_freq/controller.ts
index 08ca0b5..c457da7 100644
--- a/ui/src/tracks/cpu_freq/controller.ts
+++ b/ui/src/tracks/cpu_freq/controller.ts
@@ -13,8 +13,8 @@
// limitations under the License.
import {assertTrue} from '../../base/logging';
-import {RawQueryResult} from '../../common/protos';
-import {iter, NUM, slowlyCountRows} from '../../common/query_iterator';
+import {NUM, NUM_NULL} from '../../common/query_iterator';
+import {QueryResult} from '../../common/query_result';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -41,30 +41,37 @@
this.maximumValueSeen = await this.queryMaxFrequency();
this.maxDurNs = await this.queryMaxSourceDur();
- const result = await this.query(`
- select max(ts), dur, count(1)
+ const iter = (await this.queryV2(`
+ select max(ts) as maxTs, dur, count(1) as rowCount
from ${this.tableName('freq_idle')}
- `);
- this.maxTsEndNs =
- result.columns[0].longValues![0] + result.columns[1].longValues![0];
+ `)).firstRow({maxTs: NUM_NULL, dur: NUM_NULL, rowCount: NUM});
+ if (iter.maxTs === null || iter.dur === null) {
+ // We shoulnd't really hit this because trackDecider shouldn't create
+ // the track in the first place if there are no entries. But could happen
+ // if only one cpu has no cpufreq data.
+ return;
+ }
+ this.maxTsEndNs = iter.maxTs + iter.dur;
- const rowCount = result.columns[2].longValues![0];
+ const rowCount = iter.rowCount;
const bucketNs = this.cachedBucketSizeNs(rowCount);
if (bucketNs === undefined) {
return;
}
- await this.query(`
+
+ await this.queryV2(`
create table ${this.tableName('freq_idle_cached')} as
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cached_tsq,
- min(freq_value) as min_freq,
- max(freq_value) as max_freq,
- value_at_max_ts(ts, freq_value) as last_freq,
- value_at_max_ts(ts, idle_value) as last_idle_value
+ (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cachedTsq,
+ min(freqValue) as minFreq,
+ max(freqValue) as maxFreq,
+ value_at_max_ts(ts, freqValue) as lastFreq,
+ value_at_max_ts(ts, idleValue) as lastIdleValue
from ${this.tableName('freq_idle')}
- group by cached_tsq
- order by cached_tsq
+ group by cachedTsq
+ order by cachedTsq
`);
+
this.cachedBucketNs = bucketNs;
}
@@ -82,10 +89,10 @@
// be an even number, so we can snap in the middle.
const bucketNs =
Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
-
const freqResult = await this.queryData(startNs, endNs, bucketNs);
+ assertTrue(freqResult.isComplete());
- const numRows = slowlyCountRows(freqResult);
+ const numRows = freqResult.numRows();
const data: Data = {
start,
end,
@@ -100,68 +107,68 @@
lastIdleValues: new Int8Array(numRows),
};
- const it = iter(
- {
- 'tsq': NUM,
- 'minFreq': NUM,
- 'maxFreq': NUM,
- 'lastFreq': NUM,
- 'lastIdleValue': NUM,
- },
- freqResult);
+ const it = freqResult.iter({
+ 'tsq': NUM,
+ 'minFreq': NUM,
+ 'maxFreq': NUM,
+ 'lastFreq': NUM,
+ 'lastIdleValue': NUM,
+ });
for (let i = 0; it.valid(); ++i, it.next()) {
- data.timestamps[i] = fromNs(it.row.tsq);
- data.minFreqKHz[i] = it.row.minFreq;
- data.maxFreqKHz[i] = it.row.maxFreq;
- data.lastFreqKHz[i] = it.row.lastFreq;
- data.lastIdleValues[i] = it.row.lastIdleValue;
+ data.timestamps[i] = fromNs(it.tsq);
+ data.minFreqKHz[i] = it.minFreq;
+ data.maxFreqKHz[i] = it.maxFreq;
+ data.lastFreqKHz[i] = it.lastFreq;
+ data.lastIdleValues[i] = it.lastIdleValue;
}
+
return data;
}
private async queryData(startNs: number, endNs: number, bucketNs: number):
- Promise<RawQueryResult> {
+ Promise<QueryResult> {
const isCached = this.cachedBucketNs <= bucketNs;
if (isCached) {
- return this.query(`
+ return this.queryV2(`
select
- cached_tsq / ${bucketNs} * ${bucketNs} as tsq,
- min(min_freq) as minFreq,
- max(max_freq) as maxFreq,
- value_at_max_ts(cached_tsq, last_freq) as lastFreq,
- value_at_max_ts(cached_tsq, last_idle_value) as lastIdleValue
+ cachedTsq / ${bucketNs} * ${bucketNs} as tsq,
+ min(minFreq) as minFreq,
+ max(maxFreq) as maxFreq,
+ value_at_max_ts(cachedTsq, lastFreq) as lastFreq,
+ value_at_max_ts(cachedTsq, lastIdleValue) as lastIdleValue
from ${this.tableName('freq_idle_cached')}
where
- cached_tsq >= ${startNs - this.maxDurNs} and
- cached_tsq <= ${endNs}
+ cachedTsq >= ${startNs - this.maxDurNs} and
+ cachedTsq <= ${endNs}
group by tsq
order by tsq
`);
}
-
- const minTsFreq = await this.query(`
- select ifnull(max(ts), 0) from ${this.tableName('freq')}
+ const minTsFreq = await this.queryV2(`
+ select ifnull(max(ts), 0) as minTs from ${this.tableName('freq')}
where ts < ${startNs}
`);
- let minTs = minTsFreq.columns[0].longValues![0];
+
+ let minTs = minTsFreq.iter({minTs: NUM}).minTs;
if (this.config.idleTrackId !== undefined) {
- const minTsIdle = await this.query(`
- select ifnull(max(ts), 0) from ${this.tableName('idle')}
+ const minTsIdle = await this.queryV2(`
+ select ifnull(max(ts), 0) as minTs from ${this.tableName('idle')}
where ts < ${startNs}
`);
- minTs = Math.min(minTsIdle.columns[0].longValues![0], minTs);
+ minTs = Math.min(minTsIdle.iter({minTs: NUM}).minTs, minTs);
}
+
const geqConstraint = this.config.idleTrackId === undefined ?
`ts >= ${minTs}` :
`source_geq(ts, ${minTs})`;
- return this.query(`
+ return this.queryV2(`
select
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
- min(freq_value) as minFreq,
- max(freq_value) as maxFreq,
- value_at_max_ts(ts, freq_value) as lastFreq,
- value_at_max_ts(ts, idle_value) as lastIdleValue
+ min(freqValue) as minFreq,
+ max(freqValue) as maxFreq,
+ value_at_max_ts(ts, freqValue) as lastFreq,
+ value_at_max_ts(ts, idleValue) as lastIdleValue
from ${this.tableName('freq_idle')}
where
${geqConstraint} and
@@ -172,59 +179,59 @@
}
private async queryMaxFrequency(): Promise<number> {
- const result = await this.query(`
- select max(freq_value)
+ const result = await this.queryV2(`
+ select max(freqValue) as maxFreq
from ${this.tableName('freq')}
`);
- return result.columns[0].doubleValues![0];
+ return result.firstRow({'maxFreq': NUM_NULL}).maxFreq || 0;
}
private async queryMaxSourceDur(): Promise<number> {
- const maxDurFreqResult =
- await this.query(`select max(dur) from ${this.tableName('freq')}`);
- const maxFreqDurNs = maxDurFreqResult.columns[0].longValues![0];
+ const maxDurFreqResult = await this.queryV2(
+ `select ifnull(max(dur), 0) as maxDur from ${this.tableName('freq')}`);
+ const maxDurNs = maxDurFreqResult.firstRow({'maxDur': NUM}).maxDur;
if (this.config.idleTrackId === undefined) {
- return maxFreqDurNs;
+ return maxDurNs;
}
- const maxDurIdleResult =
- await this.query(`select max(dur) from ${this.tableName('idle')}`);
- return Math.max(maxFreqDurNs, maxDurIdleResult.columns[0].longValues![0]);
+ const maxDurIdleResult = await this.queryV2(
+ `select ifnull(max(dur), 0) as maxDur from ${this.tableName('idle')}`);
+ return Math.max(maxDurNs, maxDurIdleResult.firstRow({maxDur: NUM}).maxDur);
}
private async createFreqIdleViews() {
- await this.query(`create view ${this.tableName('freq')} as
+ await this.queryV2(`create view ${this.tableName('freq')} as
select
ts,
dur,
- value as freq_value
+ value as freqValue
from experimental_counter_dur c
where track_id = ${this.config.freqTrackId};
`);
if (this.config.idleTrackId === undefined) {
- await this.query(`create view ${this.tableName('freq_idle')} as
+ await this.queryV2(`create view ${this.tableName('freq_idle')} as
select
ts,
dur,
- -1 as idle_value,
- freq_value
+ -1 as idleValue,
+ freqValue
from ${this.tableName('freq')};
`);
return;
}
- await this.query(`
+ await this.queryV2(`
create view ${this.tableName('idle')} as
select
ts,
dur,
- iif(value = 4294967295, -1, cast(value as int)) as idle_value
+ iif(value = 4294967295, -1, cast(value as int)) as idleValue
from experimental_counter_dur c
where track_id = ${this.config.idleTrackId};
`);
- await this.query(`
+ await this.queryV2(`
create virtual table ${this.tableName('freq_idle')}
using span_join(${this.tableName('freq')}, ${this.tableName('idle')});
`);
diff --git a/ui/src/tracks/cpu_profile/controller.ts b/ui/src/tracks/cpu_profile/controller.ts
index 0042888..a6c5456 100644
--- a/ui/src/tracks/cpu_profile/controller.ts
+++ b/ui/src/tracks/cpu_profile/controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {iter, NUM, slowlyCountRows} from '../../common/query_iterator';
+import {NUM} from '../../common/query_iterator';
import {
TrackController,
trackControllerRegistry
@@ -36,9 +36,8 @@
where utid = ${this.config.utid}
order by ts`;
- const result = await this.query(query);
-
- const numRows = slowlyCountRows(result);
+ const result = await this.queryV2(query);
+ const numRows = result.numRows();
const data: Data = {
start,
end,
@@ -49,11 +48,11 @@
callsiteId: new Uint32Array(numRows),
};
- const it = iter({id: NUM, ts: NUM, callsiteId: NUM}, result);
- for (let i = 0; it.valid(); it.next(), ++i) {
- data.ids[i] = it.row.id;
- data.tsStarts[i] = it.row.ts;
- data.callsiteId[i] = it.row.callsiteId;
+ const it = result.iter({id: NUM, ts: NUM, callsiteId: NUM});
+ for (let row = 0; it.valid(); it.next(), ++row) {
+ data.ids[row] = it.id;
+ data.tsStarts[row] = it.ts;
+ data.callsiteId[row] = it.callsiteId;
}
return data;
diff --git a/ui/src/tracks/cpu_slices/controller.ts b/ui/src/tracks/cpu_slices/controller.ts
index d6ac723..2d891ef 100644
--- a/ui/src/tracks/cpu_slices/controller.ts
+++ b/ui/src/tracks/cpu_slices/controller.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {assertTrue} from '../../base/logging';
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -29,7 +29,7 @@
private maxDurNs = 0;
async onSetup() {
- await this.query(`
+ await this.queryV2(`
create view ${this.tableName('sched')} as
select
ts,
@@ -40,18 +40,18 @@
where cpu = ${this.config.cpu} and utid != 0
`);
- const rawResult = await this.query(`
- select max(dur), count(1)
+ const queryRes = await this.queryV2(`
+ select ifnull(max(dur), 0) as maxDur, count(1) as rowCount
from ${this.tableName('sched')}
`);
- this.maxDurNs = rawResult.columns[0].longValues![0];
-
- const rowCount = rawResult.columns[1].longValues![0];
+ const row = queryRes.firstRow({maxDur: NUM, rowCount: NUM});
+ this.maxDurNs = row.maxDur;
+ const rowCount = row.rowCount;
const bucketNs = this.cachedBucketSizeNs(rowCount);
if (bucketNs === undefined) {
return;
}
- await this.query(`
+ await this.queryV2(`
create table ${this.tableName('sched_cached')} as
select
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cached_tsq,
@@ -90,7 +90,7 @@
isCached ? this.tableName('sched_cached') : this.tableName('sched');
const constraintColumn = isCached ? 'cached_tsq' : 'ts';
- const rawResult = await this.query(`
+ const queryRes = await this.queryV2(`
select
${queryTsq} as tsq,
ts,
@@ -105,7 +105,7 @@
order by tsq
`);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = queryRes.numRows();
const slices: Data = {
start,
end,
@@ -117,31 +117,30 @@
utids: new Uint32Array(numRows),
};
- const cols = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- const startNsQ = +cols[0].longValues![row];
- const startNs = +cols[1].longValues![row];
- const durNs = +cols[2].longValues![row];
+ const it = queryRes.iter({tsq: NUM, ts: NUM, dur: NUM, utid: NUM, id: NUM});
+ for (let row = 0; it.valid(); it.next(), row++) {
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- if (startNsQ === endNsQ) {
- throw new Error('Should never happen');
- }
+ assertTrue(startNsQ !== endNsQ);
slices.starts[row] = fromNs(startNsQ);
slices.ends[row] = fromNs(endNsQ);
- slices.utids[row] = +cols[3].longValues![row];
- slices.ids[row] = +cols[4].longValues![row];
+ slices.utids[row] = it.utid;
+ slices.ids[row] = it.id;
}
return slices;
}
async onDestroy() {
- await this.query(`drop table if exists ${this.tableName('sched_cached')}`);
+ await this.queryV2(
+ `drop table if exists ${this.tableName('sched_cached')}`);
}
}
diff --git a/ui/src/tracks/debug_slices/controller.ts b/ui/src/tracks/debug_slices/controller.ts
index 0afcb55..8400af8 100644
--- a/ui/src/tracks/debug_slices/controller.ts
+++ b/ui/src/tracks/debug_slices/controller.ts
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertTrue} from '../../base/logging';
import {Actions} from '../../common/actions';
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM, NUM_NULL, STR} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {globals} from '../../controller/globals';
import {
@@ -28,28 +27,26 @@
static readonly kind = DEBUG_SLICE_TRACK_KIND;
async onReload() {
- const rawResult = await this.query(`select max(depth) from debug_slices`);
- const maxDepth = (slowlyCountRows(rawResult) === 0) ?
- 1 :
- rawResult.columns[0].longValues![0];
+ const rawResult = await this.queryV2(
+ `select ifnull(max(depth), 1) as maxDepth from debug_slices`);
+ const maxDepth = rawResult.firstRow({maxDepth: NUM}).maxDepth;
globals.dispatch(
Actions.updateTrackConfig({id: this.trackId, config: {maxDepth}}));
}
async onBoundsChange(start: number, end: number, resolution: number):
Promise<Data> {
- const rawResult = await this.query(`select id, name, ts,
- iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur),
- depth from debug_slices where
- (ts + dur) >= ${toNs(start)} and ts <= ${toNs(end)}`);
+ const queryRes = await this.queryV2(`select
+ ifnull(id, -1) as id,
+ ifnull(name, '[null]') as name,
+ ts,
+ iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur) as dur,
+ ifnull(depth, 0) as depth
+ from debug_slices
+ where (ts + dur) >= ${toNs(start)} and ts <= ${toNs(end)}`);
- assertTrue(rawResult.columns.length === 5);
- const [idCol, nameCol, tsCol, durCol, depthCol] = rawResult.columns;
- const idValues = idCol.longValues! || idCol.doubleValues!;
- const tsValues = tsCol.longValues! || tsCol.doubleValues!;
- const durValues = durCol.longValues! || durCol.doubleValues!;
+ const numRows = queryRes.numRows();
- const numRows = slowlyCountRows(rawResult);
const slices: Data = {
start,
end,
@@ -75,24 +72,24 @@
return idx;
}
- for (let i = 0; i < slowlyCountRows(rawResult); i++) {
+ const it = queryRes.iter(
+ {id: NUM, name: STR, ts: NUM_NULL, dur: NUM_NULL, depth: NUM});
+ for (let row = 0; it.valid(); it.next(), row++) {
let sliceStart: number, sliceEnd: number;
- if (tsCol.isNulls![i] || durCol.isNulls![i]) {
+ if (it.ts === null || it.dur === null) {
sliceStart = sliceEnd = -1;
} else {
- sliceStart = tsValues[i];
- const sliceDur = durValues[i];
- sliceEnd = sliceStart + sliceDur;
+ sliceStart = it.ts;
+ sliceEnd = sliceStart + it.dur;
}
- slices.sliceIds[i] = idCol.isNulls![i] ? -1 : idValues[i];
- slices.starts[i] = fromNs(sliceStart);
- slices.ends[i] = fromNs(sliceEnd);
- slices.depths[i] = depthCol.isNulls![i] ? 0 : depthCol.longValues![i];
- const sliceName =
- nameCol.isNulls![i] ? '[null]' : nameCol.stringValues![i];
- slices.titles[i] = internString(sliceName);
- slices.isInstant[i] = 0;
- slices.isIncomplete[i] = 0;
+ slices.sliceIds[row] = it.id;
+ slices.starts[row] = fromNs(sliceStart);
+ slices.ends[row] = fromNs(sliceEnd);
+ slices.depths[row] = it.depth;
+ const sliceName = it.name;
+ slices.titles[row] = internString(sliceName);
+ slices.isInstant[row] = 0;
+ slices.isIncomplete[row] = 0;
}
return slices;
diff --git a/ui/src/tracks/expected_frames/controller.ts b/ui/src/tracks/expected_frames/controller.ts
index 9707059..5227a81 100644
--- a/ui/src/tracks/expected_frames/controller.ts
+++ b/ui/src/tracks/expected_frames/controller.ts
@@ -12,14 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists} from '../../base/logging';
-import {
- iter,
- NUM,
- singleRow,
- slowlyCountRows,
- STR
-} from '../../common/query_iterator';
+import {assertTrue} from '../../base/logging';
+import {NUM, NUM_NULL, STR} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -44,17 +38,16 @@
const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
if (this.maxDurNs === 0) {
- const maxDurResult = await this.query(`
+ const maxDurResult = await this.queryV2(`
select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
as maxDur
from experimental_slice_layout
where filter_track_ids = '${this.config.trackIds.join(',')}'
`);
- const row = singleRow({maxDur: NUM}, maxDurResult);
- this.maxDurNs = assertExists(row).maxDur;
+ this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
}
- const rawResult = await this.query(`
+ const queryRes = await this.queryV2(`
SELECT
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
ts,
@@ -73,7 +66,7 @@
order by tsq, layout_depth
`);
- const numRows = slowlyCountRows(rawResult);
+ const numRows = queryRes.numRows();
const slices: Data = {
start,
end,
@@ -101,39 +94,35 @@
}
const greenIndex = internString('#4CAF50');
- const it = iter(
- {
- tsq: NUM,
- ts: NUM,
- dur: NUM,
- layoutDepth: NUM,
- id: NUM,
- name: STR,
- isInstant: NUM,
- isIncomplete: NUM,
- },
- rawResult);
- for (let i = 0; it.valid(); it.next(), ++i) {
- const startNsQ = it.row.tsq;
- const startNs = it.row.ts;
- const durNs = it.row.dur;
+ const it = queryRes.iter({
+ tsq: NUM,
+ ts: NUM,
+ dur: NUM,
+ layoutDepth: NUM,
+ id: NUM,
+ name: STR,
+ isInstant: NUM,
+ isIncomplete: NUM,
+ });
+ for (let row = 0; it.valid(); it.next(), ++row) {
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- if (startNsQ === endNsQ) {
- throw new Error('Should never happen');
- }
+ assertTrue(startNsQ !== endNsQ);
- slices.starts[i] = fromNs(startNsQ);
- slices.ends[i] = fromNs(endNsQ);
- slices.depths[i] = it.row.layoutDepth;
- slices.titles[i] = internString(it.row.name);
- slices.sliceIds[i] = it.row.id;
- slices.isInstant[i] = it.row.isInstant;
- slices.isIncomplete[i] = it.row.isIncomplete;
- slices.colors![i] = greenIndex;
+ slices.starts[row] = fromNs(startNsQ);
+ slices.ends[row] = fromNs(endNsQ);
+ slices.depths[row] = it.layoutDepth;
+ slices.titles[row] = internString(it.name);
+ slices.sliceIds[row] = it.id;
+ slices.isInstant[row] = it.isInstant;
+ slices.isIncomplete[row] = it.isIncomplete;
+ slices.colors![row] = greenIndex;
}
return slices;
}
diff --git a/ui/src/tracks/heap_profile/controller.ts b/ui/src/tracks/heap_profile/controller.ts
index 2422af1..9873744 100644
--- a/ui/src/tracks/heap_profile/controller.ts
+++ b/ui/src/tracks/heap_profile/controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM, STR} from '../../common/query_iterator';
import {
TrackController,
trackControllerRegistry
@@ -38,7 +38,7 @@
types: new Array<string>()
};
}
- const result = await this.query(`
+ const queryRes = await this.queryV2(`
select * from
(select distinct(ts) as ts, 'native' as type from heap_profile_allocation
where upid = ${this.config.upid}
@@ -46,7 +46,7 @@
select distinct(graph_sample_ts) as ts, 'graph' as type from
heap_graph_object
where upid = ${this.config.upid}) order by ts`);
- const numRows = slowlyCountRows(result);
+ const numRows = queryRes.numRows();
const data: Data = {
start,
end,
@@ -56,11 +56,11 @@
types: new Array<string>(numRows),
};
- for (let row = 0; row < numRows; row++) {
- data.tsStarts[row] = +result.columns[0].longValues![row];
- data.types[row] = result.columns[1].stringValues![row];
+ const it = queryRes.iter({ts: NUM, type: STR});
+ for (let row = 0; it.valid(); it.next(), row++) {
+ data.tsStarts[row] = it.ts;
+ data.types[row] = it.type;
}
-
return data;
}
}
diff --git a/ui/src/tracks/process_scheduling/controller.ts b/ui/src/tracks/process_scheduling/controller.ts
index 687d16d..9441448 100644
--- a/ui/src/tracks/process_scheduling/controller.ts
+++ b/ui/src/tracks/process_scheduling/controller.ts
@@ -13,8 +13,8 @@
// limitations under the License.
import {assertTrue} from '../../base/logging';
-import {RawQueryResult} from '../../common/protos';
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM} from '../../common/query_iterator';
+import {QueryResult} from '../../common/query_result';
import {fromNs, toNs} from '../../common/time';
import {
TrackController,
@@ -45,18 +45,19 @@
assertTrue(cpus.length > 0);
this.maxCpu = Math.max(...cpus) + 1;
- const result = await this.query(`
- select max(dur), count(1)
+ const result = (await this.queryV2(`
+ select ifnull(max(dur), 0) as maxDur, count(1) as count
from ${this.tableName('process_sched')}
- `);
- this.maxDurNs = result.columns[0].longValues![0];
+ `)).iter({maxDur: NUM, count: NUM});
+ assertTrue(result.valid());
+ this.maxDurNs = result.maxDur;
- const rowCount = result.columns[1].longValues![0];
+ const rowCount = result.count;
const bucketNs = this.cachedBucketSizeNs(rowCount);
if (bucketNs === undefined) {
return;
}
- await this.query(`
+ await this.queryV2(`
create table ${this.tableName('process_sched_cached')} as
select
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cached_tsq,
@@ -88,9 +89,8 @@
const bucketNs =
Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
- const rawResult = await this.queryData(startNs, endNs, bucketNs);
-
- const numRows = slowlyCountRows(rawResult);
+ const queryRes = await this.queryData(startNs, endNs, bucketNs);
+ const numRows = queryRes.numRows();
const slices: Data = {
kind: 'slice',
start,
@@ -104,38 +104,43 @@
utids: new Uint32Array(numRows),
};
- const cols = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- const startNsQ = +cols[0].longValues![row];
- const startNs = +cols[1].longValues![row];
- const durNs = +cols[2].longValues![row];
+ const it = queryRes.iter({
+ tsq: NUM,
+ ts: NUM,
+ dur: NUM,
+ cpu: NUM,
+ utid: NUM,
+ });
+
+ for (let row = 0; it.valid(); it.next(), row++) {
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- if (startNsQ === endNsQ) {
- throw new Error('Should never happen');
- }
+ assertTrue(startNsQ !== endNsQ);
slices.starts[row] = fromNs(startNsQ);
slices.ends[row] = fromNs(endNsQ);
- slices.cpus[row] = +cols[3].longValues![row];
- slices.utids[row] = +cols[4].longValues![row];
+ slices.cpus[row] = it.cpu;
+ slices.utids[row] = it.utid;
slices.end = Math.max(slices.ends[row], slices.end);
}
return slices;
}
private queryData(startNs: number, endNs: number, bucketNs: number):
- Promise<RawQueryResult> {
+ Promise<QueryResult> {
const isCached = this.cachedBucketNs <= bucketNs;
const tsq = isCached ? `cached_tsq / ${bucketNs} * ${bucketNs}` :
`(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
const queryTable = isCached ? this.tableName('process_sched_cached') :
this.tableName('process_sched');
const constraintColumn = isCached ? 'cached_tsq' : 'ts';
- return this.query(`
+ return this.queryV2(`
select
${tsq} as tsq,
ts,
@@ -152,7 +157,7 @@
}
private async createSchedView() {
- await this.query(`
+ await this.queryV2(`
create view ${this.tableName('process_sched')} as
select ts, dur, cpu, utid
from experimental_sched_upid
diff --git a/ui/src/tracks/process_summary/controller.ts b/ui/src/tracks/process_summary/controller.ts
index 75242fe..9e6ec5e 100644
--- a/ui/src/tracks/process_summary/controller.ts
+++ b/ui/src/tracks/process_summary/controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {slowlyCountRows} from '../../common/query_iterator';
+import {NUM} from '../../common/query_iterator';
import {fromNs, toNs} from '../../common/time';
import {LIMIT} from '../../common/track_data';
import {
@@ -39,29 +39,35 @@
const endNs = toNs(end);
if (this.setup === false) {
- await this.query(
+ await this.queryV2(
`create virtual table ${this.tableName('window')} using window;`);
let utids = [this.config.utid];
if (this.config.upid) {
- const threadQuery = await this.query(
+ const threadQuery = await this.queryV2(
`select utid from thread where upid=${this.config.upid}`);
- utids = threadQuery.columns[0].longValues!;
+ utids = [];
+ for (const it = threadQuery.iter({utid: NUM}); it.valid(); it.next()) {
+ utids.push(it.utid);
+ }
}
- const trackQuery = await this.query(
+ const trackQuery = await this.queryV2(
`select id from thread_track where utid in (${utids.join(',')})`);
- const tracks = trackQuery.columns[0].longValues!;
+ const tracks = [];
+ for (const it = trackQuery.iter({id: NUM}); it.valid(); it.next()) {
+ tracks.push(it.id);
+ }
const processSliceView = this.tableName('process_slice_view');
- await this.query(
+ await this.queryV2(
`create view ${processSliceView} as ` +
// 0 as cpu is a dummy column to perform span join on.
`select ts, dur/${utids.length} as dur ` +
`from slice s ` +
`where depth = 0 and track_id in ` +
`(${tracks.join(',')})`);
- await this.query(`create virtual table ${this.tableName('span')}
+ await this.queryV2(`create virtual table ${this.tableName('span')}
using span_join(${processSliceView},
${this.tableName('window')});`);
this.setup = true;
@@ -73,7 +79,7 @@
const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
const windowDurNs = Math.max(1, endNs - windowStartNs);
- this.query(`update ${this.tableName('window')} set
+ await this.queryV2(`update ${this.tableName('window')} set
window_start=${windowStartNs},
window_dur=${windowDurNs},
quantum=${bucketSizeNs}
@@ -98,9 +104,6 @@
group by quantum_ts
limit ${LIMIT}`;
- const rawResult = await this.query(query);
- const numRows = slowlyCountRows(rawResult);
-
const summary: Data = {
start,
end,
@@ -109,21 +112,24 @@
bucketSizeSeconds: fromNs(bucketSizeNs),
utilizations: new Float64Array(numBuckets),
};
- const cols = rawResult.columns;
- for (let row = 0; row < numRows; row++) {
- const bucket = +cols[0].longValues![row];
+
+ const queryRes = await this.queryV2(query);
+ const it = queryRes.iter({bucket: NUM, utilization: NUM});
+ for (; it.valid(); it.next()) {
+ const bucket = it.bucket;
if (bucket > numBuckets) {
continue;
}
- summary.utilizations[bucket] = +cols[1].doubleValues![row];
+ summary.utilizations[bucket] = it.utilization;
}
+
return summary;
}
onDestroy(): void {
if (this.setup) {
- this.query(`drop table ${this.tableName('window')}`);
- this.query(`drop table ${this.tableName('span')}`);
+ this.queryV2(`drop table ${this.tableName('window')}`);
+ this.queryV2(`drop table ${this.tableName('span')}`);
this.setup = false;
}
}
diff --git a/ui/src/tracks/thread_state/controller.ts b/ui/src/tracks/thread_state/controller.ts
index a4718c6..0de3a3e 100644
--- a/ui/src/tracks/thread_state/controller.ts
+++ b/ui/src/tracks/thread_state/controller.ts
@@ -14,10 +14,8 @@
import {assertFalse} from '../../base/logging';
import {
- iter,
NUM,
NUM_NULL,
- slowlyCountRows,
STR_NULL
} from '../../common/query_iterator';
import {translateState} from '../../common/thread_state';
@@ -39,7 +37,7 @@
private maxDurNs = 0;
async onSetup() {
- await this.query(`
+ await this.queryV2(`
create view ${this.tableName('thread_state')} as
select
id,
@@ -52,11 +50,11 @@
where utid = ${this.config.utid} and utid != 0
`);
- const rawResult = await this.query(`
- select max(dur)
+ const queryRes = await this.queryV2(`
+ select ifnull(max(dur), 0) as maxDur
from ${this.tableName('thread_state')}
`);
- this.maxDurNs = rawResult.columns[0].longValues![0];
+ this.maxDurNs = queryRes.firstRow({maxDur: NUM}).maxDur;
}
async onBoundsChange(start: number, end: number, resolution: number):
@@ -75,10 +73,10 @@
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
ts,
max(dur) as dur,
- cast(cpu as integer) as cpu,
+ ifnull(cast(cpu as integer), -1) as cpu,
state,
io_wait,
- id
+ ifnull(id, -1) as id
from ${this.tableName('thread_state')}
where
ts >= ${startNs - this.maxDurNs} and
@@ -87,8 +85,8 @@
order by tsq, state, io_wait
`;
- const result = await this.query(query);
- const numRows = slowlyCountRows(result);
+ const queryRes = await this.queryV2(query);
+ const numRows = queryRes.numRows();
const data: Data = {
start,
@@ -113,31 +111,28 @@
stringIndexes.set({shortState, ioWait}, idx);
return idx;
}
- iter(
- {
- 'ts': NUM,
- 'dur': NUM,
- 'cpu': NUM_NULL,
- 'state': STR_NULL,
- 'io_wait': NUM_NULL,
- 'id': NUM_NULL,
- },
- result);
- for (let row = 0; row < numRows; row++) {
- const cols = result.columns;
- const startNsQ = +cols[0].longValues![row];
- const startNs = +cols[1].longValues![row];
- const durNs = +cols[2].longValues![row];
+ const it = queryRes.iter({
+ 'tsq': NUM,
+ 'ts': NUM,
+ 'dur': NUM,
+ 'cpu': NUM,
+ 'state': STR_NULL,
+ 'io_wait': NUM_NULL,
+ 'id': NUM,
+ });
+ for (let row = 0; it.valid(); it.next(), row++) {
+ const startNsQ = it.tsq;
+ const startNs = it.ts;
+ const durNs = it.dur;
const endNs = startNs + durNs;
let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- const cpu = cols[3].isNulls![row] ? -1 : cols[3].longValues![row];
- const state = cols[4].stringValues![row];
- const ioWait =
- cols[5].isNulls![row] ? undefined : !!cols[5].longValues![row];
- const id = cols[6].isNulls![row] ? -1 : cols[6].longValues![row];
+ const cpu = it.cpu;
+ const state = it.state || '[null]';
+ const ioWait = it.io_wait === null ? undefined : !!it.io_wait;
+ const id = it.id;
// We should never have the end timestamp being the same as the bucket
// start.
@@ -153,7 +148,8 @@
}
async onDestroy() {
- await this.query(`drop table if exists ${this.tableName('thread_state')}`);
+ await this.queryV2(
+ `drop table if exists ${this.tableName('thread_state')}`);
}
}