@parca/profile 0.16.56 → 0.16.57

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.57](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.56...@parca/profile@0.16.57) (2022-11-02)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## 0.16.56 (2022-10-31)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -1,4 +1,5 @@
1
- import { CallgraphNode, FlamegraphRootNode } from '@parca/client';
1
+ import { CallgraphNode, FlamegraphNode, FlamegraphNodeMeta, FlamegraphRootNode } from '@parca/client';
2
+ import { Function, Location, Mapping } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
2
3
  interface GraphTooltipProps {
3
4
  x: number;
4
5
  y: number;
@@ -8,12 +9,16 @@ interface GraphTooltipProps {
8
9
  contextElement: Element | null;
9
10
  isFixed?: boolean;
10
11
  virtualContextElement?: boolean;
12
+ strings?: string[];
13
+ mappings?: Mapping[];
14
+ locations?: Location[];
15
+ functions?: Function[];
11
16
  }
12
- export interface HoveringNode extends CallgraphNode, FlamegraphRootNode {
17
+ export interface HoveringNode extends CallgraphNode, FlamegraphRootNode, FlamegraphNode {
13
18
  diff: string;
14
- meta?: {
19
+ meta?: FlamegraphNodeMeta | {
15
20
  [key: string]: any;
16
21
  };
17
22
  }
18
- declare const GraphTooltip: ({ x, y, unit, total, hoveringNode, contextElement, isFixed, virtualContextElement, }: GraphTooltipProps) => JSX.Element;
23
+ declare const GraphTooltip: ({ x, y, unit, total, hoveringNode, contextElement, isFixed, virtualContextElement, strings, mappings, locations, functions, }: GraphTooltipProps) => JSX.Element;
19
24
  export default GraphTooltip;
@@ -70,9 +70,33 @@ function generateGetBoundingClientRect(contextElement, x, y) {
70
70
  }
71
71
  var TooltipMetaInfo = function (_a) {
72
72
  var _b, _c, _d, _e;
73
- var hoveringNode = _a.hoveringNode, onCopy = _a.onCopy;
73
+ var hoveringNode = _a.hoveringNode, onCopy = _a.onCopy, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
74
74
  if (hoveringNode.meta === undefined)
75
75
  return _jsx(_Fragment, {});
76
+ // populate meta from the flamegraph metadata tables
77
+ if (locations !== undefined) {
78
+ var location_1 = locations[hoveringNode.meta.locationIndex];
79
+ hoveringNode.meta.location = location_1;
80
+ if (mappings !== undefined) {
81
+ var mapping = mappings[location_1.mappingIndex];
82
+ if (strings !== undefined) {
83
+ mapping.file = strings[mapping.fileStringIndex];
84
+ mapping.buildId = strings[mapping.buildIdStringIndex];
85
+ }
86
+ hoveringNode.meta.mapping = mapping;
87
+ }
88
+ location_1.lines.forEach(function (line) {
89
+ if (functions !== undefined && hoveringNode.meta !== undefined) {
90
+ var func = functions[line.functionIndex];
91
+ if (strings !== undefined) {
92
+ func.name = strings[func.nameStringIndex];
93
+ func.systemName = strings[func.systemNameStringIndex];
94
+ func.filename = strings[func.filenameStringIndex];
95
+ }
96
+ hoveringNode.meta.function = func;
97
+ }
98
+ });
99
+ }
76
100
  var getTextForFile = function (hoveringNode) {
77
101
  var _a, _b, _c, _d;
78
102
  if (hoveringNode.meta === undefined)
@@ -91,7 +115,7 @@ var TooltipMetaInfo = function (_a) {
91
115
  };
92
116
  var timeoutHandle = null;
93
117
  var GraphTooltipContent = function (_a) {
94
- var hoveringNode = _a.hoveringNode, unit = _a.unit, total = _a.total, isFixed = _a.isFixed;
118
+ var hoveringNode = _a.hoveringNode, unit = _a.unit, total = _a.total, isFixed = _a.isFixed, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
95
119
  var _b = useState(false), isCopied = _b[0], setIsCopied = _b[1];
96
120
  var onCopy = function () {
97
121
  setIsCopied(true);
@@ -108,7 +132,9 @@ var GraphTooltipContent = function (_a) {
108
132
  var diffValueText = diffSign + valueFormatter(diff, unit, 1);
109
133
  var diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
110
134
  var diffText = "".concat(diffValueText, " (").concat(diffPercentageText, ")");
111
- var metaRows = hoveringNode.meta === undefined ? (_jsx(_Fragment, {})) : (_jsx(TooltipMetaInfo, { onCopy: onCopy, hoveringNode: hoveringNode }));
135
+ var metaRows = hoveringNode.meta === undefined ? (_jsx(_Fragment, {})) : (_jsx(TooltipMetaInfo, { onCopy: onCopy,
136
+ // @ts-expect-error
137
+ hoveringNode: hoveringNode, strings: strings, mappings: mappings, locations: locations, functions: functions }));
112
138
  var getTextForCumulative = function (hoveringNodeCumulative) {
113
139
  return "".concat(valueFormatter(hoveringNodeCumulative, unit, 2), " (\n ").concat(((hoveringNodeCumulative * 100) / total).toFixed(2), "%)");
114
140
  };
@@ -117,7 +143,7 @@ var GraphTooltipContent = function (_a) {
117
143
  parseInt(hoveringNode.meta.location.address, 10) !== 0 ? (_jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: hexifyAddress(hoveringNode.meta.location.address) }, { children: _jsx("button", __assign({ className: "cursor-pointer text-left" }, { children: hexifyAddress(hoveringNode.meta.location.address) })) }))) : (_jsx("p", { children: "unknown" })) })) })) })), _jsx("span", __assign({ className: "text-gray-700 dark:text-gray-300 my-2" }, { children: _jsx("table", __assign({ className: "table-fixed" }, { children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Cumulative" })), _jsx("td", __assign({ className: "w-4/5" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: getTextForCumulative(hoveringNodeCumulative) }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: getTextForCumulative(hoveringNodeCumulative) })) })) }))] }), hoveringNode.diff !== undefined && diff !== 0 && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/5" }, { children: "Diff" })), _jsx("td", __assign({ className: "w-4/5" }, { children: _jsx(CopyToClipboard, __assign({ onCopy: onCopy, text: diffText }, { children: _jsx("button", __assign({ className: "cursor-pointer" }, { children: diffText })) })) }))] })), metaRows] }) })) })), _jsx("span", __assign({ className: "block text-gray-500 text-xs mt-2" }, { children: isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.' }))] })) })) })) })) })));
118
144
  };
119
145
  var GraphTooltip = function (_a) {
120
- var x = _a.x, y = _a.y, unit = _a.unit, total = _a.total, hoveringNode = _a.hoveringNode, contextElement = _a.contextElement, _b = _a.isFixed, isFixed = _b === void 0 ? false : _b, _c = _a.virtualContextElement, virtualContextElement = _c === void 0 ? true : _c;
146
+ var x = _a.x, y = _a.y, unit = _a.unit, total = _a.total, hoveringNode = _a.hoveringNode, contextElement = _a.contextElement, _b = _a.isFixed, isFixed = _b === void 0 ? false : _b, _c = _a.virtualContextElement, virtualContextElement = _c === void 0 ? true : _c, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions;
121
147
  var _d = useState(null), popperElement = _d[0], setPopperElement = _d[1];
122
148
  var _e = usePopper(virtualContextElement ? virtualElement : contextElement, popperElement, {
123
149
  placement: 'auto-start',
@@ -150,6 +176,6 @@ var GraphTooltip = function (_a) {
150
176
  }, [x, y, contextElement, update, isShiftDown]);
151
177
  if (hoveringNode === undefined || hoveringNode == null)
152
178
  return _jsx(_Fragment, {});
153
- return isFixed ? (_jsx(GraphTooltipContent, { hoveringNode: hoveringNode, unit: unit, total: total, isFixed: isFixed })) : (_jsx("div", __assign({ ref: setPopperElement, style: styles.popper }, attributes.popper, { children: _jsx(GraphTooltipContent, { hoveringNode: hoveringNode, unit: unit, total: total, isFixed: isFixed }) })));
179
+ return isFixed ? (_jsx(GraphTooltipContent, { hoveringNode: hoveringNode, unit: unit, total: total, isFixed: isFixed })) : (_jsx("div", __assign({ ref: setPopperElement, style: styles.popper }, attributes.popper, { children: _jsx(GraphTooltipContent, { hoveringNode: hoveringNode, unit: unit, total: total, isFixed: isFixed, strings: strings, mappings: mappings, locations: locations, functions: functions }) })));
154
180
  };
155
181
  export default GraphTooltip;
@@ -1,4 +1,5 @@
1
1
  import { Flamegraph, FlamegraphNode, FlamegraphRootNode } from '@parca/client';
2
+ import { Mapping, Function, Location } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
2
3
  interface IcicleGraphProps {
3
4
  graph: Flamegraph;
4
5
  sampleUnit: string;
@@ -8,6 +9,10 @@ interface IcicleGraphProps {
8
9
  }
9
10
  interface IcicleGraphNodesProps {
10
11
  data: FlamegraphNode[];
12
+ strings: string[];
13
+ mappings: Mapping[];
14
+ locations: Location[];
15
+ functions: Function[];
11
16
  x: number;
12
17
  y: number;
13
18
  total: number;
@@ -21,6 +26,10 @@ interface IcicleGraphNodesProps {
21
26
  }
22
27
  interface IcicleGraphRootNodeProps {
23
28
  node: FlamegraphRootNode;
29
+ strings: string[];
30
+ mappings: Mapping[];
31
+ locations: Location[];
32
+ functions: Function[];
24
33
  xScale: (value: number) => number;
25
34
  total: number;
26
35
  totalWidth: number;
@@ -28,8 +37,8 @@ interface IcicleGraphRootNodeProps {
28
37
  setCurPath: (path: string[]) => void;
29
38
  setHoveringNode: (node: FlamegraphNode | FlamegraphRootNode | undefined) => void;
30
39
  }
31
- export declare function nodeLabel(node: FlamegraphNode): string;
32
- export declare function IcicleGraphNodes({ data, x, y, xScale, total, totalWidth, level, setHoveringNode, path, setCurPath, curPath, }: IcicleGraphNodesProps): JSX.Element;
33
- export declare function IcicleGraphRootNode({ node, xScale, total, totalWidth, setHoveringNode, setCurPath, curPath, }: IcicleGraphRootNodeProps): JSX.Element;
40
+ export declare function nodeLabel(node: FlamegraphNode, strings: string[], mappings: Mapping[], locations: Location[], functions: Function[]): string;
41
+ export declare function IcicleGraphNodes({ data, strings, mappings, locations, functions, x, y, xScale, total, totalWidth, level, setHoveringNode, path, setCurPath, curPath, }: IcicleGraphNodesProps): JSX.Element;
42
+ export declare function IcicleGraphRootNode({ node, strings, mappings, locations, functions, xScale, total, totalWidth, setHoveringNode, setCurPath, curPath, }: IcicleGraphRootNodeProps): JSX.Element;
34
43
  export default function IcicleGraph({ graph, width, setCurPath, curPath, sampleUnit, }: IcicleGraphProps): JSX.Element;
35
44
  export {};
@@ -27,8 +27,8 @@ import { throttle } from 'lodash';
27
27
  import { pointer } from 'd3-selection';
28
28
  import { scaleLinear } from 'd3-scale';
29
29
  import GraphTooltip from './GraphTooltip';
30
- import { getLastItem, diffColor, isSearchMatch } from '@parca/functions';
31
- import { useAppSelector, selectDarkMode, selectSearchNodeString } from '@parca/store';
30
+ import { diffColor, getLastItem, isSearchMatch } from '@parca/functions';
31
+ import { selectDarkMode, selectSearchNodeString, useAppSelector } from '@parca/store';
32
32
  import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
33
33
  import { hexifyAddress } from './utils';
34
34
  var RowHeight = 26;
@@ -55,24 +55,28 @@ function IcicleRect(_a) {
55
55
  fill: color,
56
56
  } }), width > 5 && (_jsx("svg", __assign({ width: width - 5, height: height }, { children: _jsx("text", __assign({ x: 5, y: 15, style: { fontSize: '12px' } }, { children: name })) })))] })));
