@parca/profile 0.16.219 → 0.16.221

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.221](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.220...@parca/profile@0.16.221) (2023-08-09)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## 0.16.220 (2023-08-09)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## 0.16.219 (2023-08-07)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -6,7 +6,8 @@ interface GraphTooltipArrowContentProps {
6
6
  total: bigint;
7
7
  totalUnfiltered: bigint;
8
8
  row: number | null;
9
+ level: number;
9
10
  isFixed: boolean;
10
11
  }
11
- declare const GraphTooltipArrowContent: ({ table, unit, total, totalUnfiltered, row, isFixed, }: GraphTooltipArrowContentProps) => React.JSX.Element;
12
+ declare const GraphTooltipArrowContent: ({ table, unit, total, totalUnfiltered, row, level, isFixed, }: GraphTooltipArrowContentProps) => React.JSX.Element;
12
13
  export default GraphTooltipArrowContent;
@@ -14,20 +14,20 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
14
14
  import { useState } from 'react';
15
15
  import { CopyToClipboard } from 'react-copy-to-clipboard';
16
16
  import { divide, getLastItem, valueFormatter } from '@parca/utilities';
17
- import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_FILE_NAME, FIELD_FUNCTION_NAME, FIELD_FUNCTION_START_LINE, FIELD_LABELS, FIELD_LOCATION_ADDRESS, FIELD_LOCATION_LINE, FIELD_MAPPING_BUILD_ID, FIELD_MAPPING_FILE, } from '../ProfileIcicleGraph/IcicleGraphArrow';
17
+ import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_FILE_NAME, FIELD_FUNCTION_START_LINE, FIELD_LABELS, FIELD_LOCATION_ADDRESS, FIELD_LOCATION_LINE, FIELD_MAPPING_BUILD_ID, FIELD_MAPPING_FILE, } from '../ProfileIcicleGraph/IcicleGraphArrow';
18
+ import { nodeLabel } from '../ProfileIcicleGraph/IcicleGraphArrow/utils';
18
19
  import { hexifyAddress, truncateString, truncateStringReverse } from '../utils';
19
20
  import { ExpandOnHover } from './ExpandOnHoverValue';
20
21
  let timeoutHandle = null;
21
22
  const NoData = () => {
22
23
  return _jsx("span", { className: "rounded bg-gray-200 px-2 dark:bg-gray-800", children: "Not available" });
23
24
  };
