@parca/profile 0.16.218 → 0.16.220
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/GraphTooltip/index.js +3 -1
- package/dist/GraphTooltipArrow/Content.d.ts +2 -1
- package/dist/GraphTooltipArrow/Content.js +6 -4
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +2 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +9 -7
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +3 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +7 -1
- package/package.json +2 -2
- package/src/GraphTooltip/index.tsx +3 -1
- package/src/GraphTooltipArrow/Content.tsx +9 -5
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +11 -3
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +3 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +12 -1
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.220 (2023-08-09)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## 0.16.219 (2023-08-07)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## 0.16.218 (2023-08-02)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -109,7 +109,9 @@ export const GraphTooltipContent = ({ hoveringNode, unit, total, totalUnfiltered
|
|
|
109
109
|
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
110
110
|
};
|
|
111
111
|
const hoveringNodeCumulative = hoveringNode.cumulative;
|
|
112
|
-
|
|
112
|
+
// '?? 0n' is needed because because diff is undefined in callgraph for some nodes,
|
|
113
|
+
// even though the type doesn't allow it.
|
|
114
|
+
const diff = hoveringNode.diff ?? 0n;
|
|
113
115
|
const prevValue = hoveringNodeCumulative - diff;
|
|
114
116
|
const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
|
|
115
117
|
const diffSign = diff > 0 ? '+' : '';
|
|
@@ -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,
|
|
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:
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.220",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@parca/client": "^0.16.81",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"access": "public",
|
|
48
48
|
"registry": "https://registry.npmjs.org/"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "bde0102b8a66edcad07f909f42611ddaa0796605"
|
|
51
51
|
}
|
|
@@ -276,7 +276,9 @@ export const GraphTooltipContent = ({
|
|
|
276
276
|
};
|
|
277
277
|
|
|
278
278
|
const hoveringNodeCumulative = hoveringNode.cumulative;
|
|
279
|
-
|
|
279
|
+
// '?? 0n' is needed because because diff is undefined in callgraph for some nodes,
|
|
280
|
+
// even though the type doesn't allow it.
|
|
281
|
+
const diff = hoveringNode.diff ?? 0n;
|
|
280
282
|
const prevValue = hoveringNodeCumulative - diff;
|
|
281
283
|
const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
|
|
282
284
|
const diffSign = diff > 0 ? '+' : '';
|
|
@@ -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
|
-
{
|
|
107
|
-
<CopyToClipboard onCopy={onCopy} text={
|
|
108
|
-
<button className="cursor-pointer text-left">{
|
|
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(
|
|
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
|
|