From 8f99f80a3bb67abe62801f563323499d6af7558c Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Wed, 8 Apr 2026 17:18:17 -0700 Subject: [PATCH 1/2] feat(profiling): New stack trace in span profile details + fix maxDepth `StackTraceProvider` was applying `maxDepth` to both `allRows` (the full frame list used as the render loop) and `rows` (the filtered visible frames). Since `allRows` includes all system frames and `rows` only includes in-app frames, `maxDepth` would slice different ranges and the intersection could lose in-app frames entirely. Fix: only apply `maxDepth` to `rows`. `allRows` drives the render loop but frames outside `rows` are either null or Activity-hidden, so visible count is still capped. Also uses the new stack trace in the "Most Frequent Stacks in this Span" section behind `issue-details-new-stack-trace`. Co-Authored-By: Claude Opus 4.6 --- .../interfaces/spans/spanProfileDetails.tsx | 49 +++++++++++++------ .../components/stackTrace/getRows.spec.tsx | 3 -- static/app/components/stackTrace/getRows.tsx | 2 +- .../components/stackTrace/stackTrace.spec.tsx | 40 +++++++++++++++ .../stackTrace/stackTraceProvider.tsx | 3 +- 5 files changed, 77 insertions(+), 20 deletions(-) diff --git a/static/app/components/events/interfaces/spans/spanProfileDetails.tsx b/static/app/components/events/interfaces/spans/spanProfileDetails.tsx index 96b1ddf158d0c0..c407859d196579 100644 --- a/static/app/components/events/interfaces/spans/spanProfileDetails.tsx +++ b/static/app/components/events/interfaces/spans/spanProfileDetails.tsx @@ -7,6 +7,10 @@ import {SectionHeading} from 'sentry/components/charts/styles'; import {StackTraceContent} from 'sentry/components/events/interfaces/crashContent/stackTrace'; import {StackTraceContentPanel} from 'sentry/components/events/interfaces/crashContent/stackTrace/content'; import {QuestionTooltip} from 'sentry/components/questionTooltip'; +import {FrameContent} from 'sentry/components/stackTrace/frame/frameContent'; +import {StackTraceViewStateProvider} from 'sentry/components/stackTrace/stackTraceContext'; +import {StackTraceFrames} from 'sentry/components/stackTrace/stackTraceFrames'; +import {StackTraceProvider} from 'sentry/components/stackTrace/stackTraceProvider'; import {IconChevron, IconProfiling} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {EntryType, type EventTransaction, type Frame} from 'sentry/types/event'; @@ -298,20 +302,37 @@ export function SpanProfileDetails({ - + {organization.features.includes('issue-details-new-stack-trace') ? ( + + + + + + ) : ( + + )} ); } diff --git a/static/app/components/stackTrace/getRows.spec.tsx b/static/app/components/stackTrace/getRows.spec.tsx index 48efd0f1b502fa..6b624ba1412d73 100644 --- a/static/app/components/stackTrace/getRows.spec.tsx +++ b/static/app/components/stackTrace/getRows.spec.tsx @@ -78,7 +78,6 @@ describe('stackTrace rows utils', () => { frameCountMap, newestFirst: false, framesOmitted: null, - maxDepth: undefined, }); expect(rows).toHaveLength(4); @@ -108,7 +107,6 @@ describe('stackTrace rows utils', () => { frameCountMap: getFrameCountMap(frames, true), newestFirst: false, framesOmitted: null, - maxDepth: undefined, }); expect(rows).toHaveLength(2); @@ -130,7 +128,6 @@ describe('stackTrace rows utils', () => { frameCountMap: getFrameCountMap(frames, true), newestFirst: false, framesOmitted: [1, 3], - maxDepth: undefined, }); expect(rowsWithOmitted.some(row => row.kind === 'omitted')).toBe(true); diff --git a/static/app/components/stackTrace/getRows.tsx b/static/app/components/stackTrace/getRows.tsx index f6a2ce5b9e4f06..b5c701cd8a8528 100644 --- a/static/app/components/stackTrace/getRows.tsx +++ b/static/app/components/stackTrace/getRows.tsx @@ -116,8 +116,8 @@ export function getRows({ framesOmitted: [number, number] | null | undefined; hiddenFrameToggleMap: Record; includeSystemFrames: boolean; - maxDepth: number | undefined; newestFirst: boolean; + maxDepth?: number; }): Row[] { const hiddenFrameIndices = getHiddenFrameIndices({ frames, diff --git a/static/app/components/stackTrace/stackTrace.spec.tsx b/static/app/components/stackTrace/stackTrace.spec.tsx index fb9270a0677c24..62df2e5f9e082a 100644 --- a/static/app/components/stackTrace/stackTrace.spec.tsx +++ b/static/app/components/stackTrace/stackTrace.spec.tsx @@ -812,6 +812,46 @@ describe('Core StackTrace', () => { expect(screen.getByText('abc123')).toBeInTheDocument(); }); + it('shows in-app frames with maxDepth even when system frames outnumber them', async () => { + const {event, stacktrace} = makeStackTraceData(); + const frame = stacktrace.frames[stacktrace.frames.length - 1]!; + + // 2 in-app frames followed by 10 system frames — the in-app frames are + // near the start, so a naive maxDepth slice on all frames would miss them. + const appFrames = Array.from({length: 2}, (_, i) => ({ + ...frame, + inApp: true, + function: `app_fn_${i}`, + lineNo: i + 1, + instructionAddr: `0xA${i}`, + })); + const systemFrames = Array.from({length: 10}, (_, i) => ({ + ...frame, + inApp: false, + function: `system_fn_${i}`, + lineNo: i + 100, + instructionAddr: `0xS${i}`, + })); + + render( + + + + ); + + const rows = await screen.findAllByTestId('core-stacktrace-frame-row'); + expect(rows.length).toBeGreaterThanOrEqual(2); + expect(screen.getByText('app_fn_0')).toBeInTheDocument(); + expect(screen.getByText('app_fn_1')).toBeInTheDocument(); + }); + it('renders empty source notation for single frame with no details', async () => { const {event, stacktrace} = makeStackTraceData(); const frame = stacktrace.frames[stacktrace.frames.length - 1]!; diff --git a/static/app/components/stackTrace/stackTraceProvider.tsx b/static/app/components/stackTrace/stackTraceProvider.tsx index 3fd3775a5d882f..256c8d1d8038c1 100644 --- a/static/app/components/stackTrace/stackTraceProvider.tsx +++ b/static/app/components/stackTrace/stackTraceProvider.tsx @@ -66,9 +66,8 @@ export function StackTraceProvider({ frameCountMap: {}, newestFirst: isNewestFirst, framesOmitted: activeStacktrace.framesOmitted, - maxDepth, }), - [frames, isNewestFirst, activeStacktrace.framesOmitted, maxDepth] + [frames, isNewestFirst, activeStacktrace.framesOmitted] ); const rows = useMemo( From abf79c4d24213276e88f4064d8665349fe82afb6 Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Wed, 8 Apr 2026 18:01:54 -0700 Subject: [PATCH 2/2] fix(profiling): Add frame actions to new stack trace in span profiles Co-Authored-By: Claude Opus 4.6 --- .../events/interfaces/spans/spanProfileDetails.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/static/app/components/events/interfaces/spans/spanProfileDetails.tsx b/static/app/components/events/interfaces/spans/spanProfileDetails.tsx index c407859d196579..9e4bb6b6305064 100644 --- a/static/app/components/events/interfaces/spans/spanProfileDetails.tsx +++ b/static/app/components/events/interfaces/spans/spanProfileDetails.tsx @@ -8,6 +8,7 @@ import {StackTraceContent} from 'sentry/components/events/interfaces/crashConten import {StackTraceContentPanel} from 'sentry/components/events/interfaces/crashContent/stackTrace/content'; import {QuestionTooltip} from 'sentry/components/questionTooltip'; import {FrameContent} from 'sentry/components/stackTrace/frame/frameContent'; +import {IssueFrameActions} from 'sentry/components/stackTrace/issueStackTrace/issueFrameActions'; import {StackTraceViewStateProvider} from 'sentry/components/stackTrace/stackTraceContext'; import {StackTraceFrames} from 'sentry/components/stackTrace/stackTraceFrames'; import {StackTraceProvider} from 'sentry/components/stackTrace/stackTraceProvider'; @@ -314,7 +315,11 @@ export function SpanProfileDetails({ }} maxDepth={MAX_STACK_DEPTH} > - + ) : (