@parca/profile 0.16.209 → 0.16.211
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 +8 -0
- package/dist/GraphTooltipArrow/Content.d.ts +12 -0
- package/dist/GraphTooltipArrow/Content.js +81 -0
- package/dist/GraphTooltipArrow/index.d.ts +2 -34
- package/dist/GraphTooltipArrow/index.js +4 -152
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +2 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +7 -8
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +3 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +6 -1
- package/dist/ProfileIcicleGraph/index.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +5 -5
- package/src/GraphTooltipArrow/Content.tsx +272 -0
- package/src/GraphTooltipArrow/index.tsx +5 -431
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +9 -4
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +16 -7
- package/src/ProfileIcicleGraph/index.tsx +1 -1
|
@@ -11,71 +11,20 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {useEffect,
|
|
14
|
+
import React, {useEffect, useState} from 'react';
|
|
15
15
|
|
|
16
|
-
import {Table} from 'apache-arrow';
|
|
17
16
|
import {pointer} from 'd3-selection';
|
|
18
|
-
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
|
19
17
|
import {usePopper} from 'react-popper';
|
|
20
18
|
|
|
21
|
-
import {
|
|
22
|
-
CallgraphNode,
|
|
23
|
-
CallgraphNodeMeta,
|
|
24
|
-
FlamegraphNode,
|
|
25
|
-
FlamegraphNodeMeta,
|
|
26
|
-
FlamegraphRootNode,
|
|
27
|
-
} from '@parca/client';
|
|
28
|
-
import {
|
|
29
|
-
Location,
|
|
30
|
-
Mapping,
|
|
31
|
-
Function as ParcaFunction,
|
|
32
|
-
} from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
|
|
33
19
|
import {useKeyDown} from '@parca/components';
|
|
34
|
-
import {selectHoveringRow, useAppSelector} from '@parca/store';
|
|
35
|
-
import {divide, getLastItem, valueFormatter} from '@parca/utilities';
|
|
36
|
-
|
|
37
|
-
import {hexifyAddress, truncateString, truncateStringReverse} from '../';
|
|
38
|
-
import {
|
|
39
|
-
FIELD_CUMULATIVE,
|
|
40
|
-
FIELD_DIFF,
|
|
41
|
-
FIELD_FUNCTION_FILE_NAME,
|
|
42
|
-
FIELD_FUNCTION_NAME,
|
|
43
|
-
FIELD_LOCATION_ADDRESS,
|
|
44
|
-
FIELD_MAPPING_BUILD_ID,
|
|
45
|
-
FIELD_MAPPING_FILE,
|
|
46
|
-
} from '../ProfileIcicleGraph/IcicleGraphArrow';
|
|
47
|
-
import {ExpandOnHover} from './ExpandOnHoverValue';
|
|
48
|
-
|
|
49
|
-
const NoData = (): React.JSX.Element => {
|
|
50
|
-
return <span className="rounded bg-gray-200 px-2 dark:bg-gray-800">Not available</span>;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
interface ExtendedCallgraphNodeMeta extends CallgraphNodeMeta {
|
|
54
|
-
lineIndex: number;
|
|
55
|
-
locationIndex: number;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface HoveringNode extends FlamegraphRootNode, FlamegraphNode, CallgraphNode {
|
|
59
|
-
diff: bigint;
|
|
60
|
-
meta?: FlamegraphNodeMeta | ExtendedCallgraphNodeMeta;
|
|
61
|
-
}
|
|
62
20
|
|
|
63
21
|
interface GraphTooltipProps {
|
|
64
|
-
|
|
22
|
+
children: React.ReactNode;
|
|
65
23
|
x?: number;
|
|
66
24
|
y?: number;
|
|
67
|
-
unit: string;
|
|
68
|
-
total: bigint;
|
|
69
|
-
totalUnfiltered: bigint;
|
|
70
|
-
hoveringNode?: HoveringNode;
|
|
71
25
|
contextElement: Element | null;
|
|
72
26
|
isFixed?: boolean;
|
|
73
27
|
virtualContextElement?: boolean;
|
|
74
|
-
strings?: string[];
|
|
75
|
-
mappings?: Mapping[];
|
|
76
|
-
locations?: Location[];
|
|
77
|
-
functions?: ParcaFunction[];
|
|
78
|
-
type?: string;
|
|
79
28
|
}
|
|
80
29
|
|
|
81
30
|
const virtualElement = {
|
|
@@ -105,369 +54,14 @@ function generateGetBoundingClientRect(contextElement: Element, x = 0, y = 0): (
|
|
|
105
54
|
} as DOMRect);
|
|
106
55
|
}
|
|
107
56
|
|
|
108
|
-
const TooltipMetaInfo = ({
|
|
109
|
-
hoveringNode,
|
|
110
|
-
onCopy,
|
|
111
|
-
strings,
|
|
112
|
-
mappings,
|
|
113
|
-
locations,
|
|
114
|
-
functions,
|
|
115
|
-
type = 'flamegraph',
|
|
116
|
-
}: {
|
|
117
|
-
hoveringNode: HoveringNode;
|
|
118
|
-
onCopy: () => void;
|
|
119
|
-
strings?: string[];
|
|
120
|
-
mappings?: Mapping[];
|
|
121
|
-
locations?: Location[];
|
|
122
|
-
functions?: ParcaFunction[];
|
|
123
|
-
type?: string;
|
|
124
|
-
}): React.JSX.Element => {
|
|
125
|
-
// populate meta from the flamegraph metadata tables
|
|
126
|
-
if (
|
|
127
|
-
type === 'flamegraph' &&
|
|
128
|
-
locations !== undefined &&
|
|
129
|
-
hoveringNode.meta?.locationIndex !== undefined &&
|
|
130
|
-
hoveringNode.meta.locationIndex !== 0
|
|
131
|
-
) {
|
|
132
|
-
const location = locations[hoveringNode.meta.locationIndex - 1];
|
|
133
|
-
hoveringNode.meta.location = location;
|
|
134
|
-
|
|
135
|
-
if (location !== undefined) {
|
|
136
|
-
if (
|
|
137
|
-
mappings !== undefined &&
|
|
138
|
-
location.mappingIndex !== undefined &&
|
|
139
|
-
location.mappingIndex !== 0
|
|
140
|
-
) {
|
|
141
|
-
const mapping = mappings[location.mappingIndex - 1];
|
|
142
|
-
if (strings !== undefined && mapping !== undefined) {
|
|
143
|
-
mapping.file =
|
|
144
|
-
mapping?.fileStringIndex !== undefined ? strings[mapping.fileStringIndex] : '';
|
|
145
|
-
mapping.buildId =
|
|
146
|
-
mapping?.buildIdStringIndex !== undefined ? strings[mapping.buildIdStringIndex] : '';
|
|
147
|
-
}
|
|
148
|
-
hoveringNode.meta.mapping = mapping;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
functions !== undefined &&
|
|
153
|
-
location.lines !== undefined &&
|
|
154
|
-
hoveringNode.meta.lineIndex !== undefined &&
|
|
155
|
-
hoveringNode.meta.lineIndex < location.lines.length
|
|
156
|
-
) {
|
|
157
|
-
const func = functions[location.lines[hoveringNode.meta.lineIndex].functionIndex - 1];
|
|
158
|
-
if (strings !== undefined) {
|
|
159
|
-
func.name = strings[func.nameStringIndex];
|
|
160
|
-
func.systemName = strings[func.systemNameStringIndex];
|
|
161
|
-
func.filename = strings[func.filenameStringIndex];
|
|
162
|
-
}
|
|
163
|
-
hoveringNode.meta.function = func;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const getTextForFile = (hoveringNode: HoveringNode): string => {
|
|
169
|
-
if (hoveringNode.meta?.function == null) return '<unknown>';
|
|
170
|
-
|
|
171
|
-
return `${hoveringNode.meta.function.filename} ${
|
|
172
|
-
hoveringNode.meta.line?.line !== undefined && hoveringNode.meta.line?.line !== 0n
|
|
173
|
-
? ` +${hoveringNode.meta.line.line.toString()}`
|
|
174
|
-
: `${
|
|
175
|
-
hoveringNode.meta.function?.startLine !== undefined &&
|
|
176
|
-
hoveringNode.meta.function?.startLine !== 0n
|
|
177
|
-
? ` +${hoveringNode.meta.function.startLine}`
|
|
178
|
-
: ''
|
|
179
|
-
}`
|
|
180
|
-
}`;
|
|
181
|
-
};
|
|
182
|
-
const file = getTextForFile(hoveringNode);
|
|
183
|
-
|
|
184
|
-
return (
|
|
185
|
-
<>
|
|
186
|
-
<tr>
|
|
187
|
-
<td className="w-1/4">File</td>
|
|
188
|
-
<td className="w-3/4 break-all">
|
|
189
|
-
{hoveringNode.meta?.function?.filename == null ||
|
|
190
|
-
hoveringNode.meta?.function.filename === '' ? (
|
|
191
|
-
<NoData />
|
|
192
|
-
) : (
|
|
193
|
-
<CopyToClipboard onCopy={onCopy} text={file}>
|
|
194
|
-
<button className="cursor-pointer whitespace-nowrap text-left">
|
|
195
|
-
<ExpandOnHover value={file} displayValue={truncateStringReverse(file, 40)} />
|
|
196
|
-
</button>
|
|
197
|
-
</CopyToClipboard>
|
|
198
|
-
)}
|
|
199
|
-
</td>
|
|
200
|
-
</tr>
|
|
201
|
-
|
|
202
|
-
<tr>
|
|
203
|
-
<td className="w-1/4">Address</td>
|
|
204
|
-
<td className="w-3/4 break-all">
|
|
205
|
-
{hoveringNode.meta?.location?.address == null ||
|
|
206
|
-
hoveringNode.meta?.location.address === 0n ? (
|
|
207
|
-
<NoData />
|
|
208
|
-
) : (
|
|
209
|
-
<CopyToClipboard
|
|
210
|
-
onCopy={onCopy}
|
|
211
|
-
text={hexifyAddress(hoveringNode.meta.location.address)}
|
|
212
|
-
>
|
|
213
|
-
<button className="cursor-pointer">
|
|
214
|
-
{hexifyAddress(hoveringNode.meta.location.address)}
|
|
215
|
-
</button>
|
|
216
|
-
</CopyToClipboard>
|
|
217
|
-
)}
|
|
218
|
-
</td>
|
|
219
|
-
</tr>
|
|
220
|
-
<tr>
|
|
221
|
-
<td className="w-1/4">Binary</td>
|
|
222
|
-
<td className="w-3/4 break-all">
|
|
223
|
-
{hoveringNode.meta?.mapping == null || hoveringNode.meta.mapping.file === '' ? (
|
|
224
|
-
<NoData />
|
|
225
|
-
) : (
|
|
226
|
-
<CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.mapping.file}>
|
|
227
|
-
<button className="cursor-pointer">
|
|
228
|
-
{getLastItem(hoveringNode.meta.mapping.file)}
|
|
229
|
-
</button>
|
|
230
|
-
</CopyToClipboard>
|
|
231
|
-
)}
|
|
232
|
-
</td>
|
|
233
|
-
</tr>
|
|
234
|
-
|
|
235
|
-
<tr>
|
|
236
|
-
<td className="w-1/4">Build Id</td>
|
|
237
|
-
<td className="w-3/4 break-all">
|
|
238
|
-
{hoveringNode.meta?.mapping == null || hoveringNode.meta?.mapping.buildId === '' ? (
|
|
239
|
-
<NoData />
|
|
240
|
-
) : (
|
|
241
|
-
<CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.mapping.buildId}>
|
|
242
|
-
<button className="cursor-pointer">
|
|
243
|
-
{truncateString(getLastItem(hoveringNode.meta.mapping.buildId) as string, 28)}
|
|
244
|
-
</button>
|
|
245
|
-
</CopyToClipboard>
|
|
246
|
-
)}
|
|
247
|
-
</td>
|
|
248
|
-
</tr>
|
|
249
|
-
</>
|
|
250
|
-
);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
254
|
-
|
|
255
|
-
export const GraphTooltipContent = ({
|
|
256
|
-
hoveringNode,
|
|
257
|
-
unit,
|
|
258
|
-
total,
|
|
259
|
-
totalUnfiltered,
|
|
260
|
-
isFixed,
|
|
261
|
-
strings,
|
|
262
|
-
mappings,
|
|
263
|
-
locations,
|
|
264
|
-
functions,
|
|
265
|
-
type = 'flamegraph',
|
|
266
|
-
}: {
|
|
267
|
-
hoveringNode: HoveringNode;
|
|
268
|
-
unit: string;
|
|
269
|
-
total: bigint;
|
|
270
|
-
totalUnfiltered: bigint;
|
|
271
|
-
isFixed: boolean;
|
|
272
|
-
strings?: string[];
|
|
273
|
-
mappings?: Mapping[];
|
|
274
|
-
locations?: Location[];
|
|
275
|
-
functions?: ParcaFunction[];
|
|
276
|
-
type?: string;
|
|
277
|
-
}): React.JSX.Element => {
|
|
278
|
-
const [isCopied, setIsCopied] = useState<boolean>(false);
|
|
279
|
-
|
|
280
|
-
const onCopy = (): void => {
|
|
281
|
-
setIsCopied(true);
|
|
282
|
-
|
|
283
|
-
if (timeoutHandle !== null) {
|
|
284
|
-
clearTimeout(timeoutHandle);
|
|
285
|
-
}
|
|
286
|
-
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
const hoveringNodeCumulative = hoveringNode.cumulative;
|
|
290
|
-
const diff = hoveringNode.diff;
|
|
291
|
-
const prevValue = hoveringNodeCumulative - diff;
|
|
292
|
-
const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
|
|
293
|
-
const diffSign = diff > 0 ? '+' : '';
|
|
294
|
-
const diffValueText = diffSign + valueFormatter(diff, unit, 1);
|
|
295
|
-
const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
|
|
296
|
-
const diffText = `${diffValueText} (${diffPercentageText})`;
|
|
297
|
-
|
|
298
|
-
const getTextForCumulative = (hoveringNodeCumulative: bigint): string => {
|
|
299
|
-
const filtered =
|
|
300
|
-
totalUnfiltered > total
|
|
301
|
-
? ` / ${(100 * divide(hoveringNodeCumulative, total)).toFixed(2)}% of filtered`
|
|
302
|
-
: '';
|
|
303
|
-
return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
|
|
304
|
-
(${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
return (
|
|
308
|
-
<div className={`flex text-sm ${isFixed ? 'w-full' : ''}`}>
|
|
309
|
-
<div className={`m-auto w-full ${isFixed ? 'w-full' : ''}`}>
|
|
310
|
-
<div className="min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900">
|
|
311
|
-
<div className="flex flex-row">
|
|
312
|
-
<div className="mx-2">
|
|
313
|
-
<div className="flex h-10 items-center break-all font-semibold">
|
|
314
|
-
{hoveringNode.meta === undefined ? (
|
|
315
|
-
<p>root</p>
|
|
316
|
-
) : (
|
|
317
|
-
<>
|
|
318
|
-
{hoveringNode.meta.function !== undefined &&
|
|
319
|
-
hoveringNode.meta.function.name !== '' ? (
|
|
320
|
-
<CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.function.name}>
|
|
321
|
-
<button className="cursor-pointer text-left">
|
|
322
|
-
{hoveringNode.meta.function.name}
|
|
323
|
-
</button>
|
|
324
|
-
</CopyToClipboard>
|
|
325
|
-
) : (
|
|
326
|
-
<>
|
|
327
|
-
{hoveringNode.meta.location !== undefined &&
|
|
328
|
-
hoveringNode.meta.location.address !== 0n ? (
|
|
329
|
-
<CopyToClipboard
|
|
330
|
-
onCopy={onCopy}
|
|
331
|
-
text={hexifyAddress(hoveringNode.meta.location.address)}
|
|
332
|
-
>
|
|
333
|
-
<button className="cursor-pointer text-left">
|
|
334
|
-
{hexifyAddress(hoveringNode.meta.location.address)}
|
|
335
|
-
</button>
|
|
336
|
-
</CopyToClipboard>
|
|
337
|
-
) : (
|
|
338
|
-
<p>unknown</p>
|
|
339
|
-
)}
|
|
340
|
-
</>
|
|
341
|
-
)}
|
|
342
|
-
</>
|
|
343
|
-
)}
|
|
344
|
-
</div>
|
|
345
|
-
<table className="my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300">
|
|
346
|
-
<tbody>
|
|
347
|
-
<tr>
|
|
348
|
-
<td className="w-1/4">Cumulative</td>
|
|
349
|
-
|
|
350
|
-
<td className="w-3/4">
|
|
351
|
-
<CopyToClipboard
|
|
352
|
-
onCopy={onCopy}
|
|
353
|
-
text={getTextForCumulative(hoveringNodeCumulative)}
|
|
354
|
-
>
|
|
355
|
-
<button className="cursor-pointer">
|
|
356
|
-
{getTextForCumulative(hoveringNodeCumulative)}
|
|
357
|
-
</button>
|
|
358
|
-
</CopyToClipboard>
|
|
359
|
-
</td>
|
|
360
|
-
</tr>
|
|
361
|
-
{hoveringNode.diff !== undefined && diff !== 0n && (
|
|
362
|
-
<tr>
|
|
363
|
-
<td className="w-1/4">Diff</td>
|
|
364
|
-
<td className="w-3/4">
|
|
365
|
-
<CopyToClipboard onCopy={onCopy} text={diffText}>
|
|
366
|
-
<button className="cursor-pointer">{diffText}</button>
|
|
367
|
-
</CopyToClipboard>
|
|
368
|
-
</td>
|
|
369
|
-
</tr>
|
|
370
|
-
)}
|
|
371
|
-
<TooltipMetaInfo
|
|
372
|
-
onCopy={onCopy}
|
|
373
|
-
hoveringNode={hoveringNode}
|
|
374
|
-
strings={strings}
|
|
375
|
-
mappings={mappings}
|
|
376
|
-
locations={locations}
|
|
377
|
-
functions={functions}
|
|
378
|
-
type={type}
|
|
379
|
-
/>
|
|
380
|
-
</tbody>
|
|
381
|
-
</table>
|
|
382
|
-
</div>
|
|
383
|
-
</div>
|
|
384
|
-
<span className="mx-2 block text-xs text-gray-500">
|
|
385
|
-
{isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.'}
|
|
386
|
-
</span>
|
|
387
|
-
</div>
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
);
|
|
391
|
-
};
|
|
392
|
-
|
|
393
57
|
const GraphTooltip = ({
|
|
394
|
-
|
|
58
|
+
children,
|
|
395
59
|
x,
|
|
396
60
|
y,
|
|
397
|
-
unit,
|
|
398
|
-
total,
|
|
399
|
-
totalUnfiltered,
|
|
400
61
|
contextElement,
|
|
401
62
|
isFixed = false,
|
|
402
63
|
virtualContextElement = true,
|
|
403
|
-
strings,
|
|
404
|
-
mappings,
|
|
405
|
-
locations,
|
|
406
|
-
functions,
|
|
407
|
-
type = 'flamegraph',
|
|
408
64
|
}: GraphTooltipProps): React.JSX.Element => {
|
|
409
|
-
const hoveringNodeState = useAppSelector(selectHoveringRow);
|
|
410
|
-
const hoveringNode = useMemo<HoveringNode | undefined>(() => {
|
|
411
|
-
const s = hoveringNodeState;
|
|
412
|
-
if (s === undefined) {
|
|
413
|
-
return undefined;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const mappingFile: string = table.getChild(FIELD_MAPPING_FILE)?.get(s.row) ?? '';
|
|
417
|
-
const mappingBuildID: string = table.getChild(FIELD_MAPPING_BUILD_ID)?.get(s.row) ?? '';
|
|
418
|
-
const locationAddress: bigint = table.getChild(FIELD_LOCATION_ADDRESS)?.get(s.row) ?? 0n;
|
|
419
|
-
const functionName: string = table.getChild(FIELD_FUNCTION_NAME)?.get(s.row) ?? '';
|
|
420
|
-
const functionFileName: string = table.getChild(FIELD_FUNCTION_FILE_NAME)?.get(s.row) ?? '';
|
|
421
|
-
const cumulative: bigint = table.getChild(FIELD_CUMULATIVE)?.get(s.row) ?? 0n;
|
|
422
|
-
const diff: bigint = table.getChild(FIELD_DIFF)?.get(s.row) ?? 0n;
|
|
423
|
-
|
|
424
|
-
const fhn: HoveringNode = {
|
|
425
|
-
id: '',
|
|
426
|
-
flat: 0n,
|
|
427
|
-
children: [],
|
|
428
|
-
|
|
429
|
-
cumulative,
|
|
430
|
-
diff: diff === null ? 0n : diff,
|
|
431
|
-
meta: {
|
|
432
|
-
locationIndex: 0,
|
|
433
|
-
lineIndex: 0,
|
|
434
|
-
mapping: {
|
|
435
|
-
id: '',
|
|
436
|
-
start: 0n,
|
|
437
|
-
limit: 0n,
|
|
438
|
-
offset: 0n,
|
|
439
|
-
file: mappingFile,
|
|
440
|
-
buildId: mappingBuildID,
|
|
441
|
-
hasFunctions: false,
|
|
442
|
-
hasFilenames: false,
|
|
443
|
-
hasLineNumbers: false,
|
|
444
|
-
hasInlineFrames: false,
|
|
445
|
-
fileStringIndex: 0,
|
|
446
|
-
buildIdStringIndex: 0,
|
|
447
|
-
},
|
|
448
|
-
location: {
|
|
449
|
-
id: '',
|
|
450
|
-
address: locationAddress,
|
|
451
|
-
mappingId: '',
|
|
452
|
-
isFolded: false,
|
|
453
|
-
lines: [],
|
|
454
|
-
mappingIndex: 0,
|
|
455
|
-
},
|
|
456
|
-
function: {
|
|
457
|
-
id: '',
|
|
458
|
-
startLine: 0n,
|
|
459
|
-
name: functionName,
|
|
460
|
-
systemName: '',
|
|
461
|
-
filename: functionFileName,
|
|
462
|
-
nameStringIndex: 0,
|
|
463
|
-
systemNameStringIndex: 0,
|
|
464
|
-
filenameStringIndex: 0,
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
};
|
|
468
|
-
return fhn;
|
|
469
|
-
}, [table, hoveringNodeState]);
|
|
470
|
-
|
|
471
65
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
472
66
|
|
|
473
67
|
const {styles, attributes, ...popperProps} = usePopper(
|
|
@@ -532,31 +126,11 @@ const GraphTooltip = ({
|
|
|
532
126
|
};
|
|
533
127
|
}, [contextElement, popperProps, isShiftDown, x, y]);
|
|
534
128
|
|
|
535
|
-
if (hoveringNode === undefined || hoveringNode == null) return <></>;
|
|
536
|
-
|
|
537
129
|
return isFixed ? (
|
|
538
|
-
|
|
539
|
-
hoveringNode={hoveringNode}
|
|
540
|
-
unit={unit}
|
|
541
|
-
total={total}
|
|
542
|
-
totalUnfiltered={totalUnfiltered}
|
|
543
|
-
isFixed={isFixed}
|
|
544
|
-
type={type}
|
|
545
|
-
/>
|
|
130
|
+
<>{children}</>
|
|
546
131
|
) : (
|
|
547
132
|
<div ref={setPopperElement} style={styles.popper} {...attributes.popper}>
|
|
548
|
-
|
|
549
|
-
hoveringNode={hoveringNode}
|
|
550
|
-
unit={unit}
|
|
551
|
-
total={total}
|
|
552
|
-
totalUnfiltered={totalUnfiltered}
|
|
553
|
-
isFixed={isFixed}
|
|
554
|
-
strings={strings}
|
|
555
|
-
mappings={mappings}
|
|
556
|
-
locations={locations}
|
|
557
|
-
functions={functions}
|
|
558
|
-
type={type}
|
|
559
|
-
/>
|
|
133
|
+
{children}
|
|
560
134
|
</div>
|
|
561
135
|
);
|
|
562
136
|
};
|
|
@@ -17,7 +17,7 @@ import {Table} from 'apache-arrow';
|
|
|
17
17
|
import cx from 'classnames';
|
|
18
18
|
|
|
19
19
|
import {useKeyDown} from '@parca/components';
|
|
20
|
-
import {selectBinaries,
|
|
20
|
+
import {selectBinaries, useAppSelector} from '@parca/store';
|
|
21
21
|
import {isSearchMatch, scaleLinear} from '@parca/utilities';
|
|
22
22
|
|
|
23
23
|
import {
|
|
@@ -44,6 +44,7 @@ interface IcicleGraphNodesProps {
|
|
|
44
44
|
level: number;
|
|
45
45
|
curPath: string[];
|
|
46
46
|
setCurPath: (path: string[]) => void;
|
|
47
|
+
setHoveringRow: (row: number | null) => void;
|
|
47
48
|
path: string[];
|
|
48
49
|
xScale: (value: bigint) => number;
|
|
49
50
|
searchString?: string;
|
|
@@ -64,6 +65,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
|
|
|
64
65
|
level,
|
|
65
66
|
path,
|
|
66
67
|
setCurPath,
|
|
68
|
+
setHoveringRow,
|
|
67
69
|
curPath,
|
|
68
70
|
sortBy,
|
|
69
71
|
searchString,
|
|
@@ -100,6 +102,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
|
|
|
100
102
|
height={RowHeight}
|
|
101
103
|
path={path}
|
|
102
104
|
setCurPath={setCurPath}
|
|
105
|
+
setHoveringRow={setHoveringRow}
|
|
103
106
|
level={level}
|
|
104
107
|
curPath={curPath}
|
|
105
108
|
total={total}
|
|
@@ -132,6 +135,7 @@ interface IcicleNodeProps {
|
|
|
132
135
|
path: string[];
|
|
133
136
|
total: bigint;
|
|
134
137
|
setCurPath: (path: string[]) => void;
|
|
138
|
+
setHoveringRow: (row: number | null) => void;
|
|
135
139
|
xScale: (value: bigint) => number;
|
|
136
140
|
isRoot?: boolean;
|
|
137
141
|
searchString?: string;
|
|
@@ -166,12 +170,12 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
166
170
|
xScale,
|
|
167
171
|
isRoot = false,
|
|
168
172
|
searchString,
|
|
173
|
+
setHoveringRow,
|
|
169
174
|
sortBy,
|
|
170
175
|
darkMode,
|
|
171
176
|
compareMode,
|
|
172
177
|
}: IcicleNodeProps): React.JSX.Element {
|
|
173
178
|
const {isShiftDown} = useKeyDown();
|
|
174
|
-
const dispatch = useAppDispatch();
|
|
175
179
|
|
|
176
180
|
// get the columns to read from
|
|
177
181
|
const mappingColumn = table.getChild(FIELD_MAPPING_FILE);
|
|
@@ -272,12 +276,12 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
272
276
|
|
|
273
277
|
const onMouseEnter = (): void => {
|
|
274
278
|
if (isShiftDown) return;
|
|
275
|
-
|
|
279
|
+
setHoveringRow(row);
|
|
276
280
|
};
|
|
277
281
|
|
|
278
282
|
const onMouseLeave = (): void => {
|
|
279
283
|
if (isShiftDown) return;
|
|
280
|
-
|
|
284
|
+
setHoveringRow(null);
|
|
281
285
|
};
|
|
282
286
|
|
|
283
287
|
return (
|
|
@@ -326,6 +330,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
326
330
|
path={nextPath}
|
|
327
331
|
curPath={nextCurPath}
|
|
328
332
|
setCurPath={setCurPath}
|
|
333
|
+
setHoveringRow={setHoveringRow}
|
|
329
334
|
searchString={searchString}
|
|
330
335
|
sortBy={sortBy}
|
|
331
336
|
darkMode={darkMode}
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from '@parca/utilities';
|
|
33
33
|
|
|
34
34
|
import GraphTooltipArrow from '../../GraphTooltipArrow';
|
|
35
|
+
import GraphTooltipArrowContent from '../../GraphTooltipArrow/Content';
|
|
35
36
|
import ColorStackLegend from './ColorStackLegend';
|
|
36
37
|
import {IcicleNode, RowHeight, mappingColors} from './IcicleGraphNodes';
|
|
37
38
|
import {extractFeature} from './utils';
|
|
@@ -39,9 +40,12 @@ import {extractFeature} from './utils';
|
|
|
39
40
|
export const FIELD_MAPPING_FILE = 'mapping_file';
|
|
40
41
|
export const FIELD_MAPPING_BUILD_ID = 'mapping_build_id';
|
|
41
42
|
export const FIELD_LOCATION_ADDRESS = 'location_address';
|
|
43
|
+
export const FIELD_LOCATION_LINE = 'location_line';
|
|
42
44
|
export const FIELD_FUNCTION_NAME = 'function_name';
|
|
43
45
|
export const FIELD_FUNCTION_FILE_NAME = 'function_file_name';
|
|
46
|
+
export const FIELD_FUNCTION_START_LINE = 'function_startline';
|
|
44
47
|
export const FIELD_CHILDREN = 'children';
|
|
48
|
+
export const FIELD_LABELS = 'labels';
|
|
45
49
|
export const FIELD_CUMULATIVE = 'cumulative';
|
|
46
50
|
export const FIELD_DIFF = 'diff';
|
|
47
51
|
|
|
@@ -73,6 +77,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
73
77
|
const isDarkMode = useAppSelector(selectDarkMode);
|
|
74
78
|
|
|
75
79
|
const [height, setHeight] = useState(0);
|
|
80
|
+
const [hoveringRow, setHoveringRow] = useState<number | null>(null);
|
|
76
81
|
const sortBy = FIELD_FUNCTION_NAME; // TODO: make this configurable via UI
|
|
77
82
|
const svg = useRef(null);
|
|
78
83
|
const ref = useRef<SVGGElement>(null);
|
|
@@ -166,13 +171,16 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
166
171
|
navigateTo={navigateTo}
|
|
167
172
|
compareMode={compareMode}
|
|
168
173
|
/>
|
|
169
|
-
<GraphTooltipArrow
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
<GraphTooltipArrow contextElement={svg.current}>
|
|
175
|
+
<GraphTooltipArrowContent
|
|
176
|
+
table={table}
|
|
177
|
+
row={hoveringRow}
|
|
178
|
+
isFixed={false}
|
|
179
|
+
total={total}
|
|
180
|
+
totalUnfiltered={total + filtered}
|
|
181
|
+
unit={sampleUnit}
|
|
182
|
+
/>
|
|
183
|
+
</GraphTooltipArrow>
|
|
176
184
|
<svg
|
|
177
185
|
className="font-robotoMono"
|
|
178
186
|
width={width}
|
|
@@ -198,6 +206,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
198
206
|
level={0}
|
|
199
207
|
isRoot={true}
|
|
200
208
|
searchString={currentSearchString}
|
|
209
|
+
setHoveringRow={setHoveringRow}
|
|
201
210
|
sortBy={sortBy}
|
|
202
211
|
darkMode={isDarkMode}
|
|
203
212
|
compareMode={compareMode}
|