Skip to content

Commit

Permalink
ui: Move traceTime out of state
Browse files Browse the repository at this point in the history
TraceTime (i.e. the start and end timestamps of the trace) has no
business being in state, it's a constant property of the trace and the
user never changes it.

This CL moves this state out into globals, grouping it together with
the various UTC time offsets.

Change-Id: Iece6e2d951c1ccd6385ebc48b38948d22b4bb613
  • Loading branch information
stevegolton committed May 14, 2024
1 parent 66c6c13 commit 435bdae
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 178 deletions.
9 changes: 2 additions & 7 deletions ui/src/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -63,7 +63,6 @@ import {
State,
Status,
ThreadTrackSortKey,
TraceTime,
TrackSortKey,
UtidToTrackSortKey,
VisibleState,
Expand Down Expand Up @@ -474,10 +473,6 @@ export const StateActions = {
state.permalink = {};
},

setTraceTime(state: StateDraft, args: TraceTime): void {
state.traceTime = args;
},

updateStatus(state: StateDraft, args: Status): void {
if (statusTraceEvent) {
traceEventEnd(statusTraceEvent);
Expand Down Expand Up @@ -673,7 +668,7 @@ export const StateActions = {
};
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),
Expand Down
11 changes: 3 additions & 8 deletions ui/src/common/empty_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@ import {
} 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',
Expand Down Expand Up @@ -92,7 +87,6 @@ export function createEmptyState(): State {
version: STATE_VERSION,
nextId: '-1',
newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE',
traceTime: {...defaultTraceTime},
tracks: {},
utidToThreadSortKey: {},
aggregatePreferences: {},
Expand All @@ -112,7 +106,8 @@ export function createEmptyState(): State {

frontendLocalState: {
visibleState: {
...defaultTraceTime,
start: Time.ZERO,
end: Time.ZERO,
lastUpdate: 0,
resolution: 0n,
},
Expand Down
16 changes: 3 additions & 13 deletions ui/src/common/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -150,7 +150,8 @@ export const MAX_TIME = 180;
// 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';

Expand Down Expand Up @@ -315,11 +316,6 @@ export interface PermalinkConfig {
isRecordingConfig?: boolean; // this permalink request is for a recording config only
}

export interface TraceTime {
start: time;
end: time;
}

export interface FrontendLocalState {
visibleState: VisibleState;
}
Expand Down Expand Up @@ -479,7 +475,6 @@ export interface State {
*/
newEngineMode: NewEngineMode;
engine?: EngineConfig;
traceTime: TraceTime;
traceUuid?: string;
trackGroups: ObjectById<TrackGroupState>;
tracks: ObjectByKey<TrackState>;
Expand Down Expand Up @@ -558,11 +553,6 @@ export interface State {
plugins: {[key: string]: any};
}

export const defaultTraceTime = {
start: Time.ZERO,
end: Time.fromSeconds(10),
};

export declare type RecordMode =
| 'STOP_WHEN_FULL'
| 'RING_BUFFER'
Expand Down
2 changes: 1 addition & 1 deletion ui/src/controller/selection_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export class SelectionController extends Controller<'main'> {
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);
Expand Down
185 changes: 93 additions & 92 deletions ui/src/controller/trace_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@ import {
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';
Expand Down Expand Up @@ -450,13 +451,8 @@ export class TraceController extends Controller<States> {
// 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;
Expand Down Expand Up @@ -485,12 +481,11 @@ export class TraceController extends Controller<States> {
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,
);
Expand Down Expand Up @@ -530,7 +525,9 @@ export class TraceController extends Controller<States> {
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
Expand All @@ -544,82 +541,12 @@ export class TraceController extends Controller<States> {
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;
Expand Down Expand Up @@ -663,7 +590,7 @@ export class TraceController extends Controller<States> {
return engineMode;
}

private async selectPerfSample() {
private async selectPerfSample(traceTime: {start: time; end: time}) {
const query = `select upid
from perf_sample
join thread using (utid)
Expand All @@ -673,8 +600,8 @@ export class TraceController extends Controller<States> {
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,
Expand Down Expand Up @@ -1217,3 +1144,77 @@ async function computeVisibleTime(
}
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,
};
}
Loading

0 comments on commit 435bdae

Please sign in to comment.