57
57
  }
58
- export function nodeLabel(node) {
59
- var _a, _b, _c, _d, _e, _f, _g, _h;
60
- if (node.meta === undefined)
58
+ export function nodeLabel(node, strings, mappings, locations, functions) {
59
+ var _a, _b;
60
+ if (((_a = node.meta) === null || _a === void 0 ? void 0 : _a.locationIndex) === undefined)
61
61
  return '<unknown>';
62
- var mapping = "".concat(((_b = (_a = node.meta) === null || _a === void 0 ? void 0 : _a.mapping) === null || _b === void 0 ? void 0 : _b.file) !== undefined && ((_d = (_c = node.meta) === null || _c === void 0 ? void 0 : _c.mapping) === null || _d === void 0 ? void 0 : _d.file) !== ''
63
- ? '[' + ((_e = getLastItem(node.meta.mapping.file)) !== null && _e !== void 0 ? _e : '') + '] '
64
- : '');
65
- if (((_f = node.meta.function) === null || _f === void 0 ? void 0 : _f.name) !== undefined && ((_g = node.meta.function) === null || _g === void 0 ? void 0 : _g.name) !== '')
66
- return mapping + node.meta.function.name;
67
- var address = hexifyAddress((_h = node.meta.location) === null || _h === void 0 ? void 0 : _h.address);
68
- var fallback = "".concat(mapping).concat(address);
62
+ var location = locations[node.meta.locationIndex];
63
+ var mappingFile = strings[mappings[location.mappingIndex].fileStringIndex];
64
+ var mappingString = "".concat(mappingFile !== '' ? '[' + ((_b = getLastItem(mappingFile)) !== null && _b !== void 0 ? _b : '') + '] ' : '');
65
+ if (location.lines.length > 0) {
66
+ var funcName = strings[functions[location.lines[0].functionIndex].nameStringIndex];
67
+ return "".concat(mappingString, " ").concat(funcName);
68
+ }
69
+ var address = hexifyAddress(location.address);
70
+ var fallback = "".concat(mappingString).concat(address);
69
71
  return fallback === '' ? '<unknown>' : fallback;
70
72
  }
71
73
  export function IcicleGraphNodes(_a) {
72
- var data = _a.data, x = _a.x, y = _a.y, xScale = _a.xScale, total = _a.total, totalWidth = _a.totalWidth, level = _a.level, setHoveringNode = _a.setHoveringNode, path = _a.path, setCurPath = _a.setCurPath, curPath = _a.curPath;
74
+ var data = _a.data, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions, x = _a.x, y = _a.y, xScale = _a.xScale, total = _a.total, totalWidth = _a.totalWidth, level = _a.level, setHoveringNode = _a.setHoveringNode, path = _a.path, setCurPath = _a.setCurPath, curPath = _a.curPath;
73
75
  var isDarkMode = useAppSelector(selectDarkMode);
74
76
  var isShiftDown = useIsShiftDown();
75
- var nodes = curPath.length === 0 ? data : data.filter(function (d) { return d != null && curPath[0] === nodeLabel(d); });
77
+ var nodes = curPath.length === 0
78
+ ? data
79
+ : data.filter(function (d) { return d != null && curPath[0] === nodeLabel(d, strings, mappings, locations, functions); });
76
80
  var nextLevel = level + 1;
77
81
  return (_jsx("g", __assign({ transform: "translate(".concat(x, ", ").concat(y, ")") }, { children: nodes.map(function (d, i) {
78
82
  var cumulative = parseFloat(d.cumulative);
@@ -85,7 +89,7 @@ export function IcicleGraphNodes(_a) {
85
89
  if (width <= 1) {
86
90
  return null;
87
91
  }
88
- var name = nodeLabel(d);
92
+ var name = nodeLabel(d, strings, mappings, locations, functions);
89
93
  var key = "".concat(level, "-").concat(i);
90
94
  var nextPath = path.concat([name]);
91
95
  var color = diffColor(diff, cumulative, isDarkMode);
@@ -106,12 +110,12 @@ export function IcicleGraphNodes(_a) {
106
110
  return;
107
111
  setHoveringNode(undefined);
108
112
  };
109
- return (_jsxs(React.Fragment, { children: [_jsx(IcicleRect, { x: xStart, y: 0, width: width, height: RowHeight, name: name, color: color, onClick: onClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, curPath: curPath }, "rect-".concat(key)), data !== undefined && data.length > 0 && (_jsx(IcicleGraphNodes, { data: d.children, x: xStart, y: RowHeight, xScale: newXScale, total: total, totalWidth: totalWidth, level: nextLevel, setHoveringNode: setHoveringNode, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath }, "node-".concat(key)))] }, "node-".concat(key)));
113
+ return (_jsxs(React.Fragment, { children: [_jsx(IcicleRect, { x: xStart, y: 0, width: width, height: RowHeight, name: name, color: color, onClick: onClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, curPath: curPath }, "rect-".concat(key)), data !== undefined && data.length > 0 && (_jsx(IcicleGraphNodes, { data: d.children, strings: strings, mappings: mappings, locations: locations, functions: functions, x: xStart, y: RowHeight, xScale: newXScale, total: total, totalWidth: totalWidth, level: nextLevel, setHoveringNode: setHoveringNode, path: nextPath, curPath: nextCurPath, setCurPath: setCurPath }, "node-".concat(key)))] }, "node-".concat(key)));
110
114
  }) })));
111
115
  }
112
116
  var MemoizedIcicleGraphNodes = React.memo(IcicleGraphNodes);
113
117
  export function IcicleGraphRootNode(_a) {
114
- var node = _a.node, xScale = _a.xScale, total = _a.total, totalWidth = _a.totalWidth, setHoveringNode = _a.setHoveringNode, setCurPath = _a.setCurPath, curPath = _a.curPath;
118
+ var node = _a.node, strings = _a.strings, mappings = _a.mappings, locations = _a.locations, functions = _a.functions, xScale = _a.xScale, total = _a.total, totalWidth = _a.totalWidth, setHoveringNode = _a.setHoveringNode, setCurPath = _a.setCurPath, curPath = _a.curPath;
115
119
  var isDarkMode = useAppSelector(selectDarkMode);
116
120
  var isShiftDown = useIsShiftDown();
117
121
  var cumulative = parseFloat(node.cumulative);
@@ -129,7 +133,7 @@ export function IcicleGraphRootNode(_a) {
129
133
  setHoveringNode(undefined);
130
134
  };
131
135
  var path = [];
132
- return (_jsxs("g", __assign({ transform: 'translate(0, 0)' }, { children: [_jsx(IcicleRect, { x: 0, y: 0, width: totalWidth, height: RowHeight, name: 'root', color: color, onClick: onClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, curPath: curPath }), _jsx(MemoizedIcicleGraphNodes, { data: node.children, x: 0, y: RowHeight, xScale: xScale, total: total, totalWidth: totalWidth, level: 0, setHoveringNode: setHoveringNode, path: path, curPath: curPath, setCurPath: setCurPath })] })));
136
+ return (_jsxs("g", __assign({ transform: 'translate(0, 0)' }, { children: [_jsx(IcicleRect, { x: 0, y: 0, width: totalWidth, height: RowHeight, name: 'root', color: color, onClick: onClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, curPath: curPath }), _jsx(MemoizedIcicleGraphNodes, { data: node.children, strings: strings, mappings: mappings, locations: locations, functions: functions, x: 0, y: RowHeight, xScale: xScale, total: total, totalWidth: totalWidth, level: 0, setHoveringNode: setHoveringNode, path: path, curPath: curPath, setCurPath: setCurPath })] })));
133
137
  }
134
138
  var MemoizedIcicleGraphRootNode = React.memo(IcicleGraphRootNode);
135
139
  export default function IcicleGraph(_a) {
@@ -154,5 +158,5 @@ export default function IcicleGraph(_a) {
154
158
  };
155
159
  var total = parseFloat(graph.total);
156
160
  var xScale = scaleLinear().domain([0, total]).range([0, width]);
157
- return (_jsxs("div", __assign({ onMouseLeave: function () { return setHoveringNode(undefined); } }, { children: [_jsx(GraphTooltip, { unit: sampleUnit, total: total, x: pos[0], y: pos[1], hoveringNode: hoveringNode, contextElement: svg.current }), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, onMouseMove: onMouseMove, preserveAspectRatio: "xMinYMid", ref: svg }, { children: _jsx("g", __assign({ ref: ref }, { children: _jsx(MemoizedIcicleGraphRootNode, { node: graph.root, setHoveringNode: setHoveringNode, curPath: curPath, setCurPath: setCurPath, xScale: xScale, total: total, totalWidth: width }) })) }))] })));
161
+ return (_jsxs("div", __assign({ onMouseLeave: function () { return setHoveringNode(undefined); } }, { children: [_jsx(GraphTooltip, { unit: sampleUnit, total: total, x: pos[0], y: pos[1], hoveringNode: hoveringNode, contextElement: svg.current, strings: graph.stringTable, mappings: graph.mapping, locations: graph.locations, functions: graph.function }), _jsx("svg", __assign({ className: "font-robotoMono", width: width, height: height, onMouseMove: onMouseMove, preserveAspectRatio: "xMinYMid", ref: svg }, { children: _jsx("g", __assign({ ref: ref }, { children: _jsx(MemoizedIcicleGraphRootNode, { node: graph.root, strings: graph.stringTable, mappings: graph.mapping, locations: graph.locations, functions: graph.function, setHoveringNode: setHoveringNode, curPath: curPath, setCurPath: setCurPath, xScale: xScale, total: total, totalWidth: width }) })) }))] })));
158
162
  }
