@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.
Files changed (54) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/GraphTooltip/index.js +2 -2
  3. package/dist/MetricsGraph/index.js +2 -3
  4. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +1 -1
  5. package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +1 -1
  6. package/dist/ProfileExplorer/ProfileExplorerSingle.js +2 -1
  7. package/dist/ProfileExplorer/index.d.ts +1 -1
  8. package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.d.ts +8 -0
  9. package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.js +75 -0
  10. package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.d.ts +47 -0
  11. package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.js +98 -0
  12. package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts +14 -0
  13. package/dist/ProfileIcicleGraph/IcicleGraph/index.js +78 -0
  14. package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.d.ts +14 -0
  15. package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.js +60 -0
  16. package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.d.ts +7 -0
  17. package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.js +32 -0
  18. package/dist/ProfileIcicleGraph/IcicleGraph/utils.d.ts +5 -0
  19. package/dist/ProfileIcicleGraph/IcicleGraph/utils.js +59 -0
  20. package/dist/ProfileIcicleGraph/index.d.ts +3 -1
  21. package/dist/ProfileIcicleGraph/index.js +6 -5
  22. package/dist/ProfileView/FilterByFunctionButton.d.ts +1 -1
  23. package/dist/ProfileView/FilterByFunctionButton.js +1 -2
  24. package/dist/ProfileView/ViewSelector.d.ts +1 -1
  25. package/dist/ProfileView/ViewSelector.js +1 -14
  26. package/dist/ProfileView/index.js +4 -4
  27. package/dist/ProfileViewWithData.d.ts +1 -1
  28. package/dist/ProfileViewWithData.js +2 -2
  29. package/dist/TopTable/index.d.ts +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +1 -1
  32. package/dist/styles.css +1 -1
  33. package/package.json +6 -5
  34. package/src/GraphTooltip/index.tsx +2 -2
  35. package/src/MetricsGraph/index.tsx +2 -3
  36. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +1 -1
  37. package/src/ProfileExplorer/ProfileExplorerSingle.tsx +4 -3
  38. package/src/ProfileExplorer/index.tsx +1 -1
  39. package/src/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.tsx +93 -0
  40. package/src/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.tsx +262 -0
  41. package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +166 -0
  42. package/src/ProfileIcicleGraph/IcicleGraph/useColoredGraph.ts +107 -0
  43. package/src/ProfileIcicleGraph/IcicleGraph/useNodeColor.ts +42 -0
  44. package/src/ProfileIcicleGraph/IcicleGraph/utils.ts +94 -0
  45. package/src/ProfileIcicleGraph/index.tsx +25 -20
  46. package/src/ProfileView/FilterByFunctionButton.tsx +2 -2
  47. package/src/ProfileView/ViewSelector.tsx +2 -2
  48. package/src/ProfileView/index.tsx +6 -5
  49. package/src/ProfileViewWithData.tsx +3 -2
  50. package/src/TopTable/index.tsx +1 -1
  51. package/src/index.tsx +1 -1
  52. package/dist/IcicleGraph.d.ts +0 -45
  53. package/dist/IcicleGraph.js +0 -180
  54. 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 = useIsShiftDown();
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 = useIsShiftDown();
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(_Fragment, { 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
+ 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,7 @@
1
+ import type { ColoredFlamegraphNode } from './useColoredGraph';
2
+ interface Props {
3
+ data: ColoredFlamegraphNode;
4
+ compareMode: boolean;
5
+ }
6
+ declare const useNodeColor: ({ data, compareMode }: Props) => string;
7
+ export default useNodeColor;
@@ -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;