@parca/profile 0.16.360 → 0.16.361

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,10 @@
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.361](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.360...@parca/profile@0.16.361) (2024-04-26)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## 0.16.360 (2024-04-25)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -20,12 +20,13 @@ const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, le
20
20
  if (graphTooltipData === null) {
21
21
  return _jsx(_Fragment, {});
22
22
  }
23
- const { name, locationAddress, cumulativeText, diffText, diff, row: rowNumber } = graphTooltipData;
23
+ const { name, locationAddress, cumulativeText, cumulativePerSecondText, diffText, diff, row: rowNumber, } = graphTooltipData;
24
+ const delta = unit === 'nanoseconds';
24
25
  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-start justify-between gap-4 break-all font-semibold", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx("p", { children: name !== ''
25
26
  ? name
26
27
  : locationAddress !== 0n
27
28
  ? hexifyAddress(locationAddress)
28
- : '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("div", { children: cumulativeText }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx("div", { children: diffText }) })] })), _jsx(TooltipMetaInfo, { table: table, row: rowNumber, navigateTo: navigateTo })] }) })] }) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to show context menu" })] })] }) }) }));
29
+ : 'unknown' })) }), _jsx("table", { className: "my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300", children: _jsxs("tbody", { children: [delta ? (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Per Second" }), _jsx("td", { className: "w-3/4", children: _jsx("div", { children: cumulativePerSecondText }) })] })) : (_jsx(_Fragment, {})), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Cumulative" }), _jsx("td", { className: "w-3/4", children: _jsx("div", { children: cumulativeText }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx("div", { children: diffText }) })] })), _jsx(TooltipMetaInfo, { table: table, row: rowNumber, navigateTo: navigateTo })] }) })] }) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to show context menu" })] })] }) }) }));
29
30
  };
