diff --git a/front_end/models/trace/extras/StackTraceForEvent.ts b/front_end/models/trace/extras/StackTraceForEvent.ts index 3f202497edc..f1b30462b60 100644 --- a/front_end/models/trace/extras/StackTraceForEvent.ts +++ b/front_end/models/trace/extras/StackTraceForEvent.ts @@ -50,6 +50,46 @@ export function get(event: Types.Events.Event, parsedTrace: Handlers.Types.Parse return result; } +export function getForReactNative(event: Types.Events.Event, parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace | +null { + let cacheForTrace = stackTraceForEventInTrace.get(parsedTrace); + if (!cacheForTrace) { + cacheForTrace = new Map(); + stackTraceForEventInTrace.set(parsedTrace, cacheForTrace); + } + const resultFromCache = cacheForTrace.get(event); + if (resultFromCache) { + return resultFromCache; + } + let result: Protocol.Runtime.StackTrace|null = null; + + if (Types.Events.isUserTiming(event) && Types.Events.isPerformanceMeasureBegin(event)) { + result = extractStackTraceForReactNative(event); + } else if (Types.Extensions.isSyntheticExtensionEntry(event)) { + result = extractStackTraceForReactNative(event.rawSourceEvent); + } else if (Types.Events.isConsoleTimeStamp(event)) { + result = extractStackTraceForReactNative(event); + } + + if (result) { + cacheForTrace.set(event, result); + } + return result; +} + +export function extractStackTraceForReactNative(event: Types.Events.Event): Protocol.Runtime.StackTrace | null { + const data = event.args?.data; + if (!data) { + return null; + } + + if (!data.rnStackTrace) { + return null; + } + + return data.rnStackTrace; +} + function getForProfileCall( event: Types.Events.SyntheticProfileCall, parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace { // When working with a CPU profile the renderer handler won't have diff --git a/front_end/models/trace/types/TraceEvents.ts b/front_end/models/trace/types/TraceEvents.ts index 63bb482fd7e..e9f38c83a98 100644 --- a/front_end/models/trace/types/TraceEvents.ts +++ b/front_end/models/trace/types/TraceEvents.ts @@ -93,6 +93,8 @@ export interface Args { export interface ArgsData { stackTrace?: CallFrame[]; + // [RN] Custom field for linking stack traces to User Timings and TimeStamp events. + rnStackTrace?: Protocol.Runtime.StackTrace; sampleTraceId?: number; url?: string; navigationId?: string; diff --git a/front_end/panels/timeline/TimelineUIUtils.ts b/front_end/panels/timeline/TimelineUIUtils.ts index 03c40f9e5cb..43165965c3b 100644 --- a/front_end/panels/timeline/TimelineUIUtils.ts +++ b/front_end/panels/timeline/TimelineUIUtils.ts @@ -1838,22 +1838,32 @@ export class TimelineUIUtils { const {startTime} = Trace.Helpers.Timing.eventTimingsMilliSeconds(event); let initiatorStackLabel = i18nString(UIStrings.initiatorStackTrace); let stackLabel = i18nString(UIStrings.stackTrace); - const stackTraceForEvent = Trace.Extras.StackTraceForEvent.get(event, parsedTrace); + // [RN] Used to scope down available features for React Native targets // See https://docs.google.com/document/d/1_mtLIHEd9bFQN4xWBSVDR357GaRo56khB1aOxgWDeu4/edit?tab=t.0 for context. const isReactNative = Root.Runtime.experiments.isEnabled( Root.Runtime.ExperimentName.REACT_NATIVE_SPECIFIC_UI, ); - if (stackTraceForEvent && !isReactNative) { - contentHelper.addSection(i18nString(UIStrings.stackTrace)); - contentHelper.createChildStackTraceElement(stackTraceForEvent); - // TODO(andoli): also build stack trace component for other events - // that have a stack trace using the StackTraceForEvent helper. - } else if (!isReactNative) { - const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceForEvent(event); - if (stackTrace?.length) { - contentHelper.addSection(stackLabel); - contentHelper.createChildStackTraceElement(TimelineUIUtils.stackTraceFromCallFrames(stackTrace)); + if (isReactNative) { + const rnStackTrace = Trace.Extras.StackTraceForEvent.getForReactNative(event, parsedTrace); + + if (rnStackTrace) { + contentHelper.addSection(i18nString(UIStrings.stackTrace)); + contentHelper.createChildStackTraceElement(rnStackTrace); + } + } else { + const stackTraceForEvent = Trace.Extras.StackTraceForEvent.get(event, parsedTrace); + if (stackTraceForEvent) { + contentHelper.addSection(i18nString(UIStrings.stackTrace)); + contentHelper.createChildStackTraceElement(stackTraceForEvent); + // TODO(andoli): also build stack trace component for other events + // that have a stack trace using the StackTraceForEvent helper. + } else { + const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceForEvent(event); + if (stackTrace?.length) { + contentHelper.addSection(stackLabel); + contentHelper.createChildStackTraceElement(TimelineUIUtils.stackTraceFromCallFrames(stackTrace)); + } } } switch (event.name) {