@parca/profile 0.19.12 → 0.19.14
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.js +1 -1
- package/dist/GraphTooltipArrow/index.js +2 -2
- package/dist/MatchersInput/index.d.ts +3 -1
- package/dist/MatchersInput/index.d.ts.map +1 -1
- package/dist/MatchersInput/index.js +8 -4
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +12 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.js +9 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +7 -3
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.js +17 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +2 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +23 -8
- package/dist/ProfileIcicleGraph/index.d.ts +3 -1
- package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/index.js +6 -4
- package/dist/ProfileSelector/QueryControls.d.ts.map +1 -1
- package/dist/ProfileSelector/QueryControls.js +1 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +4 -2
- package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
- package/dist/ProfileView/components/DashboardItems/index.js +1 -1
- package/dist/ProfileView/components/ShareButton/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ShareButton/index.js +1 -1
- package/dist/ProfileView/components/Toolbars/index.d.ts +0 -2
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/index.js +4 -5
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/index.js +18 -11
- package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
- package/dist/ProfileView/context/DashboardContext.js +5 -0
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +4 -3
- package/dist/Sandwich/components/CalleesSection.d.ts +1 -2
- package/dist/Sandwich/components/CalleesSection.d.ts.map +1 -1
- package/dist/Sandwich/components/CalleesSection.js +2 -6
- package/dist/Sandwich/components/CallersSection.d.ts +4 -2
- package/dist/Sandwich/components/CallersSection.d.ts.map +1 -1
- package/dist/Sandwich/components/CallersSection.js +45 -9
- package/dist/Sandwich/components/TableSection.js +1 -1
- package/dist/Sandwich/index.d.ts +0 -1
- package/dist/Sandwich/index.d.ts.map +1 -1
- package/dist/Sandwich/index.js +27 -79
- package/dist/SimpleMatchers/index.d.ts +2 -0
- package/dist/SimpleMatchers/index.d.ts.map +1 -1
- package/dist/SimpleMatchers/index.js +16 -6
- package/dist/Table/MoreDropdown.d.ts.map +1 -1
- package/dist/Table/MoreDropdown.js +1 -2
- package/dist/Table/TableContextMenu.d.ts +9 -0
- package/dist/Table/TableContextMenu.d.ts.map +1 -0
- package/dist/Table/TableContextMenu.js +38 -0
- package/dist/Table/TableContextMenuWrapper.d.ts +10 -0
- package/dist/Table/TableContextMenuWrapper.d.ts.map +1 -0
- package/dist/Table/TableContextMenuWrapper.js +30 -0
- package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
- package/dist/Table/hooks/useTableConfiguration.js +2 -20
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +65 -5
- package/dist/ViewMatchers/index.d.ts +2 -0
- package/dist/ViewMatchers/index.d.ts.map +1 -1
- package/dist/ViewMatchers/index.js +14 -4
- package/dist/contexts/MatchersInputLabelsContext.d.ts +3 -1
- package/dist/contexts/MatchersInputLabelsContext.d.ts.map +1 -1
- package/dist/contexts/MatchersInputLabelsContext.js +3 -3
- package/dist/contexts/SimpleMatchersLabelContext.d.ts +3 -1
- package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +1 -1
- package/dist/contexts/SimpleMatchersLabelContext.js +2 -2
- package/dist/styles.css +1 -1
- package/package.json +3 -3
- package/src/GraphTooltipArrow/Content.tsx +3 -3
- package/src/GraphTooltipArrow/index.tsx +2 -2
- package/src/MatchersInput/index.tsx +17 -4
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +19 -3
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenuWrapper.tsx +10 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +19 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/MemoizedTooltip.tsx +20 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +40 -6
- package/src/ProfileIcicleGraph/index.tsx +20 -2
- package/src/ProfileSelector/QueryControls.tsx +6 -0
- package/src/ProfileSelector/index.tsx +12 -2
- package/src/ProfileView/components/DashboardItems/index.tsx +0 -1
- package/src/ProfileView/components/ShareButton/index.tsx +9 -3
- package/src/ProfileView/components/Toolbars/index.tsx +7 -23
- package/src/ProfileView/components/ViewSelector/index.tsx +20 -11
- package/src/ProfileView/context/DashboardContext.tsx +6 -0
- package/src/ProfileView/index.tsx +12 -4
- package/src/Sandwich/components/CalleesSection.tsx +1 -7
- package/src/Sandwich/components/CallersSection.tsx +92 -35
- package/src/Sandwich/components/TableSection.tsx +2 -2
- package/src/Sandwich/index.tsx +20 -107
- package/src/SimpleMatchers/index.tsx +20 -4
- package/src/Table/MoreDropdown.tsx +1 -2
- package/src/Table/TableContextMenu.tsx +70 -0
- package/src/Table/TableContextMenuWrapper.tsx +48 -0
- package/src/Table/hooks/useTableConfiguration.tsx +2 -25
- package/src/Table/index.tsx +84 -5
- package/src/ViewMatchers/index.tsx +17 -3
- package/src/contexts/MatchersInputLabelsContext.tsx +10 -2
- package/src/contexts/SimpleMatchersLabelContext.tsx +5 -1
|
@@ -15,12 +15,11 @@ import {useEffect, useMemo, useState} from 'react';
|
|
|
15
15
|
|
|
16
16
|
import {createColumnHelper, type ColumnDef} from '@tanstack/table-core';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {useURLState} from '@parca/components';
|
|
19
19
|
import {valueFormatter} from '@parca/utilities';
|
|
20
20
|
|
|
21
21
|
import {type Row} from '..';
|
|
22
22
|
import {ColorCell} from '../ColorCell';
|
|
23
|
-
import MoreDropdown from '../MoreDropdown';
|
|
24
23
|
import {addPlusSign, ratioString, type ColumnName} from '../utils/functions';
|
|
25
24
|
|
|
26
25
|
interface UseTableConfigurationProps {
|
|
@@ -47,10 +46,6 @@ export function useTableConfiguration({
|
|
|
47
46
|
const [tableColumns] = useURLState<string[]>('table_columns', {
|
|
48
47
|
alwaysReturnArray: true,
|
|
49
48
|
});
|
|
50
|
-
const [dashboardItems] = useURLState<string[]>('dashboard_items', {
|
|
51
|
-
alwaysReturnArray: true,
|
|
52
|
-
});
|
|
53
|
-
const {enableSandwichView} = useParcaContext();
|
|
54
49
|
|
|
55
50
|
const [columnVisibility, setColumnVisibility] = useState(() => {
|
|
56
51
|
return {
|
|
@@ -204,26 +199,8 @@ export function useTableConfiguration({
|
|
|
204
199
|
}),
|
|
205
200
|
];
|
|
206
201
|
|
|
207
|
-
if (
|
|
208
|
-
dashboardItems.length === 1 &&
|
|
209
|
-
dashboardItems[0] === 'table' &&
|
|
210
|
-
enableSandwichView === true
|
|
211
|
-
) {
|
|
212
|
-
baseColumns.unshift(
|
|
213
|
-
columnHelper.accessor('moreActions', {
|
|
214
|
-
id: 'moreActions',
|
|
215
|
-
header: '',
|
|
216
|
-
cell: info => {
|
|
217
|
-
return <MoreDropdown functionName={info.row.original.name} />;
|
|
218
|
-
},
|
|
219
|
-
size: 10,
|
|
220
|
-
enableSorting: false,
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
202
|
return baseColumns;
|
|
226
|
-
}, [unit, total, filtered, columnHelper
|
|
203
|
+
}, [unit, total, filtered, columnHelper]);
|
|
227
204
|
|
|
228
205
|
const initialSorting = useMemo(() => {
|
|
229
206
|
return [
|
package/src/Table/index.tsx
CHANGED
|
@@ -11,10 +11,11 @@
|
|
|
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, useState} from 'react';
|
|
14
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {tableFromIPC} from 'apache-arrow';
|
|
17
17
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
18
|
+
import {useContextMenu} from 'react-contexify';
|
|
18
19
|
|
|
19
20
|
import {
|
|
20
21
|
Table as TableComponent,
|
|
@@ -30,6 +31,7 @@ import useMappingList, {
|
|
|
30
31
|
useFilenamesList,
|
|
31
32
|
} from '../ProfileIcicleGraph/IcicleGraphArrow/useMappingList';
|
|
32
33
|
import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
|
|
34
|
+
import TableContextMenuWrapper, {TableContextMenuWrapperRef} from './TableContextMenuWrapper';
|
|
33
35
|
import {useColorManagement} from './hooks/useColorManagement';
|
|
34
36
|
import {useTableConfiguration} from './hooks/useTableConfiguration';
|
|
35
37
|
import {DataRow, ROW_HEIGHT, RowName, getRowColor} from './utils/functions';
|
|
@@ -78,13 +80,19 @@ export const Table = React.memo(function Table({
|
|
|
78
80
|
const [dashboardItems] = useURLState<string[]>('dashboard_items', {
|
|
79
81
|
alwaysReturnArray: true,
|
|
80
82
|
});
|
|
81
|
-
|
|
83
|
+
const [_, setSandwichFunctionName] = useURLState<string | undefined>('sandwich_function_name');
|
|
82
84
|
const [colorBy, setColorBy] = useURLState('color_by');
|
|
83
85
|
const {isDarkMode} = useParcaContext();
|
|
84
86
|
const [scrollToIndex, setScrollToIndex] = useState<number | undefined>(undefined);
|
|
85
87
|
|
|
86
88
|
const {compareMode} = useProfileViewContext();
|
|
87
89
|
|
|
90
|
+
const MENU_ID = 'table-context-menu';
|
|
91
|
+
const contextMenuRef = useRef<TableContextMenuWrapperRef>(null);
|
|
92
|
+
const {show} = useContextMenu({
|
|
93
|
+
id: MENU_ID,
|
|
94
|
+
});
|
|
95
|
+
|
|
88
96
|
const table = useMemo(() => {
|
|
89
97
|
if (loading || data == null) {
|
|
90
98
|
return null;
|
|
@@ -130,9 +138,13 @@ export const Table = React.memo(function Table({
|
|
|
130
138
|
|
|
131
139
|
const selectSpan = useCallback(
|
|
132
140
|
(span: string): void => {
|
|
133
|
-
|
|
141
|
+
if (dashboardItems.includes('icicle')) {
|
|
142
|
+
setSearchString(span.trim());
|
|
143
|
+
} else {
|
|
144
|
+
setSandwichFunctionName(span.trim());
|
|
145
|
+
}
|
|
134
146
|
},
|
|
135
|
-
[setSearchString]
|
|
147
|
+
[setSearchString, setSandwichFunctionName, dashboardItems]
|
|
136
148
|
);
|
|
137
149
|
|
|
138
150
|
const onRowClick = useCallback(
|
|
@@ -213,6 +225,72 @@ export const Table = React.memo(function Table({
|
|
|
213
225
|
return rows;
|
|
214
226
|
}, [table, colorByColors, colorBy]);
|
|
215
227
|
|
|
228
|
+
const handleTableContextMenu = useCallback(
|
|
229
|
+
(e: React.MouseEvent) => {
|
|
230
|
+
e.preventDefault();
|
|
231
|
+
|
|
232
|
+
// Find the closest table row element
|
|
233
|
+
const target = e.target as Element;
|
|
234
|
+
const rowElement = target.closest('tr');
|
|
235
|
+
|
|
236
|
+
if (rowElement !== null) {
|
|
237
|
+
// Look for a data attribute that might contain the actual row ID
|
|
238
|
+
const rowId = rowElement.getAttribute('data-row-id') ?? rowElement.getAttribute('data-id');
|
|
239
|
+
|
|
240
|
+
if (rowId != null && rowId.length > 0) {
|
|
241
|
+
// Find the row by ID
|
|
242
|
+
const actualRowIndex = parseInt(rowId, 10);
|
|
243
|
+
|
|
244
|
+
if (actualRowIndex >= 0 && actualRowIndex < rows.length) {
|
|
245
|
+
const row = rows[actualRowIndex];
|
|
246
|
+
|
|
247
|
+
contextMenuRef.current?.setRow(row, () => {
|
|
248
|
+
show({
|
|
249
|
+
event: e,
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Fallback: try to find row by matching text content
|
|
257
|
+
const nameCell = rowElement.querySelector('td:last-child'); // Name is usually the last column
|
|
258
|
+
if (nameCell !== null) {
|
|
259
|
+
const cellText = nameCell.textContent?.trim();
|
|
260
|
+
|
|
261
|
+
if (cellText != null && cellText.length > 0) {
|
|
262
|
+
// First try exact match
|
|
263
|
+
let matchingRow = rows.find(row => row.name === cellText);
|
|
264
|
+
|
|
265
|
+
// If no exact match, try partial match (in case of truncation)
|
|
266
|
+
if (matchingRow == null) {
|
|
267
|
+
matchingRow = rows.find(
|
|
268
|
+
row => row.name.includes(cellText) || cellText.includes(row.name)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// If still no match, try matching the end of the name (for cases like package.function)
|
|
273
|
+
if (matchingRow == null) {
|
|
274
|
+
matchingRow = rows.find(
|
|
275
|
+
row =>
|
|
276
|
+
row.name.endsWith(cellText) || cellText.endsWith(row.name.split('.').pop() ?? '')
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (matchingRow != null) {
|
|
281
|
+
contextMenuRef.current?.setRow(matchingRow, () => {
|
|
282
|
+
show({
|
|
283
|
+
event: e,
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
[rows, show]
|
|
292
|
+
);
|
|
293
|
+
|
|
216
294
|
useEffect(() => {
|
|
217
295
|
setTimeout(() => {
|
|
218
296
|
if (currentSearchString == null || rows.length === 0) return;
|
|
@@ -249,7 +327,8 @@ export const Table = React.memo(function Table({
|
|
|
249
327
|
transition={{duration: 0.5}}
|
|
250
328
|
>
|
|
251
329
|
<div className="relative">
|
|
252
|
-
<
|
|
330
|
+
<TableContextMenuWrapper ref={contextMenuRef} menuId={MENU_ID} />
|
|
331
|
+
<div className="font-robotoMono h-[80vh] w-full" onContextMenu={handleTableContextMenu}>
|
|
253
332
|
<TableComponent
|
|
254
333
|
data={rows}
|
|
255
334
|
columns={columns}
|
|
@@ -19,7 +19,7 @@ import cx from 'classnames';
|
|
|
19
19
|
import {QueryServiceClient} from '@parca/client';
|
|
20
20
|
import {useGrpcMetadata} from '@parca/components';
|
|
21
21
|
import {Query} from '@parca/parser';
|
|
22
|
-
import {sanitizeLabelValue} from '@parca/utilities';
|
|
22
|
+
import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
|
|
23
23
|
|
|
24
24
|
import CustomSelect, {SelectItem} from '../SimpleMatchers/Select';
|
|
25
25
|
|
|
@@ -30,6 +30,8 @@ interface Props {
|
|
|
30
30
|
currentQuery: Query;
|
|
31
31
|
queryClient: QueryServiceClient;
|
|
32
32
|
setMatchersString: (arg: string) => void;
|
|
33
|
+
start?: number;
|
|
34
|
+
end?: number;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
const ViewMatchers: React.FC<Props> = ({
|
|
@@ -38,6 +40,8 @@ const ViewMatchers: React.FC<Props> = ({
|
|
|
38
40
|
queryClient,
|
|
39
41
|
runQuery,
|
|
40
42
|
setMatchersString,
|
|
43
|
+
start,
|
|
44
|
+
end,
|
|
41
45
|
currentQuery,
|
|
42
46
|
}) => {
|
|
43
47
|
const [labelValuesMap, setLabelValuesMap] = useState<Record<string, string[]>>({});
|
|
@@ -77,7 +81,17 @@ const ViewMatchers: React.FC<Props> = ({
|
|
|
77
81
|
async (labelName: string): Promise<string[]> => {
|
|
78
82
|
try {
|
|
79
83
|
const response = await queryClient.values(
|
|
80
|
-
{
|
|
84
|
+
{
|
|
85
|
+
labelName,
|
|
86
|
+
match: [],
|
|
87
|
+
profileType,
|
|
88
|
+
...(start !== undefined && end !== undefined
|
|
89
|
+
? {
|
|
90
|
+
start: millisToProtoTimestamp(start),
|
|
91
|
+
end: millisToProtoTimestamp(end),
|
|
92
|
+
}
|
|
93
|
+
: {}),
|
|
94
|
+
},
|
|
81
95
|
{meta: metadata}
|
|
82
96
|
).response;
|
|
83
97
|
return sanitizeLabelValue(response.labelValues);
|
|
@@ -86,7 +100,7 @@ const ViewMatchers: React.FC<Props> = ({
|
|
|
86
100
|
return [];
|
|
87
101
|
}
|
|
88
102
|
},
|
|
89
|
-
[queryClient, metadata, profileType]
|
|
103
|
+
[queryClient, metadata, profileType, start, end]
|
|
90
104
|
);
|
|
91
105
|
|
|
92
106
|
useEffect(() => {
|
|
@@ -40,6 +40,8 @@ interface LabelsProviderProps {
|
|
|
40
40
|
children: React.ReactNode;
|
|
41
41
|
queryClient: QueryServiceClient;
|
|
42
42
|
profileType: string;
|
|
43
|
+
start?: number;
|
|
44
|
+
end?: number;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
// With there being the possibility of having utilization labels, we need to be able to determine whether the labels to be used are utilization labels or profiling data labels.
|
|
@@ -48,13 +50,17 @@ export function LabelsProvider({
|
|
|
48
50
|
children,
|
|
49
51
|
queryClient,
|
|
50
52
|
profileType,
|
|
53
|
+
start,
|
|
54
|
+
end,
|
|
51
55
|
}: LabelsProviderProps): JSX.Element {
|
|
52
56
|
const [currentLabelName, setCurrentLabelName] = React.useState<string | null>(null);
|
|
53
57
|
const utilizationLabels = useUtilizationLabels();
|
|
54
58
|
|
|
55
59
|
const {result: labelNamesResponse, loading: isLabelNamesLoading} = useLabelNames(
|
|
56
60
|
queryClient,
|
|
57
|
-
profileType
|
|
61
|
+
profileType,
|
|
62
|
+
start,
|
|
63
|
+
end
|
|
58
64
|
);
|
|
59
65
|
|
|
60
66
|
const labelNamesFromAPI = useMemo(() => {
|
|
@@ -68,7 +74,9 @@ export function LabelsProvider({
|
|
|
68
74
|
const {result: labelValuesOriginal, loading: isLabelValuesLoading} = useLabelValues(
|
|
69
75
|
queryClient,
|
|
70
76
|
currentLabelName ?? '',
|
|
71
|
-
profileType
|
|
77
|
+
profileType,
|
|
78
|
+
start,
|
|
79
|
+
end
|
|
72
80
|
);
|
|
73
81
|
|
|
74
82
|
const utilizationLabelValues = useFetchUtilizationLabelValues(
|
|
@@ -38,6 +38,8 @@ interface LabelProviderProps {
|
|
|
38
38
|
queryClient: QueryServiceClient;
|
|
39
39
|
profileType: string;
|
|
40
40
|
labelNameFromMatchers: string[];
|
|
41
|
+
start?: number;
|
|
42
|
+
end?: number;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
// With there being the possibility of having utilization labels, we need to be able to determine whether the labels to be used are utilization labels or profiling data labels.
|
|
@@ -48,9 +50,11 @@ export function LabelProvider({
|
|
|
48
50
|
queryClient,
|
|
49
51
|
profileType,
|
|
50
52
|
labelNameFromMatchers,
|
|
53
|
+
start,
|
|
54
|
+
end,
|
|
51
55
|
}: LabelProviderProps): JSX.Element {
|
|
52
56
|
const utilizationLabelResponse = useUtilizationLabels();
|
|
53
|
-
const {loading, result} = useLabelNames(queryClient, profileType);
|
|
57
|
+
const {loading, result} = useLabelNames(queryClient, profileType, start, end);
|
|
54
58
|
|
|
55
59
|
const profileValues = useMemo(() => {
|
|
56
60
|
const profileLabelNames =
|