@parca/profile 0.19.142 → 0.19.144
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/useGraphTooltipMetaInfo/index.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +22 -28
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +72 -73
- package/dist/ProfileFlameChart/SamplesStrips/index.d.ts +2 -2
- package/dist/ProfileFlameChart/SamplesStrips/index.d.ts.map +1 -1
- package/dist/ProfileFlameChart/index.d.ts.map +1 -1
- package/dist/ProfileFlameChart/index.js +20 -24
- package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +13 -14
- package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.js +6 -5
- package/dist/ProfileFlameGraph/index.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/index.js +8 -7
- package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/index.js +6 -8
- package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
- package/dist/ProfileSelector/MetricsGraphSection.js +48 -55
- package/dist/ProfileSelector/index.d.ts +1 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +216 -210
- package/dist/ProfileSelector/useAutoQuerySelector.d.ts +1 -3
- package/dist/ProfileSelector/useAutoQuerySelector.d.ts.map +1 -1
- package/dist/ProfileSelector/useAutoQuerySelector.js +133 -104
- package/dist/ProfileView/components/ActionButtons/SortByDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/ActionButtons/SortByDropdown.js +24 -25
- package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -1
- package/dist/ProfileView/components/ColorStackLegend.js +3 -5
- package/dist/ProfileView/components/InvertCallStack/index.d.ts.map +1 -1
- package/dist/ProfileView/components/InvertCallStack/index.js +47 -47
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts +1 -2
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts.map +1 -1
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +37 -34
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +282 -294
- package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +7 -8
- package/dist/ProfileView/components/Toolbars/index.d.ts +2 -2
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/index.js +1 -1
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/index.js +53 -75
- package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
- package/dist/ProfileView/context/DashboardContext.js +36 -44
- package/dist/ProfileView/hooks/useResetFlameGraphState.d.ts.map +1 -1
- package/dist/ProfileView/hooks/useResetFlameGraphState.js +8 -7
- package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.d.ts.map +1 -1
- package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.js +59 -59
- package/dist/ProfileView/hooks/useResetStateOnSeriesChange.d.ts.map +1 -1
- package/dist/ProfileView/hooks/useResetStateOnSeriesChange.js +37 -22
- package/dist/ProfileView/hooks/useVisualizationState.d.ts +3 -3
- package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
- package/dist/ProfileView/hooks/useVisualizationState.js +116 -147
- package/dist/ProfileViewWithData.d.ts.map +1 -1
- package/dist/ProfileViewWithData.js +35 -45
- package/dist/Sandwich/index.d.ts.map +1 -1
- package/dist/Sandwich/index.js +6 -5
- package/dist/SourceView/index.d.ts.map +1 -1
- package/dist/SourceView/index.js +6 -4
- package/dist/SourceView/useSelectedLineRange.d.ts.map +1 -1
- package/dist/SourceView/useSelectedLineRange.js +52 -76
- package/dist/Table/MoreDropdown.d.ts.map +1 -1
- package/dist/Table/MoreDropdown.js +42 -53
- package/dist/Table/TableContextMenu.d.ts.map +1 -1
- package/dist/Table/TableContextMenu.js +15 -19
- package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
- package/dist/Table/hooks/useTableConfiguration.js +107 -115
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +16 -16
- package/dist/TopTable/index.d.ts.map +1 -1
- package/dist/TopTable/index.js +112 -127
- package/dist/hooks/urlParsers.d.ts +18 -0
- package/dist/hooks/urlParsers.d.ts.map +1 -0
- package/dist/hooks/urlParsers.js +44 -0
- package/dist/hooks/useColorBy.d.ts +5 -0
- package/dist/hooks/useColorBy.d.ts.map +1 -0
- package/dist/hooks/useColorBy.js +63 -0
- package/dist/hooks/useCompareModeMeta.d.ts.map +1 -1
- package/dist/hooks/useCompareModeMeta.js +94 -138
- package/dist/hooks/useDashboardItems.d.ts +5 -0
- package/dist/hooks/useDashboardItems.d.ts.map +1 -0
- package/dist/hooks/useDashboardItems.js +68 -0
- package/dist/hooks/useQueryState.d.ts +4 -4
- package/dist/hooks/useQueryState.d.ts.map +1 -1
- package/dist/hooks/useQueryState.js +127 -122
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -12
- package/dist/useQuery.js +2 -1
- package/dist/useSumBy.d.ts +1 -1
- package/dist/useSumBy.d.ts.map +1 -1
- package/dist/useSumBy.js +2 -2
- package/package.json +4 -3
- package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +11 -13
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +11 -9
- package/src/ProfileFlameChart/SamplesStrips/index.tsx +2 -2
- package/src/ProfileFlameChart/index.tsx +21 -28
- package/src/ProfileFlameGraph/FlameGraphArrow/ContextMenu.tsx +10 -9
- package/src/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.tsx +5 -3
- package/src/ProfileFlameGraph/index.tsx +6 -9
- package/src/ProfileMetricsGraph/index.tsx +6 -8
- package/src/ProfileSelector/MetricsGraphSection.tsx +5 -10
- package/src/ProfileSelector/index.tsx +33 -33
- package/src/ProfileSelector/useAutoQuerySelector.ts +64 -42
- package/src/ProfileView/components/ActionButtons/SortByDropdown.tsx +10 -6
- package/src/ProfileView/components/ColorStackLegend.tsx +2 -4
- package/src/ProfileView/components/InvertCallStack/index.tsx +5 -4
- package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.tsx +94 -192
- package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts +21 -21
- package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +24 -25
- package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +4 -5
- package/src/ProfileView/components/Toolbars/index.tsx +3 -3
- package/src/ProfileView/components/ViewSelector/index.tsx +9 -16
- package/src/ProfileView/context/DashboardContext.tsx +6 -6
- package/src/ProfileView/hooks/useResetFlameGraphState.ts +6 -4
- package/src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts +24 -26
- package/src/ProfileView/hooks/useResetStateOnSeriesChange.ts +16 -8
- package/src/ProfileView/hooks/useVisualizationState.ts +61 -69
- package/src/ProfileViewWithData.tsx +29 -35
- package/src/Sandwich/index.tsx +4 -3
- package/src/SourceView/index.tsx +4 -2
- package/src/SourceView/useSelectedLineRange.ts +34 -19
- package/src/Table/MoreDropdown.tsx +9 -11
- package/src/Table/TableContextMenu.tsx +10 -13
- package/src/Table/hooks/useTableConfiguration.tsx +3 -4
- package/src/Table/index.tsx +12 -21
- package/src/TopTable/index.tsx +3 -4
- package/src/hooks/urlParsers.ts +38 -0
- package/src/hooks/useColorBy.ts +42 -0
- package/src/hooks/useCompareModeMeta.ts +61 -91
- package/src/hooks/useDashboardItems.ts +46 -0
- package/src/hooks/useQueryState.test.tsx +275 -345
- package/src/hooks/useQueryState.ts +153 -120
- package/src/index.tsx +16 -15
- package/src/useQuery.tsx +1 -1
- package/src/useSumBy.ts +3 -3
|
@@ -13,14 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
import {useEffect, useMemo, useRef} from 'react';
|
|
15
15
|
|
|
16
|
+
import {createParser, useQueryState} from 'nuqs';
|
|
17
|
+
|
|
16
18
|
import {LabelSet, QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
|
|
17
|
-
import {
|
|
18
|
-
Button,
|
|
19
|
-
useParcaContext,
|
|
20
|
-
useURLState,
|
|
21
|
-
useURLStateCustom,
|
|
22
|
-
type OptionsCustom,
|
|
23
|
-
} from '@parca/components';
|
|
19
|
+
import {Button, useParcaContext} from '@parca/components';
|
|
24
20
|
import {Matcher, MatcherTypes, ProfileType, Query} from '@parca/parser';
|
|
25
21
|
import {TimeUnits, formatDate, formatDuration} from '@parca/utilities';
|
|
26
22
|
|
|
@@ -29,6 +25,7 @@ import {boundsFromProfileSource} from '../ProfileFlameGraph/FlameGraphArrow/util
|
|
|
29
25
|
import {MergedProfileSource, ProfileSource, timeFormat} from '../ProfileSource';
|
|
30
26
|
import {useProfileFilters} from '../ProfileView/components/ProfileFilters/useProfileFilters';
|
|
31
27
|
import type {SamplesData} from '../ProfileView/types/visualization';
|
|
28
|
+
import {flamechartDimensionParser} from '../hooks/urlParsers';
|
|
32
29
|
import {useQuery} from '../useQuery';
|
|
33
30
|
import {NumberDuo} from '../utils';
|
|
34
31
|
import {SamplesStrip} from './SamplesStrips';
|
|
@@ -38,11 +35,8 @@ interface SelectedTimeframe {
|
|
|
38
35
|
bounds: NumberDuo;
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
const
|
|
42
|
-
parse: (value: string
|
|
43
|
-
if (value == null || value === '' || value === 'undefined' || Array.isArray(value)) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
38
|
+
const timeframeParser = createParser<SelectedTimeframe>({
|
|
39
|
+
parse: (value: string) => {
|
|
46
40
|
try {
|
|
47
41
|
const [labelPart, boundsPart] = value.split('|');
|
|
48
42
|
if (labelPart != null && boundsPart != null) {
|
|
@@ -61,16 +55,13 @@ const TimeframeStateSerializer: OptionsCustom<SelectedTimeframe | undefined> = {
|
|
|
61
55
|
} catch {
|
|
62
56
|
// Ignore parsing errors
|
|
63
57
|
}
|
|
64
|
-
return
|
|
58
|
+
return null;
|
|
65
59
|
},
|
|
66
|
-
|
|
67
|
-
if (value == null) {
|
|
68
|
-
return '';
|
|
69
|
-
}
|
|
60
|
+
serialize: (value: SelectedTimeframe) => {
|
|
70
61
|
const labelsStr = value.labels.labels.map(l => `${l.name}:${l.value}`).join(',');
|
|
71
62
|
return `${labelsStr}|${value.bounds[0]},${value.bounds[1]}`;
|
|
72
63
|
},
|
|
73
|
-
};
|
|
64
|
+
}).withOptions({history: 'replace'});
|
|
74
65
|
|
|
75
66
|
interface ProfileFlameChartProps {
|
|
76
67
|
samplesData?: SamplesData;
|
|
@@ -132,14 +123,16 @@ export const ProfileFlameChart = ({
|
|
|
132
123
|
const {protoFilters} = useProfileFilters();
|
|
133
124
|
const zoomControlsRef = useRef<HTMLDivElement>(null);
|
|
134
125
|
|
|
135
|
-
const [selectedTimeframe, setSelectedTimeframe] =
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
const [selectedTimeframe, setSelectedTimeframe] = useQueryState(
|
|
127
|
+
'flamechart_timeframe',
|
|
128
|
+
timeframeParser
|
|
129
|
+
);
|
|
138
130
|
|
|
139
131
|
// Read flamechart dimension from URL state to detect changes
|
|
140
|
-
const [flamechartDimension] =
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
const [flamechartDimension] = useQueryState(
|
|
133
|
+
'flamechart_dimension',
|
|
134
|
+
flamechartDimensionParser.withDefault([])
|
|
135
|
+
);
|
|
143
136
|
|
|
144
137
|
// Reset selection when the parent time range (profileSource) changes
|
|
145
138
|
const timeBoundsKey = boundsFromProfileSource(profileSource).join(',');
|
|
@@ -147,7 +140,7 @@ export const ProfileFlameChart = ({
|
|
|
147
140
|
useEffect(() => {
|
|
148
141
|
if (prevTimeBoundsKey.current !== timeBoundsKey) {
|
|
149
142
|
prevTimeBoundsKey.current = timeBoundsKey;
|
|
150
|
-
setSelectedTimeframe(
|
|
143
|
+
void setSelectedTimeframe(null);
|
|
151
144
|
}
|
|
152
145
|
}, [timeBoundsKey, setSelectedTimeframe]);
|
|
153
146
|
|
|
@@ -157,16 +150,16 @@ export const ProfileFlameChart = ({
|
|
|
157
150
|
useEffect(() => {
|
|
158
151
|
if (prevDimensionKey.current !== dimensionKey) {
|
|
159
152
|
prevDimensionKey.current = dimensionKey;
|
|
160
|
-
setSelectedTimeframe(
|
|
153
|
+
void setSelectedTimeframe(null);
|
|
161
154
|
}
|
|
162
155
|
}, [dimensionKey, setSelectedTimeframe]);
|
|
163
156
|
|
|
164
157
|
// Handle timeframe selection from strips
|
|
165
158
|
const handleSelectedTimeframe = (labels: LabelSet, bounds: NumberDuo | undefined): void => {
|
|
166
159
|
if (bounds === undefined) {
|
|
167
|
-
setSelectedTimeframe(
|
|
160
|
+
void setSelectedTimeframe(null);
|
|
168
161
|
} else {
|
|
169
|
-
setSelectedTimeframe({labels, bounds});
|
|
162
|
+
void setSelectedTimeframe({labels, bounds});
|
|
170
163
|
}
|
|
171
164
|
};
|
|
172
165
|
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
import {Icon} from '@iconify/react';
|
|
15
15
|
import {Table} from '@uwdata/flechette';
|
|
16
16
|
import cx from 'classnames';
|
|
17
|
+
import {useQueryState} from 'nuqs';
|
|
17
18
|
import {Item, Menu, Separator, Submenu} from 'react-contexify';
|
|
18
19
|
import {Tooltip} from 'react-tooltip';
|
|
19
20
|
|
|
20
|
-
import {useParcaContext
|
|
21
|
+
import {useParcaContext} from '@parca/components';
|
|
21
22
|
import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
|
|
22
23
|
import {ProfileType} from '@parca/parser';
|
|
23
24
|
import {TEST_IDS} from '@parca/test-utils';
|
|
@@ -25,6 +26,8 @@ import {getLastItem} from '@parca/utilities';
|
|
|
25
26
|
|
|
26
27
|
import {useGraphTooltip} from '../../GraphTooltipArrow/useGraphTooltip';
|
|
27
28
|
import {useGraphTooltipMetaInfo} from '../../GraphTooltipArrow/useGraphTooltipMetaInfo';
|
|
29
|
+
import {stringParam} from '../../hooks/urlParsers';
|
|
30
|
+
import {useDashboardItems} from '../../hooks/useDashboardItems';
|
|
28
31
|
import {hexifyAddress, truncateString} from '../../utils';
|
|
29
32
|
|
|
30
33
|
interface ContextMenuProps {
|
|
@@ -83,12 +86,10 @@ const ContextMenu = ({
|
|
|
83
86
|
inlined,
|
|
84
87
|
} = useGraphTooltipMetaInfo({table, row});
|
|
85
88
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const [sandwichFunctionName, setSandwichFunctionName] = useURLState<string | undefined>(
|
|
91
|
-
'sandwich_function_name'
|
|
89
|
+
const {dashboardItems, setDashboardItems} = useDashboardItems();
|
|
90
|
+
const [_sandwichFunctionName, setSandwichFunctionName] = useQueryState(
|
|
91
|
+
'sandwich_function_name',
|
|
92
|
+
stringParam
|
|
92
93
|
);
|
|
93
94
|
|
|
94
95
|
if (contextMenuData === null) {
|
|
@@ -195,12 +196,12 @@ const ContextMenu = ({
|
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
if (dashboardItems.includes('sandwich')) {
|
|
198
|
-
setSandwichFunctionName(functionName);
|
|
199
|
+
void setSandwichFunctionName(functionName);
|
|
199
200
|
hideMenu();
|
|
200
201
|
return;
|
|
201
202
|
}
|
|
202
203
|
|
|
203
|
-
setSandwichFunctionName(functionName);
|
|
204
|
+
void setSandwichFunctionName(functionName);
|
|
204
205
|
setDashboardItems([...dashboardItems, 'sandwich']);
|
|
205
206
|
hideMenu();
|
|
206
207
|
}}
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
import {useEffect, useRef, useState} from 'react';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {useQueryState} from 'nuqs';
|
|
19
|
+
|
|
20
|
+
import {stringParam} from '../../hooks/urlParsers';
|
|
19
21
|
|
|
20
22
|
interface Props {
|
|
21
23
|
text: string;
|
|
@@ -69,9 +71,9 @@ function TextWithEllipsis({text, x, y, width}: Props): JSX.Element {
|
|
|
69
71
|
'use no memo';
|
|
70
72
|
const textRef = useRef<SVGTextElement>(null);
|
|
71
73
|
const [displayText, setDisplayText] = useState(text);
|
|
72
|
-
const [alignFunctionName] =
|
|
74
|
+
const [alignFunctionName] = useQueryState('align_function_name', stringParam.withDefault('left'));
|
|
73
75
|
|
|
74
|
-
const showFunctionNameFromLeft = alignFunctionName === 'left'
|
|
76
|
+
const showFunctionNameFromLeft = alignFunctionName === 'left';
|
|
75
77
|
|
|
76
78
|
useEffect(() => {
|
|
77
79
|
const textElement = textRef.current;
|
|
@@ -15,15 +15,11 @@ import React, {LegacyRef, ReactNode, useCallback, useEffect, useMemo, useState}
|
|
|
15
15
|
|
|
16
16
|
import cx from 'classnames';
|
|
17
17
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
18
|
+
import {useQueryState} from 'nuqs';
|
|
18
19
|
import {useMeasure} from 'react-use';
|
|
19
20
|
|
|
20
21
|
import {FlamegraphArrow} from '@parca/client';
|
|
21
|
-
import {
|
|
22
|
-
FlameGraphSkeleton,
|
|
23
|
-
SandwichFlameGraphSkeleton,
|
|
24
|
-
useParcaContext,
|
|
25
|
-
useURLState,
|
|
26
|
-
} from '@parca/components';
|
|
22
|
+
import {FlameGraphSkeleton, SandwichFlameGraphSkeleton, useParcaContext} from '@parca/components';
|
|
27
23
|
import {ProfileType} from '@parca/parser';
|
|
28
24
|
import {TEST_IDS, testId} from '@parca/test-utils';
|
|
29
25
|
import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
|
|
@@ -33,6 +29,7 @@ import DiffLegend from '../ProfileView/components/DiffLegend';
|
|
|
33
29
|
import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
|
|
34
30
|
import {useProfileMetadata} from '../ProfileView/hooks/useProfileMetadata';
|
|
35
31
|
import {useVisualizationState} from '../ProfileView/hooks/useVisualizationState';
|
|
32
|
+
import {boolParam} from '../hooks/urlParsers';
|
|
36
33
|
import {FlameGraphArrow} from './FlameGraphArrow';
|
|
37
34
|
import {CurrentPathFrame} from './FlameGraphArrow/utils';
|
|
38
35
|
|
|
@@ -137,8 +134,8 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
|
|
|
137
134
|
// For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
|
|
138
135
|
const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
|
|
139
136
|
|
|
140
|
-
const [compareAbsolute
|
|
141
|
-
const isCompareAbsolute = compareAbsolute === 'true';
|
|
137
|
+
const [compareAbsolute] = useQueryState('compare_absolute', boolParam);
|
|
138
|
+
const isCompareAbsolute = compareAbsolute ?? compareAbsoluteDefault === 'true';
|
|
142
139
|
|
|
143
140
|
const mappingsListCount = useMemo(
|
|
144
141
|
() => mappingsList.filter(m => m !== '').length,
|
|
@@ -180,7 +177,7 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
|
|
|
180
177
|
// If there is only one mapping file, we want to color by filename by default.
|
|
181
178
|
useEffect(() => {
|
|
182
179
|
if (mappingsListCount === 1 && colorBy !== 'filename') {
|
|
183
|
-
setColorBy('filename');
|
|
180
|
+
void setColorBy('filename');
|
|
184
181
|
}
|
|
185
182
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
186
183
|
}, [mappingsListCount]);
|
|
@@ -15,6 +15,7 @@ import {useEffect, useMemo, useState} from 'react';
|
|
|
15
15
|
|
|
16
16
|
import {Icon} from '@iconify/react';
|
|
17
17
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
18
|
+
import {useQueryState} from 'nuqs';
|
|
18
19
|
|
|
19
20
|
import {
|
|
20
21
|
Label,
|
|
@@ -25,11 +26,8 @@ import {
|
|
|
25
26
|
import {
|
|
26
27
|
DateTimeRange,
|
|
27
28
|
MetricsGraphSkeleton,
|
|
28
|
-
NumberParser,
|
|
29
|
-
NumberSerializer,
|
|
30
29
|
TextWithTooltip,
|
|
31
30
|
useParcaContext,
|
|
32
|
-
useURLStateCustom,
|
|
33
31
|
} from '@parca/components';
|
|
34
32
|
import {Query} from '@parca/parser';
|
|
35
33
|
import {TEST_IDS, testId} from '@parca/test-utils';
|
|
@@ -38,6 +36,7 @@ import {capitalizeOnlyFirstLetter, formatDate, timePattern, valueFormatter} from
|
|
|
38
36
|
import {MergedProfileSelection, ProfileSelection} from '..';
|
|
39
37
|
import MetricsGraph, {ContextMenuItemOrSubmenu, Series, SeriesPoint} from '../MetricsGraph';
|
|
40
38
|
import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
|
|
39
|
+
import {intParam} from '../hooks/urlParsers';
|
|
41
40
|
import {getStepCountFromScreenWidth, useQueryRange} from './hooks/useQueryRange';
|
|
42
41
|
|
|
43
42
|
const createProfileContextMenuItems = (
|
|
@@ -200,11 +199,10 @@ const ProfileMetricsGraph = ({
|
|
|
200
199
|
comparing = false,
|
|
201
200
|
sumBy,
|
|
202
201
|
}: ProfileMetricsGraphProps): JSX.Element => {
|
|
203
|
-
const [rawStepCount] =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
});
|
|
202
|
+
const [rawStepCount] = useQueryState(
|
|
203
|
+
'step_count',
|
|
204
|
+
intParam.withDefault(getStepCountFromScreenWidth(10))
|
|
205
|
+
);
|
|
208
206
|
// Clamp step count so the step duration is at least 1 second as we don't have this enforced server-side anymore.
|
|
209
207
|
const stepCount = useMemo(() => {
|
|
210
208
|
const maxForOneSecond = Math.floor((to - from) / 1000);
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import cx from 'classnames';
|
|
15
15
|
|
|
16
16
|
import {Label, QueryServiceClient} from '@parca/client';
|
|
17
|
-
import {DateTimeRange, useParcaContext
|
|
17
|
+
import {DateTimeRange, useParcaContext} from '@parca/components';
|
|
18
18
|
import {Query} from '@parca/parser';
|
|
19
19
|
|
|
20
20
|
import {ProfileSelection} from '..';
|
|
@@ -67,7 +67,6 @@ export function MetricsGraphSection({
|
|
|
67
67
|
hasNoProfileTypes = false,
|
|
68
68
|
}: MetricsGraphSectionProps): JSX.Element {
|
|
69
69
|
const resetStateOnSeriesChange = useResetStateOnSeriesChange();
|
|
70
|
-
const batchUpdates = useURLStateBatch();
|
|
71
70
|
const {profileExplorer} = useParcaContext();
|
|
72
71
|
const {heightStyle} = useMetricsGraphDimensions(comparing, profileExplorer?.metricsGraph.height);
|
|
73
72
|
const handleTimeRangeChange = (range: DateTimeRange): void => {
|
|
@@ -117,10 +116,8 @@ export function MetricsGraphSection({
|
|
|
117
116
|
|
|
118
117
|
if (hasChanged) {
|
|
119
118
|
// Immediately apply the filter when adding label matchers from the graph
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
commitDraft(undefined, newQuery.toString());
|
|
123
|
-
});
|
|
119
|
+
setNewQueryExpression(newQuery.toString());
|
|
120
|
+
commitDraft(undefined, newQuery.toString());
|
|
124
121
|
}
|
|
125
122
|
};
|
|
126
123
|
|
|
@@ -141,10 +138,8 @@ export function MetricsGraphSection({
|
|
|
141
138
|
|
|
142
139
|
const mergeFrom = timestamp;
|
|
143
140
|
const mergeTo = query.profileType().delta ? mergeFrom + BigInt(duration) : mergeFrom;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
setProfileSelection(mergeFrom, mergeTo, query);
|
|
147
|
-
});
|
|
141
|
+
resetStateOnSeriesChange(); // reset some state when a new series is selected
|
|
142
|
+
setProfileSelection(mergeFrom, mergeTo, query);
|
|
148
143
|
};
|
|
149
144
|
|
|
150
145
|
return (
|
|
@@ -14,16 +14,10 @@
|
|
|
14
14
|
import {Dispatch, SetStateAction, useCallback, useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
17
|
+
import {useQueryState as useNuqsQueryState} from 'nuqs';
|
|
17
18
|
|
|
18
19
|
import {ProfileTypesRequest, ProfileTypesResponse, QueryServiceClient} from '@parca/client';
|
|
19
|
-
import {
|
|
20
|
-
DateTimeRange,
|
|
21
|
-
IconButton,
|
|
22
|
-
useGrpcMetadata,
|
|
23
|
-
useParcaContext,
|
|
24
|
-
useURLState,
|
|
25
|
-
useURLStateBatch,
|
|
26
|
-
} from '@parca/components';
|
|
20
|
+
import {DateTimeRange, IconButton, useGrpcMetadata, useParcaContext} from '@parca/components';
|
|
27
21
|
import {CloseIcon} from '@parca/icons';
|
|
28
22
|
import {Query} from '@parca/parser';
|
|
29
23
|
import {TEST_IDS, testId} from '@parca/test-utils';
|
|
@@ -36,6 +30,7 @@ import {
|
|
|
36
30
|
import {QueryControls} from '../QueryControls';
|
|
37
31
|
import {LabelsQueryProvider, useLabelsQueryProvider} from '../contexts/LabelsQueryProvider';
|
|
38
32
|
import {UnifiedLabelsProvider} from '../contexts/UnifiedLabelsContext';
|
|
33
|
+
import {stringParam} from '../hooks/urlParsers';
|
|
39
34
|
import {useLabelNames} from '../hooks/useLabels';
|
|
40
35
|
import {useQueryState} from '../hooks/useQueryState';
|
|
41
36
|
import useGrpcQuery from '../useGrpcQuery';
|
|
@@ -109,7 +104,7 @@ const ProfileSelector = ({
|
|
|
109
104
|
closeProfile,
|
|
110
105
|
enforcedProfileName,
|
|
111
106
|
comparing,
|
|
112
|
-
navigateTo,
|
|
107
|
+
navigateTo: _navigateTo,
|
|
113
108
|
showMetricsGraph = true,
|
|
114
109
|
showSumBySelector = true,
|
|
115
110
|
showProfileTypeSelector = true,
|
|
@@ -118,8 +113,16 @@ const ProfileSelector = ({
|
|
|
118
113
|
onSearchHook,
|
|
119
114
|
}: ProfileSelectorProps): JSX.Element => {
|
|
120
115
|
const {externalProfilerComponent, additionalMetricsGraph} = useParcaContext();
|
|
121
|
-
const [queryBrowserMode,
|
|
122
|
-
|
|
116
|
+
const [queryBrowserMode, setRawQueryBrowserMode] = useNuqsQueryState(
|
|
117
|
+
'query_browser_mode',
|
|
118
|
+
stringParam
|
|
119
|
+
);
|
|
120
|
+
const setQueryBrowserMode = useCallback(
|
|
121
|
+
(mode: string | null) => {
|
|
122
|
+
void setRawQueryBrowserMode(mode);
|
|
123
|
+
},
|
|
124
|
+
[setRawQueryBrowserMode]
|
|
125
|
+
);
|
|
123
126
|
|
|
124
127
|
const profileFilterDefaults = externalProfilerComponent?.profileFilterDefaults as
|
|
125
128
|
| ProfileFilter[]
|
|
@@ -245,27 +248,25 @@ const ProfileSelector = ({
|
|
|
245
248
|
const selectedProfileName = query.profileName();
|
|
246
249
|
|
|
247
250
|
const setQueryExpression = (updateTs = false): void => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
});
|
|
251
|
+
if (onSearchHook != null) {
|
|
252
|
+
onSearchHook();
|
|
253
|
+
}
|
|
254
|
+
// When updateTs is true, re-evaluate the time range to current values
|
|
255
|
+
if (updateTs) {
|
|
256
|
+
// Force re-evaluation of time range (important for relative ranges like "last 15 minutes")
|
|
257
|
+
const currentFrom = timeRangeSelection.getFromMs(true);
|
|
258
|
+
const currentTo = timeRangeSelection.getToMs(true);
|
|
259
|
+
const currentRangeKey = timeRangeSelection.getRangeKey();
|
|
260
|
+
// Commit with refreshed time range
|
|
261
|
+
commitDraft({
|
|
262
|
+
from: currentFrom,
|
|
263
|
+
to: currentTo,
|
|
264
|
+
timeSelection: currentRangeKey,
|
|
265
|
+
});
|
|
266
|
+
} else {
|
|
267
|
+
// Commit the draft with existing values
|
|
268
|
+
commitDraft();
|
|
269
|
+
}
|
|
269
270
|
};
|
|
270
271
|
|
|
271
272
|
const setMatchersString = (matchers: string): void => {
|
|
@@ -293,7 +294,6 @@ const ProfileSelector = ({
|
|
|
293
294
|
setProfileName,
|
|
294
295
|
setQueryExpression,
|
|
295
296
|
querySelection,
|
|
296
|
-
navigateTo,
|
|
297
297
|
loading: sumByLoading,
|
|
298
298
|
defaultProfileType: externalProfilerComponent?.defaultProfileType,
|
|
299
299
|
});
|
|
@@ -13,13 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
import {useEffect, useRef} from 'react';
|
|
15
15
|
|
|
16
|
+
import {useQueryStates} from 'nuqs';
|
|
17
|
+
|
|
16
18
|
import {ProfileTypesResponse} from '@parca/client';
|
|
17
19
|
import {selectAutoQuery, setAutoQuery, useAppDispatch, useAppSelector} from '@parca/store';
|
|
18
|
-
import {type NavigateFunction} from '@parca/utilities';
|
|
19
20
|
|
|
20
|
-
import {ProfileSelectionFromParams
|
|
21
|
+
import {ProfileSelectionFromParams} from '..';
|
|
21
22
|
import {QuerySelection} from '../ProfileSelector';
|
|
22
23
|
import {constructProfileName} from '../ProfileTypeSelector';
|
|
24
|
+
import {boolParam, stringParam} from '../hooks/urlParsers';
|
|
25
|
+
import {useDashboardItems} from '../hooks/useDashboardItems';
|
|
23
26
|
|
|
24
27
|
interface Props {
|
|
25
28
|
selectedProfileName: string;
|
|
@@ -27,7 +30,6 @@ interface Props {
|
|
|
27
30
|
setProfileName: (name: string) => void;
|
|
28
31
|
setQueryExpression: () => void;
|
|
29
32
|
querySelection: QuerySelection;
|
|
30
|
-
navigateTo: NavigateFunction;
|
|
31
33
|
loading: boolean;
|
|
32
34
|
defaultProfileType?: string;
|
|
33
35
|
}
|
|
@@ -38,18 +40,40 @@ export const useAutoQuerySelector = ({
|
|
|
38
40
|
setProfileName,
|
|
39
41
|
setQueryExpression,
|
|
40
42
|
querySelection,
|
|
41
|
-
navigateTo,
|
|
42
43
|
loading,
|
|
43
44
|
defaultProfileType,
|
|
44
45
|
}: Props): void => {
|
|
45
46
|
const autoQuery = useAppSelector(selectAutoQuery);
|
|
46
47
|
const dispatch = useAppDispatch();
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
|
|
49
|
+
const {setDashboardItems} = useDashboardItems();
|
|
50
|
+
|
|
51
|
+
const [compareState, setCompareParams] = useQueryStates(
|
|
52
|
+
{
|
|
53
|
+
compare_a: boolParam,
|
|
54
|
+
compare_b: boolParam,
|
|
55
|
+
expression_a: stringParam,
|
|
56
|
+
from_a: stringParam,
|
|
57
|
+
to_a: stringParam,
|
|
58
|
+
time_selection_a: stringParam,
|
|
59
|
+
sum_by_a: stringParam,
|
|
60
|
+
merge_from_a: stringParam,
|
|
61
|
+
merge_to_a: stringParam,
|
|
62
|
+
selection_a: stringParam,
|
|
63
|
+
expression_b: stringParam,
|
|
64
|
+
from_b: stringParam,
|
|
65
|
+
to_b: stringParam,
|
|
66
|
+
time_selection_b: stringParam,
|
|
67
|
+
sum_by_b: stringParam,
|
|
68
|
+
search_string: stringParam,
|
|
69
|
+
},
|
|
70
|
+
{history: 'replace'}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Read compare params through nuqs (not location.search) to stay in sync
|
|
74
|
+
const comparing = compareState.compare_a === true || compareState.compare_b === true;
|
|
75
|
+
const expressionA = compareState.expression_a;
|
|
76
|
+
const expressionB = compareState.expression_b;
|
|
53
77
|
|
|
54
78
|
// Track if we've already set up compare mode to prevent infinite loops
|
|
55
79
|
const hasSetupCompareMode = useRef(false);
|
|
@@ -64,13 +88,7 @@ export const useAutoQuerySelector = ({
|
|
|
64
88
|
// 2. expressionA exists
|
|
65
89
|
// 3. expressionB doesn't exist yet (meaning we need to set it up)
|
|
66
90
|
// 4. We haven't already set it up in this session
|
|
67
|
-
if (
|
|
68
|
-
comparing &&
|
|
69
|
-
expressionA !== null &&
|
|
70
|
-
expressionA !== undefined &&
|
|
71
|
-
expressionB === null &&
|
|
72
|
-
!hasSetupCompareMode.current
|
|
73
|
-
) {
|
|
91
|
+
if (comparing && expressionA !== null && expressionB === null && !hasSetupCompareMode.current) {
|
|
74
92
|
if (querySelection.expression === undefined) {
|
|
75
93
|
return;
|
|
76
94
|
}
|
|
@@ -87,42 +105,46 @@ export const useAutoQuerySelector = ({
|
|
|
87
105
|
sumBy: querySelection.sumBy,
|
|
88
106
|
};
|
|
89
107
|
|
|
90
|
-
const sumBy = queryA.sumBy?.join(',');
|
|
108
|
+
const sumBy = queryA.sumBy?.join(',') ?? null;
|
|
109
|
+
|
|
110
|
+
const mergeFromA = profileA != null ? profileA.HistoryParams().merge_from?.toString() : null;
|
|
111
|
+
const mergeToA = profileA != null ? profileA.HistoryParams().merge_to?.toString() : null;
|
|
112
|
+
const selectionA = profileA != null ? profileA.HistoryParams().selection?.toString() : null;
|
|
113
|
+
|
|
114
|
+
hasSetupCompareMode.current = true;
|
|
91
115
|
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
// Set all compare params atomically via nuqs
|
|
117
|
+
void setCompareParams({
|
|
118
|
+
compare_a: true,
|
|
119
|
+
compare_b: true,
|
|
94
120
|
expression_a: queryA.expression,
|
|
95
121
|
from_a: queryA.from.toString(),
|
|
96
122
|
to_a: queryA.to.toString(),
|
|
97
123
|
time_selection_a: queryA.timeSelection,
|
|
98
|
-
|
|
99
|
-
|
|
124
|
+
sum_by_a: sumBy,
|
|
125
|
+
merge_from_a: mergeFromA,
|
|
126
|
+
merge_to_a: mergeToA,
|
|
127
|
+
selection_a: selectionA,
|
|
100
128
|
expression_b: queryA.expression,
|
|
101
129
|
from_b: queryA.from.toString(),
|
|
102
130
|
to_b: queryA.to.toString(),
|
|
103
131
|
time_selection_b: queryA.timeSelection,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (sumBy != null) {
|
|
107
|
-
compareQuery.sum_by_a = sumBy;
|
|
108
|
-
compareQuery.sum_by_b = sumBy;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (profileA != null) {
|
|
112
|
-
compareQuery = {
|
|
113
|
-
...SuffixParams(profileA.HistoryParams(), '_a'),
|
|
114
|
-
...compareQuery,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
hasSetupCompareMode.current = true;
|
|
119
|
-
void navigateTo('/', {
|
|
120
|
-
...compareQuery,
|
|
121
|
-
search_string: '',
|
|
122
|
-
dashboard_items: ['flamegraph'],
|
|
132
|
+
sum_by_b: sumBy,
|
|
133
|
+
search_string: null,
|
|
123
134
|
});
|
|
135
|
+
|
|
136
|
+
setDashboardItems(['flamegraph']);
|
|
124
137
|
}
|
|
125
|
-
}, [
|
|
138
|
+
}, [
|
|
139
|
+
comparing,
|
|
140
|
+
querySelection,
|
|
141
|
+
expressionA,
|
|
142
|
+
expressionB,
|
|
143
|
+
dispatch,
|
|
144
|
+
loading,
|
|
145
|
+
setCompareParams,
|
|
146
|
+
setDashboardItems,
|
|
147
|
+
]);
|
|
126
148
|
|
|
127
149
|
// Effect to load some initial data on load when is no selection
|
|
128
150
|
useEffect(() => {
|
|
@@ -11,19 +11,23 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {useQueryState} from 'nuqs';
|
|
15
|
+
|
|
16
|
+
import {Select} from '@parca/components';
|
|
15
17
|
|
|
16
18
|
import {
|
|
17
19
|
FIELD_CUMULATIVE,
|
|
18
20
|
FIELD_DIFF,
|
|
19
21
|
FIELD_FUNCTION_NAME,
|
|
20
22
|
} from '../../../ProfileFlameGraph/FlameGraphArrow';
|
|
23
|
+
import {stringParam} from '../../../hooks/urlParsers';
|
|
21
24
|
import {useProfileViewContext} from '../../context/ProfileViewContext';
|
|
22
25
|
|
|
23
26
|
const SortByDropdown = (): React.JSX.Element => {
|
|
24
|
-
const [storeSortBy, setStoreSortBy] =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
const [storeSortBy, setStoreSortBy] = useQueryState(
|
|
28
|
+
'sort_by',
|
|
29
|
+
stringParam.withDefault(FIELD_FUNCTION_NAME)
|
|
30
|
+
);
|
|
27
31
|
|
|
28
32
|
const {compareMode} = useProfileViewContext();
|
|
29
33
|
|
|
@@ -70,8 +74,8 @@ const SortByDropdown = (): React.JSX.Element => {
|
|
|
70
74
|
},
|
|
71
75
|
},
|
|
72
76
|
]}
|
|
73
|
-
selectedKey={storeSortBy
|
|
74
|
-
onSelection={key => setStoreSortBy(key)}
|
|
77
|
+
selectedKey={storeSortBy}
|
|
78
|
+
onSelection={key => void setStoreSortBy(key)}
|
|
75
79
|
placeholder={'Sort By'}
|
|
76
80
|
primary={false}
|
|
77
81
|
disabled={false}
|
|
@@ -16,12 +16,12 @@ import React, {useMemo} from 'react';
|
|
|
16
16
|
import {Icon} from '@iconify/react';
|
|
17
17
|
import cx from 'classnames';
|
|
18
18
|
|
|
19
|
-
import {useURLState} from '@parca/components';
|
|
20
19
|
import {USER_PREFERENCES, useCurrentColorProfile, useUserPreference} from '@parca/hooks';
|
|
21
20
|
import {EVERYTHING_ELSE, selectDarkMode, useAppSelector} from '@parca/store';
|
|
22
21
|
|
|
23
22
|
import {getMappingColors} from '../../ProfileFlameGraph/FlameGraphArrow';
|
|
24
23
|
import useMappingList from '../../ProfileFlameGraph/FlameGraphArrow/useMappingList';
|
|
24
|
+
import {useColorBy} from '../../hooks/useColorBy';
|
|
25
25
|
import {useProfileFilters} from './ProfileFilters/useProfileFilters';
|
|
26
26
|
|
|
27
27
|
interface Props {
|
|
@@ -37,9 +37,7 @@ const ColorStackLegend = ({mappings, compareMode = false, loading}: Props): Reac
|
|
|
37
37
|
USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key
|
|
38
38
|
);
|
|
39
39
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const colorBy = colorByValue === 'binary' || colorByValue === undefined ? 'binary' : 'filename';
|
|
40
|
+
const {colorBy} = useColorBy();
|
|
43
41
|
|
|
44
42
|
const {appliedFilters, removeExcludeBinary, excludeBinary} = useProfileFilters();
|
|
45
43
|
|