30
31
  const TooltipMetaInfo = ({ table, row, navigateTo, }) => {
31
32
  const { labelPairs, functionFilename, file, locationAddress, mappingFile, mappingBuildID, inlined, } = useGraphTooltipMetaInfo({ table, row, navigateTo });
@@ -11,6 +11,7 @@ interface GraphTooltipData {
11
11
  name: string;
12
12
  locationAddress: bigint;
13
13
  cumulativeText: string;
14
+ cumulativePerSecondText: string;
14
15
  diffText: string;
15
16
  diff: bigint;
16
17
  row: number;
@@ -11,8 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { divide, valueFormatter } from '@parca/utilities';
14
- import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_LOCATION_ADDRESS, } from '../../ProfileIcicleGraph/IcicleGraphArrow';
15
- import { getTextForCumulative, nodeLabel } from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
14
+ import { FIELD_CUMULATIVE, FIELD_CUMULATIVE_PER_SECOND, FIELD_DIFF, FIELD_DIFF_PER_SECOND, FIELD_LOCATION_ADDRESS, } from '../../ProfileIcicleGraph/IcicleGraphArrow';
15
+ import { getTextForCumulative, getTextForCumulativePerSecond, nodeLabel, } from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
16
16
  export const useGraphTooltip = ({ table, unit, total, totalUnfiltered, row, level, }) => {
17
17
  if (row === null) {
18
18
  return null;
@@ -21,20 +21,39 @@ export const useGraphTooltip = ({ table, unit, total, totalUnfiltered, row, leve
21
21
  const cumulative = table.getChild(FIELD_CUMULATIVE)?.get(row) !== null
22
22
  ? BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row))
23
23
  : 0n;
24
+ const cumulativePerSecond = table.getChild(FIELD_CUMULATIVE_PER_SECOND)?.get(row) !== null
25
+ ? table.getChild(FIELD_CUMULATIVE_PER_SECOND)?.get(row)
26
+ : 0;
24
27
  const diff = table.getChild(FIELD_DIFF)?.get(row) !== null
25
28
  ? BigInt(table.getChild(FIELD_DIFF)?.get(row))
26
29
  : 0n;
27
- const prevValue = cumulative - diff;
28
- const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
29
- const diffSign = diff > 0 ? '+' : '';
30
- const diffValueText = diffSign + valueFormatter(diff, unit, 1);
31
- const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
32
- const diffText = `${diffValueText} (${diffPercentageText})`;
30
+ const diffPerSecond = table.getChild(FIELD_DIFF_PER_SECOND)?.get(row) !== null
31
+ ? table.getChild(FIELD_DIFF_PER_SECOND)?.get(row)
32
+ : 0;
33
+ const delta = unit === 'nanoseconds';
34
+ let diffText = '';
35
+ if (delta) {
36
+ const prevValue = cumulativePerSecond - diffPerSecond;
37
+ const diffRatio = diffPerSecond !== 0 ? diffPerSecond / prevValue : 0;
38
+ const diffSign = diffPerSecond > 0 ? '+' : '';
39
+ const diffValueText = diffSign + valueFormatter(diffPerSecond, 'CPU Cores', 5);
40
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
41
+ diffText = `${diffValueText} (${diffPercentageText})`;
42
+ }
43
+ else {
44
+ const prevValue = cumulative - diff;
45
+ const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
46
+ const diffSign = diff > 0 ? '+' : '';
47
+ const diffValueText = diffSign + valueFormatter(diff, unit, 1);
48
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
49
+ diffText = `${diffValueText} (${diffPercentageText})`;
50
+ }
33
51
  const name = nodeLabel(table, row, level, false);
34
52
  return {
35
53
  name,
36
54
  locationAddress,
37
55
  cumulativeText: getTextForCumulative(cumulative, totalUnfiltered, total, unit),
56
+ cumulativePerSecondText: getTextForCumulativePerSecond(cumulativePerSecond, unit),
38
57
  diffText,
39
58
  diff,
40
59
  row,
@@ -22,6 +22,7 @@ interface IcicleGraphNodesProps {
22
22
  sortBy: string;
23
23
  darkMode: boolean;
24
24
  compareMode: boolean;
25
+ delta: boolean;
25
26
  isContextMenuOpen: boolean;
26
27
  hoveringName: string | null;
27
28
  setHoveringName: (name: string | null) => void;
@@ -54,6 +55,7 @@ interface IcicleNodeProps {
54
55
  sortBy: string;
55
56
  darkMode: boolean;
56
57
  compareMode: boolean;
58
+ delta: boolean;
57
59
  isContextMenuOpen: boolean;
58
60
  hoveringName: string | null;
59
61
  setHoveringName: (name: string | null) => void;
@@ -16,11 +16,11 @@ import cx from 'classnames';
16
16
  import { selectBinaries, useAppSelector } from '@parca/store';
17
17
  import { isSearchMatch, scaleLinear } from '@parca/utilities';
18
18
  import 'react-contexify/dist/ReactContexify.css';
19
- import { FIELD_CHILDREN, FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_NAME, FIELD_MAPPING_FILE, } from './index';
19
+ import { FIELD_CHILDREN, FIELD_CUMULATIVE, FIELD_CUMULATIVE_PER_SECOND, FIELD_DIFF, FIELD_DIFF_PER_SECOND, FIELD_FUNCTION_NAME, FIELD_MAPPING_FILE, } from './index';
20
20
  import useNodeColor from './useNodeColor';
21
21
  import { arrowToString, 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, setHoveringLevel, curPath, sortBy, searchString, darkMode, compareMode, isContextMenuOpen, hoveringName, setHoveringName, hoveringRow, colorForSimilarNodes, highlightSimilarStacksPreference, }) {
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, delta, isContextMenuOpen, hoveringName, setHoveringName, hoveringRow, colorForSimilarNodes, highlightSimilarStacksPreference, }) {
24
24
  const cumulatives = table.getChild(FIELD_CUMULATIVE);
25
25
  if (childRows === undefined || childRows.length === 0) {
26
26
  return _jsx(_Fragment, {});
@@ -35,7 +35,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({ tab
35
35
  const xStart = Math.floor(xScale(BigInt(childrenCumulative)));
36
36
  const c = BigInt(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, setHoveringLevel: setHoveringLevel, level: level, curPath: curPath, total: total, xScale: xScale, sortBy: sortBy, searchString: searchString, darkMode: darkMode, compareMode: compareMode, isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }, `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, delta: delta, isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }, `node-${level}-${i}`));
39
39
  });
40
40
  return _jsx("g", { transform: `translate(${x}, ${y})`, children: childrenElements });
41
41
  });
@@ -48,17 +48,21 @@ 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, setHoveringLevel, sortBy, darkMode, compareMode, isContextMenuOpen, hoveringName, setHoveringName, hoveringRow, colorForSimilarNodes, highlightSimilarStacksPreference, }) {
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, delta, isContextMenuOpen, hoveringName, setHoveringName, hoveringRow, colorForSimilarNodes, highlightSimilarStacksPreference, }) {
52
52
  // get the columns to read from
53
53
  const mappingColumn = table.getChild(FIELD_MAPPING_FILE);
54
54
  const functionNameColumn = table.getChild(FIELD_FUNCTION_NAME);
55
55
  const cumulativeColumn = table.getChild(FIELD_CUMULATIVE);
56
+ const cumulativePerSecondColumn = table.getChild(FIELD_CUMULATIVE_PER_SECOND);
56
57
  const diffColumn = table.getChild(FIELD_DIFF);
58
+ const diffPerSecondColumn = table.getChild(FIELD_DIFF_PER_SECOND);
57
59
  // get the actual values from the columns
58
60
  const mappingFile = arrowToString(mappingColumn?.get(row));
59
61
  const functionName = arrowToString(functionNameColumn?.get(row));
60
62
  const cumulative = cumulativeColumn?.get(row) !== null ? BigInt(cumulativeColumn?.get(row)) : 0n;
63
+ const cumulativePerSecond = cumulativePerSecondColumn?.get(row) != null ? cumulativePerSecondColumn.get(row) : 0;
61
64
  const diff = diffColumn?.get(row) !== null ? BigInt(diffColumn?.get(row)) : null;
65
+ const diffPerSecond = diffPerSecondColumn?.get(row) != null ? diffPerSecondColumn.get(row) : null;
62
66
  const childRows = Array.from(table.getChild(FIELD_CHILDREN)?.get(row) ?? []);
63
67
  const highlightedNodes = useMemo(() => {
64
68
  if (!highlightSimilarStacksPreference) {
@@ -93,6 +97,14 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
93
97
  });
94
98
  break;
95
99
  case FIELD_CUMULATIVE:
100
+ if (delta) {
101
+ childRows.sort((a, b) => {
102
+ const aCumulativePerSecond = cumulativePerSecondColumn?.get(a);
103
+ const bCumulativePerSecond = cumulativePerSecondColumn?.get(b);
104
+ return bCumulativePerSecond - aCumulativePerSecond;
105
+ });
106
+ break;
107
+ }
96
108
  childRows.sort((a, b) => {
97
109
  const aCumulative = cumulativeColumn?.get(a);
98
110
  const bCumulative = cumulativeColumn?.get(b);
@@ -101,15 +113,54 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
101
113
  break;
102
114
  case FIELD_DIFF:
103
115
  childRows.sort((a, b) => {
116
+ if (delta) {
117
+ let aRatio = null;
118
+ let bRatio = null;
119
+ const aDiff = diffPerSecondColumn?.get(a);
120
+ if (aDiff !== null) {
121
+ const cumulative = cumulativePerSecondColumn?.get(a);
122
+ const prev = cumulative - aDiff;
123
+ aRatio = aDiff / prev;
124
+ }
125
+ const bDiff = diffPerSecondColumn?.get(b);
126
+ if (bDiff !== null) {
127
+ const cumulative = cumulativePerSecondColumn?.get(b);
128
+ const prev = cumulative - bDiff;
129
+ bRatio = bDiff / prev;
130
+ }
131
+ if (aRatio !== null && bRatio !== null) {
132
+ return bRatio - aRatio;
133
+ }
134
+ if (aRatio === null && bRatio !== null) {
135
+ return -1;
136
+ }
137
+ if (aRatio !== null && bRatio === null) {
138
+ return 1;
139
+ }
140
+ // both are null
141
+ return 0;
142
+ }
143
+ let aRatio = null;
104
144
  const aDiff = diffColumn?.get(a);
145
+ if (aDiff !== null) {
146
+ const cumulative = cumulativeColumn?.get(a) ?? 0n;
147
+ const prev = cumulative - aDiff;
148
+ aRatio = Number(aDiff) / Number(prev);
149
+ }
150
+ let bRatio = null;
105
151
  const bDiff = diffColumn?.get(b);
106
- if (aDiff !== null && bDiff !== null) {
107
- return Number(bDiff - aDiff);
152
+ if (bDiff !== null) {
153
+ const cumulative = cumulativeColumn?.get(b) ?? 0n;
154
+ const prev = cumulative - bDiff;
155
+ bRatio = Number(bDiff) / Number(prev);
156
+ }
157
+ if (aRatio !== null && bRatio !== null) {
158
+ return bRatio - aRatio;
108
159
  }
109
- if (aDiff === null && bDiff !== null) {
160
+ if (aRatio === null && bRatio !== null) {
110
161
  return -1;
111
162
  }
112
- if (aDiff !== null && bDiff === null) {
163
+ if (aRatio !== null && bRatio === null) {
113
164
  return 1;
114
165
  }
115
166
  // both are null
@@ -122,7 +173,9 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
122
173
  isDarkMode: darkMode,
123
174
  compareMode,
124
175
  cumulative,
176
+ cumulativePerSecond,
125
177
  diff,
178
+ diffPerSecond,
126
179
  mappingColors,
127
180
  mappingFile,
128
181
  functionName,
@@ -172,5 +225,5 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
172
225
  ? `${colorForSimilarNodes} stroke-[3] [stroke-dasharray:6,4] [stroke-linecap:round] [stroke-linejoin:round] h-6`
173
226
  : 'stroke-white dark:stroke-gray-700', {
174
227
  'opacity-50': isHighlightEnabled && !isHighlighted,
175
- }) }), 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, isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }))] }));
228
+ }) }), 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, delta: delta, compareMode: compareMode, isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }))] }));
176
229
  });
@@ -14,7 +14,9 @@ export declare const FIELD_FUNCTION_START_LINE = "function_startline";
14
14
  export declare const FIELD_CHILDREN = "children";
15
15
  export declare const FIELD_LABELS = "labels";
16
16
  export declare const FIELD_CUMULATIVE = "cumulative";
17
+ export declare const FIELD_CUMULATIVE_PER_SECOND = "cumulative_per_second";
17
18
  export declare const FIELD_DIFF = "diff";
19
+ export declare const FIELD_DIFF_PER_SECOND = "diff_per_second";
18
20
  interface IcicleGraphArrowProps {
19
21
  arrow: FlamegraphArrow;
20
22
  total: bigint;
@@ -38,7 +38,9 @@ export const FIELD_FUNCTION_START_LINE = 'function_startline';
38
38
  export const FIELD_CHILDREN = 'children';
39
39
  export const FIELD_LABELS = 'labels';
40
40
  export const FIELD_CUMULATIVE = 'cumulative';
41
+ export const FIELD_CUMULATIVE_PER_SECOND = 'cumulative_per_second';
41
42
  export const FIELD_DIFF = 'diff';
43
+ export const FIELD_DIFF_PER_SECOND = 'diff_per_second';
42
44
  export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, sampleUnit, navigateTo, sortBy, }) {
43
45
  const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
44
46
  const dispatch = useAppDispatch();
@@ -139,13 +141,14 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, f
139
141
  };
140
142
  // useMemo for the root graph as it otherwise renders the whole graph if the hoveringRow changes.
141
143
  const root = useMemo(() => {
142
- return (_jsx("svg", { className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg, onContextMenu: displayMenu, 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, isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }) }) }) }));
144
+ return (_jsx("svg", { className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg, onContextMenu: displayMenu, 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, delta: sampleUnit === 'nanoseconds', isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }) }) }) }));
143
145
  }, [
144
146
  compareMode,
145
147
  curPath,
146
148
  currentSearchString,
147
149
  height,
148
150
  isDarkMode,
151
+ sampleUnit,
149
152
  mappingColors,
150
153
  setCurPath,
151
154
  sortBy,
@@ -5,10 +5,12 @@ interface Props {
5
5
  isDarkMode: boolean;
6
6
  compareMode: boolean;
7
7
  cumulative: bigint;
8
+ cumulativePerSecond: number | null;
8
9
  diff: bigint | null;
10
+ diffPerSecond: number | null;
9
11
  mappingColors: mappingColors;
10
12
  functionName: string | null;
11
13
  mappingFile: string | null;
12
14
  }
13
- declare const useNodeColor: ({ isDarkMode, compareMode, cumulative, diff, mappingColors, functionName, mappingFile, }: Props) => string;
15
+ declare const useNodeColor: ({ isDarkMode, compareMode, cumulative, cumulativePerSecond, diff, diffPerSecond, mappingColors, functionName, mappingFile, }: Props) => string;
14
16
  export default useNodeColor;
@@ -11,9 +11,12 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { EVERYTHING_ELSE } from '@parca/store';
14
- import { diffColor, getLastItem } from '@parca/utilities';
15
- const useNodeColor = ({ isDarkMode, compareMode, cumulative, diff, mappingColors, functionName, mappingFile, }) => {
14
+ import { diffColor, diffColorPerSecond, getLastItem } from '@parca/utilities';
15
+ const useNodeColor = ({ isDarkMode, compareMode, cumulative, cumulativePerSecond, diff, diffPerSecond, mappingColors, functionName, mappingFile, }) => {
16
16
  if (compareMode) {
17
+ if (cumulativePerSecond !== null && diffPerSecond !== null) {
18
+ return diffColorPerSecond(diffPerSecond, cumulativePerSecond, isDarkMode);
19
+ }
17
20
  return diffColor(diff ?? 0n, cumulative, isDarkMode);
18
21
  }
19
22
  // To get the color we first check if the function name starts with 'runtime'.
@@ -3,4 +3,5 @@ import { type Feature } from '@parca/store';
3
3
  export declare function nodeLabel(table: Table<any>, row: number, level: number, showBinaryName: boolean): string;
4
4
  export declare const extractFeature: (mapping: string) => Feature;
5
5
  export declare const getTextForCumulative: (hoveringNodeCumulative: bigint, totalUnfiltered: bigint, total: bigint, unit: string) => string;
6
+ export declare const getTextForCumulativePerSecond: (hoveringNodeCumulative: number, unit: string) => string;
6
7
  export declare const arrowToString: (buffer: any) => string | null;
@@ -60,6 +60,9 @@ export const getTextForCumulative = (hoveringNodeCumulative, totalUnfiltered, to
60
60
  return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
61
61
  (${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
62
62
  };
63
+ export const getTextForCumulativePerSecond = (hoveringNodeCumulative, unit) => {
64
+ return `${valueFormatter(hoveringNodeCumulative, unit === 'nanoseconds' ? 'CPU Cores' : unit, 5)}`;
65
+ };
63
66
  export const arrowToString = (buffer) => {
64
67
  if (buffer == null || typeof buffer === 'string') {
65
68
  return buffer;
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.360",
3
+ "version": "0.16.361",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@parca/client": "^0.16.107",
7
- "@parca/components": "^0.16.266",
7
+ "@parca/components": "^0.16.267",
8
8
  "@parca/dynamicsize": "^0.16.61",
9
- "@parca/hooks": "^0.0.47",
9
+ "@parca/hooks": "^0.0.48",
10
10
  "@parca/parser": "^0.16.69",
11
- "@parca/store": "^0.16.136",
12
- "@parca/utilities": "^0.0.64",
11
+ "@parca/store": "^0.16.137",
12
+ "@parca/utilities": "^0.0.65",
13
13
  "@tanstack/react-query": "^4.0.5",
14
14
  "@types/react-beautiful-dnd": "^13.1.8",
15
15
  "apache-arrow": "^12.0.0",
@@ -50,5 +50,5 @@
50
50
  "access": "public",
51
51
  "registry": "https://registry.npmjs.org/"
52
52
  },
53
- "gitHead": "6430a8027b0edb84336feea5c774e409595f40a6"
53
+ "gitHead": "d900cb1de5375362e3e211809c98990a6e686140"
54
54
  }
@@ -61,7 +61,17 @@ const GraphTooltipArrowContent = ({
61
61
  return <></>;
62
62
  }
63
63
 
64
- const {name, locationAddress, cumulativeText, diffText, diff, row: rowNumber} = graphTooltipData;
64
+ const {
65
+ name,
66
+ locationAddress,
67
+ cumulativeText,
68
+ cumulativePerSecondText,
69
+ diffText,
70
+ diff,
71
+ row: rowNumber,
72
+ } = graphTooltipData;
73
+
74
+ const delta = unit === 'nanoseconds';
65
75
 
66
76
  return (
67
77
  <div className={`flex text-sm ${isFixed ? 'w-full' : ''}`}>
@@ -84,6 +94,16 @@ const GraphTooltipArrowContent = ({
84
94
  </div>
85
95
  <table className="my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300">
86
96
  <tbody>
97
+ {delta ? (
98
+ <tr>
99
+ <td className="w-1/4">Per Second</td>
100
+ <td className="w-3/4">
101
+ <div>{cumulativePerSecondText}</div>
102
+ </td>
103
+ </tr>
104
+ ) : (
105
+ <></>
106
+ )}
87
107
  <tr>
88
108
  <td className="w-1/4">Cumulative</td>
89
109
 
@@ -17,10 +17,16 @@ import {divide, valueFormatter} from '@parca/utilities';
17
17
 
18
18
  import {
19
19
  FIELD_CUMULATIVE,
20
+ FIELD_CUMULATIVE_PER_SECOND,
20
21
  FIELD_DIFF,
22
+ FIELD_DIFF_PER_SECOND,
21
23
  FIELD_LOCATION_ADDRESS,
22
24
  } from '../../ProfileIcicleGraph/IcicleGraphArrow';
23
- import {getTextForCumulative, nodeLabel} from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
25
+ import {
26
+ getTextForCumulative,
27
+ getTextForCumulativePerSecond,
28
+ nodeLabel,
29
+ } from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
24
30
 
25
31
  interface Props {
26
32
  table: Table<any>;
@@ -35,6 +41,7 @@ interface GraphTooltipData {
35
41
  name: string;
36
42
  locationAddress: bigint;
37
43
  cumulativeText: string;
44
+ cumulativePerSecondText: string;
38
45
  diffText: string;
39
46
  diff: bigint;
40
47
  row: number;
@@ -58,17 +65,37 @@ export const useGraphTooltip = ({
58
65
  table.getChild(FIELD_CUMULATIVE)?.get(row) !== null
59
66
  ? BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row))
60
67
  : 0n;
68
+ const cumulativePerSecond: number =
69
+ table.getChild(FIELD_CUMULATIVE_PER_SECOND)?.get(row) !== null
70
+ ? table.getChild(FIELD_CUMULATIVE_PER_SECOND)?.get(row)
71
+ : 0;
61
72
  const diff: bigint =
62
73
  table.getChild(FIELD_DIFF)?.get(row) !== null
63
74
  ? BigInt(table.getChild(FIELD_DIFF)?.get(row))
64
75
  : 0n;
76
+ const diffPerSecond: number =
77
+ table.getChild(FIELD_DIFF_PER_SECOND)?.get(row) !== null
78
+ ? table.getChild(FIELD_DIFF_PER_SECOND)?.get(row)
79
+ : 0;
80
+
81
+ const delta = unit === 'nanoseconds';
65
82
 
66
- const prevValue = cumulative - diff;
67
- const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
68
- const diffSign = diff > 0 ? '+' : '';
69
- const diffValueText = diffSign + valueFormatter(diff, unit, 1);
70
- const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
71
- const diffText = `${diffValueText} (${diffPercentageText})`;
83
+ let diffText = '';
84
+ if (delta) {
85
+ const prevValue = cumulativePerSecond - diffPerSecond;
86
+ const diffRatio = diffPerSecond !== 0 ? diffPerSecond / prevValue : 0;
87
+ const diffSign = diffPerSecond > 0 ? '+' : '';
88
+ const diffValueText = diffSign + valueFormatter(diffPerSecond, 'CPU Cores', 5);
89
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
90
+ diffText = `${diffValueText} (${diffPercentageText})`;
91
+ } else {
92
+ const prevValue = cumulative - diff;
93
+ const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
94
+ const diffSign = diff > 0 ? '+' : '';
95
+ const diffValueText = diffSign + valueFormatter(diff, unit, 1);
96
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
97
+ diffText = `${diffValueText} (${diffPercentageText})`;
98
+ }
72
99
 
73
100
  const name = nodeLabel(table, row, level, false);
74
101
 
@@ -76,6 +103,7 @@ export const useGraphTooltip = ({
76
103
  name,
77
104
  locationAddress,
78
105
  cumulativeText: getTextForCumulative(cumulative, totalUnfiltered, total, unit),
106
+ cumulativePerSecondText: getTextForCumulativePerSecond(cumulativePerSecond, unit),
79
107
  diffText,
80
108
  diff,
81
109
  row,
@@ -24,7 +24,9 @@ import 'react-contexify/dist/ReactContexify.css';
24
24
  import {
25
25
  FIELD_CHILDREN,
26
26
  FIELD_CUMULATIVE,
27
+ FIELD_CUMULATIVE_PER_SECOND,
27
28
  FIELD_DIFF,
29
+ FIELD_DIFF_PER_SECOND,
28
30
  FIELD_FUNCTION_NAME,
29
31
  FIELD_MAPPING_FILE,
30
32
  } from './index';
@@ -53,6 +55,7 @@ interface IcicleGraphNodesProps {
53
55
  sortBy: string;
54
56
  darkMode: boolean;
55
57
  compareMode: boolean;
58
+ delta: boolean;
56
59
  isContextMenuOpen: boolean;
57
60
  hoveringName: string | null;
58
61
  setHoveringName: (name: string | null) => void;
@@ -80,6 +83,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
80
83
  searchString,
81
84
  darkMode,
82
85
  compareMode,
86
+ delta,
83
87
  isContextMenuOpen,
84
88
  hoveringName,
85
89
  setHoveringName,
@@ -127,6 +131,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
127
131
  searchString={searchString}
128
132
  darkMode={darkMode}
129
133
  compareMode={compareMode}
134
+ delta={delta}
130
135
  isContextMenuOpen={isContextMenuOpen}
131
136
  hoveringName={hoveringName}
132
137
  setHoveringName={setHoveringName}
@@ -165,6 +170,7 @@ interface IcicleNodeProps {
165
170
  sortBy: string;
166
171
  darkMode: boolean;
167
172
  compareMode: boolean;
173
+ delta: boolean;
168
174
  isContextMenuOpen: boolean;
169
175
  hoveringName: string | null;
170
176
  setHoveringName: (name: string | null) => void;
@@ -204,6 +210,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
204
210
  sortBy,
205
211
  darkMode,
206
212
  compareMode,
213
+ delta,
207
214
  isContextMenuOpen,
208
215
  hoveringName,
209
216
  setHoveringName,
@@ -215,12 +222,18 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
215
222
  const mappingColumn = table.getChild(FIELD_MAPPING_FILE);
216
223
  const functionNameColumn = table.getChild(FIELD_FUNCTION_NAME);
217
224
  const cumulativeColumn = table.getChild(FIELD_CUMULATIVE);
225
+ const cumulativePerSecondColumn = table.getChild(FIELD_CUMULATIVE_PER_SECOND);
218
226
  const diffColumn = table.getChild(FIELD_DIFF);
227
+ const diffPerSecondColumn = table.getChild(FIELD_DIFF_PER_SECOND);
219
228
  // get the actual values from the columns
220
229
  const mappingFile: string | null = arrowToString(mappingColumn?.get(row));
221
230
  const functionName: string | null = arrowToString(functionNameColumn?.get(row));
222
231
  const cumulative = cumulativeColumn?.get(row) !== null ? BigInt(cumulativeColumn?.get(row)) : 0n;
232
+ const cumulativePerSecond: number | null =
233
+ cumulativePerSecondColumn?.get(row) != null ? cumulativePerSecondColumn.get(row) : 0;
223
234
  const diff: bigint | null = diffColumn?.get(row) !== null ? BigInt(diffColumn?.get(row)) : null;
235
+ const diffPerSecond: number | null =
236
+ diffPerSecondColumn?.get(row) != null ? diffPerSecondColumn.get(row) : null;
224
237
  const childRows: number[] = Array.from(table.getChild(FIELD_CHILDREN)?.get(row) ?? []);
225
238
 
226
239
  const highlightedNodes = useMemo(() => {
@@ -259,6 +272,15 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
259
272
  });
260
273
  break;
261
274
  case FIELD_CUMULATIVE:
275
+ if (delta) {
276
+ childRows.sort((a, b) => {
277
+ const aCumulativePerSecond = cumulativePerSecondColumn?.get(a);
278
+ const bCumulativePerSecond = cumulativePerSecondColumn?.get(b);
279
+ return bCumulativePerSecond - aCumulativePerSecond;
280
+ });
281
+ break;
282
+ }
283
+
262
284
  childRows.sort((a, b) => {
263
285
  const aCumulative: bigint = cumulativeColumn?.get(a);
264
286
  const bCumulative: bigint = cumulativeColumn?.get(b);
@@ -267,15 +289,59 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
267
289
  break;
268
290
  case FIELD_DIFF:
269
291
  childRows.sort((a, b) => {
292
+ if (delta) {
293
+ let aRatio: number | null = null;
294
+ let bRatio: number | null = null;
295
+
296
+ const aDiff: number | null = diffPerSecondColumn?.get(a);
297
+ if (aDiff !== null) {
298
+ const cumulative: number = cumulativePerSecondColumn?.get(a);
299
+ const prev = cumulative - aDiff;
300
+ aRatio = aDiff / prev;
301
+ }
302
+
303
+ const bDiff: number | null = diffPerSecondColumn?.get(b);
304
+ if (bDiff !== null) {
305
+ const cumulative: number = cumulativePerSecondColumn?.get(b);
306
+ const prev = cumulative - bDiff;
307
+ bRatio = bDiff / prev;
308
+ }
309
+
310
+ if (aRatio !== null && bRatio !== null) {
311
+ return bRatio - aRatio;
312
+ }
313
+ if (aRatio === null && bRatio !== null) {
314
+ return -1;
315
+ }
316
+ if (aRatio !== null && bRatio === null) {
317
+ return 1;
318
+ }
319
+ // both are null
320
+ return 0;
321
+ }
322
+
323
+ let aRatio: number | null = null;
270
324
  const aDiff: bigint | null = diffColumn?.get(a);
325
+ if (aDiff !== null) {
326
+ const cumulative: bigint = cumulativeColumn?.get(a) ?? 0n;
327
+ const prev: bigint = cumulative - aDiff;
328
+ aRatio = Number(aDiff) / Number(prev);
329
+ }
330
+ let bRatio: number | null = null;
271
331
  const bDiff: bigint | null = diffColumn?.get(b);
272
- if (aDiff !== null && bDiff !== null) {
273
- return Number(bDiff - aDiff);
332
+ if (bDiff !== null) {
333
+ const cumulative: bigint = cumulativeColumn?.get(b) ?? 0n;
334
+ const prev: bigint = cumulative - bDiff;
335
+ bRatio = Number(bDiff) / Number(prev);
336
+ }
337
+
338
+ if (aRatio !== null && bRatio !== null) {
339
+ return bRatio - aRatio;
274
340
  }
275
- if (aDiff === null && bDiff !== null) {
341
+ if (aRatio === null && bRatio !== null) {
276
342
  return -1;
277
343
  }
278
- if (aDiff !== null && bDiff === null) {
344
+ if (aRatio !== null && bRatio === null) {
279
345
  return 1;
280
346
  }
281
347
  // both are null
@@ -289,7 +355,9 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
289
355
  isDarkMode: darkMode,
290
356
  compareMode,
291
357
  cumulative,
358
+ cumulativePerSecond,
292
359
  diff,
360
+ diffPerSecond,
293
361
  mappingColors,
294
362
  mappingFile,
295
363
  functionName,
@@ -393,6 +461,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
393
461
  searchString={searchString}
394
462
  sortBy={sortBy}
395
463
  darkMode={darkMode}
464
+ delta={delta}
396
465
  compareMode={compareMode}
397
466
  isContextMenuOpen={isContextMenuOpen}
398
467
  hoveringName={hoveringName}
@@ -49,7 +49,9 @@ export const FIELD_FUNCTION_START_LINE = 'function_startline';
49
49
  export const FIELD_CHILDREN = 'children';
50
50
  export const FIELD_LABELS = 'labels';
51
51
  export const FIELD_CUMULATIVE = 'cumulative';
52
+ export const FIELD_CUMULATIVE_PER_SECOND = 'cumulative_per_second';
52
53
  export const FIELD_DIFF = 'diff';
54
+ export const FIELD_DIFF_PER_SECOND = 'diff_per_second';
53
55
 
54
56
  interface IcicleGraphArrowProps {
55
57
  arrow: FlamegraphArrow;
@@ -229,6 +231,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
229
231
  sortBy={sortBy}
230
232
  darkMode={isDarkMode}
231
233
  compareMode={compareMode}
234
+ delta={sampleUnit === 'nanoseconds'}
232
235
  isContextMenuOpen={isContextMenuOpen}
233
236
  hoveringName={hoveringName}
234
237
  setHoveringName={setHoveringName}
@@ -246,6 +249,7 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
246
249
  currentSearchString,
247
250
  height,
248
251
  isDarkMode,
252
+ sampleUnit,
249
253
  mappingColors,
250
254
  setCurPath,
251
255
  sortBy,
@@ -12,7 +12,7 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {EVERYTHING_ELSE} from '@parca/store';
15
- import {diffColor, getLastItem} from '@parca/utilities';
15
+ import {diffColor, diffColorPerSecond, getLastItem} from '@parca/utilities';
16
16
 
17
17
  interface mappingColors {
18
18
  [key: string]: string;
@@ -22,7 +22,9 @@ interface Props {
22
22
  isDarkMode: boolean;
23
23
  compareMode: boolean;
24
24
  cumulative: bigint;
25
+ cumulativePerSecond: number | null;
25
26
  diff: bigint | null;
27
+ diffPerSecond: number | null;
26
28
  mappingColors: mappingColors;
27
29
  functionName: string | null;
28
30
  mappingFile: string | null;
@@ -32,12 +34,18 @@ const useNodeColor = ({
32
34
  isDarkMode,
33
35
  compareMode,
34
36
  cumulative,
37
+ cumulativePerSecond,
35
38
  diff,
39
+ diffPerSecond,
36
40
  mappingColors,
37
41
  functionName,
38
42
  mappingFile,
39
43
  }: Props): string => {
40
44
  if (compareMode) {
45
+ if (cumulativePerSecond !== null && diffPerSecond !== null) {
46
+ return diffColorPerSecond(diffPerSecond, cumulativePerSecond, isDarkMode);
47
+ }
48
+
41
49
  return diffColor(diff ?? 0n, cumulative, isDarkMode);
42
50
  }
43
51
 
@@ -91,6 +91,17 @@ export const getTextForCumulative = (
91
91
  (${(100 * divide(hoveringNodeCumulative, totalUnfiltered)).toFixed(2)}%${filtered})`;
92
92
  };
93
93
 
94
+ export const getTextForCumulativePerSecond = (
95
+ hoveringNodeCumulative: number,
96
+ unit: string
97
+ ): string => {
98
+ return `${valueFormatter(
99
+ hoveringNodeCumulative,
100
+ unit === 'nanoseconds' ? 'CPU Cores' : unit,
101
+ 5
102
+ )}`;
103
+ };
104
+
94
105
  export const arrowToString = (buffer: any): string | null => {
95
106
  if (buffer == null || typeof buffer === 'string') {
96
107
  return buffer;