24
- const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, isFixed, }) => {
25
+ const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, level, isFixed, }) => {
25
26
  const [isCopied, setIsCopied] = useState(false);
26
27
  if (row === null) {
27
28
  return _jsx(_Fragment, {});
28
29
  }
29
30
  const locationAddress = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
30
- const functionName = table.getChild(FIELD_FUNCTION_NAME)?.get(row) ?? '';
31
31
  const cumulative = table.getChild(FIELD_CUMULATIVE)?.get(row) ?? 0n;
32
32
  const diff = table.getChild(FIELD_DIFF)?.get(row) ?? 0n;
33
33
  const onCopy = () => {
@@ -43,6 +43,8 @@ const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, is
43
43
  const diffValueText = diffSign + valueFormatter(diff, unit, 1);
44
44
  const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
45
45
  const diffText = `${diffValueText} (${diffPercentageText})`;
46
+ const name = nodeLabel(table, row, level, false);
47
+ console.log(level, row, name);
46
48
  const getTextForCumulative = (hoveringNodeCumulative) => {
47
49
  const filtered = totalUnfiltered > total
48
50
  ? ` / ${(100 * divide(hoveringNodeCumulative, total)).toFixed(2)}% of filtered`
@@ -50,7 +52,7 @@ const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, is
50
52
  return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
51
53
  (${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
52
54
  };
53
- return (_jsx("div", { className: `flex text-sm ${isFixed ? 'w-full' : ''}`, children: _jsx("div", { className: `m-auto w-full ${isFixed ? 'w-full' : ''}`, children: _jsxs("div", { className: "min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900", children: [_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "mx-2", children: [_jsx("div", { className: "flex h-10 items-center break-all font-semibold", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: functionName !== '' ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: functionName, children: _jsx("button", { className: "cursor-pointer text-left", children: functionName }) })) : (_jsx(_Fragment, { children: locationAddress !== 0n ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer text-left", children: hexifyAddress(locationAddress) }) })) : (_jsx("p", { children: "unknown" })) })) })) }), _jsx("table", { className: "my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Cumulative" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: getTextForCumulative(cumulative), children: _jsx("button", { className: "cursor-pointer", children: getTextForCumulative(cumulative) }) }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: diffText, children: _jsx("button", { className: "cursor-pointer", children: diffText }) }) })] })), _jsx(TooltipMetaInfo, { table: table, row: row, onCopy: onCopy })] }) })] }) }), _jsx("span", { className: "mx-2 block text-xs text-gray-500", children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' })] }) }) }));
55
+ return (_jsx("div", { className: `flex text-sm ${isFixed ? 'w-full' : ''}`, children: _jsx("div", { className: `m-auto w-full ${isFixed ? 'w-full' : ''}`, children: _jsxs("div", { className: "min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900", children: [_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "mx-2", children: [_jsx("div", { className: "flex h-10 items-center break-all font-semibold", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx(_Fragment, { children: name !== '' ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: name, children: _jsx("button", { className: "cursor-pointer text-left", children: name }) })) : (_jsx(_Fragment, { children: locationAddress !== 0n ? (_jsx(CopyToClipboard, { onCopy: onCopy, text: hexifyAddress(locationAddress), children: _jsx("button", { className: "cursor-pointer text-left", children: hexifyAddress(locationAddress) }) })) : (_jsx("p", { children: "unknown" })) })) })) }), _jsx("table", { className: "my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Cumulative" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: getTextForCumulative(cumulative), children: _jsx("button", { className: "cursor-pointer", children: getTextForCumulative(cumulative) }) }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx(CopyToClipboard, { onCopy: onCopy, text: diffText, children: _jsx("button", { className: "cursor-pointer", children: diffText }) }) })] })), _jsx(TooltipMetaInfo, { table: table, row: row, onCopy: onCopy })] }) })] }) }), _jsx("span", { className: "mx-2 block text-xs text-gray-500", children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' })] }) }) }));
54
56
  };
55
57
  const TooltipMetaInfo = ({ table,
56
58
  // total,
@@ -14,6 +14,7 @@ interface IcicleGraphNodesProps {
14
14
  curPath: string[];
15
15
  setCurPath: (path: string[]) => void;
16
16
  setHoveringRow: (row: number | null) => void;
17
+ setHoveringLevel: (level: number | null) => void;
17
18
  path: string[];
18
19
  xScale: (value: bigint) => number;
19
20
  searchString?: string;
@@ -39,6 +40,7 @@ interface IcicleNodeProps {
39
40
  total: bigint;
40
41
  setCurPath: (path: string[]) => void;
41
42
  setHoveringRow: (row: number | null) => void;
43
+ setHoveringLevel: (level: number | null) => void;
42
44
  xScale: (value: bigint) => number;
43
45
  isRoot?: boolean;
44
46
  searchString?: string;
@@ -20,7 +20,7 @@ import { FIELD_CHILDREN, FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_NAME, FIEL
20
20
  import useNodeColor from './useNodeColor';
21
21
  import { nodeLabel } from './utils';
22
22
  export const RowHeight = 26;
23
- export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({ table, childRows, mappingColors, x, y, xScale, total, totalWidth, level, path, setCurPath, setHoveringRow, curPath, sortBy, searchString, darkMode, compareMode, }) {
23
+ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({ table, childRows, mappingColors, x, y, xScale, total, totalWidth, level, path, setCurPath, setHoveringRow, setHoveringLevel, curPath, sortBy, searchString, darkMode, compareMode, }) {
24
24
  const cumulatives = table.getChild(FIELD_CUMULATIVE);
25
25
  if (childRows === undefined || childRows.length === 0) {
26
26
  return _jsx(_Fragment, {});
@@ -28,14 +28,14 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({ tab
28
28
  childRows =
29
29
  curPath.length === 0
30
30
  ? childRows
31
- : childRows.filter(c => nodeLabel(table, c, false) === curPath[0]);
31
+ : childRows.filter(c => nodeLabel(table, c, level, false) === curPath[0]);
32
32
  let childrenCumulative = BigInt(0);
33
33
  const childrenElements = [];
34
34
  childRows.forEach((child, i) => {
35
35
  const xStart = Math.floor(xScale(childrenCumulative));
36
36
  const c = cumulatives?.get(child);
37
37
  childrenCumulative += c;
38
- childrenElements.push(_jsx(IcicleNode, { table: table, row: child, mappingColors: mappingColors, x: xStart, y: 0, totalWidth: totalWidth, height: RowHeight, path: path, setCurPath: setCurPath, setHoveringRow: setHoveringRow, level: level, curPath: curPath, total: total, xScale: xScale, sortBy: sortBy, searchString: searchString, darkMode: darkMode, compareMode: compareMode }, `node-${level}-${i}`));
38
+ childrenElements.push(_jsx(IcicleNode, { table: table, row: child, mappingColors: mappingColors, x: xStart, y: 0, totalWidth: totalWidth, height: RowHeight, path: path, setCurPath: setCurPath, setHoveringRow: setHoveringRow, setHoveringLevel: setHoveringLevel, level: level, curPath: curPath, total: total, xScale: xScale, sortBy: sortBy, searchString: searchString, darkMode: darkMode, compareMode: compareMode }, `node-${level}-${i}`));
39
39
  });
40
40
  return _jsx("g", { transform: `translate(${x}, ${y})`, children: childrenElements });
41
41
  });
@@ -48,7 +48,7 @@ const fadedIcicleRectStyles = {
48
48
  transition: 'opacity .15s linear',
49
49
  opacity: '0.5',
50
50
  };
51
- export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, mappingColors, x, y, height, setCurPath, curPath, level, path, total, totalWidth, xScale, isRoot = false, searchString, setHoveringRow, sortBy, darkMode, compareMode, }) {
51
+ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, mappingColors, x, y, height, setCurPath, curPath, level, path, total, totalWidth, xScale, isRoot = false, searchString, setHoveringRow, setHoveringLevel, sortBy, darkMode, compareMode, }) {
52
52
  const { isShiftDown } = useKeyDown();
53
53
  // get the columns to read from
54
54
  const mappingColumn = table.getChild(FIELD_MAPPING_FILE);
@@ -117,8 +117,8 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
117
117
  functionName,
118
118
  });
119
119
  const name = useMemo(() => {
120
- return isRoot ? 'root' : nodeLabel(table, row, binaries.length > 1);
121
- }, [table, row, isRoot, binaries]);
120
+ return isRoot ? 'root' : nodeLabel(table, row, level, binaries.length > 1);
121
+ }, [table, row, level, isRoot, binaries]);
122
122
  const nextPath = path.concat([name]);
123
123
  const isFaded = curPath.length > 0 && name !== curPath[curPath.length - 1];
124
124
  const styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
@@ -143,11 +143,13 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
143
143
  if (isShiftDown)
144
144
  return;
145
145
  setHoveringRow(row);
146
+ setHoveringLevel(level);
146
147
  };
147
148
  const onMouseLeave = () => {
148
149
  if (isShiftDown)
149
150
  return;
150
151
  setHoveringRow(null);
152
+ setHoveringLevel(null);
151
153
  };
152
154
  return (_jsxs(_Fragment, { children: [_jsxs("g", { transform: `translate(${x + 1}, ${y + 1})`, style: styles, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onClick: () => {
153
155
  setCurPath(nextPath);
@@ -155,5 +157,5 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
155
157
  fill: colorResult,
156
158
  }, className: cx('stroke-white dark:stroke-gray-700', {
157
159
  'opacity-50': isHighlightEnabled && !isHighlighted,
158
- }) }), width > 5 && (_jsx("svg", { width: width - 5, height: height, children: _jsx("text", { x: 5, y: 15, style: { fontSize: '12px' }, children: name }) }))] }), childRows.length > 0 && (_jsx(IcicleGraphNodes, { table: table, row: row, mappingColors: mappingColors, childRows: childRows, x: x, y: RowHeight, xScale: newXScale, total: total, totalWidth: totalWidth, level: nextLevel, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath, setHoveringRow: setHoveringRow, searchString: searchString, sortBy: sortBy, darkMode: darkMode, compareMode: compareMode }))] }));
160
+ }) }), width > 5 && (_jsx("svg", { width: width - 5, height: height, children: _jsx("text", { x: 5, y: 15, style: { fontSize: '12px' }, children: name }) }))] }), childRows.length > 0 && (_jsx(IcicleGraphNodes, { table: table, row: row, mappingColors: mappingColors, childRows: childRows, x: x, y: RowHeight, xScale: newXScale, total: total, totalWidth: totalWidth, level: nextLevel, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath, setHoveringRow: setHoveringRow, setHoveringLevel: setHoveringLevel, searchString: searchString, sortBy: sortBy, darkMode: darkMode, compareMode: compareMode }))] }));
159
161
  });
