@parca/profile 0.16.251 → 0.16.252
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.js +20 -89
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.d.ts +12 -0
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.js +63 -0
- package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts +19 -0
- package/dist/GraphTooltipArrow/useGraphTooltip/index.js +38 -0
- package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.d.ts +19 -0
- package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +94 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +3 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +8 -1
- package/dist/styles.css +1 -1
- package/package.json +5 -5
- package/src/GraphTooltipArrow/Content.tsx +39 -130
- package/src/GraphTooltipArrow/DockedGraphTooltip/index.tsx +249 -0
- package/src/GraphTooltipArrow/useGraphTooltip/index.ts +76 -0
- package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +153 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +18 -5
- package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +15 -1
|
@@ -18,27 +18,14 @@ import cx from 'classnames';
|
|
|
18
18
|
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
|
19
19
|
import {Tooltip} from 'react-tooltip';
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
21
|
+
import {Button, IconButton, useParcaContext} from '@parca/components';
|
|
22
|
+
import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
|
|
23
|
+
import {getLastItem, type NavigateFunction} from '@parca/utilities';
|
|
24
24
|
|
|
25
|
-
import {
|
|
26
|
-
FIELD_CUMULATIVE,
|
|
27
|
-
FIELD_DIFF,
|
|
28
|
-
FIELD_FUNCTION_FILE_NAME,
|
|
29
|
-
FIELD_FUNCTION_START_LINE,
|
|
30
|
-
FIELD_LABELS,
|
|
31
|
-
FIELD_LOCATION_ADDRESS,
|
|
32
|
-
FIELD_LOCATION_LINE,
|
|
33
|
-
FIELD_MAPPING_BUILD_ID,
|
|
34
|
-
FIELD_MAPPING_FILE,
|
|
35
|
-
} from '../ProfileIcicleGraph/IcicleGraphArrow';
|
|
36
|
-
import {arrowToString, nodeLabel} from '../ProfileIcicleGraph/IcicleGraphArrow/utils';
|
|
37
|
-
import {ProfileSource} from '../ProfileSource';
|
|
38
|
-
import {useProfileViewContext} from '../ProfileView/ProfileViewContext';
|
|
39
|
-
import {useQuery} from '../useQuery';
|
|
40
25
|
import {hexifyAddress, truncateString, truncateStringReverse} from '../utils';
|
|
41
26
|
import {ExpandOnHover} from './ExpandOnHoverValue';
|
|
27
|
+
import {useGraphTooltip} from './useGraphTooltip';
|
|
28
|
+
import {useGraphTooltipMetaInfo} from './useGraphTooltipMetaInfo';
|
|
42
29
|
|
|
43
30
|
let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
44
31
|
|
|
@@ -69,14 +56,20 @@ const GraphTooltipArrowContent = ({
|
|
|
69
56
|
}: GraphTooltipArrowContentProps): React.JSX.Element => {
|
|
70
57
|
const [isCopied, setIsCopied] = useState<boolean>(false);
|
|
71
58
|
|
|
72
|
-
|
|
59
|
+
const graphTooltipData = useGraphTooltip({
|
|
60
|
+
table,
|
|
61
|
+
unit,
|
|
62
|
+
total,
|
|
63
|
+
totalUnfiltered,
|
|
64
|
+
row,
|
|
65
|
+
level,
|
|
66
|
+
});
|
|
67
|
+
const [_, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
|
|
68
|
+
|
|
69
|
+
if (graphTooltipData === null) {
|
|
73
70
|
return <></>;
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
const locationAddress: bigint = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
|
|
77
|
-
const cumulative: bigint = BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row)) ?? 0n;
|
|
78
|
-
const diff: bigint = BigInt(table.getChild(FIELD_DIFF)?.get(row)) ?? 0n;
|
|
79
|
-
|
|
80
73
|
const onCopy = (): void => {
|
|
81
74
|
setIsCopied(true);
|
|
82
75
|
|
|
@@ -86,23 +79,7 @@ const GraphTooltipArrowContent = ({
|
|
|
86
79
|
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
87
80
|
};
|
|
88
81
|
|
|
89
|
-
const
|
|
90
|
-
const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
|
|
91
|
-
const diffSign = diff > 0 ? '+' : '';
|
|
92
|
-
const diffValueText = diffSign + valueFormatter(diff, unit, 1);
|
|
93
|
-
const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
|
|
94
|
-
const diffText = `${diffValueText} (${diffPercentageText})`;
|
|
95
|
-
|
|
96
|
-
const name = nodeLabel(table, row, level, false);
|
|
97
|
-
|
|
98
|
-
const getTextForCumulative = (hoveringNodeCumulative: bigint): string => {
|
|
99
|
-
const filtered =
|
|
100
|
-
totalUnfiltered > total
|
|
101
|
-
? ` / ${(100 * divide(hoveringNodeCumulative, total)).toFixed(2)}% of filtered`
|
|
102
|
-
: '';
|
|
103
|
-
return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
|
|
104
|
-
(${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
|
|
105
|
-
};
|
|
82
|
+
const {name, locationAddress, cumulativeText, diffText, diff, row: rowNumber} = graphTooltipData;
|
|
106
83
|
|
|
107
84
|
return (
|
|
108
85
|
<div className={`flex text-sm ${isFixed ? 'w-full' : ''}`}>
|
|
@@ -110,7 +87,7 @@ const GraphTooltipArrowContent = ({
|
|
|
110
87
|
<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">
|
|
111
88
|
<div className="flex flex-row">
|
|
112
89
|
<div className="mx-2">
|
|
113
|
-
<div className="flex h-10 items-
|
|
90
|
+
<div className="flex h-10 items-start justify-between gap-4 break-all font-semibold">
|
|
114
91
|
{row === 0 ? (
|
|
115
92
|
<p>root</p>
|
|
116
93
|
) : (
|
|
@@ -134,6 +111,11 @@ const GraphTooltipArrowContent = ({
|
|
|
134
111
|
)}
|
|
135
112
|
</>
|
|
136
113
|
)}
|
|
114
|
+
<IconButton
|
|
115
|
+
onClick={() => setIsDocked(true)}
|
|
116
|
+
icon="mdi:dock-bottom"
|
|
117
|
+
title="Dock MetaInfo Panel"
|
|
118
|
+
/>
|
|
137
119
|
</div>
|
|
138
120
|
<table className="my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300">
|
|
139
121
|
<tbody>
|
|
@@ -141,10 +123,8 @@ const GraphTooltipArrowContent = ({
|
|
|
141
123
|
<td className="w-1/4">Cumulative</td>
|
|
142
124
|
|
|
143
125
|
<td className="w-3/4">
|
|
144
|
-
<CopyToClipboard onCopy={onCopy} text={
|
|
145
|
-
<button className="cursor-pointer">
|
|
146
|
-
{getTextForCumulative(cumulative)}
|
|
147
|
-
</button>
|
|
126
|
+
<CopyToClipboard onCopy={onCopy} text={cumulativeText}>
|
|
127
|
+
<button className="cursor-pointer">{cumulativeText}</button>
|
|
148
128
|
</CopyToClipboard>
|
|
149
129
|
</td>
|
|
150
130
|
</tr>
|
|
@@ -160,7 +140,7 @@ const GraphTooltipArrowContent = ({
|
|
|
160
140
|
)}
|
|
161
141
|
<TooltipMetaInfo
|
|
162
142
|
table={table}
|
|
163
|
-
row={
|
|
143
|
+
row={rowNumber}
|
|
164
144
|
onCopy={onCopy}
|
|
165
145
|
navigateTo={navigateTo}
|
|
166
146
|
/>
|
|
@@ -190,57 +170,18 @@ const TooltipMetaInfo = ({
|
|
|
190
170
|
onCopy: () => void;
|
|
191
171
|
navigateTo: NavigateFunction;
|
|
192
172
|
}): React.JSX.Element => {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
const labelColumnNames = table.schema.fields.filter(field =>
|
|
205
|
-
field.name.startsWith(pprofLabelPrefix)
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
const {queryServiceClient, enableSourcesView} = useParcaContext();
|
|
209
|
-
const {profileSource} = useProfileViewContext();
|
|
210
|
-
|
|
211
|
-
const {isLoading: sourceLoading, response: sourceResponse} = useQuery(
|
|
212
|
-
queryServiceClient,
|
|
213
|
-
profileSource as ProfileSource,
|
|
214
|
-
QueryRequest_ReportType.SOURCE,
|
|
215
|
-
{
|
|
216
|
-
skip:
|
|
217
|
-
enableSourcesView === false ||
|
|
218
|
-
profileSource === undefined ||
|
|
219
|
-
// eslint-disable-next-line no-extra-boolean-cast
|
|
220
|
-
!Boolean(mappingBuildID) ||
|
|
221
|
-
// eslint-disable-next-line no-extra-boolean-cast
|
|
222
|
-
!Boolean(functionFilename),
|
|
223
|
-
sourceBuildID: mappingBuildID,
|
|
224
|
-
sourceFilename: functionFilename,
|
|
225
|
-
sourceOnly: true,
|
|
226
|
-
}
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
const isSourceAvailable = !sourceLoading && sourceResponse?.report != null;
|
|
230
|
-
|
|
231
|
-
const getTextForFile = (): string => {
|
|
232
|
-
if (functionFilename === '') return '<unknown>';
|
|
173
|
+
const {
|
|
174
|
+
labelPairs,
|
|
175
|
+
functionFilename,
|
|
176
|
+
file,
|
|
177
|
+
openFile,
|
|
178
|
+
isSourceAvailable,
|
|
179
|
+
locationAddress,
|
|
180
|
+
mappingFile,
|
|
181
|
+
mappingBuildID,
|
|
182
|
+
} = useGraphTooltipMetaInfo({table, row, navigateTo});
|
|
183
|
+
const {enableSourcesView} = useParcaContext();
|
|
233
184
|
|
|
234
|
-
return `${functionFilename} ${lineNumber !== undefined ? ` +${lineNumber.toString()}` : ''}`;
|
|
235
|
-
};
|
|
236
|
-
const file = getTextForFile();
|
|
237
|
-
|
|
238
|
-
const labelPairs = labelColumnNames
|
|
239
|
-
.map((field, i) => [
|
|
240
|
-
labelColumnNames[i].name.slice(pprofLabelPrefix.length),
|
|
241
|
-
arrowToString(table.getChild(field.name)?.get(row)) ?? '',
|
|
242
|
-
])
|
|
243
|
-
.filter(value => value[1] !== '');
|
|
244
185
|
const labels = labelPairs.map(
|
|
245
186
|
(l): React.JSX.Element => (
|
|
246
187
|
<span
|
|
@@ -252,38 +193,6 @@ const TooltipMetaInfo = ({
|
|
|
252
193
|
)
|
|
253
194
|
);
|
|
254
195
|
|
|
255
|
-
const [dashboardItems, setDashboardItems] = useURLState({
|
|
256
|
-
param: 'dashboard_items',
|
|
257
|
-
navigateTo,
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
261
|
-
const [unusedBuildId, setSourceBuildId] = useURLState({
|
|
262
|
-
param: 'source_buildid',
|
|
263
|
-
navigateTo,
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
267
|
-
const [unusedFilename, setSourceFilename] = useURLState({
|
|
268
|
-
param: 'source_filename',
|
|
269
|
-
navigateTo,
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
273
|
-
const [unusedLine, setSourceLine] = useURLState({
|
|
274
|
-
param: 'source_line',
|
|
275
|
-
navigateTo,
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
const openFile = (): void => {
|
|
279
|
-
setDashboardItems([dashboardItems[0], 'source']);
|
|
280
|
-
setSourceBuildId(mappingBuildID);
|
|
281
|
-
setSourceFilename(functionFilename);
|
|
282
|
-
if (lineNumber !== undefined) {
|
|
283
|
-
setSourceLine(lineNumber.toString());
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
|
|
287
196
|
return (
|
|
288
197
|
<>
|
|
289
198
|
<tr>
|
|
@@ -333,7 +242,7 @@ const TooltipMetaInfo = ({
|
|
|
333
242
|
<tr>
|
|
334
243
|
<td className="w-1/4">Binary</td>
|
|
335
244
|
<td className="w-3/4 break-all">
|
|
336
|
-
{mappingFile ===
|
|
245
|
+
{mappingFile === null ? (
|
|
337
246
|
<NoData />
|
|
338
247
|
) : (
|
|
339
248
|
<CopyToClipboard onCopy={onCopy} text={mappingFile}>
|
|
@@ -345,7 +254,7 @@ const TooltipMetaInfo = ({
|
|
|
345
254
|
<tr>
|
|
346
255
|
<td className="w-1/4">Build Id</td>
|
|
347
256
|
<td className="w-3/4 break-all">
|
|
348
|
-
{mappingBuildID ===
|
|
257
|
+
{mappingBuildID === null ? (
|
|
349
258
|
<NoData />
|
|
350
259
|
) : (
|
|
351
260
|
<CopyToClipboard onCopy={onCopy} text={mappingBuildID}>
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {useState} from 'react';
|
|
15
|
+
|
|
16
|
+
import {Table} from 'apache-arrow';
|
|
17
|
+
import cx from 'classnames';
|
|
18
|
+
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
|
19
|
+
import {Tooltip} from 'react-tooltip';
|
|
20
|
+
import {useWindowSize} from 'react-use';
|
|
21
|
+
|
|
22
|
+
import {Button, IconButton, useParcaContext} from '@parca/components';
|
|
23
|
+
import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
|
|
24
|
+
import {getLastItem} from '@parca/utilities';
|
|
25
|
+
|
|
26
|
+
import {hexifyAddress, truncateString, truncateStringReverse} from '../../utils';
|
|
27
|
+
import {ExpandOnHover} from '../ExpandOnHoverValue';
|
|
28
|
+
import {useGraphTooltip} from '../useGraphTooltip';
|
|
29
|
+
import {useGraphTooltipMetaInfo} from '../useGraphTooltipMetaInfo';
|
|
30
|
+
|
|
31
|
+
let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
32
|
+
|
|
33
|
+
interface Props {
|
|
34
|
+
table: Table<any>;
|
|
35
|
+
unit: string;
|
|
36
|
+
total: bigint;
|
|
37
|
+
totalUnfiltered: bigint;
|
|
38
|
+
row: number | null;
|
|
39
|
+
level: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const InfoSection = ({
|
|
43
|
+
title,
|
|
44
|
+
value,
|
|
45
|
+
onCopy,
|
|
46
|
+
copyText,
|
|
47
|
+
minWidth = '',
|
|
48
|
+
}: {
|
|
49
|
+
title: string;
|
|
50
|
+
value: string | JSX.Element;
|
|
51
|
+
copyText: string;
|
|
52
|
+
onCopy: () => void;
|
|
53
|
+
minWidth?: string;
|
|
54
|
+
}): JSX.Element => {
|
|
55
|
+
return (
|
|
56
|
+
<div className={cx('flex shrink-0 flex-col gap-1 p-2', {[minWidth]: minWidth != null})}>
|
|
57
|
+
<p className="text-sm font-medium leading-5 text-gray-500 dark:text-gray-400">{title}</p>
|
|
58
|
+
<div className="text-lg font-normal text-gray-900 dark:text-gray-50">
|
|
59
|
+
<CopyToClipboard onCopy={onCopy} text={copyText}>
|
|
60
|
+
<button>{value}</button>
|
|
61
|
+
</CopyToClipboard>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const DockedGraphTooltip = ({
|
|
68
|
+
table,
|
|
69
|
+
unit,
|
|
70
|
+
total,
|
|
71
|
+
totalUnfiltered,
|
|
72
|
+
row,
|
|
73
|
+
level,
|
|
74
|
+
}: Props): JSX.Element => {
|
|
75
|
+
let {width} = useWindowSize();
|
|
76
|
+
const {profileExplorer, navigateTo, enableSourcesView} = useParcaContext();
|
|
77
|
+
const {PaddingX} = profileExplorer ?? {PaddingX: 0};
|
|
78
|
+
width = width - PaddingX - 24;
|
|
79
|
+
const [isCopied, setIsCopied] = useState<boolean>(false);
|
|
80
|
+
|
|
81
|
+
const onCopy = (): void => {
|
|
82
|
+
setIsCopied(true);
|
|
83
|
+
|
|
84
|
+
if (timeoutHandle !== null) {
|
|
85
|
+
clearTimeout(timeoutHandle);
|
|
86
|
+
}
|
|
87
|
+
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const graphTooltipData = useGraphTooltip({
|
|
91
|
+
table,
|
|
92
|
+
unit,
|
|
93
|
+
total,
|
|
94
|
+
totalUnfiltered,
|
|
95
|
+
row,
|
|
96
|
+
level,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const {
|
|
100
|
+
labelPairs,
|
|
101
|
+
functionFilename,
|
|
102
|
+
file,
|
|
103
|
+
openFile,
|
|
104
|
+
isSourceAvailable,
|
|
105
|
+
locationAddress,
|
|
106
|
+
mappingFile,
|
|
107
|
+
mappingBuildID,
|
|
108
|
+
} = useGraphTooltipMetaInfo({table, row: row ?? 0, navigateTo});
|
|
109
|
+
|
|
110
|
+
const [_, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
|
|
111
|
+
|
|
112
|
+
if (graphTooltipData === null) {
|
|
113
|
+
return <></>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const {name, cumulativeText, diffText, diff} = graphTooltipData;
|
|
117
|
+
|
|
118
|
+
const labels = labelPairs.map(
|
|
119
|
+
(l): React.JSX.Element => (
|
|
120
|
+
<span
|
|
121
|
+
key={l[0]}
|
|
122
|
+
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"
|
|
123
|
+
>
|
|
124
|
+
{`${l[0]}="${l[1]}"`}
|
|
125
|
+
</span>
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const addressText = locationAddress !== 0n ? hexifyAddress(locationAddress) : 'unknown';
|
|
130
|
+
const fileText = functionFilename !== '' ? file : 'Not available';
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div
|
|
134
|
+
className="fixed bottom-0 z-20 overflow-hidden rounded-t-lg border-l border-r border-t border-gray-400 bg-white bg-opacity-90 px-8 py-3 dark:border-gray-600 dark:bg-black dark:bg-opacity-80"
|
|
135
|
+
style={{width}}
|
|
136
|
+
>
|
|
137
|
+
<div className="flex flex-col gap-4">
|
|
138
|
+
<div className="flex justify-between gap-4">
|
|
139
|
+
{row === 0 ? (
|
|
140
|
+
<p>root</p>
|
|
141
|
+
) : (
|
|
142
|
+
<>
|
|
143
|
+
{name !== '' ? (
|
|
144
|
+
<CopyToClipboard onCopy={onCopy} text={name}>
|
|
145
|
+
<button className="cursor-pointer text-left">{name}</button>
|
|
146
|
+
</CopyToClipboard>
|
|
147
|
+
) : (
|
|
148
|
+
<>
|
|
149
|
+
{locationAddress !== 0n ? (
|
|
150
|
+
<CopyToClipboard onCopy={onCopy} text={hexifyAddress(locationAddress)}>
|
|
151
|
+
<button className="cursor-pointer text-left">
|
|
152
|
+
{hexifyAddress(locationAddress)}
|
|
153
|
+
</button>
|
|
154
|
+
</CopyToClipboard>
|
|
155
|
+
) : (
|
|
156
|
+
<p>unknown</p>
|
|
157
|
+
)}
|
|
158
|
+
</>
|
|
159
|
+
)}
|
|
160
|
+
</>
|
|
161
|
+
)}
|
|
162
|
+
<IconButton
|
|
163
|
+
onClick={() => setIsDocked(false)}
|
|
164
|
+
icon="mdi:dock-window"
|
|
165
|
+
title="Undock MetaInfo Panel"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
<div className="flex justify-between gap-3">
|
|
169
|
+
<InfoSection
|
|
170
|
+
title="Cumulative"
|
|
171
|
+
value={cumulativeText}
|
|
172
|
+
onCopy={onCopy}
|
|
173
|
+
copyText={cumulativeText}
|
|
174
|
+
minWidth="w-44"
|
|
175
|
+
/>
|
|
176
|
+
{diff !== 0n ? (
|
|
177
|
+
<InfoSection
|
|
178
|
+
title="Diff"
|
|
179
|
+
value={diffText}
|
|
180
|
+
onCopy={onCopy}
|
|
181
|
+
copyText={diffText}
|
|
182
|
+
minWidth="w-44"
|
|
183
|
+
/>
|
|
184
|
+
) : null}
|
|
185
|
+
<InfoSection
|
|
186
|
+
title="File"
|
|
187
|
+
value={
|
|
188
|
+
<div className="flex gap-2">
|
|
189
|
+
<ExpandOnHover
|
|
190
|
+
value={fileText}
|
|
191
|
+
displayValue={truncateStringReverse(fileText, 45)}
|
|
192
|
+
/>
|
|
193
|
+
<div
|
|
194
|
+
className={cx('flex items-center gap-2', {
|
|
195
|
+
hidden: enableSourcesView === false || functionFilename === '',
|
|
196
|
+
})}
|
|
197
|
+
>
|
|
198
|
+
<div
|
|
199
|
+
data-tooltip-id="open-source-button-help"
|
|
200
|
+
data-tooltip-content="There is no source code uploaded for this build"
|
|
201
|
+
>
|
|
202
|
+
<Button
|
|
203
|
+
variant={'neutral'}
|
|
204
|
+
onClick={() => openFile()}
|
|
205
|
+
className="shrink-0"
|
|
206
|
+
disabled={!isSourceAvailable}
|
|
207
|
+
>
|
|
208
|
+
open
|
|
209
|
+
</Button>
|
|
210
|
+
</div>
|
|
211
|
+
{!isSourceAvailable ? <Tooltip id="open-source-button-help" /> : null}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
}
|
|
215
|
+
onCopy={onCopy}
|
|
216
|
+
copyText={file}
|
|
217
|
+
minWidth={'w-[460px]'}
|
|
218
|
+
/>
|
|
219
|
+
<InfoSection
|
|
220
|
+
title="Address"
|
|
221
|
+
value={addressText}
|
|
222
|
+
onCopy={onCopy}
|
|
223
|
+
copyText={addressText}
|
|
224
|
+
minWidth="w-44"
|
|
225
|
+
/>
|
|
226
|
+
<InfoSection
|
|
227
|
+
title="Binary"
|
|
228
|
+
value={(mappingFile != null ? getLastItem(mappingFile) : null) ?? 'Not available'}
|
|
229
|
+
onCopy={onCopy}
|
|
230
|
+
copyText={mappingFile ?? 'Not available'}
|
|
231
|
+
minWidth="w-44"
|
|
232
|
+
/>
|
|
233
|
+
<InfoSection
|
|
234
|
+
title="Build ID"
|
|
235
|
+
value={truncateString(getLastItem(mappingBuildID) ?? 'Not available', 28)}
|
|
236
|
+
onCopy={onCopy}
|
|
237
|
+
copyText={mappingBuildID ?? 'Not available'}
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
<div>
|
|
241
|
+
<div className="flex h-5 gap-1">{labels}</div>
|
|
242
|
+
</div>
|
|
243
|
+
<span className="mx-2 block text-xs text-gray-500">
|
|
244
|
+
{isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.'}
|
|
245
|
+
</span>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {Table} from 'apache-arrow';
|
|
15
|
+
|
|
16
|
+
import {divide, valueFormatter} from '@parca/utilities';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
FIELD_CUMULATIVE,
|
|
20
|
+
FIELD_DIFF,
|
|
21
|
+
FIELD_LOCATION_ADDRESS,
|
|
22
|
+
} from '../../ProfileIcicleGraph/IcicleGraphArrow';
|
|
23
|
+
import {getTextForCumulative, nodeLabel} from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
table: Table<any>;
|
|
27
|
+
unit: string;
|
|
28
|
+
total: bigint;
|
|
29
|
+
totalUnfiltered: bigint;
|
|
30
|
+
row: number | null;
|
|
31
|
+
level: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface GraphTooltipData {
|
|
35
|
+
name: string;
|
|
36
|
+
locationAddress: bigint;
|
|
37
|
+
cumulativeText: string;
|
|
38
|
+
diffText: string;
|
|
39
|
+
diff: bigint;
|
|
40
|
+
row: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const useGraphTooltip = ({
|
|
44
|
+
table,
|
|
45
|
+
unit,
|
|
46
|
+
total,
|
|
47
|
+
totalUnfiltered,
|
|
48
|
+
row,
|
|
49
|
+
level,
|
|
50
|
+
}: Props): GraphTooltipData | null => {
|
|
51
|
+
if (row === null) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const locationAddress: bigint = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
|
|
56
|
+
const cumulative: bigint = BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row)) ?? 0n;
|
|
57
|
+
const diff: bigint = BigInt(table.getChild(FIELD_DIFF)?.get(row)) ?? 0n;
|
|
58
|
+
|
|
59
|
+
const prevValue = cumulative - diff;
|
|
60
|
+
const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
|
|
61
|
+
const diffSign = diff > 0 ? '+' : '';
|
|
62
|
+
const diffValueText = diffSign + valueFormatter(diff, unit, 1);
|
|
63
|
+
const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
|
|
64
|
+
const diffText = `${diffValueText} (${diffPercentageText})`;
|
|
65
|
+
|
|
66
|
+
const name = nodeLabel(table, row, level, false);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
name,
|
|
70
|
+
locationAddress,
|
|
71
|
+
cumulativeText: getTextForCumulative(cumulative, totalUnfiltered, total, unit),
|
|
72
|
+
diffText,
|
|
73
|
+
diff,
|
|
74
|
+
row,
|
|
75
|
+
};
|
|
76
|
+
};
|