@parca/profile 0.16.130 → 0.16.132

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 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.132 (2023-03-07)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.131](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.130...@parca/profile@0.16.131) (2023-03-06)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## 0.16.130 (2023-03-06)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -5,7 +5,7 @@ interface GraphTooltipProps {
5
5
  y?: number;
6
6
  unit: string;
7
7
  total: number;
8
- hoveringNode: HoveringNode;
8
+ hoveringNode?: HoveringNode;
9
9
  contextElement: Element | null;
10
10
  isFixed?: boolean;
11
11
  virtualContextElement?: boolean;
@@ -20,5 +20,5 @@ export interface HoveringNode extends CallgraphNode, FlamegraphRootNode, Flamegr
20
20
  [key: string]: any;
21
21
  };
22
22
  }
23
- declare const GraphTooltip: ({ x, y, unit, total, hoveringNode, contextElement, isFixed, virtualContextElement, strings, mappings, locations, functions, }: GraphTooltipProps) => JSX.Element;
23
+ declare const GraphTooltip: ({ x, y, unit, total, hoveringNode: hoveringNodeProp, contextElement, isFixed, virtualContextElement, strings, mappings, locations, functions, }: GraphTooltipProps) => JSX.Element;
24
24
  export default GraphTooltip;
@@ -33,12 +33,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
33
33
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34
34
  // See the License for the specific language governing permissions and
35
35
  // limitations under the License.
36
- import { useEffect, useState } from 'react';
36
+ import { useEffect, useMemo, useState } from 'react';
37
37
  import { pointer } from 'd3-selection';
38
38
  import { CopyToClipboard } from 'react-copy-to-clipboard';
39
39
  import { usePopper } from 'react-popper';
40
40
  import { useKeyDown } from '@parca/components';
41
41
  import { getLastItem, valueFormatter } from '@parca/functions';
42
+ import { selectHoveringNode, useAppSelector } from '@parca/store';
42
43
  import { hexifyAddress, truncateString, truncateStringReverse } from '../';
43
44
  import { ExpandOnHover } from './ExpandOnHoverValue';
44
45
  var NoData = function () {
@@ -154,7 +155,16 @@ var GraphTooltipContent = function (_a) {
154
155
  hoveringNode: hoveringNode, strings: strings, mappings: mappings, locations: locations, functions: functions })] }) }))] })) })), _jsx("span", __assign({ className: "block text-gray-500 text-xs mx-2" }, { children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' }))] })) })) })));
155
156
  };
156
157
  var GraphTooltip = function (_a) {
157
- var x = _a.x, y = _a.y, unit = _a.unit, total = _a.total, hoveringNode = _a.hoveringNode, contextElement = _a.contextElement, _b = _a.isFixed, isFixed = _b === void 0 ? false : _b, _c = _a.virtualContextElement, virtualContextElement = _c === void 0 ? true : _c, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
158
+ var x = _a.x, y = _a.y, unit = _a.unit, total = _a.total, hoveringNodeProp = _a.hoveringNode, contextElement = _a.contextElement, _b = _a.isFixed, isFixed = _b === void 0 ? false : _b, _c = _a.virtualContextElement, virtualContextElement = _c === void 0 ? true : _c, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
159
+ var hoveringNodeState = useAppSelector(selectHoveringNode);
160
+ var hoveringNode = useMemo(function () {
161
+ var h = (hoveringNodeProp !== null && hoveringNodeProp !== void 0 ? hoveringNodeProp : hoveringNodeState);
162
+ if (h == null) {
163
+ return h;
164
+ }
165
+ // Cloning the object to avoid the mutating error as this is Redux store object and we are modifying the meta object in GraphTooltipContent component.
166
+ return __assign(__assign({}, h), { meta: __assign({}, h.meta) });
167
+ }, [hoveringNodeProp, hoveringNodeState]);
158
168
  var _d = useState(null), popperElement = _d[0], setPopperElement = _d[1];
159
169
  var _e = usePopper(virtualContextElement ? virtualElement : contextElement, popperElement, {
160
170
  placement: 'bottom-start',
@@ -10,11 +10,10 @@ var __assign = (this && this.__assign) || function () {
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { KeyDownProvider } from '@parca/components';
14
13
  import { ProfileViewWithData } from '..';
15
14
  import ProfileSelector from '../ProfileSelector';
16
15
  var ProfileExplorerSingle = function (_a) {
17
16
  var queryClient = _a.queryClient, query = _a.query, selectQuery = _a.selectQuery, selectProfile = _a.selectProfile, profile = _a.profile, compareProfile = _a.compareProfile, navigateTo = _a.navigateTo;
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, {})) }) }))] }));
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, {})) }) }))] }));
19
18
  };