@@ -37,6 +37,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ table, total, f
37
37
  const isDarkMode = useAppSelector(selectDarkMode);
38
38
  const [height, setHeight] = useState(0);
39
39
  const [hoveringRow, setHoveringRow] = useState(null);
40
+ const [hoveringLevel, setHoveringLevel] = useState(null);
40
41
  const svg = useRef(null);
41
42
  const ref = useRef(null);
42
43
  const currentSearchString = selectQueryParam('search_string') ?? '';
@@ -108,7 +109,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ table, total, f
108
109
  }, [total, width]);
109
110
  // useMemo for the root graph as it otherwise renders the whole graph if the hoveringRow changes.
110
111
  const root = useMemo(() => {
111
- return (_jsx("svg", { className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg, children: _jsx("g", { ref: ref, children: _jsx("g", { transform: 'translate(0, 0)', children: _jsx(IcicleNode, { table: table, row: 0, mappingColors: mappingColors, x: 0, y: 0, totalWidth: width ?? 1, height: RowHeight, setCurPath: setCurPath, curPath: curPath, total: total, xScale: xScale, path: [], level: 0, isRoot: true, searchString: currentSearchString, setHoveringRow: setHoveringRow, sortBy: sortBy, darkMode: isDarkMode, compareMode: compareMode }) }) }) }));
112
+ return (_jsx("svg", { className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg, children: _jsx("g", { ref: ref, children: _jsx("g", { transform: 'translate(0, 0)', children: _jsx(IcicleNode, { table: table, row: 0, mappingColors: mappingColors, x: 0, y: 0, totalWidth: width ?? 1, height: RowHeight, setCurPath: setCurPath, curPath: curPath, total: total, xScale: xScale, path: [], level: 0, isRoot: true, searchString: currentSearchString, setHoveringRow: setHoveringRow, setHoveringLevel: setHoveringLevel, sortBy: sortBy, darkMode: isDarkMode, compareMode: compareMode }) }) }) }));
112
113
  }, [
113
114
  compareMode,
114
115
  curPath,
@@ -126,6 +127,6 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ table, total, f
126
127
  if (table.numRows === 0 || width === undefined) {
127
128
  return _jsx(_Fragment, {});
128
129
  }
129
- return (_jsxs("div", { onMouseLeave: () => dispatch(setHoveringNode(undefined)), children: [_jsx(ColorStackLegend, { mappingColors: mappingColors, navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltipArrow, { contextElement: svg.current, children: _jsx(GraphTooltipArrowContent, { table: table, row: hoveringRow, isFixed: false, total: total, totalUnfiltered: total + filtered, unit: sampleUnit }) }), root] }));
130
+ return (_jsxs("div", { onMouseLeave: () => dispatch(setHoveringNode(undefined)), children: [_jsx(ColorStackLegend, { mappingColors: mappingColors, navigateTo: navigateTo, compareMode: compareMode }), _jsx(GraphTooltipArrow, { contextElement: svg.current, children: _jsx(GraphTooltipArrowContent, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, isFixed: false, total: total, totalUnfiltered: total + filtered, unit: sampleUnit }) }), root] }));
130
131
  });
