@parca/profile 0.16.64 → 0.16.66
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/MatchersInput/index.js +2 -2
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +1 -2
- package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +1 -2
- package/dist/ProfileExplorer/index.d.ts +1 -1
- package/dist/ProfileExplorer/index.js +28 -24
- package/dist/ProfileMetricsGraph/index.js +2 -2
- package/dist/ProfileSource.d.ts +11 -6
- package/dist/ProfileSource.js +18 -10
- package/dist/ProfileView/FilterByFunctionButton.d.ts +2 -0
- package/dist/ProfileView/FilterByFunctionButton.js +26 -0
- package/dist/{ProfileView.d.ts → ProfileView/index.d.ts} +2 -2
- package/dist/{ProfileView.js → ProfileView/index.js} +13 -11
- package/dist/ProfileViewWithData.d.ts +1 -1
- package/dist/ProfileViewWithData.js +9 -1
- package/dist/styles.css +1 -1
- package/dist/useQuery.js +0 -1
- package/package.json +8 -8
- package/src/MatchersInput/index.tsx +3 -2
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +1 -2
- package/src/ProfileExplorer/ProfileExplorerSingle.tsx +1 -2
- package/src/ProfileExplorer/index.tsx +31 -29
- package/src/ProfileMetricsGraph/index.tsx +2 -2
- package/src/ProfileSource.tsx +29 -10
- package/src/ProfileView/FilterByFunctionButton.tsx +44 -0
- package/src/{ProfileView.tsx → ProfileView/index.tsx} +60 -58
- package/src/ProfileViewWithData.tsx +11 -2
- package/src/useQuery.tsx +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 0.16.66 (2022-11-14)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## 0.16.65 (2022-11-09)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## [0.16.64](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.63...@parca/profile@0.16.64) (2022-11-08)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -27,7 +27,7 @@ import { Transition } from '@headlessui/react';
|
|
|
27
27
|
import { Query } from '@parca/parser';
|
|
28
28
|
import { usePopper } from 'react-popper';
|
|
29
29
|
import cx from 'classnames';
|
|
30
|
-
import {
|
|
30
|
+
import { useParcaContext, useGrpcMetadata } from '@parca/components';
|
|
31
31
|
export var useLabelNames = function (client) {
|
|
32
32
|
var _a = useState(true), loading = _a[0], setLoading = _a[1];
|
|
33
33
|
var _b = useState({}), result = _b[0], setResult = _b[1];
|
|
@@ -73,7 +73,7 @@ var MatchersInput = function (_a) {
|
|
|
73
73
|
placement: 'bottom-start',
|
|
74
74
|
}), styles = _l.styles, attributes = _l.attributes;
|
|
75
75
|
var metadata = useGrpcMetadata();
|
|
76
|
-
var Spinner =
|
|
76
|
+
var Spinner = useParcaContext().loader;
|
|
77
77
|
var _m = useLabelNames(queryClient), labelNamesLoading = _m.loading, result = _m.result;
|
|
78
78
|
var labelNamesResponse = result.response, labelNamesError = result.error;
|
|
79
79
|
var LoadingSpinner = function () {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ProfileSelection } from '..';
|
|
1
|
+
import { ProfileSelection, NavigateFunction } from '..';
|
|
2
2
|
import { QueryServiceClient } from '@parca/client';
|
|
3
|
-
import { NavigateFunction } from '.';
|
|
4
3
|
import { QuerySelection } from '../ProfileSelector';
|
|
5
4
|
interface ProfileExplorerCompareProps {
|
|
6
5
|
queryClient: QueryServiceClient;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { QueryServiceClient } from '@parca/client';
|
|
2
|
-
import { ProfileSelection } from '..';
|
|
3
|
-
import { NavigateFunction } from '../ProfileExplorer';
|
|
2
|
+
import { ProfileSelection, NavigateFunction } from '..';
|
|
4
3
|
import { QuerySelection } from '../ProfileSelector';
|
|
5
4
|
interface ProfileExplorerSingleProps {
|
|
6
5
|
queryClient: QueryServiceClient;
|
|
@@ -13,7 +13,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
13
|
import { ProfileSelectionFromParams, SuffixParams } from '..';
|
|
14
14
|
import ProfileExplorerSingle from './ProfileExplorerSingle';
|
|
15
15
|
import ProfileExplorerCompare from './ProfileExplorerCompare';
|
|
16
|
-
import { useAppSelector, useAppDispatch, setCompare, selectCompareMode, setSearchNodeString, store, } from '@parca/store';
|
|
16
|
+
import { useAppSelector, useAppDispatch, setCompare, selectCompareMode, setSearchNodeString, store, selectFilterByFunction, } from '@parca/store';
|
|
17
17
|
import { Provider, batch } from 'react-redux';
|
|
18
18
|
import { DateTimeRange } from '@parca/components';
|
|
19
19
|
import { useEffect } from 'react';
|
|
@@ -31,6 +31,21 @@ var sanitizeDateRange = function (time_selection_a, from_a, to_a) {
|
|
|
31
31
|
return { time_selection_a: range.getRangeKey(), from_a: from_a, to_a: to_a };
|
|
32
32
|
};
|
|
33
33
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
34
|
+
var filterSuffix = function (o, suffix) {
|
|
35
|
+
return Object.fromEntries(Object.entries(o).filter(function (_a) {
|
|
36
|
+
var key = _a[0];
|
|
37
|
+
return !key.endsWith(suffix);
|
|
38
|
+
}));
|
|
39
|
+
};
|
|
40
|
+
var swapQueryParameters = function (o) {
|
|
41
|
+
Object.entries(o).forEach(function (_a) {
|
|
42
|
+
var key = _a[0], value = _a[1];
|
|
43
|
+
if (key.endsWith('_b')) {
|
|
44
|
+
o[key.slice(0, -2) + '_a'] = value;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return o;
|
|
48
|
+
};
|
|
34
49
|
var ProfileExplorerApp = function (_a) {
|
|
35
50
|
var _b, _c;
|
|
36
51
|
var queryClient = _a.queryClient, queryParams = _a.queryParams, navigateTo = _a.navigateTo;
|
|
@@ -39,6 +54,7 @@ var ProfileExplorerApp = function (_a) {
|
|
|
39
54
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
40
55
|
var from_a = queryParams.from_a, to_a = queryParams.to_a, merge_a = queryParams.merge_a, profile_name_a = queryParams.profile_name_a, labels_a = queryParams.labels_a, time_a = queryParams.time_a, time_selection_a = queryParams.time_selection_a, compare_a = queryParams.compare_a, from_b = queryParams.from_b, to_b = queryParams.to_b, merge_b = queryParams.merge_b, profile_name_b = queryParams.profile_name_b, labels_b = queryParams.labels_b, time_b = queryParams.time_b, time_selection_b = queryParams.time_selection_b, compare_b = queryParams.compare_b;
|
|
41
56
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
57
|
+
var filterByFunction = useAppSelector(selectFilterByFunction);
|
|
42
58
|
var sanitizedRange = sanitizeDateRange(time_selection_a, from_a, to_a);
|
|
43
59
|
time_selection_a = sanitizedRange.time_selection_a;
|
|
44
60
|
from_a = sanitizedRange.from_a;
|
|
@@ -59,30 +75,16 @@ var ProfileExplorerApp = function (_a) {
|
|
|
59
75
|
dispatch(setCompare(false));
|
|
60
76
|
}
|
|
61
77
|
}, [dispatch, compare_a, compare_b]);
|
|
62
|
-
var
|
|
63
|
-
return Object.fromEntries(Object.entries(o).filter(function (_a) {
|
|
64
|
-
var key = _a[0];
|
|
65
|
-
return !key.endsWith(suffix);
|
|
66
|
-
}));
|
|
67
|
-
};
|
|
68
|
-
var swapQueryParameters = function (o) {
|
|
69
|
-
Object.entries(o).forEach(function (_a) {
|
|
70
|
-
var key = _a[0], value = _a[1];
|
|
71
|
-
if (key.endsWith('_b')) {
|
|
72
|
-
o[key.slice(0, -2) + '_a'] = value;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
return o;
|
|
76
|
-
};
|
|
77
|
-
var selectProfileA = function (p) {
|
|
78
|
+
var selectProfile = function (p, suffix) {
|
|
78
79
|
queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
|
|
79
80
|
queryParams.expression_b = encodeURIComponent(queryParams.expression_b);
|
|
80
|
-
return navigateTo('/', __assign(__assign({}, queryParams), SuffixParams(p.HistoryParams(),
|
|
81
|
+
return navigateTo('/', __assign(__assign({}, queryParams), SuffixParams(p.HistoryParams(), suffix)));
|
|
82
|
+
};
|
|
83
|
+
var selectProfileA = function (p) {
|
|
84
|
+
return selectProfile(p, '_a');
|
|
81
85
|
};
|
|
82
86
|
var selectProfileB = function (p) {
|
|
83
|
-
|
|
84
|
-
queryParams.expression_b = encodeURIComponent(queryParams.expression_b);
|
|
85
|
-
return navigateTo('/', __assign(__assign({}, queryParams), SuffixParams(p.HistoryParams(), '_b')));
|
|
87
|
+
return selectProfile(p, '_b');
|
|
86
88
|
};
|
|
87
89
|
// Show the SingleProfileExplorer when not comparing
|
|
88
90
|
if (compare_a !== 'true' && compare_b !== 'true') {
|
|
@@ -94,7 +96,7 @@ var ProfileExplorerApp = function (_a) {
|
|
|
94
96
|
profile_name: profile_name_a,
|
|
95
97
|
timeSelection: time_selection_a,
|
|
96
98
|
};
|
|
97
|
-
var profile_1 = ProfileSelectionFromParams(expression_a, from_a, to_a, merge_a, labels_a, profile_name_a, time_a);
|
|
99
|
+
var profile_1 = ProfileSelectionFromParams(expression_a, from_a, to_a, merge_a, labels_a, profile_name_a, time_a, filterByFunction);
|
|
98
100
|
var selectQuery = function (q) {
|
|
99
101
|
return navigateTo('/', __assign(__assign({}, filterSuffix(queryParams, '_a')), {
|
|
100
102
|
expression_a: encodeURIComponent(q.expression),
|
|
@@ -105,7 +107,7 @@ var ProfileExplorerApp = function (_a) {
|
|
|
105
107
|
currentProfileView: 'icicle',
|
|
106
108
|
}));
|
|
107
109
|
};
|
|
108
|
-
var
|
|
110
|
+
var selectProfile_1 = function (p) {
|
|
109
111
|
queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
|
|
110
112
|
return navigateTo('/', __assign(__assign({}, queryParams), SuffixParams(p.HistoryParams(), '_a')));
|
|
111
113
|
};
|
|
@@ -138,7 +140,7 @@ var ProfileExplorerApp = function (_a) {
|
|
|
138
140
|
});
|
|
139
141
|
void navigateTo('/', compareQuery);
|
|
140
142
|
};
|
|
141
|
-
return (_jsx(ProfileExplorerSingle, { queryClient: queryClient, query: query_1, profile: profile_1, selectQuery: selectQuery, selectProfile:
|
|
143
|
+
return (_jsx(ProfileExplorerSingle, { queryClient: queryClient, query: query_1, profile: profile_1, selectQuery: selectQuery, selectProfile: selectProfile_1, compareProfile: compareProfile, navigateTo: navigateTo }));
|
|
142
144
|
}
|
|
143
145
|
var queryA = {
|
|
144
146
|
expression: expression_a,
|
|
@@ -167,6 +169,7 @@ var ProfileExplorerApp = function (_a) {
|
|
|
167
169
|
to_a: q.to.toString(),
|
|
168
170
|
merge_a: q.merge,
|
|
169
171
|
time_selection_a: q.timeSelection,
|
|
172
|
+
filterByFunction: filterByFunction,
|
|
170
173
|
}));
|
|
171
174
|
};
|
|
172
175
|
var selectQueryB = function (q) {
|
|
@@ -178,6 +181,7 @@ var ProfileExplorerApp = function (_a) {
|
|
|
178
181
|
to_b: q.to.toString(),
|
|
179
182
|
merge_b: q.merge,
|
|
180
183
|
time_selection_b: q.timeSelection,
|
|
184
|
+
filterByFunction: filterByFunction,
|
|
181
185
|
}));
|
|
182
186
|
};
|
|
183
187
|
var closeProfile = function (card) {
|
|
@@ -62,7 +62,7 @@ import { useState, useEffect } from 'react';
|
|
|
62
62
|
import MetricsGraph from '../MetricsGraph';
|
|
63
63
|
import { SingleProfileSelection } from '..';
|
|
64
64
|
import { Timestamp } from '@parca/client';
|
|
65
|
-
import { useGrpcMetadata,
|
|
65
|
+
import { useGrpcMetadata, useParcaContext } from '@parca/components';
|
|
66
66
|
import { Query } from '@parca/parser';
|
|
67
67
|
import useDelayedLoader from '../useDelayedLoader';
|
|
68
68
|
export var useQueryRange = function (client, queryExpression, start, end) {
|
|
@@ -100,7 +100,7 @@ var ProfileMetricsGraph = function (_a) {
|
|
|
100
100
|
var queryClient = _a.queryClient, queryExpression = _a.queryExpression, profile = _a.profile, from = _a.from, to = _a.to, select = _a.select, setTimeRange = _a.setTimeRange, addLabelMatcher = _a.addLabelMatcher;
|
|
101
101
|
var _b = useQueryRange(queryClient, queryExpression, from, to), isLoading = _b.isLoading, response = _b.response, error = _b.error;
|
|
102
102
|
var isLoaderVisible = useDelayedLoader(isLoading);
|
|
103
|
-
var loader =
|
|
103
|
+
var loader = useParcaContext().loader;
|
|
104
104
|
if (isLoaderVisible) {
|
|
105
105
|
return _jsx(_Fragment, { children: loader });
|
|
106
106
|
}
|
package/dist/ProfileSource.d.ts
CHANGED
|
@@ -26,12 +26,13 @@ export declare function SuffixParams(params: {
|
|
|
26
26
|
[key: string]: any;
|
|
27
27
|
};
|
|
28
28
|
export declare function ParseLabels(labels: string[]): Label[];
|
|
29
|
-
export declare function ProfileSelectionFromParams(expression: string | undefined, from: string | undefined, to: string | undefined, merge: string | undefined, labels: string[] | undefined, profileName: string | undefined, time: string | undefined): ProfileSelection | null;
|
|
29
|
+
export declare function ProfileSelectionFromParams(expression: string | undefined, from: string | undefined, to: string | undefined, merge: string | undefined, labels: string[] | undefined, profileName: string | undefined, time: string | undefined, filterQuery?: string): ProfileSelection | null;
|
|
30
30
|
export declare class SingleProfileSelection implements ProfileSelection {
|
|
31
31
|
profileName: string;
|
|
32
32
|
labels: Label[];
|
|
33
33
|
time: number;
|
|
34
|
-
|
|
34
|
+
filterQuery: string | undefined;
|
|
35
|
+
constructor(profileName: string, labels: Label[], time: number, filterQuery?: string);
|
|
35
36
|
ProfileName(): string;
|
|
36
37
|
HistoryParams(): {
|
|
37
38
|
[key: string]: any;
|
|
@@ -43,7 +44,8 @@ export declare class MergedProfileSelection implements ProfileSelection {
|
|
|
43
44
|
from: number;
|
|
44
45
|
to: number;
|
|
45
46
|
query: string;
|
|
46
|
-
|
|
47
|
+
filterQuery: string | undefined;
|
|
48
|
+
constructor(from: number, to: number, query: string, filterQuery?: string);
|
|
47
49
|
ProfileName(): string;
|
|
48
50
|
HistoryParams(): {
|
|
49
51
|
[key: string]: string;
|
|
@@ -55,7 +57,8 @@ export declare class SingleProfileSource implements ProfileSource {
|
|
|
55
57
|
profName: string;
|
|
56
58
|
labels: Label[];
|
|
57
59
|
time: number;
|
|
58
|
-
|
|
60
|
+
filterQuery: string | undefined;
|
|
61
|
+
constructor(profileName: string, labels: Label[], time: number, filterQuery?: string);
|
|
59
62
|
query(): string;
|
|
60
63
|
DiffSelection(): ProfileDiffSelection;
|
|
61
64
|
QueryRequest(): QueryRequest;
|
|
@@ -68,7 +71,8 @@ export declare class SingleProfileSource implements ProfileSource {
|
|
|
68
71
|
export declare class ProfileDiffSource implements ProfileSource {
|
|
69
72
|
a: ProfileSource;
|
|
70
73
|
b: ProfileSource;
|
|
71
|
-
|
|
74
|
+
filterQuery: string | undefined;
|
|
75
|
+
constructor(a: ProfileSource, b: ProfileSource, filterQuery?: string);
|
|
72
76
|
DiffSelection(): ProfileDiffSelection;
|
|
73
77
|
QueryRequest(): QueryRequest;
|
|
74
78
|
ProfileType(): ProfileType;
|
|
@@ -79,7 +83,8 @@ export declare class MergedProfileSource implements ProfileSource {
|
|
|
79
83
|
from: number;
|
|
80
84
|
to: number;
|
|
81
85
|
query: string;
|
|
82
|
-
|
|
86
|
+
filterQuery: string | undefined;
|
|
87
|
+
constructor(from: number, to: number, query: string, filterQuery?: string);
|
|
83
88
|
DiffSelection(): ProfileDiffSelection;
|
|
84
89
|
QueryRequest(): QueryRequest;
|
|
85
90
|
ProfileType(): ProfileType;
|
package/dist/ProfileSource.js
CHANGED
|
@@ -34,24 +34,25 @@ export function ParseLabels(labels) {
|
|
|
34
34
|
return { name: parts[0], value: parts[1] };
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
export function ProfileSelectionFromParams(expression, from, to, merge, labels, profileName, time) {
|
|
37
|
+
export function ProfileSelectionFromParams(expression, from, to, merge, labels, profileName, time, filterQuery) {
|
|
38
38
|
if (merge !== undefined &&
|
|
39
39
|
merge === 'true' &&
|
|
40
40
|
from !== undefined &&
|
|
41
41
|
to !== undefined &&
|
|
42
42
|
expression !== undefined) {
|
|
43
|
-
return new MergedProfileSelection(parseInt(from), parseInt(to), expression);
|
|
43
|
+
return new MergedProfileSelection(parseInt(from), parseInt(to), expression, filterQuery);
|
|
44
44
|
}
|
|
45
45
|
if (labels !== undefined && time !== undefined && profileName !== undefined) {
|
|
46
|
-
return new SingleProfileSelection(profileName, ParseLabels(labels), parseInt(time));
|
|
46
|
+
return new SingleProfileSelection(profileName, ParseLabels(labels), parseInt(time), filterQuery);
|
|
47
47
|
}
|
|
48
48
|
return null;
|
|
49
49
|
}
|
|
50
50
|
var SingleProfileSelection = /** @class */ (function () {
|
|
51
|
-
function SingleProfileSelection(profileName, labels, time) {
|
|
51
|
+
function SingleProfileSelection(profileName, labels, time, filterQuery) {
|
|
52
52
|
this.profileName = profileName;
|
|
53
53
|
this.labels = labels;
|
|
54
54
|
this.time = time;
|
|
55
|
+
this.filterQuery = filterQuery;
|
|
55
56
|
}
|
|
56
57
|
SingleProfileSelection.prototype.ProfileName = function () {
|
|
57
58
|
return this.profileName;
|
|
@@ -67,16 +68,17 @@ var SingleProfileSelection = /** @class */ (function () {
|
|
|
67
68
|
return 'single';
|
|
68
69
|
};
|
|
69
70
|
SingleProfileSelection.prototype.ProfileSource = function () {
|
|
70
|
-
return new SingleProfileSource(this.profileName, this.labels, this.time);
|
|
71
|
+
return new SingleProfileSource(this.profileName, this.labels, this.time, this.filterQuery);
|
|
71
72
|
};
|
|
72
73
|
return SingleProfileSelection;
|
|
73
74
|
}());
|
|
74
75
|
export { SingleProfileSelection };
|
|
75
76
|
var MergedProfileSelection = /** @class */ (function () {
|
|
76
|
-
function MergedProfileSelection(from, to, query) {
|
|
77
|
+
function MergedProfileSelection(from, to, query, filterQuery) {
|
|
77
78
|
this.from = from;
|
|
78
79
|
this.to = to;
|
|
79
80
|
this.query = query;
|
|
81
|
+
this.filterQuery = filterQuery;
|
|
80
82
|
}
|
|
81
83
|
MergedProfileSelection.prototype.ProfileName = function () {
|
|
82
84
|
return Query.parse(this.query).profileName();
|
|
@@ -93,16 +95,17 @@ var MergedProfileSelection = /** @class */ (function () {
|
|
|
93
95
|
return 'merge';
|
|
94
96
|
};
|
|
95
97
|
MergedProfileSelection.prototype.ProfileSource = function () {
|
|
96
|
-
return new MergedProfileSource(this.from, this.to, this.query);
|
|
98
|
+
return new MergedProfileSource(this.from, this.to, this.query, this.filterQuery);
|
|
97
99
|
};
|
|
98
100
|
return MergedProfileSelection;
|
|
99
101
|
}());
|
|
100
102
|
export { MergedProfileSelection };
|
|
101
103
|
var SingleProfileSource = /** @class */ (function () {
|
|
102
|
-
function SingleProfileSource(profileName, labels, time) {
|
|
104
|
+
function SingleProfileSource(profileName, labels, time, filterQuery) {
|
|
103
105
|
this.profName = profileName;
|
|
104
106
|
this.labels = labels;
|
|
105
107
|
this.time = time;
|
|
108
|
+
this.filterQuery = filterQuery;
|
|
106
109
|
}
|
|
107
110
|
SingleProfileSource.prototype.query = function () {
|
|
108
111
|
var seriesQuery = this.profName +
|
|
@@ -134,6 +137,7 @@ var SingleProfileSource = /** @class */ (function () {
|
|
|
134
137
|
},
|
|
135
138
|
reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
|
|
136
139
|
mode: QueryRequest_Mode.SINGLE_UNSPECIFIED,
|
|
140
|
+
filterQuery: this.filterQuery,
|
|
137
141
|
};
|
|
138
142
|
};
|
|
139
143
|
SingleProfileSource.prototype.ProfileType = function () {
|
|
@@ -160,9 +164,10 @@ var SingleProfileSource = /** @class */ (function () {
|
|
|
160
164
|
}());
|
|
161
165
|
export { SingleProfileSource };
|
|
162
166
|
var ProfileDiffSource = /** @class */ (function () {
|
|
163
|
-
function ProfileDiffSource(a, b) {
|
|
167
|
+
function ProfileDiffSource(a, b, filterQuery) {
|
|
164
168
|
this.a = a;
|
|
165
169
|
this.b = b;
|
|
170
|
+
this.filterQuery = filterQuery;
|
|
166
171
|
}
|
|
167
172
|
ProfileDiffSource.prototype.DiffSelection = function () {
|
|
168
173
|
throw new Error('Method not implemented.');
|
|
@@ -178,6 +183,7 @@ var ProfileDiffSource = /** @class */ (function () {
|
|
|
178
183
|
},
|
|
179
184
|
reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
|
|
180
185
|
mode: QueryRequest_Mode.DIFF,
|
|
186
|
+
filterQuery: this.filterQuery,
|
|
181
187
|
};
|
|
182
188
|
};
|
|
183
189
|
ProfileDiffSource.prototype.ProfileType = function () {
|
|
@@ -193,10 +199,11 @@ var ProfileDiffSource = /** @class */ (function () {
|
|
|
193
199
|
}());
|
|
194
200
|
export { ProfileDiffSource };
|
|
195
201
|
var MergedProfileSource = /** @class */ (function () {
|
|
196
|
-
function MergedProfileSource(from, to, query) {
|
|
202
|
+
function MergedProfileSource(from, to, query, filterQuery) {
|
|
197
203
|
this.from = from;
|
|
198
204
|
this.to = to;
|
|
199
205
|
this.query = query;
|
|
206
|
+
this.filterQuery = filterQuery;
|
|
200
207
|
}
|
|
201
208
|
MergedProfileSource.prototype.DiffSelection = function () {
|
|
202
209
|
return {
|
|
@@ -223,6 +230,7 @@ var MergedProfileSource = /** @class */ (function () {
|
|
|
223
230
|
},
|
|
224
231
|
reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
|
|
225
232
|
mode: QueryRequest_Mode.MERGE,
|
|
233
|
+
filterQuery: this.filterQuery,
|
|
226
234
|
};
|
|
227
235
|
};
|
|
228
236
|
MergedProfileSource.prototype.ProfileType = function () {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { Input } from '@parca/components';
|
|
15
|
+
import { selectFilterByFunction, setFilterByFunction, useAppDispatch, useAppSelector, } from '@parca/store';
|
|
16
|
+
import { useState } from 'react';
|
|
17
|
+
var FilterByFunctionButton = function () {
|
|
18
|
+
var _a = useState(''), value = _a[0], setValue = _a[1];
|
|
19
|
+
var dispatch = useAppDispatch();
|
|
20
|
+
var storeVal = useAppSelector(selectFilterByFunction);
|
|
21
|
+
var onAction = function () {
|
|
22
|
+
dispatch(setFilterByFunction(value));
|
|
23
|
+
};
|
|
24
|
+
return (_jsx(Input, { placeholder: "Filter by function", className: "text-sm", onAction: onAction, onChange: function (e) { return setValue(e.target.value); }, value: value !== null && value !== void 0 ? value : '', onBlur: function () { return setValue(storeVal !== null && storeVal !== void 0 ? storeVal : ''); } }));
|
|
25
|
+
};
|
|
26
|
+
export default FilterByFunctionButton;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QueryServiceClient, Flamegraph, Top, Callgraph as CallgraphType } from '@parca/client';
|
|
2
|
-
import { ProfileSource } from '
|
|
3
|
-
import '
|
|
2
|
+
import { ProfileSource } from '../ProfileSource';
|
|
3
|
+
import '../ProfileView.styles.css';
|
|
4
4
|
declare type NavigateFunction = (path: string, queryParams: any) => void;
|
|
5
5
|
export interface FlamegraphData {
|
|
6
6
|
loading: boolean;
|
|
@@ -22,19 +22,20 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
22
22
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
23
|
// See the License for the specific language governing permissions and
|
|
24
24
|
// limitations under the License.
|
|
25
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
25
|
+
import { Profiler, useEffect, useMemo, useState } from 'react';
|
|
26
|
+
import { scaleLinear } from 'd3';
|
|
26
27
|
import { getNewSpanColor, parseParams } from '@parca/functions';
|
|
27
28
|
import useUIFeatureFlag from '@parca/functions/useUIFeatureFlag';
|
|
28
|
-
import { Button, Card, SearchNodes,
|
|
29
|
-
import { Callgraph } from './';
|
|
29
|
+
import { Button, Card, SearchNodes, useParcaContext } from '@parca/components';
|
|
30
30
|
import { useContainerDimensions } from '@parca/dynamicsize';
|
|
31
31
|
import { useAppSelector, selectDarkMode, selectSearchNodeString } from '@parca/store';
|
|
32
|
-
import
|
|
33
|
-
import
|
|
34
|
-
import
|
|
35
|
-
import
|
|
36
|
-
import
|
|
37
|
-
import '
|
|
32
|
+
import { Callgraph } from '../';
|
|
33
|
+
import ProfileShareButton from '../components/ProfileShareButton';
|
|
34
|
+
import FilterByFunctionButton from './FilterByFunctionButton';
|
|
35
|
+
import ProfileIcicleGraph from '../ProfileIcicleGraph';
|
|
36
|
+
import TopTable from '../TopTable';
|
|
37
|
+
import useDelayedLoader from '../useDelayedLoader';
|
|
38
|
+
import '../ProfileView.styles.css';
|
|
38
39
|
function arrayEquals(a, b) {
|
|
39
40
|
return (Array.isArray(a) &&
|
|
40
41
|
Array.isArray(b) &&
|
|
@@ -63,7 +64,8 @@ export var ProfileView = function (_a) {
|
|
|
63
64
|
var isDarkMode = useAppSelector(selectDarkMode);
|
|
64
65
|
var currentSearchString = useAppSelector(selectSearchNodeString);
|
|
65
66
|
var callgraphEnabled = useUIFeatureFlag('callgraph')[0];
|
|
66
|
-
var
|
|
67
|
+
var filterByFunctionEnabled = useUIFeatureFlag('filterByFunction')[0];
|
|
68
|
+
var _d = useParcaContext(), loader = _d.loader, perf = _d.perf;
|
|
67
69
|
useEffect(function () {
|
|
68
70
|
// Reset the current path when the profile source changes
|
|
69
71
|
setCurPath([]);
|
|
@@ -115,5 +117,5 @@ export var ProfileView = function (_a) {
|
|
|
115
117
|
return (_jsx(_Fragment, { children: _jsx("div", __assign({ className: "py-3" }, { children: _jsx(Card, { children: _jsxs(Card.Body, { children: [_jsxs("div", __assign({ className: "flex py-3 w-full" }, { children: [_jsxs("div", __assign({ className: "w-2/5 flex space-x-4" }, { children: [_jsxs("div", __assign({ className: "flex space-x-1" }, { children: [profileSource != null && queryClient != null ? (_jsx(ProfileShareButton, { queryRequest: profileSource.QueryRequest(), queryClient: queryClient })) : null, _jsx(Button, __assign({ color: "neutral", onClick: function (e) {
|
|
116
118
|
e.preventDefault();
|
|
117
119
|
onDownloadPProf();
|
|
118
|
-
} }, { children: "Download pprof" }))] })), _jsx(SearchNodes, {})] })), _jsxs("div", __assign({ className: "flex ml-auto" }, { children: [_jsx(
|
|
120
|
+
} }, { children: "Download pprof" }))] })), filterByFunctionEnabled ? _jsx(FilterByFunctionButton, {}) : _jsx(SearchNodes, {})] })), _jsxs("div", __assign({ className: "flex ml-auto gap-2" }, { children: [filterByFunctionEnabled ? _jsx(SearchNodes, {}) : null, _jsx(Button, __assign({ color: "neutral", onClick: resetIcicleGraph, disabled: curPath.length === 0, className: "whitespace-nowrap text-ellipsis" }, { children: "Reset View" })), callgraphEnabled ? (_jsx(Button, __assign({ variant: "".concat(currentView === 'callgraph' ? 'primary' : 'neutral'), onClick: function () { return switchProfileView('callgraph'); }, className: "whitespace-nowrap text-ellipsis" }, { children: "Callgraph" }))) : null, _jsxs("div", __assign({ className: "flex" }, { children: [_jsx(Button, __assign({ variant: "".concat(currentView === 'table' ? 'primary' : 'neutral'), className: "items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons", onClick: function () { return switchProfileView('table'); } }, { children: "Table" })), _jsx(Button, __assign({ variant: "".concat(currentView === 'both' ? 'primary' : 'neutral'), 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", onClick: function () { return switchProfileView('both'); } }, { children: "Both" })), _jsx(Button, __assign({ variant: "".concat(currentView === 'icicle' ? 'primary' : 'neutral'), className: "items-center rounded-tl-none rounded-bl-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons", onClick: function () { return switchProfileView('icicle'); } }, { children: "Icicle Graph" }))] }))] }))] })), _jsxs("div", __assign({ ref: ref, className: "flex space-x-4 justify-between w-full" }, { children: [currentView === 'icicle' && (flamegraphData === null || flamegraphData === void 0 ? void 0 : flamegraphData.data) != null && (_jsx("div", __assign({ className: "w-full" }, { children: _jsx(Profiler, __assign({ id: "icicleGraph", onRender: perf.onRender }, { children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit }) })) }))), currentView === 'callgraph' && (callgraphData === null || callgraphData === void 0 ? void 0 : callgraphData.data) != null && (_jsx("div", __assign({ className: "w-full" }, { children: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) !== undefined && (_jsx(Callgraph, { graph: callgraphData.data, sampleUnit: sampleUnit, width: dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, colorRange: colorRange })) }))), currentView === 'table' && topTableData != null && (_jsx("div", __assign({ className: "w-full" }, { children: _jsx(TopTable, { data: topTableData.data, sampleUnit: sampleUnit }) }))), currentView === 'both' && (_jsxs(_Fragment, { children: [_jsx("div", __assign({ className: "w-1/2" }, { children: _jsx(TopTable, { data: topTableData === null || topTableData === void 0 ? void 0 : topTableData.data, sampleUnit: sampleUnit }) })), _jsx("div", __assign({ className: "w-1/2" }, { children: flamegraphData != null && (_jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, graph: flamegraphData.data, sampleUnit: sampleUnit })) }))] }))] }))] }) }) })) }));
|
|
119
121
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QueryServiceClient } from '@parca/client';
|
|
2
2
|
import { ProfileSource } from './ProfileSource';
|
|
3
|
-
declare type NavigateFunction = (path: string, queryParams: any) => void;
|
|
3
|
+
export declare type NavigateFunction = (path: string, queryParams: any) => void;
|
|
4
4
|
interface ProfileViewWithDataProps {
|
|
5
5
|
queryClient: QueryServiceClient;
|
|
6
6
|
profileSource: ProfileSource;
|
|
@@ -51,8 +51,9 @@ import { QueryRequest_ReportType } from '@parca/client';
|
|
|
51
51
|
import { useQuery } from './useQuery';
|
|
52
52
|
import { ProfileView, useProfileVisState } from './ProfileView';
|
|
53
53
|
import { downloadPprof } from './utils';
|
|
54
|
-
import { useGrpcMetadata } from '@parca/components';
|
|
54
|
+
import { useGrpcMetadata, useParcaContext } from '@parca/components';
|
|
55
55
|
import { saveAsBlob } from '@parca/functions';
|
|
56
|
+
import { useEffect } from 'react';
|
|
56
57
|
export var ProfileViewWithData = function (_a) {
|
|
57
58
|
var _b, _c;
|
|
58
59
|
var queryClient = _a.queryClient, profileSource = _a.profileSource, navigateTo = _a.navigateTo;
|
|
@@ -62,6 +63,13 @@ export var ProfileViewWithData = function (_a) {
|
|
|
62
63
|
var _d = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
|
|
63
64
|
skip: currentView !== 'icicle' && currentView !== 'both',
|
|
64
65
|
}), flamegraphLoading = _d.isLoading, flamegraphResponse = _d.response, flamegraphError = _d.error;
|
|
66
|
+
var perf = useParcaContext().perf;
|
|
67
|
+
useEffect(function () {
|
|
68
|
+
if (flamegraphLoading) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
perf.markInteraction('Flamegraph Render');
|
|
72
|
+
}, [flamegraphLoading, flamegraphResponse, perf]);
|
|
65
73
|
var _e = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
|
|
66
74
|
skip: currentView !== 'table' && currentView !== 'both',
|
|
67
75
|
}), topTableLoading = _e.isLoading, topTableResponse = _e.response, topTableError = _e.error;
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/*! tailwindcss v3.2.1 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.absolute{position:absolute}.relative{position:relative}.left-0{left:0}.right-0{right:0}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-0{margin:0}.m-2{margin:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mr-3{margin-right:.75rem}.ml-auto{margin-left:auto}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.mb-2{margin-bottom:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.h-36{height:9rem}.h-1{height:.25rem}.h-4{height:1rem}.max-h-\[400px\]{max-height:400px}.w-full{width:100%}.w-2\/5{width:40%}.w-auto{width:auto}.w-1\/2{width:50%}.w-\[150px\]{width:150px}.w-1\/5{width:20%}.w-4\/5{width:80%}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-40{width:10rem}.w-8{width:2rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-not-allowed{cursor:not-allowed}.cursor-default{cursor:default}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-scroll{overflow-x:scroll}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-none{border-top-right-radius:0}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl-none{border-top-left-radius:0}.rounded-bl-none{border-bottom-left-radius:0}.border{border-width:1px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-\[\#161616\]{fill:#161616}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.p-10{padding:2.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-20{padding-bottom:5rem;padding-top:5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pt-2{padding-top:.5rem}.pb-2{padding-bottom:.5rem}.pl-2{padding-left:.5rem}.pr-2{padding-right:.5rem}.pb-4{padding-bottom:1rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-xs{font-size:.75rem;line-height:1rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-90{opacity:.9}.opacity-100{opacity:1}.opacity-0{opacity:0}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:bg-\[\#62626212\]:hover{background-color:#62626212}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:divide-gray-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:fill-\[\#ffffff\]{fill:#fff}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}[class~=theme-dark] .dark\:hover\:bg-\[\#ffffff12\]:hover{background-color:#ffffff12}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
|
|
1
|
+
/*! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.absolute{position:absolute}.relative{position:relative}.left-0{left:0}.right-0{right:0}.z-50{z-index:50}.z-10{z-index:10}.m-auto{margin:auto}.m-0{margin:0}.m-2{margin:.5rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-20{margin-bottom:5rem;margin-top:5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mr-3{margin-right:.75rem}.ml-2{margin-left:.5rem}.mr-6{margin-right:1.5rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.ml-auto{margin-left:auto}.mb-2{margin-bottom:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.grid{display:grid}.h-36{height:9rem}.h-1{height:.25rem}.h-4{height:1rem}.max-h-\[400px\]{max-height:400px}.w-full{width:100%}.w-\[150px\]{width:150px}.w-1\/5{width:20%}.w-4\/5{width:80%}.w-1\/4{width:25%}.w-3\/4{width:75%}.w-40{width:10rem}.w-2\/5{width:40%}.w-auto{width:auto}.w-1\/2{width:50%}.w-8{width:2rem}.w-16{width:4rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-\[420px\]{width:420px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.translate-y-1{--tw-translate-y:0.25rem}.translate-y-0,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.cursor-not-allowed{cursor:not-allowed}.cursor-default{cursor:default}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-scroll{overflow-x:scroll}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-l{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.rounded-r{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-none{border-top-right-radius:0}.rounded-br-none{border-bottom-right-radius:0}.rounded-tl-none{border-top-left-radius:0}.rounded-bl-none{border-bottom-left-radius:0}.border{border-width:1px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-inherit{background-color:inherit}.fill-\[\#161616\]{fill:#161616}.fill-transparent{fill:transparent}.fill-current{fill:currentColor}.p-3{padding:.75rem}.p-10{padding:2.5rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-20{padding-bottom:5rem;padding-top:5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-8{padding-left:2rem;padding-right:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pt-2{padding-top:.5rem}.pb-2{padding-bottom:.5rem}.pl-2{padding-left:.5rem}.pr-2{padding-right:.5rem}.pb-4{padding-bottom:1rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-xs{font-size:.75rem;line-height:1rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.\!text-indigo-600{--tw-text-opacity:1!important;color:rgb(79 70 229/var(--tw-text-opacity))!important}.opacity-90{opacity:.9}.opacity-100{opacity:1}.opacity-0{opacity:0}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-150{transition-duration:.15s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:bg-\[\#62626212\]:hover{background-color:#62626212}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-indigo-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(55 48 163/var(--tw-ring-opacity))}[class~=theme-dark] .dark\:divide-gray-700>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity))}[class~=theme-dark] .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}[class~=theme-dark] .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}[class~=theme-dark] .dark\:fill-\[\#ffffff\]{fill:#fff}[class~=theme-dark] .dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}[class~=theme-dark] .dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}[class~=theme-dark] .dark\:\!text-indigo-400{--tw-text-opacity:1!important;color:rgb(129 140 248/var(--tw-text-opacity))!important}[class~=theme-dark] .dark\:hover\:bg-\[\#ffffff12\]:hover{background-color:#ffffff12}@media (min-width:640px){.sm\:inline{display:inline}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
|
package/dist/useQuery.js
CHANGED
|
@@ -30,7 +30,6 @@ export var useQuery = function (client, profileSource, reportType, options) {
|
|
|
30
30
|
isLoading: true,
|
|
31
31
|
});
|
|
32
32
|
var req = profileSource.QueryRequest();
|
|
33
|
-
console.log('req', req);
|
|
34
33
|
req.reportType = reportType;
|
|
35
34
|
var call = client.query(req, { meta: metadata });
|
|
36
35
|
call.response
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.66",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@
|
|
7
|
-
"@parca/
|
|
8
|
-
"@parca/components": "^0.16.59",
|
|
6
|
+
"@parca/client": "^0.16.55",
|
|
7
|
+
"@parca/components": "^0.16.61",
|
|
9
8
|
"@parca/dynamicsize": "^0.16.51",
|
|
10
9
|
"@parca/functions": "^0.16.51",
|
|
11
10
|
"@parca/parser": "^0.16.50",
|
|
12
|
-
"@parca/store": "^0.16.
|
|
11
|
+
"@parca/store": "^0.16.50",
|
|
13
12
|
"d3": "7.6.1",
|
|
14
13
|
"d3-scale": "^4.0.2",
|
|
15
14
|
"d3-selection": "3.0.0",
|
|
@@ -30,8 +29,9 @@
|
|
|
30
29
|
"scripts": {
|
|
31
30
|
"test": "jest --coverage --config ../../../jest.config.js ./src/*",
|
|
32
31
|
"prepublish": "yarn build",
|
|
33
|
-
"build": "tsc &&
|
|
34
|
-
"watch": "tsc-watch --onSuccess '
|
|
32
|
+
"build": "tsc && yarn compile:styles",
|
|
33
|
+
"watch": "tsc-watch --onSuccess 'yarn compile:styles'",
|
|
34
|
+
"compile:styles": "tailwindcss -o dist/styles.css --minify && cp src/*.css ./dist/"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [],
|
|
37
37
|
"author": "",
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"access": "public",
|
|
41
41
|
"registry": "https://registry.npmjs.org/"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "3906954545b45cfa106e146c6dc648a855a0e330"
|
|
44
44
|
}
|
|
@@ -17,7 +17,8 @@ import {Query} from '@parca/parser';
|
|
|
17
17
|
import {LabelsResponse, QueryServiceClient} from '@parca/client';
|
|
18
18
|
import {usePopper} from 'react-popper';
|
|
19
19
|
import cx from 'classnames';
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
import {useParcaContext, useGrpcMetadata} from '@parca/components';
|
|
21
22
|
|
|
22
23
|
interface MatchersInputProps {
|
|
23
24
|
queryClient: QueryServiceClient;
|
|
@@ -97,7 +98,7 @@ const MatchersInput = ({
|
|
|
97
98
|
placement: 'bottom-start',
|
|
98
99
|
});
|
|
99
100
|
const metadata = useGrpcMetadata();
|
|
100
|
-
const {loader: Spinner} =
|
|
101
|
+
const {loader: Spinner} = useParcaContext();
|
|
101
102
|
|
|
102
103
|
const {loading: labelNamesLoading, result} = useLabelNames(queryClient);
|
|
103
104
|
const {response: labelNamesResponse, error: labelNamesError} = result;
|
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {ProfileDiffSource, ProfileSelection, ProfileViewWithData} from '..';
|
|
14
|
+
import {ProfileDiffSource, ProfileSelection, ProfileViewWithData, NavigateFunction} from '..';
|
|
15
15
|
import {Query} from '@parca/parser';
|
|
16
16
|
import {QueryServiceClient} from '@parca/client';
|
|
17
17
|
|
|
18
|
-
import {NavigateFunction} from '.';
|
|
19
18
|
import ProfileSelector, {QuerySelection} from '../ProfileSelector';
|
|
20
19
|
|
|
21
20
|
interface ProfileExplorerCompareProps {
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
14
|
import {QueryServiceClient} from '@parca/client';
|
|
15
|
-
import {ProfileSelection, ProfileViewWithData} from '..';
|
|
15
|
+
import {ProfileSelection, ProfileViewWithData, NavigateFunction} from '..';
|
|
16
16
|
|
|
17
|
-
import {NavigateFunction} from '../ProfileExplorer';
|
|
18
17
|
import ProfileSelector, {QuerySelection} from '../ProfileSelector';
|
|
19
18
|
interface ProfileExplorerSingleProps {
|
|
20
19
|
queryClient: QueryServiceClient;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
14
|
import {QuerySelection} from '../ProfileSelector';
|
|
15
|
-
import {ProfileSelection, ProfileSelectionFromParams, SuffixParams} from '..';
|
|
15
|
+
import {ProfileSelection, ProfileSelectionFromParams, SuffixParams, NavigateFunction} from '..';
|
|
16
16
|
import ProfileExplorerSingle from './ProfileExplorerSingle';
|
|
17
17
|
import ProfileExplorerCompare from './ProfileExplorerCompare';
|
|
18
18
|
import {QueryServiceClient} from '@parca/client';
|
|
@@ -23,13 +23,12 @@ import {
|
|
|
23
23
|
selectCompareMode,
|
|
24
24
|
setSearchNodeString,
|
|
25
25
|
store,
|
|
26
|
+
selectFilterByFunction,
|
|
26
27
|
} from '@parca/store';
|
|
27
28
|
import {Provider, batch} from 'react-redux';
|
|
28
29
|
import {DateTimeRange} from '@parca/components';
|
|
29
30
|
import {useEffect} from 'react';
|
|
30
31
|
|
|
31
|
-
export type NavigateFunction = (path: string, queryParams: any) => void;
|
|
32
|
-
|
|
33
32
|
interface ProfileExplorerProps {
|
|
34
33
|
queryClient: QueryServiceClient;
|
|
35
34
|
queryParams: any;
|
|
@@ -56,6 +55,23 @@ const sanitizeDateRange = (
|
|
|
56
55
|
};
|
|
57
56
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
58
57
|
|
|
58
|
+
const filterSuffix = (
|
|
59
|
+
o: {[key: string]: string | string[] | undefined},
|
|
60
|
+
suffix: string
|
|
61
|
+
): {[key: string]: string | string[] | undefined} =>
|
|
62
|
+
Object.fromEntries(Object.entries(o).filter(([key]) => !key.endsWith(suffix)));
|
|
63
|
+
|
|
64
|
+
const swapQueryParameters = (o: {
|
|
65
|
+
[key: string]: string | string[] | undefined;
|
|
66
|
+
}): {[key: string]: string | string[] | undefined} => {
|
|
67
|
+
Object.entries(o).forEach(([key, value]) => {
|
|
68
|
+
if (key.endsWith('_b')) {
|
|
69
|
+
o[key.slice(0, -2) + '_a'] = value;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return o;
|
|
73
|
+
};
|
|
74
|
+
|
|
59
75
|
const ProfileExplorerApp = ({
|
|
60
76
|
queryClient,
|
|
61
77
|
queryParams,
|
|
@@ -84,6 +100,7 @@ const ProfileExplorerApp = ({
|
|
|
84
100
|
compare_b,
|
|
85
101
|
} = queryParams;
|
|
86
102
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
103
|
+
const filterByFunction = useAppSelector(selectFilterByFunction);
|
|
87
104
|
|
|
88
105
|
const sanitizedRange = sanitizeDateRange(time_selection_a, from_a, to_a);
|
|
89
106
|
time_selection_a = sanitizedRange.time_selection_a;
|
|
@@ -107,39 +124,21 @@ const ProfileExplorerApp = ({
|
|
|
107
124
|
}
|
|
108
125
|
}, [dispatch, compare_a, compare_b]);
|
|
109
126
|
|
|
110
|
-
const
|
|
111
|
-
o: {[key: string]: string | string[] | undefined},
|
|
112
|
-
suffix: string
|
|
113
|
-
): {[key: string]: string | string[] | undefined} =>
|
|
114
|
-
Object.fromEntries(Object.entries(o).filter(([key]) => !key.endsWith(suffix)));
|
|
115
|
-
|
|
116
|
-
const swapQueryParameters = (o: {
|
|
117
|
-
[key: string]: string | string[] | undefined;
|
|
118
|
-
}): {[key: string]: string | string[] | undefined} => {
|
|
119
|
-
Object.entries(o).forEach(([key, value]) => {
|
|
120
|
-
if (key.endsWith('_b')) {
|
|
121
|
-
o[key.slice(0, -2) + '_a'] = value;
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
return o;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const selectProfileA = (p: ProfileSelection): void => {
|
|
127
|
+
const selectProfile = (p: ProfileSelection, suffix: string): void => {
|
|
128
128
|
queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
|
|
129
129
|
queryParams.expression_b = encodeURIComponent(queryParams.expression_b);
|
|
130
130
|
return navigateTo('/', {
|
|
131
131
|
...queryParams,
|
|
132
|
-
...SuffixParams(p.HistoryParams(),
|
|
132
|
+
...SuffixParams(p.HistoryParams(), suffix),
|
|
133
133
|
});
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
+
const selectProfileA = (p: ProfileSelection): void => {
|
|
137
|
+
return selectProfile(p, '_a');
|
|
138
|
+
};
|
|
139
|
+
|
|
136
140
|
const selectProfileB = (p: ProfileSelection): void => {
|
|
137
|
-
|
|
138
|
-
queryParams.expression_b = encodeURIComponent(queryParams.expression_b);
|
|
139
|
-
return navigateTo('/', {
|
|
140
|
-
...queryParams,
|
|
141
|
-
...SuffixParams(p.HistoryParams(), '_b'),
|
|
142
|
-
});
|
|
141
|
+
return selectProfile(p, '_b');
|
|
143
142
|
};
|
|
144
143
|
|
|
145
144
|
// Show the SingleProfileExplorer when not comparing
|
|
@@ -160,7 +159,8 @@ const ProfileExplorerApp = ({
|
|
|
160
159
|
merge_a as string,
|
|
161
160
|
labels_a as string[],
|
|
162
161
|
profile_name_a as string,
|
|
163
|
-
time_a as string
|
|
162
|
+
time_a as string,
|
|
163
|
+
filterByFunction
|
|
164
164
|
);
|
|
165
165
|
|
|
166
166
|
const selectQuery = (q: QuerySelection): void => {
|
|
@@ -295,6 +295,7 @@ const ProfileExplorerApp = ({
|
|
|
295
295
|
to_a: q.to.toString(),
|
|
296
296
|
merge_a: q.merge,
|
|
297
297
|
time_selection_a: q.timeSelection,
|
|
298
|
+
filterByFunction,
|
|
298
299
|
},
|
|
299
300
|
}
|
|
300
301
|
);
|
|
@@ -315,6 +316,7 @@ const ProfileExplorerApp = ({
|
|
|
315
316
|
to_b: q.to.toString(),
|
|
316
317
|
merge_b: q.merge,
|
|
317
318
|
time_selection_b: q.timeSelection,
|
|
319
|
+
filterByFunction,
|
|
318
320
|
},
|
|
319
321
|
}
|
|
320
322
|
);
|
|
@@ -16,7 +16,7 @@ import MetricsGraph from '../MetricsGraph';
|
|
|
16
16
|
import {ProfileSelection, SingleProfileSelection} from '..';
|
|
17
17
|
import {QueryServiceClient, QueryRangeResponse, Label, Timestamp} from '@parca/client';
|
|
18
18
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
19
|
-
import {DateTimeRange, useGrpcMetadata,
|
|
19
|
+
import {DateTimeRange, useGrpcMetadata, useParcaContext} from '@parca/components';
|
|
20
20
|
import {Query} from '@parca/parser';
|
|
21
21
|
import useDelayedLoader from '../useDelayedLoader';
|
|
22
22
|
|
|
@@ -89,7 +89,7 @@ const ProfileMetricsGraph = ({
|
|
|
89
89
|
}: ProfileMetricsGraphProps): JSX.Element => {
|
|
90
90
|
const {isLoading, response, error} = useQueryRange(queryClient, queryExpression, from, to);
|
|
91
91
|
const isLoaderVisible = useDelayedLoader(isLoading);
|
|
92
|
-
const {loader} =
|
|
92
|
+
const {loader} = useParcaContext();
|
|
93
93
|
|
|
94
94
|
if (isLoaderVisible) {
|
|
95
95
|
return <>{loader}</>;
|
package/src/ProfileSource.tsx
CHANGED
|
@@ -70,7 +70,8 @@ export function ProfileSelectionFromParams(
|
|
|
70
70
|
merge: string | undefined,
|
|
71
71
|
labels: string[] | undefined,
|
|
72
72
|
profileName: string | undefined,
|
|
73
|
-
time: string | undefined
|
|
73
|
+
time: string | undefined,
|
|
74
|
+
filterQuery?: string
|
|
74
75
|
): ProfileSelection | null {
|
|
75
76
|
if (
|
|
76
77
|
merge !== undefined &&
|
|
@@ -79,10 +80,15 @@ export function ProfileSelectionFromParams(
|
|
|
79
80
|
to !== undefined &&
|
|
80
81
|
expression !== undefined
|
|
81
82
|
) {
|
|
82
|
-
return new MergedProfileSelection(parseInt(from), parseInt(to), expression);
|
|
83
|
+
return new MergedProfileSelection(parseInt(from), parseInt(to), expression, filterQuery);
|
|
83
84
|
}
|
|
84
85
|
if (labels !== undefined && time !== undefined && profileName !== undefined) {
|
|
85
|
-
return new SingleProfileSelection(
|
|
86
|
+
return new SingleProfileSelection(
|
|
87
|
+
profileName,
|
|
88
|
+
ParseLabels(labels),
|
|
89
|
+
parseInt(time),
|
|
90
|
+
filterQuery
|
|
91
|
+
);
|
|
86
92
|
}
|
|
87
93
|
return null;
|
|
88
94
|
}
|
|
@@ -91,11 +97,13 @@ export class SingleProfileSelection implements ProfileSelection {
|
|
|
91
97
|
profileName: string;
|
|
92
98
|
labels: Label[];
|
|
93
99
|
time: number;
|
|
100
|
+
filterQuery: string | undefined;
|
|
94
101
|
|
|
95
|
-
constructor(profileName: string, labels: Label[], time: number) {
|
|
102
|
+
constructor(profileName: string, labels: Label[], time: number, filterQuery?: string) {
|
|
96
103
|
this.profileName = profileName;
|
|
97
104
|
this.labels = labels;
|
|
98
105
|
this.time = time;
|
|
106
|
+
this.filterQuery = filterQuery;
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
ProfileName(): string {
|
|
@@ -115,7 +123,7 @@ export class SingleProfileSelection implements ProfileSelection {
|
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
ProfileSource(): ProfileSource {
|
|
118
|
-
return new SingleProfileSource(this.profileName, this.labels, this.time);
|
|
126
|
+
return new SingleProfileSource(this.profileName, this.labels, this.time, this.filterQuery);
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
|
|
@@ -123,11 +131,13 @@ export class MergedProfileSelection implements ProfileSelection {
|
|
|
123
131
|
from: number;
|
|
124
132
|
to: number;
|
|
125
133
|
query: string;
|
|
134
|
+
filterQuery: string | undefined;
|
|
126
135
|
|
|
127
|
-
constructor(from: number, to: number, query: string) {
|
|
136
|
+
constructor(from: number, to: number, query: string, filterQuery?: string) {
|
|
128
137
|
this.from = from;
|
|
129
138
|
this.to = to;
|
|
130
139
|
this.query = query;
|
|
140
|
+
this.filterQuery = filterQuery;
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
ProfileName(): string {
|
|
@@ -148,7 +158,7 @@ export class MergedProfileSelection implements ProfileSelection {
|
|
|
148
158
|
}
|
|
149
159
|
|
|
150
160
|
ProfileSource(): ProfileSource {
|
|
151
|
-
return new MergedProfileSource(this.from, this.to, this.query);
|
|
161
|
+
return new MergedProfileSource(this.from, this.to, this.query, this.filterQuery);
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
164
|
|
|
@@ -156,11 +166,13 @@ export class SingleProfileSource implements ProfileSource {
|
|
|
156
166
|
profName: string;
|
|
157
167
|
labels: Label[];
|
|
158
168
|
time: number;
|
|
169
|
+
filterQuery: string | undefined;
|
|
159
170
|
|
|
160
|
-
constructor(profileName: string, labels: Label[], time: number) {
|
|
171
|
+
constructor(profileName: string, labels: Label[], time: number, filterQuery?: string) {
|
|
161
172
|
this.profName = profileName;
|
|
162
173
|
this.labels = labels;
|
|
163
174
|
this.time = time;
|
|
175
|
+
this.filterQuery = filterQuery;
|
|
164
176
|
}
|
|
165
177
|
|
|
166
178
|
query(): string {
|
|
@@ -196,6 +208,7 @@ export class SingleProfileSource implements ProfileSource {
|
|
|
196
208
|
},
|
|
197
209
|
reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
|
|
198
210
|
mode: QueryRequest_Mode.SINGLE_UNSPECIFIED,
|
|
211
|
+
filterQuery: this.filterQuery,
|
|
199
212
|
};
|
|
200
213
|
}
|
|
201
214
|
|
|
@@ -247,10 +260,12 @@ export class SingleProfileSource implements ProfileSource {
|
|
|
247
260
|
export class ProfileDiffSource implements ProfileSource {
|
|
248
261
|
a: ProfileSource;
|
|
249
262
|
b: ProfileSource;
|
|
263
|
+
filterQuery: string | undefined;
|
|
250
264
|
|
|
251
|
-
constructor(a: ProfileSource, b: ProfileSource) {
|
|
265
|
+
constructor(a: ProfileSource, b: ProfileSource, filterQuery?: string) {
|
|
252
266
|
this.a = a;
|
|
253
267
|
this.b = b;
|
|
268
|
+
this.filterQuery = filterQuery;
|
|
254
269
|
}
|
|
255
270
|
|
|
256
271
|
DiffSelection(): ProfileDiffSelection {
|
|
@@ -268,6 +283,7 @@ export class ProfileDiffSource implements ProfileSource {
|
|
|
268
283
|
},
|
|
269
284
|
reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
|
|
270
285
|
mode: QueryRequest_Mode.DIFF,
|
|
286
|
+
filterQuery: this.filterQuery,
|
|
271
287
|
};
|
|
272
288
|
}
|
|
273
289
|
|
|
@@ -292,11 +308,13 @@ export class MergedProfileSource implements ProfileSource {
|
|
|
292
308
|
from: number;
|
|
293
309
|
to: number;
|
|
294
310
|
query: string;
|
|
311
|
+
filterQuery: string | undefined;
|
|
295
312
|
|
|
296
|
-
constructor(from: number, to: number, query: string) {
|
|
313
|
+
constructor(from: number, to: number, query: string, filterQuery?: string) {
|
|
297
314
|
this.from = from;
|
|
298
315
|
this.to = to;
|
|
299
316
|
this.query = query;
|
|
317
|
+
this.filterQuery = filterQuery;
|
|
300
318
|
}
|
|
301
319
|
|
|
302
320
|
DiffSelection(): ProfileDiffSelection {
|
|
@@ -325,6 +343,7 @@ export class MergedProfileSource implements ProfileSource {
|
|
|
325
343
|
},
|
|
326
344
|
reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
|
|
327
345
|
mode: QueryRequest_Mode.MERGE,
|
|
346
|
+
filterQuery: this.filterQuery,
|
|
328
347
|
};
|
|
329
348
|
}
|
|
330
349
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {Input} from '@parca/components';
|
|
15
|
+
import {
|
|
16
|
+
selectFilterByFunction,
|
|
17
|
+
setFilterByFunction,
|
|
18
|
+
useAppDispatch,
|
|
19
|
+
useAppSelector,
|
|
20
|
+
} from '@parca/store';
|
|
21
|
+
import {useState} from 'react';
|
|
22
|
+
|
|
23
|
+
const FilterByFunctionButton = (): JSX.Element => {
|
|
24
|
+
const [value, setValue] = useState<string>('');
|
|
25
|
+
const dispatch = useAppDispatch();
|
|
26
|
+
const storeVal = useAppSelector(selectFilterByFunction);
|
|
27
|
+
|
|
28
|
+
const onAction = (): void => {
|
|
29
|
+
dispatch(setFilterByFunction(value));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Input
|
|
34
|
+
placeholder="Filter by function"
|
|
35
|
+
className="text-sm"
|
|
36
|
+
onAction={onAction}
|
|
37
|
+
onChange={e => setValue(e.target.value)}
|
|
38
|
+
value={value ?? ''}
|
|
39
|
+
onBlur={() => setValue(storeVal ?? '')}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default FilterByFunctionButton;
|
|
@@ -11,24 +11,25 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import
|
|
14
|
+
import {Profiler, useEffect, useMemo, useState} from 'react';
|
|
15
|
+
import {scaleLinear} from 'd3';
|
|
15
16
|
|
|
16
17
|
import {getNewSpanColor, parseParams} from '@parca/functions';
|
|
17
18
|
import useUIFeatureFlag from '@parca/functions/useUIFeatureFlag';
|
|
18
19
|
import {QueryServiceClient, Flamegraph, Top, Callgraph as CallgraphType} from '@parca/client';
|
|
19
|
-
import {Button, Card, SearchNodes,
|
|
20
|
-
import {Callgraph} from './';
|
|
20
|
+
import {Button, Card, SearchNodes, useParcaContext} from '@parca/components';
|
|
21
21
|
import {useContainerDimensions} from '@parca/dynamicsize';
|
|
22
22
|
import {useAppSelector, selectDarkMode, selectSearchNodeString} from '@parca/store';
|
|
23
23
|
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
24
|
+
import {Callgraph} from '../';
|
|
25
|
+
import ProfileShareButton from '../components/ProfileShareButton';
|
|
26
|
+
import FilterByFunctionButton from './FilterByFunctionButton';
|
|
27
|
+
import ProfileIcicleGraph from '../ProfileIcicleGraph';
|
|
28
|
+
import {ProfileSource} from '../ProfileSource';
|
|
29
|
+
import TopTable from '../TopTable';
|
|
30
|
+
import useDelayedLoader from '../useDelayedLoader';
|
|
30
31
|
|
|
31
|
-
import '
|
|
32
|
+
import '../ProfileView.styles.css';
|
|
32
33
|
|
|
33
34
|
type NavigateFunction = (path: string, queryParams: any) => void;
|
|
34
35
|
|
|
@@ -113,8 +114,9 @@ export const ProfileView = ({
|
|
|
113
114
|
const currentSearchString = useAppSelector(selectSearchNodeString);
|
|
114
115
|
|
|
115
116
|
const [callgraphEnabled] = useUIFeatureFlag('callgraph');
|
|
117
|
+
const [filterByFunctionEnabled] = useUIFeatureFlag('filterByFunction');
|
|
116
118
|
|
|
117
|
-
const {loader} =
|
|
119
|
+
const {loader, perf} = useParcaContext();
|
|
118
120
|
|
|
119
121
|
useEffect(() => {
|
|
120
122
|
// Reset the current path when the profile source changes
|
|
@@ -208,69 +210,69 @@ export const ProfileView = ({
|
|
|
208
210
|
Download pprof
|
|
209
211
|
</Button>
|
|
210
212
|
</div>
|
|
211
|
-
|
|
212
|
-
<SearchNodes />
|
|
213
|
+
{filterByFunctionEnabled ? <FilterByFunctionButton /> : <SearchNodes />}
|
|
213
214
|
</div>
|
|
214
215
|
|
|
215
|
-
<div className="flex ml-auto">
|
|
216
|
-
<
|
|
216
|
+
<div className="flex ml-auto gap-2">
|
|
217
|
+
{filterByFunctionEnabled ? <SearchNodes /> : null}
|
|
218
|
+
<Button
|
|
219
|
+
color="neutral"
|
|
220
|
+
onClick={resetIcicleGraph}
|
|
221
|
+
disabled={curPath.length === 0}
|
|
222
|
+
className="whitespace-nowrap text-ellipsis"
|
|
223
|
+
>
|
|
224
|
+
Reset View
|
|
225
|
+
</Button>
|
|
226
|
+
|
|
227
|
+
{callgraphEnabled ? (
|
|
217
228
|
<Button
|
|
218
|
-
|
|
219
|
-
onClick={
|
|
220
|
-
disabled={curPath.length === 0}
|
|
229
|
+
variant={`${currentView === 'callgraph' ? 'primary' : 'neutral'}`}
|
|
230
|
+
onClick={() => switchProfileView('callgraph')}
|
|
221
231
|
className="whitespace-nowrap text-ellipsis"
|
|
222
232
|
>
|
|
223
|
-
|
|
233
|
+
Callgraph
|
|
224
234
|
</Button>
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
{callgraphEnabled ? (
|
|
228
|
-
<div className="mr-3">
|
|
229
|
-
<Button
|
|
230
|
-
variant={`${currentView === 'callgraph' ? 'primary' : 'neutral'}`}
|
|
231
|
-
onClick={() => switchProfileView('callgraph')}
|
|
232
|
-
className="whitespace-nowrap text-ellipsis"
|
|
233
|
-
>
|
|
234
|
-
Callgraph
|
|
235
|
-
</Button>
|
|
236
|
-
</div>
|
|
237
235
|
) : null}
|
|
238
236
|
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
237
|
+
<div className="flex">
|
|
238
|
+
<Button
|
|
239
|
+
variant={`${currentView === 'table' ? 'primary' : 'neutral'}`}
|
|
240
|
+
className="items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
|
|
241
|
+
onClick={() => switchProfileView('table')}
|
|
242
|
+
>
|
|
243
|
+
Table
|
|
244
|
+
</Button>
|
|
246
245
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
<Button
|
|
247
|
+
variant={`${currentView === 'both' ? 'primary' : 'neutral'}`}
|
|
248
|
+
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"
|
|
249
|
+
onClick={() => switchProfileView('both')}
|
|
250
|
+
>
|
|
251
|
+
Both
|
|
252
|
+
</Button>
|
|
254
253
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
254
|
+
<Button
|
|
255
|
+
variant={`${currentView === 'icicle' ? 'primary' : 'neutral'}`}
|
|
256
|
+
className="items-center rounded-tl-none rounded-bl-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
|
|
257
|
+
onClick={() => switchProfileView('icicle')}
|
|
258
|
+
>
|
|
259
|
+
Icicle Graph
|
|
260
|
+
</Button>
|
|
261
|
+
</div>
|
|
262
262
|
</div>
|
|
263
263
|
</div>
|
|
264
264
|
|
|
265
265
|
<div ref={ref} className="flex space-x-4 justify-between w-full">
|
|
266
266
|
{currentView === 'icicle' && flamegraphData?.data != null && (
|
|
267
267
|
<div className="w-full">
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
<Profiler id="icicleGraph" onRender={perf.onRender}>
|
|
269
|
+
<ProfileIcicleGraph
|
|
270
|
+
curPath={curPath}
|
|
271
|
+
setNewCurPath={setNewCurPath}
|
|
272
|
+
graph={flamegraphData.data}
|
|
273
|
+
sampleUnit={sampleUnit}
|
|
274
|
+
/>
|
|
275
|
+
</Profiler>
|
|
274
276
|
</div>
|
|
275
277
|
)}
|
|
276
278
|
{currentView === 'callgraph' && callgraphData?.data != null && (
|
|
@@ -17,10 +17,11 @@ import {useQuery} from './useQuery';
|
|
|
17
17
|
import {ProfileView, useProfileVisState} from './ProfileView';
|
|
18
18
|
import {ProfileSource} from './ProfileSource';
|
|
19
19
|
import {downloadPprof} from './utils';
|
|
20
|
-
import {useGrpcMetadata} from '@parca/components';
|
|
20
|
+
import {useGrpcMetadata, useParcaContext} from '@parca/components';
|
|
21
21
|
import {saveAsBlob} from '@parca/functions';
|
|
22
|
+
import {useEffect} from 'react';
|
|
22
23
|
|
|
23
|
-
type NavigateFunction = (path: string, queryParams: any) => void;
|
|
24
|
+
export type NavigateFunction = (path: string, queryParams: any) => void;
|
|
24
25
|
|
|
25
26
|
interface ProfileViewWithDataProps {
|
|
26
27
|
queryClient: QueryServiceClient;
|
|
@@ -44,6 +45,14 @@ export const ProfileViewWithData = ({
|
|
|
44
45
|
} = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
|
|
45
46
|
skip: currentView !== 'icicle' && currentView !== 'both',
|
|
46
47
|
});
|
|
48
|
+
const {perf} = useParcaContext();
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (flamegraphLoading) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
perf.markInteraction('Flamegraph Render');
|
|
55
|
+
}, [flamegraphLoading, flamegraphResponse, perf]);
|
|
47
56
|
|
|
48
57
|
const {
|
|
49
58
|
isLoading: topTableLoading,
|