@parca/profile 0.19.82 → 0.19.83
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 +6 -0
- package/dist/MatchersInput/index.d.ts +34 -2
- package/dist/MatchersInput/index.d.ts.map +1 -1
- package/dist/MatchersInput/index.js +91 -14
- package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts +29 -0
- package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts.map +1 -0
- package/dist/MetricsGraph/UtilizationMetrics/Throughput.js +175 -0
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +28 -0
- package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -0
- package/dist/MetricsGraph/UtilizationMetrics/index.js +186 -0
- package/dist/MetricsGraph/index.d.ts.map +1 -1
- package/dist/MetricsGraph/index.js +1 -13
- package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
- package/dist/ProfileMetricsGraph/index.js +29 -6
- package/dist/ProfileSelector/MetricsGraphSection.d.ts +9 -2
- package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
- package/dist/ProfileSelector/MetricsGraphSection.js +38 -3
- package/dist/ProfileSelector/QueryControls.d.ts +43 -0
- package/dist/ProfileSelector/QueryControls.d.ts.map +1 -0
- package/dist/{QueryControls/index.js → ProfileSelector/QueryControls.js} +13 -16
- package/dist/ProfileSelector/index.d.ts +29 -1
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +9 -12
- package/dist/SimpleMatchers/Select.js +1 -1
- package/dist/SimpleMatchers/index.d.ts +11 -2
- package/dist/SimpleMatchers/index.d.ts.map +1 -1
- package/dist/SimpleMatchers/index.js +45 -34
- package/dist/ViewMatchers/index.d.ts +9 -0
- package/dist/ViewMatchers/index.d.ts.map +1 -1
- package/dist/ViewMatchers/index.js +12 -20
- package/dist/contexts/MatchersInputLabelsContext.d.ts +29 -0
- package/dist/contexts/MatchersInputLabelsContext.d.ts.map +1 -0
- package/dist/contexts/MatchersInputLabelsContext.js +79 -0
- package/dist/contexts/SimpleMatchersLabelContext.d.ts +25 -0
- package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +1 -0
- package/dist/contexts/SimpleMatchersLabelContext.js +115 -0
- package/dist/contexts/UtilizationLabelsContext.d.ts +15 -0
- package/dist/contexts/UtilizationLabelsContext.d.ts.map +1 -0
- package/dist/contexts/UtilizationLabelsContext.js +25 -0
- package/dist/hooks/useQueryState.d.ts +0 -2
- package/dist/hooks/useQueryState.d.ts.map +1 -1
- package/dist/hooks/useQueryState.js +0 -18
- package/dist/index.d.ts +3 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -9
- package/dist/styles.css +1 -1
- package/dist/useSumBy.js +1 -1
- package/package.json +2 -2
- package/src/MatchersInput/index.tsx +163 -17
- package/src/MetricsGraph/UtilizationMetrics/Throughput.tsx +405 -0
- package/src/MetricsGraph/UtilizationMetrics/index.tsx +426 -0
- package/src/MetricsGraph/index.tsx +1 -17
- package/src/ProfileMetricsGraph/index.tsx +98 -17
- package/src/ProfileSelector/MetricsGraphSection.tsx +115 -14
- package/src/{QueryControls/index.tsx → ProfileSelector/QueryControls.tsx} +84 -66
- package/src/ProfileSelector/index.tsx +109 -106
- package/src/SimpleMatchers/Select.tsx +1 -1
- package/src/SimpleMatchers/index.tsx +85 -46
- package/src/ViewMatchers/index.tsx +30 -22
- package/src/contexts/MatchersInputLabelsContext.tsx +141 -0
- package/src/contexts/SimpleMatchersLabelContext.tsx +189 -0
- package/src/contexts/UtilizationLabelsContext.tsx +45 -0
- package/src/hooks/useQueryState.ts +0 -25
- package/src/index.tsx +3 -29
- package/src/useSumBy.ts +1 -1
- package/dist/QueryControls/index.d.ts +0 -42
- package/dist/QueryControls/index.d.ts.map +0 -1
- package/dist/contexts/LabelsQueryProvider.d.ts +0 -35
- package/dist/contexts/LabelsQueryProvider.d.ts.map +0 -1
- package/dist/contexts/LabelsQueryProvider.js +0 -70
- package/dist/contexts/UnifiedLabelsContext.d.ts +0 -37
- package/dist/contexts/UnifiedLabelsContext.d.ts.map +0 -1
- package/dist/contexts/UnifiedLabelsContext.js +0 -88
- package/dist/contexts/utils.d.ts +0 -10
- package/dist/contexts/utils.d.ts.map +0 -1
- package/dist/contexts/utils.js +0 -31
- package/dist/hooks/useLabels.d.ts +0 -23
- package/dist/hooks/useLabels.d.ts.map +0 -1
- package/dist/hooks/useLabels.js +0 -75
- package/src/contexts/LabelsQueryProvider.tsx +0 -142
- package/src/contexts/UnifiedLabelsContext.tsx +0 -155
- package/src/contexts/utils.ts +0 -43
- package/src/hooks/useLabels.ts +0 -121
|
@@ -18,9 +18,11 @@ import {DateTimeRange, useURLStateBatch} from '@parca/components';
|
|
|
18
18
|
import {Query} from '@parca/parser';
|
|
19
19
|
|
|
20
20
|
import {ProfileSelection} from '..';
|
|
21
|
+
import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
|
|
22
|
+
import AreaChart from '../MetricsGraph/UtilizationMetrics/Throughput';
|
|
21
23
|
import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
|
|
22
24
|
import {useResetStateOnSeriesChange} from '../ProfileView/hooks/useResetStateOnSeriesChange';
|
|
23
|
-
import {QuerySelection} from './index';
|
|
25
|
+
import {QuerySelection, type UtilizationMetrics as UtilizationMetricsType} from './index';
|
|
24
26
|
|
|
25
27
|
interface MetricsGraphSectionProps {
|
|
26
28
|
showMetricsGraph: boolean;
|
|
@@ -39,6 +41,13 @@ interface MetricsGraphSectionProps {
|
|
|
39
41
|
query: Query;
|
|
40
42
|
setNewQueryExpression: (queryExpression: string, commit?: boolean) => void;
|
|
41
43
|
setQueryExpression: (updateTs?: boolean) => void;
|
|
44
|
+
utilizationMetrics?: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
humanReadableName: string;
|
|
47
|
+
data: UtilizationMetricsType[];
|
|
48
|
+
}>;
|
|
49
|
+
utilizationMetricsLoading?: boolean;
|
|
50
|
+
onUtilizationSeriesSelect?: (name: string, seriesIndex: number) => void;
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
export function MetricsGraphSection({
|
|
@@ -57,6 +66,9 @@ export function MetricsGraphSection({
|
|
|
57
66
|
setProfileSelection,
|
|
58
67
|
query,
|
|
59
68
|
setNewQueryExpression,
|
|
69
|
+
utilizationMetrics,
|
|
70
|
+
utilizationMetricsLoading,
|
|
71
|
+
onUtilizationSeriesSelect,
|
|
60
72
|
}: MetricsGraphSectionProps): JSX.Element {
|
|
61
73
|
const resetStateOnSeriesChange = useResetStateOnSeriesChange();
|
|
62
74
|
const batchUpdates = useURLStateBatch();
|
|
@@ -135,6 +147,89 @@ export function MetricsGraphSection({
|
|
|
135
147
|
});
|
|
136
148
|
};
|
|
137
149
|
|
|
150
|
+
const UtilizationGraphToShow = ({
|
|
151
|
+
utilizationMetrics,
|
|
152
|
+
}: {
|
|
153
|
+
utilizationMetrics: Array<{
|
|
154
|
+
name: string;
|
|
155
|
+
humanReadableName: string;
|
|
156
|
+
data: UtilizationMetricsType[];
|
|
157
|
+
}>;
|
|
158
|
+
}): JSX.Element => {
|
|
159
|
+
const throughputMetrics = utilizationMetrics.filter(
|
|
160
|
+
metric =>
|
|
161
|
+
metric.name === 'gpu_pcie_throughput_transmit_bytes' ||
|
|
162
|
+
metric.name === 'gpu_pcie_throughput_receive_bytes'
|
|
163
|
+
);
|
|
164
|
+
const transmitData =
|
|
165
|
+
throughputMetrics.find(metric => metric.name === 'gpu_pcie_throughput_transmit_bytes')
|
|
166
|
+
?.data ?? [];
|
|
167
|
+
const receiveData =
|
|
168
|
+
throughputMetrics.find(metric => metric.name === 'gpu_pcie_throughput_receive_bytes')?.data ??
|
|
169
|
+
[];
|
|
170
|
+
|
|
171
|
+
if (utilizationMetrics.length === 0) {
|
|
172
|
+
return <></>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div>
|
|
177
|
+
{utilizationMetrics.map(({name, humanReadableName, data}) => {
|
|
178
|
+
if (
|
|
179
|
+
name !== 'gpu_pcie_throughput_transmit_bytes' &&
|
|
180
|
+
name !== 'gpu_pcie_throughput_receive_bytes'
|
|
181
|
+
) {
|
|
182
|
+
return (
|
|
183
|
+
<UtilizationMetricsGraph
|
|
184
|
+
key={name}
|
|
185
|
+
data={data}
|
|
186
|
+
setTimeRange={handleTimeRangeChange}
|
|
187
|
+
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
188
|
+
humanReadableName={humanReadableName}
|
|
189
|
+
from={querySelection.from}
|
|
190
|
+
to={querySelection.to}
|
|
191
|
+
yAxisUnit="percentage"
|
|
192
|
+
addLabelMatcher={addLabelMatcher}
|
|
193
|
+
onSeriesClick={seriesIndex => {
|
|
194
|
+
// For generic UtilizationMetrics, just pass the series index
|
|
195
|
+
if (onUtilizationSeriesSelect != null) {
|
|
196
|
+
onUtilizationSeriesSelect(name, seriesIndex);
|
|
197
|
+
}
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
})}
|
|
204
|
+
{throughputMetrics.length > 0 && (
|
|
205
|
+
<AreaChart
|
|
206
|
+
transmitData={transmitData}
|
|
207
|
+
receiveData={receiveData}
|
|
208
|
+
addLabelMatcher={addLabelMatcher}
|
|
209
|
+
setTimeRange={handleTimeRangeChange}
|
|
210
|
+
name={throughputMetrics[0].name}
|
|
211
|
+
humanReadableName={throughputMetrics[0].humanReadableName}
|
|
212
|
+
from={querySelection.from}
|
|
213
|
+
to={querySelection.to}
|
|
214
|
+
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
215
|
+
selectedSeries={undefined}
|
|
216
|
+
onSeriesClick={(_, seriesIndex) => {
|
|
217
|
+
// For throughput metrics, just pass the series index
|
|
218
|
+
if (onUtilizationSeriesSelect != null) {
|
|
219
|
+
let name = 'gpu_pcie_throughput_transmit_bytes';
|
|
220
|
+
if (seriesIndex > transmitData.length - 1) {
|
|
221
|
+
name = 'gpu_pcie_throughput_receive_bytes';
|
|
222
|
+
seriesIndex -= transmitData.length;
|
|
223
|
+
}
|
|
224
|
+
onUtilizationSeriesSelect(name, seriesIndex);
|
|
225
|
+
}
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
|
|
138
233
|
return (
|
|
139
234
|
<div className={cx('relative', {'py-4': !showMetricsGraph})}>
|
|
140
235
|
{setDisplayHideMetricsGraphButton != null ? (
|
|
@@ -156,19 +251,25 @@ export function MetricsGraphSection({
|
|
|
156
251
|
querySelection.from !== undefined &&
|
|
157
252
|
querySelection.to !== undefined ? (
|
|
158
253
|
<>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
254
|
+
{utilizationMetrics !== undefined ? (
|
|
255
|
+
<UtilizationGraphToShow utilizationMetrics={utilizationMetrics} />
|
|
256
|
+
) : (
|
|
257
|
+
<>
|
|
258
|
+
<ProfileMetricsGraph
|
|
259
|
+
queryClient={queryClient}
|
|
260
|
+
queryExpression={querySelection.expression}
|
|
261
|
+
from={querySelection.from}
|
|
262
|
+
to={querySelection.to}
|
|
263
|
+
profile={profileSelection}
|
|
264
|
+
comparing={comparing}
|
|
265
|
+
sumBy={querySelection.sumBy ?? sumBy ?? []}
|
|
266
|
+
sumByLoading={defaultSumByLoading}
|
|
267
|
+
setTimeRange={handleTimeRangeChange}
|
|
268
|
+
addLabelMatcher={addLabelMatcher}
|
|
269
|
+
onPointClick={handlePointClick}
|
|
270
|
+
/>
|
|
271
|
+
</>
|
|
272
|
+
)}
|
|
172
273
|
</>
|
|
173
274
|
) : (
|
|
174
275
|
profileSelection === null && (
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {Switch} from '@headlessui/react';
|
|
17
17
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
@@ -24,10 +24,9 @@ import {TEST_IDS, testId} from '@parca/test-utils';
|
|
|
24
24
|
|
|
25
25
|
import MatchersInput from '../MatchersInput';
|
|
26
26
|
import ProfileTypeSelector from '../ProfileTypeSelector';
|
|
27
|
-
import
|
|
28
|
-
import SimpleMatchers from '../SimpleMatchers
|
|
27
|
+
import SelectWithRefresh from '../SelectWithRefresh';
|
|
28
|
+
import SimpleMatchers from '../SimpleMatchers';
|
|
29
29
|
import ViewMatchers from '../ViewMatchers';
|
|
30
|
-
import {useLabelNames} from '../hooks/useLabels';
|
|
31
30
|
|
|
32
31
|
interface SelectOption {
|
|
33
32
|
label: string;
|
|
@@ -35,22 +34,13 @@ interface SelectOption {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
interface QueryControlsProps {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
timeRangeSelection: DateTimeRange;
|
|
42
|
-
setTimeRangeSelection: (range: DateTimeRange) => void;
|
|
43
|
-
setMatchersString: (matchers: string) => void;
|
|
44
|
-
setQueryExpression: (updateTs?: boolean) => void;
|
|
45
|
-
searchDisabled: boolean;
|
|
46
|
-
showProfileTypeSelector?: boolean;
|
|
47
|
-
showSumBySelector?: boolean;
|
|
48
|
-
showAdvancedMode?: boolean;
|
|
49
|
-
disableExplorativeQuerying?: boolean;
|
|
37
|
+
showProfileTypeSelector: boolean;
|
|
38
|
+
showSumBySelector: boolean;
|
|
39
|
+
disableExplorativeQuerying: boolean;
|
|
50
40
|
profileTypesData?: ProfileTypesResponse;
|
|
51
|
-
profileTypesLoading
|
|
52
|
-
selectedProfileName
|
|
53
|
-
setProfileName
|
|
41
|
+
profileTypesLoading: boolean;
|
|
42
|
+
selectedProfileName: string;
|
|
43
|
+
setProfileName: (name: string | undefined) => void;
|
|
54
44
|
profileTypesError?: RpcError;
|
|
55
45
|
viewComponent?: {
|
|
56
46
|
disableProfileTypesDropdown?: boolean;
|
|
@@ -58,55 +48,58 @@ interface QueryControlsProps {
|
|
|
58
48
|
labelnames?: string[];
|
|
59
49
|
createViewComponent?: React.ReactNode;
|
|
60
50
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
queryBrowserMode: string;
|
|
52
|
+
setQueryBrowserMode: (mode: string) => void;
|
|
53
|
+
advancedModeForQueryBrowser: boolean;
|
|
54
|
+
setAdvancedModeForQueryBrowser: (mode: boolean) => void;
|
|
55
|
+
setMatchersString: (matchers: string) => void;
|
|
56
|
+
setQueryExpression: (updateTs?: boolean) => void;
|
|
57
|
+
query: Query;
|
|
58
|
+
queryBrowserRef: React.RefObject<HTMLDivElement>;
|
|
59
|
+
timeRangeSelection: DateTimeRange;
|
|
60
|
+
setTimeRangeSelection: (range: DateTimeRange) => void;
|
|
61
|
+
searchDisabled: boolean;
|
|
62
|
+
queryClient: QueryServiceClient;
|
|
63
|
+
labels: string[];
|
|
64
|
+
sumBySelection: string[];
|
|
65
|
+
sumBySelectionLoading: boolean;
|
|
66
|
+
setUserSumBySelection: (sumBy: string[]) => void;
|
|
67
|
+
sumByRef: React.RefObject<SelectInstance>;
|
|
68
|
+
profileType: ProfileType;
|
|
69
|
+
refreshLabelNames: () => Promise<void>;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export function QueryControls({
|
|
73
|
-
|
|
74
|
-
timeRangeSelection,
|
|
75
|
-
setTimeRangeSelection,
|
|
76
|
-
setQueryExpression,
|
|
77
|
-
searchDisabled,
|
|
78
|
-
showProfileTypeSelector = false,
|
|
79
|
-
showSumBySelector = false,
|
|
80
|
-
showAdvancedMode = true,
|
|
73
|
+
showProfileTypeSelector,
|
|
81
74
|
profileTypesData,
|
|
82
|
-
profileTypesLoading
|
|
75
|
+
profileTypesLoading,
|
|
83
76
|
selectedProfileName,
|
|
84
77
|
setProfileName,
|
|
85
|
-
profileTypesError,
|
|
86
78
|
viewComponent,
|
|
87
79
|
setQueryBrowserMode,
|
|
88
|
-
advancedModeForQueryBrowser
|
|
80
|
+
advancedModeForQueryBrowser,
|
|
89
81
|
setAdvancedModeForQueryBrowser,
|
|
82
|
+
setMatchersString,
|
|
83
|
+
setQueryExpression,
|
|
84
|
+
query,
|
|
90
85
|
queryBrowserRef,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
timeRangeSelection,
|
|
87
|
+
setTimeRangeSelection,
|
|
88
|
+
searchDisabled,
|
|
89
|
+
queryClient,
|
|
90
|
+
labels,
|
|
91
|
+
sumBySelection,
|
|
92
|
+
sumBySelectionLoading,
|
|
94
93
|
setUserSumBySelection,
|
|
95
94
|
sumByRef,
|
|
96
|
-
|
|
95
|
+
profileType,
|
|
96
|
+
showSumBySelector,
|
|
97
|
+
profileTypesError,
|
|
98
|
+
refreshLabelNames,
|
|
97
99
|
}: QueryControlsProps): JSX.Element {
|
|
98
100
|
const {timezone} = useParcaContext();
|
|
99
|
-
const defaultQueryBrowserRef = useRef<HTMLDivElement>(null);
|
|
100
|
-
const actualQueryBrowserRef = queryBrowserRef ?? defaultQueryBrowserRef;
|
|
101
101
|
const [searchExecutedTimestamp, setSearchExecutedTimestamp] = useState<number>(0);
|
|
102
102
|
|
|
103
|
-
const {refetch: refetchLabelNames} = useLabelNames(
|
|
104
|
-
queryClient,
|
|
105
|
-
profileType as string,
|
|
106
|
-
timeRangeSelection.getFromMs(),
|
|
107
|
-
timeRangeSelection.getToMs()
|
|
108
|
-
);
|
|
109
|
-
|
|
110
103
|
return (
|
|
111
104
|
<div
|
|
112
105
|
className="flex w-full flex-wrap items-start gap-2"
|
|
@@ -121,7 +114,7 @@ export function QueryControls({
|
|
|
121
114
|
profileTypesData={profileTypesData}
|
|
122
115
|
loading={profileTypesLoading}
|
|
123
116
|
selectedKey={selectedProfileName}
|
|
124
|
-
onSelection={setProfileName
|
|
117
|
+
onSelection={setProfileName}
|
|
125
118
|
error={profileTypesError}
|
|
126
119
|
disabled={viewComponent?.disableProfileTypesDropdown}
|
|
127
120
|
/>
|
|
@@ -130,7 +123,7 @@ export function QueryControls({
|
|
|
130
123
|
|
|
131
124
|
<div
|
|
132
125
|
className="w-full flex-1 flex flex-col gap-1 mt-auto"
|
|
133
|
-
ref={
|
|
126
|
+
ref={queryBrowserRef}
|
|
134
127
|
{...testId(TEST_IDS.QUERY_BROWSER_CONTAINER)}
|
|
135
128
|
>
|
|
136
129
|
<div className="flex items-center justify-between">
|
|
@@ -138,13 +131,13 @@ export function QueryControls({
|
|
|
138
131
|
<label className="text-xs" {...testId(TEST_IDS.QUERY_LABEL)}>
|
|
139
132
|
Query
|
|
140
133
|
</label>
|
|
141
|
-
{
|
|
134
|
+
{viewComponent?.disableExplorativeQuerying !== true && (
|
|
142
135
|
<>
|
|
143
136
|
<Switch
|
|
144
137
|
checked={advancedModeForQueryBrowser}
|
|
145
138
|
onChange={() => {
|
|
146
|
-
setAdvancedModeForQueryBrowser
|
|
147
|
-
setQueryBrowserMode
|
|
139
|
+
setAdvancedModeForQueryBrowser(!advancedModeForQueryBrowser);
|
|
140
|
+
setQueryBrowserMode(advancedModeForQueryBrowser ? 'simple' : 'advanced');
|
|
148
141
|
}}
|
|
149
142
|
className={`${
|
|
150
143
|
advancedModeForQueryBrowser ? 'bg-indigo-600' : 'bg-gray-400 dark:bg-gray-800'
|
|
@@ -171,12 +164,36 @@ export function QueryControls({
|
|
|
171
164
|
{viewComponent?.disableExplorativeQuerying === true &&
|
|
172
165
|
viewComponent?.labelnames !== undefined &&
|
|
173
166
|
viewComponent?.labelnames.length >= 1 ? (
|
|
174
|
-
<ViewMatchers
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
<ViewMatchers
|
|
168
|
+
labelNames={viewComponent.labelnames}
|
|
169
|
+
setMatchersString={setMatchersString}
|
|
170
|
+
profileType={selectedProfileName}
|
|
171
|
+
runQuery={setQueryExpression}
|
|
172
|
+
currentQuery={query}
|
|
173
|
+
queryClient={queryClient}
|
|
174
|
+
start={timeRangeSelection.getFromMs()}
|
|
175
|
+
end={timeRangeSelection.getToMs()}
|
|
176
|
+
/>
|
|
177
|
+
) : advancedModeForQueryBrowser ? (
|
|
178
|
+
<MatchersInput
|
|
179
|
+
setMatchersString={setMatchersString}
|
|
180
|
+
runQuery={setQueryExpression}
|
|
181
|
+
currentQuery={query}
|
|
182
|
+
profileType={selectedProfileName}
|
|
183
|
+
queryClient={queryClient}
|
|
184
|
+
start={timeRangeSelection.getFromMs()}
|
|
185
|
+
end={timeRangeSelection.getToMs()}
|
|
186
|
+
/>
|
|
177
187
|
) : (
|
|
178
188
|
<SimpleMatchers
|
|
179
|
-
|
|
189
|
+
setMatchersString={setMatchersString}
|
|
190
|
+
runQuery={setQueryExpression}
|
|
191
|
+
currentQuery={query}
|
|
192
|
+
profileType={selectedProfileName}
|
|
193
|
+
queryBrowserRef={queryBrowserRef}
|
|
194
|
+
queryClient={queryClient}
|
|
195
|
+
start={timeRangeSelection.getFromMs()}
|
|
196
|
+
end={timeRangeSelection.getToMs()}
|
|
180
197
|
searchExecutedTimestamp={searchExecutedTimestamp}
|
|
181
198
|
/>
|
|
182
199
|
)}
|
|
@@ -199,9 +216,9 @@ export function QueryControls({
|
|
|
199
216
|
options={labels.map(label => ({label, value: label}))}
|
|
200
217
|
className="parca-select-container text-sm w-full max-w-80"
|
|
201
218
|
classNamePrefix="parca-select"
|
|
202
|
-
value={sumBySelection.map(sumBy => ({label: sumBy, value: sumBy}))}
|
|
219
|
+
value={(sumBySelection ?? []).map(sumBy => ({label: sumBy, value: sumBy}))}
|
|
203
220
|
onChange={newValue => {
|
|
204
|
-
setUserSumBySelection
|
|
221
|
+
setUserSumBySelection(newValue.map(option => option.value));
|
|
205
222
|
}}
|
|
206
223
|
placeholder="Labels..."
|
|
207
224
|
styles={{
|
|
@@ -215,13 +232,14 @@ export function QueryControls({
|
|
|
215
232
|
minWidth: '320px',
|
|
216
233
|
position: 'absolute',
|
|
217
234
|
}),
|
|
235
|
+
// menu: provided => ({...provided, width: 'max-content', zIndex: 50}), // Setting the same zIndex as drop down menus
|
|
218
236
|
}}
|
|
219
237
|
isLoading={sumBySelectionLoading}
|
|
220
|
-
isDisabled={!
|
|
238
|
+
isDisabled={!profileType.delta}
|
|
221
239
|
// @ts-expect-error
|
|
222
240
|
ref={sumByRef}
|
|
223
241
|
onKeyDown={e => {
|
|
224
|
-
const currentRef = sumByRef
|
|
242
|
+
const currentRef = sumByRef.current as unknown as SelectInstance | null;
|
|
225
243
|
if (currentRef == null) {
|
|
226
244
|
return;
|
|
227
245
|
}
|
|
@@ -239,7 +257,7 @@ export function QueryControls({
|
|
|
239
257
|
currentRef.blur();
|
|
240
258
|
}
|
|
241
259
|
}}
|
|
242
|
-
onRefresh={
|
|
260
|
+
onRefresh={refreshLabelNames}
|
|
243
261
|
refreshTitle="Refresh label names"
|
|
244
262
|
refreshTestId="sum-by-refresh-button"
|
|
245
263
|
menuTestId={TEST_IDS.SUM_BY_SELECT_FLYOUT}
|
|
@@ -29,14 +29,13 @@ import {Query} from '@parca/parser';
|
|
|
29
29
|
import {TEST_IDS, testId} from '@parca/test-utils';
|
|
30
30
|
import {millisToProtoTimestamp, type NavigateFunction} from '@parca/utilities';
|
|
31
31
|
|
|
32
|
+
import {useLabelNames} from '../MatchersInput/index';
|
|
32
33
|
import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
|
|
33
|
-
import {
|
|
34
|
-
import {LabelsQueryProvider, useLabelsQueryProvider} from '../contexts/LabelsQueryProvider';
|
|
35
|
-
import {UnifiedLabelsProvider} from '../contexts/UnifiedLabelsContext';
|
|
36
|
-
import {useLabelNames} from '../hooks/useLabels';
|
|
34
|
+
import {UtilizationLabelsProvider} from '../contexts/UtilizationLabelsContext';
|
|
37
35
|
import {useQueryState} from '../hooks/useQueryState';
|
|
38
36
|
import useGrpcQuery from '../useGrpcQuery';
|
|
39
37
|
import {MetricsGraphSection} from './MetricsGraphSection';
|
|
38
|
+
import {QueryControls} from './QueryControls';
|
|
40
39
|
import {useAutoQuerySelector} from './useAutoQuerySelector';
|
|
41
40
|
|
|
42
41
|
export interface QuerySelection {
|
|
@@ -53,9 +52,31 @@ interface ProfileSelectorFeatures {
|
|
|
53
52
|
showMetricsGraph: boolean;
|
|
54
53
|
showSumBySelector?: boolean;
|
|
55
54
|
showProfileTypeSelector?: boolean;
|
|
55
|
+
disableExplorativeQuerying?: boolean;
|
|
56
56
|
disableProfileTypesDropdown?: boolean;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export interface UtilizationMetrics {
|
|
60
|
+
isSelected: boolean;
|
|
61
|
+
labelset: {
|
|
62
|
+
labels: Array<{
|
|
63
|
+
name: string;
|
|
64
|
+
value: string;
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
67
|
+
samples: Array<{
|
|
68
|
+
timestamp: number;
|
|
69
|
+
value: number;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface UtilizationLabels {
|
|
74
|
+
utilizationLabelNames?: string[];
|
|
75
|
+
utilizationFetchLabelValues?: (key: string) => Promise<string[]>;
|
|
76
|
+
utilizationLabelValues?: string[];
|
|
77
|
+
utilizationLabelNamesLoading?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
60
81
|
queryClient: QueryServiceClient;
|
|
61
82
|
closeProfile: () => void;
|
|
@@ -64,6 +85,14 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
|
|
|
64
85
|
navigateTo: NavigateFunction;
|
|
65
86
|
setDisplayHideMetricsGraphButton?: Dispatch<SetStateAction<boolean>>;
|
|
66
87
|
suffix?: '_a' | '_b'; // For comparison mode
|
|
88
|
+
utilizationMetrics?: Array<{
|
|
89
|
+
name: string;
|
|
90
|
+
humanReadableName: string;
|
|
91
|
+
data: UtilizationMetrics[];
|
|
92
|
+
}>;
|
|
93
|
+
utilizationMetricsLoading?: boolean;
|
|
94
|
+
utilizationLabels?: UtilizationLabels;
|
|
95
|
+
onUtilizationSeriesSelect?: (name: string, seriesIndex: number) => void;
|
|
67
96
|
onSearchHook?: () => void;
|
|
68
97
|
}
|
|
69
98
|
|
|
@@ -110,11 +139,16 @@ const ProfileSelector = ({
|
|
|
110
139
|
showMetricsGraph = true,
|
|
111
140
|
showSumBySelector = true,
|
|
112
141
|
showProfileTypeSelector = true,
|
|
142
|
+
disableExplorativeQuerying = false,
|
|
113
143
|
setDisplayHideMetricsGraphButton,
|
|
114
144
|
suffix,
|
|
145
|
+
utilizationMetrics,
|
|
146
|
+
utilizationMetricsLoading,
|
|
147
|
+
utilizationLabels,
|
|
148
|
+
onUtilizationSeriesSelect,
|
|
115
149
|
onSearchHook,
|
|
116
150
|
}: ProfileSelectorProps): JSX.Element => {
|
|
117
|
-
const {heightStyle} = useMetricsGraphDimensions(comparing,
|
|
151
|
+
const {heightStyle} = useMetricsGraphDimensions(comparing, utilizationMetrics != null);
|
|
118
152
|
const {viewComponent} = useParcaContext();
|
|
119
153
|
const [queryBrowserMode, setQueryBrowserMode] = useURLState('query_browser_mode');
|
|
120
154
|
const batchUpdates = useURLStateBatch();
|
|
@@ -167,7 +201,7 @@ const ProfileSelector = ({
|
|
|
167
201
|
error,
|
|
168
202
|
} = useProfileTypes(queryClient, from, to);
|
|
169
203
|
|
|
170
|
-
const {result} = useLabelNames(queryClient, profileType.toString(), from, to);
|
|
204
|
+
const {result, refetch} = useLabelNames(queryClient, profileType.toString(), from, to);
|
|
171
205
|
|
|
172
206
|
const labels = useMemo(() => {
|
|
173
207
|
return result.response?.labelNames === undefined ? [] : result.response.labelNames;
|
|
@@ -264,108 +298,77 @@ const ProfileSelector = ({
|
|
|
264
298
|
const sumByRef = useRef(null);
|
|
265
299
|
|
|
266
300
|
return (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
301
|
+
<UtilizationLabelsProvider value={{...utilizationLabels}}>
|
|
302
|
+
<>
|
|
303
|
+
<div className="mb-2 flex">
|
|
304
|
+
<QueryControls
|
|
305
|
+
showProfileTypeSelector={showProfileTypeSelector}
|
|
306
|
+
showSumBySelector={showSumBySelector}
|
|
307
|
+
disableExplorativeQuerying={disableExplorativeQuerying}
|
|
308
|
+
profileTypesData={profileTypesData}
|
|
309
|
+
profileTypesLoading={profileTypesLoading}
|
|
310
|
+
selectedProfileName={selectedProfileName}
|
|
311
|
+
setProfileName={setProfileName}
|
|
312
|
+
setMatchersString={setMatchersString}
|
|
313
|
+
setQueryExpression={setQueryExpression}
|
|
314
|
+
query={query}
|
|
315
|
+
queryBrowserRef={queryBrowserRef}
|
|
316
|
+
timeRangeSelection={timeRangeSelection}
|
|
317
|
+
setTimeRangeSelection={handleTimeRangeChange}
|
|
318
|
+
searchDisabled={searchDisabled}
|
|
319
|
+
queryBrowserMode={queryBrowserMode as string}
|
|
320
|
+
setQueryBrowserMode={setQueryBrowserMode}
|
|
321
|
+
advancedModeForQueryBrowser={advancedModeForQueryBrowser}
|
|
322
|
+
setAdvancedModeForQueryBrowser={setAdvancedModeForQueryBrowser}
|
|
323
|
+
queryClient={queryClient}
|
|
324
|
+
sumByRef={sumByRef}
|
|
325
|
+
labels={labels}
|
|
326
|
+
sumBySelection={draftSelection.sumBy ?? []}
|
|
327
|
+
sumBySelectionLoading={sumByLoading}
|
|
328
|
+
setUserSumBySelection={setDraftSumBy}
|
|
329
|
+
profileType={profileType}
|
|
330
|
+
profileTypesError={error}
|
|
331
|
+
viewComponent={viewComponent}
|
|
332
|
+
refreshLabelNames={refetch}
|
|
333
|
+
/>
|
|
334
|
+
{comparing && (
|
|
335
|
+
<div>
|
|
336
|
+
<IconButton
|
|
337
|
+
onClick={() => closeProfile()}
|
|
338
|
+
icon={<CloseIcon />}
|
|
339
|
+
{...testId(TEST_IDS.COMPARE_CLOSE_BUTTON)}
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
<MetricsGraphSection
|
|
345
|
+
showMetricsGraph={showMetricsGraph}
|
|
346
|
+
setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
|
|
347
|
+
heightStyle={
|
|
348
|
+
utilizationMetrics !== undefined && utilizationMetrics?.length > 0
|
|
349
|
+
? 'auto'
|
|
350
|
+
: heightStyle
|
|
351
|
+
}
|
|
352
|
+
querySelection={querySelection}
|
|
353
|
+
profileSelection={profileSelection}
|
|
354
|
+
comparing={comparing}
|
|
355
|
+
sumBy={querySelection.sumBy ?? []}
|
|
356
|
+
defaultSumByLoading={sumByLoading}
|
|
274
357
|
queryClient={queryClient}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
setQueryExpression={setQueryExpression}
|
|
289
|
-
query={query}
|
|
290
|
-
queryBrowserRef={queryBrowserRef}
|
|
291
|
-
timeRangeSelection={timeRangeSelection}
|
|
292
|
-
setTimeRangeSelection={handleTimeRangeChange}
|
|
293
|
-
searchDisabled={searchDisabled}
|
|
294
|
-
setQueryBrowserMode={setQueryBrowserMode}
|
|
295
|
-
advancedModeForQueryBrowser={advancedModeForQueryBrowser}
|
|
296
|
-
setAdvancedModeForQueryBrowser={setAdvancedModeForQueryBrowser}
|
|
297
|
-
queryClient={queryClient}
|
|
298
|
-
sumByRef={sumByRef}
|
|
299
|
-
labels={labels}
|
|
300
|
-
sumBySelection={draftSelection.sumBy ?? []}
|
|
301
|
-
sumBySelectionLoading={sumByLoading}
|
|
302
|
-
setUserSumBySelection={setDraftSumBy}
|
|
303
|
-
profileType={profileType}
|
|
304
|
-
profileTypesError={error}
|
|
305
|
-
viewComponent={viewComponent}
|
|
306
|
-
/>
|
|
307
|
-
</LabelsSource>
|
|
308
|
-
</LabelsQueryProvider>
|
|
309
|
-
{comparing && (
|
|
310
|
-
<div>
|
|
311
|
-
<IconButton
|
|
312
|
-
onClick={() => closeProfile()}
|
|
313
|
-
icon={<CloseIcon />}
|
|
314
|
-
{...testId(TEST_IDS.COMPARE_CLOSE_BUTTON)}
|
|
315
|
-
/>
|
|
316
|
-
</div>
|
|
317
|
-
)}
|
|
318
|
-
</div>
|
|
319
|
-
<MetricsGraphSection
|
|
320
|
-
showMetricsGraph={showMetricsGraph}
|
|
321
|
-
setDisplayHideMetricsGraphButton={setDisplayHideMetricsGraphButton}
|
|
322
|
-
heightStyle={heightStyle}
|
|
323
|
-
querySelection={querySelection}
|
|
324
|
-
profileSelection={profileSelection}
|
|
325
|
-
comparing={comparing}
|
|
326
|
-
sumBy={querySelection.sumBy ?? []}
|
|
327
|
-
defaultSumByLoading={sumByLoading}
|
|
328
|
-
queryClient={queryClient}
|
|
329
|
-
queryExpressionString={queryExpressionString}
|
|
330
|
-
setTimeRangeSelection={handleTimeRangeChange}
|
|
331
|
-
selectQuery={commitDraft}
|
|
332
|
-
setProfileSelection={setProfileSelection}
|
|
333
|
-
query={query}
|
|
334
|
-
setQueryExpression={setQueryExpression}
|
|
335
|
-
setNewQueryExpression={setDraftExpression}
|
|
336
|
-
/>
|
|
337
|
-
</>
|
|
358
|
+
queryExpressionString={queryExpressionString}
|
|
359
|
+
setTimeRangeSelection={handleTimeRangeChange}
|
|
360
|
+
selectQuery={commitDraft}
|
|
361
|
+
setProfileSelection={setProfileSelection}
|
|
362
|
+
query={query}
|
|
363
|
+
setQueryExpression={setQueryExpression}
|
|
364
|
+
setNewQueryExpression={setDraftExpression}
|
|
365
|
+
utilizationMetrics={utilizationMetrics}
|
|
366
|
+
utilizationMetricsLoading={utilizationMetricsLoading}
|
|
367
|
+
onUtilizationSeriesSelect={onUtilizationSeriesSelect}
|
|
368
|
+
/>
|
|
369
|
+
</>
|
|
370
|
+
</UtilizationLabelsProvider>
|
|
338
371
|
);
|
|
339
372
|
};
|
|
340
373
|
|
|
341
374
|
export default ProfileSelector;
|
|
342
|
-
|
|
343
|
-
const LabelsSource = ({children}: {children: React.ReactNode}): JSX.Element => {
|
|
344
|
-
const {
|
|
345
|
-
labelNames,
|
|
346
|
-
labelValues,
|
|
347
|
-
isLabelNamesLoading,
|
|
348
|
-
isLabelValuesLoading,
|
|
349
|
-
refetchLabelValues,
|
|
350
|
-
refetchLabelNames,
|
|
351
|
-
currentLabelName,
|
|
352
|
-
setCurrentLabelName,
|
|
353
|
-
suffix,
|
|
354
|
-
} = useLabelsQueryProvider();
|
|
355
|
-
|
|
356
|
-
return (
|
|
357
|
-
<UnifiedLabelsProvider
|
|
358
|
-
labelNames={labelNames}
|
|
359
|
-
labelValues={labelValues}
|
|
360
|
-
isLabelNamesLoading={isLabelNamesLoading}
|
|
361
|
-
isLabelValuesLoading={isLabelValuesLoading}
|
|
362
|
-
refetchLabelValues={refetchLabelValues}
|
|
363
|
-
refetchLabelNames={refetchLabelNames}
|
|
364
|
-
currentLabelName={currentLabelName}
|
|
365
|
-
setCurrentLabelName={setCurrentLabelName}
|
|
366
|
-
suffix={suffix}
|
|
367
|
-
>
|
|
368
|
-
{children}
|
|
369
|
-
</UnifiedLabelsProvider>
|
|
370
|
-
);
|
|
371
|
-
};
|