20
19
  export default ProfileExplorerSingle;
@@ -24,7 +24,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
24
24
  // limitations under the License.
25
25
  import { useEffect, useState } from 'react';
26
26
  import { Provider } from 'react-redux';
27
- import { DateTimeRange, useParcaContext } from '@parca/components';
27
+ import { DateTimeRange, KeyDownProvider, useParcaContext } from '@parca/components';
28
28
  import { store } from '@parca/store';
29
29
  import { ProfileSelectionFromParams, SuffixParams } from '..';
30
30
  import { useProfileTypes } from '../ProfileSelector';
@@ -201,6 +201,6 @@ var ProfileExplorerApp = function (_a) {
201
201
  var ProfileExplorer = function (_a) {
202
202
  var queryClient = _a.queryClient, queryParams = _a.queryParams, navigateTo = _a.navigateTo;
203
203
  var reduxStore = store().store;
204
- return (_jsx(Provider, __assign({ store: reduxStore }, { children: _jsx(ProfileExplorerApp, { queryClient: queryClient, queryParams: queryParams, navigateTo: navigateTo }) })));
204
+ return (_jsx(Provider, __assign({ store: reduxStore }, { children: _jsx(KeyDownProvider, { children: _jsx(ProfileExplorerApp, { queryClient: queryClient, queryParams: queryParams, navigateTo: navigateTo }) }) })));
205
205
  };
206
206
  export default ProfileExplorer;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { FlamegraphNode, FlamegraphRootNode } from '@parca/client';
2
+ import { FlamegraphNode } from '@parca/client';
3
3
  import { Location, Mapping, Function as ParcaFunction } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
4
4
  export declare const RowHeight = 26;
5
5
  interface IcicleGraphNodesProps {
@@ -15,7 +15,6 @@ interface IcicleGraphNodesProps {
15
15
  level: number;
16
16
  curPath: string[];
17
17
  setCurPath: (path: string[]) => void;
18
- setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
19
18
  path: string[];
20
19
  xScale: (value: number) => number;
21
20
  searchString?: string;
@@ -37,7 +36,6 @@ interface IcicleNodeProps {
37
36
  path: string[];
38
37
  total: number;
39
38
  setCurPath: (path: string[]) => void;
40
- setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
41
39
  xScale: (value: number) => number;
42
40
  isRoot?: boolean;
43
41
  searchString?: string;
@@ -27,12 +27,12 @@ import cx from 'classnames';
27
27
  import { scaleLinear } from 'd3-scale';
28
28
  import { useKeyDown } from '@parca/components';
29
29
  import { isSearchMatch } from '@parca/functions';
30
- import { selectBinaries, useAppSelector } from '@parca/store';
30
+ import { selectBinaries, setHoveringNode, useAppDispatch, useAppSelector } from '@parca/store';
31
31
  import useNodeColor from './useNodeColor';
32
32
  import { nodeLabel } from './utils';
33
33
  export var RowHeight = 26;
34
- export var IcicleGraphNodes = React.memo(function IcicleGraphNodes(_a) {
35
- 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;
34
+ export var IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo(_a) {
35
+ 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, path = _a.path, setCurPath = _a.setCurPath, curPath = _a.curPath, searchString = _a.searchString, compareMode = _a.compareMode;
36
36
  var binaries = useAppSelector(selectBinaries);
37
37
  var nodes = curPath.length === 0
38
38
  ? data
@@ -41,10 +41,10 @@ export var IcicleGraphNodes = React.memo(function IcicleGraphNodes(_a) {
41
41
  curPath[0] ===
42
42
  nodeLabel(d, strings, mappings, locations, functions, binaries.length > 1);
43
43
  });
44
- return (_jsx("g", __assign({ transform: "translate(".concat(x, ", ").concat(y, ")") }, { children: nodes.map(function (d, i) {
44
+ return (_jsx("g", __assign({ transform: "translate(".concat(x, ", ").concat(y, ")") }, { children: nodes.map(function nodeMapper(d, i) {
45
45
  var start = nodes.slice(0, i).reduce(function (sum, d) { return sum + parseFloat(d.cumulative); }, 0);
46
46
  var xStart = xScale(start);
47
- 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)));
47
+ return (_jsx(IcicleNode, { x: xStart, y: 0, totalWidth: totalWidth, height: RowHeight, path: path, setCurPath: setCurPath, 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)));
48
48
  }) })));
49
49
  });
50
50
  var icicleRectStyles = {
@@ -56,9 +56,10 @@ var fadedIcicleRectStyles = {
56
56
  transition: 'opacity .15s linear',
57
57
  opacity: '0.5',
58
58
  };
59
- export var IcicleNode = React.memo(function IcicleNode(_a) {
60
- 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;
59
+ export var IcicleNode = React.memo(function IcicleNodeNoMemo(_a) {
60
+ var x = _a.x, y = _a.y, height = _a.height, setCurPath = _a.setCurPath, 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;
61
61
  var binaries = useAppSelector(selectBinaries);
62
+ var dispatch = useAppDispatch();
62
63
  var isShiftDown = useKeyDown().isShiftDown;
63
64
  var colorResult = useNodeColor({ data: data, compareMode: compareMode });
64
65
  var name = useMemo(function () {
@@ -90,12 +91,12 @@ export var IcicleNode = React.memo(function IcicleNode(_a) {
90
91
  var onMouseEnter = function () {
91
92
  if (isShiftDown)
92
93
  return;
93
- setHoveringNode(data);
94
+ dispatch(setHoveringNode(data));
94
95
  };
95
96
  var onMouseLeave = function () {
96
97
  if (isShiftDown)
97
98
  return;
98
- setHoveringNode(undefined);
99
+ dispatch(setHoveringNode(undefined));
99
100
  };
100
101
  return (_jsxs(_Fragment, { children: [_jsxs("g", __assign({ transform: "translate(".concat(x + 1, ", ").concat(y + 1, ")"), style: styles, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onClick: function () {
101
102
  setCurPath(nextPath);
@@ -103,5 +104,5 @@ export var IcicleNode = React.memo(function IcicleNode(_a) {
103
104
  fill: colorResult,
104
105
  }, className: cx('stroke-white dark:stroke-gray-700', {
105
106
  'opacity-50': isHighlightEnabled && !isHighlighted,
106
- }) }), 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 }))] }));
107
+ }) }), 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, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath, searchString: searchString, compareMode: compareMode }))] }));
107
108
  });
@@ -25,6 +25,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
25
25
  import { memo, useEffect, useMemo, useRef, useState } from 'react';
26
26
  import { scaleLinear } from 'd3-scale';
27
27
  import { selectQueryParam } from '@parca/functions';
28
+ import { setHoveringNode, useAppDispatch } from '@parca/store';
28
29
  import GraphTooltip from '../../GraphTooltip';
29
30
  import ColorStackLegend from './ColorStackLegend';
30
31
  import { IcicleNode, RowHeight } from './IcicleGraphNodes';
@@ -32,8 +33,8 @@ import useColoredGraph from './useColoredGraph';
32
33
  export var IcicleGraph = memo(function IcicleGraph(_a) {
33
34
  var _b;
34
35
  var graph = _a.graph, width = _a.width, setCurPath = _a.setCurPath, curPath = _a.curPath, sampleUnit = _a.sampleUnit, navigateTo = _a.navigateTo;
35
- var _c = useState(), hoveringNode = _c[0], setHoveringNode = _c[1];
36
- var _d = useState(0), height = _d[0], setHeight = _d[1];
36
+ var dispatch = useAppDispatch();
37
+ var _c = useState(0), height = _c[0], setHeight = _c[1];
37
38
  var svg = useRef(null);
38
39
  var ref = useRef(null);
39
40
  var coloredGraph = useColoredGraph(graph);
@@ -54,6 +55,6 @@ export var IcicleGraph = memo(function IcicleGraph(_a) {
54
55
  if (coloredGraph.root === undefined || width === undefined) {
55
56
  return _jsx(_Fragment, {});
56
57
  }
57
- return (_jsxs("div", __assign({ onMouseLeave: function () { return setHoveringNode(undefined); } }, { children: [_jsx(ColorStackLegend, { navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltip, { unit: sampleUnit, total: total, hoveringNode: hoveringNode, contextElement: svg.current, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function }), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, 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 }) })) })) }))] })));
58
+ return (_jsxs("div", __assign({ onMouseLeave: function () { return dispatch(setHoveringNode(undefined)); } }, { children: [_jsx(ColorStackLegend, { navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltip, { unit: sampleUnit, total: total, contextElement: svg.current, strings: coloredGraph.stringTable, mappings: coloredGraph.mapping, locations: coloredGraph.locations, functions: coloredGraph.function }), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, 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, 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 }) })) })) }))] })));
58
59
  });
