@parca/profile 0.16.59 → 0.16.60
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.d.ts +2 -0
- package/dist/Callgraph/constants.js +14 -0
- package/dist/Callgraph/index.d.ts +2 -2
- package/dist/Callgraph/index.js +106 -35
- package/dist/Callgraph/utils.d.ts +6 -6
- package/dist/Callgraph/utils.js +55 -29
- package/dist/GraphTooltip/index.d.ts +0 -1
- package/dist/IcicleGraph.d.ts +0 -1
- package/dist/MatchersInput/index.d.ts +0 -1
- package/dist/MetricsCircle/index.d.ts +0 -1
- package/dist/MetricsGraph/index.d.ts +0 -1
- package/dist/MetricsSeries/index.d.ts +0 -1
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +0 -1
- package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +0 -1
- package/dist/ProfileExplorer/index.d.ts +0 -1
- package/dist/ProfileIcicleGraph.d.ts +0 -1
- package/dist/ProfileMetricsGraph/index.d.ts +0 -1
- package/dist/ProfileSelector/CompareButton.d.ts +0 -1
- package/dist/ProfileSelector/MergeButton.d.ts +0 -1
- package/dist/ProfileSelector/index.d.ts +0 -1
- package/dist/ProfileSource.d.ts +0 -1
- package/dist/ProfileTypeSelector/index.d.ts +0 -1
- package/dist/ProfileView.d.ts +2 -3
- package/dist/ProfileView.js +15 -7
- package/dist/ProfileViewWithData.d.ts +0 -1
- package/dist/TopTable.d.ts +0 -1
- package/dist/components/DiffLegend.d.ts +0 -1
- package/dist/components/ProfileShareButton/ResultBox.d.ts +0 -1
- package/dist/components/ProfileShareButton/index.d.ts +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/package.json +7 -6
- package/src/Callgraph/constants.ts +15 -0
- package/src/Callgraph/index.tsx +232 -66
- package/src/Callgraph/utils.ts +68 -39
- package/src/ProfileView.tsx +128 -114
- package/dist/Callgraph/Edge/index.d.ts +0 -23
- package/dist/Callgraph/Edge/index.js +0 -30
- package/dist/Callgraph/Node/index.d.ts +0 -20
- package/dist/Callgraph/Node/index.js +0 -37
- package/src/Callgraph/Edge/index.tsx +0 -59
- package/src/Callgraph/Node/index.tsx +0 -66
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.16.60](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.59...@parca/profile@0.16.60) (2022-11-03)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
6
10
|
## [0.16.59](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.58...@parca/profile@0.16.59) (2022-11-03)
|
|
7
11
|
|
|
8
12
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
export var DEFAULT_NODE_HEIGHT = 20;
|
|
14
|
+
export var GRAPH_MARGIN = 15;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { Callgraph as CallgraphType } from '@parca/client';
|
|
3
2
|
export interface Props {
|
|
4
3
|
graph: CallgraphType;
|
|
5
4
|
sampleUnit: string;
|
|
6
5
|
width: number;
|
|
6
|
+
colorRange: [string, string];
|
|
7
7
|
}
|
|
8
|
-
declare const Callgraph: ({ graph, sampleUnit, width }: Props) => JSX.Element;
|
|
8
|
+
declare const Callgraph: ({ graph, sampleUnit, width, colorRange }: Props) => JSX.Element;
|
|
9
9
|
export default Callgraph;
|
package/dist/Callgraph/index.js
CHANGED
|
@@ -45,7 +45,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
45
45
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
-
import {
|
|
48
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
49
49
|
// Copyright 2022 The Parca Authors
|
|
50
50
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
51
51
|
// you may not use this file except in compliance with the License.
|
|
@@ -61,26 +61,65 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
61
61
|
import { useState, useEffect, useRef } from 'react';
|
|
62
62
|
import graphviz from 'graphviz-wasm';
|
|
63
63
|
import * as d3 from 'd3';
|
|
64
|
-
import { Stage, Layer } from 'react-konva';
|
|
64
|
+
import { Stage, Layer, Rect, Arrow, Text, Label } from 'react-konva';
|
|
65
|
+
import { jsonToDot, getCurvePoints } from './utils';
|
|
66
|
+
import { useAppSelector, selectSearchNodeString } from '@parca/store';
|
|
67
|
+
import { isSearchMatch } from '@parca/functions';
|
|
65
68
|
import Tooltip from '../GraphTooltip';
|
|
66
|
-
import {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
import { DEFAULT_NODE_HEIGHT, GRAPH_MARGIN } from './constants';
|
|
70
|
+
var Node = function (_a) {
|
|
71
|
+
var node = _a.node, hoveredNode = _a.hoveredNode, setHoveredNode = _a.setHoveredNode, isCurrentSearchMatch = _a.isCurrentSearchMatch;
|
|
72
|
+
var id = node.data.id, x = node.x, y = node.y, color = node.color, functionName = node.functionName, widthString = node.width, heightString = node.height;
|
|
73
|
+
var isHovered = Boolean(hoveredNode) && (hoveredNode === null || hoveredNode === void 0 ? void 0 : hoveredNode.data.id) === id;
|
|
74
|
+
var width = Number(widthString);
|
|
75
|
+
var height = Number(heightString);
|
|
76
|
+
var textPadding = 6;
|
|
77
|
+
var opacity = isCurrentSearchMatch ? 1 : 0.1;
|
|
78
|
+
return (_jsxs(Label, __assign({ x: x - width / 2, y: y - height / 2 }, { children: [_jsx(Rect, { width: width, height: height, fill: color, opacity: opacity, cornerRadius: 3, stroke: isHovered ? 'black' : color, strokeWidth: 2, onMouseOver: function (e) {
|
|
79
|
+
setHoveredNode(__assign(__assign({}, node), { mouseX: e.evt.clientX, mouseY: e.evt.clientY }));
|
|
80
|
+
}, onMouseOut: function () {
|
|
81
|
+
setHoveredNode(null);
|
|
82
|
+
} }), width > DEFAULT_NODE_HEIGHT + 10 && (_jsx(Text, { text: functionName, fontSize: 10, fill: "white", width: width - textPadding, height: height - textPadding, x: textPadding / 2, y: textPadding / 2, align: "center", verticalAlign: "middle", listening: false }))] })));
|
|
83
|
+
};
|
|
84
|
+
var Edge = function (_a) {
|
|
85
|
+
var edge = _a.edge, sourceNode = _a.sourceNode, targetNode = _a.targetNode, xScale = _a.xScale, yScale = _a.yScale, isCurrentSearchMatch = _a.isCurrentSearchMatch;
|
|
86
|
+
var pos = edge.pos, color = edge.color, head = edge.head, tail = edge.tail, opacity = edge.opacity, boxHeight = edge.boxHeight;
|
|
87
|
+
var points = getCurvePoints({
|
|
88
|
+
pos: pos,
|
|
89
|
+
xScale: xScale,
|
|
90
|
+
yScale: yScale,
|
|
91
|
+
source: [sourceNode.x, sourceNode.y],
|
|
92
|
+
target: [targetNode.x, targetNode.y],
|
|
93
|
+
offset: boxHeight / 2,
|
|
94
|
+
isSelfLoop: head === tail,
|
|
95
|
+
});
|
|
96
|
+
return (_jsx(Arrow, { points: points, bezier: true, stroke: color, strokeWidth: 3, pointerLength: 10, pointerWidth: 10, fill: color, opacity: isCurrentSearchMatch ? Number(opacity) : 0 }));
|
|
97
|
+
};
|
|
69
98
|
var Callgraph = function (_a) {
|
|
70
99
|
var _b, _c;
|
|
71
|
-
var graph = _a.graph, sampleUnit = _a.sampleUnit, width = _a.width;
|
|
100
|
+
var graph = _a.graph, sampleUnit = _a.sampleUnit, width = _a.width, colorRange = _a.colorRange;
|
|
72
101
|
var containerRef = useRef(null);
|
|
73
102
|
var _d = useState(null), graphData = _d[0], setGraphData = _d[1];
|
|
74
103
|
var _e = useState(null), hoveredNode = _e[0], setHoveredNode = _e[1];
|
|
104
|
+
var _f = useState({
|
|
105
|
+
scale: { x: 1, y: 1 },
|
|
106
|
+
x: 0,
|
|
107
|
+
y: 0,
|
|
108
|
+
}), stage = _f[0], setStage = _f[1];
|
|
75
109
|
var rawNodes = graph.nodes, total = graph.cumulative;
|
|
76
|
-
var
|
|
110
|
+
var currentSearchString = useAppSelector(selectSearchNodeString);
|
|
111
|
+
var isSearchEmpty = currentSearchString === undefined;
|
|
77
112
|
useEffect(function () {
|
|
78
113
|
var getDataWithPositions = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
79
114
|
var dataAsDot, jsonGraph;
|
|
80
115
|
return __generator(this, function (_a) {
|
|
81
116
|
switch (_a.label) {
|
|
82
117
|
case 0:
|
|
83
|
-
dataAsDot = jsonToDot({
|
|
118
|
+
dataAsDot = jsonToDot({
|
|
119
|
+
graph: graph,
|
|
120
|
+
width: width,
|
|
121
|
+
colorRange: colorRange,
|
|
122
|
+
});
|
|
84
123
|
// 2. Use Graphviz-WASM to translate the 'dot' graph to a 'JSON' graph
|
|
85
124
|
return [4 /*yield*/, graphviz.loadWASM()];
|
|
86
125
|
case 1:
|
|
@@ -95,43 +134,75 @@ var Callgraph = function (_a) {
|
|
|
95
134
|
if (width !== null) {
|
|
96
135
|
void getDataWithPositions();
|
|
97
136
|
}
|
|
98
|
-
}, [graph, width]);
|
|
137
|
+
}, [graph, width, colorRange]);
|
|
99
138
|
// 3. Render the graph with calculated layout in Canvas container
|
|
100
139
|
if (width == null || graphData == null)
|
|
101
140
|
return _jsx(_Fragment, {});
|
|
102
|
-
var
|
|
103
|
-
var _f = JSON.parse(graphData), objects = _f.objects, gvizEdges = _f.edges, boundingBox = _f.bb;
|
|
104
|
-
var cumulatives = objects
|
|
105
|
-
.filter(function (node) { return node !== undefined; })
|
|
106
|
-
.map(function (node) { return node.cumulative; });
|
|
107
|
-
if (cumulatives.length === 0) {
|
|
108
|
-
cumulatives.push('0');
|
|
109
|
-
}
|
|
110
|
-
var valueRange = d3.extent(cumulatives).map(function (value) { return parseInt(value); });
|
|
111
|
-
var colorScale = d3
|
|
112
|
-
.scaleSequentialLog(d3.interpolateRdGy)
|
|
113
|
-
.domain(valueRange)
|
|
114
|
-
.range(['lightgrey', 'red']);
|
|
141
|
+
var _g = JSON.parse(graphData), gvizNodes = _g.objects, edges = _g.edges, boundingBox = _g.bb;
|
|
115
142
|
var graphBB = boundingBox.split(',');
|
|
143
|
+
var bbWidth = Number(graphBB[2]);
|
|
144
|
+
var bbHeight = Number(graphBB[3]);
|
|
145
|
+
var height = (width * bbHeight) / bbWidth;
|
|
116
146
|
var xScale = d3
|
|
117
147
|
.scaleLinear()
|
|
118
|
-
.domain([0,
|
|
119
|
-
.range([0, width]);
|
|
148
|
+
.domain([0, bbWidth])
|
|
149
|
+
.range([0, width - 2 * GRAPH_MARGIN]);
|
|
120
150
|
var yScale = d3
|
|
121
151
|
.scaleLinear()
|
|
122
|
-
.domain([0,
|
|
123
|
-
.range([0, height]);
|
|
124
|
-
var nodes =
|
|
152
|
+
.domain([0, bbHeight])
|
|
153
|
+
.range([0, height - 2 * GRAPH_MARGIN]);
|
|
154
|
+
var nodes = gvizNodes.map(function (node) {
|
|
125
155
|
var _a;
|
|
126
|
-
var
|
|
127
|
-
return __assign(__assign({},
|
|
156
|
+
var _b = node.pos.split(','), x = _b[0], y = _b[1];
|
|
157
|
+
return __assign(__assign({}, node), { x: xScale(Number(x)), y: yScale(Number(y)), data: (_a = rawNodes.find(function (n) { return n.id === node.name; })) !== null && _a !== void 0 ? _a : { id: 'n0' } });
|
|
128
158
|
});
|
|
129
|
-
|
|
130
|
-
|
|
159
|
+
// 4. Add zooming
|
|
160
|
+
var handleWheel = function (e) {
|
|
161
|
+
var _a;
|
|
162
|
+
e.evt.preventDefault();
|
|
163
|
+
var scaleXBy = 0.95;
|
|
164
|
+
var scaleYBy = 1.05;
|
|
165
|
+
var stage = e.target.getStage();
|
|
166
|
+
if (stage !== null) {
|
|
167
|
+
var oldScale = stage.scaleX();
|
|
168
|
+
var _b = (_a = stage.getPointerPosition()) !== null && _a !== void 0 ? _a : { x: 0, y: 0 }, x = _b.x, y = _b.y;
|
|
169
|
+
var mousePointTo = {
|
|
170
|
+
x: x / oldScale - stage.x() / oldScale,
|
|
171
|
+
y: y / oldScale - stage.y() / oldScale,
|
|
172
|
+
};
|
|
173
|
+
var newXScale = e.evt.deltaX > 0 ? oldScale * scaleXBy : oldScale / scaleXBy;
|
|
174
|
+
var newYScale = e.evt.deltaY > 0 ? oldScale * scaleYBy : oldScale / scaleYBy;
|
|
175
|
+
stage.scale({ x: newXScale, y: newYScale });
|
|
176
|
+
setStage({
|
|
177
|
+
scale: { x: newXScale, y: newYScale },
|
|
178
|
+
x: -(mousePointTo.x - x / newXScale) * newXScale,
|
|
179
|
+
y: -(mousePointTo.y - y / newYScale) * newYScale,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return (_jsx("div", __assign({ className: "relative" }, { children: _jsxs("div", __assign({ className: "w-[".concat(width, "px] h-[").concat(height, "px]"), ref: containerRef }, { children: [_jsx(Stage, __assign({ width: width, height: height, onWheel: handleWheel, scaleX: stage.scale.x, scaleY: stage.scale.y, x: stage.x, y: stage.y, draggable: true }, { children: _jsxs(Layer, __assign({ offsetX: -GRAPH_MARGIN, offsetY: -GRAPH_MARGIN }, { children: [edges.map(function (edge) {
|
|
131
184
|
var _a, _b;
|
|
132
|
-
|
|
133
|
-
var
|
|
134
|
-
|
|
135
|
-
|
|
185
|
+
// 'tail' in graphviz-wasm means 'source' and 'head' means 'target'
|
|
186
|
+
var sourceNode = (_a = nodes.find(function (n) { return n._gvid === edge.tail; })) !== null && _a !== void 0 ? _a : {
|
|
187
|
+
x: 0,
|
|
188
|
+
y: 0,
|
|
189
|
+
functionName: '',
|
|
190
|
+
};
|
|
191
|
+
var targetNode = (_b = nodes.find(function (n) { return n._gvid === edge.head; })) !== null && _b !== void 0 ? _b : {
|
|
192
|
+
x: 0,
|
|
193
|
+
y: 0,
|
|
194
|
+
functionName: '',
|
|
195
|
+
};
|
|
196
|
+
var isCurrentSearchMatch = isSearchEmpty
|
|
197
|
+
? true
|
|
198
|
+
: isSearchMatch(currentSearchString, sourceNode.functionName) &&
|
|
199
|
+
isSearchMatch(currentSearchString, targetNode.functionName);
|
|
200
|
+
return (_jsx(Edge, { edge: edge, xScale: xScale, yScale: yScale, sourceNode: sourceNode, targetNode: targetNode, isCurrentSearchMatch: isCurrentSearchMatch }, "edge-".concat(edge.tail, "-").concat(edge.head)));
|
|
201
|
+
}), nodes.map(function (node) {
|
|
202
|
+
var isCurrentSearchMatch = isSearchEmpty
|
|
203
|
+
? true
|
|
204
|
+
: isSearchMatch(currentSearchString, node.functionName);
|
|
205
|
+
return (_jsx(Node, { node: node, hoveredNode: hoveredNode, setHoveredNode: setHoveredNode, isCurrentSearchMatch: isCurrentSearchMatch }, "node-".concat(node._gvid)));
|
|
206
|
+
})] })) })), _jsx(Tooltip, { hoveringNode: rawNodes.find(function (n) { return n.id === (hoveredNode === null || hoveredNode === void 0 ? void 0 : hoveredNode.data.id); }), unit: sampleUnit, total: +total, isFixed: false, x: (_b = hoveredNode === null || hoveredNode === void 0 ? void 0 : hoveredNode.mouseX) !== null && _b !== void 0 ? _b : 0, y: (_c = hoveredNode === null || hoveredNode === void 0 ? void 0 : hoveredNode.mouseY) !== null && _c !== void 0 ? _c : 0, contextElement: containerRef.current })] })) })));
|
|
136
207
|
};
|
|
137
208
|
export default Callgraph;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { CallgraphNode, CallgraphEdge } from '@parca/client';
|
|
2
2
|
export declare const pixelsToInches: (pixels: number) => number;
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const getCurvePoints: ({ pos, xScale, yScale, source, target, offset, isSelfLoop, }: {
|
|
4
4
|
pos: string;
|
|
5
|
-
xScale?: ((pos: number) =>
|
|
6
|
-
yScale?: ((pos: number) =>
|
|
5
|
+
xScale?: ((pos: number) => number) | undefined;
|
|
6
|
+
yScale?: ((pos: number) => number) | undefined;
|
|
7
7
|
source?: number[] | undefined;
|
|
8
8
|
target?: number[] | undefined;
|
|
9
|
-
nodeRadius: number;
|
|
10
9
|
isSelfLoop?: boolean | undefined;
|
|
10
|
+
offset?: number | undefined;
|
|
11
11
|
}) => number[];
|
|
12
|
-
export declare const jsonToDot: ({ graph,
|
|
12
|
+
export declare const jsonToDot: ({ graph, colorRange, }: {
|
|
13
13
|
graph: {
|
|
14
14
|
nodes: CallgraphNode[];
|
|
15
15
|
edges: CallgraphEdge[];
|
|
16
16
|
};
|
|
17
17
|
width: number;
|
|
18
|
-
|
|
18
|
+
colorRange: [string, string];
|
|
19
19
|
}) => string;
|
package/dist/Callgraph/utils.js
CHANGED
|
@@ -19,48 +19,68 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
19
19
|
}
|
|
20
20
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
21
21
|
};
|
|
22
|
+
import * as d3 from 'd3';
|
|
23
|
+
import { DEFAULT_NODE_HEIGHT } from './constants';
|
|
22
24
|
export var pixelsToInches = function (pixels) { return pixels / 96; };
|
|
23
|
-
export var
|
|
24
|
-
var _b;
|
|
25
|
-
var pos = _a.pos, _c = _a.xScale, xScale = _c === void 0 ? function (n) { return n; } : _c, _d = _a.yScale, yScale = _d === void 0 ? function (n) { return n; } : _d, _e = _a.source, source = _e === void 0 ? [] : _e, _f = _a.target, target = _f === void 0 ? [] : _f, nodeRadius = _a.nodeRadius, _g = _a.isSelfLoop, isSelfLoop = _g === void 0 ? false : _g;
|
|
26
|
-
var parts = pos.split(' ');
|
|
27
|
-
var arrow = (_b = parts.shift()) !== null && _b !== void 0 ? _b : '';
|
|
28
|
-
var partsAsArrays = parts.map(function (part) { return part.split(','); });
|
|
29
|
-
var scalePosArray = function (posArr) { return [+xScale(+posArr[0]), +yScale(+posArr[1])]; };
|
|
30
|
-
var _h = partsAsArrays.map(function (posArr) { return scalePosArray(posArr); }), _start = _h[0], cp1 = _h[1], cp2 = _h[2], _end = _h[3];
|
|
31
|
-
var arrowEnd = scalePosArray(arrow.replace('e,', '').split(','));
|
|
32
|
-
var getTargetWithOffset = function (target, lastEdgePoint) {
|
|
33
|
-
var diffX = target[0] - lastEdgePoint[0];
|
|
34
|
-
var diffY = target[1] - lastEdgePoint[1];
|
|
35
|
-
var diffZ = Math.hypot(diffX, diffY);
|
|
36
|
-
var offsetX = (diffX * nodeRadius) / diffZ;
|
|
37
|
-
var offsetY = (diffY * nodeRadius) / diffZ;
|
|
38
|
-
return [target[0] - offsetX, target[1] - offsetY];
|
|
39
|
-
};
|
|
25
|
+
export var getCurvePoints = function (_a) {
|
|
26
|
+
var pos = _a.pos, _b = _a.xScale, xScale = _b === void 0 ? function (n) { return n; } : _b, _c = _a.yScale, yScale = _c === void 0 ? function (n) { return n; } : _c, _d = _a.source, source = _d === void 0 ? [] : _d, _e = _a.target, target = _e === void 0 ? [] : _e, _f = _a.offset, offset = _f === void 0 ? 0 : _f, _g = _a.isSelfLoop, isSelfLoop = _g === void 0 ? false : _g;
|
|
40
27
|
if (isSelfLoop) {
|
|
41
28
|
var sourceX = source[0], sourceY = source[1];
|
|
42
29
|
var targetX = target[0], targetY = target[1];
|
|
43
30
|
return [
|
|
44
31
|
sourceX,
|
|
45
|
-
sourceY +
|
|
32
|
+
sourceY + offset,
|
|
46
33
|
sourceX,
|
|
47
|
-
sourceY + 3 *
|
|
48
|
-
targetX + 5 *
|
|
34
|
+
sourceY + 3 * offset,
|
|
35
|
+
targetX + 5 * offset,
|
|
49
36
|
targetY,
|
|
50
|
-
targetX +
|
|
37
|
+
targetX + offset,
|
|
51
38
|
targetY,
|
|
52
39
|
];
|
|
53
40
|
}
|
|
54
|
-
|
|
41
|
+
// graphviz pos format is in format 'endpoint,startpoint,triple(cp1,cp2,end),...triple...'
|
|
42
|
+
var scalePoint = function (point) { return [xScale(point[0]), yScale(point[1])]; };
|
|
43
|
+
var strAsNumArray = function (string) {
|
|
44
|
+
return string
|
|
45
|
+
.replace('e,', '')
|
|
46
|
+
.split(',')
|
|
47
|
+
.map(function (str) { return Number(str); });
|
|
48
|
+
};
|
|
49
|
+
var getLastPointWithOffset = function (target, last, offset) {
|
|
50
|
+
var targetX = target[0], targetY = target[1];
|
|
51
|
+
var lastX = last[0], lastY = last[1];
|
|
52
|
+
var diffX = targetX - lastX;
|
|
53
|
+
var diffY = targetY - lastY;
|
|
54
|
+
var diffZ = Math.hypot(diffX, diffY);
|
|
55
|
+
var offsetX = (diffX * offset) / diffZ;
|
|
56
|
+
var offsetY = (diffY * offset) / diffZ;
|
|
57
|
+
return [targetX - offsetX, targetY - offsetY];
|
|
58
|
+
};
|
|
59
|
+
var points = pos.split(' ').map(function (str) { return strAsNumArray(str); });
|
|
60
|
+
var scaledPoints = points.map(function (point) { return scalePoint(point); });
|
|
61
|
+
var lastPointIndex = scaledPoints.length - 1;
|
|
62
|
+
var lastPointWithOffset = getLastPointWithOffset(target, scaledPoints[lastPointIndex], offset);
|
|
63
|
+
return __spreadArray(__spreadArray([source], scaledPoints.slice(2, points.length - 1), true), [lastPointWithOffset], false).flat();
|
|
64
|
+
};
|
|
65
|
+
var objectAsDotAttributes = function (obj) {
|
|
66
|
+
return Object.entries(obj)
|
|
67
|
+
.map(function (entry) { return "".concat(entry[0], "=\"").concat(entry[1], "\""); })
|
|
68
|
+
.join(' ');
|
|
55
69
|
};
|
|
56
70
|
export var jsonToDot = function (_a) {
|
|
57
|
-
var graph = _a.graph,
|
|
71
|
+
var graph = _a.graph, colorRange = _a.colorRange;
|
|
58
72
|
var nodes = graph.nodes, edges = graph.edges;
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
var cumulatives = nodes.map(function (node) { return node.cumulative; });
|
|
74
|
+
var cumulativesRange = d3.extent(cumulatives).map(function (value) { return Number(value); });
|
|
75
|
+
var colorScale = d3
|
|
76
|
+
.scaleSequentialLog(d3.interpolateBlues)
|
|
77
|
+
.domain(cumulativesRange)
|
|
78
|
+
.range(colorRange);
|
|
79
|
+
var colorOpacityScale = d3.scaleSequentialLog().domain(cumulativesRange).range([0.2, 1]);
|
|
80
|
+
var boxWidthScale = d3
|
|
81
|
+
.scaleLog()
|
|
82
|
+
.domain(cumulativesRange)
|
|
83
|
+
.range([DEFAULT_NODE_HEIGHT, DEFAULT_NODE_HEIGHT + 40]);
|
|
64
84
|
var nodesAsStrings = nodes.map(function (node) {
|
|
65
85
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
66
86
|
var dataAttributes = {
|
|
@@ -68,15 +88,21 @@ export var jsonToDot = function (_a) {
|
|
|
68
88
|
functionName: (_f = (_e = (_d = node.meta) === null || _d === void 0 ? void 0 : _d.function) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : '',
|
|
69
89
|
cumulative: (_g = node.cumulative) !== null && _g !== void 0 ? _g : '',
|
|
70
90
|
root: (node.id === 'root').toString(),
|
|
91
|
+
// TODO: set box width scale to be based on flat value once we have that value available
|
|
92
|
+
width: boxWidthScale(Number(node.cumulative)),
|
|
93
|
+
color: colorScale(Number(node.cumulative)),
|
|
71
94
|
};
|
|
72
95
|
return "\"".concat(node.id, "\" [").concat(objectAsDotAttributes(dataAttributes), "]");
|
|
73
96
|
});
|
|
74
97
|
var edgesAsStrings = edges.map(function (edge) {
|
|
75
98
|
var dataAttributes = {
|
|
76
99
|
cumulative: edge.cumulative,
|
|
100
|
+
color: colorRange[1],
|
|
101
|
+
opacity: colorOpacityScale(Number(edge.cumulative)),
|
|
102
|
+
boxHeight: DEFAULT_NODE_HEIGHT,
|
|
77
103
|
};
|
|
78
104
|
return "\"".concat(edge.source, "\" -> \"").concat(edge.target, "\" [").concat(objectAsDotAttributes(dataAttributes), "]");
|
|
79
105
|
});
|
|
80
|
-
var graphAsDot = "digraph \"callgraph\" {\n rankdir=\"
|
|
106
|
+
var graphAsDot = "digraph \"callgraph\" {\n rankdir=\"BT\"\n overlap=\"prism\"\n ratio=\"1,3\"\n margin=15\n edge [margin=0]\n node [shape=box style=rounded height=".concat(DEFAULT_NODE_HEIGHT, "]\n ").concat(nodesAsStrings.join(' '), "\n ").concat(edgesAsStrings.join(' '), "\n }");
|
|
81
107
|
return graphAsDot;
|
|
82
108
|
};
|
package/dist/IcicleGraph.d.ts
CHANGED
package/dist/ProfileSource.d.ts
CHANGED
package/dist/ProfileView.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { QueryServiceClient, Flamegraph, Top, Callgraph } from '@parca/client';
|
|
1
|
+
import { QueryServiceClient, Flamegraph, Top, Callgraph as CallgraphType } from '@parca/client';
|
|
3
2
|
import { ProfileSource } from './ProfileSource';
|
|
4
3
|
import './ProfileView.styles.css';
|
|
5
4
|
declare type NavigateFunction = (path: string, queryParams: any) => void;
|
|
@@ -15,7 +14,7 @@ export interface TopTableData {
|
|
|
15
14
|
}
|
|
16
15
|
interface CallgraphData {
|
|
17
16
|
loading: boolean;
|
|
18
|
-
data?:
|
|
17
|
+
data?: CallgraphType;
|
|
19
18
|
error?: any;
|
|
20
19
|
}
|
|
21
20
|
export declare type VisualizationType = 'icicle' | 'table' | 'callgraph' | 'both';
|
package/dist/ProfileView.js
CHANGED
|
@@ -23,15 +23,17 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
23
23
|
// See the License for the specific language governing permissions and
|
|
24
24
|
// limitations under the License.
|
|
25
25
|
import { useEffect, useMemo, useState } from 'react';
|
|
26
|
-
import { parseParams } from '@parca/functions';
|
|
26
|
+
import { getNewSpanColor, parseParams } from '@parca/functions';
|
|
27
27
|
import useUIFeatureFlag from '@parca/functions/useUIFeatureFlag';
|
|
28
28
|
import { Button, Card, SearchNodes, useParcaTheme } from '@parca/components';
|
|
29
|
-
import { Callgraph
|
|
29
|
+
import { Callgraph } from './';
|
|
30
30
|
import { useContainerDimensions } from '@parca/dynamicsize';
|
|
31
|
+
import { useAppSelector, selectDarkMode, selectSearchNodeString } from '@parca/store';
|
|
31
32
|
import ProfileShareButton from './components/ProfileShareButton';
|
|
32
33
|
import ProfileIcicleGraph from './ProfileIcicleGraph';
|
|
33
34
|
import TopTable from './TopTable';
|
|
34
35
|
import useDelayedLoader from './useDelayedLoader';
|
|
36
|
+
import { scaleLinear } from 'd3';
|
|
35
37
|
import './ProfileView.styles.css';
|
|
36
38
|
function arrayEquals(a, b) {
|
|
37
39
|
return (Array.isArray(a) &&
|
|
@@ -58,6 +60,8 @@ export var ProfileView = function (_a) {
|
|
|
58
60
|
var _b = useContainerDimensions(), ref = _b.ref, dimensions = _b.dimensions;
|
|
59
61
|
var _c = useState([]), curPath = _c[0], setCurPath = _c[1];
|
|
60
62
|
var currentView = profileVisState.currentView, setCurrentView = profileVisState.setCurrentView;
|
|
63
|
+
var isDarkMode = useAppSelector(selectDarkMode);
|
|
64
|
+
var currentSearchString = useAppSelector(selectSearchNodeString);
|
|
61
65
|
var callgraphEnabled = useUIFeatureFlag('callgraph')[0];
|
|
62
66
|
var loader = useParcaTheme().loader;
|
|
63
67
|
useEffect(function () {
|
|
@@ -102,10 +106,14 @@ export var ProfileView = function (_a) {
|
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
104
108
|
var router = parseParams(window.location.search);
|
|
105
|
-
navigateTo('/', __assign(__assign({}, router), { currentProfileView: view }));
|
|
109
|
+
navigateTo('/', __assign(__assign(__assign({}, router), { currentProfileView: view }), { searchString: currentSearchString }));
|
|
106
110
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
var maxColor = getNewSpanColor(isDarkMode);
|
|
112
|
+
// TODO: fix colors for dark mode
|
|
113
|
+
var minColor = scaleLinear([isDarkMode ? 'black' : 'white', maxColor])(0.3);
|
|
114
|
+
var colorRange = [minColor, maxColor];
|
|
115
|
+
return (_jsx(_Fragment, { children: _jsx("div", __assign({ className: "py-3" }, { children: _jsx(Card, { children: _jsxs(Card.Body, { children: [_jsxs("div", __assign({ className: "flex py-3 w-full" }, { children: [_jsxs("div", __assign({ className: "w-2/5 flex space-x-4" }, { children: [_jsxs("div", __assign({ className: "flex space-x-1" }, { children: [profileSource != null && queryClient != null ? (_jsx(ProfileShareButton, { queryRequest: profileSource.QueryRequest(), queryClient: queryClient })) : null, _jsx(Button, __assign({ color: "neutral", onClick: function (e) {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
onDownloadPProf();
|
|
118
|
+
} }, { children: "Download pprof" }))] })), _jsx(SearchNodes, {})] })), _jsxs("div", __assign({ className: "flex ml-auto" }, { children: [_jsx("div", __assign({ className: "mr-3" }, { children: _jsx(Button, __assign({ color: "neutral", onClick: resetIcicleGraph, disabled: curPath.length === 0, className: "whitespace-nowrap text-ellipsis" }, { children: "Reset View" })) })), callgraphEnabled ? (_jsx("div", __assign({ className: "mr-3" }, { children: _jsx(Button, __assign({ variant: "".concat(currentView === 'callgraph' ? 'primary' : 'neutral'), onClick: function () { return switchProfileView('callgraph'); }, className: "whitespace-nowrap text-ellipsis" }, { children: "Callgraph" })) }))) : null, _jsx(Button, __assign({ variant: "".concat(currentView === 'table' ? 'primary' : 'neutral'), className: "items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons", onClick: function () { return switchProfileView('table'); } }, { children: "Table" })), _jsx(Button, __assign({ variant: "".concat(currentView === 'both' ? 'primary' : 'neutral'), className: "items-center rounded-tl-none rounded-tr-none rounded-bl-none rounded-br-none border-l-0 border-r-0 w-auto px-8 whitespace-nowrap no-outline-on-buttons text-ellipsis", onClick: function () { return switchProfileView('both'); } }, { children: "Both" })), _jsx(Button, __assign({ variant: "".concat(currentView === 'icicle' ? 'primary' : 'neutral'), className: "items-center rounded-tl-none rounded-bl-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons", onClick: function () { return switchProfileView('icicle'); } }, { children: "Icicle Graph" }))] }))] })), _jsxs("div", __assign({ ref: ref, className: "flex space-x-4 justify-between w-full" }, { children: [currentView === 'icicle' && (flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.data) != null && (_jsx("div", __assign({ className: "w-full" }, { children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit }) }))), currentView === 'callgraph' && (callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.data) != null && (_jsx("div", __assign({ className: "w-full" }, { children: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) !== undefined && (_jsx(Callgraph, { graph: callgraphData.data, sampleUnit: sampleUnit, width: dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, colorRange: colorRange })) }))), currentView === 'table' && topTableData != null && (_jsx("div", __assign({ className: "w-full" }, { children: _jsx(TopTable, { data: topTableData.data, sampleUnit: sampleUnit }) }))), currentView === 'both' && (_jsxs(_Fragment, { children: [_jsx("div", __assign({ className: "w-1/2" }, { children: _jsx(TopTable, { data: topTableData === null || topTableData === void 0 ? void 0 : topTableData.data, sampleUnit: sampleUnit }) })), _jsx("div", __assign({ className: "w-1/2" }, { children: flamegraphData != null && (_jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit })) }))] }))] }))] }) }) })) }));
|
|
111
119
|
};
|
package/dist/TopTable.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export * from './ProfileViewWithData';
|
|
|
12
12
|
export * from './utils';
|
|
13
13
|
export * from './ProfileTypeSelector';
|
|
14
14
|
export type { CallgraphProps };
|
|
15
|
-
declare const Callgraph: React.LazyExoticComponent<({ graph, sampleUnit, width }: CallgraphProps) => JSX.Element>;
|
|
15
|
+
declare const Callgraph: React.LazyExoticComponent<({ graph, sampleUnit, width, colorRange }: CallgraphProps) => JSX.Element>;
|
|
16
16
|
export { Callgraph, ProfileExplorer, ProfileTypeSelector };
|
|
17
17
|
interface GrafanaParcaDataPayload {
|
|
18
18
|
flamegraphData: FlamegraphData;
|