@parca/profile 0.18.3 → 0.19.0

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.
Files changed (121) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts.map +1 -1
  3. package/dist/GraphTooltipArrow/useGraphTooltip/index.js +6 -3
  4. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -1
  5. package/dist/MetricsGraph/UtilizationMetrics/index.js +6 -2
  6. package/dist/MetricsGraph/index.d.ts.map +1 -1
  7. package/dist/MetricsGraph/index.js +8 -4
  8. package/dist/MetricsSeries/index.d.ts +2 -1
  9. package/dist/MetricsSeries/index.d.ts.map +1 -1
  10. package/dist/MetricsSeries/index.js +2 -1
  11. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts +2 -1
  12. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts.map +1 -1
  13. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +13 -3
  14. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts +1 -0
  15. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts.map +1 -1
  16. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +4 -0
  17. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
  18. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +15 -6
  19. package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.d.ts.map +1 -1
  20. package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.js +5 -4
  21. package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.d.ts +2 -0
  22. package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.d.ts.map +1 -1
  23. package/dist/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.js +3 -2
  24. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +3 -0
  25. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
  26. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +4 -4
  27. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +1 -1
  28. package/dist/ProfileIcicleGraph/index.d.ts +4 -1
  29. package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
  30. package/dist/ProfileIcicleGraph/index.js +22 -6
  31. package/dist/ProfileView/components/DashboardItems/index.d.ts +3 -1
  32. package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
  33. package/dist/ProfileView/components/DashboardItems/index.js +4 -1
  34. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
  35. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +0 -13
  36. package/dist/ProfileView/components/Toolbars/index.d.ts +8 -0
  37. package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
  38. package/dist/ProfileView/components/Toolbars/index.js +6 -2
  39. package/dist/ProfileView/components/ViewSelector/Dropdown.d.ts +1 -0
  40. package/dist/ProfileView/components/ViewSelector/Dropdown.d.ts.map +1 -1
  41. package/dist/ProfileView/components/ViewSelector/Dropdown.js +1 -1
  42. package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
  43. package/dist/ProfileView/components/ViewSelector/index.js +9 -0
  44. package/dist/ProfileView/hooks/useVisualizationState.d.ts +4 -0
  45. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  46. package/dist/ProfileView/hooks/useVisualizationState.js +9 -1
  47. package/dist/ProfileView/index.d.ts.map +1 -1
  48. package/dist/ProfileView/index.js +3 -2
  49. package/dist/ProfileView/types/visualization.d.ts +1 -1
  50. package/dist/ProfileView/types/visualization.d.ts.map +1 -1
  51. package/dist/ProfileViewWithData.js +1 -1
  52. package/dist/Sandwich/components/CalleesSection.d.ts +25 -0
  53. package/dist/Sandwich/components/CalleesSection.d.ts.map +1 -0
  54. package/dist/Sandwich/components/CalleesSection.js +11 -0
  55. package/dist/Sandwich/components/CallersSection.d.ts +25 -0
  56. package/dist/Sandwich/components/CallersSection.d.ts.map +1 -0
  57. package/dist/Sandwich/components/CallersSection.js +11 -0
  58. package/dist/Sandwich/components/TableSection.d.ts +21 -0
  59. package/dist/Sandwich/components/TableSection.d.ts.map +1 -0
  60. package/dist/Sandwich/components/TableSection.js +7 -0
  61. package/dist/Sandwich/index.d.ts +19 -0
  62. package/dist/Sandwich/index.d.ts.map +1 -0
  63. package/dist/Sandwich/index.js +182 -0
  64. package/dist/Sandwich/utils/processRowData.d.ts +11 -0
  65. package/dist/Sandwich/utils/processRowData.d.ts.map +1 -0
  66. package/dist/Sandwich/utils/processRowData.js +53 -0
  67. package/dist/Table/ColorCell.d.ts +7 -0
  68. package/dist/Table/ColorCell.d.ts.map +1 -0
  69. package/dist/Table/ColorCell.js +2 -0
  70. package/dist/Table/MoreDropdown.d.ts +5 -0
  71. package/dist/Table/MoreDropdown.d.ts.map +1 -0
  72. package/dist/Table/MoreDropdown.js +39 -0
  73. package/dist/Table/hooks/useColorManagement.d.ts +14 -0
  74. package/dist/Table/hooks/useColorManagement.d.ts.map +1 -0
  75. package/dist/Table/hooks/useColorManagement.js +32 -0
  76. package/dist/Table/hooks/useTableConfiguration.d.ts +21 -0
  77. package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -0
  78. package/dist/Table/hooks/useTableConfiguration.js +204 -0
  79. package/dist/Table/index.d.ts +14 -4
  80. package/dist/Table/index.d.ts.map +1 -1
  81. package/dist/Table/index.js +34 -332
  82. package/dist/Table/utils/functions.d.ts +1 -0
  83. package/dist/Table/utils/functions.d.ts.map +1 -1
  84. package/dist/styles.css +1 -1
  85. package/dist/useQuery.d.ts +1 -0
  86. package/dist/useQuery.d.ts.map +1 -1
  87. package/dist/useQuery.js +7 -1
  88. package/package.json +7 -7
  89. package/src/GraphTooltipArrow/useGraphTooltip/index.ts +6 -3
  90. package/src/MetricsGraph/UtilizationMetrics/index.tsx +6 -2
  91. package/src/MetricsGraph/index.tsx +12 -2
  92. package/src/MetricsSeries/index.tsx +3 -0
  93. package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +23 -1
  94. package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.tsx +1 -0
  95. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +34 -5
  96. package/src/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.tsx +6 -4
  97. package/src/ProfileIcicleGraph/IcicleGraphArrow/TooltipContext.tsx +5 -1
  98. package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +13 -1
  99. package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +1 -1
  100. package/src/ProfileIcicleGraph/index.tsx +50 -18
  101. package/src/ProfileView/components/DashboardItems/index.tsx +21 -0
  102. package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +11 -25
  103. package/src/ProfileView/components/Toolbars/index.tsx +42 -1
  104. package/src/ProfileView/components/ViewSelector/Dropdown.tsx +2 -1
  105. package/src/ProfileView/components/ViewSelector/index.tsx +11 -0
  106. package/src/ProfileView/hooks/useVisualizationState.ts +16 -1
  107. package/src/ProfileView/index.tsx +7 -0
  108. package/src/ProfileView/types/visualization.ts +7 -1
  109. package/src/ProfileViewWithData.tsx +1 -1
  110. package/src/Sandwich/components/CalleesSection.tsx +87 -0
  111. package/src/Sandwich/components/CallersSection.tsx +88 -0
  112. package/src/Sandwich/components/TableSection.tsx +67 -0
  113. package/src/Sandwich/index.tsx +342 -0
  114. package/src/Sandwich/utils/processRowData.ts +78 -0
  115. package/src/Table/ColorCell.tsx +26 -0
  116. package/src/Table/MoreDropdown.tsx +75 -0
  117. package/src/Table/hooks/useColorManagement.ts +58 -0
  118. package/src/Table/hooks/useTableConfiguration.tsx +237 -0
  119. package/src/Table/index.tsx +37 -470
  120. package/src/Table/utils/functions.ts +1 -0
  121. package/src/useQuery.tsx +10 -1