131
132
  export default IcicleGraphArrow;
@@ -1,4 +1,4 @@
1
1
  import { Table } from 'apache-arrow';
2
2
  import { type Feature } from '@parca/store';
3
- export declare function nodeLabel(table: Table<any>, row: number, showBinaryName: boolean): string;
3
+ export declare function nodeLabel(table: Table<any>, row: number, level: number, showBinaryName: boolean): string;
4
4
  export declare const extractFeature: (mapping: string) => Feature;
@@ -14,9 +14,15 @@ import { EVERYTHING_ELSE, FEATURE_TYPES } from '@parca/store';
14
14
  import { getLastItem } from '@parca/utilities';
15
15
  import { hexifyAddress } from '../../utils';
16
16
  import { FIELD_FUNCTION_NAME, FIELD_LOCATION_ADDRESS, FIELD_MAPPING_FILE } from './index';
17
- export function nodeLabel(table, row, showBinaryName) {
17
+ export function nodeLabel(table, row, level, showBinaryName) {
18
18
  const functionName = table.getChild(FIELD_FUNCTION_NAME)?.get(row);
19
19
  if (functionName !== null && functionName !== '') {
20
+ if (level === 1 && functionName.startsWith('{') && functionName.endsWith('}')) {
21
+ return Object.entries(JSON.parse(functionName))
22
+ .sort(([a], [b]) => a.localeCompare(b))
23
+ .map(([k, v]) => `${k}="${v}"`)
24
+ .join(', ');
25
+ }
20
26
  return functionName;
21
27
  }
22
28
  let mappingString = '';
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.219",
3
+ "version": "0.16.221",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
- "@parca/client": "^0.16.81",
7
- "@parca/components": "^0.16.175",
6
+ "@parca/client": "^0.16.82",
7
+ "@parca/components": "^0.16.176",
8
8
  "@parca/dynamicsize": "^0.16.54",
9
- "@parca/hooks": "^0.0.16",
9
+ "@parca/hooks": "^0.0.17",
10
10
  "@parca/parser": "^0.16.55",
11
- "@parca/store": "^0.16.94",
12
- "@parca/utilities": "^0.0.23",
11
+ "@parca/store": "^0.16.95",
12
+ "@parca/utilities": "^0.0.24",
13
13
  "@tanstack/react-query": "^4.0.5",
14
14
  "@types/react-beautiful-dnd": "^13.1.3",
15
15
  "apache-arrow": "^12.0.0",
@@ -47,5 +47,5 @@
47
47
  "access": "public",
48
48
  "registry": "https://registry.npmjs.org/"
49
49
  },
50
- "gitHead": "b1ee3cd937d11b03c4f5c9f6cee1cff753f27fa5"
50
+ "gitHead": "247b54f3d042a88c844577df37b008236028baf3"
51
51
  }
