@parca/profile 0.19.43 → 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.
Files changed (50) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/GraphTooltipArrow/Content.d.ts.map +1 -1
  3. package/dist/GraphTooltipArrow/Content.js +1 -1
  4. package/dist/MetricsGraph/MetricsContextMenu/index.d.ts +20 -11
  5. package/dist/MetricsGraph/MetricsContextMenu/index.d.ts.map +1 -1
  6. package/dist/MetricsGraph/MetricsContextMenu/index.js +16 -20
  7. package/dist/MetricsGraph/MetricsTooltip/index.d.ts +2 -8
  8. package/dist/MetricsGraph/MetricsTooltip/index.d.ts.map +1 -1
  9. package/dist/MetricsGraph/MetricsTooltip/index.js +46 -55
  10. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts +2 -5
  11. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts.map +1 -1
  12. package/dist/MetricsGraph/UtilizationMetrics/Throughput.js +126 -205
  13. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +9 -17
  14. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -1
  15. package/dist/MetricsGraph/UtilizationMetrics/index.js +149 -208
  16. package/dist/MetricsGraph/index.d.ts +19 -26
  17. package/dist/MetricsGraph/index.d.ts.map +1 -1
  18. package/dist/MetricsGraph/index.js +50 -115
  19. package/dist/ProfileFlameGraph/index.d.ts.map +1 -1
  20. package/dist/ProfileFlameGraph/index.js +3 -1
  21. package/dist/ProfileMetricsGraph/index.d.ts +1 -1
  22. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  23. package/dist/ProfileMetricsGraph/index.js +232 -23
  24. package/dist/ProfileSelector/MetricsGraphSection.d.ts +1 -4
  25. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  26. package/dist/ProfileSelector/MetricsGraphSection.js +8 -4
  27. package/dist/ProfileSelector/QueryControls.d.ts.map +1 -1
  28. package/dist/ProfileSelector/QueryControls.js +3 -2
  29. package/dist/ProfileSelector/index.d.ts +3 -6
  30. package/dist/ProfileSelector/index.d.ts.map +1 -1
  31. package/dist/ProfileSelector/index.js +2 -2
  32. package/dist/ProfileSource.d.ts +9 -6
  33. package/dist/ProfileSource.d.ts.map +1 -1
  34. package/dist/ProfileSource.js +23 -8
  35. package/dist/styles.css +1 -1
  36. package/dist/useQuery.js +1 -1
  37. package/package.json +6 -6
  38. package/src/GraphTooltipArrow/Content.tsx +2 -4
  39. package/src/MetricsGraph/MetricsContextMenu/index.tsx +78 -66
  40. package/src/MetricsGraph/MetricsTooltip/index.tsx +53 -210
  41. package/src/MetricsGraph/UtilizationMetrics/Throughput.tsx +242 -434
  42. package/src/MetricsGraph/UtilizationMetrics/index.tsx +312 -448
  43. package/src/MetricsGraph/index.tsx +99 -185
  44. package/src/ProfileFlameGraph/index.tsx +3 -1
  45. package/src/ProfileMetricsGraph/index.tsx +430 -37
  46. package/src/ProfileSelector/MetricsGraphSection.tsx +12 -8
  47. package/src/ProfileSelector/QueryControls.tsx +4 -1
  48. package/src/ProfileSelector/index.tsx +5 -5
  49. package/src/ProfileSource.tsx +34 -17
  50. package/src/useQuery.tsx +1 -1
@@ -13,262 +13,105 @@
13
13
 
14
14
  import {useEffect, useMemo, useState} from 'react';
15
15
 
16
- import {Icon} from '@iconify/react';
17
- import type {VirtualElement} from '@popperjs/core';
18
16
  import {usePopper} from 'react-popper';
19
17
 
20
- import {Label} from '@parca/client';
21
- import {TextWithTooltip, useParcaContext} from '@parca/components';
22
- import {formatDate, timePattern, valueFormatter} from '@parca/utilities';
23
-
24
- import {HighlightedSeries} from '../';
18
+ interface VirtualElement {
19
+ getBoundingClientRect: () => DOMRect;
20
+ }
25
21
 
26
22
  interface Props {
27
23
  x: number;
28
24
  y: number;
29
- highlighted: HighlightedSeries;
30
25
  contextElement: Element | null;
31
- sampleType: string;
32
- sampleUnit: string;
33
- delta: boolean;
34
- utilizationMetrics?: boolean;
35
- valuePrefix?: string;
26
+ content: React.ReactNode;
36
27
  }
37
28
 
