@parca/profile 0.16.65 → 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 CHANGED
@@ -3,6 +3,10 @@
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
+
6
10
  ## 0.16.65 (2022-11-09)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -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;
@@ -1,5 +1,5 @@
1
+ import { NavigateFunction } from '..';
1
2
  import { QueryServiceClient } from '@parca/client';
2
- export declare type NavigateFunction = (path: string, queryParams: any) => void;
3
3
  interface ProfileExplorerProps {
4
4
  queryClient: QueryServiceClient;
5
5
  queryParams: any;
@@ -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 filterSuffix = function (o, suffix) {
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(), '_a')));
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
- queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
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 selectProfile = function (p) {
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: selectProfile, compareProfile: compareProfile, navigateTo: navigateTo }));
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) {
@@ -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
- constructor(profileName: string, labels: Label[], time: number);
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
- constructor(from: number, to: number, query: string);
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
- constructor(profileName: string, labels: Label[], time: number);
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
- constructor(a: ProfileSource, b: ProfileSource);
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
- constructor(from: number, to: number, query: string);
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;
@@ -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,2 @@
1
+ declare const FilterByFunctionButton: () => JSX.Element;
2
+ export default FilterByFunctionButton;
@@ -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 './ProfileSource';
3
- import './ProfileView.styles.css';
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;
@@ -29,12 +29,13 @@ import useUIFeatureFlag from '@parca/functions/useUIFeatureFlag';
29
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 { Callgraph } from './';
33
- import ProfileShareButton from './components/ProfileShareButton';
34
- import ProfileIcicleGraph from './ProfileIcicleGraph';
35
- import TopTable from './TopTable';
36
- import useDelayedLoader from './useDelayedLoader';
37
- import './ProfileView.styles.css';
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,6 +64,7 @@ export var ProfileView = function (_a) {
63
64
  var isDarkMode = useAppSelector(selectDarkMode);
64
65
  var currentSearchString = useAppSelector(selectSearchNodeString);
65
66
  var callgraphEnabled = useUIFeatureFlag('callgraph')[0];
67
+ var filterByFunctionEnabled = useUIFeatureFlag('filterByFunction')[0];
66
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
@@ -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("div", __assign({ className: "mr-3" }, { children: _jsx(Button, __assign({ color: "neutral", onClick: resetIcicleGraph, disabled: curPath.length === 0, className: "whitespace-nowrap text-ellipsis" }, { children: "Reset View" })) })), callgraphEnabled ? (_jsx("div", __assign({ className: "mr-3" }, { children: _jsx(Button, __assign({ variant: "".concat(currentView === 'callgraph' ? 'primary' : 'neutral'), onClick: function () { return switchProfileView('callgraph'); }, className: "whitespace-nowrap text-ellipsis" }, { children: "Callgraph" })) }))) : null, _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 })) }))] }))] }))] }) }) })) }));
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;
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/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.65",
3
+ "version": "0.16.66",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
- "@iconify/react": "^3.2.2",
7
- "@parca/client": "^0.16.54",
8
- "@parca/components": "^0.16.60",
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.49",
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 && tailwindcss -o dist/styles.css --minify && cp src/*.css ./dist/",
34
- "watch": "tsc-watch --onSuccess 'tailwindcss -o dist/styles.css --minify'"
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": "0b1285b6be9aee7b2716d230e1d4bdc5a0cea4d2"
43
+ "gitHead": "3906954545b45cfa106e146c6dc648a855a0e330"
44
44
  }
@@ -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 filterSuffix = (
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(), '_a'),
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
- queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
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
  );
@@ -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(profileName, ParseLabels(labels), parseInt(time));
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;
@@ -21,14 +21,15 @@ 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 {Callgraph} from './';
25
- import ProfileShareButton from './components/ProfileShareButton';
26
- import ProfileIcicleGraph from './ProfileIcicleGraph';
27
- import {ProfileSource} from './ProfileSource';
28
- import TopTable from './TopTable';
29
- import useDelayedLoader from './useDelayedLoader';
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 './ProfileView.styles.css';
32
+ import '../ProfileView.styles.css';
32
33
 
33
34
  type NavigateFunction = (path: string, queryParams: any) => void;
34
35
 
@@ -113,6 +114,7 @@ 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
119
  const {loader, perf} = useParcaContext();
118
120
 
@@ -208,57 +210,55 @@ 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
- <div className="mr-3">
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
- color="neutral"
219
- onClick={resetIcicleGraph}
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
- Reset View
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
- <Button
240
- variant={`${currentView === 'table' ? 'primary' : 'neutral'}`}
241
- className="items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
242
- onClick={() => switchProfileView('table')}
243
- >
244
- Table
245
- </Button>
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
- <Button
248
- variant={`${currentView === 'both' ? 'primary' : 'neutral'}`}
249
- 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"
250
- onClick={() => switchProfileView('both')}
251
- >
252
- Both
253
- </Button>
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
- <Button
256
- variant={`${currentView === 'icicle' ? 'primary' : 'neutral'}`}
257
- className="items-center rounded-tl-none rounded-bl-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
258
- onClick={() => switchProfileView('icicle')}
259
- >
260
- Icicle Graph
261
- </Button>
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
 
@@ -21,7 +21,7 @@ import {useGrpcMetadata, useParcaContext} from '@parca/components';
21
21
  import {saveAsBlob} from '@parca/functions';
22
22
  import {useEffect} from 'react';
23
23
 
24
- type NavigateFunction = (path: string, queryParams: any) => void;
24
+ export type NavigateFunction = (path: string, queryParams: any) => void;
25
25
 
26
26
  interface ProfileViewWithDataProps {
27
27
  queryClient: QueryServiceClient;