@parca/profile 0.16.91 → 0.16.93
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/Callgraph/index.js +11 -10
- package/dist/IcicleGraph.js +6 -4
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +2 -1
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +1 -13
- package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +2 -1
- package/dist/ProfileExplorer/index.d.ts +1 -1
- package/dist/ProfileExplorer/index.js +15 -33
- package/dist/ProfileIcicleGraph/index.js +2 -2
- package/dist/ProfileView/FilterByFunctionButton.d.ts +4 -1
- package/dist/ProfileView/FilterByFunctionButton.js +14 -11
- package/dist/ProfileView/ViewSelector.d.ts +13 -0
- package/dist/ProfileView/ViewSelector.js +80 -0
- package/dist/ProfileView/index.d.ts +4 -9
- package/dist/ProfileView/index.js +45 -68
- package/dist/ProfileViewWithData.d.ts +1 -1
- package/dist/ProfileViewWithData.js +8 -9
- package/dist/TopTable/index.d.ts +3 -1
- package/dist/TopTable/index.js +9 -8
- package/dist/styles.css +1 -1
- package/package.json +5 -5
- package/src/Callgraph/index.tsx +5 -4
- package/src/IcicleGraph.tsx +15 -3
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +2 -1
- package/src/ProfileExplorer/ProfileExplorerSingle.tsx +2 -1
- package/src/ProfileExplorer/index.tsx +21 -44
- package/src/ProfileIcicleGraph/index.tsx +3 -2
- package/src/ProfileView/FilterByFunctionButton.tsx +18 -18
- package/src/ProfileView/ViewSelector.tsx +117 -0
- package/src/ProfileView/index.tsx +107 -184
- package/src/ProfileViewWithData.tsx +7 -12
- package/src/TopTable/index.tsx +24 -13
|
@@ -14,32 +14,26 @@
|
|
|
14
14
|
import {Profiler, useEffect, useMemo, useState} from 'react';
|
|
15
15
|
import {scaleLinear} from 'd3';
|
|
16
16
|
|
|
17
|
-
import
|
|
18
|
-
import
|
|
17
|
+
import cx from 'classnames';
|
|
18
|
+
import {getNewSpanColor, useURLState} from '@parca/functions';
|
|
19
|
+
import {CloseIcon} from '@parca/icons';
|
|
19
20
|
import {QueryServiceClient, Flamegraph, Top, Callgraph as CallgraphType} from '@parca/client';
|
|
20
21
|
import {Button, Card, useParcaContext} from '@parca/components';
|
|
21
22
|
import {useContainerDimensions} from '@parca/dynamicsize';
|
|
22
|
-
import {
|
|
23
|
-
useAppSelector,
|
|
24
|
-
selectDarkMode,
|
|
25
|
-
selectSearchNodeString,
|
|
26
|
-
selectFilterByFunction,
|
|
27
|
-
useAppDispatch,
|
|
28
|
-
setSearchNodeString,
|
|
29
|
-
} from '@parca/store';
|
|
23
|
+
import {useAppSelector, selectDarkMode} from '@parca/store';
|
|
30
24
|
|
|
31
25
|
import {Callgraph} from '../';
|
|
32
26
|
import ProfileShareButton from '../components/ProfileShareButton';
|
|
33
27
|
import FilterByFunctionButton from './FilterByFunctionButton';
|
|
28
|
+
import ViewSelector from './ViewSelector';
|
|
34
29
|
import ProfileIcicleGraph, {ResizeHandler} from '../ProfileIcicleGraph';
|
|
35
30
|
import {ProfileSource} from '../ProfileSource';
|
|
36
31
|
import TopTable from '../TopTable';
|
|
37
32
|
import useDelayedLoader from '../useDelayedLoader';
|
|
38
33
|
|
|
39
34
|
import '../ProfileView.styles.css';
|
|
40
|
-
import useUserPreference, {USER_PREFERENCES} from '@parca/functions/useUserPreference';
|
|
41
35
|
|
|
42
|
-
type NavigateFunction = (path: string, queryParams: any) => void;
|
|
36
|
+
type NavigateFunction = (path: string, queryParams: any, options?: {replace?: boolean}) => void;
|
|
43
37
|
|
|
44
38
|
export interface FlamegraphData {
|
|
45
39
|
loading: boolean;
|
|
@@ -59,19 +53,11 @@ interface CallgraphData {
|
|
|
59
53
|
error?: any;
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
export type VisualizationType = 'icicle' | 'table' | 'callgraph' | 'both';
|
|
63
|
-
|
|
64
|
-
export interface ProfileVisState {
|
|
65
|
-
currentView: VisualizationType;
|
|
66
|
-
setCurrentView: (view: VisualizationType) => void;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
56
|
export interface ProfileViewProps {
|
|
70
57
|
flamegraphData?: FlamegraphData;
|
|
71
58
|
topTableData?: TopTableData;
|
|
72
59
|
callgraphData?: CallgraphData;
|
|
73
60
|
sampleUnit: string;
|
|
74
|
-
profileVisState: ProfileVisState;
|
|
75
61
|
profileSource?: ProfileSource;
|
|
76
62
|
queryClient?: QueryServiceClient;
|
|
77
63
|
navigateTo?: NavigateFunction;
|
|
@@ -88,22 +74,6 @@ function arrayEquals<T>(a: T[], b: T[]): boolean {
|
|
|
88
74
|
a.every((val, index) => val === b[index])
|
|
89
75
|
);
|
|
90
76
|
}
|
|
91
|
-
export const useProfileVisState = (): ProfileVisState => {
|
|
92
|
-
const [currentView, setCurrentView] = useState<VisualizationType>(() => {
|
|
93
|
-
if (typeof window === 'undefined') {
|
|
94
|
-
return 'icicle';
|
|
95
|
-
}
|
|
96
|
-
const router = parseParams(window.location.search);
|
|
97
|
-
const currentViewFromURL = router.currentProfileView as string;
|
|
98
|
-
|
|
99
|
-
if (currentViewFromURL != null) {
|
|
100
|
-
return currentViewFromURL as VisualizationType;
|
|
101
|
-
}
|
|
102
|
-
return 'icicle';
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
return {currentView, setCurrentView};
|
|
106
|
-
};
|
|
107
77
|
|
|
108
78
|
export const ProfileView = ({
|
|
109
79
|
flamegraphData,
|
|
@@ -113,22 +83,18 @@ export const ProfileView = ({
|
|
|
113
83
|
profileSource,
|
|
114
84
|
queryClient,
|
|
115
85
|
navigateTo,
|
|
116
|
-
profileVisState,
|
|
117
86
|
onDownloadPProf,
|
|
118
87
|
onFlamegraphContainerResize,
|
|
119
88
|
}: ProfileViewProps): JSX.Element => {
|
|
120
|
-
const dispatch = useAppDispatch();
|
|
121
89
|
const {ref, dimensions} = useContainerDimensions();
|
|
122
90
|
const [curPath, setCurPath] = useState<string[]>([]);
|
|
123
|
-
const
|
|
91
|
+
const [rawDashboardItems, setDashboardItems] = useURLState({
|
|
92
|
+
param: 'dashboard_items',
|
|
93
|
+
navigateTo,
|
|
94
|
+
});
|
|
95
|
+
const dashboardItems = rawDashboardItems as string[];
|
|
124
96
|
const isDarkMode = useAppSelector(selectDarkMode);
|
|
125
|
-
const
|
|
126
|
-
const filterByFunctionString = useAppSelector(selectFilterByFunction);
|
|
127
|
-
|
|
128
|
-
const [callgraphEnabled] = useUIFeatureFlag('callgraph');
|
|
129
|
-
const [highlightAfterFilteringEnabled] = useUserPreference<boolean>(
|
|
130
|
-
USER_PREFERENCES.HIGHTLIGHT_AFTER_FILTERING.key
|
|
131
|
-
);
|
|
97
|
+
const isSinglePanelView = dashboardItems.length === 1;
|
|
132
98
|
|
|
133
99
|
const {loader, perf} = useParcaContext();
|
|
134
100
|
|
|
@@ -138,42 +104,17 @@ export const ProfileView = ({
|
|
|
138
104
|
}, [profileSource]);
|
|
139
105
|
|
|
140
106
|
const isLoading = useMemo(() => {
|
|
141
|
-
if (
|
|
107
|
+
if (dashboardItems.includes('icicle')) {
|
|
142
108
|
return Boolean(flamegraphData?.loading);
|
|
143
109
|
}
|
|
144
|
-
if (
|
|
110
|
+
if (dashboardItems.includes('callgraph')) {
|
|
145
111
|
return Boolean(callgraphData?.loading);
|
|
146
112
|
}
|
|
147
|
-
if (
|
|
113
|
+
if (dashboardItems.includes('table')) {
|
|
148
114
|
return Boolean(topTableData?.loading);
|
|
149
115
|
}
|
|
150
|
-
if (currentView === 'both') {
|
|
151
|
-
return Boolean(flamegraphData?.loading) || Boolean(topTableData?.loading);
|
|
152
|
-
}
|
|
153
116
|
return false;
|
|
154
|
-
}, [
|
|
155
|
-
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
if (!highlightAfterFilteringEnabled) {
|
|
158
|
-
if (currentSearchString !== undefined && currentSearchString !== '') {
|
|
159
|
-
dispatch(setSearchNodeString(''));
|
|
160
|
-
}
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
if (isLoading) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (filterByFunctionString === currentSearchString) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
dispatch(setSearchNodeString(filterByFunctionString));
|
|
170
|
-
}, [
|
|
171
|
-
isLoading,
|
|
172
|
-
filterByFunctionString,
|
|
173
|
-
dispatch,
|
|
174
|
-
highlightAfterFilteringEnabled,
|
|
175
|
-
currentSearchString,
|
|
176
|
-
]);
|
|
117
|
+
}, [dashboardItems, callgraphData?.loading, flamegraphData?.loading, topTableData?.loading]);
|
|
177
118
|
|
|
178
119
|
const isLoaderVisible = useDelayedLoader(isLoading);
|
|
179
120
|
|
|
@@ -186,36 +127,94 @@ export const ProfileView = ({
|
|
|
186
127
|
);
|
|
187
128
|
}
|
|
188
129
|
|
|
189
|
-
const resetIcicleGraph = (): void => setCurPath([]);
|
|
190
|
-
|
|
191
130
|
const setNewCurPath = (path: string[]): void => {
|
|
192
131
|
if (!arrayEquals(curPath, path)) {
|
|
193
132
|
setCurPath(path);
|
|
194
133
|
}
|
|
195
134
|
};
|
|
196
135
|
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
setCurrentView(view);
|
|
136
|
+
const maxColor: string = getNewSpanColor(isDarkMode);
|
|
137
|
+
const minColor: string = scaleLinear([isDarkMode ? 'black' : 'white', maxColor])(0.3);
|
|
138
|
+
const colorRange: [string, string] = [minColor, maxColor];
|
|
202
139
|
|
|
203
|
-
|
|
204
|
-
|
|
140
|
+
const getDashboardItemByType = ({
|
|
141
|
+
type,
|
|
142
|
+
isHalfScreen,
|
|
143
|
+
}: {
|
|
144
|
+
type: string;
|
|
145
|
+
isHalfScreen: boolean;
|
|
146
|
+
}): JSX.Element => {
|
|
147
|
+
switch (type) {
|
|
148
|
+
case 'icicle': {
|
|
149
|
+
return flamegraphData?.data != null ? (
|
|
150
|
+
<Profiler id="icicleGraph" onRender={perf?.onRender as React.ProfilerOnRenderCallback}>
|
|
151
|
+
<ProfileIcicleGraph
|
|
152
|
+
curPath={curPath}
|
|
153
|
+
setNewCurPath={setNewCurPath}
|
|
154
|
+
graph={flamegraphData.data}
|
|
155
|
+
sampleUnit={sampleUnit}
|
|
156
|
+
onContainerResize={onFlamegraphContainerResize}
|
|
157
|
+
/>
|
|
158
|
+
</Profiler>
|
|
159
|
+
) : (
|
|
160
|
+
<></>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
case 'callgraph': {
|
|
164
|
+
return callgraphData?.data != null && dimensions?.width !== undefined ? (
|
|
165
|
+
<Callgraph
|
|
166
|
+
graph={callgraphData.data}
|
|
167
|
+
sampleUnit={sampleUnit}
|
|
168
|
+
width={isHalfScreen ? dimensions?.width / 2 : dimensions?.width}
|
|
169
|
+
colorRange={colorRange}
|
|
170
|
+
/>
|
|
171
|
+
) : (
|
|
172
|
+
<></>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
case 'table': {
|
|
176
|
+
return topTableData != null ? (
|
|
177
|
+
<TopTable data={topTableData.data} sampleUnit={sampleUnit} navigateTo={navigateTo} />
|
|
178
|
+
) : (
|
|
179
|
+
<></>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
default: {
|
|
183
|
+
return <></>;
|
|
184
|
+
}
|
|
205
185
|
}
|
|
206
|
-
const router = parseParams(window.location.search);
|
|
207
|
-
navigateTo('/', {
|
|
208
|
-
...router,
|
|
209
|
-
...{currentProfileView: view},
|
|
210
|
-
...{searchString: currentSearchString},
|
|
211
|
-
});
|
|
212
186
|
};
|
|
213
187
|
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
188
|
+
const handleResetView = (): void => {
|
|
189
|
+
setDashboardItems(['icicle']);
|
|
190
|
+
};
|
|
217
191
|
|
|
218
|
-
const
|
|
192
|
+
const handleClosePanel = (visualizationType: string): void => {
|
|
193
|
+
const newDashboardItems = dashboardItems.filter(item => item !== visualizationType);
|
|
194
|
+
setDashboardItems(newDashboardItems);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const dashboardItemsWithViewSelector = dashboardItems.map((dashboardItem, index) => {
|
|
198
|
+
return (
|
|
199
|
+
<div
|
|
200
|
+
key={index}
|
|
201
|
+
className={cx(
|
|
202
|
+
'border dark:bg-gray-700 rounded border-gray-300 dark:border-gray-500 p-3',
|
|
203
|
+
isSinglePanelView ? 'w-full' : 'w-1/2'
|
|
204
|
+
)}
|
|
205
|
+
>
|
|
206
|
+
<div className="w-full flex justify-end pb-2">
|
|
207
|
+
<ViewSelector defaultValue={dashboardItem} navigateTo={navigateTo} position={index} />
|
|
208
|
+
{!isSinglePanelView && (
|
|
209
|
+
<button type="button" onClick={() => handleClosePanel(dashboardItem)} className="pl-2">
|
|
210
|
+
<CloseIcon />
|
|
211
|
+
</button>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
{getDashboardItemByType({type: dashboardItem, isHalfScreen: !isSinglePanelView})}
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
});
|
|
219
218
|
|
|
220
219
|
return (
|
|
221
220
|
<>
|
|
@@ -244,54 +243,28 @@ export const ProfileView = ({
|
|
|
244
243
|
Download pprof
|
|
245
244
|
</Button>
|
|
246
245
|
</div>
|
|
247
|
-
<FilterByFunctionButton />
|
|
246
|
+
<FilterByFunctionButton navigateTo={navigateTo} />
|
|
248
247
|
</div>
|
|
249
248
|
|
|
250
249
|
<div className="flex ml-auto gap-2">
|
|
251
250
|
<Button
|
|
252
251
|
color="neutral"
|
|
253
|
-
onClick={
|
|
254
|
-
disabled={
|
|
252
|
+
onClick={handleResetView}
|
|
253
|
+
disabled={isSinglePanelView}
|
|
255
254
|
className="whitespace-nowrap text-ellipsis"
|
|
256
255
|
>
|
|
257
|
-
Reset
|
|
256
|
+
Reset Panels
|
|
258
257
|
</Button>
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
<div className="flex">
|
|
271
|
-
<Button
|
|
272
|
-
variant={`${currentView === 'table' ? 'primary' : 'neutral'}`}
|
|
273
|
-
className="items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
|
|
274
|
-
onClick={() => switchProfileView('table')}
|
|
275
|
-
>
|
|
276
|
-
Table
|
|
277
|
-
</Button>
|
|
278
|
-
|
|
279
|
-
<Button
|
|
280
|
-
variant={`${currentView === 'both' ? 'primary' : 'neutral'}`}
|
|
281
|
-
className="items-center rounded-tl-none rounded-tr-none rounded-bl-none rounded-br-none border-l-0 border-r-0 w-auto px-8 whitespace-nowrap no-outline-on-buttons text-ellipsis"
|
|
282
|
-
onClick={() => switchProfileView('both')}
|
|
283
|
-
>
|
|
284
|
-
Both
|
|
285
|
-
</Button>
|
|
286
|
-
|
|
287
|
-
<Button
|
|
288
|
-
variant={`${currentView === 'icicle' ? 'primary' : 'neutral'}`}
|
|
289
|
-
className="items-center rounded-tl-none rounded-bl-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
|
|
290
|
-
onClick={() => switchProfileView('icicle')}
|
|
291
|
-
>
|
|
292
|
-
Icicle Graph
|
|
293
|
-
</Button>
|
|
294
|
-
</div>
|
|
259
|
+
<ViewSelector
|
|
260
|
+
defaultValue=""
|
|
261
|
+
navigateTo={navigateTo}
|
|
262
|
+
position={-1}
|
|
263
|
+
placeholderText="Add panel..."
|
|
264
|
+
primary
|
|
265
|
+
addView={true}
|
|
266
|
+
disabled={!isSinglePanelView || dashboardItems.length < 1}
|
|
267
|
+
/>
|
|
295
268
|
</div>
|
|
296
269
|
</div>
|
|
297
270
|
|
|
@@ -299,57 +272,7 @@ export const ProfileView = ({
|
|
|
299
272
|
<>{loader}</>
|
|
300
273
|
) : (
|
|
301
274
|
<div ref={ref} className="flex space-x-4 justify-between w-full">
|
|
302
|
-
{
|
|
303
|
-
<div className="w-full">
|
|
304
|
-
<Profiler
|
|
305
|
-
id="icicleGraph"
|
|
306
|
-
onRender={perf?.onRender as React.ProfilerOnRenderCallback}
|
|
307
|
-
>
|
|
308
|
-
<ProfileIcicleGraph
|
|
309
|
-
curPath={curPath}
|
|
310
|
-
setNewCurPath={setNewCurPath}
|
|
311
|
-
graph={flamegraphData.data}
|
|
312
|
-
sampleUnit={sampleUnit}
|
|
313
|
-
onContainerResize={onFlamegraphContainerResize}
|
|
314
|
-
/>
|
|
315
|
-
</Profiler>
|
|
316
|
-
</div>
|
|
317
|
-
)}
|
|
318
|
-
{currentView === 'callgraph' && callgraphData?.data != null && (
|
|
319
|
-
<div className="w-full">
|
|
320
|
-
{dimensions?.width !== undefined && (
|
|
321
|
-
<Callgraph
|
|
322
|
-
graph={callgraphData.data}
|
|
323
|
-
sampleUnit={sampleUnit}
|
|
324
|
-
width={dimensions?.width}
|
|
325
|
-
colorRange={colorRange}
|
|
326
|
-
/>
|
|
327
|
-
)}
|
|
328
|
-
</div>
|
|
329
|
-
)}
|
|
330
|
-
{currentView === 'table' && topTableData != null && (
|
|
331
|
-
<div className="w-full">
|
|
332
|
-
<TopTable data={topTableData.data} sampleUnit={sampleUnit} />
|
|
333
|
-
</div>
|
|
334
|
-
)}
|
|
335
|
-
{currentView === 'both' && (
|
|
336
|
-
<>
|
|
337
|
-
<div className="w-1/2">
|
|
338
|
-
<TopTable data={topTableData?.data} sampleUnit={sampleUnit} />
|
|
339
|
-
</div>
|
|
340
|
-
|
|
341
|
-
<div className="w-1/2">
|
|
342
|
-
{flamegraphData != null && (
|
|
343
|
-
<ProfileIcicleGraph
|
|
344
|
-
curPath={curPath}
|
|
345
|
-
setNewCurPath={setNewCurPath}
|
|
346
|
-
graph={flamegraphData.data}
|
|
347
|
-
sampleUnit={sampleUnit}
|
|
348
|
-
/>
|
|
349
|
-
)}
|
|
350
|
-
</div>
|
|
351
|
-
</>
|
|
352
|
-
)}
|
|
275
|
+
{dashboardItemsWithViewSelector}
|
|
353
276
|
</div>
|
|
354
277
|
)}
|
|
355
278
|
</Card.Body>
|
|
@@ -11,19 +11,16 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
import {useEffect, useState} from 'react';
|
|
14
15
|
import {QueryServiceClient, QueryRequest_ReportType} from '@parca/client';
|
|
15
|
-
|
|
16
16
|
import {useQuery} from './useQuery';
|
|
17
|
-
import {ProfileView
|
|
17
|
+
import {ProfileView} from './ProfileView';
|
|
18
18
|
import {ProfileSource} from './ProfileSource';
|
|
19
19
|
import {downloadPprof} from './utils';
|
|
20
20
|
import {useGrpcMetadata, useParcaContext} from '@parca/components';
|
|
21
|
-
import {saveAsBlob} from '@parca/functions';
|
|
22
|
-
import {useEffect, useState} from 'react';
|
|
21
|
+
import {saveAsBlob, NavigateFunction, useURLState} from '@parca/functions';
|
|
23
22
|
import useUserPreference, {USER_PREFERENCES} from '@parca/functions/useUserPreference';
|
|
24
23
|
|
|
25
|
-
export type NavigateFunction = (path: string, queryParams: any) => void;
|
|
26
|
-
|
|
27
24
|
interface ProfileViewWithDataProps {
|
|
28
25
|
queryClient: QueryServiceClient;
|
|
29
26
|
profileSource: ProfileSource;
|
|
@@ -36,9 +33,8 @@ export const ProfileViewWithData = ({
|
|
|
36
33
|
profileSource,
|
|
37
34
|
navigateTo,
|
|
38
35
|
}: ProfileViewWithDataProps): JSX.Element => {
|
|
39
|
-
const profileVisState = useProfileVisState();
|
|
40
36
|
const metadata = useGrpcMetadata();
|
|
41
|
-
const
|
|
37
|
+
const [dashboardItems] = useURLState({param: 'dashboard_items', navigateTo});
|
|
42
38
|
const [nodeTrimThreshold, setNodeTrimThreshold] = useState<number>(0);
|
|
43
39
|
const [disableTrimming] = useUserPreference<boolean>(USER_PREFERENCES.DISABLE_GRAPH_TRIMMING.key);
|
|
44
40
|
|
|
@@ -64,7 +60,7 @@ export const ProfileViewWithData = ({
|
|
|
64
60
|
response: flamegraphResponse,
|
|
65
61
|
error: flamegraphError,
|
|
66
62
|
} = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
|
|
67
|
-
skip:
|
|
63
|
+
skip: !dashboardItems.includes('icicle'),
|
|
68
64
|
nodeTrimThreshold,
|
|
69
65
|
});
|
|
70
66
|
const {perf} = useParcaContext();
|
|
@@ -86,7 +82,7 @@ export const ProfileViewWithData = ({
|
|
|
86
82
|
response: topTableResponse,
|
|
87
83
|
error: topTableError,
|
|
88
84
|
} = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
|
|
89
|
-
skip:
|
|
85
|
+
skip: !dashboardItems.includes('table'),
|
|
90
86
|
});
|
|
91
87
|
|
|
92
88
|
const {
|
|
@@ -94,7 +90,7 @@ export const ProfileViewWithData = ({
|
|
|
94
90
|
response: callgraphResponse,
|
|
95
91
|
error: callgraphError,
|
|
96
92
|
} = useQuery(queryClient, profileSource, QueryRequest_ReportType.CALLGRAPH, {
|
|
97
|
-
skip:
|
|
93
|
+
skip: !dashboardItems.includes('callgraph'),
|
|
98
94
|
});
|
|
99
95
|
|
|
100
96
|
const sampleUnit = profileSource.ProfileType().sampleUnit;
|
|
@@ -136,7 +132,6 @@ export const ProfileViewWithData = ({
|
|
|
136
132
|
: undefined,
|
|
137
133
|
error: callgraphError,
|
|
138
134
|
}}
|
|
139
|
-
profileVisState={profileVisState}
|
|
140
135
|
sampleUnit={sampleUnit}
|
|
141
136
|
profileSource={profileSource}
|
|
142
137
|
queryClient={queryClient}
|
package/src/TopTable/index.tsx
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
|
|
14
14
|
import React, {useCallback, useMemo} from 'react';
|
|
15
15
|
|
|
16
|
-
import {getLastItem, valueFormatter, isSearchMatch} from '@parca/functions';
|
|
17
16
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
getLastItem,
|
|
18
|
+
valueFormatter,
|
|
19
|
+
isSearchMatch,
|
|
20
|
+
NavigateFunction,
|
|
21
|
+
parseParams,
|
|
22
|
+
selectQueryParam,
|
|
23
|
+
} from '@parca/functions';
|
|
24
24
|
import {TopNode, TopNodeMeta, Top} from '@parca/client';
|
|
25
25
|
import {Table} from '@parca/components';
|
|
26
26
|
import {createColumnHelper, ColumnDef} from '@tanstack/react-table';
|
|
@@ -32,6 +32,7 @@ import '../TopTable.styles.css';
|
|
|
32
32
|
interface TopTableProps {
|
|
33
33
|
data?: Top;
|
|
34
34
|
sampleUnit: string;
|
|
35
|
+
navigateTo?: NavigateFunction;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export const RowLabel = (meta: TopNodeMeta | undefined): string => {
|
|
@@ -60,10 +61,11 @@ const addPlusSign = (num: string): string => {
|
|
|
60
61
|
return `+${num}`;
|
|
61
62
|
};
|
|
62
63
|
|
|
63
|
-
export const TopTable = ({data: top, sampleUnit: unit}: TopTableProps): JSX.Element => {
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
64
|
+
export const TopTable = ({data: top, sampleUnit: unit, navigateTo}: TopTableProps): JSX.Element => {
|
|
65
|
+
const router = parseParams(window.location.search);
|
|
66
|
+
const currentSearchString = selectQueryParam('search_string') as string;
|
|
67
|
+
const compareMode =
|
|
68
|
+
Boolean(selectQueryParam('compare_a')) && Boolean(selectQueryParam('compare_b'));
|
|
67
69
|
|
|
68
70
|
const columns = React.useMemo(() => {
|
|
69
71
|
const cols: Array<ColumnDef<TopNode, any>> = [
|
|
@@ -117,9 +119,18 @@ export const TopTable = ({data: top, sampleUnit: unit}: TopTableProps): JSX.Elem
|
|
|
117
119
|
|
|
118
120
|
const selectSpan = useCallback(
|
|
119
121
|
(span: string): void => {
|
|
120
|
-
|
|
122
|
+
if (navigateTo != null) {
|
|
123
|
+
navigateTo(
|
|
124
|
+
'/',
|
|
125
|
+
{
|
|
126
|
+
...router,
|
|
127
|
+
...{search_string: span.trim()},
|
|
128
|
+
},
|
|
129
|
+
{replace: true}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
121
132
|
},
|
|
122
|
-
[
|
|
133
|
+
[navigateTo, router]
|
|
123
134
|
);
|
|
124
135
|
|
|
125
136
|
const onRowClick = useCallback(
|