@@ -59,7 +59,7 @@ export var ProfileViewWithData = function (_a) {
59
59
  var profileVisState = useProfileVisState();
60
60
  var metadata = useGrpcMetadata();
61
61
  var currentView = profileVisState.currentView;
62
- var _d = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED, {
62
+ var _d = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
63
63
  skip: currentView !== 'icicle' && currentView !== 'both',
64
64
  }), flamegraphLoading = _d.isLoading, flamegraphResponse = _d.response, flamegraphError = _d.error;
65
65
  var _e = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.56",
3
+ "version": "0.16.57",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@iconify/react": "^3.2.2",
7
- "@parca/client": "^0.16.52",
8
- "@parca/components": "^0.16.53",
7
+ "@parca/client": "^0.16.53",
8
+ "@parca/components": "^0.16.54",
9
9
  "@parca/dynamicsize": "^0.16.51",
10
10
  "@parca/functions": "^0.16.51",
11
11
  "@parca/parser": "^0.16.49",
@@ -39,5 +39,5 @@
39
39
  "access": "public",
40
40
  "registry": "https://registry.npmjs.org/"
41
41
  },
42
- "gitHead": "931dedcfe76e4fddaa68a693874d94ecb7e36fdb"
42
+ "gitHead": "4a84ba064260df403f1b84ae72737744d11ceafd"
43
43
  }
