Skip to content

Commit 887e28e

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Speed up updateSourceMapEntities
When tracing gemini.google.com, it takes the Performance panel ~22.0 s to execute updateSourceMapEntities (via resolveMappingsForProfileNodes). With this CL, it takes ~8.7 s. * getZeroIndexedStackTraceInEventPayload was a hot function, and it performed an expensive switch statement on strings for each call frame. That switch statement was done in a `.map`, but it is invariant to the mapped element, so I moved the switch statement up. * updateSourceMapEntities actually only needs the top frame (as do many other callers). So I added getStackTraceTopCallFrameInEventPayload to perform much less work. Bug: 444483828 Change-Id: I4bdfcc5bc9e53942a010c03822816f318ec03240 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6941582 Reviewed-by: Paul Irish <[email protected]> Commit-Queue: Connor Clark <[email protected]> Commit-Queue: Paul Irish <[email protected]> Auto-Submit: Connor Clark <[email protected]>
1 parent bb5aa18 commit 887e28e

File tree

9 files changed

+57
-27
lines changed

9 files changed

+57
-27
lines changed

front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ export class PerformanceTraceFormatter {
147147
function nodeToText(this: PerformanceTraceFormatter, node: Trace.Extras.TraceTree.Node): string {
148148
const event = node.event;
149149

150-
let frame = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event)?.[0];
150+
let frame;
151151
if (Trace.Types.Events.isProfileCall(event)) {
152152
frame = event.callFrame;
153+
} else {
154+
frame = Trace.Helpers.Trace.getStackTraceTopCallFrameInEventPayload(event);
153155
}
154156

155157
let source = TimelineUtils.EntryName.nameForEntry(event);

front_end/models/trace/handlers/NetworkRequestsHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ export async function finalize(): Promise<void> {
555555

556556
// Establish initiator relationships
557557
const initiatorUrl = networkEvent.args.data.initiator?.url ||
558-
Helpers.Trace.getZeroIndexedStackTraceInEventPayload(networkEvent)?.at(0)?.url;
558+
Helpers.Trace.getStackTraceTopCallFrameInEventPayload(networkEvent)?.url;
559559
if (initiatorUrl) {
560560
const events = networkRequestEventByInitiatorUrl.get(initiatorUrl) ?? [];
561561
events.push(networkEvent);

front_end/models/trace/helpers/Trace.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -471,23 +471,52 @@ export function getZeroIndexedStackTraceInEventPayload(event: Types.Events.Event
471471
if (!stack) {
472472
return null;
473473
}
474-
return stack.map(callFrame => {
475-
switch (event.name) {
476-
case Types.Events.Name.SCHEDULE_STYLE_RECALCULATION:
477-
case Types.Events.Name.INVALIDATE_LAYOUT:
478-
case Types.Events.Name.FUNCTION_CALL:
479-
case Types.Events.Name.LAYOUT:
480-
case Types.Events.Name.UPDATE_LAYOUT_TREE: {
481-
return makeZeroBasedCallFrame(callFrame);
474+
475+
switch (event.name) {
476+
case Types.Events.Name.SCHEDULE_STYLE_RECALCULATION:
477+
case Types.Events.Name.INVALIDATE_LAYOUT:
478+
case Types.Events.Name.FUNCTION_CALL:
479+
case Types.Events.Name.LAYOUT:
480+
case Types.Events.Name.UPDATE_LAYOUT_TREE: {
481+
return stack.map(makeZeroBasedCallFrame);
482+
}
483+
484+
default: {
485+
if (Types.Events.isUserTiming(event) || Types.Extensions.isSyntheticExtensionEntry(event)) {
486+
return stack.map(makeZeroBasedCallFrame);
482487
}
483-
default: {
484-
if (Types.Events.isUserTiming(event) || Types.Extensions.isSyntheticExtensionEntry(event)) {
485-
return makeZeroBasedCallFrame(callFrame);
486-
}
488+
489+
return stack;
490+
}
491+
}
492+
}
493+
494+
/**
495+
* Same as getZeroIndexedStackTraceInEventPayload, but only returns the top call frame.
496+
*/
497+
export function getStackTraceTopCallFrameInEventPayload(event: Types.Events.Event): Types.Events.CallFrame|null {
498+
const stack = stackTraceInEvent(event);
499+
if (!stack || stack.length === 0) {
500+
return null;
501+
}
502+
503+
switch (event.name) {
504+
case Types.Events.Name.SCHEDULE_STYLE_RECALCULATION:
505+
case Types.Events.Name.INVALIDATE_LAYOUT:
506+
case Types.Events.Name.FUNCTION_CALL:
507+
case Types.Events.Name.LAYOUT:
508+
case Types.Events.Name.UPDATE_LAYOUT_TREE: {
509+
return makeZeroBasedCallFrame(stack[0]);
510+
}
511+
512+
default: {
513+
if (Types.Events.isUserTiming(event) || Types.Extensions.isSyntheticExtensionEntry(event)) {
514+
return makeZeroBasedCallFrame(stack[0]);
487515
}
516+
517+
return stack[0];
488518
}
489-
return callFrame;
490-
});
519+
}
491520
}
492521

493522
/**

front_end/models/trace/insights/ForcedReflow.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ function finalize(partialModel: PartialInsightModel<ForcedReflowInsightModel>):
153153
function getBottomCallFrameForEvent(event: Types.Events.Event, traceParsedData: Handlers.Types.HandlerData):
154154
Types.Events.CallFrame|Protocol.Runtime.CallFrame|null {
155155
const profileStackTrace = Extras.StackTraceForEvent.get(event, traceParsedData);
156-
const eventStackTrace = Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event);
156+
const eventTopCallFrame = Helpers.Trace.getStackTraceTopCallFrameInEventPayload(event);
157157

158-
return profileStackTrace?.callFrames[0] ?? eventStackTrace?.[0] ?? null;
158+
return profileStackTrace?.callFrames[0] ?? eventTopCallFrame ?? null;
159159
}
160160

161161
export function isForcedReflowInsight(model: InsightModel): model is ForcedReflowInsightModel {

front_end/models/trace/insights/NetworkDependencyTree.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ function isCritical(request: Types.Events.SyntheticNetworkRequest, context: Insi
205205
// Requests that have no initiatorRequest are typically ambiguous late-load assets.
206206
// Even on the off chance they were important, we don't have any parent to display for them.
207207
const initiatorUrl =
208-
request.args.data.initiator?.url || Helpers.Trace.getZeroIndexedStackTraceInEventPayload(request)?.at(0)?.url;
208+
request.args.data.initiator?.url || Helpers.Trace.getStackTraceTopCallFrameInEventPayload(request)?.url;
209209
if (!initiatorUrl) {
210210
return false;
211211
}

front_end/panels/timeline/TimelineUIUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,9 +1510,9 @@ export class TimelineUIUtils {
15101510
}
15111511
}
15121512

1513-
const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event);
1513+
const hasStackTrace = Boolean(Trace.Helpers.Trace.getStackTraceTopCallFrameInEventPayload(event));
15141514
if (Trace.Types.Events.isUserTiming(event) || Trace.Types.Extensions.isSyntheticExtensionEntry(event) ||
1515-
Trace.Types.Events.isProfileCall(event) || initiator || initiatorFor || stackTrace ||
1515+
Trace.Types.Events.isProfileCall(event) || initiator || initiatorFor || hasStackTrace ||
15161516
parsedTrace?.data.Invalidations.invalidationsForEvent.get(event)) {
15171517
await TimelineUIUtils.generateCauses(event, contentHelper, parsedTrace);
15181518
}

front_end/panels/timeline/components/NetworkRequestDetails.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ function renderInitiatedBy(
445445
};
446446
// If we have a stack trace, that is the most reliable way to get the initiator data and display a link to the source.
447447
if (hasStackTrace) {
448-
const topFrame = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(request)?.at(0) ?? null;
448+
const topFrame = Trace.Helpers.Trace.getStackTraceTopCallFrameInEventPayload(request) ?? null;
449449
if (topFrame) {
450450
link = linkifier.maybeLinkifyConsoleCallFrame(target, topFrame, options);
451451
}

front_end/panels/timeline/utils/EntityMapper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ export class EntityMapper {
103103
// The events that don't match the source location, but that we should keep mapped to its current entity.
104104
const unrelatedEvents: Trace.Types.Events.Event[] = [];
105105
currentEntityEvents?.forEach(e => {
106-
const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(e);
107-
const cf = stackTrace?.at(0);
106+
const cf = Trace.Helpers.Trace.getStackTraceTopCallFrameInEventPayload(e);
108107

109108
const matchesCallFrame = cf && Trace.Helpers.Trace.isMatchingCallFrame(cf, callFrame);
110109
if (matchesCallFrame) {

front_end/panels/timeline/utils/SourceMapsResolver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ export class SourceMapsResolver extends EventTarget {
7878
if (Trace.Types.Events.isProfileCall(entry)) {
7979
callFrame = entry.callFrame;
8080
} else {
81-
const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(entry);
82-
if (stackTrace === null || stackTrace.length < 1) {
81+
const topCallFrame = Trace.Helpers.Trace.getStackTraceTopCallFrameInEventPayload(entry);
82+
if (!topCallFrame) {
8383
return null;
8484
}
85-
callFrame = stackTrace[0];
85+
callFrame = topCallFrame;
8686
}
8787
return SourceMapsResolver.resolvedCodeLocationForCallFrame(callFrame as Protocol.Runtime.CallFrame);
8888
}

0 commit comments

Comments
 (0)