@parca/profile 0.16.107 → 0.16.109
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/GraphTooltip/index.js +2 -2
- package/dist/MetricsGraph/index.js +2 -3
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +1 -1
- package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +1 -1
- package/dist/ProfileExplorer/ProfileExplorerSingle.js +2 -1
- package/dist/ProfileExplorer/index.d.ts +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.d.ts +8 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.js +75 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.d.ts +47 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.js +98 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts +14 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/index.js +78 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.d.ts +14 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.js +60 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.d.ts +7 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.js +32 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.d.ts +5 -0
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.js +59 -0
- package/dist/ProfileIcicleGraph/index.d.ts +3 -1
- package/dist/ProfileIcicleGraph/index.js +6 -5
- package/dist/ProfileView/FilterByFunctionButton.d.ts +1 -1
- package/dist/ProfileView/FilterByFunctionButton.js +1 -2
- package/dist/ProfileView/ViewSelector.d.ts +1 -1
- package/dist/ProfileView/ViewSelector.js +1 -14
- package/dist/ProfileView/index.js +4 -4
- package/dist/ProfileViewWithData.d.ts +1 -1
- package/dist/ProfileViewWithData.js +2 -2
- package/dist/TopTable/index.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +6 -5
- package/src/GraphTooltip/index.tsx +2 -2
- package/src/MetricsGraph/index.tsx +2 -3
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +1 -1
- package/src/ProfileExplorer/ProfileExplorerSingle.tsx +4 -3
- package/src/ProfileExplorer/index.tsx +1 -1
- package/src/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.tsx +93 -0
- package/src/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.tsx +262 -0
- package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +166 -0
- package/src/ProfileIcicleGraph/IcicleGraph/useColoredGraph.ts +107 -0
- package/src/ProfileIcicleGraph/IcicleGraph/useNodeColor.ts +42 -0
- package/src/ProfileIcicleGraph/IcicleGraph/utils.ts +94 -0
- package/src/ProfileIcicleGraph/index.tsx +25 -20
- package/src/ProfileView/FilterByFunctionButton.tsx +2 -2
- package/src/ProfileView/ViewSelector.tsx +2 -2
- package/src/ProfileView/index.tsx +6 -5
- package/src/ProfileViewWithData.tsx +3 -2
- package/src/TopTable/index.tsx +1 -1
- package/src/index.tsx +1 -1
- package/dist/IcicleGraph.d.ts +0 -45
- package/dist/IcicleGraph.js +0 -180
- package/src/IcicleGraph.tsx +0 -481
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
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.109 (2023-02-08)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.108](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.107...@parca/profile@0.16.108) (2023-02-08)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## 0.16.107 (2023-02-07)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -37,8 +37,8 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
|
37
37
|
import { useState, useEffect } from 'react';
|
|
38
38
|
import { usePopper } from 'react-popper';
|
|
39
39
|
import { getLastItem, valueFormatter } from '@parca/functions';
|
|
40
|
-
import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
|
|
41
40
|
import { hexifyAddress, truncateString } from '../';
|
|
41
|
+
import { useKeyDown } from '@parca/components';
|
|
42
42
|
var virtualElement = {
|
|
43
43
|
getBoundingClientRect: function () {
|
|
44
44
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -173,7 +173,7 @@ var GraphTooltip = function (_a) {
|
|
|
173
173
|
],
|
|
174
174
|
}), styles = _e.styles, attributes = _e.attributes, popperProps = __rest(_e, ["styles", "attributes"]);
|
|
175
175
|
var update = popperProps.update;
|
|
176
|
-
var isShiftDown =
|
|
176
|
+
var isShiftDown = useKeyDown().isShiftDown;
|
|
177
177
|
useEffect(function () {
|
|
178
178
|
if (contextElement != null) {
|
|
179
179
|
if (isShiftDown)
|
|
@@ -42,9 +42,8 @@ import { cutToMaxStringLength } from '@parca/functions/string';
|
|
|
42
42
|
import throttle from 'lodash.throttle';
|
|
43
43
|
import { usePopper } from 'react-popper';
|
|
44
44
|
import { valueFormatter, formatDate, sanitizeHighlightedValues } from '@parca/functions';
|
|
45
|
-
import { DateTimeRange } from '@parca/components';
|
|
45
|
+
import { DateTimeRange, useKeyDown } from '@parca/components';
|
|
46
46
|
import { useContainerDimensions } from '@parca/dynamicsize';
|
|
47
|
-
import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
|
|
48
47
|
import MetricsSeries from '../MetricsSeries';
|
|
49
48
|
import MetricsCircle from '../MetricsCircle';
|
|
50
49
|
var MetricsGraph = function (_a) {
|
|
@@ -135,7 +134,7 @@ export var RawMetricsGraph = function (_a) {
|
|
|
135
134
|
var _d = useState(-1), relPos = _d[0], setRelPos = _d[1];
|
|
136
135
|
var _e = useState([0, 0]), pos = _e[0], setPos = _e[1];
|
|
137
136
|
var metricPointRef = useRef(null);
|
|
138
|
-
var isShiftDown =
|
|
137
|
+
var isShiftDown = useKeyDown().isShiftDown;
|
|
139
138
|
var time = parseFloat(profile === null || profile === void 0 ? void 0 : profile.HistoryParams().time);
|
|
140
139
|
if (width === undefined || width == null) {
|
|
141
140
|
width = 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { NavigateFunction } from '@parca/functions';
|
|
2
|
+
import type { NavigateFunction } from '@parca/functions';
|
|
3
3
|
import { QueryServiceClient } from '@parca/client';
|
|
4
4
|
import { ProfileSelection } from '..';
|
|
5
5
|
import { QuerySelection } from '../ProfileSelector';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { NavigateFunction } from '@parca/functions';
|
|
2
|
+
import type { NavigateFunction } from '@parca/functions';
|
|
3
3
|
import { QueryServiceClient } from '@parca/client';
|
|
4
4
|
import { ProfileSelection } from '..';
|
|
5
5
|
import { QuerySelection } from '../ProfileSelector';
|
|
@@ -12,8 +12,9 @@ var __assign = (this && this.__assign) || function () {
|
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { ProfileViewWithData } from '..';
|
|
14
14
|
import ProfileSelector from '../ProfileSelector';
|
|
15
|
+
import { KeyDownProvider } from '@parca/components';
|
|
15
16
|
var ProfileExplorerSingle = function (_a) {
|
|
16
17
|
var queryClient = _a.queryClient, query = _a.query, selectQuery = _a.selectQuery, selectProfile = _a.selectProfile, profile = _a.profile, compareProfile = _a.compareProfile, navigateTo = _a.navigateTo;
|
|
17
|
-
return (_jsxs(
|
|
18
|
+
return (_jsxs(KeyDownProvider, { children: [_jsx("div", __assign({ className: "grid grid-cols-1" }, { children: _jsx("div", { children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: query, selectQuery: selectQuery, selectProfile: selectProfile, closeProfile: function () { }, profileSelection: profile, comparing: false, onCompareProfile: compareProfile, enforcedProfileName: '' }) }) })), _jsx("div", __assign({ className: "grid grid-cols-1" }, { children: _jsx("div", { children: profile != null ? (_jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: profile.ProfileSource(), navigateTo: navigateTo })) : (_jsx(_Fragment, {})) }) }))] }));
|
|
18
19
|
};
|
|
19
20
|
export default ProfileExplorerSingle;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { QueryServiceClient } from '@parca/client';
|
|
3
|
-
import { NavigateFunction } from '@parca/functions';
|
|
3
|
+
import type { NavigateFunction } from '@parca/functions';
|
|
4
4
|
interface ProfileExplorerProps {
|
|
5
5
|
queryClient: QueryServiceClient;
|
|
6
6
|
queryParams: any;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { NavigateFunction } from '@parca/functions';
|
|
3
|
+
interface Props {
|
|
4
|
+
navigateTo?: NavigateFunction;
|
|
5
|
+
compareMode?: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare const ColorStackLegend: ({ navigateTo, compareMode }: Props) => JSX.Element;
|
|
8
|
+
export default ColorStackLegend;
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
// Copyright 2022 The Parca Authors
|
|
14
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
15
|
+
// you may not use this file except in compliance with the License.
|
|
16
|
+
// You may obtain a copy of the License at
|
|
17
|
+
//
|
|
18
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
19
|
+
//
|
|
20
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
// See the License for the specific language governing permissions and
|
|
24
|
+
// limitations under the License.
|
|
25
|
+
import cx from 'classnames';
|
|
26
|
+
import { Icon } from '@iconify/react';
|
|
27
|
+
import useUserPreference, { USER_PREFERENCES } from '@parca/functions/useUserPreference';
|
|
28
|
+
import { EVERYTHING_ELSE, selectStackColors, useAppSelector } from '@parca/store';
|
|
29
|
+
import { useMemo } from 'react';
|
|
30
|
+
import { useURLState } from '@parca/components';
|
|
31
|
+
var ColorStackLegend = function (_a) {
|
|
32
|
+
var navigateTo = _a.navigateTo, _b = _a.compareMode, compareMode = _b === void 0 ? false : _b;
|
|
33
|
+
var colorProfileName = useUserPreference(USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key)[0];
|
|
34
|
+
var _c = useURLState({ param: 'search_string', navigateTo: navigateTo }), currentSearchString = _c[0], setSearchString = _c[1];
|
|
35
|
+
var stackColors = useAppSelector(selectStackColors);
|
|
36
|
+
var stackColorArray = useMemo(function () {
|
|
37
|
+
return Object.entries(stackColors).sort(function (_a, _b) {
|
|
38
|
+
var _c;
|
|
39
|
+
var featureA = _a[0];
|
|
40
|
+
var featureB = _b[0];
|
|
41
|
+
if (featureA === EVERYTHING_ELSE) {
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
if (featureB === EVERYTHING_ELSE) {
|
|
45
|
+
return -1;
|
|
46
|
+
}
|
|
47
|
+
return (_c = featureA === null || featureA === void 0 ? void 0 : featureA.localeCompare(featureB !== null && featureB !== void 0 ? featureB : '')) !== null && _c !== void 0 ? _c : 0;
|
|
48
|
+
});
|
|
49
|
+
}, [stackColors]);
|
|
50
|
+
if (colorProfileName === 'default' || compareMode) {
|
|
51
|
+
return _jsx(_Fragment, {});
|
|
52
|
+
}
|
|
53
|
+
return (_jsx("div", __assign({ className: "flex flex-wrap gap-2 my-6 justify-center" }, { children: stackColorArray.map(function (_a) {
|
|
54
|
+
var feature = _a[0], color = _a[1];
|
|
55
|
+
var filteringAllowed = feature !== EVERYTHING_ELSE;
|
|
56
|
+
var isHighlighted = currentSearchString === feature;
|
|
57
|
+
return (_jsxs("div", __assign({ className: cx('flex gap-1 items-center text-ellipsis p-1 justify-between pr-2', {
|
|
58
|
+
'cursor-pointer': filteringAllowed,
|
|
59
|
+
'bg-gray-200 dark:bg-gray-800': isHighlighted,
|
|
60
|
+
}), onClick: function () {
|
|
61
|
+
if (!filteringAllowed) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (isHighlighted) {
|
|
65
|
+
setSearchString('');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setSearchString(feature);
|
|
69
|
+
} }, { children: [_jsxs("div", __assign({ className: "flex items-center" }, { children: [_jsx("div", { className: "w-4 h-4 mr-1 inline-block", style: { backgroundColor: color } }), _jsx("span", __assign({ className: "text-sm" }, { children: feature }))] })), isHighlighted ? (_jsx(Icon, { icon: "radix-icons:cross-circled", onClick: function (e) {
|
|
70
|
+
setSearchString('');
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
} })) : null] }), feature));
|
|
73
|
+
}) })));
|
|
74
|
+
};
|
|
75
|
+
export default ColorStackLegend;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Mapping, Function as ParcaFunction, Location } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
|
|
3
|
+
import { FlamegraphNode, FlamegraphRootNode } from '@parca/client';
|
|
4
|
+
export declare const RowHeight = 26;
|
|
5
|
+
interface IcicleGraphNodesProps {
|
|
6
|
+
data: FlamegraphNode[];
|
|
7
|
+
strings: string[];
|
|
8
|
+
mappings: Mapping[];
|
|
9
|
+
locations: Location[];
|
|
10
|
+
functions: ParcaFunction[];
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
total: number;
|
|
14
|
+
totalWidth: number;
|
|
15
|
+
level: number;
|
|
16
|
+
curPath: string[];
|
|
17
|
+
setCurPath: (path: string[]) => void;
|
|
18
|
+
setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
|
|
19
|
+
path: string[];
|
|
20
|
+
xScale: (value: number) => number;
|
|
21
|
+
searchString?: string;
|
|
22
|
+
compareMode: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare const IcicleGraphNodes: React.NamedExoticComponent<IcicleGraphNodesProps>;
|
|
25
|
+
interface IcicleNodeProps {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
height: number;
|
|
29
|
+
totalWidth: number;
|
|
30
|
+
curPath: string[];
|
|
31
|
+
level: number;
|
|
32
|
+
data: FlamegraphNode;
|
|
33
|
+
strings: string[];
|
|
34
|
+
mappings: Mapping[];
|
|
35
|
+
locations: Location[];
|
|
36
|
+
functions: ParcaFunction[];
|
|
37
|
+
path: string[];
|
|
38
|
+
total: number;
|
|
39
|
+
setCurPath: (path: string[]) => void;
|
|
40
|
+
setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
|
|
41
|
+
xScale: (value: number) => number;
|
|
42
|
+
isRoot?: boolean;
|
|
43
|
+
searchString?: string;
|
|
44
|
+
compareMode: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare const IcicleNode: React.NamedExoticComponent<IcicleNodeProps>;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
// Copyright 2022 The Parca Authors
|
|
14
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
15
|
+
// you may not use this file except in compliance with the License.
|
|
16
|
+
// You may obtain a copy of the License at
|
|
17
|
+
//
|
|
18
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
19
|
+
//
|
|
20
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
// See the License for the specific language governing permissions and
|
|
24
|
+
// limitations under the License.
|
|
25
|
+
import React, { useMemo } from 'react';
|
|
26
|
+
import cx from 'classnames';
|
|
27
|
+
import { scaleLinear } from 'd3-scale';
|
|
28
|
+
import { isSearchMatch } from '@parca/functions';
|
|
29
|
+
import { nodeLabel } from './utils';
|
|
30
|
+
import useNodeColor from './useNodeColor';
|
|
31
|
+
import { useKeyDown } from '@parca/components';
|
|
32
|
+
export var RowHeight = 26;
|
|
33
|
+
export var IcicleGraphNodes = React.memo(function IcicleGraphNodes(_a) {
|
|
34
|
+
var data = _a.data, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions, x = _a.x, y = _a.y, xScale = _a.xScale, total = _a.total, totalWidth = _a.totalWidth, level = _a.level, setHoveringNode = _a.setHoveringNode, path = _a.path, setCurPath = _a.setCurPath, curPath = _a.curPath, searchString = _a.searchString, compareMode = _a.compareMode;
|
|
35
|
+
var nodes = curPath.length === 0
|
|
36
|
+
? data
|
|
37
|
+
: data.filter(function (d) { return d != null && curPath[0] === nodeLabel(d, strings, mappings, locations, functions); });
|
|
38
|
+
return (_jsx("g", __assign({ transform: "translate(".concat(x, ", ").concat(y, ")") }, { children: nodes.map(function (d, i) {
|
|
39
|
+
var start = nodes.slice(0, i).reduce(function (sum, d) { return sum + parseFloat(d.cumulative); }, 0);
|
|
40
|
+
var xStart = xScale(start);
|
|
41
|
+
return (_jsx(IcicleNode, { x: xStart, y: 0, totalWidth: totalWidth, height: RowHeight, path: path, setCurPath: setCurPath, setHoveringNode: setHoveringNode, level: level, curPath: curPath, data: d, strings: strings, mappings: mappings, locations: locations, functions: functions, total: total, xScale: xScale, searchString: searchString, compareMode: compareMode }, "node-".concat(level, "-").concat(i)));
|
|
42
|
+
}) })));
|
|
43
|
+
});
|
|
44
|
+
var icicleRectStyles = {
|
|
45
|
+
cursor: 'pointer',
|
|
46
|
+
transition: 'opacity .15s linear',
|
|
47
|
+
};
|
|
48
|
+
var fadedIcicleRectStyles = {
|
|
49
|
+
cursor: 'pointer',
|
|
50
|
+
transition: 'opacity .15s linear',
|
|
51
|
+
opacity: '0.5',
|
|
52
|
+
};
|
|
53
|
+
export var IcicleNode = React.memo(function IcicleNode(_a) {
|
|
54
|
+
var x = _a.x, y = _a.y, height = _a.height, setCurPath = _a.setCurPath, setHoveringNode = _a.setHoveringNode, curPath = _a.curPath, level = _a.level, path = _a.path, data = _a.data, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions, total = _a.total, totalWidth = _a.totalWidth, xScale = _a.xScale, _b = _a.isRoot, isRoot = _b === void 0 ? false : _b, searchString = _a.searchString, compareMode = _a.compareMode;
|
|
55
|
+
var isShiftDown = useKeyDown().isShiftDown;
|
|
56
|
+
var colorResult = useNodeColor({ data: data, compareMode: compareMode });
|
|
57
|
+
var name = useMemo(function () {
|
|
58
|
+
return isRoot ? 'root' : nodeLabel(data, strings, mappings, locations, functions);
|
|
59
|
+
}, [data, strings, mappings, locations, functions, isRoot]);
|
|
60
|
+
var nextPath = path.concat([name]);
|
|
61
|
+
var isFaded = curPath.length > 0 && name !== curPath[curPath.length - 1];
|
|
62
|
+
var styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
|
|
63
|
+
var nextLevel = level + 1;
|
|
64
|
+
var cumulative = parseFloat(data.cumulative);
|
|
65
|
+
var nextCurPath = curPath.length === 0 ? [] : curPath.slice(1);
|
|
66
|
+
var newXScale = nextCurPath.length === 0 && curPath.length === 1
|
|
67
|
+
? scaleLinear().domain([0, cumulative]).range([0, totalWidth])
|
|
68
|
+
: xScale;
|
|
69
|
+
var width = nextCurPath.length > 0 || (nextCurPath.length === 0 && curPath.length === 1)
|
|
70
|
+
? totalWidth
|
|
71
|
+
: xScale(cumulative);
|
|
72
|
+
var _c = useMemo(function () {
|
|
73
|
+
if (searchString === undefined || searchString === '') {
|
|
74
|
+
return { isHighlightEnabled: false };
|
|
75
|
+
}
|
|
76
|
+
return { isHighlightEnabled: true, isHighlighted: isSearchMatch(searchString, name) };
|
|
77
|
+
}, [searchString, name]), _d = _c.isHighlightEnabled, isHighlightEnabled = _d === void 0 ? false : _d, _e = _c.isHighlighted, isHighlighted = _e === void 0 ? false : _e;
|
|
78
|
+
if (width <= 1) {
|
|
79
|
+
return _jsx(_Fragment, { children: null });
|
|
80
|
+
}
|
|
81
|
+
var onMouseEnter = function () {
|
|
82
|
+
if (isShiftDown)
|
|
83
|
+
return;
|
|
84
|
+
setHoveringNode(data);
|
|
85
|
+
};
|
|
86
|
+
var onMouseLeave = function () {
|
|
87
|
+
if (isShiftDown)
|
|
88
|
+
return;
|
|
89
|
+
setHoveringNode(undefined);
|
|
90
|
+
};
|
|
91
|
+
return (_jsxs(_Fragment, { children: [_jsxs("g", __assign({ transform: "translate(".concat(x + 1, ", ").concat(y + 1, ")"), style: styles, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onClick: function () {
|
|
92
|
+
setCurPath(nextPath);
|
|
93
|
+
} }, { children: [_jsx("rect", { x: 0, y: 0, width: width - 1, height: height - 1, style: {
|
|
94
|
+
fill: colorResult,
|
|
95
|
+
}, className: cx({
|
|
96
|
+
'opacity-50': isHighlightEnabled && !isHighlighted,
|
|
97
|
+
}) }), width > 5 && (_jsx("svg", __assign({ width: width - 5, height: height }, { children: _jsx("text", __assign({ x: 5, y: 15, style: { fontSize: '12px' } }, { children: name })) })))] })), data.children !== undefined && data.children.length > 0 && (_jsx(IcicleGraphNodes, { data: data.children, strings: strings, mappings: mappings, locations: locations, functions: functions, x: x, y: RowHeight, xScale: newXScale, total: total, totalWidth: totalWidth, level: nextLevel, setHoveringNode: setHoveringNode, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath, searchString: searchString, compareMode: compareMode }))] }));
|
|
98
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Flamegraph } from '@parca/client';
|
|
3
|
+
import type { NavigateFunction } from '@parca/functions';
|
|
4
|
+
interface IcicleGraphProps {
|
|
5
|
+
graph: Flamegraph;
|
|
6
|
+
sampleUnit: string;
|
|
7
|
+
width?: number;
|
|
8
|
+
curPath: string[];
|
|
9
|
+
setCurPath: (path: string[]) => void;
|
|
10
|
+
navigateTo?: NavigateFunction;
|
|
11
|
+
isTrimmed?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const IcicleGraph: React.NamedExoticComponent<IcicleGraphProps>;
|
|
14
|
+
export default IcicleGraph;
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
// Copyright 2022 The Parca Authors
|
|
14
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
15
|
+
// you may not use this file except in compliance with the License.
|
|
16
|
+
// You may obtain a copy of the License at
|
|
17
|
+
//
|
|
18
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
19
|
+
//
|
|
20
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
// See the License for the specific language governing permissions and
|
|
24
|
+
// limitations under the License.
|
|
25
|
+
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
26
|
+
import cx from 'classnames';
|
|
27
|
+
import { throttle } from 'lodash';
|
|
28
|
+
import { pointer } from 'd3-selection';
|
|
29
|
+
import { scaleLinear } from 'd3-scale';
|
|
30
|
+
import GraphTooltip from '../../GraphTooltip';
|
|
31
|
+
import { Button, useURLState } from '@parca/components';
|
|
32
|
+
import { IcicleNode, RowHeight } from './IcicleGraphNodes';
|
|
33
|
+
import useColoredGraph from './useColoredGraph';
|
|
34
|
+
import { selectQueryParam } from '@parca/functions';
|
|
35
|
+
import ColorStackLegend from './ColorStackLegend';
|
|
36
|
+
export var IcicleGraph = memo(function IcicleGraph(_a) {
|
|
37
|
+
var _b;
|
|
38
|
+
var graph = _a.graph, width = _a.width, setCurPath = _a.setCurPath, curPath = _a.curPath, sampleUnit = _a.sampleUnit, navigateTo = _a.navigateTo, _c = _a.isTrimmed, isTrimmed = _c === void 0 ? false : _c;
|
|
39
|
+
var _d = useState(), hoveringNode = _d[0], setHoveringNode = _d[1];
|
|
40
|
+
var _e = useState([0, 0]), pos = _e[0], setPos = _e[1];
|
|
41
|
+
var _f = useState(0), height = _f[0], setHeight = _f[1];
|
|
42
|
+
var svg = useRef(null);
|
|
43
|
+
var ref = useRef(null);
|
|
44
|
+
var rawDashboardItems = useURLState({
|
|
45
|
+
param: 'dashboard_items',
|
|
46
|
+
})[0];
|
|
47
|
+
var dashboardItems = rawDashboardItems;
|
|
48
|
+
var coloredGraph = useColoredGraph(graph);
|
|
49
|
+
var currentSearchString = (_b = selectQueryParam('search_string')) !== null && _b !== void 0 ? _b : '';
|
|
50
|
+
var compareMode = selectQueryParam('compare_a') === 'true' && selectQueryParam('compare_b') === 'true';
|
|
51
|
+
useEffect(function () {
|
|
52
|
+
if (ref.current != null) {
|
|
53
|
+
setHeight(ref === null || ref === void 0 ? void 0 : ref.current.getBoundingClientRect().height);
|
|
54
|
+
}
|
|
55
|
+
}, [width, coloredGraph]);
|
|
56
|
+
var total = useMemo(function () { return parseFloat(coloredGraph.total); }, [coloredGraph.total]);
|
|
57
|
+
var xScale = useMemo(function () {
|
|
58
|
+
if (width === undefined) {
|
|
59
|
+
return function () { return 0; };
|
|
60
|
+
}
|
|
61
|
+
return scaleLinear().domain([0, total]).range([0, width]);
|
|
62
|
+
}, [total, width]);
|
|
63
|
+
if (coloredGraph.root === undefined || width === undefined) {
|
|
64
|
+
return _jsx(_Fragment, {});
|
|
65
|
+
}
|
|
66
|
+
var throttledSetPos = throttle(setPos, 20);
|
|
67
|
+
var onMouseMove = function (e) {
|
|
68
|
+
// X/Y coordinate array relative to svg
|
|
69
|
+
var rel = pointer(e);
|
|
70
|
+
throttledSetPos([rel[0], rel[1]]);
|
|
71
|
+
};
|
|
72
|
+
return (_jsxs("div", __assign({ onMouseLeave: function () { return setHoveringNode(undefined); } }, { children: [_jsx(ColorStackLegend, { navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltip, { unit: sampleUnit, total: total, x: pos[0], y: pos[1], hoveringNode: hoveringNode, contextElement: svg.current, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function }), _jsx("div", __assign({ className: cx(dashboardItems.length > 1
|
|
73
|
+
? "".concat(isTrimmed ? 'top-[-54px]' : 'top-[-70px]', " left-[25px]")
|
|
74
|
+
: isTrimmed
|
|
75
|
+
? 'top-[-54px]'
|
|
76
|
+
: 'top-[-69px]', 'flex justify-start absolute ') }, { children: _jsx(Button, __assign({ color: "neutral", onClick: function () { return setCurPath([]); }, disabled: curPath.length === 0, className: "w-auto", variant: "neutral" }, { children: "Reset View" })) })), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, onMouseMove: onMouseMove, preserveAspectRatio: "xMinYMid", ref: svg }, { children: _jsx("g", __assign({ ref: ref }, { children: _jsx("g", __assign({ transform: 'translate(0, 0)' }, { children: _jsx(IcicleNode, { x: 0, y: 0, totalWidth: width, height: RowHeight, setCurPath: setCurPath, setHoveringNode: setHoveringNode, curPath: curPath, data: coloredGraph.root, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function, total: total, xScale: xScale, path: [], level: 0, isRoot: true, searchString: currentSearchString, compareMode: compareMode }) })) })) }))] })));
|
|
77
|
+
});
|
|
78
|
+
export default IcicleGraph;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Flamegraph, FlamegraphNode, FlamegraphRootNode } from '@parca/client';
|
|
2
|
+
export interface ColoredFlamegraphNode extends FlamegraphNode {
|
|
3
|
+
feature?: string;
|
|
4
|
+
children: ColoredFlamegraphNode[];
|
|
5
|
+
}
|
|
6
|
+
export interface ColoredFlamgraphRootNode extends FlamegraphRootNode {
|
|
7
|
+
feature?: string;
|
|
8
|
+
children: ColoredFlamegraphNode[];
|
|
9
|
+
}
|
|
10
|
+
export interface ColoredFlamegraph extends Flamegraph {
|
|
11
|
+
root: ColoredFlamgraphRootNode;
|
|
12
|
+
}
|
|
13
|
+
declare const useColoredGraph: (graph: Flamegraph) => ColoredFlamegraph;
|
|
14
|
+
export default useColoredGraph;
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
var __assign = (this && this.__assign) || function () {
|
|
14
|
+
__assign = Object.assign || function(t) {
|
|
15
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
16
|
+
s = arguments[i];
|
|
17
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
18
|
+
t[p] = s[p];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
22
|
+
return __assign.apply(this, arguments);
|
|
23
|
+
};
|
|
24
|
+
import useUserPreference, { USER_PREFERENCES } from '@parca/functions/useUserPreference';
|
|
25
|
+
import { setFeatures, useAppDispatch, useAppSelector, selectDarkMode } from '@parca/store';
|
|
26
|
+
import { useEffect, useMemo } from 'react';
|
|
27
|
+
import { extractFeature } from './utils';
|
|
28
|
+
var colorNodes = function (nodes, strings, mappings, locations, functions, features) {
|
|
29
|
+
if (nodes === undefined) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return nodes.map(function (node) {
|
|
33
|
+
var coloredNode = __assign({}, node);
|
|
34
|
+
if (node.children != null) {
|
|
35
|
+
coloredNode.children = colorNodes(node.children, strings, mappings, locations, functions, features);
|
|
36
|
+
}
|
|
37
|
+
coloredNode.feature = extractFeature(node, mappings, locations, strings, functions);
|
|
38
|
+
features[coloredNode.feature] = true;
|
|
39
|
+
return coloredNode;
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
var useColoredGraph = function (graph) {
|
|
43
|
+
var dispatch = useAppDispatch();
|
|
44
|
+
var colorProfile = useUserPreference(USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key)[0];
|
|
45
|
+
var isDarkMode = useAppSelector(selectDarkMode);
|
|
46
|
+
var _a = useMemo(function () {
|
|
47
|
+
var _a;
|
|
48
|
+
if (graph.root == null) {
|
|
49
|
+
return [graph, []];
|
|
50
|
+
}
|
|
51
|
+
var features = {};
|
|
52
|
+
var coloredGraph = __assign(__assign({}, graph), { root: __assign(__assign({}, graph.root), { children: colorNodes((_a = graph.root.children) !== null && _a !== void 0 ? _a : [], graph.stringTable, graph.mapping, graph.locations, graph.function, features) }) });
|
|
53
|
+
return [coloredGraph, Object.keys(features)];
|
|
54
|
+
}, [graph]), coloredGraph = _a[0], features = _a[1];
|
|
55
|
+
useEffect(function () {
|
|
56
|
+
dispatch(setFeatures({ features: features, colorProfileName: colorProfile, isDarkMode: isDarkMode }));
|
|
57
|
+
}, [features, colorProfile, dispatch, isDarkMode]);
|
|
58
|
+
return coloredGraph;
|
|
59
|
+
};
|
|
60
|
+
export default useColoredGraph;
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
import { diffColor } from '@parca/functions';
|
|
14
|
+
import { EVERYTHING_ELSE, selectDarkMode, selectStackColors, useAppSelector } from '@parca/store';
|
|
15
|
+
import { useMemo } from 'react';
|
|
16
|
+
var useNodeColor = function (_a) {
|
|
17
|
+
var data = _a.data, compareMode = _a.compareMode;
|
|
18
|
+
var colors = useAppSelector(selectStackColors);
|
|
19
|
+
var isDarkMode = useAppSelector(selectDarkMode);
|
|
20
|
+
var color = useMemo(function () {
|
|
21
|
+
var _a;
|
|
22
|
+
if (compareMode) {
|
|
23
|
+
var diff = parseFloat(data.diff);
|
|
24
|
+
var cumulative = parseFloat(data.cumulative);
|
|
25
|
+
return diffColor(diff, cumulative, isDarkMode);
|
|
26
|
+
}
|
|
27
|
+
var color = colors[(_a = data.feature) !== null && _a !== void 0 ? _a : EVERYTHING_ELSE];
|
|
28
|
+
return color;
|
|
29
|
+
}, [data, colors, isDarkMode, compareMode]);
|
|
30
|
+
return color;
|
|
31
|
+
};
|
|
32
|
+
export default useNodeColor;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FlamegraphNode } from '@parca/client';
|
|
2
|
+
import { Mapping, Function as ParcaFunction, Location } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
|
|
3
|
+
export declare const getBinaryName: (node: FlamegraphNode, mappings: Mapping[], locations: Location[], strings: string[]) => string | undefined;
|
|
4
|
+
export declare function nodeLabel(node: FlamegraphNode, strings: string[], mappings: Mapping[], locations: Location[], functions: ParcaFunction[]): string;
|
|
5
|
+
export declare const extractFeature: (data: FlamegraphNode, mappings: Mapping[], locations: Location[], strings: string[], functions: ParcaFunction[]) => string;
|
|
@@ -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
|
+
import { getLastItem } from '@parca/functions';
|
|
14
|
+
import { hexifyAddress } from '../../utils';
|
|
15
|
+
import { EVERYTHING_ELSE } from '@parca/store';
|
|
16
|
+
export var getBinaryName = function (node, mappings, locations, strings) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
if (((_a = node.meta) === null || _a === void 0 ? void 0 : _a.locationIndex) === undefined || ((_b = node.meta) === null || _b === void 0 ? void 0 : _b.locationIndex) === 0) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
var location = locations[node.meta.locationIndex - 1];
|
|
22
|
+
if (location.mappingIndex === undefined || location.mappingIndex === 0) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
var mapping = mappings[location.mappingIndex - 1];
|
|
26
|
+
if (mapping == null || mapping.fileStringIndex == null) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
var mappingFile = strings[mapping.fileStringIndex];
|
|
30
|
+
return getLastItem(mappingFile);
|
|
31
|
+
};
|
|
32
|
+
export function nodeLabel(node, strings, mappings, locations, functions) {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
if (((_a = node.meta) === null || _a === void 0 ? void 0 : _a.locationIndex) === undefined)
|
|
35
|
+
return '<unknown>';
|
|
36
|
+
if (((_b = node.meta) === null || _b === void 0 ? void 0 : _b.locationIndex) === 0)
|
|
37
|
+
return '<unknown>';
|
|
38
|
+
var location = locations[node.meta.locationIndex - 1];
|
|
39
|
+
var binary = getBinaryName(node, mappings, locations, strings);
|
|
40
|
+
var mappingString = binary != null ? "[".concat(binary, "]") : '';
|
|
41
|
+
if (location.lines.length > 0) {
|
|
42
|
+
var funcName = strings[functions[location.lines[node.meta.lineIndex].functionIndex - 1].nameStringIndex];
|
|
43
|
+
return "".concat(mappingString, " ").concat(funcName);
|
|
44
|
+
}
|
|
45
|
+
var address = hexifyAddress(location.address);
|
|
46
|
+
var fallback = "".concat(mappingString).concat(address);
|
|
47
|
+
return fallback === '' ? '<unknown>' : fallback;
|
|
48
|
+
}
|
|
49
|
+
export var extractFeature = function (data, mappings, locations, strings, functions) {
|
|
50
|
+
var name = nodeLabel(data, strings, mappings, locations, functions).trim();
|
|
51
|
+
if (name.startsWith('runtime') || name === 'root') {
|
|
52
|
+
return 'runtime';
|
|
53
|
+
}
|
|
54
|
+
var binaryName = getBinaryName(data, mappings, locations, strings);
|
|
55
|
+
if (binaryName != null) {
|
|
56
|
+
return binaryName;
|
|
57
|
+
}
|
|
58
|
+
return EVERYTHING_ELSE;
|
|
59
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { Flamegraph } from '@parca/client';
|
|
3
|
+
import type { NavigateFunction } from '@parca/functions';
|
|
3
4
|
export type ResizeHandler = (width: number, height: number) => void;
|
|
4
5
|
interface ProfileIcicleGraphProps {
|
|
5
6
|
width?: number;
|
|
@@ -8,7 +9,8 @@ interface ProfileIcicleGraphProps {
|
|
|
8
9
|
curPath: string[] | [];
|
|
9
10
|
setNewCurPath: (path: string[]) => void;
|
|
10
11
|
onContainerResize?: ResizeHandler;
|
|
12
|
+
navigateTo?: NavigateFunction;
|
|
11
13
|
loading: boolean;
|
|
12
14
|
}
|
|
13
|
-
declare const ProfileIcicleGraph: ({ graph, curPath, setNewCurPath, sampleUnit, onContainerResize, loading, }: ProfileIcicleGraphProps) => JSX.Element;
|
|
15
|
+
declare const ProfileIcicleGraph: ({ graph, curPath, setNewCurPath, sampleUnit, onContainerResize, navigateTo, loading, }: ProfileIcicleGraphProps) => JSX.Element;
|
|
14
16
|
export default ProfileIcicleGraph;
|