@@ -11,20 +11,10 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
14
+ import React, {useCallback, useEffect, useMemo, useState} from 'react';
15
15
 
16
- import {flexRender} from '@tanstack/react-table';
17
- import {
18
- createColumnHelper,
19
- type CellContext,
20
- type ColumnDef,
21
- type ExpandedState,
22
- type Row as RowType,
23
- } from '@tanstack/table-core';
24
- import {Int64, Vector, tableFromIPC, vectorFromArray} from 'apache-arrow';
25
- import cx from 'classnames';
16
+ import {tableFromIPC} from 'apache-arrow';
26
17
  import {AnimatePresence, motion} from 'framer-motion';
27
- import {Tooltip} from 'react-tooltip';
28
18
 
29
19
  import {
30
20
  Table as TableComponent,
@@ -32,60 +22,33 @@ import {
32
22
  useParcaContext,
33
23
  useURLState,
34
24
  } from '@parca/components';
35
- import {type RowRendererProps} from '@parca/components/dist/Table';
36
25
  import {useCurrentColorProfile} from '@parca/hooks';
37
26
  import {ProfileType} from '@parca/parser';
38
- import {getLastItem, isSearchMatch, valueFormatter} from '@parca/utilities';
27
+ import {isSearchMatch} from '@parca/utilities';
39
28
 
40
- import {getFilenameColors, getMappingColors} from '../ProfileIcicleGraph/IcicleGraphArrow/';
41
- import {colorByColors} from '../ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes';
42
29
  import useMappingList, {
43
30
  useFilenamesList,
44
31
  } from '../ProfileIcicleGraph/IcicleGraphArrow/useMappingList';
45
32
  import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
46
- import {
47
- ColumnName,
48
- DataRow,
49
- DummyRow,
50
- ROW_HEIGHT,
51
- RowName,
52
- addPlusSign,
53
- getCalleeRows,
54
- getCallerRows,
55
- getRowColor,
56
- getScrollTargetIndex,
57
- isFirstSubRow,
58
- isLastSubRow,
59
- isSubRow,
60
- ratioString,
61
- rowBgClassNames,
62
- sizeToBottomStyle,
63
- sizeToHeightStyle,
64
- sizeToWidthStyle,
65
- } from './utils/functions';
66
- import {getTopAndBottomExpandedRowModel} from './utils/topAndBottomExpandedRowModel';
67
-
68
- const FIELD_MAPPING_FILE = 'mapping_file';
69
- const FIELD_LOCATION_ADDRESS = 'location_address';
70
- const FIELD_FUNCTION_NAME = 'function_name';
71
- const FIELD_FUNCTION_SYSTEM_NAME = 'function_system_name';
72
- const FIELD_FUNCTION_FILE_NAME = 'function_file_name';
73
- const FIELD_FLAT = 'flat';
74
- const FIELD_FLAT_DIFF = 'flat_diff';
75
- const FIELD_CUMULATIVE = 'cumulative';
76
- const FIELD_CUMULATIVE_DIFF = 'cumulative_diff';
77
- const FIELD_CALLERS = 'callers';
78
- const FIELD_CALLEES = 'callees';
79
-
80
- export type Row = DataRow | DummyRow;
81
-
82
- export const isDummyRow = (row: Row): row is DummyRow => {
83
- return 'size' in row;
84
- };
85
-
86
- let doubleClickTimer: NodeJS.Timeout | null = null;
87
-
88
- interface TableProps {
33
+ import {useColorManagement} from './hooks/useColorManagement';
34
+ import {useTableConfiguration} from './hooks/useTableConfiguration';
35
+ import {DataRow, ROW_HEIGHT, RowName, getRowColor} from './utils/functions';
36
+
37
+ export const FIELD_MAPPING_FILE = 'mapping_file';
38
+ export const FIELD_LOCATION_ADDRESS = 'location_address';
39
+ export const FIELD_FUNCTION_NAME = 'function_name';
40
+ export const FIELD_FUNCTION_SYSTEM_NAME = 'function_system_name';
41
+ export const FIELD_FUNCTION_FILE_NAME = 'function_file_name';
42
+ export const FIELD_FLAT = 'flat';
43
+ export const FIELD_FLAT_DIFF = 'flat_diff';
44
+ export const FIELD_CUMULATIVE = 'cumulative';
45
+ export const FIELD_CUMULATIVE_DIFF = 'cumulative_diff';
46
+ export const FIELD_CALLERS = 'callers';
47
+ export const FIELD_CALLEES = 'callees';
48
+
49
+ export type Row = DataRow;
50
+
51
+ export interface TableProps {
89
52
  data?: Uint8Array;
90
53
  total: bigint;
91
54
  filtered: bigint;
@@ -99,134 +62,6 @@ interface TableProps {
99
62
  metadataMappingFiles?: string[];
100
63
  }
101
64
 
102
- const CustomRowRenderer = ({
103
- row,
104
- usePointerCursor,
105
- onRowClick,
106
- onRowDoubleClick,
107
- enableHighlighting,
108
- shouldHighlightRow,
109
- rows,
110
- }: RowRendererProps<Row>): React.JSX.Element => {
111
- const data = row.original;
112
- const isExpanded = row.getIsExpanded();
113
- const _isSubRow = isSubRow(data);
114
- const _isLastSubRow = isLastSubRow(row, rows);
115
- const _isFirstSubRow = isFirstSubRow(row, rows);
116
- const bgClassNames = rowBgClassNames(isExpanded, _isSubRow);
117
- const ref = useRef<HTMLTableRowElement>(null);
118
- const [rowHeight, setRowHeight] = useState<number>(ROW_HEIGHT);
119
-
120
- useEffect(() => {
121
- if (ref.current != null) {
122
- setRowHeight(ref.current.clientHeight + 1); // +1 to account for the bottom border
123
- }
124
- }, []);
125
-
126
- const paddingElement = (
127
- <div
128
- className={cx(
129
- 'bg-white dark:bg-indigo-500 w-[18px] absolute top-[-1px] left-0 border-x border-gray-200 dark:border-gray-700',
130
- {
131
- 'border-b': _isLastSubRow,
132
- 'border-t': _isFirstSubRow,
133
- }
134
- )}
135
- style={{height: `${rowHeight}px`}}
136
- />
137
- );
138
-
139
- if (isDummyRow(data)) {
140
- return (
141
- <tr key={row.id} className={cx(bgClassNames)} ref={ref}>
142
- {paddingElement}
143
- <td colSpan={100} className={`text-center`} style={sizeToHeightStyle(data.size)}>
144
- {data.message}
145
- </td>
146
- </tr>
147
- );
148
- }
149
-
150
- return (
151
- <tr
152
- key={row.id}
153
- ref={ref}
154
- className={cx(usePointerCursor === true ? 'cursor-pointer' : 'cursor-auto', bgClassNames, {
155
- 'hover:bg-[#62626212] dark:hover:bg-[#ffffff12] ': !isExpanded && !_isSubRow,
156
- 'hover:bg-indigo-200 dark:hover:bg-indigo-500': isExpanded || _isSubRow,
157
- })}
158
- onClick={e => {
159
- if (typeof onRowClick !== 'function') {
160
- return;
161
- }
162
- if (e.detail === 2 && doubleClickTimer != null) {
163
- // Prevent the click event from being triggered as it is part of a double click
164
- clearTimeout(doubleClickTimer);
165
- doubleClickTimer = null;
166
- return;
167
- }
168
- if (e.detail === 1) {
169
- // Schedule a single click event to be triggered after 150ms
170
- doubleClickTimer = setTimeout(() => {
171
- doubleClickTimer = null;
172
- onRowClick(row.original);
173
- }, 150);
174
- }
175
- }}
176
- onDoubleClick={
177
- onRowDoubleClick != null
178
- ? () => {
179
- onRowDoubleClick(row, rows);
180
- window.getSelection()?.removeAllRanges();
181
- }
182
- : undefined
183
- }
184
- style={
185
- enableHighlighting !== true || shouldHighlightRow === undefined
186
- ? undefined
187
- : {opacity: shouldHighlightRow(row.original) ? 1 : 0.5}
188
- }
189
- >
190
- {row.getVisibleCells().map((cell, idx) => {
191
- return (
192
- <td
193
- key={cell.id}
194
- className={cx('p-1.5 align-top relative', {
195
- /* @ts-expect-error */
196
- 'text-right': cell.column.columnDef.meta?.align === 'right',
197
- /* @ts-expect-error */
198
- 'text-left': cell.column.columnDef.meta?.align === 'left',
199
- 'pl-5 whitespace-nowrap': idx === 0,
200
- })}
201
- >
202
- {idx === 0 && isExpanded ? (
203
- <>
204
- <div
205
- className={`absolute top-0 left-0 bg-white dark:bg-indigo-500 px-1 uppercase -rotate-90 origin-top-left z-[9] text-[10px] border-l border-y border-gray-200 dark:border-gray-700 text-left`}
206
- style={{...sizeToWidthStyle(3)}}
207
- >
208
- Callers {'->'}
209
- </div>
210
- <div
211
- className={`absolute left-[18px] bg-white dark:bg-indigo-500 px-1 uppercase -rotate-90 origin-bottom-left z-[9] text-[10px] border-r border-y border-gray-200 dark:border-gray-700`}
212
- style={{
213
- ...sizeToWidthStyle(3),
214
- ...sizeToBottomStyle(3),
215
- }}
216
- >
217
- {'<-'} Callees
218
- </div>
219
- </>
220
- ) : null}
221
- {idx === 0 && _isSubRow ? paddingElement : null}
222
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
223
- </td>
224
- );
225
- })}
226
- </tr>
227
- );
228
- };
229
-
230
65
  export const Table = React.memo(function Table({
231
66
  data,
232
67
  total,
@@ -244,12 +79,8 @@ export const Table = React.memo(function Table({
244
79
  alwaysReturnArray: true,
245
80
  });
246
81
 
247
- const [tableColumns] = useURLState<string[]>('table_columns', {
248
- alwaysReturnArray: true,
249
- });
250
82
  const [colorBy, setColorBy] = useURLState('color_by');
251
83
  const {isDarkMode} = useParcaContext();
252
- const [expanded, setExpanded] = useState<ExpandedState>({});
253
84
  const [scrollToIndex, setScrollToIndex] = useState<number | undefined>(undefined);
254
85
 
255
86
  const {compareMode} = useProfileViewContext();
@@ -264,7 +95,6 @@ export const Table = React.memo(function Table({
264
95
 
265
96
  const mappingsList = useMappingList(metadataMappingFiles);
266
97
  const filenamesList = useFilenamesList(table);
267
- const colorByValue = colorBy === undefined || colorBy === '' ? 'binary' : (colorBy as string);
268
98
 
269
99
  const mappingsListCount = useMemo(
270
100
  () => mappingsList.filter(m => m !== '').length,
@@ -279,205 +109,24 @@ export const Table = React.memo(function Table({
279
109
  // eslint-disable-next-line react-hooks/exhaustive-deps
280
110
  }, [mappingsListCount]);
281
111
 
282
- const filenameColors = useMemo(() => {
283
- const colors = getFilenameColors(filenamesList, isDarkMode, currentColorProfile);
284
- return colors;
285
- }, [isDarkMode, filenamesList, currentColorProfile]);
286
-
287
- const mappingColors = useMemo(() => {
288
- const colors = getMappingColors(mappingsList, isDarkMode, currentColorProfile);
289
- return colors;
290
- }, [isDarkMode, mappingsList, currentColorProfile]);
291
-
292
- const colorByList = {
293
- filename: filenameColors,
294
- binary: mappingColors,
295
- };
296
-
297
- type ColorByKey = keyof typeof colorByList;
298
-
299
- const colorByColors: colorByColors = colorByList[colorByValue as ColorByKey];
300
-
301
- const columnHelper = createColumnHelper<Row>();
112
+ const {colorByColors} = useColorManagement({
113
+ isDarkMode,
114
+ currentColorProfile,
115
+ mappingsList,
116
+ filenamesList,
117
+ colorBy: colorBy as string,
118
+ });
302
119
 
303
120
  unit = useMemo(() => unit ?? profileType?.sampleUnit ?? '', [unit, profileType?.sampleUnit]);
304
121
 
305
- const columns = useMemo<Array<ColumnDef<Row>>>(() => {
306
- return [
307
- columnHelper.accessor('colorProperty', {
308
- id: 'color',
309
- header: '',
310
- cell: info => {
311
- const color = info.getValue() as {color: string; mappingFile: string};
312
- return (
313
- <>
314
- <div
315
- className="w-4 h-4 rounded-[4px]"
316
- style={{backgroundColor: color.color}}
317
- data-tooltip-id="table-color-tooltip"
318
- data-tooltip-content={getLastItem(color.mappingFile)}
319
- />
320
- <Tooltip id="table-color-tooltip" />
321
- </>
322
- );
323
- },
324
- size: 10,
325
- }),
326
- columnHelper.accessor('flat', {
327
- id: 'flat',
328
- header: 'Flat',
329
- cell: info => valueFormatter((info as CellContext<DataRow, bigint>).getValue(), unit, 2),
330
- size: 80,
331
- meta: {
332
- align: 'right',
333
- },
334
- invertSorting: true,
335
- }),
336
- columnHelper.accessor('flat', {
337
- id: 'flatPercentage',
338
- header: 'Flat (%)',
339
- cell: info => {
340
- if (isDummyRow(info.row.original)) {
341
- return '';
342
- }
343
- return ratioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
344
- },
345
- size: 120,
346
- meta: {
347
- align: 'right',
348
- },
349
- invertSorting: true,
350
- }),
351
- columnHelper.accessor('flatDiff', {
352
- id: 'flatDiff',
353
- header: 'Flat Diff',
354
- cell: info =>
355
- addPlusSign(valueFormatter((info as CellContext<DataRow, bigint>).getValue(), unit, 2)),
356
- size: 120,
357
- meta: {
358
- align: 'right',
359
- },
360
- invertSorting: true,
361
- }),
362
- columnHelper.accessor('flatDiff', {
363
- id: 'flatDiffPercentage',
364
- header: 'Flat Diff (%)',
365
- cell: info => {
366
- if (isDummyRow(info.row.original)) {
367
- return '';
368
- }
369
- return ratioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
370
- },
371
- size: 120,
372
- meta: {
373
- align: 'right',
374
- },
375
- invertSorting: true,
376
- }),
377
- columnHelper.accessor('cumulative', {
378
- id: 'cumulative',
379
- header: 'Cumulative',
380
- cell: info => valueFormatter((info as CellContext<DataRow, bigint>).getValue(), unit, 2),
381
- size: 150,
382
- meta: {
383
- align: 'right',
384
- },
385
- invertSorting: true,
386
- }),
387
- columnHelper.accessor('cumulative', {
388
- id: 'cumulativePercentage',
389
- header: 'Cumulative (%)',
390
- cell: info => {
391
- if (isDummyRow(info.row.original)) {
392
- return '';
393
- }
394
- return ratioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
395
- },
396
- size: 150,
397
- meta: {
398
- align: 'right',
399
- },
400
- invertSorting: true,
401
- }),
402
- columnHelper.accessor('cumulativeDiff', {
403
- id: 'cumulativeDiff',
404
- header: 'Cumulative Diff',
405
- cell: info =>
406
- addPlusSign(valueFormatter((info as CellContext<DataRow, bigint>).getValue(), unit, 2)),
407
- size: 170,
408
- meta: {
409
- align: 'right',
410
- },
411
- invertSorting: true,
412
- }),
413
- columnHelper.accessor('cumulativeDiff', {
414
- id: 'cumulativeDiffPercentage',
415
- header: 'Cumulative Diff (%)',
416
- cell: info => {
417
- if (isDummyRow(info.row.original)) {
418
- return '';
419
- }
420
- return ratioString((info as CellContext<DataRow, bigint>).getValue(), total, filtered);
421
- },
422
- size: 170,
423
- meta: {
424
- align: 'right',
425
- },
426
- invertSorting: true,
427
- }),
428
- columnHelper.accessor('name', {
429
- id: 'name',
430
- header: 'Name',
431
- cell: info => info.getValue(),
432
- }),
433
- columnHelper.accessor('functionSystemName', {
434
- id: 'functionSystemName',
435
- header: 'Function System Name',
436
- cell: info => info.getValue(),
437
- }),
438
- columnHelper.accessor('functionFileName', {
439
- id: 'functionFileName',
440
- header: 'Function File Name',
441
- cell: info => info.getValue(),
442
- }),
443
- columnHelper.accessor('mappingFile', {
444
- id: 'mappingFile',
445
- header: 'Mapping File',
446
- cell: info => info.getValue(),
447
- }),
448
- ];
449
- // eslint-disable-next-line react-hooks/exhaustive-deps
450
- }, [profileType, unit]);
451
-
452
- const [columnVisibility, setColumnVisibility] = useState(() => {
453
- return {
454
- color: true,
455
- flat: true,
456
- flatPercentage: false,
457
- flatDiff: compareMode,
458
- flatDiffPercentage: false,
459
- cumulative: true,
460
- cumulativePercentage: false,
461
- cumulativeDiff: compareMode,
462
- cumulativeDiffPercentage: false,
463
- name: true,
464
- functionSystemName: false,
465
- functionFileName: false,
466
- mappingFile: false,
467
- };
122
+ const tableConfig = useTableConfiguration({
123
+ unit,
124
+ total,
125
+ filtered,
126
+ compareMode,
468
127
  });
469
128
 
470
- useEffect(() => {
471
- if (Array.isArray(tableColumns)) {
472
- setColumnVisibility(prevState => {
473
- const newState = {...prevState};
474
- (Object.keys(newState) as ColumnName[]).forEach(column => {
475
- newState[column] = tableColumns.includes(column);
476
- });
477
- return newState;
478
- });
479
- }
480
- }, [tableColumns]);
129
+ const {columns, initialSorting, columnVisibility} = tableConfig;
481
130
 
482
131
  const selectSpan = useCallback(
483
132
  (span: string): void => {
@@ -488,10 +137,6 @@ export const Table = React.memo(function Table({
488
137
 
489
138
  const onRowClick = useCallback(
490
139
  (row: Row) => {
491
- if (isDummyRow(row)) {
492
- return;
493
- }
494
-
495
140
  // If there is only one dashboard item, we don't want to select a span
496
141
  if (dashboardItems.length <= 1) {
497
142
  return;
@@ -501,41 +146,6 @@ export const Table = React.memo(function Table({
501
146
  [selectSpan, dashboardItems.length]
502
147
  );
503
148
 
504
- const onRowDoubleClick = useCallback(
505
- (row: RowType<Row>, rows: Array<RowType<Row>>) => {
506
- if (isDummyRow(row.original)) {
507
- return;
508
- }
509
- if (!isSubRow(row.original)) {
510
- row.toggleExpanded();
511
- return;
512
- }
513
- // find the original row for this subrow and toggle it
514
- const newRow = rows.find(
515
- r =>
516
- !isDummyRow(r.original) &&
517
- !isDummyRow(row.original) &&
518
- r.original.name === row.original.name &&
519
- !isSubRow(r.original)
520
- );
521
- const parentRow = rows.find(r => {
522
- const parent = row.getParentRow()!;
523
- if (isDummyRow(parent.original) || isDummyRow(r.original)) {
524
- return false;
525
- }
526
- return r.original.name === parent.original.name;
527
- });
528
- if (parentRow == null || newRow == null) {
529
- return;
530
- }
531
-
532
- newRow.toggleExpanded();
533
-
534
- setScrollToIndex(getScrollTargetIndex(rows, parentRow, newRow));
535
- },
536
- [setScrollToIndex]
537
- );
538
-
539
149
  const shouldHighlightRow = useCallback(
540
150
  (row: Row) => {
541
151
  if (!('name' in row)) {
@@ -551,15 +161,6 @@ export const Table = React.memo(function Table({
551
161
  return currentSearchString != null && currentSearchString?.length > 0;
552
162
  }, [currentSearchString]);
553
163
 
554
- const initialSorting = useMemo(() => {
555
- return [
556
- {
557
- id: compareMode ? 'flatDiff' : 'flat',
558
- desc: false, // columns sorting are inverted - so this is actually descending
559
- },
560
- ];
561
- }, [compareMode]);
562
-
563
164
  const rows: DataRow[] = useMemo(() => {
564
165
  if (table == null || table.numRows === 0) {
565
166
  return [];
@@ -574,8 +175,6 @@ export const Table = React.memo(function Table({
574
175
  const functionFileNameColumn = table.getChild(FIELD_FUNCTION_FILE_NAME);
575
176
  const mappingFileColumn = table.getChild(FIELD_MAPPING_FILE);
576
177
  const locationAddressColumn = table.getChild(FIELD_LOCATION_ADDRESS);
577
- const callersColumn = table.getChild(FIELD_CALLERS);
578
- const calleesColumn = table.getChild(FIELD_CALLEES);
579
178
 
580
179
  const getRow = (i: number): DataRow => {
581
180
  const flat: bigint = flatColumn?.get(i) ?? 0n;
@@ -609,25 +208,7 @@ export const Table = React.memo(function Table({
609
208
  };
610
209
  };
611
210
 
612
- const rows: DataRow[] = [];
613
- for (let i = 0; i < table.numRows; i++) {
614
- const row = getRow(i);
615
- const callerIndices: Vector<Int64> = callersColumn?.get(i) ?? vectorFromArray([]);
616
- const callers: DataRow[] = Array.from(callerIndices.toArray().values()).map(rowIdx => {
617
- return getRow(Number(rowIdx));
618
- });
619
-
620
- const calleeIndices: Vector<Int64> = calleesColumn?.get(i) ?? vectorFromArray([]);
621
- const callees: DataRow[] = Array.from(calleeIndices.toArray().values()).map(rowIdx => {
622
- return getRow(Number(rowIdx));
623
- });
624
-
625
- row.callers = callers;
626
- row.callees = callees;
627
- row.subRows = [...getCallerRows(callers), ...getCalleeRows(callees)];
628
-
629
- rows.push(row);
630
- }
211
+ const rows: DataRow[] = Array.from({length: table.numRows}, (_, i) => getRow(i));
631
212
 
632
213
  return rows;
633
214
  }, [table, colorByColors, colorBy]);
@@ -637,7 +218,7 @@ export const Table = React.memo(function Table({
637
218
  if (currentSearchString == null || rows.length === 0) return;
638
219
 
639
220
  const firstHighlightedRowIndex = rows.findIndex(row => {
640
- return !isDummyRow(row) && isSearchMatch(currentSearchString, row.name);
221
+ return isSearchMatch(currentSearchString, row.name);
641
222
  });
642
223
 
643
224
  if (firstHighlightedRowIndex !== -1) {
@@ -678,20 +259,6 @@ export const Table = React.memo(function Table({
678
259
  enableHighlighting={enableHighlighting}
679
260
  shouldHighlightRow={shouldHighlightRow}
680
261
  usePointerCursor={dashboardItems.length > 1}
681
- onRowDoubleClick={onRowDoubleClick}
682
- getSubRows={row => (isDummyRow(row) ? [] : row.subRows ?? [])}
683
- getCustomExpandedRowModel={getTopAndBottomExpandedRowModel}
684
- expandedState={expanded}
685
- onExpandedChange={getNewState => {
686
- // We only want the new expanded row so passing the exisitng state as empty
687
- // @ts-expect-error
688
- let newState = getNewState({});
689
- if (Object.keys(newState)[0] === Object.keys(expanded)[0]) {
690
- newState = {};
691
- }
692
- setExpanded(newState);
693
- }}
694
- CustomRowRenderer={CustomRowRenderer}
695
262
  scrollToIndex={scrollToIndex}
696
263
  estimatedRowHeight={ROW_HEIGHT}
697
264
  />
@@ -21,6 +21,7 @@ import {hexifyAddress} from '../../utils';
21
21
 
22
22
  export interface DataRow {
23
23
  id: number;
24
+ moreActions?: string;
24
25
  colorProperty: {
25
26
  color: string;
26
27
  mappingFile: string;
package/src/useQuery.tsx CHANGED
@@ -34,6 +34,7 @@ interface UseQueryOptions {
34
34
  sourceOnly?: boolean;
35
35
  invertCallStack?: boolean;
36
36
  binaryFrameFilter?: string[];
37
+ sandwichByFunction?: string;
37
38
  }
38
39
 
39
40
  export const useQuery = (
@@ -57,6 +58,7 @@ export const useQuery = (
57
58
  options?.invertCallStack ?? false,
58
59
  options?.binaryFrameFilter ?? '',
59
60
  profileSource.excludeFunction ?? false,
61
+ options?.sandwichByFunction ?? '',
60
62
  ],
61
63
  queryFn: async () => {
62
64
  const req = profileSource.QueryRequest();
@@ -73,8 +75,10 @@ export const useQuery = (
73
75
  };
74
76
  }
75
77
  req.invertCallStack = options?.invertCallStack ?? false;
78
+
79
+ // Handle filter from ProfileSource (filter by function toolbar)
76
80
  const functionToFilter = req.filterQuery;
77
- if (functionToFilter !== undefined) {
81
+ if (functionToFilter !== undefined && functionToFilter !== '') {
78
82
  req.filter = [
79
83
  ...req.filter,
80
84
  {
@@ -94,6 +98,11 @@ export const useQuery = (
94
98
  ];
95
99
  }
96
100
 
101
+ // Handle sandwich view filter separately
102
+ if (options?.sandwichByFunction !== undefined) {
103
+ req.sandwichByFunction = options.sandwichByFunction;
104
+ }
105
+
97
106
  if (options?.binaryFrameFilter !== undefined && options?.binaryFrameFilter.length > 0) {
98
107
  req.filter = [
99
108
  ...req.filter,