Merge "ui: Move traceTime out of state" into main
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 92cad97..4cebecb 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -15,7 +15,7 @@
import {Draft} from 'immer';
import {assertExists, assertTrue} from '../base/logging';
-import {duration, time} from '../base/time';
+import {duration, Time, time} from '../base/time';
import {RecordConfig} from '../controller/record_config_types';
import {
GenericSliceDetailsTabConfig,
@@ -63,7 +63,6 @@
State,
Status,
ThreadTrackSortKey,
- TraceTime,
TrackSortKey,
UtidToTrackSortKey,
VisibleState,
@@ -474,10 +473,6 @@
state.permalink = {};
},
- setTraceTime(state: StateDraft, args: TraceTime): void {
- state.traceTime = args;
- },
-
updateStatus(state: StateDraft, args: Status): void {
if (statusTraceEvent) {
traceEventEnd(statusTraceEvent);
@@ -673,7 +668,7 @@
};
this.openFlamegraph(state, {
type: args.type,
- start: state.traceTime.start as time, // TODO(stevegolton): Avoid type assertion here.
+ start: Time.ZERO,
end: args.ts,
upids: [args.upid],
viewingOption: defaultViewingOption(args.type),
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index f866914..e3ed2b7 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -22,12 +22,7 @@
} from '../frontend/record_config';
import {SqlTables} from '../frontend/sql_table/well_known_tables';
-import {
- defaultTraceTime,
- NonSerializableState,
- State,
- STATE_VERSION,
-} from './state';
+import {NonSerializableState, State, STATE_VERSION} from './state';
const AUTOLOAD_STARTED_CONFIG_FLAG = featureFlags.register({
id: 'autoloadStartedConfig',
@@ -92,7 +87,6 @@
version: STATE_VERSION,
nextId: '-1',
newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE',
- traceTime: {...defaultTraceTime},
tracks: {},
utidToThreadSortKey: {},
aggregatePreferences: {},
@@ -112,7 +106,8 @@
frontendLocalState: {
visibleState: {
- ...defaultTraceTime,
+ start: Time.ZERO,
+ end: Time.ZERO,
lastUpdate: 0,
resolution: 0n,
},
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index d0af73b..7ca7a74 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {BigintMath} from '../base/bigint_math';
-import {duration, Time, time} from '../base/time';
+import {duration, time} from '../base/time';
import {RecordConfig} from '../controller/record_config_types';
import {
Aggregation,
@@ -150,7 +150,8 @@
// 51. Changed structure of FlamegraphState.expandedCallsiteByViewingOption.
// 52. Update track group state - don't make the summary track the first track.
// 53. Remove android log state.
-export const STATE_VERSION = 53;
+// 54. Remove traceTime.
+export const STATE_VERSION = 54;
export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
@@ -315,11 +316,6 @@
isRecordingConfig?: boolean; // this permalink request is for a recording config only
}
-export interface TraceTime {
- start: time;
- end: time;
-}
-
export interface FrontendLocalState {
visibleState: VisibleState;
}
@@ -479,7 +475,6 @@
*/
newEngineMode: NewEngineMode;
engine?: EngineConfig;
- traceTime: TraceTime;
traceUuid?: string;
trackGroups: ObjectById<TrackGroupState>;
tracks: ObjectByKey<TrackState>;
@@ -558,11 +553,6 @@
plugins: {[key: string]: any};
}
-export const defaultTraceTime = {
- start: Time.ZERO,
- end: Time.fromSeconds(10),
-};
-
export declare type RecordMode =
| 'STOP_WHEN_FULL'
| 'RING_BUFFER'
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index dc905ea..1b6dd0f 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -441,7 +441,7 @@
IFNULL(value, 0) as value
FROM counter WHERE ts < ${ts} and track_id = ${trackId}`);
const previousValue = previous.firstRow({value: NUM}).value;
- const endTs = rightTs !== -1n ? rightTs : globals.state.traceTime.end;
+ const endTs = rightTs !== -1n ? rightTs : globals.traceTime.end;
const delta = value - previousValue;
const duration = endTs - ts;
const trackKey = globals.trackManager.trackKeyByTrackId.get(trackId);
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index a2fd592..7c9766c 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -26,21 +26,22 @@
isMetatracingEnabled,
} from '../common/metatracing';
import {pluginManager} from '../common/plugins';
+import {EngineMode, PendingDeeplinkState, ProfileType} from '../common/state';
+import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../core/feature_flags';
import {
defaultTraceTime,
- EngineMode,
- PendingDeeplinkState,
- ProfileType,
-} from '../common/state';
-import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../core/feature_flags';
-import {globals, QuantizedLoad, ThreadDesc} from '../frontend/globals';
+ globals,
+ QuantizedLoad,
+ ThreadDesc,
+ TraceTime,
+} from '../frontend/globals';
import {
clearOverviewData,
publishHasFtrace,
publishMetricError,
publishOverviewData,
- publishRealtimeOffset,
publishThreads,
+ publishTraceDetails,
} from '../frontend/publish';
import {addQueryResultsTab} from '../frontend/query_result_tab';
import {Router} from '../frontend/router';
@@ -450,13 +451,8 @@
// traceUuid will be '' if the trace is not cacheable (URL or RPC).
const traceUuid = await this.cacheCurrentTrace();
- const traceTime = await this.engine.getTraceTimeBounds();
- const start = traceTime.start;
- const end = traceTime.end;
- const traceTimeState = {
- start,
- end,
- };
+ const traceDetails = await getTraceTimeDetails(this.engine);
+ publishTraceDetails(traceDetails);
const shownJsonWarning =
window.localStorage.getItem(SHOWN_JSON_WARNING_KEY) !== null;
@@ -485,12 +481,11 @@
const actions: DeferredAction[] = [
Actions.setOmnibox(emptyOmniboxState),
Actions.setTraceUuid({traceUuid}),
- Actions.setTraceTime(traceTimeState),
];
const visibleTimeSpan = await computeVisibleTime(
- traceTime.start,
- traceTime.end,
+ traceDetails.start,
+ traceDetails.end,
isJsonTrace,
this.engine,
);
@@ -530,7 +525,9 @@
this.decideTabs();
await this.listThreads();
- await this.loadTimelineOverview(traceTime);
+ await this.loadTimelineOverview(
+ new TimeSpan(traceDetails.start, traceDetails.end),
+ );
{
// Check if we have any ftrace events at all
@@ -544,82 +541,12 @@
publishHasFtrace(res.numRows() > 0);
}
- {
- // Find the first REALTIME or REALTIME_COARSE clock snapshot.
- // Prioritize REALTIME over REALTIME_COARSE.
- const query = `select
- ts,
- clock_value as clockValue,
- clock_name as clockName
- from clock_snapshot
- where
- snapshot_id = 0 AND
- clock_name in ('REALTIME', 'REALTIME_COARSE')
- `;
- const result = await assertExists(this.engine).query(query);
- const it = result.iter({
- ts: LONG,
- clockValue: LONG,
- clockName: STR,
- });
-
- let snapshot = {
- clockName: '',
- ts: Time.ZERO,
- clockValue: Time.ZERO,
- };
-
- // Find the most suitable snapshot
- for (let row = 0; it.valid(); it.next(), row++) {
- if (it.clockName === 'REALTIME') {
- snapshot = {
- clockName: it.clockName,
- ts: Time.fromRaw(it.ts),
- clockValue: Time.fromRaw(it.clockValue),
- };
- break;
- } else if (it.clockName === 'REALTIME_COARSE') {
- if (snapshot.clockName !== 'REALTIME') {
- snapshot = {
- clockName: it.clockName,
- ts: Time.fromRaw(it.ts),
- clockValue: Time.fromRaw(it.clockValue),
- };
- }
- }
- }
-
- // The max() is so the query returns NULL if the tz info doesn't exist.
- const queryTz = `select max(int_value) as tzOffMin from metadata
- where name = 'timezone_off_mins'`;
- const resTz = await assertExists(this.engine).query(queryTz);
- const tzOffMin = resTz.firstRow({tzOffMin: NUM_NULL}).tzOffMin ?? 0;
-
- // This is the offset between the unix epoch and ts in the ts domain.
- // I.e. the value of ts at the time of the unix epoch - usually some large
- // negative value.
- const realtimeOffset = Time.sub(snapshot.ts, snapshot.clockValue);
-
- // Find the previous closest midnight from the trace start time.
- const utcOffset = Time.getLatestMidnight(
- globals.state.traceTime.start,
- realtimeOffset,
- );
-
- const traceTzOffset = Time.getLatestMidnight(
- globals.state.traceTime.start,
- Time.sub(realtimeOffset, Time.fromSeconds(tzOffMin * 60)),
- );
-
- publishRealtimeOffset(realtimeOffset, utcOffset, traceTzOffset);
- }
-
globals.dispatch(Actions.sortThreadTracks({}));
globals.dispatch(Actions.maybeExpandOnlyTrackGroup({}));
await this.selectFirstHeapProfile();
if (PERF_SAMPLE_FLAG.get()) {
- await this.selectPerfSample();
+ await this.selectPerfSample(traceDetails);
}
const pendingDeeplink = globals.state.pendingDeeplink;
@@ -663,7 +590,7 @@
return engineMode;
}
- private async selectPerfSample() {
+ private async selectPerfSample(traceTime: {start: time; end: time}) {
const query = `select upid
from perf_sample
join thread using (utid)
@@ -673,8 +600,8 @@
if (profile.numRows() !== 1) return;
const row = profile.firstRow({upid: NUM});
const upid = row.upid;
- const leftTs = globals.state.traceTime.start;
- const rightTs = globals.state.traceTime.end;
+ const leftTs = traceTime.start;
+ const rightTs = traceTime.end;
globals.dispatch(
Actions.selectPerfSamples({
id: 0,
@@ -1217,3 +1144,77 @@
}
return HighPrecisionTimeSpan.fromTime(visibleStart, visibleEnd);
}
+
+async function getTraceTimeDetails(engine: Engine): Promise<TraceTime> {
+ const traceTime = await engine.getTraceTimeBounds();
+
+ // Find the first REALTIME or REALTIME_COARSE clock snapshot.
+ // Prioritize REALTIME over REALTIME_COARSE.
+ const query = `select
+ ts,
+ clock_value as clockValue,
+ clock_name as clockName
+ from clock_snapshot
+ where
+ snapshot_id = 0 AND
+ clock_name in ('REALTIME', 'REALTIME_COARSE')
+ `;
+ const result = await engine.query(query);
+ const it = result.iter({
+ ts: LONG,
+ clockValue: LONG,
+ clockName: STR,
+ });
+
+ let snapshot = {
+ clockName: '',
+ ts: Time.ZERO,
+ clockValue: Time.ZERO,
+ };
+
+ // Find the most suitable snapshot
+ for (let row = 0; it.valid(); it.next(), row++) {
+ if (it.clockName === 'REALTIME') {
+ snapshot = {
+ clockName: it.clockName,
+ ts: Time.fromRaw(it.ts),
+ clockValue: Time.fromRaw(it.clockValue),
+ };
+ break;
+ } else if (it.clockName === 'REALTIME_COARSE') {
+ if (snapshot.clockName !== 'REALTIME') {
+ snapshot = {
+ clockName: it.clockName,
+ ts: Time.fromRaw(it.ts),
+ clockValue: Time.fromRaw(it.clockValue),
+ };
+ }
+ }
+ }
+
+ // The max() is so the query returns NULL if the tz info doesn't exist.
+ const queryTz = `select max(int_value) as tzOffMin from metadata
+ where name = 'timezone_off_mins'`;
+ const resTz = await assertExists(engine).query(queryTz);
+ const tzOffMin = resTz.firstRow({tzOffMin: NUM_NULL}).tzOffMin ?? 0;
+
+ // This is the offset between the unix epoch and ts in the ts domain.
+ // I.e. the value of ts at the time of the unix epoch - usually some large
+ // negative value.
+ const realtimeOffset = Time.sub(snapshot.ts, snapshot.clockValue);
+
+ // Find the previous closest midnight from the trace start time.
+ const utcOffset = Time.getLatestMidnight(traceTime.start, realtimeOffset);
+
+ const traceTzOffset = Time.getLatestMidnight(
+ traceTime.start,
+ Time.sub(realtimeOffset, Time.fromSeconds(tzOffMin * 60)),
+ );
+
+ return {
+ ...traceTime,
+ realtimeOffset,
+ utcOffset,
+ traceTzOffset,
+ };
+}
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 18adc7f..09b1036 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -620,8 +620,8 @@
if (selection !== null && selection.kind === 'AREA') {
const area = globals.state.areas[selection.areaId];
const coversEntireTimeRange =
- globals.state.traceTime.start === area.start &&
- globals.state.traceTime.end === area.end;
+ globals.traceTime.start === area.start &&
+ globals.traceTime.end === area.end;
if (!coversEntireTimeRange) {
// If the current selection is an area which does not cover the
// entire time range, preserve the list of selected tracks and
@@ -636,7 +636,7 @@
// If the current selection is not an area, select all.
tracksToSelect = Object.keys(globals.state.tracks);
}
- const {start, end} = globals.state.traceTime;
+ const {start, end} = globals.traceTime;
globals.dispatch(
Actions.selectArea({
area: {
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index bdb46ac..04cdc17 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -221,6 +221,32 @@
pendingScrollId: number | undefined;
}
+export interface TraceTime {
+ readonly start: time;
+ readonly end: time;
+
+ // This is the ts value at the time of the Unix epoch.
+ // Normally some large negative value, because the unix epoch is normally in
+ // the past compared to ts=0.
+ readonly realtimeOffset: time;
+
+ // This is the timestamp that we should use for our offset when in UTC mode.
+ // Usually the most recent UTC midnight compared to the trace start time.
+ readonly utcOffset: time;
+
+ // Trace TZ is like UTC but keeps into account also the timezone_off_mins
+ // recorded into the trace, to show timestamps in the device local time.
+ readonly traceTzOffset: time;
+}
+
+export const defaultTraceTime: TraceTime = {
+ start: Time.ZERO,
+ end: Time.fromSeconds(10),
+ realtimeOffset: Time.ZERO,
+ utcOffset: Time.ZERO,
+ traceTzOffset: Time.ZERO,
+};
+
/**
* Global accessors for state/dispatch in the frontend.
*/
@@ -260,9 +286,6 @@
private _embeddedMode?: boolean = undefined;
private _hideSidebar?: boolean = undefined;
private _cmdManager = new CommandManager();
- private _realtimeOffset = Time.ZERO;
- private _utcOffset = Time.ZERO;
- private _traceTzOffset = Time.ZERO;
private _tabManager = new TabManager();
private _trackManager = new TrackManager(this._store);
private _selectionManager = new SelectionManager(this._store);
@@ -273,6 +296,8 @@
newVersionAvailable = false;
showPanningHint = false;
+ traceTime = defaultTraceTime;
+
// TODO(hjd): Remove once we no longer need to update UUID on redraw.
private _publishRedraw?: () => void = undefined;
@@ -691,19 +716,19 @@
// Get a timescale that covers the entire trace
getTraceTimeScale(pxSpan: PxSpan): TimeScale {
- const {start, end} = this.state.traceTime;
+ const {start, end} = this.traceTime;
const traceTime = HighPrecisionTimeSpan.fromTime(start, end);
return TimeScale.fromHPTimeSpan(traceTime, pxSpan);
}
// Get the trace time bounds
stateTraceTime(): Span<HighPrecisionTime> {
- const {start, end} = this.state.traceTime;
+ const {start, end} = this.traceTime;
return HighPrecisionTimeSpan.fromTime(start, end);
}
stateTraceTimeTP(): Span<time, duration> {
- const {start, end} = this.state.traceTime;
+ const {start, end} = this.traceTime;
return new TimeSpan(start, end);
}
@@ -723,37 +748,6 @@
return assertExists(this._cmdManager);
}
- // This is the ts value at the time of the Unix epoch.
- // Normally some large negative value, because the unix epoch is normally in
- // the past compared to ts=0.
- get realtimeOffset(): time {
- return this._realtimeOffset;
- }
-
- set realtimeOffset(time: time) {
- this._realtimeOffset = time;
- }
-
- // This is the timestamp that we should use for our offset when in UTC mode.
- // Usually the most recent UTC midnight compared to the trace start time.
- get utcOffset(): time {
- return this._utcOffset;
- }
-
- set utcOffset(offset: time) {
- this._utcOffset = offset;
- }
-
- // Trace TZ is like UTC but keeps into account also the timezone_off_mins
- // recorded into the trace, to show timestamps in the device local time.
- get traceTzOffset(): time {
- return this._traceTzOffset;
- }
-
- set traceTzOffset(offset: time) {
- this._traceTzOffset = offset;
- }
-
get tabManager() {
return this._tabManager;
}
@@ -768,14 +762,14 @@
switch (fmt) {
case TimestampFormat.Timecode:
case TimestampFormat.Seconds:
- return this.state.traceTime.start;
+ return this.traceTime.start;
case TimestampFormat.Raw:
case TimestampFormat.RawLocale:
return Time.ZERO;
case TimestampFormat.UTC:
- return this.utcOffset;
+ return this.traceTime.utcOffset;
case TimestampFormat.TraceTz:
- return this.traceTzOffset;
+ return this.traceTime.traceTzOffset;
default:
const x: never = fmt;
throw new Error(`Unsupported format ${x}`);
diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts
index f8c36cb..bfb34ca 100644
--- a/ui/src/frontend/publish.ts
+++ b/ui/src/frontend/publish.ts
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {time} from '../base/time';
import {Actions} from '../common/actions';
import {AggregateData} from '../common/aggregation_data';
import {ConversionJobStatusUpdate} from '../common/conversion_jobs';
@@ -32,6 +31,7 @@
SliceDetails,
ThreadDesc,
ThreadStateDetails,
+ TraceTime,
} from './globals';
import {findCurrentSelection} from './keyboard_event_handler';
@@ -96,14 +96,8 @@
globals.publishRedraw();
}
-export function publishRealtimeOffset(
- offset: time,
- utcOffset: time,
- traceTzOffset: time,
-) {
- globals.realtimeOffset = offset;
- globals.utcOffset = utcOffset;
- globals.traceTzOffset = traceTzOffset;
+export function publishTraceDetails(details: TraceTime): void {
+ globals.traceTime = details;
globals.publishRedraw();
}
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index d20341f..af1f3df 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -57,16 +57,16 @@
break;
case TimestampFormat.UTC:
const offsetDate = Time.toDate(
- globals.utcOffset,
- globals.realtimeOffset,
+ globals.traceTime.utcOffset,
+ globals.traceTime.realtimeOffset,
);
const dateStr = toISODateOnly(offsetDate);
ctx.fillText(`UTC ${dateStr}`, 6, 10);
break;
case TimestampFormat.TraceTz:
const offsetTzDate = Time.toDate(
- globals.traceTzOffset,
- globals.realtimeOffset,
+ globals.traceTime.traceTzOffset,
+ globals.traceTime.realtimeOffset,
);
const dateTzStr = toISODateOnly(offsetTzDate);
ctx.fillText(dateTzStr, 6, 10);
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index b599dc7..b67f2e5 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -128,7 +128,7 @@
currentY: number,
editing: boolean,
) => {
- const traceTime = globals.state.traceTime;
+ const traceTime = globals.traceTime;
const {visibleTimeScale} = timeline;
this.keepCurrentSelection = true;
if (editing) {