@parca/profile 0.16.449 → 0.16.451
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +1 -1
- package/dist/MetricsGraphStrips/AreaGraph/index.d.ts +1 -1
- package/dist/MetricsGraphStrips/AreaGraph/index.d.ts.map +1 -1
- package/dist/MetricsGraphStrips/MetricsGraphStrips.stories.d.ts +2 -1
- package/dist/MetricsGraphStrips/MetricsGraphStrips.stories.d.ts.map +1 -1
- package/dist/MetricsGraphStrips/MetricsGraphStrips.stories.js +8 -1
- package/dist/MetricsGraphStrips/index.d.ts +2 -1
- package/dist/MetricsGraphStrips/index.d.ts.map +1 -1
- package/dist/MetricsGraphStrips/index.js +13 -3
- package/dist/ProfileIcicleGraph/IcicleGraph/index.js +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +2 -2
- package/dist/ProfileIcicleGraph/index.js +2 -2
- package/dist/ProfileView/components/ActionButtons/GroupByDropdown.d.ts.map +1 -0
- package/dist/{components → ProfileView/components}/ActionButtons/GroupByDropdown.js +1 -1
- package/dist/ProfileView/components/ActionButtons/SortByDropdown.d.ts.map +1 -0
- package/dist/{components → ProfileView/components}/ActionButtons/SortByDropdown.js +2 -2
- package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -0
- package/dist/ProfileView/{ColorStackLegend.js → components/ColorStackLegend.js} +2 -2
- package/dist/ProfileView/components/DashboardItems/index.d.ts +26 -0
- package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -0
- package/dist/ProfileView/components/DashboardItems/index.js +42 -0
- package/dist/ProfileView/components/DashboardLayout/index.d.ts +15 -0
- package/dist/ProfileView/components/DashboardLayout/index.d.ts.map +1 -0
- package/dist/ProfileView/components/DashboardLayout/index.js +20 -0
- package/dist/ProfileView/components/DiffLegend.d.ts.map +1 -0
- package/dist/ProfileView/components/FilterByFunctionButton.d.ts.map +1 -0
- package/dist/ProfileView/components/ProfileHeader/index.d.ts +9 -0
- package/dist/ProfileView/components/ProfileHeader/index.d.ts.map +1 -0
- package/dist/ProfileView/components/ProfileHeader/index.js +12 -0
- package/dist/ProfileView/components/ShareButton/ResultBox.d.ts.map +1 -0
- package/dist/{components → ProfileView/components}/ShareButton/index.d.ts +1 -1
- package/dist/ProfileView/components/ShareButton/index.d.ts.map +1 -0
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -0
- package/dist/{components/VisualisationToolbar → ProfileView/components/Toolbars}/MultiLevelDropdown.js +2 -2
- package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -0
- package/dist/{components/VisualisationToolbar → ProfileView/components/Toolbars}/TableColumnsDropdown.js +4 -4
- package/dist/ProfileView/components/Toolbars/index.d.ts +41 -0
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -0
- package/dist/ProfileView/components/Toolbars/index.js +24 -0
- package/dist/ProfileView/components/ViewSelector/Dropdown.d.ts.map +1 -0
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -0
- package/dist/ProfileView/components/VisualizationContainer/index.d.ts +21 -0
- package/dist/ProfileView/components/VisualizationContainer/index.d.ts.map +1 -0
- package/dist/ProfileView/components/VisualizationContainer/index.js +8 -0
- package/dist/ProfileView/{VisualizationPanel.d.ts → components/VisualizationPanel.d.ts} +4 -3
- package/dist/ProfileView/components/VisualizationPanel.d.ts.map +1 -0
- package/dist/ProfileView/context/DashboardContext.d.ts +12 -0
- package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -0
- package/dist/ProfileView/context/DashboardContext.js +39 -0
- package/dist/ProfileView/{ProfileViewContext.d.ts → context/ProfileViewContext.d.ts} +1 -1
- package/dist/ProfileView/context/ProfileViewContext.d.ts.map +1 -0
- package/dist/ProfileView/hooks/useGraphviz.d.ts +12 -0
- package/dist/ProfileView/hooks/useGraphviz.d.ts.map +1 -0
- package/dist/ProfileView/hooks/useGraphviz.js +42 -0
- package/dist/ProfileView/hooks/useProfileMetadata.d.ts +17 -0
- package/dist/ProfileView/hooks/useProfileMetadata.d.ts.map +1 -0
- package/dist/ProfileView/hooks/useProfileMetadata.js +30 -0
- package/dist/ProfileView/hooks/useVisualizationState.d.ts +14 -0
- package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -0
- package/dist/ProfileView/hooks/useVisualizationState.js +52 -0
- package/dist/ProfileView/index.d.ts +2 -49
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +54 -173
- package/dist/ProfileView/types/visualization.d.ts +49 -0
- package/dist/ProfileView/types/visualization.d.ts.map +1 -0
- package/dist/ProfileView/types/visualization.js +13 -0
- package/dist/ProfileView/utils/colorUtils.d.ts +3 -0
- package/dist/ProfileView/utils/colorUtils.d.ts.map +1 -0
- package/dist/ProfileView/utils/colorUtils.js +21 -0
- package/dist/ProfileViewWithData.d.ts +2 -1
- package/dist/ProfileViewWithData.d.ts.map +1 -1
- package/dist/ProfileViewWithData.js +2 -2
- package/dist/SourceView/Highlighter.js +1 -1
- package/dist/Table/index.js +1 -1
- package/dist/TimelineGuide/index.d.ts +11 -0
- package/dist/TimelineGuide/index.d.ts.map +1 -0
- package/dist/{MetricsGraphStrips/TimelineGuide → TimelineGuide}/index.js +3 -13
- package/dist/TopTable/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +1 -1
- package/src/MetricsGraphStrips/AreaGraph/index.tsx +2 -2
- package/src/MetricsGraphStrips/MetricsGraphStrips.stories.tsx +11 -2
- package/src/MetricsGraphStrips/index.tsx +16 -4
- package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +1 -1
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +2 -2
- package/src/ProfileIcicleGraph/index.tsx +2 -2
- package/src/{components → ProfileView/components}/ActionButtons/GroupByDropdown.tsx +1 -1
- package/src/{components → ProfileView/components}/ActionButtons/SortByDropdown.tsx +2 -2
- package/src/ProfileView/{ColorStackLegend.tsx → components/ColorStackLegend.tsx} +2 -2
- package/src/ProfileView/components/DashboardItems/index.tsx +148 -0
- package/src/ProfileView/components/DashboardLayout/index.tsx +96 -0
- package/src/ProfileView/components/ProfileHeader/index.tsx +68 -0
- package/src/{components → ProfileView/components}/ShareButton/index.tsx +1 -1
- package/src/{components/VisualisationToolbar → ProfileView/components/Toolbars}/MultiLevelDropdown.tsx +2 -6
- package/src/{components/VisualisationToolbar → ProfileView/components/Toolbars}/TableColumnsDropdown.tsx +4 -4
- package/src/ProfileView/components/Toolbars/index.tsx +193 -0
- package/src/ProfileView/components/VisualizationContainer/index.tsx +68 -0
- package/src/ProfileView/{VisualizationPanel.tsx → components/VisualizationPanel.tsx} +5 -3
- package/src/ProfileView/context/DashboardContext.tsx +61 -0
- package/src/ProfileView/{ProfileViewContext.tsx → context/ProfileViewContext.tsx} +1 -1
- package/src/ProfileView/hooks/useGraphviz.ts +69 -0
- package/src/ProfileView/hooks/useProfileMetadata.ts +59 -0
- package/src/ProfileView/hooks/useVisualizationState.ts +82 -0
- package/src/ProfileView/index.tsx +126 -451
- package/src/ProfileView/types/visualization.ts +75 -0
- package/src/ProfileView/utils/colorUtils.ts +24 -0
- package/src/ProfileViewWithData.tsx +3 -0
- package/src/SourceView/Highlighter.tsx +1 -1
- package/src/Table/index.tsx +1 -1
- package/src/{MetricsGraphStrips/TimelineGuide → TimelineGuide}/index.tsx +7 -17
- package/src/TopTable/index.tsx +1 -1
- package/src/utils.ts +2 -0
- package/dist/MetricsGraphStrips/TimelineGuide/index.d.ts +0 -10
- package/dist/MetricsGraphStrips/TimelineGuide/index.d.ts.map +0 -1
- package/dist/ProfileView/ColorStackLegend.d.ts.map +0 -1
- package/dist/ProfileView/ProfileViewContext.d.ts.map +0 -1
- package/dist/ProfileView/VisualizationPanel.d.ts.map +0 -1
- package/dist/components/ActionButtons/GroupByDropdown.d.ts.map +0 -1
- package/dist/components/ActionButtons/SortByDropdown.d.ts.map +0 -1
- package/dist/components/DiffLegend.d.ts.map +0 -1
- package/dist/components/FilterByFunctionButton.d.ts.map +0 -1
- package/dist/components/ShareButton/ResultBox.d.ts.map +0 -1
- package/dist/components/ShareButton/index.d.ts.map +0 -1
- package/dist/components/ViewSelector/Dropdown.d.ts.map +0 -1
- package/dist/components/ViewSelector/index.d.ts.map +0 -1
- package/dist/components/VisualisationToolbar/MultiLevelDropdown.d.ts.map +0 -1
- package/dist/components/VisualisationToolbar/TableColumnsDropdown.d.ts.map +0 -1
- package/dist/components/VisualisationToolbar/index.d.ts +0 -37
- package/dist/components/VisualisationToolbar/index.d.ts.map +0 -1
- package/dist/components/VisualisationToolbar/index.js +0 -64
- package/src/components/VisualisationToolbar/index.tsx +0 -228
- /package/dist/{components → ProfileView/components}/ActionButtons/GroupByDropdown.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/ActionButtons/SortByDropdown.d.ts +0 -0
- /package/dist/ProfileView/{ColorStackLegend.d.ts → components/ColorStackLegend.d.ts} +0 -0
- /package/dist/{components → ProfileView/components}/DiffLegend.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/DiffLegend.js +0 -0
- /package/dist/{components → ProfileView/components}/FilterByFunctionButton.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/FilterByFunctionButton.js +0 -0
- /package/dist/{components → ProfileView/components}/ShareButton/ResultBox.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/ShareButton/ResultBox.js +0 -0
- /package/dist/{components → ProfileView/components}/ShareButton/index.js +0 -0
- /package/dist/{components/VisualisationToolbar → ProfileView/components/Toolbars}/MultiLevelDropdown.d.ts +0 -0
- /package/dist/{components/VisualisationToolbar → ProfileView/components/Toolbars}/TableColumnsDropdown.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/ViewSelector/Dropdown.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/ViewSelector/Dropdown.js +0 -0
- /package/dist/{components → ProfileView/components}/ViewSelector/index.d.ts +0 -0
- /package/dist/{components → ProfileView/components}/ViewSelector/index.js +0 -0
- /package/dist/ProfileView/{VisualizationPanel.js → components/VisualizationPanel.js} +0 -0
- /package/dist/ProfileView/{ProfileViewContext.js → context/ProfileViewContext.js} +0 -0
- /package/src/{components → ProfileView/components}/DiffLegend.tsx +0 -0
- /package/src/{components → ProfileView/components}/FilterByFunctionButton.tsx +0 -0
- /package/src/{components → ProfileView/components}/ShareButton/ResultBox.tsx +0 -0
- /package/src/{components → ProfileView/components}/ViewSelector/Dropdown.tsx +0 -0
- /package/src/{components → ProfileView/components}/ViewSelector/index.tsx +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {FC} from 'react';
|
|
15
|
+
|
|
16
|
+
import cx from 'classnames';
|
|
17
|
+
import {
|
|
18
|
+
DragDropContext,
|
|
19
|
+
Draggable,
|
|
20
|
+
Droppable,
|
|
21
|
+
type DraggableLocation,
|
|
22
|
+
type DropResult,
|
|
23
|
+
} from 'react-beautiful-dnd';
|
|
24
|
+
|
|
25
|
+
import {useDashboard} from '../../context/DashboardContext';
|
|
26
|
+
import {VisualizationType} from '../../types/visualization';
|
|
27
|
+
import {VisualizationContainer} from '../VisualizationContainer';
|
|
28
|
+
|
|
29
|
+
interface DashboardLayoutProps {
|
|
30
|
+
getDashboardItemByType: (props: {type: VisualizationType; isHalfScreen: boolean}) => JSX.Element;
|
|
31
|
+
actionButtons: {
|
|
32
|
+
icicle: JSX.Element;
|
|
33
|
+
table: JSX.Element;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const DashboardLayout: FC<DashboardLayoutProps> = ({
|
|
38
|
+
getDashboardItemByType,
|
|
39
|
+
actionButtons,
|
|
40
|
+
}) => {
|
|
41
|
+
const {dashboardItems, setDashboardItems, isMultiPanelView} = useDashboard();
|
|
42
|
+
|
|
43
|
+
const onDragEnd = (result: DropResult): void => {
|
|
44
|
+
const {destination, source, draggableId} = result;
|
|
45
|
+
|
|
46
|
+
if (Boolean(destination) && destination?.index !== source.index) {
|
|
47
|
+
const targetItem = draggableId;
|
|
48
|
+
const otherItems = dashboardItems.filter(item => item !== targetItem);
|
|
49
|
+
const newDashboardItems =
|
|
50
|
+
(destination as DraggableLocation).index < source.index
|
|
51
|
+
? [targetItem, ...otherItems]
|
|
52
|
+
: [...otherItems, targetItem];
|
|
53
|
+
|
|
54
|
+
setDashboardItems(newDashboardItems);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<DragDropContext onDragEnd={onDragEnd}>
|
|
60
|
+
<Droppable droppableId="droppable" direction="horizontal">
|
|
61
|
+
{provided => (
|
|
62
|
+
<div
|
|
63
|
+
ref={provided.innerRef}
|
|
64
|
+
className={cx(
|
|
65
|
+
'grid w-full gap-2',
|
|
66
|
+
isMultiPanelView ? 'grid-cols-2 mt-4' : 'grid-cols-1'
|
|
67
|
+
)}
|
|
68
|
+
{...provided.droppableProps}
|
|
69
|
+
>
|
|
70
|
+
{dashboardItems.map((dashboardItem, index) => (
|
|
71
|
+
<Draggable
|
|
72
|
+
key={dashboardItem}
|
|
73
|
+
draggableId={dashboardItem}
|
|
74
|
+
index={index}
|
|
75
|
+
isDragDisabled={!isMultiPanelView}
|
|
76
|
+
>
|
|
77
|
+
{(provided, snapshot) => (
|
|
78
|
+
<VisualizationContainer
|
|
79
|
+
provided={provided}
|
|
80
|
+
snapshot={snapshot}
|
|
81
|
+
dashboardItem={dashboardItem as VisualizationType}
|
|
82
|
+
getDashboardItemByType={getDashboardItemByType}
|
|
83
|
+
isMultiPanelView={isMultiPanelView}
|
|
84
|
+
index={index}
|
|
85
|
+
actionButtons={actionButtons}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
</Draggable>
|
|
89
|
+
))}
|
|
90
|
+
{provided.placeholder}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</Droppable>
|
|
94
|
+
</DragDropContext>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {FC} from 'react';
|
|
15
|
+
|
|
16
|
+
import cx from 'classnames';
|
|
17
|
+
|
|
18
|
+
interface ProfileHeaderProps {
|
|
19
|
+
profileSourceString?: string;
|
|
20
|
+
hasProfileSource: boolean;
|
|
21
|
+
externalMainActions?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ProfileHeader: FC<ProfileHeaderProps> = ({
|
|
25
|
+
profileSourceString,
|
|
26
|
+
hasProfileSource,
|
|
27
|
+
|
|
28
|
+
externalMainActions,
|
|
29
|
+
}) => {
|
|
30
|
+
const headerParts = profileSourceString?.split('"') ?? [];
|
|
31
|
+
|
|
32
|
+
const showDivider =
|
|
33
|
+
hasProfileSource && (externalMainActions === null || externalMainActions === undefined);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
{showDivider && (
|
|
38
|
+
<div className="border-t border-gray-200 dark:border-gray-700 h-[1px] w-full pb-4" />
|
|
39
|
+
)}
|
|
40
|
+
<div
|
|
41
|
+
className={cx(
|
|
42
|
+
'flex w-full',
|
|
43
|
+
hasProfileSource || externalMainActions != null ? 'justify-start' : 'justify-end',
|
|
44
|
+
{
|
|
45
|
+
'items-end mb-4': !hasProfileSource && externalMainActions != null,
|
|
46
|
+
'items-center mb-2': hasProfileSource,
|
|
47
|
+
}
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
<div>
|
|
51
|
+
{hasProfileSource && (
|
|
52
|
+
<div className="flex items-center gap-1">
|
|
53
|
+
<div className="text-xs font-medium">
|
|
54
|
+
{headerParts.length > 0 ? headerParts[0].replace(/"/g, '') : ''}
|
|
55
|
+
</div>
|
|
56
|
+
<div className="text-xs font-medium">
|
|
57
|
+
{headerParts.length > 1
|
|
58
|
+
? headerParts[headerParts.length - 1].replace(/"/g, '')
|
|
59
|
+
: ''}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
{externalMainActions}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -18,7 +18,7 @@ import {Icon} from '@iconify/react';
|
|
|
18
18
|
import {QueryRequest, QueryServiceClient} from '@parca/client';
|
|
19
19
|
import {Button, Dropdown, Modal, useGrpcMetadata} from '@parca/components';
|
|
20
20
|
|
|
21
|
-
import {ProfileSource} from '
|
|
21
|
+
import {ProfileSource} from '../../../ProfileSource';
|
|
22
22
|
import ResultBox from './ResultBox';
|
|
23
23
|
|
|
24
24
|
interface Props {
|
|
@@ -20,12 +20,8 @@ import {useURLState} from '@parca/components';
|
|
|
20
20
|
import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
|
|
21
21
|
import {ProfileType} from '@parca/parser';
|
|
22
22
|
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
FIELD_DIFF,
|
|
26
|
-
FIELD_FUNCTION_NAME,
|
|
27
|
-
} from '../../ProfileIcicleGraph/IcicleGraphArrow';
|
|
28
|
-
import {useProfileViewContext} from '../../ProfileView/ProfileViewContext';
|
|
23
|
+
import {FIELD_FUNCTION_NAME} from '../../../ProfileIcicleGraph/IcicleGraphArrow';
|
|
24
|
+
import {useProfileViewContext} from '../../context/ProfileViewContext';
|
|
29
25
|
|
|
30
26
|
interface MenuItemType {
|
|
31
27
|
label: string;
|
|
@@ -19,10 +19,10 @@ import {useURLState} from '@parca/components';
|
|
|
19
19
|
import {ProfileType} from '@parca/parser';
|
|
20
20
|
import {valueFormatter} from '@parca/utilities';
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
22
|
+
import {Row, isDummyRow} from '../../../Table';
|
|
23
|
+
import ColumnsVisibility from '../../../Table/ColumnsVisibility';
|
|
24
|
+
import {ColumnName, DataRow, addPlusSign, getRatioString} from '../../../Table/utils/functions';
|
|
25
|
+
import {useProfileViewContext} from '../../context/ProfileViewContext';
|
|
26
26
|
|
|
27
27
|
interface Props {
|
|
28
28
|
profileType?: ProfileType;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {FC} from 'react';
|
|
15
|
+
|
|
16
|
+
import {Icon} from '@iconify/react';
|
|
17
|
+
|
|
18
|
+
import {QueryServiceClient} from '@parca/client';
|
|
19
|
+
import {Button, UserPreferencesModal} from '@parca/components';
|
|
20
|
+
import {ProfileType} from '@parca/parser';
|
|
21
|
+
|
|
22
|
+
import {ProfileSource} from '../../../ProfileSource';
|
|
23
|
+
import {useDashboard} from '../../context/DashboardContext';
|
|
24
|
+
import GroupByDropdown from '../ActionButtons/GroupByDropdown';
|
|
25
|
+
import SortByDropdown from '../ActionButtons/SortByDropdown';
|
|
26
|
+
import FilterByFunctionButton from '../FilterByFunctionButton';
|
|
27
|
+
import ShareButton from '../ShareButton';
|
|
28
|
+
import ViewSelector from '../ViewSelector';
|
|
29
|
+
import MultiLevelDropdown from './MultiLevelDropdown';
|
|
30
|
+
import TableColumnsDropdown from './TableColumnsDropdown';
|
|
31
|
+
|
|
32
|
+
export interface VisualisationToolbarProps {
|
|
33
|
+
groupBy: string[];
|
|
34
|
+
toggleGroupBy: (key: string) => void;
|
|
35
|
+
hasProfileSource: boolean;
|
|
36
|
+
pprofdownloading?: boolean;
|
|
37
|
+
profileSource?: ProfileSource;
|
|
38
|
+
queryClient?: QueryServiceClient;
|
|
39
|
+
onDownloadPProf: () => void;
|
|
40
|
+
curPath: string[];
|
|
41
|
+
setNewCurPath: (path: string[]) => void;
|
|
42
|
+
profileType?: ProfileType;
|
|
43
|
+
total: bigint;
|
|
44
|
+
filtered: bigint;
|
|
45
|
+
currentSearchString?: string;
|
|
46
|
+
setSearchString?: (value: string) => void;
|
|
47
|
+
groupByLabels: string[];
|
|
48
|
+
preferencesModal?: boolean;
|
|
49
|
+
profileViewExternalSubActions?: React.ReactNode;
|
|
50
|
+
clearSelection: () => void;
|
|
51
|
+
setGroupByLabels: (labels: string[]) => void;
|
|
52
|
+
showVisualizationSelector?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface TableToolbarProps {
|
|
56
|
+
profileType?: ProfileType;
|
|
57
|
+
total: bigint;
|
|
58
|
+
filtered: bigint;
|
|
59
|
+
clearSelection: () => void;
|
|
60
|
+
currentSearchString?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface IcicleGraphToolbarProps {
|
|
64
|
+
curPath: string[];
|
|
65
|
+
setNewCurPath: (path: string[]) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const TableToolbar: FC<TableToolbarProps> = ({
|
|
69
|
+
profileType,
|
|
70
|
+
total,
|
|
71
|
+
filtered,
|
|
72
|
+
clearSelection,
|
|
73
|
+
currentSearchString,
|
|
74
|
+
}) => {
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
<div className="flex w-full gap-2 items-end">
|
|
78
|
+
<TableColumnsDropdown profileType={profileType} total={total} filtered={filtered} />
|
|
79
|
+
<Button
|
|
80
|
+
color="neutral"
|
|
81
|
+
onClick={clearSelection}
|
|
82
|
+
className="w-auto"
|
|
83
|
+
variant="neutral"
|
|
84
|
+
disabled={currentSearchString === undefined || currentSearchString.length === 0}
|
|
85
|
+
>
|
|
86
|
+
Clear selection
|
|
87
|
+
</Button>
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const IcicleGraphToolbar: FC<IcicleGraphToolbarProps> = ({curPath, setNewCurPath}) => {
|
|
94
|
+
return (
|
|
95
|
+
<>
|
|
96
|
+
<div className="flex w-full gap-2 items-end">
|
|
97
|
+
<SortByDropdown />
|
|
98
|
+
<Button
|
|
99
|
+
variant="neutral"
|
|
100
|
+
className="gap-2 w-max h-fit"
|
|
101
|
+
onClick={() => setNewCurPath([])}
|
|
102
|
+
disabled={curPath.length === 0}
|
|
103
|
+
>
|
|
104
|
+
Reset graph
|
|
105
|
+
<Icon icon="system-uicons:reset" width={20} />
|
|
106
|
+
</Button>
|
|
107
|
+
</div>
|
|
108
|
+
</>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const Divider = (): JSX.Element => (
|
|
113
|
+
<div className="border-t mt-4 border-gray-200 dark:border-gray-700 h-[1px] w-full pb-4" />
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
export const VisualisationToolbar: FC<VisualisationToolbarProps> = ({
|
|
117
|
+
groupBy,
|
|
118
|
+
toggleGroupBy,
|
|
119
|
+
groupByLabels,
|
|
120
|
+
setGroupByLabels,
|
|
121
|
+
profileType,
|
|
122
|
+
preferencesModal,
|
|
123
|
+
profileSource,
|
|
124
|
+
queryClient,
|
|
125
|
+
onDownloadPProf,
|
|
126
|
+
pprofdownloading,
|
|
127
|
+
profileViewExternalSubActions,
|
|
128
|
+
curPath,
|
|
129
|
+
setNewCurPath,
|
|
130
|
+
total,
|
|
131
|
+
filtered,
|
|
132
|
+
currentSearchString,
|
|
133
|
+
clearSelection,
|
|
134
|
+
showVisualizationSelector = true,
|
|
135
|
+
}) => {
|
|
136
|
+
const {dashboardItems} = useDashboard();
|
|
137
|
+
|
|
138
|
+
const isTableViz = dashboardItems?.includes('table');
|
|
139
|
+
const isGraphViz = dashboardItems?.includes('icicle');
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<>
|
|
143
|
+
<div className="flex w-full justify-between items-end">
|
|
144
|
+
<div className="flex gap-3 items-end">
|
|
145
|
+
<>
|
|
146
|
+
<GroupByDropdown
|
|
147
|
+
groupBy={groupBy}
|
|
148
|
+
toggleGroupBy={toggleGroupBy}
|
|
149
|
+
labels={groupByLabels}
|
|
150
|
+
setGroupByLabels={setGroupByLabels}
|
|
151
|
+
/>
|
|
152
|
+
<MultiLevelDropdown profileType={profileType} onSelect={() => {}} />
|
|
153
|
+
</>
|
|
154
|
+
|
|
155
|
+
<FilterByFunctionButton />
|
|
156
|
+
|
|
157
|
+
{profileViewExternalSubActions != null ? profileViewExternalSubActions : null}
|
|
158
|
+
</div>
|
|
159
|
+
<div className="flex gap-3">
|
|
160
|
+
{preferencesModal != null ? <UserPreferencesModal /> : null}
|
|
161
|
+
<ShareButton
|
|
162
|
+
profileSource={profileSource}
|
|
163
|
+
queryClient={queryClient}
|
|
164
|
+
queryRequest={profileSource?.QueryRequest() ?? undefined}
|
|
165
|
+
onDownloadPProf={onDownloadPProf}
|
|
166
|
+
pprofdownloading={pprofdownloading ?? false}
|
|
167
|
+
profileViewExternalSubActions={profileViewExternalSubActions}
|
|
168
|
+
/>
|
|
169
|
+
|
|
170
|
+
{showVisualizationSelector ? <ViewSelector /> : null}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
{isGraphViz && !isTableViz && (
|
|
174
|
+
<>
|
|
175
|
+
<Divider />
|
|
176
|
+
<IcicleGraphToolbar curPath={curPath} setNewCurPath={setNewCurPath} />
|
|
177
|
+
</>
|
|
178
|
+
)}
|
|
179
|
+
{isTableViz && !isGraphViz && (
|
|
180
|
+
<>
|
|
181
|
+
<Divider />
|
|
182
|
+
<TableToolbar
|
|
183
|
+
profileType={profileType}
|
|
184
|
+
total={total}
|
|
185
|
+
filtered={filtered}
|
|
186
|
+
clearSelection={clearSelection}
|
|
187
|
+
currentSearchString={currentSearchString}
|
|
188
|
+
/>
|
|
189
|
+
</>
|
|
190
|
+
)}
|
|
191
|
+
</>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {FC} from 'react';
|
|
15
|
+
|
|
16
|
+
import cx from 'classnames';
|
|
17
|
+
import type {DraggableProvided, DraggableStateSnapshot} from 'react-beautiful-dnd';
|
|
18
|
+
|
|
19
|
+
import {useDashboard} from '../../context/DashboardContext';
|
|
20
|
+
import {VisualizationType} from '../../types/visualization';
|
|
21
|
+
import {VisualizationPanel} from '../VisualizationPanel';
|
|
22
|
+
|
|
23
|
+
interface VisualizationContainerProps {
|
|
24
|
+
provided: DraggableProvided;
|
|
25
|
+
snapshot: DraggableStateSnapshot;
|
|
26
|
+
dashboardItem: VisualizationType;
|
|
27
|
+
getDashboardItemByType: (props: {type: VisualizationType; isHalfScreen: boolean}) => JSX.Element;
|
|
28
|
+
isMultiPanelView: boolean;
|
|
29
|
+
index: number;
|
|
30
|
+
actionButtons: {
|
|
31
|
+
icicle: JSX.Element;
|
|
32
|
+
table: JSX.Element;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const VisualizationContainer: FC<VisualizationContainerProps> = ({
|
|
37
|
+
provided,
|
|
38
|
+
snapshot,
|
|
39
|
+
dashboardItem,
|
|
40
|
+
getDashboardItemByType,
|
|
41
|
+
isMultiPanelView,
|
|
42
|
+
index,
|
|
43
|
+
actionButtons,
|
|
44
|
+
}) => {
|
|
45
|
+
const {handleClosePanel} = useDashboard();
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
ref={provided.innerRef}
|
|
50
|
+
{...provided.draggableProps}
|
|
51
|
+
className={cx(
|
|
52
|
+
'w-full min-h-96',
|
|
53
|
+
snapshot.isDragging ? 'bg-gray-200 dark:bg-gray-500' : 'bg-white dark:bg-gray-900',
|
|
54
|
+
isMultiPanelView ? 'border-2 border-gray-100 dark:border-gray-700 rounded-md p-3' : ''
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
<VisualizationPanel
|
|
58
|
+
handleClosePanel={handleClosePanel}
|
|
59
|
+
isMultiPanelView={isMultiPanelView}
|
|
60
|
+
dashboardItem={dashboardItem}
|
|
61
|
+
getDashboardItemByType={getDashboardItemByType}
|
|
62
|
+
dragHandleProps={provided.dragHandleProps}
|
|
63
|
+
index={index}
|
|
64
|
+
actionButtons={actionButtons}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -20,13 +20,15 @@ import type {DraggableProvidedDragHandleProps} from 'react-beautiful-dnd';
|
|
|
20
20
|
import {IconButton, useParcaContext} from '@parca/components';
|
|
21
21
|
import {CloseIcon} from '@parca/icons';
|
|
22
22
|
|
|
23
|
+
import {VisualizationType} from '../types/visualization';
|
|
24
|
+
|
|
23
25
|
interface Props {
|
|
24
|
-
dashboardItem:
|
|
26
|
+
dashboardItem: VisualizationType;
|
|
25
27
|
index: number;
|
|
26
28
|
isMultiPanelView: boolean;
|
|
27
|
-
handleClosePanel: (dashboardItem:
|
|
29
|
+
handleClosePanel: (dashboardItem: VisualizationType) => void;
|
|
28
30
|
dragHandleProps: DraggableProvidedDragHandleProps | null | undefined;
|
|
29
|
-
getDashboardItemByType: (props: {type:
|
|
31
|
+
getDashboardItemByType: (props: {type: VisualizationType; isHalfScreen: boolean}) => JSX.Element;
|
|
30
32
|
actionButtons: {
|
|
31
33
|
icicle: JSX.Element;
|
|
32
34
|
table: JSX.Element;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {FC, PropsWithChildren, createContext, useContext} from 'react';
|
|
15
|
+
|
|
16
|
+
import {useURLState} from '@parca/components';
|
|
17
|
+
|
|
18
|
+
import {VisualizationType} from '../types/visualization';
|
|
19
|
+
|
|
20
|
+
interface DashboardContextType {
|
|
21
|
+
dashboardItems: string[];
|
|
22
|
+
setDashboardItems: (items: string[]) => void;
|
|
23
|
+
handleClosePanel: (visualizationType: VisualizationType) => void;
|
|
24
|
+
isMultiPanelView: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const DashboardContext = createContext<DashboardContextType | undefined>(undefined);
|
|
28
|
+
|
|
29
|
+
export const DashboardProvider: FC<PropsWithChildren> = ({children}) => {
|
|
30
|
+
const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
|
|
31
|
+
alwaysReturnArray: true,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const handleClosePanel = (visualizationType: VisualizationType): void => {
|
|
35
|
+
const newDashboardItems = dashboardItems.filter(item => item !== visualizationType);
|
|
36
|
+
setDashboardItems(newDashboardItems);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const isMultiPanelView = dashboardItems.length > 1;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<DashboardContext.Provider
|
|
43
|
+
value={{
|
|
44
|
+
dashboardItems,
|
|
45
|
+
setDashboardItems,
|
|
46
|
+
handleClosePanel,
|
|
47
|
+
isMultiPanelView,
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</DashboardContext.Provider>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const useDashboard = (): DashboardContextType => {
|
|
56
|
+
const context = useContext(DashboardContext);
|
|
57
|
+
if (context === undefined) {
|
|
58
|
+
throw new Error('useDashboard must be used within a DashboardProvider');
|
|
59
|
+
}
|
|
60
|
+
return context;
|
|
61
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {useEffect, useState} from 'react';
|
|
15
|
+
|
|
16
|
+
import graphviz from 'graphviz-wasm';
|
|
17
|
+
|
|
18
|
+
import {Callgraph as CallgraphType} from '@parca/client';
|
|
19
|
+
|
|
20
|
+
import {jsonToDot} from '../../Callgraph/utils';
|
|
21
|
+
|
|
22
|
+
interface UseGraphvizProps {
|
|
23
|
+
callgraphData?: CallgraphType;
|
|
24
|
+
width?: number;
|
|
25
|
+
colorRange: [string, string];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const useGraphviz = ({
|
|
29
|
+
callgraphData,
|
|
30
|
+
width,
|
|
31
|
+
colorRange,
|
|
32
|
+
}: UseGraphvizProps): {
|
|
33
|
+
graphvizLoaded: boolean;
|
|
34
|
+
callgraphSVG: string | undefined;
|
|
35
|
+
} => {
|
|
36
|
+
const [graphvizLoaded, setGraphvizLoaded] = useState(false);
|
|
37
|
+
const [callgraphSVG, setCallgraphSVG] = useState<string | undefined>(undefined);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
async function loadGraphviz(): Promise<void> {
|
|
41
|
+
await graphviz.loadWASM();
|
|
42
|
+
setGraphvizLoaded(true);
|
|
43
|
+
}
|
|
44
|
+
void loadGraphviz();
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
async function loadCallgraphSVG(
|
|
49
|
+
graph: CallgraphType,
|
|
50
|
+
width: number,
|
|
51
|
+
colorRange: [string, string]
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
await setCallgraphSVG(undefined);
|
|
54
|
+
const dataAsDot = await jsonToDot({
|
|
55
|
+
graph,
|
|
56
|
+
width,
|
|
57
|
+
colorRange,
|
|
58
|
+
});
|
|
59
|
+
const svgGraph = await graphviz.layout(dataAsDot, 'svg', 'dot');
|
|
60
|
+
await setCallgraphSVG(svgGraph);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (graphvizLoaded && callgraphData != null && width != null) {
|
|
64
|
+
void loadCallgraphSVG(callgraphData, width, colorRange);
|
|
65
|
+
}
|
|
66
|
+
}, [graphvizLoaded, callgraphData, width, colorRange]);
|
|
67
|
+
|
|
68
|
+
return {graphvizLoaded, callgraphSVG};
|
|
69
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {useMemo} from 'react';
|
|
15
|
+
|
|
16
|
+
import {Table as ArrowTable, tableFromIPC} from 'apache-arrow';
|
|
17
|
+
|
|
18
|
+
import {FlamegraphArrow} from '@parca/client';
|
|
19
|
+
|
|
20
|
+
import useMappingList, {
|
|
21
|
+
useFilenamesList,
|
|
22
|
+
} from '../../ProfileIcicleGraph/IcicleGraphArrow/useMappingList';
|
|
23
|
+
|
|
24
|
+
interface UseProfileMetadataProps {
|
|
25
|
+
flamegraphArrow?: FlamegraphArrow;
|
|
26
|
+
metadataMappingFiles?: string[];
|
|
27
|
+
metadataLoading: boolean;
|
|
28
|
+
colorBy: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const useProfileMetadata = ({
|
|
32
|
+
flamegraphArrow,
|
|
33
|
+
metadataMappingFiles,
|
|
34
|
+
metadataLoading,
|
|
35
|
+
colorBy,
|
|
36
|
+
}: UseProfileMetadataProps): {
|
|
37
|
+
table: ArrowTable<any> | null;
|
|
38
|
+
mappingsList: string[];
|
|
39
|
+
filenamesList: string[];
|
|
40
|
+
colorMappings: string[];
|
|
41
|
+
metadataLoading: boolean;
|
|
42
|
+
} => {
|
|
43
|
+
const table: ArrowTable<any> | null = useMemo(() => {
|
|
44
|
+
return flamegraphArrow !== undefined ? tableFromIPC(flamegraphArrow.record) : null;
|
|
45
|
+
}, [flamegraphArrow]);
|
|
46
|
+
|
|
47
|
+
const mappingsList = useMappingList(metadataMappingFiles);
|
|
48
|
+
const filenamesList = useFilenamesList(table);
|
|
49
|
+
|
|
50
|
+
const colorMappings = colorBy === 'binary' ? mappingsList : filenamesList;
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
table,
|
|
54
|
+
mappingsList,
|
|
55
|
+
filenamesList,
|
|
56
|
+
colorMappings,
|
|
57
|
+
metadataLoading,
|
|
58
|
+
};
|
|
59
|
+
};
|