@@ -15,10 +15,16 @@ import {CopyToClipboard} from 'react-copy-to-clipboard';
15
15
  import {useState, useEffect} from 'react';
16
16
  import {usePopper} from 'react-popper';
17
17
 
18
- import {CallgraphNode, FlamegraphNode, FlamegraphRootNode} from '@parca/client';
18
+ import {CallgraphNode, FlamegraphNode, FlamegraphNodeMeta, FlamegraphRootNode} from '@parca/client';
19
19
  import {getLastItem, valueFormatter} from '@parca/functions';
20
20
  import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
21
21
  import {hexifyAddress, truncateString} from '../';
22
+ import {
23
+ Function,
24
+ Location,
25
+ Mapping,
26
+ Line,
27
+ } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
22
28
 
23
29
  interface GraphTooltipProps {
24
30
  x: number;
@@ -29,6 +35,10 @@ interface GraphTooltipProps {
29
35
  contextElement: Element | null;
30
36
  isFixed?: boolean;
31
37
  virtualContextElement?: boolean;
38
+ strings?: string[];
39
+ mappings?: Mapping[];
40
+ locations?: Location[];
41
+ functions?: Function[];
32
42
  }
33
43
 
34
44
  const virtualElement = {
@@ -61,12 +71,47 @@ function generateGetBoundingClientRect(contextElement: Element, x = 0, y = 0): (
61
71
  const TooltipMetaInfo = ({
62
72
  hoveringNode,
63
73
  onCopy,
74
+ strings,
75
+ mappings,
76
+ locations,
77
+ functions,
64
78
  }: {
65
79
  hoveringNode: FlamegraphNode;
66
80
  onCopy: () => void;
81
+ strings?: string[];
82
+ mappings?: Mapping[];
83
+ locations?: Location[];
84
+ functions?: Function[];
67
85
  }): JSX.Element => {
68
86
  if (hoveringNode.meta === undefined) return <></>;
69
87
 
88
+ // populate meta from the flamegraph metadata tables
89
+ if (locations !== undefined) {
90
+ const location = locations[hoveringNode.meta.locationIndex];
91
+ hoveringNode.meta.location = location;
92
+
93
+ if (mappings !== undefined) {
94
+ const mapping = mappings[location.mappingIndex];
95
+ if (strings !== undefined) {
96
+ mapping.file = strings[mapping.fileStringIndex];
97
+ mapping.buildId = strings[mapping.buildIdStringIndex];
98
+ }
99
+ hoveringNode.meta.mapping = mapping;
100
+ }
101
+
102
+ location.lines.forEach((line: Line) => {
103
+ if (functions !== undefined && hoveringNode.meta !== undefined) {
104
+ const func = functions[line.functionIndex];
105
+ if (strings !== undefined) {
106
+ func.name = strings[func.nameStringIndex];
107
+ func.systemName = strings[func.systemNameStringIndex];
108
+ func.filename = strings[func.filenameStringIndex];
109
+ }
110
+ hoveringNode.meta.function = func;
111
+ }
112
+ });
113
+ }
114
+
70
115
  const getTextForFile = (hoveringNode: FlamegraphNode): string => {
71
116
  if (hoveringNode.meta === undefined) return '<unknown>';
72
117
 
@@ -140,9 +185,10 @@ const TooltipMetaInfo = ({
140
185
  );
141
186
  };
142
187
 
143
- export interface HoveringNode extends CallgraphNode, FlamegraphRootNode {
188
+ // @ts-expect-error
189
+ export interface HoveringNode extends CallgraphNode, FlamegraphRootNode, FlamegraphNode {
144
190
  diff: string;
145
- meta?: {[key: string]: any};
191
+ meta?: FlamegraphNodeMeta | {[key: string]: any};
146
192
  }
147
193
 
148
194
  let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
@@ -152,11 +198,19 @@ const GraphTooltipContent = ({
152
198
  unit,
153
199
  total,
154
200
  isFixed,
201
+ strings,
202
+ mappings,
203
+ locations,
204
+ functions,
155
205
  }: {
156
206
  hoveringNode: HoveringNode;
157
207
  unit: string;
158
208
  total: number;
159
209
  isFixed: boolean;
210
+ strings?: string[];
211
+ mappings?: Mapping[];
212
+ locations?: Location[];
213
+ functions?: Function[];
160
214
  }): JSX.Element => {
161
215
  const [isCopied, setIsCopied] = useState<boolean>(false);
162
216
 
@@ -181,7 +235,15 @@ const GraphTooltipContent = ({
181
235
  hoveringNode.meta === undefined ? (
182
236
  <></>
183
237
  ) : (
184
- <TooltipMetaInfo onCopy={onCopy} hoveringNode={hoveringNode} />
238
+ <TooltipMetaInfo
239
+ onCopy={onCopy}
240
+ // @ts-expect-error
241
+ hoveringNode={hoveringNode}
242
+ strings={strings}
243
+ mappings={mappings}
244
+ locations={locations}
245
+ functions={functions}
246
+ />
185
247
  );
186
248
 
187
249
  const getTextForCumulative = (hoveringNodeCumulative: number): string => {
@@ -282,6 +344,10 @@ const GraphTooltip = ({
282
344
  contextElement,
283
345
  isFixed = false,
284
346
  virtualContextElement = true,
347
+ strings,
348
+ mappings,
349
+ locations,
350
+ functions,
285
351
  }: GraphTooltipProps): JSX.Element => {
286
352
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
287
353
 
@@ -332,6 +398,10 @@ const GraphTooltip = ({
332
398
  unit={unit}
333
399
  total={total}
334
400
  isFixed={isFixed}
401
+ strings={strings}
402
+ mappings={mappings}
403
+ locations={locations}
404
+ functions={functions}
335
405
  />
336
406
  </div>
337
407
  );
@@ -18,12 +18,13 @@ import {pointer} from 'd3-selection';
18
18
  import {scaleLinear} from 'd3-scale';
19
19
 
20
20
  import {Flamegraph, FlamegraphNode, FlamegraphRootNode} from '@parca/client';
21
+ import {Mapping, Function, Location} from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
22
+ import type {HoveringNode} from './GraphTooltip';
21
23
  import GraphTooltip from './GraphTooltip';
22
- import {getLastItem, diffColor, isSearchMatch} from '@parca/functions';
23
- import {useAppSelector, selectDarkMode, selectSearchNodeString} from '@parca/store';
24
+ import {diffColor, getLastItem, isSearchMatch} from '@parca/functions';
25
+ import {selectDarkMode, selectSearchNodeString, useAppSelector} from '@parca/store';
24
26
  import useIsShiftDown from '@parca/components/src/hooks/useIsShiftDown';
25
27
  import {hexifyAddress} from './utils';
26
- import type {HoveringNode} from './GraphTooltip';
27
28
 
28
29
  interface IcicleGraphProps {
29
30
  graph: Flamegraph;
@@ -35,6 +36,10 @@ interface IcicleGraphProps {
35
36
 
36
37
  interface IcicleGraphNodesProps {
37
38
  data: FlamegraphNode[];
39
+ strings: string[];
40
+ mappings: Mapping[];
41
+ locations: Location[];
42
+ functions: Function[];
38
43
  x: number;
39
44
  y: number;
40
45
  total: number;
@@ -49,6 +54,10 @@ interface IcicleGraphNodesProps {
49
54
 
50
55
  interface IcicleGraphRootNodeProps {
51
56
  node: FlamegraphRootNode;
57
+ strings: string[];
58
+ mappings: Mapping[];
59
+ locations: Location[];
60
+ functions: Function[];
52
61
  xScale: (value: number) => number;
53
62
  total: number;
54
63
  totalWidth: number;
@@ -132,24 +141,39 @@ function IcicleRect({
132
141
  );
133
142
  }
134
143
 
135
- export function nodeLabel(node: FlamegraphNode): string {
136
- if (node.meta === undefined) return '<unknown>';
137
- const mapping = `${
138
- node.meta?.mapping?.file !== undefined && node.meta?.mapping?.file !== ''
139
- ? '[' + (getLastItem(node.meta.mapping.file) ?? '') + '] '
140
- : ''
144
+ export function nodeLabel(
145
+ node: FlamegraphNode,
146
+ strings: string[],
147
+ mappings: Mapping[],
148
+ locations: Location[],
149
+ functions: Function[]
150
+ ): string {
151
+ if (node.meta?.locationIndex === undefined) return '<unknown>';
152
+
153
+ const location = locations[node.meta.locationIndex];
154
+ const mappingFile = strings[mappings[location.mappingIndex].fileStringIndex];
155
+
156
+ const mappingString: string = `${
157
+ mappingFile !== '' ? '[' + (getLastItem(mappingFile) ?? '') + '] ' : ''
141
158
  }`;
142
- if (node.meta.function?.name !== undefined && node.meta.function?.name !== '')
143
- return mapping + node.meta.function.name;
144
159
 
145
- const address = hexifyAddress(node.meta.location?.address);
146
- const fallback = `${mapping}${address}`;
160
+ if (location.lines.length > 0) {
161
+ const funcName = strings[functions[location.lines[0].functionIndex].nameStringIndex];
162
+ return `${mappingString} ${funcName}`;
163
+ }
164
+
165
+ const address = hexifyAddress(location.address);
166
+ const fallback = `${mappingString}${address}`;
147
167
 
148
168
  return fallback === '' ? '<unknown>' : fallback;
149
169
  }
150
170
 
151
171
  export function IcicleGraphNodes({
152
172
  data,
173
+ strings,
174
+ mappings,
175
+ locations,
176
+ functions,
153
177
  x,
154
178
  y,
155
179
  xScale,
@@ -165,7 +189,11 @@ export function IcicleGraphNodes({
165
189
  const isShiftDown = useIsShiftDown();
166
190
 
167
191
  const nodes =
168
- curPath.length === 0 ? data : data.filter(d => d != null && curPath[0] === nodeLabel(d));
192
+ curPath.length === 0
193
+ ? data
194
+ : data.filter(
195
+ d => d != null && curPath[0] === nodeLabel(d, strings, mappings, locations, functions)
196
+ );
169
197
 
170
198
  const nextLevel = level + 1;
171
199
 
@@ -186,7 +214,7 @@ export function IcicleGraphNodes({
186
214
  return null;
187
215
  }
188
216
 
189
- const name = nodeLabel(d);
217
+ const name = nodeLabel(d, strings, mappings, locations, functions);
190
218
  const key = `${level}-${i}`;
191
219
  const nextPath = path.concat([name]);
192
220
 
@@ -232,6 +260,10 @@ export function IcicleGraphNodes({
232
260
  <IcicleGraphNodes
233
261
  key={`node-${key}`}
234
262
  data={d.children}
263
+ strings={strings}
264
+ mappings={mappings}
265
+ locations={locations}
266
+ functions={functions}
235
267
  x={xStart}
236
268
  y={RowHeight}
237
269
  xScale={newXScale}
@@ -255,6 +287,10 @@ const MemoizedIcicleGraphNodes = React.memo(IcicleGraphNodes);
255
287
 
256
288
  export function IcicleGraphRootNode({
257
289
  node,
290
+ strings,
291
+ mappings,
292
+ locations,
293
+ functions,
258
294
  xScale,
259
295
  total,
260
296
  totalWidth,
@@ -299,6 +335,10 @@ export function IcicleGraphRootNode({
299
335
  />
300
336
  <MemoizedIcicleGraphNodes
301
337
  data={node.children}
338
+ strings={strings}
339
+ mappings={mappings}
340
+ locations={locations}
341
+ functions={functions}
302
342
  x={0}
303
343
  y={RowHeight}
304
344
  xScale={xScale}
@@ -359,6 +399,10 @@ export default function IcicleGraph({
359
399
  y={pos[1]}
360
400
  hoveringNode={hoveringNode as HoveringNode}
361
401
  contextElement={svg.current}
402
+ strings={graph.stringTable}
403
+ mappings={graph.mapping}
404
+ locations={graph.locations}
405
+ functions={graph.function}
362
406
  />
363
407
  <svg
364
408
  className="font-robotoMono"
@@ -371,6 +415,10 @@ export default function IcicleGraph({
371
415
  <g ref={ref}>
372
416
  <MemoizedIcicleGraphRootNode
373
417
  node={graph.root}
418
+ strings={graph.stringTable}
419
+ mappings={graph.mapping}
420
+ locations={graph.locations}
421
+ functions={graph.function}
374
422
  setHoveringNode={setHoveringNode}
375
423
  curPath={curPath}
376
424
  setCurPath={setCurPath}
@@ -41,7 +41,7 @@ export const ProfileViewWithData = ({
41
41
  isLoading: flamegraphLoading,
42
42
  response: flamegraphResponse,
43
43
  error: flamegraphError,
44
- } = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED, {
44
+ } = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
45
45
  skip: currentView !== 'icicle' && currentView !== 'both',
46
46
  });
47
47