fix(trace viewer): synchronize monotonic times between context entries (#29908)

When displaying remote context trace and local test runner trace
together, we should not compare monotonic time between the two.

This change reuses existing logic for merging actions timing to also
update context boundaries and events by the same time delta.

Fixes #29767.
This commit is contained in:
Dmitry Gozman 2024-03-12 16:32:58 -07:00 committed by GitHub
parent 166d2d4fde
commit 2ce421b27a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -89,11 +89,12 @@ export class MultiTraceModel {
this.platform = primaryContext?.platform || '';
this.title = primaryContext?.title || '';
this.options = primaryContext?.options || {};
// Next call updates all timestamps for all events in non-primary contexts, so it must be done first.
this.actions = mergeActionsAndUpdateTiming(contexts);
this.pages = ([] as PageEntry[]).concat(...contexts.map(c => c.pages));
this.wallTime = contexts.map(c => c.wallTime).reduce((prev, cur) => Math.min(prev || Number.MAX_VALUE, cur!), Number.MAX_VALUE);
this.startTime = contexts.map(c => c.startTime).reduce((prev, cur) => Math.min(prev, cur), Number.MAX_VALUE);
this.endTime = contexts.map(c => c.endTime).reduce((prev, cur) => Math.max(prev, cur), Number.MIN_VALUE);
this.pages = ([] as PageEntry[]).concat(...contexts.map(c => c.pages));
this.actions = mergeActions(contexts);
this.events = ([] as (trace.EventTraceEvent | trace.ConsoleMessageTraceEvent)[]).concat(...contexts.map(c => c.events));
this.stdio = ([] as trace.StdioTraceEvent[]).concat(...contexts.map(c => c.stdio));
this.errors = ([] as trace.ErrorTraceEvent[]).concat(...contexts.map(c => c.errors));
@ -158,7 +159,7 @@ function indexModel(context: ContextEntry) {
(event as any)[contextSymbol] = context;
}
function mergeActions(contexts: ContextEntry[]) {
function mergeActionsAndUpdateTiming(contexts: ContextEntry[]) {
const map = new Map<string, ActionTraceEventInContext>();
// Protocol call aka isPrimary contexts have startTime/endTime as server-side times.
@ -176,12 +177,16 @@ function mergeActions(contexts: ContextEntry[]) {
}
const nonPrimaryIdToPrimaryId = new Map<string, string>();
const nonPrimaryTimeDelta = new Map<ContextEntry, number>();
for (const context of nonPrimaryContexts) {
for (const action of context.actions) {
if (offset) {
const duration = action.endTime - action.startTime;
if (action.startTime)
action.startTime = action.wallTime + offset;
if (action.startTime) {
const newStartTime = action.wallTime + offset;
nonPrimaryTimeDelta.set(context, newStartTime - action.startTime);
action.startTime = newStartTime;
}
if (action.endTime)
action.endTime = action.startTime + duration;
}
@ -204,6 +209,17 @@ function mergeActions(contexts: ContextEntry[]) {
}
}
for (const [context, timeDelta] of nonPrimaryTimeDelta) {
context.startTime += timeDelta;
context.endTime += timeDelta;
for (const event of context.events)
event.time += timeDelta;
for (const page of context.pages) {
for (const frame of page.screencastFrames)
frame.timestamp += timeDelta;
}
}
const result = [...map.values()];
result.sort((a1, a2) => {
if (a2.parentId === a1.callId)