@parca/profile 0.19.80 → 0.19.82

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MatchersInput/index.d.ts +2 -34
  3. package/dist/MatchersInput/index.d.ts.map +1 -1
  4. package/dist/MatchersInput/index.js +14 -91
  5. package/dist/MetricsGraph/index.d.ts.map +1 -1
  6. package/dist/MetricsGraph/index.js +13 -1
  7. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  8. package/dist/ProfileMetricsGraph/index.js +24 -33
  9. package/dist/ProfileSelector/MetricsGraphSection.d.ts +2 -9
  10. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  11. package/dist/ProfileSelector/MetricsGraphSection.js +3 -38
  12. package/dist/ProfileSelector/index.d.ts +1 -29
  13. package/dist/ProfileSelector/index.d.ts.map +1 -1
  14. package/dist/ProfileSelector/index.js +12 -9
  15. package/dist/ProfileView/components/ProfileFilters/filterPresets.d.ts.map +1 -1
  16. package/dist/ProfileView/components/ProfileFilters/filterPresets.js +15 -3
  17. package/dist/QueryControls/index.d.ts +42 -0
  18. package/dist/QueryControls/index.d.ts.map +1 -0
  19. package/dist/{ProfileSelector/QueryControls.js → QueryControls/index.js} +16 -13
  20. package/dist/SimpleMatchers/Select.js +1 -1
  21. package/dist/SimpleMatchers/index.d.ts +2 -11
  22. package/dist/SimpleMatchers/index.d.ts.map +1 -1
  23. package/dist/SimpleMatchers/index.js +34 -45
  24. package/dist/ViewMatchers/index.d.ts +0 -9
  25. package/dist/ViewMatchers/index.d.ts.map +1 -1
  26. package/dist/ViewMatchers/index.js +20 -12
  27. package/dist/contexts/LabelsQueryProvider.d.ts +35 -0
  28. package/dist/contexts/LabelsQueryProvider.d.ts.map +1 -0
  29. package/dist/contexts/LabelsQueryProvider.js +70 -0
  30. package/dist/contexts/UnifiedLabelsContext.d.ts +37 -0
  31. package/dist/contexts/UnifiedLabelsContext.d.ts.map +1 -0
  32. package/dist/contexts/UnifiedLabelsContext.js +88 -0
  33. package/dist/contexts/utils.d.ts +10 -0
  34. package/dist/contexts/utils.d.ts.map +1 -0
  35. package/dist/contexts/utils.js +31 -0
  36. package/dist/hooks/useLabels.d.ts +23 -0
  37. package/dist/hooks/useLabels.d.ts.map +1 -0
  38. package/dist/hooks/useLabels.js +75 -0
  39. package/dist/hooks/useQueryState.d.ts +2 -0
  40. package/dist/hooks/useQueryState.d.ts.map +1 -1
  41. package/dist/hooks/useQueryState.js +18 -0
  42. package/dist/index.d.ts +9 -3
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +9 -3
  45. package/dist/styles.css +1 -1
  46. package/dist/useSumBy.js +1 -1
  47. package/package.json +2 -2
  48. package/src/MatchersInput/index.tsx +17 -163
  49. package/src/MetricsGraph/index.tsx +17 -1
  50. package/src/ProfileMetricsGraph/index.tsx +37 -102
  51. package/src/ProfileSelector/MetricsGraphSection.tsx +14 -115
  52. package/src/ProfileSelector/index.tsx +106 -109
  53. package/src/ProfileView/components/ProfileFilters/filterPresets.ts +15 -3
  54. package/src/{ProfileSelector/QueryControls.tsx → QueryControls/index.tsx} +66 -84
  55. package/src/SimpleMatchers/Select.tsx +1 -1
  56. package/src/SimpleMatchers/index.tsx +46 -85
  57. package/src/ViewMatchers/index.tsx +22 -30
  58. package/src/contexts/LabelsQueryProvider.tsx +142 -0
  59. package/src/contexts/UnifiedLabelsContext.tsx +155 -0
  60. package/src/contexts/utils.ts +43 -0
  61. package/src/hooks/useLabels.ts +121 -0
  62. package/src/hooks/useQueryState.ts +25 -0
  63. package/src/index.tsx +29 -3
  64. package/src/useSumBy.ts +1 -1
  65. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts +0 -29
  66. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts.map +0 -1
  67. package/dist/MetricsGraph/UtilizationMetrics/Throughput.js +0 -175
  68. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +0 -28
  69. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +0 -1
  70. package/dist/MetricsGraph/UtilizationMetrics/index.js +0 -186
  71. package/dist/ProfileSelector/QueryControls.d.ts +0 -43
  72. package/dist/ProfileSelector/QueryControls.d.ts.map +0 -1
  73. package/dist/contexts/MatchersInputLabelsContext.d.ts +0 -29
  74. package/dist/contexts/MatchersInputLabelsContext.d.ts.map +0 -1
  75. package/dist/contexts/MatchersInputLabelsContext.js +0 -79
  76. package/dist/contexts/SimpleMatchersLabelContext.d.ts +0 -25
  77. package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +0 -1
  78. package/dist/contexts/SimpleMatchersLabelContext.js +0 -115
  79. package/dist/contexts/UtilizationLabelsContext.d.ts +0 -15
  80. package/dist/contexts/UtilizationLabelsContext.d.ts.map +0 -1
  81. package/dist/contexts/UtilizationLabelsContext.js +0 -25
  82. package/src/MetricsGraph/UtilizationMetrics/Throughput.tsx +0 -405
  83. package/src/MetricsGraph/UtilizationMetrics/index.tsx +0 -426
  84. package/src/contexts/MatchersInputLabelsContext.tsx +0 -141
  85. package/src/contexts/SimpleMatchersLabelContext.tsx +0 -189
  86. package/src/contexts/UtilizationLabelsContext.tsx +0 -45
