Skip to content

Commit

Permalink
fix(profiling): Link profiler id in context to flamegraph
Browse files Browse the repository at this point in the history
Continuous profiles use the profiler id in the profile context and we should try
to link that to the flamegraph wherever possible as well.
  • Loading branch information
Zylphrex committed Jan 3, 2025
1 parent b6c9bd8 commit 7ee9ded
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {EventFixture} from 'sentry-fixture/event';
import {TransactionEventFixture} from 'sentry-fixture/event';
import {OrganizationFixture} from 'sentry-fixture/organization';
import {ProjectFixture} from 'sentry-fixture/project';

Expand All @@ -8,9 +8,11 @@ import ContextCard from 'sentry/components/events/contexts/contextCard';
import {getProfileContextData} from 'sentry/components/events/contexts/knownContext/profile';

const PROFILE_ID = '61d2d7c5acf448ffa8e2f8f973e2cd36';
const PROFILER_ID = '5e7250f0d25443daa3f59c20835aed31';
const MOCK_PROFILE_CONTEXT = {
type: 'default',
profile_id: PROFILE_ID,
profiler_id: PROFILER_ID,
// Extra data is still valid and preserved
extra_data: 'something',
unknown_key: 123,
Expand All @@ -28,10 +30,16 @@ const MOCK_REDACTION = {
describe('ProfileContext', function () {
const organization = OrganizationFixture();
const project = ProjectFixture();

it('returns values and according to the parameters', function () {
const event = TransactionEventFixture({
_meta: {contexts: {profile: MOCK_REDACTION}},
});

expect(
getProfileContextData({
data: MOCK_PROFILE_CONTEXT,
event,
organization,
project,
})
Expand All @@ -44,6 +52,22 @@ describe('ProfileContext', function () {
link: `/organizations/${organization.slug}/profiling/profile/${project.slug}/${PROFILE_ID}/flamegraph/`,
},
},
{
key: 'profiler_id',
subject: 'Profiler ID',
value: PROFILER_ID,
action: {
link: {
pathname: `/organizations/${organization.slug}/profiling/profile/${project.slug}/flamegraph/`,
query: {
profilerId: PROFILER_ID,
eventId: event.id,
start: '2023-12-28T15:58:21.000Z',
end: '2023-12-28T15:58:20.000Z',
},
},
},
},
{
key: 'extra_data',
subject: 'extra_data',
Expand All @@ -60,7 +84,7 @@ describe('ProfileContext', function () {
});

it('renders with meta annotations correctly', function () {
const event = EventFixture({
const event = TransactionEventFixture({
_meta: {contexts: {profile: MOCK_REDACTION}},
});

Expand Down
106 changes: 87 additions & 19 deletions static/app/components/events/contexts/knownContext/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import {getContextKeys} from 'sentry/components/events/contexts/utils';
import {t} from 'sentry/locale';
import {type ProfileContext, ProfileContextKey} from 'sentry/types/event';
import type {Event, EventTransaction, ProfileContext} from 'sentry/types/event';
import {EventOrGroupType, ProfileContextKey} from 'sentry/types/event';
import type {KeyValueListData} from 'sentry/types/group';
import type {Organization} from 'sentry/types/organization';
import type {Project} from 'sentry/types/project';
import {defined} from 'sentry/utils';
import {generateProfileFlamechartRoute} from 'sentry/utils/profiling/routes';
import {getDateFromTimestamp} from 'sentry/utils/dates';
import {
generateContinuousProfileFlamechartRouteWithQuery,
generateProfileFlamechartRoute,
} from 'sentry/utils/profiling/routes';

export function getProfileContextData({
data,
event,
organization,
project,
meta,
}: {
data: ProfileContext;
event: Event;
organization: Organization;
meta?: Record<keyof ProfileContext, any>;
project?: Project;
Expand All @@ -22,23 +29,9 @@ export function getProfileContextData({
.map(ctxKey => {
switch (ctxKey) {
case ProfileContextKey.PROFILE_ID:
const profileId = data.profile_id || '';
if (!profileId) {
return undefined;
}
const link = project?.slug
? generateProfileFlamechartRoute({
orgSlug: organization.slug,
projectSlug: project?.slug,
profileId,
})
: undefined;
return {
key: ctxKey,
subject: t('Profile ID'),
value: data.profile_id,
action: {link},
};
return getProfileIdEntry(data, organization, project);
case ProfileContextKey.PROFILER_ID:
return getProfilerIdEntry(data, event, organization, project);
default:
return {
key: ctxKey,
Expand All @@ -50,3 +43,78 @@ export function getProfileContextData({
})
.filter(defined);
}

function getProfileIdEntry(
data: ProfileContext,
organization: Organization,
project?: Project
) {
const profileId = data.profile_id || '';
if (!profileId) {
return undefined;
}
const link = project?.slug
? generateProfileFlamechartRoute({
orgSlug: organization.slug,
projectSlug: project.slug,
profileId,
})
: undefined;
return {
key: ProfileContextKey.PROFILE_ID,
subject: t('Profile ID'),
value: data.profile_id,
action: {link},
};
}

function getProfilerIdEntry(
data: ProfileContext,
event: Event,
organization: Organization,
project?: Project
) {
const profilerId = data.profiler_id || '';
if (!profilerId) {
return undefined;
}
const [start, end] = getStartEnd(event);
const link =
project?.slug && start && end
? generateContinuousProfileFlamechartRouteWithQuery({
orgSlug: organization.slug,
projectSlug: project.slug,
profilerId,
start,
end,
query: {
eventId: event.id,
},
})
: undefined;
return {
key: ProfileContextKey.PROFILER_ID,
subject: t('Profiler ID'),
value: data.profiler_id,
action: {link},
};
}

function getStartEnd(event): [string | null, string | null] {
if (!isTransaction(event)) {
return [null, null];
}

const start = getDateFromTimestamp(event.startTimestamp * 1000);
const end = getDateFromTimestamp(event.endTimestamp * 1000);

if (!start || !end) {
return [null, null];
}

return [start.toISOString(), end.toISOString()];
}

function isTransaction(event: Event): event is EventTransaction {
return event.type === EventOrGroupType.TRANSACTION;
}
8 changes: 7 additions & 1 deletion static/app/components/events/contexts/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,13 @@ export function getFormattedContextData({
case 'state':
return getStateContextData({data: contextValue, meta});
case 'profile':
return getProfileContextData({data: contextValue, meta, organization, project});
return getProfileContextData({
data: contextValue,
event,
meta,
organization,
project,
});
case 'replay':
return getReplayContextData({data: contextValue, meta});
case 'cloud_resource':
Expand Down

0 comments on commit 7ee9ded

Please sign in to comment.