59
60
  export default IcicleGraph;
@@ -45,12 +45,17 @@ export function ProfileSelectionFromParams(expression, from, to, mergeFrom, merg
45
45
  expression !== undefined) {
46
46
  // TODO: Refactor parsing the query and adding matchers
47
47
  var query_1 = Query.parse(expression);
48
- ParseLabels(labels !== null && labels !== void 0 ? labels : ['']).forEach(function (l) {
49
- var _a = query_1.setMatcher(l.name, l.value), newQuery = _a[0], changed = _a[1];
50
- if (changed) {
51
- query_1 = newQuery;
52
- }
53
- });
48
+ if (labels !== undefined) {
49
+ ParseLabels(labels !== null && labels !== void 0 ? labels : ['']).forEach(function (l) {
50
+ var hasLabels = labels.length > 0 && labels.filter(function (val) { return val !== ''; }).length > 0;
51
+ if (hasLabels) {
52
+ var _a = query_1.setMatcher(l.name, l.value), newQuery = _a[0], changed = _a[1];
53
+ if (changed) {
54
+ query_1 = newQuery;
55
+ }
56
+ }
57
+ });
58
+ }
54
59
  return new MergedProfileSelection(parseInt(mergeFrom), parseInt(mergeTo), query_1, filterQuery);
55
60
  }