@@ -11,157 +11,19 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {useEffect, useMemo, useRef, useState} from 'react';
14
+ import React, {useMemo, useRef, useState} from 'react';
15
15
 
16
- import {useQuery} from '@tanstack/react-query';
17
16
  import cx from 'classnames';
18
17
  import TextareaAutosize from 'react-textarea-autosize';
19
18
 
20
- import {LabelsRequest, LabelsResponse, QueryServiceClient, ValuesRequest} from '@parca/client';
21
- import {useGrpcMetadata} from '@parca/components';
22
19
  import {Query} from '@parca/parser';
23
20
  import {TEST_IDS, testId} from '@parca/test-utils';
24
- import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
25
21
 
26
- import {UtilizationLabels} from '../ProfileSelector';
27
- import {LabelsProvider, useLabels} from '../contexts/MatchersInputLabelsContext';
28
- import useGrpcQuery from '../useGrpcQuery';
22
+ import {useUnifiedLabels} from '../contexts/UnifiedLabelsContext';
23
+ import {useQueryState} from '../hooks/useQueryState';
29
24
  import SuggestionsList, {Suggestion, Suggestions} from './SuggestionsList';
30
25
 
31
- interface MatchersInputProps {
32
- queryClient: QueryServiceClient;
33
- setMatchersString: (arg: string) => void;
34
- runQuery: () => void;
35
- currentQuery: Query;
36
- profileType: string;
37
- start?: number;
38
- end?: number;
39
- }
40
-
41
- export interface ILabelNamesResult {
42
- response?: LabelsResponse;
43
- error?: Error;
44
- }
45
-
46
- interface UseLabelNames {
47
- result: ILabelNamesResult;
48
- loading: boolean;
49
- refetch: () => Promise<void>;
50
- }
51
-
52
- export const useLabelNames = (
53
- client: QueryServiceClient,
54
- profileType: string,
55
- start?: number,
56
- end?: number,
57
- match?: string[]
58
- ): UseLabelNames => {
59
- const metadata = useGrpcMetadata();
60
-
61
- const {data, isLoading, error, refetch} = useGrpcQuery<LabelsResponse>({
62
- key: ['labelNames', profileType, match?.join(','), start, end],
63
- queryFn: async signal => {
64
- const request: LabelsRequest = {match: match !== undefined ? match : []};
65
- if (start !== undefined && end !== undefined) {
66
- request.start = millisToProtoTimestamp(start);
67
- request.end = millisToProtoTimestamp(end);
68
- }
69
- if (profileType !== undefined) {
70
- request.profileType = profileType;
71
- }
72
- const {response} = await client.labels(request, {meta: metadata, abort: signal});
73
- return response;
74
- },
75
- options: {
76
- enabled: profileType !== undefined && profileType !== '',
77
- keepPreviousData: false,
78
- },
79
- });
80
-
81
- useEffect(() => {
82
- console.log('Label names query result:', {data, error, isLoading});
83
- }, [data, error, isLoading]);
84
-
85
- return {
86
- result: {response: data, error: error as Error},
87
- loading: isLoading,
88
- refetch: async () => {
89
- await refetch();
90
- },
91
- };
92
- };
93
-
94
- interface UseLabelValues {
95
- result: {
96
- response: string[];
97
- error?: Error;
98
- };
99
- loading: boolean;
100
- refetch: () => Promise<void>;
101
- }
102
-
103
- export const useLabelValues = (
104
- client: QueryServiceClient,
105
- labelName: string,
106
- profileType: string,
107
- start?: number,
108
- end?: number
109
- ): UseLabelValues => {
110
- const metadata = useGrpcMetadata();
111
-
112
- const {data, isLoading, error, refetch} = useGrpcQuery<string[]>({
113
- key: ['labelValues', labelName, profileType, start, end],
114
- queryFn: async signal => {
115
- const request: ValuesRequest = {labelName, match: [], profileType};
116
- if (start !== undefined && end !== undefined) {
117
- request.start = millisToProtoTimestamp(start);
118
- request.end = millisToProtoTimestamp(end);
119
- }
120
- const {response} = await client.values(request, {meta: metadata, abort: signal});
121
- return sanitizeLabelValue(response.labelValues);
122
- },
123
- options: {
124
- enabled:
125
- profileType !== undefined &&
126
- profileType !== '' &&
127
- labelName !== undefined &&
128
- labelName !== '',
129
- keepPreviousData: false,
130
- },
131
- });
132
-
133
- console.log('Label values query result:', {data, error, isLoading, labelName});
134
-
135
- return {
136
- result: {response: data ?? [], error: error as Error},
137
- loading: isLoading,
138
- refetch: async () => {
139
- await refetch();
140
- },
141
- };
142
- };
143
-
144
- export const useFetchUtilizationLabelValues = (
145
- labelName: string,
146
- utilizationLabels?: UtilizationLabels
147
- ): string[] => {
148
- const {data} = useQuery({
149
- queryKey: ['utilizationLabelValues', labelName],
150
- queryFn: async () => {
151
- const result = await utilizationLabels?.utilizationFetchLabelValues?.(labelName);
152
- return result ?? [];
153
- },
154
- enabled: utilizationLabels?.utilizationFetchLabelValues != null && labelName !== '',
155
- });
156
-
157
- return data ?? [];
158
- };
159
-
160
- const MatchersInput = ({
161
- setMatchersString,
162
- runQuery,
163
- currentQuery,
164
- }: MatchersInputProps): JSX.Element => {
26
+ const MatchersInput = (): JSX.Element => {
165
27
  const inputRef = useRef<HTMLTextAreaElement | null>(null);
166
28
  const [focusedInput, setFocusedInput] = useState(false);
167
29
  const [lastCompleted, setLastCompleted] = useState<Suggestion>(new Suggestion('', '', ''));
@@ -169,7 +31,7 @@ const MatchersInput = ({
169
31
  const {
170
32
  labelNames,
171
33
  labelValues,
172
- labelNameMappings,
34
+ labelNameMappingsForMatchersInput: labelNameMappings,
173
35
  isLabelNamesLoading,
174
36
  isLabelValuesLoading,
175
37
  currentLabelName,
@@ -177,13 +39,16 @@ const MatchersInput = ({
177
39
  shouldHandlePrefixes,
178
40
  refetchLabelValues,
179
41
  refetchLabelNames,
180
- } = useLabels();
42
+ suffix,
43
+ } = useUnifiedLabels();
44
+
45
+ const {setDraftMatchers, commitDraft, draftParsedQuery} = useQueryState({suffix});
181
46
 
182
- const value = currentQuery.matchersString();
47
+ const value = draftParsedQuery != null ? draftParsedQuery.matchersString() : '';
183
48
 
184
49
  const suggestionSections = useMemo(() => {
185
50
  const suggestionSections = new Suggestions();
186
- Query.suggest(`${currentQuery.profileName()}{${value}`).forEach(function (s) {
51
+ Query.suggest(`${draftParsedQuery?.profileName() as string}{${value}`).forEach(function (s) {
187
52
  // Skip suggestions that we just completed. This really only works,
188
53
  // because we know the language is not repetitive. For a language that
189
54
  // has a repeating word, this would not work.
@@ -256,7 +121,7 @@ const MatchersInput = ({
256
121
  });
257
122
  return suggestionSections;
258
123
  }, [
259
- currentQuery,
124
+ draftParsedQuery,
260
125
  lastCompleted,
261
126
  labelNames,
262
127
  labelValues,
@@ -271,7 +136,7 @@ const MatchersInput = ({
271
136
 
272
137
  const onChange = (e: React.ChangeEvent<HTMLTextAreaElement>): void => {
273
138
  const newValue = e.target.value;
274
- setMatchersString(newValue);
139
+ setDraftMatchers(newValue);
275
140
  resetLastCompleted();
276
141
  };
277
142
 
@@ -294,7 +159,7 @@ const MatchersInput = ({
294
159
  const applySuggestion = (suggestion: Suggestion): void => {
295
160
  const newValue = complete(suggestion);
296
161
  setLastCompleted(suggestion);
297
- setMatchersString(newValue);
162
+ setDraftMatchers(newValue);
298
163
  if (inputRef.current !== null) {
299
164
  inputRef.current.value = newValue;
300
165
  inputRef.current.focus();
@@ -309,7 +174,7 @@ const MatchersInput = ({
309
174
  setFocusedInput(false);
310
175
  };
311
176
 
312
- const profileSelected = currentQuery.profileName() === '';
177
+ const profileSelected = draftParsedQuery?.profileName() === '';
313
178
 
314
179
  return (
315
180
  <div
@@ -345,7 +210,7 @@ const MatchersInput = ({
345
210
  suggestions={suggestionSections}
346
211
  applySuggestion={applySuggestion}
347
212
  inputRef={inputRef.current}
348
- runQuery={runQuery}
213
+ runQuery={commitDraft}
349
214
  focusedInput={focusedInput}
350
215
  isLabelValuesLoading={
351
216
  isLabelValuesLoading && lastCompleted.type === 'literal' && lastCompleted.value !== ','
@@ -358,15 +223,4 @@ const MatchersInput = ({
358
223
  );
359
224
  };
360
225
 
361
- export default function MatchersInputWithProvider(props: MatchersInputProps): JSX.Element {
362
- return (
363
- <LabelsProvider
364
- queryClient={props.queryClient}
365
- profileType={props.profileType}
366
- start={props.start}
367
- end={props.end}
368
- >
369
- <MatchersInput {...props} />
370
- </LabelsProvider>
371
- );
372
- }
226
+ export default MatchersInput;
@@ -114,6 +114,7 @@ const MetricsGraph = ({
114
114
  };
115
115
 
116
116
  export default MetricsGraph;
117
+
117
118
  export type {ContextMenuItemOrSubmenu, ContextMenuItem, ContextMenuSubmenu};
118
119
 
119
120
  export const parseValue = (value: string): number | null => {
@@ -207,6 +208,13 @@ export const RawMetricsGraph = ({
207
208
  }
208
209
 
209
210
  const closestPointPerSeries = series.map(function (s) {
211
+ if (s.values.length === 0) {
212
+ return {
213
+ pointIndex: undefined,
214
+ distance: Infinity,
215
+ };
216
+ }
217
+
210
218
  const distances = s.values.map(d => {
211
219
  const x = xScale(d[0]) + margin / 2; // d[0] is timestamp_ms
212
220
  const y = yScale(d[1]) - margin / 3; // d[1] is value
@@ -216,7 +224,7 @@ export const RawMetricsGraph = ({
216
224
  });
217
225
 
218
226
  const pointIndex = d3.minIndex(distances);
219
- const minDistance = distances[pointIndex];
227
+ const minDistance = pointIndex != null ? distances[pointIndex] : Infinity;
220
228
 
221
229
  return {
222
230
  pointIndex,
@@ -225,7 +233,15 @@ export const RawMetricsGraph = ({
225
233
  });
226
234
 
227
235
  const closestSeriesIndex = d3.minIndex(closestPointPerSeries, s => s.distance);
236
+ if (closestSeriesIndex == null || closestPointPerSeries[closestSeriesIndex] == null) {
237
+ return null;
238
+ }
239
+
228
240
  const pointIndex = closestPointPerSeries[closestSeriesIndex].pointIndex;
241
+ if (pointIndex == null) {
242
+ return null;
243
+ }
244
+
229
245
  return {
230
246
  seriesIndex: closestSeriesIndex,
231
247
  pointIndex,
@@ -37,19 +37,11 @@ import MetricsGraph, {ContextMenuItemOrSubmenu, Series, SeriesPoint} from '../Me
37
37
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
38
38
  import {useQueryRange} from './hooks/useQueryRange';
39
39
 
40
- const transformUtilizationLabels = (label: string, utilizationMetrics: boolean): string => {
41
- if (utilizationMetrics) {
42
- return label.replace('attributes.', '').replace('attributes_resource.', '');
43
- }
44
- return label;
45
- };
46
-
47
40
  const createProfileContextMenuItems = (
48
41
  addLabelMatcher: (
49
42
  labels: {key: string; value: string} | Array<{key: string; value: string}>
50
43
  ) => void,
51
- data: MetricsSeriesPb[], // The original MetricsSeriesPb[] data
52
- utilizationMetrics = false
44
+ data: MetricsSeriesPb[] // The original MetricsSeriesPb[] data
53
45
  ): ContextMenuItemOrSubmenu[] => {
54
46
  return [
55
47
  {
@@ -107,7 +99,7 @@ const createProfileContextMenuItems = (
107
99
  id: `add-label-${label.name}`,
108
100
  label: (
109
101
  <div className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-300">
110
- {`${transformUtilizationLabels(label.name, utilizationMetrics)}="${label.value}"`}
102
+ {`${label.name}="${label.value}"`}
111
103
  </div>
112
104
  ),
113
105
  onClick: () => {
@@ -239,12 +231,28 @@ const ProfileMetricsGraph = ({
239
231
  if (response?.series != null) {
240
232
  // Check if user wants ALL series for THIS specific response
241
233
  const userWantsAllForThisResponse = showAllSeriesForResponse === response;
234
+ const maxSeriesLimit = 100;
235
+
236
+ // Limit the number of series to maxSeriesLimit to avoid performance issues (unless user opts to show all)
237
+ if (response.series.length > maxSeriesLimit && !userWantsAllForThisResponse) {
238
+ // Select top `maxSeriesLimit` series based on their max value (to catch series with large spikes)
239
+ const seriesWithMaxValue = response.series.map(series => {
240
+ const maxValue = series.samples.reduce((max, sample) => {
241
+ const value = sample.valuePerSecond ?? 0;
242
+ return value > max ? value : max;
243
+ }, 0);
244
+ return {series, maxValue};
245
+ });
246
+
247
+ // Sort by max value descending and take top `maxSeriesLimit` series
248
+ const topSeries = seriesWithMaxValue
249
+ .sort((a, b) => b.maxValue - a.maxValue)
250
+ .slice(0, maxSeriesLimit)
251
+ .map(item => item.series);
242
252
 
243
- // Limit the number of series to 100 to avoid performance issues (unless user opts to show all)
244
- if (response.series.length > 100 && !userWantsAllForThisResponse) {
245
253
  return [
246
- response.series.slice(0, 100),
247
- {isTrimmed: true, beforeTrim: response.series.length, afterTrim: 100},
254
+ topSeries,
255
+ {isTrimmed: true, beforeTrim: response.series.length, afterTrim: maxSeriesLimit},
248
256
  ];
249
257
  }
250
258
  return [response.series, {isTrimmed: false, beforeTrim: 0, afterTrim: 0}];
@@ -422,28 +430,6 @@ const ProfileMetricsGraph = ({
422
430
  const nameLabel = labels.find(e => e.name === '__name__');
423
431
  const highlightedNameLabel = nameLabel ?? {name: '', value: ''};
424
432
 
425
- // Calculate attributes maps for utilization metrics
426
- const utilizationMetrics = false; // This is for profile metrics, not utilization
427
- const attributesMap = labels
428
- .filter(
429
- label =>
430
- label.name.startsWith('attributes.') &&
431
- !label.name.startsWith('attributes_resource.')
432
- )
433
- .reduce<Record<string, string>>((acc, label) => {
434
- const key = label.name.replace('attributes.', '');
435
- acc[key] = label.value;
436
- return acc;
437
- }, {});
438
-
439
- const attributesResourceMap = labels
440
- .filter(label => label.name.startsWith('attributes_resource.'))
441
- .reduce<Record<string, string>>((acc, label) => {
442
- const key = label.name.replace('attributes_resource.', '');
443
- acc[key] = label.value;
444
- return acc;
445
- }, {});
446
-
447
433
  const isDeltaType =
448
434
  profile !== null
449
435
  ? (profile as MergedProfileSelection)?.query.profType.delta
@@ -512,72 +498,21 @@ const ProfileMetricsGraph = ({
512
498
  </table>
513
499
  </span>
514
500
  <span className="my-2 block text-gray-500">
515
- {utilizationMetrics ? (
516
- <>
517
- {Object.keys(attributesResourceMap).length > 0 && (
518
- <span className="text-sm font-bold text-gray-700 dark:text-white">
519
- Resource Attributes
520
- </span>
521
- )}
522
- <span className="my-2 block text-gray-500">
523
- {Object.keys(attributesResourceMap).map(name => (
524
- <div
525
- key={name}
526
- className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
527
- {...testId(TEST_IDS.TOOLTIP_LABEL)}
528
- >
529
- <TextWithTooltip
530
- text={`${name.replace('attributes.', '')}="${
531
- attributesResourceMap[name]
532
- }"`}
533
- maxTextLength={48}
534
- id={`tooltip-${name}-${attributesResourceMap[name]}`}
535
- />
536
- </div>
537
- ))}
538
- </span>
539
- {Object.keys(attributesMap).length > 0 && (
540
- <span className="text-sm font-bold text-gray-700 dark:text-white">
541
- Attributes
542
- </span>
543
- )}
544
- <span className="my-2 block text-gray-500">
545
- {Object.keys(attributesMap).map(name => (
546
- <div
547
- key={name}
548
- className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
549
- {...testId(TEST_IDS.TOOLTIP_LABEL)}
550
- >
551
- <TextWithTooltip
552
- text={`${name.replace('attributes.', '')}="${
553
- attributesMap[name]
554
- }"`}
555
- maxTextLength={48}
556
- id={`tooltip-${name}-${attributesMap[name]}`}
557
- />
558
- </div>
559
- ))}
560
- </span>
561
- </>
562
- ) : (
563
- <>
564
- {labels
565
- .filter((label: Label) => label.name !== '__name__')
566
- .map((label: Label) => (
567
- <div
568
- key={label.name}
569
- className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
570
- {...testId(TEST_IDS.TOOLTIP_LABEL)}
571
- >
572
- <TextWithTooltip
573
- text={`${label.name}="${label.value}"`}
574
- maxTextLength={37}
575
- id={`tooltip-${label.name}`}
576
- />
577
- </div>
578
- ))}
579
- </>
580
- )}
501
+ {labels
502
+ .filter((label: Label) => label.name !== '__name__')
503
+ .map((label: Label) => (
504
+ <div
505
+ key={label.name}
506
+ className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
507
+ {...testId(TEST_IDS.TOOLTIP_LABEL)}
508
+ >
509
+ <TextWithTooltip
510
+ text={`${label.name}="${label.value}"`}
511
+ maxTextLength={37}
512
+ id={`tooltip-${label.name}`}
513
+ />
514
+ </div>
515
+ ))}
581
516
  </span>
582
517
  <div className="flex w-full items-center gap-1 text-xs text-gray-500">
583
518
  <Icon icon="iconoir:mouse-button-right" />
@@ -18,11 +18,9 @@ import {DateTimeRange, useURLStateBatch} from '@parca/components';
18
18
  import {Query} from '@parca/parser';
19
19
 
20
20
  import {ProfileSelection} from '..';
21
- import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
22
- import AreaChart from '../MetricsGraph/UtilizationMetrics/Throughput';
23
21
  import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
24
22
  import {useResetStateOnSeriesChange} from '../ProfileView/hooks/useResetStateOnSeriesChange';
25
- import {QuerySelection, type UtilizationMetrics as UtilizationMetricsType} from './index';
23
+ import {QuerySelection} from './index';
26
24
 
27
25
  interface MetricsGraphSectionProps {
28
26
  showMetricsGraph: boolean;
@@ -41,13 +39,6 @@ interface MetricsGraphSectionProps {
41
39
  query: Query;
42
40
  setNewQueryExpression: (queryExpression: string, commit?: boolean) => void;
43
41
  setQueryExpression: (updateTs?: boolean) => void;
44
- utilizationMetrics?: Array<{
45
- name: string;
46
- humanReadableName: string;
47
- data: UtilizationMetricsType[];
48
- }>;
49
- utilizationMetricsLoading?: boolean;
50
- onUtilizationSeriesSelect?: (name: string, seriesIndex: number) => void;
51
42
  }
52
43
 
53
44
  export function MetricsGraphSection({
@@ -66,9 +57,6 @@ export function MetricsGraphSection({
66
57
  setProfileSelection,
67
58
  query,
68
59
  setNewQueryExpression,
69
- utilizationMetrics,
70
- utilizationMetricsLoading,
71
- onUtilizationSeriesSelect,
72
60
  }: MetricsGraphSectionProps): JSX.Element {
73
61
  const resetStateOnSeriesChange = useResetStateOnSeriesChange();
74
62
  const batchUpdates = useURLStateBatch();
@@ -147,89 +135,6 @@ export function MetricsGraphSection({
147
135
  });
148
136
  };
149
137
 
150
- const UtilizationGraphToShow = ({
151
- utilizationMetrics,
152
- }: {
153
- utilizationMetrics: Array<{
154
- name: string;
155
- humanReadableName: string;
156
- data: UtilizationMetricsType[];
157
- }>;
158
- }): JSX.Element => {
159
- const throughputMetrics = utilizationMetrics.filter(
160
- metric =>
161
- metric.name === 'gpu_pcie_throughput_transmit_bytes' ||
162
- metric.name === 'gpu_pcie_throughput_receive_bytes'
163
- );
164
- const transmitData =
165
- throughputMetrics.find(metric => metric.name === 'gpu_pcie_throughput_transmit_bytes')
166
- ?.data ?? [];
167
- const receiveData =
168
- throughputMetrics.find(metric => metric.name === 'gpu_pcie_throughput_receive_bytes')?.data ??
169
- [];
170
-
171
- if (utilizationMetrics.length === 0) {
172
- return <></>;
173
- }
174
-
175
- return (
176
- <div>
177
- {utilizationMetrics.map(({name, humanReadableName, data}) => {
178
- if (
179
- name !== 'gpu_pcie_throughput_transmit_bytes' &&
180
- name !== 'gpu_pcie_throughput_receive_bytes'
181
- ) {
182
- return (
183
- <UtilizationMetricsGraph
184
- key={name}
185
- data={data}
186
- setTimeRange={handleTimeRangeChange}
187
- utilizationMetricsLoading={utilizationMetricsLoading}
188
- humanReadableName={humanReadableName}
189
- from={querySelection.from}
190
- to={querySelection.to}
191
- yAxisUnit="percentage"
192
- addLabelMatcher={addLabelMatcher}
193
- onSeriesClick={seriesIndex => {
194
- // For generic UtilizationMetrics, just pass the series index
195
- if (onUtilizationSeriesSelect != null) {
196
- onUtilizationSeriesSelect(name, seriesIndex);
197
- }
198
- }}
199
- />
200
- );
201
- }
202
- return null;
203
- })}
204
- {throughputMetrics.length > 0 && (
205
- <AreaChart
206
- transmitData={transmitData}
207
- receiveData={receiveData}
208
- addLabelMatcher={addLabelMatcher}
209
- setTimeRange={handleTimeRangeChange}
210
- name={throughputMetrics[0].name}
211
- humanReadableName={throughputMetrics[0].humanReadableName}
212
- from={querySelection.from}
213
- to={querySelection.to}
214
- utilizationMetricsLoading={utilizationMetricsLoading}
215
- selectedSeries={undefined}
216
- onSeriesClick={(_, seriesIndex) => {
217
- // For throughput metrics, just pass the series index
218
- if (onUtilizationSeriesSelect != null) {
219
- let name = 'gpu_pcie_throughput_transmit_bytes';
220
- if (seriesIndex > transmitData.length - 1) {
221
- name = 'gpu_pcie_throughput_receive_bytes';
222
- seriesIndex -= transmitData.length;
223
- }
224
- onUtilizationSeriesSelect(name, seriesIndex);
225
- }
226
- }}
227
- />
228
- )}
229
- </div>
230
- );
231
- };
232
-
233
138
  return (
234
139
  <div className={cx('relative', {'py-4': !showMetricsGraph})}>
235
140
  {setDisplayHideMetricsGraphButton != null ? (
@@ -251,25 +156,19 @@ export function MetricsGraphSection({
251
156
  querySelection.from !== undefined &&
252
157
  querySelection.to !== undefined ? (
253
158
  <>
254
- {utilizationMetrics !== undefined ? (
255
- <UtilizationGraphToShow utilizationMetrics={utilizationMetrics} />
256
- ) : (
257
- <>
258
- <ProfileMetricsGraph
259
- queryClient={queryClient}
260
- queryExpression={querySelection.expression}
261
- from={querySelection.from}
262
- to={querySelection.to}
263
- profile={profileSelection}
264
- comparing={comparing}
265
- sumBy={querySelection.sumBy ?? sumBy ?? []}
266
- sumByLoading={defaultSumByLoading}
267
- setTimeRange={handleTimeRangeChange}
268
- addLabelMatcher={addLabelMatcher}
269
- onPointClick={handlePointClick}
270
- />
271
- </>
272
- )}
159
+ <ProfileMetricsGraph
160
+ queryClient={queryClient}
161
+ queryExpression={querySelection.expression}
162
+ from={querySelection.from}
163
+ to={querySelection.to}
164
+ profile={profileSelection}
165
+ comparing={comparing}
166
+ sumBy={querySelection.sumBy ?? sumBy ?? []}
167
+ sumByLoading={defaultSumByLoading}
168
+ setTimeRange={handleTimeRangeChange}
169
+ addLabelMatcher={addLabelMatcher}
170
+ onPointClick={handlePointClick}
171
+ />
273
172
  </>
274
173
  ) : (
275
174
  profileSelection === null && (