Skip to content

Commit 60264a4

Browse files
authored
fix(profiling): Link event id for profiles in trace view (#103921)
This ensures that we link with the appropriate event id in the trace view and do not show the transaction in the flamechart view if there's no transaction present.
1 parent 0c0fd1d commit 60264a4

File tree

5 files changed

+38
-24
lines changed

5 files changed

+38
-24
lines changed

static/app/components/profiling/flamegraph/continuousFlamegraph.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,11 @@ export function ContinuousFlamegraph(): ReactElement {
334334
return profileGroup.profiles.find(p => p.threadId === flamegraphProfiles.threadId);
335335
}, [profileGroup, flamegraphProfiles.threadId]);
336336

337-
const spanTree: SpanTree = useMemo(() => {
337+
const spanTree: SpanTree | null = useMemo(() => {
338+
if (segment.type === 'empty') {
339+
return null;
340+
}
341+
338342
if (segment.type === 'resolved' && segment.data) {
339343
return new SpanTree(
340344
segment.data,
@@ -346,7 +350,7 @@ export function ContinuousFlamegraph(): ReactElement {
346350
}, [segment]);
347351

348352
const spanChart = useMemo(() => {
349-
if (!profile) {
353+
if (!profile || !spanTree) {
350354
return null;
351355
}
352356

static/app/types/core.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ export type PageFilters = {
189189
projects: number[];
190190
};
191191

192+
type EmptyState = {type: 'empty'};
193+
192194
type InitialState = {type: 'initial'};
193195

194196
type LoadingState = {type: 'loading'};
@@ -204,6 +206,7 @@ type ErroredState = {
204206
};
205207

206208
export type RequestState<T> =
209+
| EmptyState
207210
| InitialState
208211
| LoadingState
209212
| ResolvedState<T>

static/app/utils/profiling/hooks/useSentryEvent.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@ function fetchSentryEvent<T extends Event>(
1919
export function useSentryEvent<T extends Event>(
2020
organizationSlug: string,
2121
projectSlug: string,
22-
eventId: string | null
22+
eventId: string | null,
23+
disabled?: boolean
2324
): RequestState<T> {
2425
const api = useApi();
2526
const [requestState, setRequestState] = useState<RequestState<T>>({
2627
type: 'initial',
2728
});
2829

2930
useLayoutEffect(() => {
30-
if (eventId === null || !projectSlug || !organizationSlug) {
31+
if (disabled || !eventId || !projectSlug || !organizationSlug) {
3132
return undefined;
3233
}
3334

@@ -47,7 +48,7 @@ export function useSentryEvent<T extends Event>(
4748
return () => {
4849
api.clear();
4950
};
50-
}, [api, organizationSlug, projectSlug, eventId]);
51+
}, [api, organizationSlug, projectSlug, eventId, disabled]);
5152

52-
return requestState;
53+
return disabled ? {type: 'empty'} : requestState;
5354
}

static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceProfiles.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
isTransactionNode,
2121
} from 'sentry/views/performance/newTraceDetails/traceGuards';
2222
import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree';
23+
import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode';
2324

2425
export function TraceProfiles({tree}: {tree: TraceTree}) {
2526
const {projects} = useProjects();
@@ -65,22 +66,7 @@ export function TraceProfiles({tree}: {tree: TraceTree}) {
6566
return null;
6667
}
6768

68-
const threadId = isEAPSpanNode(node)
69-
? (node.value.additional_attributes?.['thread.id'] ?? undefined)
70-
: undefined;
71-
const tid = typeof threadId === 'string' ? threadId : undefined;
72-
73-
const query = isTransactionNode(node)
74-
? {
75-
eventId: node.value.event_id,
76-
tid,
77-
}
78-
: isSpanNode(node)
79-
? {
80-
eventId: TraceTree.ParentTransaction(node)?.value?.event_id,
81-
tid,
82-
}
83-
: {tid};
69+
const query = getProfileRouteQueryFromNode(node);
8470

8571
const link =
8672
'profiler_id' in profile
@@ -158,6 +144,23 @@ export function TraceProfiles({tree}: {tree: TraceTree}) {
158144
);
159145
}
160146

147+
function getProfileRouteQueryFromNode(node: TraceTreeNode<TraceTree.NodeValue>) {
148+
if (isTransactionNode(node)) {
149+
return {eventId: node.value.event_id};
150+
}
151+
if (isSpanNode(node)) {
152+
return {eventId: TraceTree.ParentTransaction(node)?.value?.event_id};
153+
}
154+
if (isEAPSpanNode(node)) {
155+
const threadId = node.value.additional_attributes?.['thread.id'] ?? undefined;
156+
return {
157+
eventId: node.value.transaction_id,
158+
tid: typeof threadId === 'string' ? threadId : undefined,
159+
};
160+
}
161+
return {};
162+
}
163+
161164
const ProfilesTable = styled('div')`
162165
display: grid !important;
163166
grid-template-columns: 1fr min-content;

static/app/views/profiling/continuousProfileProvider.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ export default function ProfileAndTransactionProvider(): React.ReactElement {
4343
};
4444
}, [location.query.start, location.query.end, location.query.profilerId]);
4545

46+
const eventId = decodeScalar(location.query.eventId) || null;
47+
4648
const [profile, setProfile] = useState<RequestState<Profiling.ProfileInput>>({
47-
type: 'initial',
49+
type: eventId ? 'initial' : 'empty',
4850
});
4951

5052
const profileTransaction = useSentryEvent<EventTransaction>(
5153
organization.slug,
5254
projectSlug,
53-
decodeScalar(location.query.eventId) || null
55+
eventId,
56+
!eventId // disable if no event id
5457
);
5558

5659
return (

0 commit comments

Comments
 (0)