56
61
  return null;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.130",
3
+ "version": "0.16.132",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@parca/client": "^0.16.65",
7
- "@parca/components": "^0.16.106",
7
+ "@parca/components": "^0.16.107",
8
8
  "@parca/dynamicsize": "^0.16.54",
9
9
  "@parca/functions": "^0.16.66",
10
10
  "@parca/parser": "^0.16.55",
11
- "@parca/store": "^0.16.62",
11
+ "@parca/store": "^0.16.63",
12
12
  "@types/react-beautiful-dnd": "^13.1.3",
13
13
  "d3": "7.8.2",
14
14
  "d3-scale": "^4.0.2",
@@ -45,5 +45,5 @@
45
45
  "access": "public",
46
46
  "registry": "https://registry.npmjs.org/"
47
47
  },
48
- "gitHead": "2eb0e54d2fdb21adfd76f91deef4d8ab33362cac"
48
+ "gitHead": "dd55ee1fb7a1a7393433738765a0cfe488cdd1cd"
49
49
  }
@@ -11,7 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useEffect, useState} from 'react';
14
+ import {useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {pointer} from 'd3-selection';
17
17
  import {CopyToClipboard} from 'react-copy-to-clipboard';
@@ -25,6 +25,7 @@ import {
25
25
  } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
26
26
  import {useKeyDown} from '@parca/components';
27
27
  import {getLastItem, valueFormatter} from '@parca/functions';
28
+ import {selectHoveringNode, useAppSelector} from '@parca/store';
28
29
 
29
30
  import {hexifyAddress, truncateString, truncateStringReverse} from '../';
30
31
  import {ExpandOnHover} from './ExpandOnHoverValue';
@@ -38,7 +39,7 @@ interface GraphTooltipProps {
38
39
  y?: number;
39
40
  unit: string;
40
41
  total: number;
41
- hoveringNode: HoveringNode;
42
+ hoveringNode?: HoveringNode;
42
43
  contextElement: Element | null;
43
44
  isFixed?: boolean;
44
45
  virtualContextElement?: boolean;
@@ -360,7 +361,7 @@ const GraphTooltip = ({
360
361
  y,
361
362
  unit,
362
363
  total,
363
- hoveringNode,
364
+ hoveringNode: hoveringNodeProp,
364
365
  contextElement,
365
366
  isFixed = false,
366
367
  virtualContextElement = true,
@@ -369,6 +370,22 @@ const GraphTooltip = ({
369
370
  locations,
370
371
  functions,
371
372
  }: GraphTooltipProps): JSX.Element => {
373
+ const hoveringNodeState = useAppSelector(selectHoveringNode);
374
+ const hoveringNode = useMemo<HoveringNode>(() => {
375
+ const h = (hoveringNodeProp ?? hoveringNodeState) as HoveringNode;
376
+ if (h == null) {
377
+ return h;
378
+ }
379
+
380
+ // Cloning the object to avoid the mutating error as this is Redux store object and we are modifying the meta object in GraphTooltipContent component.
381
+ return {
382
+ ...h,
383
+ meta: {
384
+ ...h.meta,
385
+ },
386
+ };
387
+ }, [hoveringNodeProp, hoveringNodeState]);
388
+
372
389
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
373
390
 
374
391
  const {styles, attributes, ...popperProps} = usePopper(
@@ -12,7 +12,6 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {QueryServiceClient} from '@parca/client';
15
- import {KeyDownProvider} from '@parca/components';
16
15
  import type {NavigateFunction} from '@parca/functions';
17
16
 
18
17
  import {ProfileSelection, ProfileViewWithData} from '..';
@@ -38,7 +37,7 @@ const ProfileExplorerSingle = ({
38
37
  navigateTo,
39
38
  }: ProfileExplorerSingleProps): JSX.Element => {
40
39
  return (
41
- <KeyDownProvider>
40
+ <>
42
41
  <div className="grid grid-cols-1">
43
42
  <div>
44
43
  <ProfileSelector
@@ -67,7 +66,7 @@ const ProfileExplorerSingle = ({
67
66
  )}
68
67
  </div>
69
68
  </div>
70
- </KeyDownProvider>
69
+ </>
71
70
  );
72
71
  };
73
72
 
@@ -16,7 +16,7 @@ import {useEffect, useState} from 'react';
16
16
  import {Provider} from 'react-redux';
17
17
 
18
18
  import {QueryServiceClient} from '@parca/client';
19
- import {DateTimeRange, useParcaContext} from '@parca/components';
19
+ import {DateTimeRange, KeyDownProvider, useParcaContext} from '@parca/components';
20
20
  import type {NavigateFunction} from '@parca/functions';
21
21
  import {store} from '@parca/store';
22
22
 
@@ -391,11 +391,13 @@ const ProfileExplorer = ({
391
391
 
392
392
  return (
393
393
  <Provider store={reduxStore}>
394
- <ProfileExplorerApp
395
- queryClient={queryClient}
396
- queryParams={queryParams}
397
- navigateTo={navigateTo}
398
- />
394
+ <KeyDownProvider>
395
+ <ProfileExplorerApp
396
+ queryClient={queryClient}
397
+ queryParams={queryParams}
398
+ navigateTo={navigateTo}
399
+ />
400
+ </KeyDownProvider>
399
401
  </Provider>
400
402
  );
401
403
  };
@@ -16,7 +16,7 @@ import React, {useMemo} from 'react';
16
16
  import cx from 'classnames';
17
17
  import {scaleLinear} from 'd3-scale';
18
18
 
19
- import {FlamegraphNode, FlamegraphRootNode} from '@parca/client';
19
+ import {FlamegraphNode} from '@parca/client';
20
20
  import {
21
21
  Location,
22
22
  Mapping,
@@ -24,7 +24,7 @@ import {
24
24
  } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
25
25
  import {useKeyDown} from '@parca/components';
26
26
  import {isSearchMatch} from '@parca/functions';
27
- import {selectBinaries, useAppSelector} from '@parca/store';
27
+ import {selectBinaries, setHoveringNode, useAppDispatch, useAppSelector} from '@parca/store';
28
28
 
29
29
  import useNodeColor from './useNodeColor';
30
30
  import {nodeLabel} from './utils';
@@ -44,14 +44,13 @@ interface IcicleGraphNodesProps {
44
44
  level: number;
45
45
  curPath: string[];
46
46
  setCurPath: (path: string[]) => void;
47
- setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
48
47
  path: string[];
49
48
  xScale: (value: number) => number;
50
49
  searchString?: string;
51
50
  compareMode: boolean;
52
51
  }
53
52
 
54
- export const IcicleGraphNodes = React.memo(function IcicleGraphNodes({
53
+ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
55
54
  data,
56
55
  strings,
57
56
  mappings,
@@ -63,7 +62,6 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodes({
63
62
  total,
64
63
  totalWidth,
65
64
  level,
66
- setHoveringNode,
67
65
  path,
68
66
  setCurPath,
69
67
  curPath,
@@ -83,7 +81,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodes({
83
81
 
84
82
  return (
85
83
  <g transform={`translate(${x}, ${y})`}>
86
- {nodes.map((d, i) => {
84
+ {nodes.map(function nodeMapper(d, i) {
87
85
  const start = nodes.slice(0, i).reduce((sum, d) => sum + parseFloat(d.cumulative), 0);
88
86
  const xStart = xScale(start);
89
87
 
@@ -96,7 +94,6 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodes({
96
94
  height={RowHeight}
97
95
  path={path}
98
96
  setCurPath={setCurPath}
99
- setHoveringNode={setHoveringNode}
100
97
  level={level}
101
98
  curPath={curPath}
102
99
  data={d}
@@ -130,7 +127,6 @@ interface IcicleNodeProps {
130
127
  path: string[];
131
128
  total: number;
132
129
  setCurPath: (path: string[]) => void;
133
- setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
134
130
  xScale: (value: number) => number;
135
131
  isRoot?: boolean;
136
132
  searchString?: string;
@@ -147,12 +143,11 @@ const fadedIcicleRectStyles = {
147
143
  opacity: '0.5',
148
144
  };
149
145
 
150
- export const IcicleNode = React.memo(function IcicleNode({
146
+ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
151
147
  x,
152
148
  y,
153
149
  height,
154
150
  setCurPath,
155
- setHoveringNode,
156
151
  curPath,
157
152
  level,
158
153
  path,
@@ -169,6 +164,7 @@ export const IcicleNode = React.memo(function IcicleNode({
169
164
  compareMode,
170
165
  }: IcicleNodeProps): JSX.Element {
171
166
  const binaries = useAppSelector(selectBinaries);
167
+ const dispatch = useAppDispatch();
172
168
  const {isShiftDown} = useKeyDown();
173
169
  const colorResult = useNodeColor({data, compareMode});
174
170
  const name = useMemo(() => {
@@ -206,12 +202,12 @@ export const IcicleNode = React.memo(function IcicleNode({
206
202
  const onMouseEnter = (): void => {
207
203
  if (isShiftDown) return;
208
204
 
209
- setHoveringNode(data);
205
+ dispatch(setHoveringNode(data));
210
206
  };
211
207
  const onMouseLeave = (): void => {
212
208
  if (isShiftDown) return;
213
209
 
214
- setHoveringNode(undefined);
210
+ dispatch(setHoveringNode(undefined));
215
211
  };
216
212
 
217
213
  return (
@@ -258,7 +254,6 @@ export const IcicleNode = React.memo(function IcicleNode({
258
254
  total={total}
259
255
  totalWidth={totalWidth}
260
256
  level={nextLevel}
261
- setHoveringNode={setHoveringNode}
262
257
  path={nextPath}
263
258
  curPath={nextCurPath}
264
259
  setCurPath={setCurPath}
@@ -15,10 +15,11 @@ import {memo, useEffect, useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import {scaleLinear} from 'd3-scale';
17
17
 
18
- import {Flamegraph, FlamegraphNode, FlamegraphRootNode} from '@parca/client';
18
+ import {Flamegraph} from '@parca/client';
19
19
  import {selectQueryParam, type NavigateFunction} from '@parca/functions';
20
+ import {setHoveringNode, useAppDispatch} from '@parca/store';
20
21
 
21
- import GraphTooltip, {type HoveringNode} from '../../GraphTooltip';
22
+ import GraphTooltip from '../../GraphTooltip';
22
23
  import ColorStackLegend from './ColorStackLegend';
23
24
  import {IcicleNode, RowHeight} from './IcicleGraphNodes';
24
25
  import useColoredGraph from './useColoredGraph';
@@ -40,9 +41,7 @@ export const IcicleGraph = memo(function IcicleGraph({
40
41
  sampleUnit,
41
42
  navigateTo,
42
43
  }: IcicleGraphProps): JSX.Element {
43
- const [hoveringNode, setHoveringNode] = useState<
44
- FlamegraphNode | FlamegraphRootNode | undefined
45
- >();
44
+ const dispatch = useAppDispatch();
46
45
  const [height, setHeight] = useState(0);
47
46
  const svg = useRef(null);
48
47
  const ref = useRef<SVGGElement>(null);
@@ -71,12 +70,11 @@ export const IcicleGraph = memo(function IcicleGraph({
71
70
  }
72
71
 
73
72
  return (
74
- <div onMouseLeave={() => setHoveringNode(undefined)}>
73
+ <div onMouseLeave={() => dispatch(setHoveringNode(undefined))}>
75
74
  <ColorStackLegend navigateTo={navigateTo} compareMode={compareMode} />
76
75
  <GraphTooltip
77
76
  unit={sampleUnit}
78
77
  total={total}
79
- hoveringNode={hoveringNode as HoveringNode}
80
78
  contextElement={svg.current}
81
79
  strings={coloredGraph.stringTable}
82
80
  mappings={coloredGraph.mapping}
@@ -98,7 +96,6 @@ export const IcicleGraph = memo(function IcicleGraph({
98
96
  totalWidth={width}
99
97
  height={RowHeight}
100
98
  setCurPath={setCurPath}
101
- setHoveringNode={setHoveringNode}
102
99
  curPath={curPath}
103
100
  data={coloredGraph.root}
104
101
  strings={coloredGraph.stringTable}
@@ -82,12 +82,17 @@ export function ProfileSelectionFromParams(
82
82
  ) {
83
83
  // TODO: Refactor parsing the query and adding matchers
84
84
  let query = Query.parse(expression);
85
- ParseLabels(labels ?? ['']).forEach(l => {
86
- const [newQuery, changed] = query.setMatcher(l.name, l.value);
87
- if (changed) {
88
- query = newQuery;
89
- }
90
- });
85
+ if (labels !== undefined) {
86
+ ParseLabels(labels ?? ['']).forEach(l => {
87
+ const hasLabels = labels.length > 0 && labels.filter(val => val !== '').length > 0;
88
+ if (hasLabels) {
89
+ const [newQuery, changed] = query.setMatcher(l.name, l.value);
90
+ if (changed) {
91
+ query = newQuery;
92
+ }
93
+ }
94
+ });
95
+ }
91
96
 
92
97
  return new MergedProfileSelection(parseInt(mergeFrom), parseInt(mergeTo), query, filterQuery);
93
98
  }