@parca/profile 0.18.4 → 0.19.0
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 +6 -0
- package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/useGraphTooltip/index.js +6 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts +2 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +13 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +4 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +15 -6
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.js +5 -4
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.d.ts +2 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.js +3 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +3 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +4 -4
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +1 -1
- package/dist/ProfileIcicleGraph/index.d.ts +4 -1
- package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/index.js +22 -6
- package/dist/ProfileView/components/DashboardItems/index.d.ts +3 -1
- package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
- package/dist/ProfileView/components/DashboardItems/index.js +4 -1
- package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +0 -13
- package/dist/ProfileView/components/Toolbars/index.d.ts +8 -0
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/index.js +6 -2
- package/dist/ProfileView/components/ViewSelector/Dropdown.d.ts +1 -0
- package/dist/ProfileView/components/ViewSelector/Dropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/Dropdown.js +1 -1
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/index.js +9 -0
- package/dist/ProfileView/hooks/useVisualizationState.d.ts +4 -0
- package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
- package/dist/ProfileView/hooks/useVisualizationState.js +9 -1
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +3 -2
- package/dist/ProfileView/types/visualization.d.ts +1 -1
- package/dist/ProfileView/types/visualization.d.ts.map +1 -1
- package/dist/ProfileViewWithData.js +1 -1
- package/dist/Sandwich/components/CalleesSection.d.ts +25 -0
- package/dist/Sandwich/components/CalleesSection.d.ts.map +1 -0
- package/dist/Sandwich/components/CalleesSection.js +11 -0
- package/dist/Sandwich/components/CallersSection.d.ts +25 -0
- package/dist/Sandwich/components/CallersSection.d.ts.map +1 -0
- package/dist/Sandwich/components/CallersSection.js +11 -0
- package/dist/Sandwich/components/TableSection.d.ts +21 -0
- package/dist/Sandwich/components/TableSection.d.ts.map +1 -0
- package/dist/Sandwich/components/TableSection.js +7 -0
- package/dist/Sandwich/index.d.ts +19 -0
- package/dist/Sandwich/index.d.ts.map +1 -0
- package/dist/Sandwich/index.js +182 -0
- package/dist/Sandwich/utils/processRowData.d.ts +11 -0
- package/dist/Sandwich/utils/processRowData.d.ts.map +1 -0
- package/dist/Sandwich/utils/processRowData.js +53 -0
- package/dist/Table/ColorCell.d.ts +7 -0
- package/dist/Table/ColorCell.d.ts.map +1 -0
- package/dist/Table/ColorCell.js +2 -0
- package/dist/Table/MoreDropdown.d.ts +5 -0
- package/dist/Table/MoreDropdown.d.ts.map +1 -0
- package/dist/Table/MoreDropdown.js +39 -0
- package/dist/Table/hooks/useColorManagement.d.ts +14 -0
- package/dist/Table/hooks/useColorManagement.d.ts.map +1 -0
- package/dist/Table/hooks/useColorManagement.js +32 -0
- package/dist/Table/hooks/useTableConfiguration.d.ts +21 -0
- package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -0
- package/dist/Table/hooks/useTableConfiguration.js +204 -0
- package/dist/Table/index.d.ts +14 -4
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +34 -332
- package/dist/Table/utils/functions.d.ts +1 -0
- package/dist/Table/utils/functions.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/useQuery.d.ts +1 -0
- package/dist/useQuery.d.ts.map +1 -1
- package/dist/useQuery.js +7 -1
- package/package.json +7 -7
- package/src/GraphTooltipArrow/useGraphTooltip/index.ts +6 -3
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +23 -1
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.tsx +1 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +34 -5
- package/src/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.tsx +6 -4
- package/src/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.tsx +5 -1
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +13 -1
- package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +1 -1
- package/src/ProfileIcicleGraph/index.tsx +50 -18
- package/src/ProfileView/components/DashboardItems/index.tsx +21 -0
- package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +11 -25
- package/src/ProfileView/components/Toolbars/index.tsx +42 -1
- package/src/ProfileView/components/ViewSelector/Dropdown.tsx +2 -1
- package/src/ProfileView/components/ViewSelector/index.tsx +11 -0
- package/src/ProfileView/hooks/useVisualizationState.ts +16 -1
- package/src/ProfileView/index.tsx +7 -0
- package/src/ProfileView/types/visualization.ts +7 -1
- package/src/ProfileViewWithData.tsx +1 -1
- package/src/Sandwich/components/CalleesSection.tsx +87 -0
- package/src/Sandwich/components/CallersSection.tsx +88 -0
- package/src/Sandwich/components/TableSection.tsx +67 -0
- package/src/Sandwich/index.tsx +342 -0
- package/src/Sandwich/utils/processRowData.ts +78 -0
- package/src/Table/ColorCell.tsx +26 -0
- package/src/Table/MoreDropdown.tsx +75 -0
- package/src/Table/hooks/useColorManagement.ts +58 -0
- package/src/Table/hooks/useTableConfiguration.tsx +237 -0
- package/src/Table/index.tsx +37 -470
- package/src/Table/utils/functions.ts +1 -0
- package/src/useQuery.tsx +10 -1
package/dist/useQuery.js
CHANGED
|
@@ -28,6 +28,7 @@ export const useQuery = (client, profileSource, reportType, options) => {
|
|
|
28
28
|
options?.invertCallStack ?? false,
|
|
29
29
|
options?.binaryFrameFilter ?? '',
|
|
30
30
|
profileSource.excludeFunction ?? false,
|
|
31
|
+
options?.sandwichByFunction ?? '',
|
|
31
32
|
],
|
|
32
33
|
queryFn: async () => {
|
|
33
34
|
const req = profileSource.QueryRequest();
|
|
@@ -44,8 +45,9 @@ export const useQuery = (client, profileSource, reportType, options) => {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
req.invertCallStack = options?.invertCallStack ?? false;
|
|
48
|
+
// Handle filter from ProfileSource (filter by function toolbar)
|
|
47
49
|
const functionToFilter = req.filterQuery;
|
|
48
|
-
if (functionToFilter !== undefined) {
|
|
50
|
+
if (functionToFilter !== undefined && functionToFilter !== '') {
|
|
49
51
|
req.filter = [
|
|
50
52
|
...req.filter,
|
|
51
53
|
{
|
|
@@ -64,6 +66,10 @@ export const useQuery = (client, profileSource, reportType, options) => {
|
|
|
64
66
|
},
|
|
65
67
|
];
|
|
66
68
|
}
|
|
69
|
+
// Handle sandwich view filter separately
|
|
70
|
+
if (options?.sandwichByFunction !== undefined) {
|
|
71
|
+
req.sandwichByFunction = options.sandwichByFunction;
|
|
72
|
+
}
|
|
67
73
|
if (options?.binaryFrameFilter !== undefined && options?.binaryFrameFilter.length > 0) {
|
|
68
74
|
req.filter = [
|
|
69
75
|
...req.filter,
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@headlessui/react": "^1.7.19",
|
|
7
7
|
"@iconify/react": "^4.0.0",
|
|
8
|
-
"@parca/client": "0.
|
|
9
|
-
"@parca/components": "0.16.
|
|
8
|
+
"@parca/client": "0.17.0",
|
|
9
|
+
"@parca/components": "0.16.342",
|
|
10
10
|
"@parca/dynamicsize": "0.16.65",
|
|
11
|
-
"@parca/hooks": "0.0.
|
|
11
|
+
"@parca/hooks": "0.0.91",
|
|
12
12
|
"@parca/icons": "0.16.72",
|
|
13
13
|
"@parca/parser": "0.16.79",
|
|
14
|
-
"@parca/store": "0.16.
|
|
15
|
-
"@parca/utilities": "0.0.
|
|
14
|
+
"@parca/store": "0.16.175",
|
|
15
|
+
"@parca/utilities": "0.0.100",
|
|
16
16
|
"@popperjs/core": "^2.11.8",
|
|
17
17
|
"@protobuf-ts/runtime-rpc": "^2.5.0",
|
|
18
18
|
"@storybook/preview-api": "^8.4.3",
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"access": "public",
|
|
78
78
|
"registry": "https://registry.npmjs.org/"
|
|
79
79
|
},
|
|
80
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "f8b6abd23b6f0a18f9ee64a0bdb08ae43451849a"
|
|
81
81
|
}
|
|
@@ -61,15 +61,18 @@ export const useGraphTooltip = ({
|
|
|
61
61
|
unit = unit ?? profileType.sampleUnit;
|
|
62
62
|
|
|
63
63
|
const cumulative: bigint =
|
|
64
|
-
table.getChild(FIELD_CUMULATIVE)?.get(row) !== null
|
|
64
|
+
table.getChild(FIELD_CUMULATIVE)?.get(row) !== null &&
|
|
65
|
+
table.getChild(FIELD_CUMULATIVE)?.get(row) !== undefined
|
|
65
66
|
? BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row))
|
|
66
67
|
: 0n;
|
|
67
68
|
const flat: bigint =
|
|
68
|
-
table.getChild(FIELD_FLAT)?.get(row) !== null
|
|
69
|
+
table.getChild(FIELD_FLAT)?.get(row) !== null &&
|
|
70
|
+
table.getChild(FIELD_FLAT)?.get(row) !== undefined
|
|
69
71
|
? BigInt(table.getChild(FIELD_FLAT)?.get(row))
|
|
70
72
|
: 0n;
|
|
71
73
|
const diff: bigint =
|
|
72
|
-
table.getChild(FIELD_DIFF)?.get(row) !== null
|
|
74
|
+
table.getChild(FIELD_DIFF)?.get(row) !== null &&
|
|
75
|
+
table.getChild(FIELD_DIFF)?.get(row) !== undefined
|
|
73
76
|
? BigInt(table.getChild(FIELD_DIFF)?.get(row))
|
|
74
77
|
: 0n;
|
|
75
78
|
|
|
@@ -37,6 +37,7 @@ interface ContextMenuProps {
|
|
|
37
37
|
resetPath: () => void;
|
|
38
38
|
hideMenu: () => void;
|
|
39
39
|
hideBinary: (binaryToRemove: string) => void;
|
|
40
|
+
isSandwich?: boolean;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
const ContextMenu = ({
|
|
@@ -51,6 +52,7 @@ const ContextMenu = ({
|
|
|
51
52
|
unit,
|
|
52
53
|
hideBinary,
|
|
53
54
|
resetPath,
|
|
55
|
+
isSandwich = false,
|
|
54
56
|
}: ContextMenuProps): JSX.Element => {
|
|
55
57
|
const {isDarkMode} = useParcaContext();
|
|
56
58
|
const {enableSourcesView, checkDebuginfoStatusHandler} = useParcaContext();
|
|
@@ -83,6 +85,10 @@ const ContextMenu = ({
|
|
|
83
85
|
const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
|
|
84
86
|
alwaysReturnArray: true,
|
|
85
87
|
});
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
89
|
+
const [sandwichFunctionName, setSandwichFunctionName] = useURLState<string | undefined>(
|
|
90
|
+
'sandwich_function_name'
|
|
91
|
+
);
|
|
86
92
|
|
|
87
93
|
if (contextMenuData === null) {
|
|
88
94
|
return <></>;
|
|
@@ -159,7 +165,11 @@ const ContextMenu = ({
|
|
|
159
165
|
id="show-in-table"
|
|
160
166
|
onClick={() => {
|
|
161
167
|
setSearchString(functionName);
|
|
162
|
-
|
|
168
|
+
if (isSandwich) {
|
|
169
|
+
setDashboardItems(['table']);
|
|
170
|
+
} else {
|
|
171
|
+
setDashboardItems([...dashboardItems, 'table']);
|
|
172
|
+
}
|
|
163
173
|
}}
|
|
164
174
|
>
|
|
165
175
|
<div className="flex w-full items-center gap-2">
|
|
@@ -167,6 +177,18 @@ const ContextMenu = ({
|
|
|
167
177
|
<div>Show in table</div>
|
|
168
178
|
</div>
|
|
169
179
|
</Item>
|
|
180
|
+
<Item
|
|
181
|
+
id="show-in-sandwich"
|
|
182
|
+
onClick={() => {
|
|
183
|
+
setSandwichFunctionName(functionName);
|
|
184
|
+
setDashboardItems(['sandwich']);
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<div className="flex w-full items-center gap-2">
|
|
188
|
+
<Icon icon="tdesign:sandwich-filled" />
|
|
189
|
+
<div>Show in sandwich</div>
|
|
190
|
+
</div>
|
|
191
|
+
</Item>
|
|
170
192
|
<Item id="reset-view" onClick={handleResetView}>
|
|
171
193
|
<div className="flex w-full items-center gap-2">
|
|
172
194
|
<Icon icon="system-uicons:reset" />
|
|
@@ -61,6 +61,10 @@ export interface IcicleNodeProps {
|
|
|
61
61
|
onClick: () => void;
|
|
62
62
|
isIcicleChart: boolean;
|
|
63
63
|
profileSource: ProfileSource;
|
|
64
|
+
isFlamegraph?: boolean;
|
|
65
|
+
isSandwich?: boolean;
|
|
66
|
+
maxDepth?: number;
|
|
67
|
+
tooltipId?: string;
|
|
64
68
|
|
|
65
69
|
// Hovering row must only ever be used for highlighting similar nodes, otherwise it will cause performance issues as it causes the full iciclegraph to get rerendered every time the hovering row changes.
|
|
66
70
|
hoveringRow?: number;
|
|
@@ -95,6 +99,10 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
95
99
|
setHoveringRow,
|
|
96
100
|
isIcicleChart,
|
|
97
101
|
profileSource,
|
|
102
|
+
isFlamegraph = false,
|
|
103
|
+
isSandwich = false,
|
|
104
|
+
maxDepth = 0,
|
|
105
|
+
tooltipId = 'default',
|
|
98
106
|
}: IcicleNodeProps): React.JSX.Element {
|
|
99
107
|
// get the columns to read from
|
|
100
108
|
const mappingColumn = table.getChild(FIELD_MAPPING_FILE);
|
|
@@ -162,8 +170,9 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
162
170
|
// If the end of the node is before the selection offset or the start of the node is after the selection offset + totalWidth, we don't render it.
|
|
163
171
|
return <></>;
|
|
164
172
|
}
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
|
|
174
|
+
if (row === 0 && (isIcicleChart || isSandwich)) {
|
|
175
|
+
// The root node is not rendered in the icicle chart or sandwich view, so we return null.
|
|
167
176
|
return <></>;
|
|
168
177
|
}
|
|
169
178
|
|
|
@@ -186,7 +195,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
186
195
|
const onMouseEnter = (): void => {
|
|
187
196
|
setHoveringRow(row);
|
|
188
197
|
window.dispatchEvent(
|
|
189
|
-
new CustomEvent(
|
|
198
|
+
new CustomEvent(`icicle-tooltip-update-${tooltipId}`, {
|
|
190
199
|
detail: {row},
|
|
191
200
|
})
|
|
192
201
|
);
|
|
@@ -195,7 +204,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
195
204
|
const onMouseLeave = (): void => {
|
|
196
205
|
setHoveringRow(undefined);
|
|
197
206
|
window.dispatchEvent(
|
|
198
|
-
new CustomEvent(
|
|
207
|
+
new CustomEvent(`icicle-tooltip-update-${tooltipId}`, {
|
|
199
208
|
detail: {row: null},
|
|
200
209
|
})
|
|
201
210
|
);
|
|
@@ -212,7 +221,27 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
212
221
|
: selectedDepth > depth
|
|
213
222
|
? 0
|
|
214
223
|
: ((Number(valueOffset) - Number(selectionOffset)) / Number(total)) * totalWidth;
|
|
215
|
-
|
|
224
|
+
|
|
225
|
+
const calculateY = (
|
|
226
|
+
isFlamegraph: boolean,
|
|
227
|
+
isSandwich: boolean,
|
|
228
|
+
isIcicleChart: boolean,
|
|
229
|
+
maxDepth: number,
|
|
230
|
+
depth: number,
|
|
231
|
+
height: number
|
|
232
|
+
): number => {
|
|
233
|
+
if (isFlamegraph) {
|
|
234
|
+
return (maxDepth - depth) * height; // Flamegraph is inverted
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (isIcicleChart || isSandwich) {
|
|
238
|
+
return (depth - 1) * height;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return depth * height;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const y = calculateY(isFlamegraph, isSandwich, isIcicleChart, maxDepth, depth, height);
|
|
216
245
|
|
|
217
246
|
return (
|
|
218
247
|
<>
|
|
@@ -28,7 +28,8 @@ export const MemoizedTooltip = memo(function MemoizedTooltip({
|
|
|
28
28
|
dockedMetainfo,
|
|
29
29
|
}: MemoizedTooltipProps): React.JSX.Element | null {
|
|
30
30
|
const [tooltipRow, setTooltipRow] = useState<number | null>(null);
|
|
31
|
-
const {table, total, totalUnfiltered, profileType, unit, compareAbsolute} =
|
|
31
|
+
const {table, total, totalUnfiltered, profileType, unit, compareAbsolute, tooltipId} =
|
|
32
|
+
useTooltipContext();
|
|
32
33
|
|
|
33
34
|
// This component subscribes to tooltip updates through a callback
|
|
34
35
|
// passed to the TooltipProvider, avoiding the need to lift state
|
|
@@ -37,11 +38,12 @@ export const MemoizedTooltip = memo(function MemoizedTooltip({
|
|
|
37
38
|
setTooltipRow(event.detail.row);
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
const eventName = `icicle-tooltip-update-${tooltipId}`;
|
|
42
|
+
window.addEventListener(eventName as any, handleTooltipUpdate as any);
|
|
41
43
|
return () => {
|
|
42
|
-
window.removeEventListener(
|
|
44
|
+
window.removeEventListener(eventName as any, handleTooltipUpdate as any);
|
|
43
45
|
};
|
|
44
|
-
}, []);
|
|
46
|
+
}, [tooltipId]);
|
|
45
47
|
|
|
46
48
|
if (dockedMetainfo) {
|
|
47
49
|
return (
|
|
@@ -32,6 +32,7 @@ interface TooltipContextValue {
|
|
|
32
32
|
compareAbsolute: boolean;
|
|
33
33
|
updateTooltip: (row: number | null, x?: number, y?: number) => void;
|
|
34
34
|
tooltipState: TooltipState;
|
|
35
|
+
tooltipId: string;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const TooltipContext = createContext<TooltipContextValue | null>(null);
|
|
@@ -53,6 +54,7 @@ interface TooltipProviderProps {
|
|
|
53
54
|
unit?: string;
|
|
54
55
|
compareAbsolute: boolean;
|
|
55
56
|
onTooltipUpdate?: (state: TooltipState) => void;
|
|
57
|
+
tooltipId?: string;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export const TooltipProvider: React.FC<TooltipProviderProps> = ({
|
|
@@ -64,6 +66,7 @@ export const TooltipProvider: React.FC<TooltipProviderProps> = ({
|
|
|
64
66
|
unit,
|
|
65
67
|
compareAbsolute,
|
|
66
68
|
onTooltipUpdate,
|
|
69
|
+
tooltipId = 'default',
|
|
67
70
|
}) => {
|
|
68
71
|
const tooltipStateRef = useRef<TooltipState>({row: null, x: 0, y: 0});
|
|
69
72
|
|
|
@@ -85,8 +88,9 @@ export const TooltipProvider: React.FC<TooltipProviderProps> = ({
|
|
|
85
88
|
compareAbsolute,
|
|
86
89
|
updateTooltip,
|
|
87
90
|
tooltipState: tooltipStateRef.current,
|
|
91
|
+
tooltipId,
|
|
88
92
|
}),
|
|
89
|
-
[table, total, totalUnfiltered, profileType, unit, compareAbsolute, updateTooltip]
|
|
93
|
+
[table, total, totalUnfiltered, profileType, unit, compareAbsolute, updateTooltip, tooltipId]
|
|
90
94
|
);
|
|
91
95
|
|
|
92
96
|
return <TooltipContext.Provider value={value}>{children}</TooltipContext.Provider>;
|
|
@@ -74,6 +74,9 @@ interface IcicleGraphArrowProps {
|
|
|
74
74
|
mappingsListFromMetadata: string[];
|
|
75
75
|
compareAbsolute: boolean;
|
|
76
76
|
isIcicleChart?: boolean;
|
|
77
|
+
isFlamegraph?: boolean;
|
|
78
|
+
isSandwich?: boolean;
|
|
79
|
+
tooltipId?: string;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
export const getMappingColors = (
|
|
@@ -129,6 +132,9 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
129
132
|
mappingsListFromMetadata,
|
|
130
133
|
compareAbsolute,
|
|
131
134
|
isIcicleChart = false,
|
|
135
|
+
isFlamegraph = false,
|
|
136
|
+
isSandwich = false,
|
|
137
|
+
tooltipId = 'default',
|
|
132
138
|
}: IcicleGraphArrowProps): React.JSX.Element {
|
|
133
139
|
const [highlightSimilarStacksPreference] = useUserPreference<boolean>(
|
|
134
140
|
USER_PREFERENCES.HIGHLIGHT_SIMILAR_STACKS.key
|
|
@@ -249,7 +255,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
249
255
|
|
|
250
256
|
const depthColumn = table.getChild(FIELD_DEPTH);
|
|
251
257
|
const maxDepth = getMaxDepth(depthColumn);
|
|
252
|
-
const height = (maxDepth + 1) * RowHeight;
|
|
258
|
+
const height = isSandwich ? maxDepth * RowHeight : (maxDepth + 1) * RowHeight;
|
|
253
259
|
|
|
254
260
|
// To find the selected row, we must walk the current path and look at which
|
|
255
261
|
// children of the current frame matches the path element exactly. Until the
|
|
@@ -284,6 +290,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
284
290
|
profileType={profileType}
|
|
285
291
|
unit={arrow.unit}
|
|
286
292
|
compareAbsolute={compareAbsolute}
|
|
293
|
+
tooltipId={tooltipId}
|
|
287
294
|
>
|
|
288
295
|
<div className="relative">
|
|
289
296
|
<ContextMenuWrapper
|
|
@@ -298,6 +305,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
298
305
|
hideBinary={hideBinary}
|
|
299
306
|
unit={arrow.unit}
|
|
300
307
|
profileType={profileType}
|
|
308
|
+
isSandwich={isSandwich}
|
|
301
309
|
/>
|
|
302
310
|
<MemoizedTooltip contextElement={svg.current} dockedMetainfo={dockedMetainfo} />
|
|
303
311
|
<svg
|
|
@@ -333,6 +341,10 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
|
|
|
333
341
|
setHoveringRow={highlightSimilarStacksPreference ? setHoveringRow : noop}
|
|
334
342
|
isIcicleChart={isIcicleChart}
|
|
335
343
|
profileSource={profileSource}
|
|
344
|
+
isFlamegraph={isFlamegraph}
|
|
345
|
+
isSandwich={isSandwich}
|
|
346
|
+
maxDepth={maxDepth}
|
|
347
|
+
tooltipId={tooltipId}
|
|
336
348
|
/>
|
|
337
349
|
))}
|
|
338
350
|
</svg>
|
|
@@ -167,7 +167,7 @@ export const getCurrentPathFrameData = (table: Table<any>, row: number): Current
|
|
|
167
167
|
systemName: systemName ?? '',
|
|
168
168
|
fileName: fileName ?? '',
|
|
169
169
|
lineNumber: Number(lineNumber),
|
|
170
|
-
address
|
|
170
|
+
address,
|
|
171
171
|
inlined: inlined ?? false,
|
|
172
172
|
labels: labels ?? undefined,
|
|
173
173
|
};
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {LegacyRef, ReactNode, useEffect, useMemo, useState} from 'react';
|
|
14
|
+
import React, {LegacyRef, ReactNode, useCallback, useEffect, useMemo, useState} from 'react';
|
|
15
15
|
|
|
16
|
+
import cx from 'classnames';
|
|
16
17
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
17
18
|
import {useMeasure} from 'react-use';
|
|
18
19
|
|
|
@@ -25,7 +26,7 @@ import {MergedProfileSource, ProfileSource} from '../ProfileSource';
|
|
|
25
26
|
import DiffLegend from '../ProfileView/components/DiffLegend';
|
|
26
27
|
import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
|
|
27
28
|
import {TimelineGuide} from '../TimelineGuide';
|
|
28
|
-
import {
|
|
29
|
+
import {IcicleGraphArrow} from './IcicleGraphArrow';
|
|
29
30
|
import useMappingList from './IcicleGraphArrow/useMappingList';
|
|
30
31
|
import {CurrentPathFrame, boundsFromProfileSource} from './IcicleGraphArrow/utils';
|
|
31
32
|
|
|
@@ -49,6 +50,9 @@ interface ProfileIcicleGraphProps {
|
|
|
49
50
|
metadataMappingFiles?: string[];
|
|
50
51
|
metadataLoading?: boolean;
|
|
51
52
|
isIcicleChart?: boolean;
|
|
53
|
+
isSandwichIcicleGraph?: boolean;
|
|
54
|
+
isFlamegraph?: boolean;
|
|
55
|
+
tooltipId?: string;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
const ErrorContent = ({errorMessage}: {errorMessage: string | ReactNode}): JSX.Element => {
|
|
@@ -81,12 +85,32 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
81
85
|
metadataMappingFiles,
|
|
82
86
|
isIcicleChart = false,
|
|
83
87
|
profileSource,
|
|
88
|
+
isSandwichIcicleGraph = false,
|
|
89
|
+
isFlamegraph = false,
|
|
90
|
+
tooltipId,
|
|
84
91
|
}: ProfileIcicleGraphProps): JSX.Element {
|
|
85
92
|
const {onError, authenticationErrorMessage, isDarkMode, iciclechartHelpText} = useParcaContext();
|
|
86
93
|
const {compareMode} = useProfileViewContext();
|
|
87
94
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
88
95
|
const [icicleChartRef, {height: icicleChartHeight}] = useMeasure();
|
|
89
96
|
|
|
97
|
+
// Create local state for paths when in sandwich view to avoid URL updates
|
|
98
|
+
const [localCurPathArrow, setLocalCurPathArrow] = useState<CurrentPathFrame[]>([]);
|
|
99
|
+
|
|
100
|
+
const setCurPathArrowWrapper = useCallback(
|
|
101
|
+
(path: CurrentPathFrame[]) => {
|
|
102
|
+
if (isSandwichIcicleGraph) {
|
|
103
|
+
setLocalCurPathArrow(path);
|
|
104
|
+
} else {
|
|
105
|
+
setNewCurPathArrow(path);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
[isSandwichIcicleGraph, setNewCurPathArrow]
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Determine which paths to use based on isSandwichIcicleGraph flag
|
|
112
|
+
const effectiveCurPathArrow = isSandwichIcicleGraph ? localCurPathArrow : curPathArrow;
|
|
113
|
+
|
|
90
114
|
const mappingsList = useMappingList(metadataMappingFiles);
|
|
91
115
|
|
|
92
116
|
const [colorBy, setColorBy] = useURLState('color_by');
|
|
@@ -233,14 +257,17 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
233
257
|
arrow={arrow}
|
|
234
258
|
total={total}
|
|
235
259
|
filtered={filtered}
|
|
236
|
-
curPath={
|
|
237
|
-
setCurPath={
|
|
260
|
+
curPath={effectiveCurPathArrow}
|
|
261
|
+
setCurPath={setCurPathArrowWrapper}
|
|
238
262
|
profileType={profileType}
|
|
239
263
|
isHalfScreen={isHalfScreen}
|
|
240
264
|
mappingsListFromMetadata={mappingsList}
|
|
241
265
|
compareAbsolute={isCompareAbsolute}
|
|
242
266
|
isIcicleChart={isIcicleChart}
|
|
243
267
|
profileSource={profileSource}
|
|
268
|
+
isFlamegraph={isFlamegraph}
|
|
269
|
+
isSandwich={isSandwichIcicleGraph}
|
|
270
|
+
tooltipId={tooltipId}
|
|
244
271
|
/>
|
|
245
272
|
</div>
|
|
246
273
|
</div>
|
|
@@ -253,8 +280,6 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
253
280
|
loading,
|
|
254
281
|
width,
|
|
255
282
|
filtered,
|
|
256
|
-
curPathArrow,
|
|
257
|
-
setNewCurPathArrow,
|
|
258
283
|
profileType,
|
|
259
284
|
isHalfScreen,
|
|
260
285
|
isDarkMode,
|
|
@@ -265,6 +290,11 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
265
290
|
icicleChartHeight,
|
|
266
291
|
icicleChartRef,
|
|
267
292
|
iciclechartHelpText,
|
|
293
|
+
isFlamegraph,
|
|
294
|
+
isSandwichIcicleGraph,
|
|
295
|
+
effectiveCurPathArrow,
|
|
296
|
+
setCurPathArrowWrapper,
|
|
297
|
+
tooltipId,
|
|
268
298
|
]);
|
|
269
299
|
|
|
270
300
|
useEffect(() => {
|
|
@@ -332,20 +362,22 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
332
362
|
transition={{duration: 0.5}}
|
|
333
363
|
>
|
|
334
364
|
{compareMode ? <DiffLegend /> : null}
|
|
335
|
-
<div className=
|
|
365
|
+
<div className={cx(!isSandwichIcicleGraph ? 'min-h-48' : '')} id="h-icicle-graph">
|
|
336
366
|
<>{icicleGraph}</>
|
|
337
367
|
</div>
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
368
|
+
{!isSandwichIcicleGraph && (
|
|
369
|
+
<p className="my-2 text-xs">
|
|
370
|
+
Showing {totalFormatted}{' '}
|
|
371
|
+
{isFiltered ? (
|
|
372
|
+
<span>
|
|
373
|
+
({filteredPercentage}%) filtered of {totalUnfilteredFormatted}{' '}
|
|
374
|
+
</span>
|
|
375
|
+
) : (
|
|
376
|
+
<></>
|
|
377
|
+
)}
|
|
378
|
+
values.{' '}
|
|
379
|
+
</p>
|
|
380
|
+
)}
|
|
349
381
|
</motion.div>
|
|
350
382
|
</AnimatePresence>
|
|
351
383
|
);
|
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
import {Profiler, ProfilerOnRenderCallback} from 'react';
|
|
15
15
|
|
|
16
|
+
import {QueryServiceClient} from '@parca/client';
|
|
16
17
|
import {ConditionalWrapper} from '@parca/components';
|
|
17
18
|
|
|
18
19
|
import ProfileIcicleGraph from '../../../ProfileIcicleGraph';
|
|
19
20
|
import {CurrentPathFrame} from '../../../ProfileIcicleGraph/IcicleGraphArrow/utils';
|
|
20
21
|
import {ProfileSource} from '../../../ProfileSource';
|
|
22
|
+
import Sandwich from '../../../Sandwich';
|
|
21
23
|
import {SourceView} from '../../../SourceView';
|
|
22
24
|
import {Table} from '../../../Table';
|
|
23
25
|
import type {
|
|
@@ -47,6 +49,7 @@ interface GetDashboardItemProps {
|
|
|
47
49
|
perf?: {
|
|
48
50
|
onRender?: ProfilerOnRenderCallback;
|
|
49
51
|
};
|
|
52
|
+
queryClient?: QueryServiceClient;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export const getDashboardItem = ({
|
|
@@ -65,6 +68,7 @@ export const getDashboardItem = ({
|
|
|
65
68
|
currentSearchString,
|
|
66
69
|
setSearchString,
|
|
67
70
|
perf,
|
|
71
|
+
queryClient,
|
|
68
72
|
}: GetDashboardItemProps): JSX.Element => {
|
|
69
73
|
switch (type) {
|
|
70
74
|
case 'icicle':
|
|
@@ -142,6 +146,23 @@ export const getDashboardItem = ({
|
|
|
142
146
|
) : (
|
|
143
147
|
<></>
|
|
144
148
|
);
|
|
149
|
+
case 'sandwich':
|
|
150
|
+
return topTableData != null ? (
|
|
151
|
+
<Sandwich
|
|
152
|
+
total={total}
|
|
153
|
+
filtered={filtered}
|
|
154
|
+
loading={topTableData.loading}
|
|
155
|
+
data={topTableData.arrow?.record}
|
|
156
|
+
unit={topTableData.unit}
|
|
157
|
+
profileType={profileSource?.ProfileType()}
|
|
158
|
+
isHalfScreen={isHalfScreen}
|
|
159
|
+
metadataMappingFiles={flamegraphData.metadataMappingFiles}
|
|
160
|
+
profileSource={profileSource}
|
|
161
|
+
queryClient={queryClient}
|
|
162
|
+
/>
|
|
163
|
+
) : (
|
|
164
|
+
<></>
|
|
165
|
+
);
|
|
145
166
|
case 'source':
|
|
146
167
|
return sourceData != null ? (
|
|
147
168
|
<SourceView
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
|
|
14
14
|
import {useEffect, useMemo, useState} from 'react';
|
|
15
15
|
|
|
16
|
-
import {createColumnHelper, type
|
|
16
|
+
import {createColumnHelper, type ColumnDef} from '@tanstack/table-core';
|
|
17
17
|
|
|
18
18
|
import {useURLState} from '@parca/components';
|
|
19
19
|
import {ProfileType} from '@parca/parser';
|
|
20
20
|
import {valueFormatter} from '@parca/utilities';
|
|
21
21
|
|
|
22
|
-
import {Row
|
|
22
|
+
import {Row} from '../../../Table';
|
|
23
23
|
import ColumnsVisibility from '../../../Table/ColumnsVisibility';
|
|
24
|
-
import {ColumnName,
|
|
24
|
+
import {ColumnName, addPlusSign, getRatioString} from '../../../Table/utils/functions';
|
|
25
25
|
import {useProfileViewContext} from '../../context/ProfileViewContext';
|
|
26
26
|
|
|
27
27
|
interface Props {
|
|
@@ -45,7 +45,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
45
45
|
columnHelper.accessor('flat', {
|
|
46
46
|
id: 'flat',
|
|
47
47
|
header: 'Flat',
|
|
48
|
-
cell: info => valueFormatter(
|
|
48
|
+
cell: info => valueFormatter(info.getValue(), unit, 2),
|
|
49
49
|
size: 80,
|
|
50
50
|
meta: {
|
|
51
51
|
align: 'right',
|
|
@@ -56,10 +56,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
56
56
|
id: 'flatPercentage',
|
|
57
57
|
header: 'Flat (%)',
|
|
58
58
|
cell: info => {
|
|
59
|
-
|
|
60
|
-
return '';
|
|
61
|
-
}
|
|
62
|
-
return getRatioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
|
|
59
|
+
return getRatioString(info.getValue(), total, filtered);
|
|
63
60
|
},
|
|
64
61
|
size: 120,
|
|
65
62
|
meta: {
|
|
@@ -70,8 +67,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
70
67
|
columnHelper.accessor('flatDiff', {
|
|
71
68
|
id: 'flatDiff',
|
|
72
69
|
header: 'Flat Diff',
|
|
73
|
-
cell: info =>
|
|
74
|
-
addPlusSign(valueFormatter((info as CellContext<DataRow, bigint>).getValue(), unit, 2)),
|
|
70
|
+
cell: info => addPlusSign(valueFormatter(info.getValue(), unit, 2)),
|
|
75
71
|
size: 120,
|
|
76
72
|
meta: {
|
|
77
73
|
align: 'right',
|
|
@@ -82,10 +78,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
82
78
|
id: 'flatDiffPercentage',
|
|
83
79
|
header: 'Flat Diff (%)',
|
|
84
80
|
cell: info => {
|
|
85
|
-
|
|
86
|
-
return '';
|
|
87
|
-
}
|
|
88
|
-
return getRatioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
|
|
81
|
+
return getRatioString(info.getValue(), total, filtered);
|
|
89
82
|
},
|
|
90
83
|
size: 120,
|
|
91
84
|
meta: {
|
|
@@ -96,7 +89,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
96
89
|
columnHelper.accessor('cumulative', {
|
|
97
90
|
id: 'cumulative',
|
|
98
91
|
header: 'Cumulative',
|
|
99
|
-
cell: info => valueFormatter(
|
|
92
|
+
cell: info => valueFormatter(info.getValue(), unit, 2),
|
|
100
93
|
size: 150,
|
|
101
94
|
meta: {
|
|
102
95
|
align: 'right',
|
|
@@ -107,10 +100,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
107
100
|
id: 'cumulativePercentage',
|
|
108
101
|
header: 'Cumulative (%)',
|
|
109
102
|
cell: info => {
|
|
110
|
-
|
|
111
|
-
return '';
|
|
112
|
-
}
|
|
113
|
-
return getRatioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
|
|
103
|
+
return getRatioString(info.getValue(), total, filtered);
|
|
114
104
|
},
|
|
115
105
|
size: 150,
|
|
116
106
|
meta: {
|
|
@@ -121,8 +111,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
121
111
|
columnHelper.accessor('cumulativeDiff', {
|
|
122
112
|
id: 'cumulativeDiff',
|
|
123
113
|
header: 'Cumulative Diff',
|
|
124
|
-
cell: info =>
|
|
125
|
-
addPlusSign(valueFormatter((info as CellContext<DataRow, bigint>).getValue(), unit, 2)),
|
|
114
|
+
cell: info => addPlusSign(valueFormatter(info.getValue(), unit, 2)),
|
|
126
115
|
size: 170,
|
|
127
116
|
meta: {
|
|
128
117
|
align: 'right',
|
|
@@ -133,10 +122,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
|
|
|
133
122
|
id: 'cumulativeDiffPercentage',
|
|
134
123
|
header: 'Cumulative Diff (%)',
|
|
135
124
|
cell: info => {
|
|
136
|
-
|
|
137
|
-
return '';
|
|
138
|
-
}
|
|
139
|
-
return getRatioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
|
|
125
|
+
return getRatioString(info.getValue(), total, filtered);
|
|
140
126
|
},
|
|
141
127
|
size: 170,
|
|
142
128
|
meta: {
|