@parca/profile 0.19.12 → 0.19.13
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 +1 -1
- package/dist/GraphTooltipArrow/index.js +2 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +12 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.js +9 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +7 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.js +17 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +2 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +23 -8
- package/dist/ProfileIcicleGraph/index.d.ts +3 -1
- package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/index.js +6 -4
- package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
- package/dist/ProfileView/components/DashboardItems/index.js +1 -1
- package/dist/ProfileView/components/ShareButton/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ShareButton/index.js +1 -1
- package/dist/ProfileView/components/Toolbars/index.d.ts +0 -2
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/index.js +4 -5
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/index.js +18 -11
- package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
- package/dist/ProfileView/context/DashboardContext.js +5 -0
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +4 -3
- package/dist/Sandwich/components/CalleesSection.d.ts +1 -2
- package/dist/Sandwich/components/CalleesSection.d.ts.map +1 -1
- package/dist/Sandwich/components/CalleesSection.js +2 -6
- package/dist/Sandwich/components/CallersSection.d.ts +4 -2
- package/dist/Sandwich/components/CallersSection.d.ts.map +1 -1
- package/dist/Sandwich/components/CallersSection.js +45 -9
- package/dist/Sandwich/components/TableSection.js +1 -1
- package/dist/Sandwich/index.d.ts +0 -1
- package/dist/Sandwich/index.d.ts.map +1 -1
- package/dist/Sandwich/index.js +27 -79
- package/dist/Table/MoreDropdown.d.ts.map +1 -1
- package/dist/Table/MoreDropdown.js +1 -2
- package/dist/Table/TableContextMenu.d.ts +9 -0
- package/dist/Table/TableContextMenu.d.ts.map +1 -0
- package/dist/Table/TableContextMenu.js +38 -0
- package/dist/Table/TableContextMenuWrapper.d.ts +10 -0
- package/dist/Table/TableContextMenuWrapper.d.ts.map +1 -0
- package/dist/Table/TableContextMenuWrapper.js +30 -0
- package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
- package/dist/Table/hooks/useTableConfiguration.js +2 -20
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +65 -5
- package/dist/styles.css +1 -1
- package/package.json +3 -3
- package/src/GraphTooltipArrow/Content.tsx +3 -3
- package/src/GraphTooltipArrow/index.tsx +2 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +19 -3
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.tsx +10 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +19 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.tsx +20 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +40 -6
- package/src/ProfileIcicleGraph/index.tsx +20 -2
- package/src/ProfileView/components/DashboardItems/index.tsx +0 -1
- package/src/ProfileView/components/ShareButton/index.tsx +9 -3
- package/src/ProfileView/components/Toolbars/index.tsx +7 -23
- package/src/ProfileView/components/ViewSelector/index.tsx +20 -11
- package/src/ProfileView/context/DashboardContext.tsx +6 -0
- package/src/ProfileView/index.tsx +12 -4
- package/src/Sandwich/components/CalleesSection.tsx +1 -7
- package/src/Sandwich/components/CallersSection.tsx +92 -35
- package/src/Sandwich/components/TableSection.tsx +2 -2
- package/src/Sandwich/index.tsx +20 -107
- package/src/Table/MoreDropdown.tsx +1 -2
- package/src/Table/TableContextMenu.tsx +70 -0
- package/src/Table/TableContextMenuWrapper.tsx +48 -0
- package/src/Table/hooks/useTableConfiguration.tsx +2 -25
- package/src/Table/index.tsx +84 -5
|
@@ -18,7 +18,12 @@ import {AnimatePresence, motion} from 'framer-motion';
|
|
|
18
18
|
import {useMeasure} from 'react-use';
|
|
19
19
|
|
|
20
20
|
import {FlamegraphArrow} from '@parca/client';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
FlamegraphSkeleton,
|
|
23
|
+
IcicleGraphSkeleton,
|
|
24
|
+
useParcaContext,
|
|
25
|
+
useURLState,
|
|
26
|
+
} from '@parca/components';
|
|
22
27
|
import {ProfileType} from '@parca/parser';
|
|
23
28
|
import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
|
|
24
29
|
|
|
@@ -53,6 +58,8 @@ interface ProfileIcicleGraphProps {
|
|
|
53
58
|
isSandwichIcicleGraph?: boolean;
|
|
54
59
|
isFlamegraph?: boolean;
|
|
55
60
|
tooltipId?: string;
|
|
61
|
+
maxFrameCount?: number;
|
|
62
|
+
isExpanded?: boolean;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
const ErrorContent = ({errorMessage}: {errorMessage: string | ReactNode}): JSX.Element => {
|
|
@@ -88,6 +95,8 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
88
95
|
isSandwichIcicleGraph = false,
|
|
89
96
|
isFlamegraph = false,
|
|
90
97
|
tooltipId,
|
|
98
|
+
maxFrameCount,
|
|
99
|
+
isExpanded = false,
|
|
91
100
|
}: ProfileIcicleGraphProps): JSX.Element {
|
|
92
101
|
const {onError, authenticationErrorMessage, isDarkMode, iciclechartHelpText} = useParcaContext();
|
|
93
102
|
const {compareMode} = useProfileViewContext();
|
|
@@ -184,10 +193,15 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
184
193
|
? validateIcicleChartQuery(profileSource as MergedProfileSource)
|
|
185
194
|
: {isValid: true, isNonDelta: false, isDurationTooLong: false};
|
|
186
195
|
const isInvalidIcicleChartQuery = isIcicleChart && !isIcicleChartValid;
|
|
196
|
+
|
|
187
197
|
if (isLoading && !isInvalidIcicleChartQuery) {
|
|
188
198
|
return (
|
|
189
199
|
<div className="h-auto overflow-clip">
|
|
190
|
-
|
|
200
|
+
{isFlamegraph ? (
|
|
201
|
+
<FlamegraphSkeleton isHalfScreen={isHalfScreen} isDarkMode={isDarkMode} />
|
|
202
|
+
) : (
|
|
203
|
+
<IcicleGraphSkeleton isHalfScreen={isHalfScreen} isDarkMode={isDarkMode} />
|
|
204
|
+
)}
|
|
191
205
|
</div>
|
|
192
206
|
);
|
|
193
207
|
}
|
|
@@ -268,6 +282,8 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
268
282
|
isFlamegraph={isFlamegraph}
|
|
269
283
|
isSandwich={isSandwichIcicleGraph}
|
|
270
284
|
tooltipId={tooltipId}
|
|
285
|
+
maxFrameCount={maxFrameCount}
|
|
286
|
+
isExpanded={isExpanded}
|
|
271
287
|
/>
|
|
272
288
|
</div>
|
|
273
289
|
</div>
|
|
@@ -295,6 +311,8 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
295
311
|
effectiveCurPathArrow,
|
|
296
312
|
setCurPathArrowWrapper,
|
|
297
313
|
tooltipId,
|
|
314
|
+
maxFrameCount,
|
|
315
|
+
isExpanded,
|
|
298
316
|
]);
|
|
299
317
|
|
|
300
318
|
useEffect(() => {
|
|
@@ -155,7 +155,6 @@ export const getDashboardItem = ({
|
|
|
155
155
|
data={topTableData.arrow?.record}
|
|
156
156
|
unit={topTableData.unit}
|
|
157
157
|
profileType={profileSource?.ProfileType()}
|
|
158
|
-
isHalfScreen={isHalfScreen}
|
|
159
158
|
metadataMappingFiles={flamegraphData.metadataMappingFiles}
|
|
160
159
|
profileSource={profileSource}
|
|
161
160
|
queryClient={queryClient}
|
|
@@ -182,11 +182,17 @@ const ShareButton = ({
|
|
|
182
182
|
element={
|
|
183
183
|
<Button
|
|
184
184
|
variant="neutral"
|
|
185
|
-
className="flex items-center gap-2"
|
|
185
|
+
className="flex items-center gap-2 pr-[1.7rem]"
|
|
186
186
|
id="h-share-dropdown-button"
|
|
187
187
|
>
|
|
188
|
-
<
|
|
189
|
-
|
|
188
|
+
<div className="flex items-center gap-2">
|
|
189
|
+
<Icon icon="material-symbols:share" className="w-4 h-4" />
|
|
190
|
+
|
|
191
|
+
<span>Share</span>
|
|
192
|
+
</div>
|
|
193
|
+
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 text-gray-400">
|
|
194
|
+
<Icon icon="heroicons:chevron-down-20-solid" aria-hidden="true" />
|
|
195
|
+
</div>
|
|
190
196
|
</Button>
|
|
191
197
|
}
|
|
192
198
|
>
|
|
@@ -16,7 +16,7 @@ import {FC} from 'react';
|
|
|
16
16
|
import {Icon} from '@iconify/react';
|
|
17
17
|
|
|
18
18
|
import {QueryServiceClient} from '@parca/client';
|
|
19
|
-
import {Button
|
|
19
|
+
import {Button} from '@parca/components';
|
|
20
20
|
import {ProfileType} from '@parca/parser';
|
|
21
21
|
|
|
22
22
|
import {CurrentPathFrame} from '../../../ProfileIcicleGraph/IcicleGraphArrow/utils';
|
|
@@ -52,8 +52,6 @@ export interface VisualisationToolbarProps {
|
|
|
52
52
|
setGroupByLabels: (labels: string[]) => void;
|
|
53
53
|
showVisualizationSelector?: boolean;
|
|
54
54
|
sandwichFunctionName?: string;
|
|
55
|
-
setSandwichFunctionName: (sandwichFunctionName: string | undefined) => void;
|
|
56
|
-
resetSandwichFunctionName: () => void;
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
export interface TableToolbarProps {
|
|
@@ -150,7 +148,6 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
|
150
148
|
groupByLabels,
|
|
151
149
|
setGroupByLabels,
|
|
152
150
|
profileType,
|
|
153
|
-
preferencesModal,
|
|
154
151
|
profileSource,
|
|
155
152
|
queryClient,
|
|
156
153
|
onDownloadPProf,
|
|
@@ -163,17 +160,13 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
|
163
160
|
currentSearchString,
|
|
164
161
|
clearSelection,
|
|
165
162
|
showVisualizationSelector = true,
|
|
166
|
-
resetSandwichFunctionName,
|
|
167
|
-
sandwichFunctionName,
|
|
168
163
|
}) => {
|
|
169
164
|
const {dashboardItems} = useDashboard();
|
|
170
165
|
|
|
171
166
|
const isTableViz = dashboardItems?.includes('table');
|
|
172
167
|
const isTableVizOnly = dashboardItems?.length === 1 && isTableViz;
|
|
173
168
|
const isGraphViz = dashboardItems?.includes('icicle');
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
const isTableView = isTableVizOnly || isSandwichIcicleGraphViz;
|
|
169
|
+
const isGraphVizOnly = dashboardItems?.length === 1 && isGraphViz;
|
|
177
170
|
|
|
178
171
|
const req = profileSource?.QueryRequest();
|
|
179
172
|
if (req !== null && req !== undefined) {
|
|
@@ -186,7 +179,7 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
|
186
179
|
<>
|
|
187
180
|
<div className="flex w-full justify-between items-end">
|
|
188
181
|
<div className="flex gap-3 items-end">
|
|
189
|
-
{
|
|
182
|
+
{isGraphViz && (
|
|
190
183
|
<>
|
|
191
184
|
<GroupByDropdown
|
|
192
185
|
groupBy={groupBy}
|
|
@@ -203,13 +196,12 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
|
203
196
|
{profileViewExternalSubActions != null ? profileViewExternalSubActions : null}
|
|
204
197
|
</div>
|
|
205
198
|
<div className="flex gap-3">
|
|
206
|
-
{preferencesModal === true && <UserPreferencesModal />}
|
|
207
199
|
<MultiLevelDropdown
|
|
208
200
|
groupBy={groupBy}
|
|
209
201
|
toggleGroupBy={toggleGroupBy}
|
|
210
202
|
profileType={profileType}
|
|
211
203
|
onSelect={() => {}}
|
|
212
|
-
isTableVizOnly={
|
|
204
|
+
isTableVizOnly={isTableVizOnly}
|
|
213
205
|
/>
|
|
214
206
|
|
|
215
207
|
<ShareButton
|
|
@@ -224,13 +216,14 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
|
224
216
|
{showVisualizationSelector ? <ViewSelector profileSource={profileSource} /> : null}
|
|
225
217
|
</div>
|
|
226
218
|
</div>
|
|
227
|
-
|
|
219
|
+
|
|
220
|
+
{isGraphVizOnly && (
|
|
228
221
|
<>
|
|
229
222
|
<Divider />
|
|
230
223
|
<IcicleGraphToolbar curPath={curPath} setNewCurPath={setNewCurPath} />
|
|
231
224
|
</>
|
|
232
225
|
)}
|
|
233
|
-
{
|
|
226
|
+
{isTableVizOnly && (
|
|
234
227
|
<>
|
|
235
228
|
<Divider />
|
|
236
229
|
<TableToolbar
|
|
@@ -242,15 +235,6 @@ export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
|
242
235
|
/>
|
|
243
236
|
</>
|
|
244
237
|
)}
|
|
245
|
-
{isSandwichIcicleGraphViz && (
|
|
246
|
-
<>
|
|
247
|
-
<Divider />
|
|
248
|
-
<SandwichIcicleGraphToolbar
|
|
249
|
-
resetSandwichFunctionName={resetSandwichFunctionName}
|
|
250
|
-
sandwichFunctionName={sandwichFunctionName}
|
|
251
|
-
/>
|
|
252
|
-
</>
|
|
253
|
-
)}
|
|
254
238
|
</>
|
|
255
239
|
);
|
|
256
240
|
};
|
|
@@ -29,6 +29,7 @@ const ViewSelector = ({profileSource}: Props): JSX.Element => {
|
|
|
29
29
|
alwaysReturnArray: true,
|
|
30
30
|
}
|
|
31
31
|
);
|
|
32
|
+
const [, setSandwichFunctionName] = useURLState<string | undefined>('sandwich_function_name');
|
|
32
33
|
const {enableSourcesView, enableSandwichView} = useParcaContext();
|
|
33
34
|
|
|
34
35
|
const allItems: Array<{
|
|
@@ -38,8 +39,8 @@ const ViewSelector = ({profileSource}: Props): JSX.Element => {
|
|
|
38
39
|
supportingText?: string;
|
|
39
40
|
disabledText?: string;
|
|
40
41
|
}> = [
|
|
41
|
-
{key: 'table', label: 'Table', canBeSelected: !dashboardItems.includes('table')},
|
|
42
42
|
{key: 'icicle', label: 'icicle', canBeSelected: !dashboardItems.includes('icicle')},
|
|
43
|
+
{key: 'table', label: 'Table', canBeSelected: !dashboardItems.includes('table')},
|
|
43
44
|
{
|
|
44
45
|
key: 'iciclechart',
|
|
45
46
|
label: (
|
|
@@ -105,14 +106,8 @@ const ViewSelector = ({profileSource}: Props): JSX.Element => {
|
|
|
105
106
|
}): InnerAction | undefined => {
|
|
106
107
|
if (dashboardItems.length === 1 && item.key === dashboardItems[0]) return undefined;
|
|
107
108
|
|
|
108
|
-
//
|
|
109
|
-
if (item.key
|
|
110
|
-
return {
|
|
111
|
-
text: 'Add Panel',
|
|
112
|
-
onClick: () => {},
|
|
113
|
-
isDisabled: true, // Custom property to control button state
|
|
114
|
-
};
|
|
115
|
-
}
|
|
109
|
+
// If we already have 2 panels and this item isn't selected, don't show any action
|
|
110
|
+
if (dashboardItems.length >= 2 && !dashboardItems.includes(item.key)) return undefined;
|
|
116
111
|
|
|
117
112
|
return {
|
|
118
113
|
text:
|
|
@@ -120,12 +115,20 @@ const ViewSelector = ({profileSource}: Props): JSX.Element => {
|
|
|
120
115
|
? 'Add Panel'
|
|
121
116
|
: item.canBeSelected
|
|
122
117
|
? 'Add Panel'
|
|
123
|
-
:
|
|
118
|
+
: dashboardItems.includes(item.key)
|
|
119
|
+
? 'Close Panel'
|
|
120
|
+
: 'Add Panel',
|
|
124
121
|
onClick: () => {
|
|
125
122
|
if (item.canBeSelected) {
|
|
126
123
|
setDashboardItems([...dashboardItems, item.key]);
|
|
127
124
|
} else {
|
|
128
|
-
|
|
125
|
+
const newDashboardItems = dashboardItems.filter(v => v !== item.key);
|
|
126
|
+
setDashboardItems(newDashboardItems);
|
|
127
|
+
|
|
128
|
+
// Reset sandwich function name when removing sandwich panel
|
|
129
|
+
if (item.key === 'sandwich') {
|
|
130
|
+
setSandwichFunctionName(undefined);
|
|
131
|
+
}
|
|
129
132
|
}
|
|
130
133
|
},
|
|
131
134
|
isDisabled: dashboardItems.length === 1 && dashboardItems.includes('sandwich'),
|
|
@@ -142,6 +145,12 @@ const ViewSelector = ({profileSource}: Props): JSX.Element => {
|
|
|
142
145
|
|
|
143
146
|
const onSelection = (value: string): void => {
|
|
144
147
|
const isOnlyChart = dashboardItems.length === 1;
|
|
148
|
+
|
|
149
|
+
if (isOnlyChart && value === 'sandwich') {
|
|
150
|
+
setDashboardItems([...dashboardItems, value]);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
145
154
|
if (isOnlyChart) {
|
|
146
155
|
setDashboardItems([value]);
|
|
147
156
|
return;
|
|
@@ -30,10 +30,16 @@ export const DashboardProvider: FC<PropsWithChildren> = ({children}) => {
|
|
|
30
30
|
const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
|
|
31
31
|
alwaysReturnArray: true,
|
|
32
32
|
});
|
|
33
|
+
const [, setSandwichFunctionName] = useURLState<string | undefined>('sandwich_function_name');
|
|
33
34
|
|
|
34
35
|
const handleClosePanel = (visualizationType: VisualizationType): void => {
|
|
35
36
|
const newDashboardItems = dashboardItems.filter(item => item !== visualizationType);
|
|
36
37
|
setDashboardItems(newDashboardItems);
|
|
38
|
+
|
|
39
|
+
// Reset sandwich function name when closing sandwich panel
|
|
40
|
+
if (visualizationType === 'sandwich') {
|
|
41
|
+
setSandwichFunctionName(undefined);
|
|
42
|
+
}
|
|
37
43
|
};
|
|
38
44
|
|
|
39
45
|
const isMultiPanelView = dashboardItems.length > 1;
|
|
@@ -19,7 +19,12 @@ import ColorStackLegend from './components/ColorStackLegend';
|
|
|
19
19
|
import {getDashboardItem} from './components/DashboardItems';
|
|
20
20
|
import {DashboardLayout} from './components/DashboardLayout';
|
|
21
21
|
import {ProfileHeader} from './components/ProfileHeader';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
IcicleGraphToolbar,
|
|
24
|
+
SandwichIcicleGraphToolbar,
|
|
25
|
+
TableToolbar,
|
|
26
|
+
VisualisationToolbar,
|
|
27
|
+
} from './components/Toolbars';
|
|
23
28
|
import {DashboardProvider} from './context/DashboardContext';
|
|
24
29
|
import {ProfileViewContextProvider} from './context/ProfileViewContext';
|
|
25
30
|
import {useProfileMetadata} from './hooks/useProfileMetadata';
|
|
@@ -63,7 +68,6 @@ export const ProfileView = ({
|
|
|
63
68
|
clearSelection,
|
|
64
69
|
setGroupByLabels,
|
|
65
70
|
sandwichFunctionName,
|
|
66
|
-
setSandwichFunctionName,
|
|
67
71
|
resetSandwichFunctionName,
|
|
68
72
|
} = useVisualizationState();
|
|
69
73
|
|
|
@@ -119,6 +123,12 @@ export const ProfileView = ({
|
|
|
119
123
|
currentSearchString={currentSearchString}
|
|
120
124
|
/>
|
|
121
125
|
),
|
|
126
|
+
sandwich: (
|
|
127
|
+
<SandwichIcicleGraphToolbar
|
|
128
|
+
resetSandwichFunctionName={resetSandwichFunctionName}
|
|
129
|
+
sandwichFunctionName={sandwichFunctionName}
|
|
130
|
+
/>
|
|
131
|
+
),
|
|
122
132
|
};
|
|
123
133
|
|
|
124
134
|
const hasProfileSource = profileSource !== undefined && profileSource.toString(timezone) !== '';
|
|
@@ -154,8 +164,6 @@ export const ProfileView = ({
|
|
|
154
164
|
setGroupByLabels={setGroupByLabels}
|
|
155
165
|
showVisualizationSelector={showVisualizationSelector}
|
|
156
166
|
sandwichFunctionName={sandwichFunctionName}
|
|
157
|
-
setSandwichFunctionName={setSandwichFunctionName}
|
|
158
|
-
resetSandwichFunctionName={resetSandwichFunctionName}
|
|
159
167
|
/>
|
|
160
168
|
|
|
161
169
|
{isColorStackLegendEnabled && (
|
|
@@ -21,7 +21,6 @@ import {type ProfileSource} from '../../ProfileSource';
|
|
|
21
21
|
|
|
22
22
|
interface CalleesSectionProps {
|
|
23
23
|
calleesRef: React.RefObject<HTMLDivElement>;
|
|
24
|
-
isHalfScreen: boolean;
|
|
25
24
|
calleesFlamegraphResponse?: {
|
|
26
25
|
report: {
|
|
27
26
|
oneofKind: string;
|
|
@@ -40,7 +39,6 @@ interface CalleesSectionProps {
|
|
|
40
39
|
|
|
41
40
|
export function CalleesSection({
|
|
42
41
|
calleesRef,
|
|
43
|
-
isHalfScreen,
|
|
44
42
|
calleesFlamegraphResponse,
|
|
45
43
|
calleesFlamegraphLoading,
|
|
46
44
|
calleesFlamegraphError,
|
|
@@ -68,11 +66,7 @@ export function CalleesSection({
|
|
|
68
66
|
error={calleesFlamegraphError}
|
|
69
67
|
isHalfScreen={true}
|
|
70
68
|
width={
|
|
71
|
-
calleesRef.current != null
|
|
72
|
-
? isHalfScreen
|
|
73
|
-
? (calleesRef.current.getBoundingClientRect().width - 54) / 2
|
|
74
|
-
: calleesRef.current.getBoundingClientRect().width - 16
|
|
75
|
-
: 0
|
|
69
|
+
calleesRef.current != null ? calleesRef.current.getBoundingClientRect().width - 25 : 0
|
|
76
70
|
}
|
|
77
71
|
metadataMappingFiles={metadataMappingFiles}
|
|
78
72
|
metadataLoading={false}
|
|
@@ -11,17 +11,33 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React from 'react';
|
|
14
|
+
import React, {useMemo} from 'react';
|
|
15
|
+
|
|
16
|
+
import {Vector, tableFromIPC} from 'apache-arrow';
|
|
17
|
+
import {Tooltip} from 'react-tooltip';
|
|
15
18
|
|
|
16
19
|
import {type FlamegraphArrow} from '@parca/client';
|
|
20
|
+
import {Button} from '@parca/components';
|
|
17
21
|
|
|
18
22
|
import ProfileIcicleGraph from '../../ProfileIcicleGraph';
|
|
19
23
|
import {type CurrentPathFrame} from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
|
|
20
24
|
import {type ProfileSource} from '../../ProfileSource';
|
|
21
25
|
|
|
26
|
+
const FIELD_DEPTH = 'depth';
|
|
27
|
+
|
|
28
|
+
function getMaxDepth(depthColumn: Vector<any> | null): number {
|
|
29
|
+
if (depthColumn === null) return 0;
|
|
30
|
+
|
|
31
|
+
let max = 0;
|
|
32
|
+
for (const val of depthColumn) {
|
|
33
|
+
const numVal = Number(val);
|
|
34
|
+
if (numVal > max) max = numVal;
|
|
35
|
+
}
|
|
36
|
+
return max;
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
interface CallersSectionProps {
|
|
23
40
|
callersRef: React.RefObject<HTMLDivElement>;
|
|
24
|
-
isHalfScreen: boolean;
|
|
25
41
|
callersFlamegraphResponse?: {
|
|
26
42
|
report: {
|
|
27
43
|
oneofKind: string;
|
|
@@ -36,11 +52,13 @@ interface CallersSectionProps {
|
|
|
36
52
|
curPathArrow: CurrentPathFrame[];
|
|
37
53
|
setCurPathArrow: (path: CurrentPathFrame[]) => void;
|
|
38
54
|
metadataMappingFiles?: string[];
|
|
55
|
+
isExpanded: boolean;
|
|
56
|
+
setIsExpanded: (isExpanded: boolean) => void;
|
|
57
|
+
defaultMaxFrames: number;
|
|
39
58
|
}
|
|
40
59
|
|
|
41
60
|
export function CallersSection({
|
|
42
61
|
callersRef,
|
|
43
|
-
isHalfScreen,
|
|
44
62
|
callersFlamegraphResponse,
|
|
45
63
|
callersFlamegraphLoading,
|
|
46
64
|
callersFlamegraphError,
|
|
@@ -49,40 +67,79 @@ export function CallersSection({
|
|
|
49
67
|
curPathArrow,
|
|
50
68
|
setCurPathArrow,
|
|
51
69
|
metadataMappingFiles,
|
|
70
|
+
isExpanded,
|
|
71
|
+
setIsExpanded,
|
|
72
|
+
defaultMaxFrames,
|
|
52
73
|
}: CallersSectionProps): JSX.Element {
|
|
74
|
+
const maxDepth = useMemo(() => {
|
|
75
|
+
if (
|
|
76
|
+
callersFlamegraphResponse?.report.oneofKind === 'flamegraphArrow' &&
|
|
77
|
+
callersFlamegraphResponse?.report?.flamegraphArrow != null
|
|
78
|
+
) {
|
|
79
|
+
const table = tableFromIPC(callersFlamegraphResponse.report.flamegraphArrow.record);
|
|
80
|
+
const depthColumn = table.getChild(FIELD_DEPTH);
|
|
81
|
+
return getMaxDepth(depthColumn);
|
|
82
|
+
}
|
|
83
|
+
return 0;
|
|
84
|
+
}, [callersFlamegraphResponse]);
|
|
85
|
+
|
|
86
|
+
const shouldShowButton = maxDepth > defaultMaxFrames;
|
|
87
|
+
|
|
53
88
|
return (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
<>
|
|
90
|
+
{shouldShowButton && (
|
|
91
|
+
<Button
|
|
92
|
+
variant="neutral"
|
|
93
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
94
|
+
className="absolute right-8 top-[-46px] z-10"
|
|
95
|
+
type="button"
|
|
96
|
+
>
|
|
97
|
+
<span
|
|
98
|
+
data-tooltip-content={
|
|
99
|
+
!isExpanded
|
|
100
|
+
? `This profile has ${maxDepth} frames, showing only the top ${defaultMaxFrames} frames. Click to show more frames.`
|
|
101
|
+
: `This profile has ${maxDepth} frames, showing all frames. Click to hide frames.`
|
|
102
|
+
}
|
|
103
|
+
data-tooltip-id="show-more-frames"
|
|
104
|
+
>
|
|
105
|
+
{isExpanded ? 'Hide frames' : 'Show more frames'}
|
|
106
|
+
</span>
|
|
107
|
+
<Tooltip id="show-more-frames" />
|
|
108
|
+
</Button>
|
|
109
|
+
)}
|
|
110
|
+
<div className="flex relative flex-row overflow-hidden" ref={callersRef}>
|
|
111
|
+
<div className="[writing-mode:vertical-lr] -rotate-180 px-1 uppercase text-[10px] text-left flex-shrink-0">
|
|
112
|
+
Callers {'->'}
|
|
113
|
+
</div>
|
|
114
|
+
<div className="flex-1 overflow-hidden relative">
|
|
115
|
+
<ProfileIcicleGraph
|
|
116
|
+
arrow={
|
|
117
|
+
callersFlamegraphResponse?.report.oneofKind === 'flamegraphArrow'
|
|
118
|
+
? callersFlamegraphResponse?.report?.flamegraphArrow
|
|
119
|
+
: undefined
|
|
120
|
+
}
|
|
121
|
+
total={BigInt(callersFlamegraphResponse?.total ?? '0')}
|
|
122
|
+
filtered={filtered}
|
|
123
|
+
profileType={profileSource?.ProfileType()}
|
|
124
|
+
loading={callersFlamegraphLoading}
|
|
125
|
+
error={callersFlamegraphError}
|
|
126
|
+
isHalfScreen={true}
|
|
127
|
+
width={
|
|
128
|
+
callersRef.current != null ? callersRef.current.getBoundingClientRect().width - 25 : 0
|
|
129
|
+
}
|
|
130
|
+
metadataMappingFiles={metadataMappingFiles}
|
|
131
|
+
metadataLoading={false}
|
|
132
|
+
isSandwichIcicleGraph={true}
|
|
133
|
+
curPathArrow={curPathArrow}
|
|
134
|
+
setNewCurPathArrow={setCurPathArrow}
|
|
135
|
+
isFlamegraph={true}
|
|
136
|
+
profileSource={profileSource}
|
|
137
|
+
tooltipId="callers"
|
|
138
|
+
maxFrameCount={defaultMaxFrames}
|
|
139
|
+
isExpanded={isExpanded}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
57
142
|
</div>
|
|
58
|
-
|
|
59
|
-
arrow={
|
|
60
|
-
callersFlamegraphResponse?.report.oneofKind === 'flamegraphArrow'
|
|
61
|
-
? callersFlamegraphResponse?.report?.flamegraphArrow
|
|
62
|
-
: undefined
|
|
63
|
-
}
|
|
64
|
-
total={BigInt(callersFlamegraphResponse?.total ?? '0')}
|
|
65
|
-
filtered={filtered}
|
|
66
|
-
profileType={profileSource?.ProfileType()}
|
|
67
|
-
loading={callersFlamegraphLoading}
|
|
68
|
-
error={callersFlamegraphError}
|
|
69
|
-
isHalfScreen={true}
|
|
70
|
-
width={
|
|
71
|
-
callersRef.current != null
|
|
72
|
-
? isHalfScreen
|
|
73
|
-
? (callersRef.current.getBoundingClientRect().width - 54) / 2
|
|
74
|
-
: callersRef.current.getBoundingClientRect().width - 16
|
|
75
|
-
: 0
|
|
76
|
-
}
|
|
77
|
-
metadataMappingFiles={metadataMappingFiles}
|
|
78
|
-
metadataLoading={false}
|
|
79
|
-
isSandwichIcicleGraph={true}
|
|
80
|
-
curPathArrow={curPathArrow}
|
|
81
|
-
setNewCurPathArrow={setCurPathArrow}
|
|
82
|
-
isFlamegraph={true}
|
|
83
|
-
profileSource={profileSource}
|
|
84
|
-
tooltipId="callers"
|
|
85
|
-
/>
|
|
86
|
-
</div>
|
|
143
|
+
</>
|
|
87
144
|
);
|
|
88
145
|
}
|
|
@@ -46,8 +46,8 @@ export function TableSection({
|
|
|
46
46
|
return (
|
|
47
47
|
<div
|
|
48
48
|
style={{height: height !== undefined ? `${height}px` : '80vh'}}
|
|
49
|
-
className={`font-robotoMono
|
|
50
|
-
selectedRow != null && sandwichFunctionName !== undefined ? 'w-[50%]' : ''
|
|
49
|
+
className={`font-robotoMono cursor-pointer ${
|
|
50
|
+
selectedRow != null && sandwichFunctionName !== undefined ? 'w-[50%]' : 'w-full'
|
|
51
51
|
}`}
|
|
52
52
|
>
|
|
53
53
|
<TableComponent
|