@parca/profile 0.18.3 → 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 +10 -0
- package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/useGraphTooltip/index.js +6 -3
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -1
- package/dist/MetricsGraph/UtilizationMetrics/index.js +6 -2
- package/dist/MetricsGraph/index.d.ts.map +1 -1
- package/dist/MetricsGraph/index.js +8 -4
- package/dist/MetricsSeries/index.d.ts +2 -1
- package/dist/MetricsSeries/index.d.ts.map +1 -1
- package/dist/MetricsSeries/index.js +2 -1
- 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/MetricsGraph/UtilizationMetrics/index.tsx +6 -2
- package/src/MetricsGraph/index.tsx +12 -2
- package/src/MetricsSeries/index.tsx +3 -0
- 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
|
|
|
@@ -129,7 +129,11 @@ const RawUtilizationMetrics = ({
|
|
|
129
129
|
const lineStrokeHover = '2px';
|
|
130
130
|
const lineStrokeSelected = '3px';
|
|
131
131
|
|
|
132
|
-
const graphWidth = width - margin * 1.5 - margin / 2;
|
|
132
|
+
const graphWidth = useMemo(() => width - margin * 1.5 - margin / 2, [width, margin]);
|
|
133
|
+
const graphTransform = useMemo(() => {
|
|
134
|
+
// Adds 10px padding which aligns the graph on the grid
|
|
135
|
+
return `translate(10, 0) scale(${(graphWidth - 10) / graphWidth}, 1)`;
|
|
136
|
+
}, [graphWidth]);
|
|
133
137
|
|
|
134
138
|
const paddedFrom = from;
|
|
135
139
|
const paddedTo = to;
|
|
@@ -461,7 +465,7 @@ const RawUtilizationMetrics = ({
|
|
|
461
465
|
</text>
|
|
462
466
|
</g>
|
|
463
467
|
</g>
|
|
464
|
-
<g className="lines fill-transparent">
|
|
468
|
+
<g className="lines fill-transparent" transform={graphTransform}>
|
|
465
469
|
{series.map((s, i) => {
|
|
466
470
|
const isLimit =
|
|
467
471
|
s.metric.findIndex(m => m.name === '__type__' && m.value === 'limit') > -1;
|
|
@@ -158,7 +158,11 @@ export const RawMetricsGraph = ({
|
|
|
158
158
|
width = 0;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
const graphWidth = width - margin * 1.5 - margin / 2;
|
|
161
|
+
const graphWidth = useMemo(() => width - margin * 1.5 - margin / 2, [width, margin]);
|
|
162
|
+
const graphTransform = useMemo(() => {
|
|
163
|
+
// Adds 6px padding which aligns the graph on the grid
|
|
164
|
+
return `translate(6, 0) scale(${(graphWidth - 6) / graphWidth}, 1)`;
|
|
165
|
+
}, [graphWidth]);
|
|
162
166
|
|
|
163
167
|
const series: Series[] = data.reduce<Series[]>(function (agg: Series[], s: MetricsSeriesPb) {
|
|
164
168
|
if (s.labelset !== undefined) {
|
|
@@ -595,7 +599,11 @@ export const RawMetricsGraph = ({
|
|
|
595
599
|
</text>
|
|
596
600
|
</g>
|
|
597
601
|
</g>
|
|
598
|
-
<g
|
|
602
|
+
<g
|
|
603
|
+
className="lines fill-transparent"
|
|
604
|
+
transform={graphTransform}
|
|
605
|
+
width={graphWidth - 100}
|
|
606
|
+
>
|
|
599
607
|
{series.map((s, i) => (
|
|
600
608
|
<g key={i} className="line">
|
|
601
609
|
<MetricsSeries
|
|
@@ -618,6 +626,7 @@ export const RawMetricsGraph = ({
|
|
|
618
626
|
className="circle-group"
|
|
619
627
|
ref={metricPointRef}
|
|
620
628
|
style={{fill: color(highlighted.seriesIndex.toString())}}
|
|
629
|
+
transform={graphTransform}
|
|
621
630
|
>
|
|
622
631
|
<MetricsCircle cx={highlighted.x} cy={highlighted.y} />
|
|
623
632
|
</g>
|
|
@@ -630,6 +639,7 @@ export const RawMetricsGraph = ({
|
|
|
630
639
|
? {fill: color(selected.seriesIndex.toString())}
|
|
631
640
|
: {}
|
|
632
641
|
}
|
|
642
|
+
transform={graphTransform}
|
|
633
643
|
>
|
|
634
644
|
<MetricsCircle cx={selected.x} cy={selected.y} radius={5} />
|
|
635
645
|
</g>
|
|
@@ -19,6 +19,7 @@ interface MetricsSeriesProps {
|
|
|
19
19
|
color: string;
|
|
20
20
|
strokeWidth: string;
|
|
21
21
|
strokeDasharray?: string;
|
|
22
|
+
strokeLinecap?: React.CSSProperties['strokeLinecap'];
|
|
22
23
|
xScale: (input: number) => number;
|
|
23
24
|
yScale: (input: number) => number;
|
|
24
25
|
onClick?: () => void;
|
|
@@ -30,6 +31,7 @@ const MetricsSeries = ({
|
|
|
30
31
|
color,
|
|
31
32
|
strokeWidth,
|
|
32
33
|
strokeDasharray = '',
|
|
34
|
+
strokeLinecap = 'round',
|
|
33
35
|
onClick,
|
|
34
36
|
}: MetricsSeriesProps): JSX.Element => (
|
|
35
37
|
<g className="line-group">
|
|
@@ -40,6 +42,7 @@ const MetricsSeries = ({
|
|
|
40
42
|
stroke: color,
|
|
41
43
|
strokeWidth,
|
|
42
44
|
strokeDasharray,
|
|
45
|
+
strokeLinecap,
|
|
43
46
|
}}
|
|
44
47
|
onClick={onClick}
|
|
45
48
|
/>
|
|
@@ -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
|