@parca/profile 0.16.184 → 0.16.185
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/Callgraph/constants.js +2 -2
- package/dist/Callgraph/index.js +35 -45
- package/dist/Callgraph/mockData/index.js +28 -11
- package/dist/Callgraph/utils.js +51 -58
- package/dist/GraphTooltip/ExpandOnHoverValue.js +2 -14
- package/dist/GraphTooltip/index.d.ts +5 -5
- package/dist/GraphTooltip/index.js +96 -122
- package/dist/MatchersInput/SuggestionItem.js +5 -17
- package/dist/MatchersInput/SuggestionsList.js +29 -53
- package/dist/MatchersInput/index.js +58 -74
- package/dist/MetricsCircle/index.js +2 -16
- package/dist/MetricsGraph/MetricsTooltip/index.js +27 -53
- package/dist/MetricsGraph/index.js +79 -98
- package/dist/MetricsSeries/index.js +4 -19
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +4 -16
- package/dist/ProfileExplorer/ProfileExplorerSingle.js +2 -14
- package/dist/ProfileExplorer/index.js +129 -88
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.js +15 -31
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.d.ts +4 -4
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.js +38 -54
- package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts +2 -2
- package/dist/ProfileIcicleGraph/IcicleGraph/index.js +15 -31
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.js +22 -26
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.js +8 -9
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.js +18 -20
- package/dist/ProfileIcicleGraph/index.d.ts +2 -2
- package/dist/ProfileIcicleGraph/index.js +18 -30
- package/dist/ProfileMetricsGraph/index.js +36 -88
- package/dist/ProfileSelector/CompareButton.js +8 -20
- package/dist/ProfileSelector/index.js +69 -69
- package/dist/ProfileSource.js +56 -65
- package/dist/ProfileTypeSelector/index.js +14 -28
- package/dist/ProfileView/FilterByFunctionButton.js +6 -7
- package/dist/ProfileView/ViewSelector.js +18 -31
- package/dist/ProfileView/VisualizationPanel.js +4 -16
- package/dist/ProfileView/index.js +72 -152
- package/dist/ProfileViewWithData.js +50 -101
- package/dist/TopTable/index.js +55 -63
- package/dist/components/DiffLegend.js +16 -28
- package/dist/components/ProfileShareButton/ResultBox.js +7 -21
- package/dist/components/ProfileShareButton/index.js +31 -90
- package/dist/useDelayedLoader.js +7 -8
- package/dist/useGrpcQuery/index.js +6 -48
- package/dist/useQuery.js +14 -58
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +16 -68
- package/package.json +6 -6
- package/src/Callgraph/index.tsx +3 -3
- package/src/Callgraph/utils.ts +1 -1
- package/src/GraphTooltip/index.tsx +17 -17
- package/src/MetricsGraph/index.tsx +3 -3
- package/src/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.tsx +9 -10
- package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +4 -8
- package/src/ProfileIcicleGraph/IcicleGraph/useNodeColor.ts +2 -2
- package/src/ProfileIcicleGraph/index.tsx +8 -8
- package/src/ProfileView/index.tsx +2 -2
- package/src/TopTable/index.tsx +3 -3
- package/src/utils.ts +2 -2
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
2
|
// Copyright 2022 The Parca Authors
|
|
14
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -28,12 +17,11 @@ import cx from 'classnames';
|
|
|
28
17
|
import { IconButton } from '@parca/components';
|
|
29
18
|
import { CloseIcon } from '@parca/icons';
|
|
30
19
|
import ViewSelector from './ViewSelector';
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", __assign({ className: "flex w-full justify-end gap-2 pb-2" }, { children: [_jsxs("div", __assign({ className: "flex w-full items-center justify-between" }, { children: [_jsxs("div", __assign({ className: "flex" }, { children: [_jsx("div", __assign({ className: cx(isMultiPanelView ? 'visible' : 'invisible', 'flex items-center') }, dragHandleProps, { children: _jsx(Icon, { className: "text-xl", icon: "material-symbols:drag-indicator" }) })), _jsx(_Fragment, { children: actionButtons })] })), _jsx(ViewSelector, { defaultValue: dashboardItem, navigateTo: navigateTo, position: index })] })), isMultiPanelView && (_jsx(IconButton, { onClick: function () { return handleClosePanel(dashboardItem); }, icon: _jsx(CloseIcon, {}) }))] })), getDashboardItemByType({
|
|
20
|
+
export const VisualizationPanel = React.memo(function VisualizationPanel({ dashboardItem, index, isMultiPanelView, handleClosePanel, navigateTo, dragHandleProps, getDashboardItemByType, }) {
|
|
21
|
+
const [actionButtons, setActionButtons] = useState(_jsx(_Fragment, {}));
|
|
22
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full justify-end gap-2 pb-2", children: [_jsxs("div", { className: "flex w-full items-center justify-between", children: [_jsxs("div", { className: "flex", children: [_jsx("div", { className: cx(isMultiPanelView ? 'visible' : 'invisible', 'flex items-center'), ...dragHandleProps, children: _jsx(Icon, { className: "text-xl", icon: "material-symbols:drag-indicator" }) }), _jsx(_Fragment, { children: actionButtons })] }), _jsx(ViewSelector, { defaultValue: dashboardItem, navigateTo: navigateTo, position: index })] }), isMultiPanelView && (_jsx(IconButton, { onClick: () => handleClosePanel(dashboardItem), icon: _jsx(CloseIcon, {}) }))] }), getDashboardItemByType({
|
|
35
23
|
type: dashboardItem,
|
|
36
24
|
isHalfScreen: isMultiPanelView,
|
|
37
|
-
setActionButtons
|
|
25
|
+
setActionButtons,
|
|
38
26
|
})] }));
|
|
39
27
|
});
|
|
@@ -1,59 +1,3 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
23
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
-
function step(op) {
|
|
26
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
28
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
29
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
-
switch (op[0]) {
|
|
31
|
-
case 0: case 1: t = op; break;
|
|
32
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
-
default:
|
|
36
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
-
if (t[2]) _.ops.pop();
|
|
41
|
-
_.trys.pop(); continue;
|
|
42
|
-
}
|
|
43
|
-
op = body.call(thisArg, _);
|
|
44
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
49
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
50
|
-
if (ar || !(i in from)) {
|
|
51
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
52
|
-
ar[i] = from[i];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
56
|
-
};
|
|
57
1
|
import { createElement as _createElement } from "react";
|
|
58
2
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
59
3
|
// Copyright 2022 The Parca Authors
|
|
@@ -90,130 +34,105 @@ function arrayEquals(a, b) {
|
|
|
90
34
|
return (Array.isArray(a) &&
|
|
91
35
|
Array.isArray(b) &&
|
|
92
36
|
a.length === b.length &&
|
|
93
|
-
a.every(
|
|
37
|
+
a.every((val, index) => val === b[index]));
|
|
94
38
|
}
|
|
95
|
-
export
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
var _d = useURLState({
|
|
39
|
+
export const ProfileView = ({ total, filtered, flamegraphData, topTableData, callgraphData, sampleUnit, profileSource, queryClient, navigateTo, onDownloadPProf, onFlamegraphContainerResize, pprofDownloading, }) => {
|
|
40
|
+
const { ref, dimensions } = useContainerDimensions();
|
|
41
|
+
const [curPath, setCurPath] = useState([]);
|
|
42
|
+
const [rawDashboardItems, setDashboardItems] = useURLState({
|
|
100
43
|
param: 'dashboard_items',
|
|
101
|
-
navigateTo
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
44
|
+
navigateTo,
|
|
45
|
+
});
|
|
46
|
+
const [graphvizLoaded, setGraphvizLoaded] = useState(false);
|
|
47
|
+
const [callgraphSVG, setCallgraphSVG] = useState(undefined);
|
|
48
|
+
const [currentSearchString] = useURLState({ param: 'search_string' });
|
|
49
|
+
const dashboardItems = useMemo(() => {
|
|
107
50
|
if (rawDashboardItems !== undefined) {
|
|
108
51
|
return rawDashboardItems;
|
|
109
52
|
}
|
|
110
53
|
return ['icicle'];
|
|
111
54
|
}, [rawDashboardItems]);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
useEffect(
|
|
55
|
+
const isDarkMode = useAppSelector(selectDarkMode);
|
|
56
|
+
const isMultiPanelView = dashboardItems.length > 1;
|
|
57
|
+
const { loader, perf } = useParcaContext();
|
|
58
|
+
useEffect(() => {
|
|
116
59
|
// Reset the current path when the profile source changes
|
|
117
60
|
setCurPath([]);
|
|
118
61
|
}, [profileSource]);
|
|
119
|
-
useEffect(
|
|
120
|
-
function loadGraphviz() {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
switch (_a.label) {
|
|
124
|
-
case 0: return [4 /*yield*/, graphviz.loadWASM()];
|
|
125
|
-
case 1:
|
|
126
|
-
_a.sent();
|
|
127
|
-
setGraphvizLoaded(true);
|
|
128
|
-
return [2 /*return*/];
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
});
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
async function loadGraphviz() {
|
|
64
|
+
await graphviz.loadWASM();
|
|
65
|
+
setGraphvizLoaded(true);
|
|
132
66
|
}
|
|
133
67
|
void loadGraphviz();
|
|
134
68
|
}, []);
|
|
135
|
-
|
|
69
|
+
const isLoading = useMemo(() => {
|
|
136
70
|
if (dashboardItems.includes('icicle')) {
|
|
137
|
-
return Boolean(flamegraphData
|
|
71
|
+
return Boolean(flamegraphData?.loading);
|
|
138
72
|
}
|
|
139
73
|
if (dashboardItems.includes('callgraph')) {
|
|
140
|
-
return Boolean(callgraphData
|
|
74
|
+
return Boolean(callgraphData?.loading) || Boolean(callgraphSVG === undefined);
|
|
141
75
|
}
|
|
142
76
|
if (dashboardItems.includes('table')) {
|
|
143
|
-
return Boolean(topTableData
|
|
77
|
+
return Boolean(topTableData?.loading);
|
|
144
78
|
}
|
|
145
79
|
return false;
|
|
146
80
|
}, [
|
|
147
81
|
dashboardItems,
|
|
148
|
-
callgraphData
|
|
149
|
-
flamegraphData
|
|
150
|
-
topTableData
|
|
82
|
+
callgraphData?.loading,
|
|
83
|
+
flamegraphData?.loading,
|
|
84
|
+
topTableData?.loading,
|
|
151
85
|
callgraphSVG,
|
|
152
86
|
]);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
87
|
+
const isLoaderVisible = useDelayedLoader(isLoading);
|
|
88
|
+
const maxColor = getNewSpanColor(isDarkMode);
|
|
89
|
+
const minColor = scaleLinear([isDarkMode ? 'black' : 'white', maxColor])(0.3);
|
|
90
|
+
const colorRange = [minColor, maxColor];
|
|
157
91
|
// Note: If we want to further optimize the experience, we could try to load the graphviz layout in the ProfileViewWithData layer
|
|
158
92
|
// and pass it down to the ProfileView. This would allow us to load the layout in parallel with the flamegraph data.
|
|
159
93
|
// However, the layout calculation is dependent on the width and color range of the graph container, which is why it is done at this level
|
|
160
|
-
useEffect(
|
|
161
|
-
function loadCallgraphSVG(graph, width, colorRange) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
_a.sent();
|
|
169
|
-
return [4 /*yield*/, jsonToDot({
|
|
170
|
-
graph: graph,
|
|
171
|
-
width: width,
|
|
172
|
-
colorRange: colorRange,
|
|
173
|
-
})];
|
|
174
|
-
case 2:
|
|
175
|
-
dataAsDot = _a.sent();
|
|
176
|
-
return [4 /*yield*/, graphviz.layout(dataAsDot, 'svg', 'dot')];
|
|
177
|
-
case 3:
|
|
178
|
-
svgGraph = _a.sent();
|
|
179
|
-
return [4 /*yield*/, setCallgraphSVG(svgGraph)];
|
|
180
|
-
case 4:
|
|
181
|
-
_a.sent();
|
|
182
|
-
return [2 /*return*/];
|
|
183
|
-
}
|
|
184
|
-
});
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
async function loadCallgraphSVG(graph, width, colorRange) {
|
|
96
|
+
await setCallgraphSVG(undefined);
|
|
97
|
+
// Translate JSON to 'dot' graph string
|
|
98
|
+
const dataAsDot = await jsonToDot({
|
|
99
|
+
graph,
|
|
100
|
+
width,
|
|
101
|
+
colorRange,
|
|
185
102
|
});
|
|
103
|
+
// Use Graphviz-WASM to translate the 'dot' graph to a 'JSON' graph
|
|
104
|
+
const svgGraph = await graphviz.layout(dataAsDot, 'svg', 'dot');
|
|
105
|
+
await setCallgraphSVG(svgGraph);
|
|
186
106
|
}
|
|
187
107
|
if (graphvizLoaded &&
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
void loadCallgraphSVG(callgraphData
|
|
108
|
+
callgraphData?.data !== null &&
|
|
109
|
+
callgraphData?.data !== undefined &&
|
|
110
|
+
dimensions?.width !== undefined) {
|
|
111
|
+
void loadCallgraphSVG(callgraphData?.data, dimensions?.width, colorRange);
|
|
192
112
|
}
|
|
193
113
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
194
|
-
}, [graphvizLoaded, callgraphData
|
|
195
|
-
if (
|
|
196
|
-
console.error('Error: ', flamegraphData
|
|
197
|
-
return (_jsxs("div",
|
|
114
|
+
}, [graphvizLoaded, callgraphData?.data]);
|
|
115
|
+
if (flamegraphData?.error !== null) {
|
|
116
|
+
console.error('Error: ', flamegraphData?.error);
|
|
117
|
+
return (_jsxs("div", { className: "flex justify-center p-10", children: ["An error occurred: ", flamegraphData?.error.message] }));
|
|
198
118
|
}
|
|
199
|
-
|
|
119
|
+
const setNewCurPath = (path) => {
|
|
200
120
|
if (!arrayEquals(curPath, path)) {
|
|
201
121
|
setCurPath(path);
|
|
202
122
|
}
|
|
203
123
|
};
|
|
204
|
-
|
|
205
|
-
var type = _a.type, isHalfScreen = _a.isHalfScreen, setActionButtons = _a.setActionButtons;
|
|
124
|
+
const getDashboardItemByType = ({ type, isHalfScreen, setActionButtons, }) => {
|
|
206
125
|
switch (type) {
|
|
207
126
|
case 'icicle': {
|
|
208
|
-
return
|
|
127
|
+
return flamegraphData?.data != null ? (_jsx(ConditionalWrapper, { condition: perf?.onRender != null, WrapperComponent: Profiler, wrapperProps: {
|
|
209
128
|
id: 'icicleGraph',
|
|
210
|
-
onRender: perf
|
|
211
|
-
}
|
|
129
|
+
onRender: perf?.onRender,
|
|
130
|
+
}, children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, total: total, filtered: filtered, sampleUnit: sampleUnit, onContainerResize: onFlamegraphContainerResize, navigateTo: navigateTo, loading: flamegraphData.loading, setActionButtons: setActionButtons }) })) : (_jsx(_Fragment, { children: " " }));
|
|
212
131
|
}
|
|
213
132
|
case 'callgraph': {
|
|
214
|
-
return
|
|
133
|
+
return callgraphData?.data !== undefined &&
|
|
215
134
|
callgraphSVG !== undefined &&
|
|
216
|
-
|
|
135
|
+
dimensions?.width !== undefined ? (_jsx(Callgraph, { data: callgraphData.data, svgString: callgraphSVG, sampleUnit: sampleUnit, width: isHalfScreen ? dimensions?.width / 2 : dimensions?.width })) : (_jsx(_Fragment, {}));
|
|
217
136
|
}
|
|
218
137
|
case 'table': {
|
|
219
138
|
return topTableData != null ? (_jsx(TopTable, { loading: topTableData.loading, data: topTableData.data, sampleUnit: sampleUnit, navigateTo: navigateTo, setActionButtons: setActionButtons, currentSearchString: currentSearchString })) : (_jsx(_Fragment, {}));
|
|
@@ -223,27 +142,28 @@ export var ProfileView = function (_a) {
|
|
|
223
142
|
}
|
|
224
143
|
}
|
|
225
144
|
};
|
|
226
|
-
|
|
227
|
-
|
|
145
|
+
const handleClosePanel = (visualizationType) => {
|
|
146
|
+
const newDashboardItems = dashboardItems.filter(item => item !== visualizationType);
|
|
228
147
|
setDashboardItems(newDashboardItems);
|
|
229
148
|
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (Boolean(destination) &&
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
?
|
|
149
|
+
const onDragEnd = (result) => {
|
|
150
|
+
const { destination, source, draggableId } = result;
|
|
151
|
+
if (Boolean(destination) && destination?.index !== source.index) {
|
|
152
|
+
const targetItem = draggableId;
|
|
153
|
+
const otherItems = dashboardItems.filter(item => item !== targetItem);
|
|
154
|
+
const newDashboardItems = destination.index < source.index
|
|
155
|
+
? [targetItem, ...otherItems]
|
|
156
|
+
: [...otherItems, targetItem];
|
|
237
157
|
setDashboardItems(newDashboardItems);
|
|
238
158
|
}
|
|
239
159
|
};
|
|
240
|
-
return (_jsx(KeyDownProvider, { children: _jsx("div",
|
|
160
|
+
return (_jsx(KeyDownProvider, { children: _jsx("div", { className: "py-3", children: _jsx(Card, { children: _jsxs(Card.Body, { children: [_jsxs("div", { className: "flex w-full py-3", children: [_jsxs("div", { className: "flex space-x-4 lg:w-1/2", children: [_jsxs("div", { className: "flex space-x-1", children: [profileSource !== undefined && queryClient !== undefined ? (_jsx(ProfileShareButton, { queryRequest: profileSource.QueryRequest(), queryClient: queryClient })) : null, _jsx(Button, { color: "neutral", onClick: e => {
|
|
241
161
|
e.preventDefault();
|
|
242
162
|
onDownloadPProf();
|
|
243
|
-
}, disabled: pprofDownloading
|
|
163
|
+
}, disabled: pprofDownloading, children: pprofDownloading != null && pprofDownloading
|
|
244
164
|
? 'Downloading'
|
|
245
|
-
: 'Download pprof' })
|
|
246
|
-
return (_jsx(Draggable,
|
|
247
|
-
_jsx(VisualizationPanel, { handleClosePanel: handleClosePanel, isMultiPanelView: isMultiPanelView, dashboardItem: dashboardItem, getDashboardItemByType: getDashboardItemByType, dragHandleProps: provided.dragHandleProps, navigateTo: navigateTo, index: index })))
|
|
248
|
-
}) }))
|
|
165
|
+
: 'Download pprof' })] }), _jsx(FilterByFunctionButton, { navigateTo: navigateTo })] }), _jsx("div", { className: "ml-auto flex gap-2", children: _jsx(ViewSelector, { defaultValue: "", navigateTo: navigateTo, position: -1, placeholderText: "Add panel...", primary: true, addView: true, disabled: isMultiPanelView || dashboardItems.length < 1 }) })] }), _jsx("div", { className: "w-full", ref: ref, children: isLoaderVisible ? (_jsx(_Fragment, { children: loader })) : (_jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: "droppable", direction: "horizontal", children: provided => (_jsx("div", { ref: provided.innerRef, className: "flex w-full justify-between space-x-4", ...provided.droppableProps, children: dashboardItems.map((dashboardItem, index) => {
|
|
166
|
+
return (_jsx(Draggable, { draggableId: dashboardItem, index: index, isDragDisabled: !isMultiPanelView, children: (provided, snapshot) => (_createElement("div", { ref: provided.innerRef, ...provided.draggableProps, key: dashboardItem, className: cx('rounded border border-gray-300 p-3 dark:border-gray-500 dark:bg-gray-700', isMultiPanelView ? 'w-1/2' : 'w-full', snapshot.isDragging ? 'bg-gray-200' : 'bg-white') },
|
|
167
|
+
_jsx(VisualizationPanel, { handleClosePanel: handleClosePanel, isMultiPanelView: isMultiPanelView, dashboardItem: dashboardItem, getDashboardItemByType: getDashboardItemByType, dragHandleProps: provided.dragHandleProps, navigateTo: navigateTo, index: index }))) }, dashboardItem));
|
|
168
|
+
}) })) }) })) })] }) }) }) }));
|
|
249
169
|
};
|
|
@@ -1,39 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
38
2
|
// Copyright 2022 The Parca Authors
|
|
39
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -55,50 +19,47 @@ import { saveAsBlob } from '@parca/utilities';
|
|
|
55
19
|
import { ProfileView } from './ProfileView';
|
|
56
20
|
import { useQuery } from './useQuery';
|
|
57
21
|
import { downloadPprof } from './utils';
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
var _g = useState(false), pprofDownloading = _g[0], setPprofDownloading = _g[1];
|
|
66
|
-
useEffect(function () {
|
|
22
|
+
export const ProfileViewWithData = ({ queryClient, profileSource, navigateTo, }) => {
|
|
23
|
+
const metadata = useGrpcMetadata();
|
|
24
|
+
const [dashboardItems] = useURLState({ param: 'dashboard_items', navigateTo });
|
|
25
|
+
const [nodeTrimThreshold, setNodeTrimThreshold] = useState(0);
|
|
26
|
+
const [enableTrimming] = useUserPreference(USER_PREFERENCES.ENABLE_GRAPH_TRIMMING.key);
|
|
27
|
+
const [pprofDownloading, setPprofDownloading] = useState(false);
|
|
28
|
+
useEffect(() => {
|
|
67
29
|
if (!enableTrimming) {
|
|
68
30
|
setNodeTrimThreshold(0);
|
|
69
31
|
}
|
|
70
32
|
}, [enableTrimming]);
|
|
71
|
-
|
|
33
|
+
const onFlamegraphContainerResize = (width) => {
|
|
72
34
|
if (!enableTrimming || width === 0) {
|
|
73
35
|
return;
|
|
74
36
|
}
|
|
75
|
-
|
|
37
|
+
const threshold = (1 / width) * 100;
|
|
76
38
|
if (threshold === nodeTrimThreshold) {
|
|
77
39
|
return;
|
|
78
40
|
}
|
|
79
41
|
setNodeTrimThreshold(threshold);
|
|
80
42
|
};
|
|
81
|
-
|
|
43
|
+
const { isLoading: flamegraphLoading, response: flamegraphResponse, error: flamegraphError, } = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
|
|
82
44
|
skip: !dashboardItems.includes('icicle'),
|
|
83
|
-
nodeTrimThreshold
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
|
|
45
|
+
nodeTrimThreshold,
|
|
46
|
+
});
|
|
47
|
+
const { perf } = useParcaContext();
|
|
48
|
+
const { isLoading: topTableLoading, response: topTableResponse, error: topTableError, } = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
|
|
87
49
|
skip: !dashboardItems.includes('table'),
|
|
88
|
-
})
|
|
89
|
-
|
|
50
|
+
});
|
|
51
|
+
const { isLoading: callgraphLoading, response: callgraphResponse, error: callgraphError, } = useQuery(queryClient, profileSource, QueryRequest_ReportType.CALLGRAPH, {
|
|
90
52
|
skip: !dashboardItems.includes('callgraph'),
|
|
91
|
-
})
|
|
92
|
-
useEffect(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
perf === null || perf === void 0 ? void 0 : perf.markInteraction('Flamegraph render', flamegraphResponse.report.flamegraph.total);
|
|
53
|
+
});
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!flamegraphLoading && flamegraphResponse?.report.oneofKind === 'flamegraph') {
|
|
56
|
+
perf?.markInteraction('Flamegraph render', flamegraphResponse.report.flamegraph.total);
|
|
96
57
|
}
|
|
97
|
-
if (!topTableLoading &&
|
|
98
|
-
perf
|
|
58
|
+
if (!topTableLoading && topTableResponse?.report.oneofKind === 'top') {
|
|
59
|
+
perf?.markInteraction('Top table render', topTableResponse?.report?.top.total);
|
|
99
60
|
}
|
|
100
|
-
if (!callgraphLoading &&
|
|
101
|
-
perf
|
|
61
|
+
if (!callgraphLoading && callgraphResponse?.report.oneofKind === 'callgraph') {
|
|
62
|
+
perf?.markInteraction('Callgraph render', callgraphResponse?.report?.callgraph.cumulative);
|
|
102
63
|
}
|
|
103
64
|
}, [
|
|
104
65
|
flamegraphLoading,
|
|
@@ -109,38 +70,26 @@ export var ProfileViewWithData = function (_a) {
|
|
|
109
70
|
topTableResponse,
|
|
110
71
|
perf,
|
|
111
72
|
]);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
saveAsBlob(blob, "profile.pb.gz");
|
|
129
|
-
setPprofDownloading(false);
|
|
130
|
-
return [3 /*break*/, 4];
|
|
131
|
-
case 3:
|
|
132
|
-
error_1 = _a.sent();
|
|
133
|
-
setPprofDownloading(false);
|
|
134
|
-
console.error('Error while querying', error_1);
|
|
135
|
-
return [3 /*break*/, 4];
|
|
136
|
-
case 4: return [2 /*return*/];
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}); };
|
|
73
|
+
const sampleUnit = profileSource.ProfileType().sampleUnit;
|
|
74
|
+
const downloadPProfClick = async () => {
|
|
75
|
+
if (profileSource == null || queryClient == null) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
setPprofDownloading(true);
|
|
80
|
+
const blob = await downloadPprof(profileSource.QueryRequest(), queryClient, metadata);
|
|
81
|
+
saveAsBlob(blob, `profile.pb.gz`);
|
|
82
|
+
setPprofDownloading(false);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
setPprofDownloading(false);
|
|
86
|
+
console.error('Error while querying', error);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
140
89
|
// TODO: Refactor how we get responses such that we have a single response,
|
|
141
90
|
// regardless of the report type.
|
|
142
|
-
|
|
143
|
-
|
|
91
|
+
let total = BigInt(0);
|
|
92
|
+
let filtered = BigInt(0);
|
|
144
93
|
if (flamegraphResponse !== null) {
|
|
145
94
|
total = BigInt(flamegraphResponse.total);
|
|
146
95
|
filtered = BigInt(flamegraphResponse.filtered);
|
|
@@ -155,22 +104,22 @@ export var ProfileViewWithData = function (_a) {
|
|
|
155
104
|
}
|
|
156
105
|
return (_jsx(ProfileView, { total: total, filtered: filtered, flamegraphData: {
|
|
157
106
|
loading: flamegraphLoading,
|
|
158
|
-
data:
|
|
159
|
-
?
|
|
107
|
+
data: flamegraphResponse?.report.oneofKind === 'flamegraph'
|
|
108
|
+
? flamegraphResponse?.report?.flamegraph
|
|
160
109
|
: undefined,
|
|
161
|
-
total: BigInt(
|
|
162
|
-
filtered: BigInt(
|
|
110
|
+
total: BigInt(flamegraphResponse?.total ?? '0'),
|
|
111
|
+
filtered: BigInt(flamegraphResponse?.filtered ?? '0'),
|
|
163
112
|
error: flamegraphError,
|
|
164
113
|
}, topTableData: {
|
|
165
114
|
loading: topTableLoading,
|
|
166
|
-
data:
|
|
115
|
+
data: topTableResponse?.report.oneofKind === 'top' ? topTableResponse.report.top : undefined,
|
|
167
116
|
error: topTableError,
|
|
168
117
|
}, callgraphData: {
|
|
169
118
|
loading: callgraphLoading,
|
|
170
|
-
data:
|
|
171
|
-
?
|
|
119
|
+
data: callgraphResponse?.report.oneofKind === 'callgraph'
|
|
120
|
+
? callgraphResponse?.report?.callgraph
|
|
172
121
|
: undefined,
|
|
173
122
|
error: callgraphError,
|
|
174
|
-
}, sampleUnit: sampleUnit, profileSource: profileSource, queryClient: queryClient, navigateTo: navigateTo, onDownloadPProf:
|
|
123
|
+
}, sampleUnit: sampleUnit, profileSource: profileSource, queryClient: queryClient, navigateTo: navigateTo, onDownloadPProf: () => void downloadPProfClick(), pprofDownloading: pprofDownloading, onFlamegraphContainerResize: onFlamegraphContainerResize }));
|
|
175
124
|
};
|
|
176
125
|
export default ProfileViewWithData;
|