@parca/profile 0.19.44 → 0.19.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/GraphTooltipArrow/Content.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/Content.js +1 -1
- package/dist/MetricsGraph/MetricsContextMenu/index.d.ts +20 -11
- package/dist/MetricsGraph/MetricsContextMenu/index.d.ts.map +1 -1
- package/dist/MetricsGraph/MetricsContextMenu/index.js +16 -20
- package/dist/MetricsGraph/MetricsTooltip/index.d.ts +2 -8
- package/dist/MetricsGraph/MetricsTooltip/index.d.ts.map +1 -1
- package/dist/MetricsGraph/MetricsTooltip/index.js +46 -55
- package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts +2 -5
- package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts.map +1 -1
- package/dist/MetricsGraph/UtilizationMetrics/Throughput.js +126 -205
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +9 -17
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -1
- package/dist/MetricsGraph/UtilizationMetrics/index.js +149 -208
- package/dist/MetricsGraph/index.d.ts +19 -26
- package/dist/MetricsGraph/index.d.ts.map +1 -1
- package/dist/MetricsGraph/index.js +50 -115
- package/dist/ProfileFlameGraph/index.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/index.js +3 -1
- package/dist/ProfileMetricsGraph/index.d.ts +1 -1
- package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/index.js +232 -23
- package/dist/ProfileSelector/MetricsGraphSection.d.ts +1 -4
- package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
- package/dist/ProfileSelector/MetricsGraphSection.js +8 -4
- package/dist/ProfileSelector/index.d.ts +3 -6
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +2 -2
- package/dist/ProfileSource.d.ts +9 -6
- package/dist/ProfileSource.d.ts.map +1 -1
- package/dist/ProfileSource.js +23 -8
- package/dist/styles.css +1 -1
- package/dist/useQuery.js +1 -1
- package/package.json +6 -6
- package/src/GraphTooltipArrow/Content.tsx +2 -4
- package/src/MetricsGraph/MetricsContextMenu/index.tsx +78 -66
- package/src/MetricsGraph/MetricsTooltip/index.tsx +53 -210
- package/src/MetricsGraph/UtilizationMetrics/Throughput.tsx +242 -434
- package/src/MetricsGraph/UtilizationMetrics/index.tsx +312 -448
- package/src/MetricsGraph/index.tsx +99 -185
- package/src/ProfileFlameGraph/index.tsx +3 -1
- package/src/ProfileMetricsGraph/index.tsx +430 -37
- package/src/ProfileSelector/MetricsGraphSection.tsx +12 -8
- package/src/ProfileSelector/index.tsx +5 -5
- package/src/ProfileSource.tsx +34 -17
- package/src/useQuery.tsx +1 -1
|
@@ -18,73 +18,66 @@ import {pointer} from 'd3-selection';
|
|
|
18
18
|
import throttle from 'lodash.throttle';
|
|
19
19
|
import {useContextMenu} from 'react-contexify';
|
|
20
20
|
|
|
21
|
-
import {Label, MetricsSample, MetricsSeries as MetricsSeriesPb} from '@parca/client';
|
|
22
21
|
import {DateTimeRange, useParcaContext} from '@parca/components';
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
formatForTimespan,
|
|
26
|
-
getPrecision,
|
|
27
|
-
sanitizeHighlightedValues,
|
|
28
|
-
valueFormatter,
|
|
29
|
-
} from '@parca/utilities';
|
|
30
|
-
|
|
31
|
-
import {MergedProfileSelection} from '..';
|
|
22
|
+
import {formatDate, formatForTimespan, getPrecision, valueFormatter} from '@parca/utilities';
|
|
23
|
+
|
|
32
24
|
import MetricsCircle from '../MetricsCircle';
|
|
33
25
|
import MetricsSeries from '../MetricsSeries';
|
|
34
|
-
import MetricsContextMenu
|
|
26
|
+
import MetricsContextMenu, {
|
|
27
|
+
ContextMenuItem,
|
|
28
|
+
ContextMenuItemOrSubmenu,
|
|
29
|
+
ContextMenuSubmenu,
|
|
30
|
+
} from './MetricsContextMenu';
|
|
35
31
|
import MetricsInfoPanel from './MetricsInfoPanel';
|
|
36
32
|
import MetricsTooltip from './MetricsTooltip';
|
|
37
33
|
|
|
38
34
|
interface Props {
|
|
39
|
-
data:
|
|
35
|
+
data: Series[];
|
|
40
36
|
from: number;
|
|
41
37
|
to: number;
|
|
42
|
-
|
|
43
|
-
onSampleClick: (timestamp: number, value: number, labels: Label[], duration: number) => void;
|
|
44
|
-
addLabelMatcher: (
|
|
45
|
-
labels: {key: string; value: string} | Array<{key: string; value: string}>
|
|
46
|
-
) => void;
|
|
38
|
+
onSampleClick: (closestPoint: SeriesPoint) => void;
|
|
47
39
|
setTimeRange: (range: DateTimeRange) => void;
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
yAxisLabel: string;
|
|
41
|
+
yAxisUnit: string;
|
|
50
42
|
width?: number;
|
|
51
43
|
height?: number;
|
|
52
44
|
margin?: number;
|
|
53
|
-
|
|
45
|
+
selectedPoint?: SeriesPoint | null;
|
|
46
|
+
contextMenuItems?: ContextMenuItemOrSubmenu[];
|
|
47
|
+
renderTooltipContent?: (seriesIndex: number, pointIndex: number) => React.ReactNode;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SeriesPoint {
|
|
51
|
+
seriesIndex: number;
|
|
52
|
+
pointIndex: number;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
export interface HighlightedSeries {
|
|
57
56
|
seriesIndex: number;
|
|
58
|
-
|
|
59
|
-
timestamp: number;
|
|
60
|
-
value: number;
|
|
61
|
-
valuePerSecond: number;
|
|
62
|
-
duration: number;
|
|
57
|
+
pointIndex: number;
|
|
63
58
|
x: number;
|
|
64
59
|
y: number;
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
export interface Series {
|
|
68
|
-
|
|
69
|
-
values: number
|
|
70
|
-
labelset: string;
|
|
71
|
-
isSelected?: boolean;
|
|
63
|
+
id: string; // opaque string used to determine line color
|
|
64
|
+
values: Array<[number, number]>; // [timestamp_ms, value]
|
|
72
65
|
}
|
|
73
66
|
|
|
74
67
|
const MetricsGraph = ({
|
|
75
68
|
data,
|
|
76
69
|
from,
|
|
77
70
|
to,
|
|
78
|
-
profile,
|
|
79
71
|
onSampleClick,
|
|
80
|
-
addLabelMatcher,
|
|
81
72
|
setTimeRange,
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
yAxisLabel,
|
|
74
|
+
yAxisUnit,
|
|
84
75
|
width = 0,
|
|
85
76
|
height = 0,
|
|
86
77
|
margin = 0,
|
|
87
|
-
|
|
78
|
+
selectedPoint,
|
|
79
|
+
contextMenuItems,
|
|
80
|
+
renderTooltipContent,
|
|
88
81
|
}: Props): JSX.Element => {
|
|
89
82
|
const [isInfoPanelOpen, setIsInfoPanelOpen] = useState<boolean>(false);
|
|
90
83
|
return (
|
|
@@ -99,22 +92,23 @@ const MetricsGraph = ({
|
|
|
99
92
|
data={data}
|
|
100
93
|
from={from}
|
|
101
94
|
to={to}
|
|
102
|
-
profile={profile}
|
|
103
95
|
onSampleClick={onSampleClick}
|
|
104
|
-
addLabelMatcher={addLabelMatcher}
|
|
105
96
|
setTimeRange={setTimeRange}
|
|
106
|
-
|
|
107
|
-
|
|
97
|
+
yAxisLabel={yAxisLabel}
|
|
98
|
+
yAxisUnit={yAxisUnit}
|
|
108
99
|
width={width}
|
|
109
100
|
height={height}
|
|
110
101
|
margin={margin}
|
|
111
|
-
|
|
102
|
+
selectedPoint={selectedPoint}
|
|
103
|
+
contextMenuItems={contextMenuItems}
|
|
104
|
+
renderTooltipContent={renderTooltipContent}
|
|
112
105
|
/>
|
|
113
106
|
</div>
|
|
114
107
|
);
|
|
115
108
|
};
|
|
116
109
|
|
|
117
110
|
export default MetricsGraph;
|
|
111
|
+
export type {ContextMenuItemOrSubmenu, ContextMenuItem, ContextMenuSubmenu};
|
|
118
112
|
|
|
119
113
|
export const parseValue = (value: string): number | null => {
|
|
120
114
|
const val = parseFloat(value);
|
|
@@ -130,16 +124,16 @@ export const RawMetricsGraph = ({
|
|
|
130
124
|
data,
|
|
131
125
|
from,
|
|
132
126
|
to,
|
|
133
|
-
profile,
|
|
134
127
|
onSampleClick,
|
|
135
|
-
addLabelMatcher,
|
|
136
128
|
setTimeRange,
|
|
137
|
-
|
|
138
|
-
|
|
129
|
+
yAxisLabel,
|
|
130
|
+
yAxisUnit,
|
|
139
131
|
width,
|
|
140
132
|
height = 50,
|
|
141
133
|
margin = 0,
|
|
142
|
-
|
|
134
|
+
selectedPoint,
|
|
135
|
+
contextMenuItems,
|
|
136
|
+
renderTooltipContent,
|
|
143
137
|
}: Props): JSX.Element => {
|
|
144
138
|
const {timezone} = useParcaContext();
|
|
145
139
|
const graph = useRef(null);
|
|
@@ -151,9 +145,6 @@ export const RawMetricsGraph = ({
|
|
|
151
145
|
const metricPointRef = useRef(null);
|
|
152
146
|
const idForContextMenu = useId();
|
|
153
147
|
|
|
154
|
-
// the time of the selected point is the start of the merge window
|
|
155
|
-
const time: number = parseFloat(profile?.HistoryParams().merge_from);
|
|
156
|
-
|
|
157
148
|
if (width === undefined || width == null) {
|
|
158
149
|
width = 0;
|
|
159
150
|
}
|
|
@@ -164,30 +155,11 @@ export const RawMetricsGraph = ({
|
|
|
164
155
|
return `translate(6, 0) scale(${(graphWidth - 6) / graphWidth}, 1)`;
|
|
165
156
|
}, [graphWidth]);
|
|
166
157
|
|
|
167
|
-
const series
|
|
168
|
-
if (s.labelset !== undefined) {
|
|
169
|
-
const metric = s.labelset.labels.sort((a, b) => a.name.localeCompare(b.name));
|
|
170
|
-
agg.push({
|
|
171
|
-
metric,
|
|
172
|
-
values: s.samples.reduce<number[][]>(function (agg: number[][], d: MetricsSample) {
|
|
173
|
-
if (d.timestamp !== undefined && d.valuePerSecond !== undefined) {
|
|
174
|
-
const t = (Number(d.timestamp.seconds) * 1e9 + d.timestamp.nanos) / 1e6; // https://github.com/microsoft/TypeScript/issues/5710#issuecomment-157886246
|
|
175
|
-
agg.push([t, d.valuePerSecond, Number(d.value), Number(d.duration)]);
|
|
176
|
-
}
|
|
177
|
-
return agg;
|
|
178
|
-
}, []),
|
|
179
|
-
labelset: metric.map(m => `${m.name}=${m.value}`).join(','),
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return agg;
|
|
183
|
-
}, []);
|
|
184
|
-
|
|
185
|
-
// Sort series by id to make sure the colors are consistent
|
|
186
|
-
series.sort((a, b) => a.labelset.localeCompare(b.labelset));
|
|
158
|
+
const series = data;
|
|
187
159
|
|
|
188
160
|
const extentsY = series.map(function (s) {
|
|
189
161
|
return d3.extent(s.values, function (d) {
|
|
190
|
-
return d[1];
|
|
162
|
+
return d[1]; // d[1] is the value
|
|
191
163
|
});
|
|
192
164
|
});
|
|
193
165
|
|
|
@@ -208,21 +180,29 @@ export const RawMetricsGraph = ({
|
|
|
208
180
|
.range([height - margin, 0])
|
|
209
181
|
.nice();
|
|
210
182
|
|
|
211
|
-
|
|
183
|
+
// Create deterministic color mapping based on series IDs
|
|
184
|
+
const color = useMemo(() => {
|
|
185
|
+
const scale = d3.scaleOrdinal(d3.schemeCategory10);
|
|
186
|
+
// Pre-populate the scale with sorted series IDs to ensure consistent colors
|
|
187
|
+
const sortedIds = [...new Set(series.map(s => s.id))].sort();
|
|
188
|
+
sortedIds.forEach(id => scale(id));
|
|
189
|
+
return scale;
|
|
190
|
+
}, [series]);
|
|
212
191
|
|
|
213
|
-
const l = d3.line(
|
|
192
|
+
const l = d3.line<[number, number]>(
|
|
214
193
|
d => xScale(d[0]),
|
|
215
194
|
d => yScale(d[1])
|
|
216
195
|
);
|
|
217
196
|
|
|
218
|
-
const
|
|
197
|
+
const closestPoint = useMemo(() => {
|
|
219
198
|
// Return the closest point as the highlighted point
|
|
220
199
|
|
|
221
200
|
const closestPointPerSeries = series.map(function (s) {
|
|
222
201
|
const distances = s.values.map(d => {
|
|
223
|
-
const x = xScale(d[0]) + margin / 2;
|
|
224
|
-
const y = yScale(d[1]) - margin / 3;
|
|
202
|
+
const x = xScale(d[0]) + margin / 2; // d[0] is timestamp_ms
|
|
203
|
+
const y = yScale(d[1]) - margin / 3; // d[1] is value
|
|
225
204
|
|
|
205
|
+
// Cartesian distance from the mouse position to the point
|
|
226
206
|
return Math.sqrt(Math.pow(pos[0] - x, 2) + Math.pow(pos[1] - y, 2));
|
|
227
207
|
});
|
|
228
208
|
|
|
@@ -237,18 +217,39 @@ export const RawMetricsGraph = ({
|
|
|
237
217
|
|
|
238
218
|
const closestSeriesIndex = d3.minIndex(closestPointPerSeries, s => s.distance);
|
|
239
219
|
const pointIndex = closestPointPerSeries[closestSeriesIndex].pointIndex;
|
|
240
|
-
const point = series[closestSeriesIndex].values[pointIndex];
|
|
241
220
|
return {
|
|
242
221
|
seriesIndex: closestSeriesIndex,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
222
|
+
pointIndex,
|
|
223
|
+
};
|
|
224
|
+
}, [pos, series, xScale, yScale, margin]);
|
|
225
|
+
|
|
226
|
+
const highlighted = useMemo(() => {
|
|
227
|
+
if (series.length === 0 || closestPoint == null) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const point = series[closestPoint.seriesIndex].values[closestPoint.pointIndex];
|
|
232
|
+
return {
|
|
233
|
+
seriesIndex: closestPoint.seriesIndex,
|
|
234
|
+
pointIndex: closestPoint.pointIndex,
|
|
248
235
|
x: xScale(point[0]),
|
|
249
236
|
y: yScale(point[1]),
|
|
250
237
|
};
|
|
251
|
-
}, [
|
|
238
|
+
}, [closestPoint, series, xScale, yScale]);
|
|
239
|
+
|
|
240
|
+
const selected = useMemo(() => {
|
|
241
|
+
if (series.length === 0 || selectedPoint == null) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const point = series[selectedPoint.seriesIndex].values[selectedPoint.pointIndex];
|
|
246
|
+
return {
|
|
247
|
+
seriesIndex: selectedPoint.seriesIndex,
|
|
248
|
+
pointIndex: selectedPoint.pointIndex,
|
|
249
|
+
x: xScale(point[0]),
|
|
250
|
+
y: yScale(point[1]),
|
|
251
|
+
};
|
|
252
|
+
}, [selectedPoint, series, xScale, yScale]);
|
|
252
253
|
|
|
253
254
|
const onMouseDown = (e: React.MouseEvent<SVGSVGElement | HTMLDivElement, MouseEvent>): void => {
|
|
254
255
|
// only left mouse button
|
|
@@ -270,14 +271,9 @@ export const RawMetricsGraph = ({
|
|
|
270
271
|
e.preventDefault();
|
|
271
272
|
};
|
|
272
273
|
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
onSampleClick(
|
|
276
|
-
Math.round(highlighted.timestamp),
|
|
277
|
-
highlighted.value,
|
|
278
|
-
sanitizeHighlightedValues(highlighted.labels), // When a user clicks on any sample in the graph, replace single `\` in the `labelValues` string with doubles `\\` if available.
|
|
279
|
-
highlighted.duration
|
|
280
|
-
);
|
|
274
|
+
const handleClosestPointClick = (): void => {
|
|
275
|
+
if (closestPoint != null) {
|
|
276
|
+
onSampleClick(closestPoint);
|
|
281
277
|
}
|
|
282
278
|
};
|
|
283
279
|
|
|
@@ -292,7 +288,7 @@ export const RawMetricsGraph = ({
|
|
|
292
288
|
// This is a normal click. We tolerate tiny movements to still be a
|
|
293
289
|
// click as they can occur when clicking based on user feedback.
|
|
294
290
|
if (Math.abs(relPos - pos[0]) <= 1) {
|
|
295
|
-
|
|
291
|
+
handleClosestPointClick();
|
|
296
292
|
setRelPos(-1);
|
|
297
293
|
return;
|
|
298
294
|
}
|
|
@@ -337,69 +333,6 @@ export const RawMetricsGraph = ({
|
|
|
337
333
|
throttledSetPos([xCoordinateWithoutMargin, yCoordinateWithoutMargin]);
|
|
338
334
|
};
|
|
339
335
|
|
|
340
|
-
const findSelectedProfile = (): HighlightedSeries | null => {
|
|
341
|
-
if (profile == null) {
|
|
342
|
-
return null;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
let s: Series | null = null;
|
|
346
|
-
let seriesIndex = -1;
|
|
347
|
-
|
|
348
|
-
// if there are both query matchers and also a sumby value, we need to check if the sumby value is part of the query matchers.
|
|
349
|
-
// if it is, then we should prioritize using the sumby label name and value to find the selected profile.
|
|
350
|
-
const useSumBy =
|
|
351
|
-
sumBy !== undefined &&
|
|
352
|
-
sumBy.length > 0 &&
|
|
353
|
-
profile.query.matchers.length > 0 &&
|
|
354
|
-
profile.query.matchers.some(e => sumBy.includes(e.key));
|
|
355
|
-
|
|
356
|
-
// get only the sumby keys and values from the profile query matchers
|
|
357
|
-
const sumByMatchers =
|
|
358
|
-
sumBy !== undefined ? profile.query.matchers.filter(e => sumBy.includes(e.key)) : [];
|
|
359
|
-
|
|
360
|
-
const keysToMatch = useSumBy ? sumByMatchers : profile.query.matchers;
|
|
361
|
-
|
|
362
|
-
outer: for (let i = 0; i < series.length; i++) {
|
|
363
|
-
const keys = keysToMatch.map(e => e.key);
|
|
364
|
-
for (let j = 0; j < keys.length; j++) {
|
|
365
|
-
const matcherKey = keys[j];
|
|
366
|
-
const label = series[i].metric.find(e => e.name === matcherKey);
|
|
367
|
-
if (label === undefined) {
|
|
368
|
-
continue outer; // label doesn't exist to begin with
|
|
369
|
-
}
|
|
370
|
-
if (keysToMatch[j].value !== label.value) {
|
|
371
|
-
continue outer; // label values don't match
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
seriesIndex = i;
|
|
375
|
-
s = series[i];
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (s == null) {
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
// Find the sample that matches the timestamp
|
|
382
|
-
const sample = s.values.find(v => {
|
|
383
|
-
return Math.round(v[0]) === time;
|
|
384
|
-
});
|
|
385
|
-
if (sample === undefined) {
|
|
386
|
-
return null;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return {
|
|
390
|
-
labels: [],
|
|
391
|
-
seriesIndex,
|
|
392
|
-
timestamp: sample[0],
|
|
393
|
-
valuePerSecond: sample[1],
|
|
394
|
-
value: sample[2],
|
|
395
|
-
duration: sample[3],
|
|
396
|
-
x: xScale(sample[0]),
|
|
397
|
-
y: yScale(sample[1]),
|
|
398
|
-
};
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
const selected = findSelectedProfile();
|
|
402
|
-
|
|
403
336
|
const MENU_ID = `metrics-context-menu-${idForContextMenu}`;
|
|
404
337
|
|
|
405
338
|
const {show} = useContextMenu({
|
|
@@ -419,33 +352,17 @@ export const RawMetricsGraph = ({
|
|
|
419
352
|
setIsContextMenuOpen(isVisible);
|
|
420
353
|
};
|
|
421
354
|
|
|
422
|
-
const isDeltaType = profile !== null ? profile?.query.profType.delta : false;
|
|
423
|
-
|
|
424
|
-
let yAxisLabel = sampleUnit;
|
|
425
|
-
let yAxisUnit = sampleUnit;
|
|
426
|
-
if (isDeltaType) {
|
|
427
|
-
if (sampleUnit === 'nanoseconds') {
|
|
428
|
-
if (sampleType === 'cpu') {
|
|
429
|
-
yAxisLabel = 'CPU Cores';
|
|
430
|
-
yAxisUnit = '';
|
|
431
|
-
}
|
|
432
|
-
if (sampleType === 'cuda') {
|
|
433
|
-
yAxisLabel = 'GPU Time';
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
if (sampleUnit === 'bytes') {
|
|
437
|
-
yAxisLabel = 'Bytes per Second';
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
355
|
return (
|
|
442
356
|
<>
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
357
|
+
{contextMenuItems != null && (
|
|
358
|
+
<MetricsContextMenu
|
|
359
|
+
menuId={MENU_ID}
|
|
360
|
+
closestPoint={closestPoint}
|
|
361
|
+
series={series}
|
|
362
|
+
trackVisibility={trackVisibility}
|
|
363
|
+
menuItems={contextMenuItems}
|
|
364
|
+
/>
|
|
365
|
+
)}
|
|
449
366
|
{highlighted != null && hovering && !dragging && pos[0] !== 0 && pos[1] !== 0 && (
|
|
450
367
|
<div
|
|
451
368
|
onMouseMove={onMouseMove}
|
|
@@ -456,11 +373,8 @@ export const RawMetricsGraph = ({
|
|
|
456
373
|
<MetricsTooltip
|
|
457
374
|
x={pos[0] + margin}
|
|
458
375
|
y={pos[1] + margin}
|
|
459
|
-
highlighted={highlighted}
|
|
460
376
|
contextElement={graph.current}
|
|
461
|
-
|
|
462
|
-
sampleUnit={sampleUnit}
|
|
463
|
-
delta={isDeltaType}
|
|
377
|
+
content={renderTooltipContent?.(highlighted.seriesIndex, highlighted.pointIndex)}
|
|
464
378
|
/>
|
|
465
379
|
)}
|
|
466
380
|
</div>
|
|
@@ -605,11 +519,11 @@ export const RawMetricsGraph = ({
|
|
|
605
519
|
width={graphWidth - 100}
|
|
606
520
|
>
|
|
607
521
|
{series.map((s, i) => (
|
|
608
|
-
<g key={
|
|
522
|
+
<g key={s.id} className="line">
|
|
609
523
|
<MetricsSeries
|
|
610
524
|
data={s}
|
|
611
525
|
line={l}
|
|
612
|
-
color={color(
|
|
526
|
+
color={color(s.id)}
|
|
613
527
|
strokeWidth={
|
|
614
528
|
hovering && highlighted != null && i === highlighted.seriesIndex
|
|
615
529
|
? lineStrokeHover
|
|
@@ -625,7 +539,7 @@ export const RawMetricsGraph = ({
|
|
|
625
539
|
<g
|
|
626
540
|
className="circle-group"
|
|
627
541
|
ref={metricPointRef}
|
|
628
|
-
style={{fill: color(highlighted.seriesIndex
|
|
542
|
+
style={{fill: color(series[highlighted.seriesIndex]?.id ?? '0')}}
|
|
629
543
|
transform={graphTransform}
|
|
630
544
|
>
|
|
631
545
|
<MetricsCircle cx={highlighted.x} cy={highlighted.y} />
|
|
@@ -636,7 +550,7 @@ export const RawMetricsGraph = ({
|
|
|
636
550
|
className="circle-group"
|
|
637
551
|
style={
|
|
638
552
|
selected?.seriesIndex != null
|
|
639
|
-
? {fill: color(selected.seriesIndex
|
|
553
|
+
? {fill: color(series[selected.seriesIndex]?.id ?? '0')}
|
|
640
554
|
: {}
|
|
641
555
|
}
|
|
642
556
|
transform={graphTransform}
|
|
@@ -75,7 +75,9 @@ export const validateFlameChartQuery = (
|
|
|
75
75
|
profileSource: MergedProfileSource
|
|
76
76
|
): {isValid: boolean; isNonDelta: boolean; isDurationTooLong: boolean} => {
|
|
77
77
|
const isNonDelta = !profileSource.ProfileType().delta;
|
|
78
|
-
const
|
|
78
|
+
const duration = profileSource.mergeTo - profileSource.mergeFrom;
|
|
79
|
+
console.log('duration of flame chart query: ', duration, 'ns');
|
|
80
|
+
const isDurationTooLong = duration > 60_000_000_000n; // 60 seconds in nanoseconds
|
|
79
81
|
return {isValid: !isNonDelta && !isDurationTooLong, isNonDelta, isDurationTooLong};
|
|
80
82
|
};
|
|
81
83
|
|