@parca/profile 0.17.1 → 0.17.3
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/GraphTooltipArrow/Content.d.ts +1 -2
- package/dist/GraphTooltipArrow/Content.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/Content.js +1 -2
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.d.ts +1 -2
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.js +1 -2
- package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts +1 -2
- package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/useGraphTooltip/index.js +2 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts +2 -6
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +4 -5
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts +20 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts.map +1 -0
- package/{src/Callgraph/constants.ts → dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.js} +12 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +8 -51
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +59 -136
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.d.ts +8 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.d.ts.map +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.js +40 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.d.ts +32 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.d.ts.map +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.js +40 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +4 -5
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +62 -76
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +3 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +9 -7
- package/dist/ProfileIcicleGraph/index.d.ts +3 -6
- package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/index.js +8 -17
- package/dist/ProfileView/components/DashboardItems/index.d.ts +3 -5
- package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
- package/dist/ProfileView/components/DashboardItems/index.js +4 -9
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/index.js +1 -2
- package/dist/ProfileView/index.d.ts +1 -1
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +1 -13
- package/dist/ProfileView/types/visualization.d.ts +1 -1
- package/dist/ProfileView/types/visualization.d.ts.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/styles.css +1 -1
- package/package.json +5 -5
- package/src/GraphTooltipArrow/Content.tsx +0 -3
- package/src/GraphTooltipArrow/DockedGraphTooltip/index.tsx +0 -3
- package/src/GraphTooltipArrow/useGraphTooltip/index.ts +1 -3
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +5 -13
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.tsx +53 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +96 -310
- package/src/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.tsx +78 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.tsx +93 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +108 -216
- package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +8 -15
- package/src/ProfileIcicleGraph/index.tsx +7 -38
- package/src/ProfileView/components/DashboardItems/index.tsx +2 -27
- package/src/ProfileView/components/Toolbars/index.tsx +0 -2
- package/src/ProfileView/index.tsx +0 -14
- package/src/ProfileView/types/visualization.ts +1 -1
- package/src/index.tsx +0 -5
- package/dist/Callgraph/constants.d.ts +0 -3
- package/dist/Callgraph/constants.d.ts.map +0 -1
- package/dist/Callgraph/constants.js +0 -14
- package/dist/Callgraph/index.d.ts +0 -11
- package/dist/Callgraph/index.d.ts.map +0 -1
- package/dist/Callgraph/index.js +0 -104
- package/dist/Callgraph/mockData/index.d.ts +0 -149
- package/dist/Callgraph/mockData/index.d.ts.map +0 -1
- package/dist/Callgraph/mockData/index.js +0 -594
- package/dist/Callgraph/utils.d.ts +0 -20
- package/dist/Callgraph/utils.d.ts.map +0 -1
- package/dist/Callgraph/utils.js +0 -97
- package/dist/GraphTooltip/ExpandOnHoverValue.d.ts +0 -7
- package/dist/GraphTooltip/ExpandOnHoverValue.d.ts.map +0 -1
- package/dist/GraphTooltip/ExpandOnHoverValue.js +0 -4
- package/dist/GraphTooltip/index.d.ts +0 -41
- package/dist/GraphTooltip/index.d.ts.map +0 -1
- package/dist/GraphTooltip/index.js +0 -201
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.d.ts +0 -6
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.js +0 -59
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.d.ts +0 -47
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.js +0 -93
- package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts +0 -14
- package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/index.js +0 -48
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.d.ts +0 -15
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.js +0 -57
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.d.ts +0 -8
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.js +0 -32
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.d.ts +0 -7
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.js +0 -66
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.d.ts +0 -9
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.d.ts.map +0 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.js +0 -45
- package/dist/ProfileView/hooks/useGraphviz.d.ts +0 -12
- package/dist/ProfileView/hooks/useGraphviz.d.ts.map +0 -1
- package/dist/ProfileView/hooks/useGraphviz.js +0 -42
- package/src/Callgraph/index.tsx +0 -177
- package/src/Callgraph/mockData/index.ts +0 -605
- package/src/Callgraph/utils.ts +0 -141
- package/src/GraphTooltip/ExpandOnHoverValue.tsx +0 -30
- package/src/GraphTooltip/index.tsx +0 -509
- package/src/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.tsx +0 -96
- package/src/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.tsx +0 -266
- package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +0 -123
- package/src/ProfileIcicleGraph/IcicleGraph/useColoredGraph.ts +0 -117
- package/src/ProfileIcicleGraph/IcicleGraph/useNodeColor.ts +0 -54
- package/src/ProfileIcicleGraph/IcicleGraph/utils.ts +0 -102
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.tsx +0 -130
- package/src/ProfileView/hooks/useGraphviz.ts +0 -69
|
@@ -11,184 +11,60 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {
|
|
14
|
+
import React, {useMemo} from 'react';
|
|
15
15
|
|
|
16
16
|
import {Table} from 'apache-arrow';
|
|
17
17
|
import cx from 'classnames';
|
|
18
18
|
|
|
19
19
|
import {selectBinaries, useAppSelector} from '@parca/store';
|
|
20
|
-
import {isSearchMatch
|
|
20
|
+
import {isSearchMatch} from '@parca/utilities';
|
|
21
21
|
|
|
22
22
|
import 'react-contexify/dist/ReactContexify.css';
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import {USER_PREFERENCES} from '@parca/hooks';
|
|
25
25
|
|
|
26
|
+
import {ProfileSource} from '../../ProfileSource';
|
|
26
27
|
import TextWithEllipsis from './TextWithEllipsis';
|
|
27
28
|
import {
|
|
28
|
-
FIELD_CHILDREN,
|
|
29
29
|
FIELD_CUMULATIVE,
|
|
30
|
+
FIELD_DEPTH,
|
|
30
31
|
FIELD_DIFF,
|
|
31
32
|
FIELD_FUNCTION_FILE_NAME,
|
|
32
33
|
FIELD_FUNCTION_NAME,
|
|
33
34
|
FIELD_MAPPING_FILE,
|
|
35
|
+
FIELD_PARENT,
|
|
36
|
+
FIELD_TIMESTAMP,
|
|
37
|
+
FIELD_VALUE_OFFSET,
|
|
34
38
|
} from './index';
|
|
35
39
|
import useNodeColor from './useNodeColor';
|
|
36
|
-
import {
|
|
37
|
-
CurrentPathFrame,
|
|
38
|
-
arrowToString,
|
|
39
|
-
getCurrentPathFrameData,
|
|
40
|
-
isCurrentPathFrameMatch,
|
|
41
|
-
nodeLabel,
|
|
42
|
-
} from './utils';
|
|
40
|
+
import {arrowToString, boundsFromProfileSource, nodeLabel} from './utils';
|
|
43
41
|
|
|
44
42
|
export const RowHeight = 26;
|
|
45
43
|
|
|
46
|
-
interface IcicleGraphNodesProps {
|
|
47
|
-
table: Table<any>;
|
|
48
|
-
row: number;
|
|
49
|
-
colors: colorByColors;
|
|
50
|
-
colorBy: string;
|
|
51
|
-
childRows: number[];
|
|
52
|
-
x: number;
|
|
53
|
-
y: number;
|
|
54
|
-
total: bigint;
|
|
55
|
-
totalWidth: number;
|
|
56
|
-
level: number;
|
|
57
|
-
curPath: CurrentPathFrame[];
|
|
58
|
-
setCurPath: (path: CurrentPathFrame[]) => void;
|
|
59
|
-
setHoveringRow: (row: number | null) => void;
|
|
60
|
-
setHoveringLevel: (level: number | null) => void;
|
|
61
|
-
path: CurrentPathFrame[];
|
|
62
|
-
xScale: (value: bigint) => number;
|
|
63
|
-
searchString?: string;
|
|
64
|
-
sortBy: string;
|
|
65
|
-
darkMode: boolean;
|
|
66
|
-
compareMode: boolean;
|
|
67
|
-
profileType?: ProfileType;
|
|
68
|
-
isContextMenuOpen: boolean;
|
|
69
|
-
hoveringName: string | null;
|
|
70
|
-
setHoveringName: (name: string | null) => void;
|
|
71
|
-
hoveringRow: number | null;
|
|
72
|
-
colorForSimilarNodes: string;
|
|
73
|
-
highlightSimilarStacksPreference: boolean;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
|
|
77
|
-
table,
|
|
78
|
-
childRows,
|
|
79
|
-
colors,
|
|
80
|
-
colorBy,
|
|
81
|
-
x,
|
|
82
|
-
y,
|
|
83
|
-
xScale,
|
|
84
|
-
total,
|
|
85
|
-
totalWidth,
|
|
86
|
-
level,
|
|
87
|
-
path,
|
|
88
|
-
setCurPath,
|
|
89
|
-
setHoveringRow,
|
|
90
|
-
setHoveringLevel,
|
|
91
|
-
curPath,
|
|
92
|
-
sortBy,
|
|
93
|
-
searchString,
|
|
94
|
-
darkMode,
|
|
95
|
-
compareMode,
|
|
96
|
-
profileType,
|
|
97
|
-
isContextMenuOpen,
|
|
98
|
-
hoveringName,
|
|
99
|
-
setHoveringName,
|
|
100
|
-
hoveringRow,
|
|
101
|
-
colorForSimilarNodes,
|
|
102
|
-
highlightSimilarStacksPreference,
|
|
103
|
-
}: IcicleGraphNodesProps): React.JSX.Element {
|
|
104
|
-
const cumulatives = table.getChild(FIELD_CUMULATIVE);
|
|
105
|
-
|
|
106
|
-
if (childRows === undefined || childRows.length === 0) {
|
|
107
|
-
return <></>;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
childRows =
|
|
111
|
-
curPath.length === 0
|
|
112
|
-
? childRows
|
|
113
|
-
: childRows.filter(c => isCurrentPathFrameMatch(table, c, level, curPath[0]));
|
|
114
|
-
|
|
115
|
-
let childrenCumulative = BigInt(0);
|
|
116
|
-
const childrenElements: ReactNode[] = [];
|
|
117
|
-
childRows.forEach((child, i) => {
|
|
118
|
-
const xStart = Math.floor(xScale(BigInt(childrenCumulative)));
|
|
119
|
-
const c = BigInt(cumulatives?.get(child));
|
|
120
|
-
childrenCumulative += c;
|
|
121
|
-
|
|
122
|
-
childrenElements.push(
|
|
123
|
-
<IcicleNode
|
|
124
|
-
key={`node-${level}-${i}`}
|
|
125
|
-
table={table}
|
|
126
|
-
row={child}
|
|
127
|
-
colors={colors}
|
|
128
|
-
colorBy={colorBy}
|
|
129
|
-
x={xStart}
|
|
130
|
-
y={0}
|
|
131
|
-
totalWidth={totalWidth}
|
|
132
|
-
height={RowHeight}
|
|
133
|
-
path={path}
|
|
134
|
-
setCurPath={setCurPath}
|
|
135
|
-
setHoveringRow={setHoveringRow}
|
|
136
|
-
setHoveringLevel={setHoveringLevel}
|
|
137
|
-
level={level}
|
|
138
|
-
curPath={curPath}
|
|
139
|
-
total={total}
|
|
140
|
-
xScale={xScale}
|
|
141
|
-
sortBy={sortBy}
|
|
142
|
-
searchString={searchString}
|
|
143
|
-
darkMode={darkMode}
|
|
144
|
-
compareMode={compareMode}
|
|
145
|
-
profileType={profileType}
|
|
146
|
-
isContextMenuOpen={isContextMenuOpen}
|
|
147
|
-
hoveringName={hoveringName}
|
|
148
|
-
setHoveringName={setHoveringName}
|
|
149
|
-
hoveringRow={hoveringRow}
|
|
150
|
-
colorForSimilarNodes={colorForSimilarNodes}
|
|
151
|
-
highlightSimilarStacksPreference={highlightSimilarStacksPreference}
|
|
152
|
-
/>
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
return <g transform={`translate(${x}, ${y})`}>{childrenElements}</g>;
|
|
157
|
-
});
|
|
158
|
-
|
|
159
44
|
export interface colorByColors {
|
|
160
45
|
[key: string]: string;
|
|
161
46
|
}
|
|
162
47
|
|
|
163
48
|
export interface IcicleNodeProps {
|
|
164
|
-
x: number;
|
|
165
|
-
y: number;
|
|
166
49
|
height: number;
|
|
167
50
|
totalWidth: number;
|
|
168
|
-
curPath: CurrentPathFrame[];
|
|
169
|
-
level: number;
|
|
170
51
|
table: Table<any>;
|
|
171
52
|
row: number;
|
|
172
53
|
colors: colorByColors;
|
|
173
54
|
colorBy: string;
|
|
174
|
-
path: CurrentPathFrame[];
|
|
175
|
-
total: bigint;
|
|
176
|
-
setCurPath: (path: CurrentPathFrame[]) => void;
|
|
177
|
-
setHoveringRow: (row: number | null) => void;
|
|
178
|
-
setHoveringLevel: (level: number | null) => void;
|
|
179
|
-
xScale: (value: bigint) => number;
|
|
180
|
-
isRoot?: boolean;
|
|
181
55
|
searchString?: string;
|
|
182
|
-
sortBy: string;
|
|
183
56
|
darkMode: boolean;
|
|
184
57
|
compareMode: boolean;
|
|
185
|
-
|
|
186
|
-
isContextMenuOpen: boolean;
|
|
187
|
-
hoveringName: string | null;
|
|
188
|
-
setHoveringName: (name: string | null) => void;
|
|
189
|
-
hoveringRow: number | null;
|
|
58
|
+
onContextMenu: (e: React.MouseEvent, row: number) => void;
|
|
190
59
|
colorForSimilarNodes: string;
|
|
191
|
-
|
|
60
|
+
selectedRow: number;
|
|
61
|
+
onClick: () => void;
|
|
62
|
+
isIcicleChart: boolean;
|
|
63
|
+
profileSource: ProfileSource;
|
|
64
|
+
|
|
65
|
+
// Hovering row must only ever be used for highlighting similar nodes, otherwise it will cause performance issues as it causes the full iciclegraph to get rerendered every time the hovering row changes.
|
|
66
|
+
hoveringRow?: number;
|
|
67
|
+
setHoveringRow: (row: number | undefined) => void;
|
|
192
68
|
}
|
|
193
69
|
|
|
194
70
|
export const icicleRectStyles = {
|
|
@@ -206,136 +82,49 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
206
82
|
row,
|
|
207
83
|
colors,
|
|
208
84
|
colorBy,
|
|
209
|
-
x,
|
|
210
|
-
y,
|
|
211
85
|
height,
|
|
212
|
-
setCurPath,
|
|
213
|
-
curPath,
|
|
214
|
-
level,
|
|
215
|
-
path,
|
|
216
|
-
total,
|
|
217
86
|
totalWidth,
|
|
218
|
-
xScale,
|
|
219
|
-
isRoot = false,
|
|
220
87
|
searchString,
|
|
221
|
-
setHoveringRow,
|
|
222
|
-
setHoveringLevel,
|
|
223
|
-
sortBy,
|
|
224
88
|
darkMode,
|
|
225
89
|
compareMode,
|
|
226
|
-
profileType,
|
|
227
|
-
isContextMenuOpen,
|
|
228
|
-
hoveringName,
|
|
229
|
-
setHoveringName,
|
|
230
|
-
hoveringRow,
|
|
231
90
|
colorForSimilarNodes,
|
|
232
|
-
|
|
91
|
+
selectedRow,
|
|
92
|
+
onClick,
|
|
93
|
+
onContextMenu,
|
|
94
|
+
hoveringRow,
|
|
95
|
+
setHoveringRow,
|
|
96
|
+
isIcicleChart,
|
|
97
|
+
profileSource,
|
|
233
98
|
}: IcicleNodeProps): React.JSX.Element {
|
|
234
99
|
// get the columns to read from
|
|
235
100
|
const mappingColumn = table.getChild(FIELD_MAPPING_FILE);
|
|
236
101
|
const functionNameColumn = table.getChild(FIELD_FUNCTION_NAME);
|
|
237
102
|
const cumulativeColumn = table.getChild(FIELD_CUMULATIVE);
|
|
103
|
+
const depthColumn = table.getChild(FIELD_DEPTH);
|
|
238
104
|
const diffColumn = table.getChild(FIELD_DIFF);
|
|
239
105
|
const filenameColumn = table.getChild(FIELD_FUNCTION_FILE_NAME);
|
|
106
|
+
const valueOffsetColumn = table.getChild(FIELD_VALUE_OFFSET);
|
|
107
|
+
const tsColumn = table.getChild(FIELD_TIMESTAMP);
|
|
108
|
+
|
|
240
109
|
// get the actual values from the columns
|
|
241
110
|
const mappingFile: string | null = arrowToString(mappingColumn?.get(row));
|
|
242
111
|
const functionName: string | null = arrowToString(functionNameColumn?.get(row));
|
|
243
112
|
const cumulative = cumulativeColumn?.get(row) !== null ? BigInt(cumulativeColumn?.get(row)) : 0n;
|
|
244
113
|
const diff: bigint | null = diffColumn?.get(row) !== null ? BigInt(diffColumn?.get(row)) : null;
|
|
245
|
-
const childRows: number[] = Array.from(table.getChild(FIELD_CHILDREN)?.get(row) ?? []);
|
|
246
114
|
const filename: string | null = arrowToString(filenameColumn?.get(row));
|
|
115
|
+
const depth: number = depthColumn?.get(row) ?? 0;
|
|
116
|
+
const valueOffset: bigint =
|
|
117
|
+
valueOffsetColumn?.get(row) !== null ? BigInt(valueOffsetColumn?.get(row)) : 0n;
|
|
247
118
|
|
|
248
|
-
const colorAttribute
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (colorBy === 'filename') {
|
|
252
|
-
attr = filename;
|
|
253
|
-
} else if (colorBy === 'binary') {
|
|
254
|
-
attr = mappingFile;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return attr ?? null; // Provide a default value of null if attr is undefined
|
|
258
|
-
}, [colorBy, filename, mappingFile]);
|
|
119
|
+
const colorAttribute =
|
|
120
|
+
colorBy === 'filename' ? filename : colorBy === 'binary' ? mappingFile : null;
|
|
259
121
|
|
|
260
122
|
const colorsMap = colors;
|
|
261
123
|
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (functionName != null && functionName === hoveringName) {
|
|
268
|
-
return {functionName, row: hoveringRow};
|
|
269
|
-
}
|
|
270
|
-
return null; // Nothing to highlight
|
|
271
|
-
}, [functionName, hoveringName, hoveringRow, highlightSimilarStacksPreference]);
|
|
272
|
-
|
|
273
|
-
const shouldBeHighlightedIfSimilarStacks = useMemo(() => {
|
|
274
|
-
return highlightedNodes !== null && row !== highlightedNodes.row;
|
|
275
|
-
}, [row, highlightedNodes]);
|
|
276
|
-
|
|
277
|
-
// TODO: Maybe it's better to pass down the sorter function as prop instead of figuring this out here.
|
|
278
|
-
switch (sortBy) {
|
|
279
|
-
case FIELD_FUNCTION_NAME:
|
|
280
|
-
childRows.sort((a, b) => {
|
|
281
|
-
// TODO: Support fallthrough to comparing addresses or something
|
|
282
|
-
const afn: string | null = arrowToString(functionNameColumn?.get(a));
|
|
283
|
-
const bfn: string | null = arrowToString(functionNameColumn?.get(b));
|
|
284
|
-
if (afn !== null && bfn !== null) {
|
|
285
|
-
return afn.localeCompare(bfn);
|
|
286
|
-
}
|
|
287
|
-
if (afn === null && bfn !== null) {
|
|
288
|
-
return -1;
|
|
289
|
-
}
|
|
290
|
-
if (afn !== null && bfn === null) {
|
|
291
|
-
return 1;
|
|
292
|
-
}
|
|
293
|
-
// both are null
|
|
294
|
-
return 0;
|
|
295
|
-
});
|
|
296
|
-
break;
|
|
297
|
-
case FIELD_CUMULATIVE:
|
|
298
|
-
childRows.sort((a, b) => {
|
|
299
|
-
const aCumulative: bigint = cumulativeColumn?.get(a);
|
|
300
|
-
const bCumulative: bigint = cumulativeColumn?.get(b);
|
|
301
|
-
return Number(bCumulative - aCumulative);
|
|
302
|
-
});
|
|
303
|
-
break;
|
|
304
|
-
case FIELD_DIFF:
|
|
305
|
-
childRows.sort((a, b) => {
|
|
306
|
-
let aRatio: number | null = null;
|
|
307
|
-
const aDiff: bigint | null =
|
|
308
|
-
diffColumn?.get(a) !== null ? BigInt(diffColumn?.get(a)) : null;
|
|
309
|
-
if (aDiff !== null) {
|
|
310
|
-
const cumulative: bigint =
|
|
311
|
-
cumulativeColumn?.get(a) !== null ? BigInt(cumulativeColumn?.get(a)) : 0n;
|
|
312
|
-
const prev: bigint = cumulative - aDiff;
|
|
313
|
-
aRatio = Number(aDiff) / Number(prev);
|
|
314
|
-
}
|
|
315
|
-
let bRatio: number | null = null;
|
|
316
|
-
const bDiff: bigint | null =
|
|
317
|
-
diffColumn?.get(b) !== null ? BigInt(diffColumn?.get(b)) : null;
|
|
318
|
-
if (bDiff !== null) {
|
|
319
|
-
const cumulative: bigint =
|
|
320
|
-
cumulativeColumn?.get(b) !== null ? BigInt(cumulativeColumn?.get(b)) : 0n;
|
|
321
|
-
const prev: bigint = cumulative - bDiff;
|
|
322
|
-
bRatio = Number(bDiff) / Number(prev);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (aRatio !== null && bRatio !== null) {
|
|
326
|
-
return bRatio - aRatio;
|
|
327
|
-
}
|
|
328
|
-
if (aRatio === null && bRatio !== null) {
|
|
329
|
-
return -1;
|
|
330
|
-
}
|
|
331
|
-
if (aRatio !== null && bRatio === null) {
|
|
332
|
-
return 1;
|
|
333
|
-
}
|
|
334
|
-
// both are null
|
|
335
|
-
return 0;
|
|
336
|
-
});
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
124
|
+
const hoveringName =
|
|
125
|
+
hoveringRow !== undefined ? arrowToString(functionNameColumn?.get(hoveringRow)) : '';
|
|
126
|
+
const shouldBeHighlighted =
|
|
127
|
+
functionName != null && hoveringName != null && functionName === hoveringName;
|
|
339
128
|
|
|
340
129
|
const binaries = useAppSelector(selectBinaries);
|
|
341
130
|
const colorResult = useNodeColor({
|
|
@@ -347,24 +136,8 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
347
136
|
colorAttribute,
|
|
348
137
|
});
|
|
349
138
|
const name = useMemo(() => {
|
|
350
|
-
return
|
|
351
|
-
}, [table, row,
|
|
352
|
-
const currentPathFrame: CurrentPathFrame = getCurrentPathFrameData(table, row, level);
|
|
353
|
-
const nextPath = path.concat([currentPathFrame]);
|
|
354
|
-
const isFaded =
|
|
355
|
-
curPath.length > 0 && !isCurrentPathFrameMatch(table, row, level, curPath[curPath.length - 1]);
|
|
356
|
-
const styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
|
|
357
|
-
const nextLevel = level + 1;
|
|
358
|
-
const nextCurPath = curPath.length === 0 ? [] : curPath.slice(1);
|
|
359
|
-
const newXScale =
|
|
360
|
-
nextCurPath.length === 0 && curPath.length === 1
|
|
361
|
-
? scaleLinear([0n, BigInt(cumulative)], [0, totalWidth])
|
|
362
|
-
: xScale;
|
|
363
|
-
|
|
364
|
-
const width: number =
|
|
365
|
-
nextCurPath.length > 0 || (nextCurPath.length === 0 && curPath.length === 1)
|
|
366
|
-
? totalWidth
|
|
367
|
-
: xScale(BigInt(cumulative));
|
|
139
|
+
return row === 0 ? 'root' : nodeLabel(table, row, binaries.length > 1);
|
|
140
|
+
}, [table, row, binaries]);
|
|
368
141
|
|
|
369
142
|
const {isHighlightEnabled = false, isHighlighted = false} = useMemo(() => {
|
|
370
143
|
if (searchString === undefined || searchString === '') {
|
|
@@ -373,24 +146,69 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
373
146
|
return {isHighlightEnabled: true, isHighlighted: isSearchMatch(searchString, name)};
|
|
374
147
|
}, [searchString, name]);
|
|
375
148
|
|
|
149
|
+
const selectionOffset =
|
|
150
|
+
valueOffsetColumn?.get(selectedRow) !== null ? BigInt(valueOffsetColumn?.get(selectedRow)) : 0n;
|
|
151
|
+
const selectionCumulative =
|
|
152
|
+
cumulativeColumn?.get(selectedRow) !== null ? BigInt(cumulativeColumn?.get(selectedRow)) : 0n;
|
|
153
|
+
if (
|
|
154
|
+
valueOffset + cumulative <= selectionOffset ||
|
|
155
|
+
valueOffset >= selectionOffset + selectionCumulative
|
|
156
|
+
) {
|
|
157
|
+
// If the end of the node is before the selection offset or the start of the node is after the selection offset + totalWidth, we don't render it.
|
|
158
|
+
return <></>;
|
|
159
|
+
}
|
|
160
|
+
if (row === 0 && isIcicleChart) {
|
|
161
|
+
// The root node is not rendered in the icicle chart, so we return null.
|
|
162
|
+
return <></>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Cumulative can be larger than total when a selection is made. All parents of the selection are likely larger, but we want to only show them as 100% in the graph.
|
|
166
|
+
const tsBounds = boundsFromProfileSource(profileSource);
|
|
167
|
+
const total = cumulativeColumn?.get(selectedRow);
|
|
168
|
+
const totalRatio = cumulative > total ? 1 : Number(cumulative) / Number(total);
|
|
169
|
+
const width: number = isIcicleChart
|
|
170
|
+
? (Number(cumulative) / (Number(tsBounds[1]) - Number(tsBounds[0]))) * totalWidth
|
|
171
|
+
: totalRatio * totalWidth;
|
|
172
|
+
|
|
376
173
|
if (width <= 1) {
|
|
377
|
-
return
|
|
174
|
+
return <></>;
|
|
378
175
|
}
|
|
379
176
|
|
|
177
|
+
const selectedDepth = depthColumn?.get(selectedRow);
|
|
178
|
+
const styles =
|
|
179
|
+
selectedDepth !== undefined && selectedDepth > depth ? fadedIcicleRectStyles : icicleRectStyles;
|
|
180
|
+
|
|
380
181
|
const onMouseEnter = (): void => {
|
|
381
|
-
if (isContextMenuOpen) return;
|
|
382
182
|
setHoveringRow(row);
|
|
383
|
-
|
|
384
|
-
|
|
183
|
+
window.dispatchEvent(
|
|
184
|
+
new CustomEvent('icicle-tooltip-update', {
|
|
185
|
+
detail: {row},
|
|
186
|
+
})
|
|
187
|
+
);
|
|
385
188
|
};
|
|
386
189
|
|
|
387
190
|
const onMouseLeave = (): void => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
191
|
+
setHoveringRow(undefined);
|
|
192
|
+
window.dispatchEvent(
|
|
193
|
+
new CustomEvent('icicle-tooltip-update', {
|
|
194
|
+
detail: {row: null},
|
|
195
|
+
})
|
|
196
|
+
);
|
|
392
197
|
};
|
|
393
198
|
|
|
199
|
+
const handleContextMenu = (e: React.MouseEvent): void => {
|
|
200
|
+
onContextMenu(e, row);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const ts = tsColumn !== null ? Number(tsColumn.get(row)) : 0;
|
|
204
|
+
const x =
|
|
205
|
+
isIcicleChart && tsColumn !== null
|
|
206
|
+
? ((ts - Number(tsBounds[0])) / (Number(tsBounds[1]) - Number(tsBounds[0]))) * totalWidth
|
|
207
|
+
: selectedDepth > depth
|
|
208
|
+
? 0
|
|
209
|
+
: ((Number(valueOffset) - Number(selectionOffset)) / Number(total)) * totalWidth;
|
|
210
|
+
const y = isIcicleChart ? (depth - 1) * height : depth * height;
|
|
211
|
+
|
|
394
212
|
return (
|
|
395
213
|
<>
|
|
396
214
|
<g
|
|
@@ -398,9 +216,8 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
398
216
|
style={styles}
|
|
399
217
|
onMouseEnter={onMouseEnter}
|
|
400
218
|
onMouseLeave={onMouseLeave}
|
|
401
|
-
onClick={
|
|
402
|
-
|
|
403
|
-
}}
|
|
219
|
+
onClick={onClick}
|
|
220
|
+
onContextMenu={handleContextMenu}
|
|
404
221
|
>
|
|
405
222
|
<rect
|
|
406
223
|
x={0}
|
|
@@ -411,7 +228,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
411
228
|
fill: colorResult,
|
|
412
229
|
}}
|
|
413
230
|
className={cx(
|
|
414
|
-
|
|
231
|
+
shouldBeHighlighted
|
|
415
232
|
? `${colorForSimilarNodes} stroke-[3] [stroke-dasharray:6,4] [stroke-linecap:round] [stroke-linejoin:round] h-6`
|
|
416
233
|
: 'stroke-white dark:stroke-gray-700',
|
|
417
234
|
{
|
|
@@ -430,37 +247,6 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
430
247
|
</svg>
|
|
431
248
|
)}
|
|
432
249
|
</g>
|
|
433
|
-
{childRows.length > 0 && (
|
|
434
|
-
<IcicleGraphNodes
|
|
435
|
-
table={table}
|
|
436
|
-
row={row}
|
|
437
|
-
colors={colors}
|
|
438
|
-
colorBy={colorBy}
|
|
439
|
-
childRows={childRows}
|
|
440
|
-
x={x}
|
|
441
|
-
y={RowHeight}
|
|
442
|
-
xScale={newXScale}
|
|
443
|
-
total={total}
|
|
444
|
-
totalWidth={totalWidth}
|
|
445
|
-
level={nextLevel}
|
|
446
|
-
path={nextPath}
|
|
447
|
-
curPath={nextCurPath}
|
|
448
|
-
setCurPath={setCurPath}
|
|
449
|
-
setHoveringRow={setHoveringRow}
|
|
450
|
-
setHoveringLevel={setHoveringLevel}
|
|
451
|
-
searchString={searchString}
|
|
452
|
-
sortBy={sortBy}
|
|
453
|
-
darkMode={darkMode}
|
|
454
|
-
profileType={profileType}
|
|
455
|
-
compareMode={compareMode}
|
|
456
|
-
isContextMenuOpen={isContextMenuOpen}
|
|
457
|
-
hoveringName={hoveringName}
|
|
458
|
-
setHoveringName={setHoveringName}
|
|
459
|
-
hoveringRow={hoveringRow}
|
|
460
|
-
colorForSimilarNodes={colorForSimilarNodes}
|
|
461
|
-
highlightSimilarStacksPreference={highlightSimilarStacksPreference}
|
|
462
|
-
/>
|
|
463
|
-
)}
|
|
464
250
|
</>
|
|
465
251
|
);
|
|
466
252
|
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import React, {memo, useEffect, useState} from 'react';
|
|
15
|
+
|
|
16
|
+
import GraphTooltipArrow from '../../GraphTooltipArrow';
|
|
17
|
+
import GraphTooltipArrowContent from '../../GraphTooltipArrow/Content';
|
|
18
|
+
import {DockedGraphTooltip} from '../../GraphTooltipArrow/DockedGraphTooltip';
|
|
19
|
+
import {useTooltipContext} from './TooltipContext';
|
|
20
|
+
|
|
21
|
+
interface MemoizedTooltipProps {
|
|
22
|
+
contextElement: Element | null;
|
|
23
|
+
dockedMetainfo: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const MemoizedTooltip = memo(function MemoizedTooltip({
|
|
27
|
+
contextElement,
|
|
28
|
+
dockedMetainfo,
|
|
29
|
+
}: MemoizedTooltipProps): React.JSX.Element | null {
|
|
30
|
+
const [tooltipRow, setTooltipRow] = useState<number | null>(null);
|
|
31
|
+
const {table, total, totalUnfiltered, profileType, unit, compareAbsolute} = useTooltipContext();
|
|
32
|
+
|
|
33
|
+
// This component subscribes to tooltip updates through a callback
|
|
34
|
+
// passed to the TooltipProvider, avoiding the need to lift state
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const handleTooltipUpdate = (event: CustomEvent<{row: number | null}>): void => {
|
|
37
|
+
setTooltipRow(event.detail.row);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
window.addEventListener('icicle-tooltip-update' as any, handleTooltipUpdate as any);
|
|
41
|
+
return () => {
|
|
42
|
+
window.removeEventListener('icicle-tooltip-update' as any, handleTooltipUpdate as any);
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
if (dockedMetainfo) {
|
|
47
|
+
return (
|
|
48
|
+
<DockedGraphTooltip
|
|
49
|
+
table={table}
|
|
50
|
+
row={tooltipRow}
|
|
51
|
+
total={total}
|
|
52
|
+
totalUnfiltered={totalUnfiltered}
|
|
53
|
+
profileType={profileType}
|
|
54
|
+
unit={unit}
|
|
55
|
+
compareAbsolute={compareAbsolute}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (tooltipRow === null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<GraphTooltipArrow contextElement={contextElement}>
|
|
66
|
+
<GraphTooltipArrowContent
|
|
67
|
+
table={table}
|
|
68
|
+
row={tooltipRow}
|
|
69
|
+
isFixed={false}
|
|
70
|
+
total={total}
|
|
71
|
+
totalUnfiltered={totalUnfiltered}
|
|
72
|
+
profileType={profileType}
|
|
73
|
+
unit={unit}
|
|
74
|
+
compareAbsolute={compareAbsolute}
|
|
75
|
+
/>
|
|
76
|
+
</GraphTooltipArrow>
|
|
77
|
+
);
|
|
78
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import React, {createContext, useCallback, useContext, useMemo, useRef} from 'react';
|
|
15
|
+
|
|
16
|
+
import {Table} from 'apache-arrow';
|
|
17
|
+
|
|
18
|
+
import {ProfileType} from '@parca/parser';
|
|
19
|
+
|
|
20
|
+
interface TooltipState {
|
|
21
|
+
row: number | null;
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface TooltipContextValue {
|
|
27
|
+
table: Table<any>;
|
|
28
|
+
total: bigint;
|
|
29
|
+
totalUnfiltered: bigint;
|
|
30
|
+
profileType?: ProfileType;
|
|
31
|
+
unit?: string;
|
|
32
|
+
compareAbsolute: boolean;
|
|
33
|
+
updateTooltip: (row: number | null, x?: number, y?: number) => void;
|
|
34
|
+
tooltipState: TooltipState;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const TooltipContext = createContext<TooltipContextValue | null>(null);
|
|
38
|
+
|
|
39
|
+
export const useTooltipContext = (): TooltipContextValue => {
|
|
40
|
+
const context = useContext(TooltipContext);
|
|
41
|
+
if (context === undefined || context === null) {
|
|
42
|
+
throw new Error('useTooltipContext must be used within TooltipProvider');
|
|
43
|
+
}
|
|
44
|
+
return context;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
interface TooltipProviderProps {
|
|
48
|
+
children: React.ReactNode;
|
|
49
|
+
table: Table<any>;
|
|
50
|
+
total: bigint;
|
|
51
|
+
totalUnfiltered: bigint;
|
|
52
|
+
profileType?: ProfileType;
|
|
53
|
+
unit?: string;
|
|
54
|
+
compareAbsolute: boolean;
|
|
55
|
+
onTooltipUpdate?: (state: TooltipState) => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const TooltipProvider: React.FC<TooltipProviderProps> = ({
|
|
59
|
+
children,
|
|
60
|
+
table,
|
|
61
|
+
total,
|
|
62
|
+
totalUnfiltered,
|
|
63
|
+
profileType,
|
|
64
|
+
unit,
|
|
65
|
+
compareAbsolute,
|
|
66
|
+
onTooltipUpdate,
|
|
67
|
+
}) => {
|
|
68
|
+
const tooltipStateRef = useRef<TooltipState>({row: null, x: 0, y: 0});
|
|
69
|
+
|
|
70
|
+
const updateTooltip = useCallback(
|
|
71
|
+
(row: number | null, x = 0, y = 0) => {
|
|
72
|
+
tooltipStateRef.current = {row, x, y};
|
|
73
|
+
onTooltipUpdate?.(tooltipStateRef.current);
|
|
74
|
+
},
|
|
75
|
+
[onTooltipUpdate]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const value = useMemo(
|
|
79
|
+
() => ({
|
|
80
|
+
table,
|
|
81
|
+
total,
|
|
82
|
+
totalUnfiltered,
|
|
83
|
+
profileType,
|
|
84
|
+
unit,
|
|
85
|
+
compareAbsolute,
|
|
86
|
+
updateTooltip,
|
|
87
|
+
tooltipState: tooltipStateRef.current,
|
|
88
|
+
}),
|
|
89
|
+
[table, total, totalUnfiltered, profileType, unit, compareAbsolute, updateTooltip]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return <TooltipContext.Provider value={value}>{children}</TooltipContext.Provider>;
|
|
93
|
+
};
|