Skip to content

Commit e98dd0c

Browse files
JonasBapriscilawebdev
authored andcommitted
feat(profiling) construct profile from continuous chunk (#74171)
This PR implements measurements rendering for the continuous profiling UI. The chart to render slow and frozen frames still needs to be updated to reflect this.
1 parent 0209241 commit e98dd0c

19 files changed

+285
-86
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ function ProfilingMeasurements({
211211
onMouseDown={onStartWindowSelection}
212212
>
213213
<MemoizedChart
214-
data={data}
214+
data={data as Profiling.Measurement}
215215
type={measurementType}
216216
transactionDuration={transactionDurationInMs}
217217
/>

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

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,19 @@ import {
4646
initializeFlamegraphRenderer,
4747
useResizeCanvasObserver,
4848
} from 'sentry/utils/profiling/gl/utils';
49-
import type {ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
49+
import type {ContinuousProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
5050
import {FlamegraphRenderer2D} from 'sentry/utils/profiling/renderers/flamegraphRenderer2D';
5151
import {FlamegraphRendererWebGL} from 'sentry/utils/profiling/renderers/flamegraphRendererWebGL';
5252
import {Rect} from 'sentry/utils/profiling/speedscope';
5353
import {UIFrames} from 'sentry/utils/profiling/uiFrames';
54-
import {fromNanoJoulesToWatts} from 'sentry/utils/profiling/units/units';
54+
import {
55+
fromNanoJoulesToWatts,
56+
type ProfilingFormatterUnit,
57+
} from 'sentry/utils/profiling/units/units';
5558
import {useDevicePixelRatio} from 'sentry/utils/useDevicePixelRatio';
5659
import {useMemoWithPrevious} from 'sentry/utils/useMemoWithPrevious';
5760
import {useContinuousProfile} from 'sentry/views/profiling/continuousProfileProvider';
58-
import {useProfileGroup} from 'sentry/views/profiling/profileGroupProvider';
61+
import {useContinuousProfileGroup} from 'sentry/views/profiling/profileGroupProvider';
5962

6063
import {FlamegraphDrawer} from './flamegraphDrawer/flamegraphDrawer';
6164
import {FlamegraphWarnings} from './flamegraphOverlays/FlamegraphWarnings';
@@ -64,7 +67,7 @@ import {FlamegraphChart} from './flamegraphChart';
6467
import {FlamegraphLayout} from './flamegraphLayout';
6568
import {FlamegraphUIFrames} from './flamegraphUIFrames';
6669

67-
function getMaxConfigSpace(profileGroup: ProfileGroup): Rect {
70+
function getMaxConfigSpace(profileGroup: ContinuousProfileGroup): Rect {
6871
// We have a transaction, so we should do our best to align the profile
6972
// with the transaction's timeline.
7073
const maxProfileDuration = Math.max(...profileGroup.profiles.map(p => p.duration));
@@ -123,7 +126,7 @@ export function ContinuousFlamegraph(): ReactElement {
123126
const dispatch = useDispatchFlamegraphState();
124127

125128
const profiles = useContinuousProfile();
126-
const profileGroup = useProfileGroup();
129+
const profileGroup = useContinuousProfileGroup();
127130

128131
const flamegraphTheme = useFlamegraphTheme();
129132
const position = useFlamegraphZoomPosition();
@@ -229,7 +232,11 @@ export function ContinuousFlamegraph(): ReactElement {
229232
}
230233
return new UIFrames(
231234
{
235+
// @TODO
236+
// @ts-expect-error
232237
slow: profileGroup.measurements?.slow_frame_renders,
238+
// @TODO
239+
// @ts-expect-error
233240
frozen: profileGroup.measurements?.frozen_frame_renders,
234241
},
235242
{unit: flamegraph.profile.unit},
@@ -251,25 +258,31 @@ export function ContinuousFlamegraph(): ReactElement {
251258

252259
for (const key in profileGroup.measurements) {
253260
if (key === 'cpu_energy_usage') {
254-
measures.push({
255-
...profileGroup.measurements[key]!,
256-
values: profileGroup.measurements[key]!.values.map(v => {
257-
return {
258-
elapsed_since_start_ns: v.elapsed_since_start_ns,
259-
value: fromNanoJoulesToWatts(v.value, 0.1),
260-
};
261-
}),
262-
// some versions of cocoa send byte so we need to correct it to watt
263-
unit: 'watt',
264-
name: 'CPU energy usage',
265-
});
261+
const measurements = profileGroup.measurements[key]!;
262+
const values: ProfileSeriesMeasurement['values'] = [];
263+
264+
let offset = 0;
265+
for (let i = 0; i < measurements.values.length; i++) {
266+
const value = measurements.values[i];
267+
const next = measurements.values[i + 1] ?? value;
268+
offset += (next.timestamp - value.timestamp) * 1e3;
269+
270+
values.push({
271+
value: fromNanoJoulesToWatts(value.value, 0.1),
272+
elapsed: offset,
273+
});
274+
}
275+
276+
// some versions of cocoa send byte so we need to correct it to watt
277+
measures.push({name: 'CPU energy usage', unit: 'watt', values});
266278
}
267279
}
268280

269281
return new FlamegraphChartModel(
270282
Rect.From(flamegraph.configSpace),
271283
measures.length > 0 ? measures : [],
272-
flamegraphTheme.COLORS.BATTERY_CHART_COLORS
284+
flamegraphTheme.COLORS.BATTERY_CHART_COLORS,
285+
{timelineUnit: 'milliseconds'}
273286
);
274287
}, [profileGroup.measurements, flamegraph.configSpace, flamegraphTheme, hasCPUChart]);
275288

@@ -278,22 +291,39 @@ export function ContinuousFlamegraph(): ReactElement {
278291
return LOADING_OR_FALLBACK_CPU_CHART;
279292
}
280293

281-
const measures: ProfileSeriesMeasurement[] = [];
294+
const cpuMeasurements: ProfileSeriesMeasurement[] = [];
282295

283296
for (const key in profileGroup.measurements) {
284297
if (key.startsWith('cpu_usage')) {
285298
const name =
286299
key === 'cpu_usage'
287300
? 'Average CPU usage'
288301
: `CPU Core ${key.replace('cpu_usage_', '')}`;
289-
measures.push({...profileGroup.measurements[key]!, name});
302+
303+
const measurements = profileGroup.measurements[key]!;
304+
const values: ProfileSeriesMeasurement['values'] = [];
305+
306+
let offset = 0;
307+
for (let i = 0; i < measurements.values.length; i++) {
308+
const value = measurements.values[i];
309+
const next = measurements.values[i + 1] ?? value;
310+
offset += (next.timestamp - value.timestamp) * 1e3;
311+
312+
values.push({
313+
value: value.value,
314+
elapsed: offset,
315+
});
316+
}
317+
318+
cpuMeasurements.push({name, unit: measurements?.unit, values});
290319
}
291320
}
292321

293322
return new FlamegraphChartModel(
294323
Rect.From(flamegraph.configSpace),
295-
measures.length > 0 ? measures : [],
296-
flamegraphTheme.COLORS.CPU_CHART_COLORS
324+
cpuMeasurements.length > 0 ? cpuMeasurements : [],
325+
flamegraphTheme.COLORS.CPU_CHART_COLORS,
326+
{timelineUnit: 'milliseconds'}
297327
);
298328
}, [profileGroup.measurements, flamegraph.configSpace, flamegraphTheme, hasCPUChart]);
299329

@@ -306,25 +336,55 @@ export function ContinuousFlamegraph(): ReactElement {
306336

307337
const memory_footprint = profileGroup.measurements?.memory_footprint;
308338
if (memory_footprint) {
339+
const values: ProfileSeriesMeasurement['values'] = [];
340+
341+
let offset = 0;
342+
for (let i = 0; i < memory_footprint.values.length; i++) {
343+
const value = memory_footprint.values[i];
344+
const next = memory_footprint.values[i + 1] ?? value;
345+
offset += (next.timestamp - value.timestamp) * 1e3;
346+
347+
values.push({
348+
value: value.value,
349+
elapsed: offset,
350+
});
351+
}
352+
309353
measures.push({
310-
...memory_footprint!,
354+
unit: memory_footprint.unit,
311355
name: 'Heap Usage',
356+
values,
312357
});
313358
}
314359

315360
const native_memory_footprint = profileGroup.measurements?.memory_native_footprint;
316361
if (native_memory_footprint) {
362+
const values: ProfileSeriesMeasurement['values'] = [];
363+
364+
let offset = 0;
365+
for (let i = 0; i < native_memory_footprint.values.length; i++) {
366+
const value = native_memory_footprint.values[i];
367+
const next = native_memory_footprint.values[i + 1] ?? value;
368+
offset += (next.timestamp - value.timestamp) * 1e3;
369+
370+
values.push({
371+
value: value.value,
372+
elapsed: offset,
373+
});
374+
}
375+
317376
measures.push({
318-
...native_memory_footprint!,
377+
unit: native_memory_footprint.unit,
319378
name: 'Native Heap Usage',
379+
values,
320380
});
321381
}
322382

323383
return new FlamegraphChartModel(
324384
Rect.From(flamegraph.configSpace),
325385
measures.length > 0 ? measures : [],
326386
flamegraphTheme.COLORS.MEMORY_CHART_COLORS,
327-
{type: 'area'}
387+
{type: 'area', timelineUnit: 'milliseconds'}
328388
);
329389
}, [
330390
profileGroup.measurements,
@@ -1080,7 +1140,7 @@ export function ContinuousFlamegraph(): ReactElement {
10801140
batteryChart={
10811141
hasBatteryChart ? (
10821142
<FlamegraphChart
1083-
configViewUnit={flamegraph.profile.unit}
1143+
configViewUnit={flamegraph.profile.unit as ProfilingFormatterUnit}
10841144
status={profiles.type}
10851145
chartCanvasRef={batteryChartCanvasRef}
10861146
chartCanvas={batteryChartCanvas}
@@ -1102,7 +1162,7 @@ export function ContinuousFlamegraph(): ReactElement {
11021162
memoryChart={
11031163
hasMemoryChart ? (
11041164
<FlamegraphChart
1105-
configViewUnit={flamegraph.profile.unit}
1165+
configViewUnit={flamegraph.profile.unit as ProfilingFormatterUnit}
11061166
status={profiles.type}
11071167
chartCanvasRef={memoryChartCanvasRef}
11081168
chartCanvas={memoryChartCanvas}
@@ -1128,7 +1188,7 @@ export function ContinuousFlamegraph(): ReactElement {
11281188
cpuChart={
11291189
hasCPUChart ? (
11301190
<FlamegraphChart
1131-
configViewUnit={flamegraph.profile.unit}
1191+
configViewUnit={flamegraph.profile.unit as ProfilingFormatterUnit}
11321192
status={profiles.type}
11331193
chartCanvasRef={cpuChartCanvasRef}
11341194
chartCanvas={cpuChartCanvas}

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ function Flamegraph(): ReactElement {
391391
...profileGroup.measurements[key]!,
392392
values: profileGroup.measurements[key]!.values.map(v => {
393393
return {
394-
elapsed_since_start_ns: v.elapsed_since_start_ns,
394+
elapsed: v.elapsed_since_start_ns,
395395
value: fromNanoJoulesToWatts(v.value, 0.1),
396396
};
397397
}),
@@ -414,21 +414,32 @@ function Flamegraph(): ReactElement {
414414
return LOADING_OR_FALLBACK_CPU_CHART;
415415
}
416416

417-
const measures: ProfileSeriesMeasurement[] = [];
417+
const cpuMeasurements: ProfileSeriesMeasurement[] = [];
418418

419419
for (const key in profileGroup.measurements) {
420420
if (key.startsWith('cpu_usage')) {
421421
const name =
422422
key === 'cpu_usage'
423423
? 'Average CPU usage'
424424
: `CPU Core ${key.replace('cpu_usage_', '')}`;
425-
measures.push({...profileGroup.measurements[key]!, name});
425+
426+
const measurements = profileGroup.measurements[key]!;
427+
const values: ProfileSeriesMeasurement['values'] = [];
428+
429+
for (let i = 0; i < measurements.values.length; i++) {
430+
const value = measurements.values[i];
431+
values.push({
432+
value: value.value,
433+
elapsed: value.elapsed_since_start_ns,
434+
});
435+
}
436+
cpuMeasurements.push({name, unit: measurements?.unit, values});
426437
}
427438
}
428439

429440
return new FlamegraphChartModel(
430441
Rect.From(flamegraph.configSpace),
431-
measures.length > 0 ? measures : [],
442+
cpuMeasurements.length > 0 ? cpuMeasurements : [],
432443
flamegraphTheme.COLORS.CPU_CHART_COLORS
433444
);
434445
}, [profileGroup.measurements, flamegraph.configSpace, flamegraphTheme, hasCPUChart]);
@@ -442,17 +453,39 @@ function Flamegraph(): ReactElement {
442453

443454
const memory_footprint = profileGroup.measurements?.memory_footprint;
444455
if (memory_footprint) {
456+
const values: ProfileSeriesMeasurement['values'] = [];
457+
458+
for (let i = 0; i < memory_footprint.values.length; i++) {
459+
const value = memory_footprint.values[i];
460+
values.push({
461+
value: value.value,
462+
elapsed: value.elapsed_since_start_ns,
463+
});
464+
}
465+
445466
measures.push({
446-
...memory_footprint!,
467+
unit: memory_footprint.unit,
447468
name: 'Heap Usage',
469+
values,
448470
});
449471
}
450472

451473
const native_memory_footprint = profileGroup.measurements?.memory_native_footprint;
452474
if (native_memory_footprint) {
475+
const values: ProfileSeriesMeasurement['values'] = [];
476+
477+
for (let i = 0; i < native_memory_footprint.values.length; i++) {
478+
const value = native_memory_footprint.values[i];
479+
values.push({
480+
value: value.value,
481+
elapsed: value.elapsed_since_start_ns,
482+
});
483+
}
484+
453485
measures.push({
454-
...native_memory_footprint!,
486+
unit: native_memory_footprint.unit,
455487
name: 'Native Heap Usage',
488+
values,
456489
});
457490
}
458491

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ import {
1616
getPhysicalSpacePositionFromOffset,
1717
transformMatrixBetweenRect,
1818
} from 'sentry/utils/profiling/gl/utils';
19-
import type {Profile} from 'sentry/utils/profiling/profile/profile';
2019
import {FlamegraphChartRenderer} from 'sentry/utils/profiling/renderers/chartRenderer';
2120
import type {Rect} from 'sentry/utils/profiling/speedscope';
22-
import {formatTo} from 'sentry/utils/profiling/units/units';
21+
import {formatTo, type ProfilingFormatterUnit} from 'sentry/utils/profiling/units/units';
2322

2423
import {useCanvasScroll} from './interactions/useCanvasScroll';
2524
import {useCanvasZoomOrScroll} from './interactions/useCanvasZoomOrScroll';
@@ -38,7 +37,7 @@ interface FlamegraphChartProps {
3837
chartCanvas: FlamegraphCanvas | null;
3938
chartCanvasRef: HTMLCanvasElement | null;
4039
chartView: CanvasView<FlamegraphChartModel> | null;
41-
configViewUnit: Profile['unit'];
40+
configViewUnit: ProfilingFormatterUnit;
4241
noMeasurementMessage: string | undefined;
4342
setChartCanvasRef: (ref: HTMLCanvasElement | null) => void;
4443
status: RequestState<any>['type'];
@@ -310,6 +309,7 @@ export function FlamegraphChart({
310309
chartView={chartView}
311310
chartRenderer={chartRenderer}
312311
canvasBounds={canvasBounds}
312+
configViewUnit={configViewUnit}
313313
/>
314314
) : null}
315315
{/* transaction loads after profile, so we want to show loading even if it's in initial state */}

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {FlamegraphCanvas} from 'sentry/utils/profiling/flamegraphCanvas';
99
import type {FlamegraphChart} from 'sentry/utils/profiling/flamegraphChart';
1010
import type {FlamegraphChartRenderer} from 'sentry/utils/profiling/renderers/chartRenderer';
1111
import type {Rect} from 'sentry/utils/profiling/speedscope';
12+
import {formatTo, type ProfilingFormatterUnit} from 'sentry/utils/profiling/units/units';
1213

1314
import {
1415
FlamegraphTooltipColorIndicator,
@@ -23,6 +24,7 @@ export interface FlamegraphChartTooltipProps {
2324
chartRenderer: FlamegraphChartRenderer;
2425
chartView: CanvasView<FlamegraphChart>;
2526
configSpaceCursor: vec2;
27+
configViewUnit: ProfilingFormatterUnit;
2628
}
2729

2830
export function FlamegraphChartTooltip({
@@ -32,11 +34,14 @@ export function FlamegraphChartTooltip({
3234
chart,
3335
chartRenderer,
3436
chartView,
35-
}: // chartRenderer,
36-
FlamegraphChartTooltipProps) {
37+
configViewUnit,
38+
}: FlamegraphChartTooltipProps) {
3739
const series = useMemo(() => {
38-
return chartRenderer.findHoveredSeries(configSpaceCursor, 6e7);
39-
}, [chartRenderer, configSpaceCursor]);
40+
return chartRenderer.findHoveredSeries(
41+
configSpaceCursor,
42+
formatTo(100, 'milliseconds', configViewUnit)
43+
);
44+
}, [chartRenderer, configSpaceCursor, configViewUnit]);
4045

4146
return series.length > 0 ? (
4247
<BoundTooltip

0 commit comments

Comments
 (0)