38
29
  const virtualElement: VirtualElement = {
39
30
  getBoundingClientRect: () => {
40
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
41
- return {
31
+ const emptyRect: DOMRect = {
42
32
  width: 0,
43
33
  height: 0,
44
34
  top: 0,
45
- left: 0,
46
35
  right: 0,
47
36
  bottom: 0,
48
- } as DOMRect;
37
+ left: 0,
38
+ x: 0,
39
+ y: 0,
40
+ toJSON: () => ({}),
41
+ };
42
+ return emptyRect;
49
43
  },
50
44
  };
51
45
 
52
- function generateGetBoundingClientRect(contextElement: Element, x = 0, y = 0): () => DOMRect {
53
- const domRect = contextElement.getBoundingClientRect();
54
- return () =>
55
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
56
- ({
57
- width: 0,
58
- height: 0,
59
- top: domRect.y + y,
60
- left: domRect.x + x,
61
- right: domRect.x + x,
62
- bottom: domRect.y + y,
63
- } as DOMRect);
64
- }
65
-
66
- const MetricsTooltip = ({
67
- x,
68
- y,
69
- highlighted,
70
- contextElement,
71
- sampleType,
72
- sampleUnit,
73
- delta,
74
- utilizationMetrics = false,
75
- valuePrefix,
76
- }: Props): JSX.Element => {
77
- const {timezone} = useParcaContext();
46
+ const createDomRect = (x: number, y: number): DOMRect => {
47
+ const domRect: DOMRect = {
48
+ width: 0,
49
+ height: 0,
50
+ top: y,
51
+ right: x,
52
+ bottom: y,
53
+ left: x,
54
+ x,
55
+ y,
56
+ toJSON: () => ({}),
57
+ };
58
+ return domRect;
59
+ };
78
60
 
61
+ const MetricsTooltip = ({x, y, contextElement, content}: Props): JSX.Element => {
79
62
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
80
63
 
81
- const {styles, attributes, ...popperProps} = usePopper(virtualElement, popperElement, {
64
+ const {styles, attributes, update} = usePopper(virtualElement, popperElement, {
82
65
  placement: 'auto-start',
83
66
  strategy: 'absolute',
84
67
  modifiers: [
85
68
  {
86
69
  name: 'preventOverflow',
87
70
  options: {
88
- tether: false,
89
- altAxis: true,
71
+ boundary: contextElement ?? undefined,
90
72
  },
91
73
  },
92
74
  {
93
75
  name: 'offset',
94
76
  options: {
95
- offset: [30, 30],
77
+ offset: [15, 15],
96
78
  },
97
79
  },
98
80
  ],
99
81
  });
100
82
 
101
- const update = popperProps.update;
102
-
103
- const attributesMap = useMemo(() => {
104
- return highlighted.labels
105
- .filter(
106
- label =>
107
- label.name.startsWith('attributes.') && !label.name.startsWith('attributes_resource.')
108
- )
109
- .reduce<Record<string, string>>((acc, label) => {
110
- const key = label.name.replace('attributes.', '');
111
- acc[key] = label.value;
112
- return acc;
113
- }, {});
114
- }, [highlighted.labels]);
115
-
116
- const attributesResourceMap = useMemo(() => {
117
- return highlighted.labels
118
- .filter(label => label.name.startsWith('attributes_resource.'))
119
- .reduce<Record<string, string>>((acc, label) => {
120
- const key = label.name.replace('attributes_resource.', '');
121
- acc[key] = label.value;
122
- return acc;
123
- }, {});
124
- }, [highlighted.labels]);
83
+ useMemo(() => {
84
+ virtualElement.getBoundingClientRect = (): DOMRect => {
85
+ const domRect: DOMRect = (contextElement as Element)?.getBoundingClientRect() ?? {
86
+ width: 0,
87
+ height: 0,
88
+ top: 0,
89
+ right: 0,
90
+ bottom: 0,
91
+ left: 0,
92
+ x: 0,
93
+ y: 0,
94
+ toJSON: () => ({}),
95
+ };
96
+ return createDomRect(domRect.x + x, domRect.y + y);
97
+ };
98
+ }, [x, y, contextElement]);
125
99
 
126
100
  useEffect(() => {
127
- if (contextElement != null) {
128
- virtualElement.getBoundingClientRect = generateGetBoundingClientRect(contextElement, x, y);
129
- void update?.();
130
- }
131
- }, [x, y, contextElement, update]);
101
+ void update?.();
102
+ }, [x, y, update]);
132
103
 
133
- const nameLabel: Label | undefined = highlighted?.labels.find(e => e.name === '__name__');
134
- const highlightedNameLabel: Label = nameLabel !== undefined ? nameLabel : {name: '', value: ''};
104
+ // Don't render anything if content is null or undefined
105
+ if (content == null) {
106
+ return <></>;
107
+ }
135
108
 
136
109
  return (
137
110
  <div ref={setPopperElement} style={styles.popper} {...attributes.popper} className="z-50">
138
111
  <div className="flex max-w-lg">
139
112
  <div className="m-auto">
140
- <div
141
- className="rounded-lg border-gray-300 bg-gray-50 p-3 opacity-90 shadow-lg dark:border-gray-500 dark:bg-gray-900"
142
- style={{borderWidth: 1}}
143
- >
144
- <div className="flex flex-row">
145
- <div className="ml-2 mr-6">
146
- <span className="font-semibold">{highlightedNameLabel.value}</span>
147
- <span className="my-2 block text-gray-700 dark:text-gray-300">
148
- <table className="table-auto">
149
- <tbody>
150
- {delta ? (
151
- <>
152
- <tr>
153
- <td className="w-1/4 pr-3">Per&nbsp;Second</td>
154
- <td className="w-3/4">
155
- {valueFormatter(
156
- highlighted.valuePerSecond,
157
- sampleUnit === 'nanoseconds' && sampleType === 'cpu'
158
- ? 'CPU Cores'
159
- : sampleUnit,
160
- 5
161
- )}
162
- </td>
163
- </tr>
164
- <tr>
165
- <td className="w-1/4">Total</td>
166
- <td className="w-3/4">
167
- {valueFormatter(highlighted.value, sampleUnit, 2)}
168
- </td>
169
- </tr>
170
- </>
171
- ) : (
172
- <tr>
173
- <td className="w-1/4">
174
- {valuePrefix ?? ''}
175
- Value
176
- </td>
177
- <td className="w-3/4">
178
- {valueFormatter(highlighted.valuePerSecond, sampleUnit, 5)}
179
- </td>
180
- </tr>
181
- )}
182
- {highlighted.duration > 0 && (
183
- <tr>
184
- <td className="w-1/4">Duration</td>
185
- <td className="w-3/4">
186
- {valueFormatter(highlighted.duration, 'nanoseconds', 2)}
187
- </td>
188
- </tr>
189
- )}
190
- <tr>
191
- <td className="w-1/4">At</td>
192
- <td className="w-3/4">
193
- {formatDate(
194
- highlighted.timestamp,
195
- timePattern(timezone as string),
196
- timezone
197
- )}
198
- </td>
199
- </tr>
200
- </tbody>
201
- </table>
202
- </span>
203
- <span className="my-2 block text-gray-500">
204
- {utilizationMetrics ? (
205
- <>
206
- {Object.keys(attributesResourceMap).length > 0 && (
207
- <span className="text-sm font-bold text-gray-700 dark:text-white">
208
- Resource Attributes
209
- </span>
210
- )}
211
- <span className="my-2 block text-gray-500">
212
- {Object.keys(attributesResourceMap).map(name => (
213
- <div
214
- key={name}
215
- 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"
216
- >
217
- <TextWithTooltip
218
- text={`${name.replace('attributes.', '')}="${
219
- attributesResourceMap[name]
220
- }"`}
221
- maxTextLength={48}
222
- id={`tooltip-${name}-${attributesResourceMap[name]}`}
223
- />
224
- </div>
225
- ))}
226
- </span>
227
- {Object.keys(attributesMap).length > 0 && (
228
- <span className="text-sm font-bold text-gray-700 dark:text-white">
229
- Attributes
230
- </span>
231
- )}
232
- <span className="my-2 block text-gray-500">
233
- {Object.keys(attributesMap).map(name => (
234
- <div
235
- key={name}
236
- 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"
237
- >
238
- <TextWithTooltip
239
- text={`${name.replace('attributes.', '')}="${attributesMap[name]}"`}
240
- maxTextLength={48}
241
- id={`tooltip-${name}-${attributesMap[name]}`}
242
- />
243
- </div>
244
- ))}
245
- </span>
246
- </>
247
- ) : (
248
- <>
249
- {highlighted.labels
250
- .filter((label: Label) => label.name !== '__name__')
251
- .map((label: Label) => (
252
- <div
253
- key={label.name}
254
- 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"
255
- >
256
- <TextWithTooltip
257
- text={`${label.name}="${label.value}"`}
258
- maxTextLength={37}
259
- id={`tooltip-${label.name}`}
260
- />
261
- </div>
262
- ))}
263
- </>
264
- )}
265
- </span>
266
- <div className="flex w-full items-center gap-1 text-xs text-gray-500">
267
- <Icon icon="iconoir:mouse-button-right" />
268
- <div>Right click to add labels to query.</div>
269
- </div>
270
- </div>
271
- </div>
113
+ <div className="border border-gray-300 bg-gray-50 dark:border-gray-500 dark:bg-gray-900 rounded-lg shadow-lg px-3 py-2">
114
+ {content}
272
115
  </div>
273
116
  </div>
274
117
  </div>