@@ -22,7 +22,6 @@ import {
22
22
  FIELD_CUMULATIVE,
23
23
  FIELD_DIFF,
24
24
  FIELD_FUNCTION_FILE_NAME,
25
- FIELD_FUNCTION_NAME,
26
25
  FIELD_FUNCTION_START_LINE,
27
26
  FIELD_LABELS,
28
27
  FIELD_LOCATION_ADDRESS,
@@ -30,6 +29,7 @@ import {
30
29
  FIELD_MAPPING_BUILD_ID,
31
30
  FIELD_MAPPING_FILE,
32
31
  } from '../ProfileIcicleGraph/IcicleGraphArrow';
32
+ import {nodeLabel} from '../ProfileIcicleGraph/IcicleGraphArrow/utils';
33
33
  import {hexifyAddress, truncateString, truncateStringReverse} from '../utils';
34
34
  import {ExpandOnHover} from './ExpandOnHoverValue';
35
35
 
@@ -41,6 +41,7 @@ interface GraphTooltipArrowContentProps {
41
41
  total: bigint;
42
42
  totalUnfiltered: bigint;
43
43
  row: number | null;
44
+ level: number;
44
45
  isFixed: boolean;
45
46
  }
46
47
 
@@ -54,6 +55,7 @@ const GraphTooltipArrowContent = ({
54
55
  total,
55
56
  totalUnfiltered,
56
57
  row,
58
+ level,
57
59
  isFixed,
58
60
  }: GraphTooltipArrowContentProps): React.JSX.Element => {
59
61
  const [isCopied, setIsCopied] = useState<boolean>(false);
@@ -63,7 +65,6 @@ const GraphTooltipArrowContent = ({
63
65
  }
64
66
 
65
67
  const locationAddress: bigint = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row) ?? 0n;
66
- const functionName: string = table.getChild(FIELD_FUNCTION_NAME)?.get(row) ?? '';
67
68
  const cumulative: bigint = table.getChild(FIELD_CUMULATIVE)?.get(row) ?? 0n;
68
69
  const diff: bigint = table.getChild(FIELD_DIFF)?.get(row) ?? 0n;
69
70
 
@@ -83,6 +84,9 @@ const GraphTooltipArrowContent = ({
83
84
  const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
84
85
  const diffText = `${diffValueText} (${diffPercentageText})`;
85
86
 
87
+ const name = nodeLabel(table, row, level, false);
88
+ console.log(level, row, name);
89
+
86
90
  const getTextForCumulative = (hoveringNodeCumulative: bigint): string => {
87
91
  const filtered =
88
92
  totalUnfiltered > total
@@ -103,9 +107,9 @@ const GraphTooltipArrowContent = ({
103
107
  <p>root</p>
104
108
  ) : (
105
109
  <>
106
- {functionName !== '' ? (
107
- <CopyToClipboard onCopy={onCopy} text={functionName}>
108
- <button className="cursor-pointer text-left">{functionName}</button>
110
+ {name !== '' ? (
111
+ <CopyToClipboard onCopy={onCopy} text={name}>
112
+ <button className="cursor-pointer text-left">{name}</button>
109
113
  </CopyToClipboard>
110
114
  ) : (
111
115
  <>
@@ -45,6 +45,7 @@ interface IcicleGraphNodesProps {
45
45
  curPath: string[];
46
46
  setCurPath: (path: string[]) => void;
47
47
  setHoveringRow: (row: number | null) => void;
48
+ setHoveringLevel: (level: number | null) => void;
48
49
  path: string[];
49
50
  xScale: (value: bigint) => number;
50
51
  searchString?: string;
@@ -66,6 +67,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
66
67
  path,
67
68
  setCurPath,
68
69
  setHoveringRow,
70
+ setHoveringLevel,
69
71
  curPath,
70
72
  sortBy,
71
73
  searchString,
@@ -81,7 +83,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
81
83
  childRows =
82
84
  curPath.length === 0
83
85
  ? childRows
84
- : childRows.filter(c => nodeLabel(table, c, false) === curPath[0]);
86
+ : childRows.filter(c => nodeLabel(table, c, level, false) === curPath[0]);
85
87
 
86
88
  let childrenCumulative = BigInt(0);
87
89
  const childrenElements: ReactNode[] = [];
@@ -103,6 +105,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
103
105
  path={path}
104
106
  setCurPath={setCurPath}
105
107
  setHoveringRow={setHoveringRow}
108
+ setHoveringLevel={setHoveringLevel}
106
109
  level={level}
107
110
  curPath={curPath}
108
111
  total={total}
@@ -136,6 +139,7 @@ interface IcicleNodeProps {
136
139
  total: bigint;
137
140
  setCurPath: (path: string[]) => void;
138
141
  setHoveringRow: (row: number | null) => void;
142
+ setHoveringLevel: (level: number | null) => void;
139
143
  xScale: (value: bigint) => number;
140
144
  isRoot?: boolean;
141
145
  searchString?: string;
@@ -171,6 +175,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
171
175
  isRoot = false,
172
176
  searchString,
173
177
  setHoveringRow,
178
+ setHoveringLevel,
174
179
  sortBy,
175
180
  darkMode,
176
181
  compareMode,
@@ -246,8 +251,8 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
246
251
  functionName,
247
252
  });
248
253
  const name = useMemo(() => {
249
- return isRoot ? 'root' : nodeLabel(table, row, binaries.length > 1);
250
- }, [table, row, isRoot, binaries]);
254
+ return isRoot ? 'root' : nodeLabel(table, row, level, binaries.length > 1);
255
+ }, [table, row, level, isRoot, binaries]);
251
256
  const nextPath = path.concat([name]);
252
257
  const isFaded = curPath.length > 0 && name !== curPath[curPath.length - 1];
253
258
  const styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
@@ -277,11 +282,13 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
277
282
  const onMouseEnter = (): void => {
278
283
  if (isShiftDown) return;
279
284
  setHoveringRow(row);
285
+ setHoveringLevel(level);
280
286
  };
281
287
 
282
288
  const onMouseLeave = (): void => {
283
289
  if (isShiftDown) return;
284
290
  setHoveringRow(null);
291
+ setHoveringLevel(null);
285
292
  };
286
293
 
287
294
  return (
@@ -331,6 +338,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
331
338
  curPath={nextCurPath}
332
339
  setCurPath={setCurPath}
333
340
  setHoveringRow={setHoveringRow}
341
+ setHoveringLevel={setHoveringLevel}
334
342
  searchString={searchString}
335
343
  sortBy={sortBy}
336
344
  darkMode={darkMode}
@@ -80,6 +80,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
80
80
 
81
81
  const [height, setHeight] = useState(0);
82
82
  const [hoveringRow, setHoveringRow] = useState<number | null>(null);
83
+ const [hoveringLevel, setHoveringLevel] = useState<number | null>(null);
83
84
  const svg = useRef(null);
84
85
  const ref = useRef<SVGGElement>(null);
85
86
 
@@ -190,6 +191,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
190
191
  isRoot={true}
191
192
  searchString={currentSearchString}
192
193
  setHoveringRow={setHoveringRow}
194
+ setHoveringLevel={setHoveringLevel}
193
195
  sortBy={sortBy}
194
196
  darkMode={isDarkMode}
195
197
  compareMode={compareMode}
@@ -228,6 +230,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
228
230
  <GraphTooltipArrowContent
229
231
  table={table}
230
232
  row={hoveringRow}
233
+ level={hoveringLevel ?? 0}
231
234
  isFixed={false}
232
235
  total={total}
233
236
  totalUnfiltered={total + filtered}
@@ -19,9 +19,20 @@ import {getLastItem} from '@parca/utilities';
19
19
  import {hexifyAddress} from '../../utils';
20
20
  import {FIELD_FUNCTION_NAME, FIELD_LOCATION_ADDRESS, FIELD_MAPPING_FILE} from './index';
21
21
 
22
- export function nodeLabel(table: Table<any>, row: number, showBinaryName: boolean): string {
22
+ export function nodeLabel(
23
+ table: Table<any>,
24
+ row: number,
25
+ level: number,
26
+ showBinaryName: boolean
27
+ ): string {
23
28
  const functionName: string | null = table.getChild(FIELD_FUNCTION_NAME)?.get(row);
24
29
  if (functionName !== null && functionName !== '') {
30
+ if (level === 1 && functionName.startsWith('{') && functionName.endsWith('}')) {
31
+ return Object.entries(JSON.parse(functionName))
32
+ .sort(([a], [b]) => a.localeCompare(b))
33
+ .map(([k, v]) => `${k}="${v as string}"`)
34
+ .join(', ');
35
+ }
25
36
  return functionName;
26
37
  }
27
38