Skip to content

Commit 6654070

Browse files
scttcperclaude
andauthored
feat(profiling): New stack trace in span profile details (#112559)
Uses the new stack trace component in the "Most Frequent Stacks in this Span" section, gated behind `issue-details-new-stack-trace`. Also fixes a bug where `maxDepth` was applied to both `allRows` and `rows` in `StackTraceProvider`. Since `allRows` includes all system frames and `rows` only includes visible (in-app) frames, slicing both independently could lose in-app frames when system frames dominate the end of the stack. before <img width="605" height="501" alt="Screenshot 2026-04-08 at 5 14 48 PM" src="https://github.com/user-attachments/assets/6b352cb6-ade7-42fb-8677-8e991987c1d8" /> after <img width="553" height="440" alt="image" src="https://github.com/user-attachments/assets/31fb33a3-bf0d-4564-b042-d1685334f5da" /> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dac98fc commit 6654070

5 files changed

Lines changed: 82 additions & 20 deletions

File tree

static/app/components/events/interfaces/spans/spanProfileDetails.tsx

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import {SectionHeading} from 'sentry/components/charts/styles';
77
import {StackTraceContent} from 'sentry/components/events/interfaces/crashContent/stackTrace';
88
import {StackTraceContentPanel} from 'sentry/components/events/interfaces/crashContent/stackTrace/content';
99
import {QuestionTooltip} from 'sentry/components/questionTooltip';
10+
import {FrameContent} from 'sentry/components/stackTrace/frame/frameContent';
11+
import {IssueFrameActions} from 'sentry/components/stackTrace/issueStackTrace/issueFrameActions';
12+
import {StackTraceViewStateProvider} from 'sentry/components/stackTrace/stackTraceContext';
13+
import {StackTraceFrames} from 'sentry/components/stackTrace/stackTraceFrames';
14+
import {StackTraceProvider} from 'sentry/components/stackTrace/stackTraceProvider';
1015
import {IconChevron, IconProfiling} from 'sentry/icons';
1116
import {t, tct} from 'sentry/locale';
1217
import {EntryType, type EventTransaction, type Frame} from 'sentry/types/event';
@@ -298,20 +303,41 @@ export function SpanProfileDetails({
298303
</LinkButton>
299304
</SpanDetailsItem>
300305
</SpanDetails>
301-
<StackTraceContent
302-
event={processedEvent}
303-
newestFirst
304-
platform={event.platform || 'other'}
305-
stacktrace={{
306-
framesOmitted: null,
307-
hasSystemFrames: false,
308-
registers: null,
309-
frames,
310-
}}
311-
stackView={StackView.APP}
312-
inlined
313-
maxDepth={MAX_STACK_DEPTH}
314-
/>
306+
{organization.features.includes('issue-details-new-stack-trace') ? (
307+
<StackTraceViewStateProvider platform={event.platform || 'other'}>
308+
<StackTraceProvider
309+
event={processedEvent}
310+
stacktrace={{
311+
framesOmitted: null,
312+
hasSystemFrames: false,
313+
registers: null,
314+
frames,
315+
}}
316+
maxDepth={MAX_STACK_DEPTH}
317+
>
318+
<StackTraceFrames
319+
borderless
320+
frameActionsComponent={IssueFrameActions}
321+
frameContextComponent={FrameContent}
322+
/>
323+
</StackTraceProvider>
324+
</StackTraceViewStateProvider>
325+
) : (
326+
<StackTraceContent
327+
event={processedEvent}
328+
newestFirst
329+
platform={event.platform || 'other'}
330+
stacktrace={{
331+
framesOmitted: null,
332+
hasSystemFrames: false,
333+
registers: null,
334+
frames,
335+
}}
336+
stackView={StackView.APP}
337+
inlined
338+
maxDepth={MAX_STACK_DEPTH}
339+
/>
340+
)}
315341
</SpanContainer>
316342
);
317343
}

static/app/components/stackTrace/getRows.spec.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ describe('stackTrace rows utils', () => {
7878
frameCountMap,
7979
newestFirst: false,
8080
framesOmitted: null,
81-
maxDepth: undefined,
8281
});
8382

8483
expect(rows).toHaveLength(4);
@@ -108,7 +107,6 @@ describe('stackTrace rows utils', () => {
108107
frameCountMap: getFrameCountMap(frames, true),
109108
newestFirst: false,
110109
framesOmitted: null,
111-
maxDepth: undefined,
112110
});
113111

114112
expect(rows).toHaveLength(2);
@@ -130,7 +128,6 @@ describe('stackTrace rows utils', () => {
130128
frameCountMap: getFrameCountMap(frames, true),
131129
newestFirst: false,
132130
framesOmitted: [1, 3],
133-
maxDepth: undefined,
134131
});
135132

136133
expect(rowsWithOmitted.some(row => row.kind === 'omitted')).toBe(true);

static/app/components/stackTrace/getRows.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ export function getRows({
116116
framesOmitted: [number, number] | null | undefined;
117117
hiddenFrameToggleMap: Record<number, boolean>;
118118
includeSystemFrames: boolean;
119-
maxDepth: number | undefined;
120119
newestFirst: boolean;
120+
maxDepth?: number;
121121
}): Row[] {
122122
const hiddenFrameIndices = getHiddenFrameIndices({
123123
frames,

static/app/components/stackTrace/stackTrace.spec.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,46 @@ describe('Core StackTrace', () => {
812812
expect(screen.getByText('abc123')).toBeInTheDocument();
813813
});
814814

815+
it('shows in-app frames with maxDepth even when system frames outnumber them', async () => {
816+
const {event, stacktrace} = makeStackTraceData();
817+
const frame = stacktrace.frames[stacktrace.frames.length - 1]!;
818+
819+
// 2 in-app frames followed by 10 system frames — the in-app frames are
820+
// near the start, so a naive maxDepth slice on all frames would miss them.
821+
const appFrames = Array.from({length: 2}, (_, i) => ({
822+
...frame,
823+
inApp: true,
824+
function: `app_fn_${i}`,
825+
lineNo: i + 1,
826+
instructionAddr: `0xA${i}`,
827+
}));
828+
const systemFrames = Array.from({length: 10}, (_, i) => ({
829+
...frame,
830+
inApp: false,
831+
function: `system_fn_${i}`,
832+
lineNo: i + 100,
833+
instructionAddr: `0xS${i}`,
834+
}));
835+
836+
render(
837+
<TestStackTraceProvider
838+
event={event}
839+
stacktrace={{
840+
...stacktrace,
841+
frames: [...appFrames, ...systemFrames],
842+
}}
843+
maxDepth={4}
844+
>
845+
<StackTraceFrames frameContextComponent={FrameContent} />
846+
</TestStackTraceProvider>
847+
);
848+
849+
const rows = await screen.findAllByTestId('core-stacktrace-frame-row');
850+
expect(rows.length).toBeGreaterThanOrEqual(2);
851+
expect(screen.getByText('app_fn_0')).toBeInTheDocument();
852+
expect(screen.getByText('app_fn_1')).toBeInTheDocument();
853+
});
854+
815855
it('renders empty source notation for single frame with no details', async () => {
816856
const {event, stacktrace} = makeStackTraceData();
817857
const frame = stacktrace.frames[stacktrace.frames.length - 1]!;

static/app/components/stackTrace/stackTraceProvider.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,8 @@ export function StackTraceProvider({
6666
frameCountMap: {},
6767
newestFirst: isNewestFirst,
6868
framesOmitted: activeStacktrace.framesOmitted,
69-
maxDepth,
7069
}),
71-
[frames, isNewestFirst, activeStacktrace.framesOmitted, maxDepth]
70+
[frames, isNewestFirst, activeStacktrace.framesOmitted]
7271
);
7372

7473
const rows = useMemo(

0 commit comments

Comments
 (0)