Skip to content

Commit

Permalink
Merge "ui: Move traceTime out of state" into main
Browse files Browse the repository at this point in the history
  • Loading branch information
stevegolton authored and Gerrit Code Review committed May 14, 2024
2 parents 2b4e930 + 435bdae commit 2a0df8b
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 2a0df8b

Please sign in to comment.