@parca/profile 0.16.390 → 0.16.391

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.391 (2024-06-24)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## [0.16.390](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.389...@parca/profile@0.16.390) (2024-06-20)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -10,6 +10,6 @@ const ProfileExplorerCompare = ({ queryClient, queryA, queryB, profileA, profile
10
10
  const closeProfileB = () => {
11
11
  closeProfile('B');
12
12
  };
13
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex justify-between gap-2", children: [_jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true, onCompareProfile: () => { } }) }), _jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true, onCompareProfile: () => { } }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { children: _jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileViewWithData, { navigateTo: navigateTo, queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource()) }) }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
13
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex justify-between gap-2", children: [_jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true }) }), _jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { children: _jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileViewWithData, { navigateTo: navigateTo, queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource()) }) }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
14
14
  };
15
15
  export default ProfileExplorerCompare;
@@ -9,8 +9,7 @@ interface ProfileExplorerSingleProps {
9
9
  selectQuery: (query: QuerySelection) => void;
10
10
  selectProfile: (source: ProfileSelection) => void;
11
11
  profile: ProfileSelection | null;
12
- compareProfile: () => void;
13
12
  navigateTo: NavigateFunction;
14
13
  }
15
- declare const ProfileExplorerSingle: ({ queryClient, query, selectQuery, selectProfile, profile, compareProfile, navigateTo, }: ProfileExplorerSingleProps) => JSX.Element;
14
+ declare const ProfileExplorerSingle: ({ queryClient, query, selectQuery, selectProfile, profile, navigateTo, }: ProfileExplorerSingleProps) => JSX.Element;
16
15
  export default ProfileExplorerSingle;
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { Card } from '@parca/components';
3
3
  import { ProfileViewWithData } from '..';
4
4
  import ProfileSelector from '../ProfileSelector';
5
- const ProfileExplorerSingle = ({ queryClient, query, selectQuery, selectProfile, profile, compareProfile, navigateTo, }) => {
6
- return (_jsxs(_Fragment, { children: [_jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: query, selectQuery: selectQuery, selectProfile: selectProfile, closeProfile: () => { }, profileSelection: profile, comparing: false, onCompareProfile: compareProfile, enforcedProfileName: '' }) }), profile != null ? (_jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: profile.ProfileSource(), navigateTo: navigateTo }) })) : (_jsx(_Fragment, {}))] }));
5
+ const ProfileExplorerSingle = ({ queryClient, query, selectQuery, selectProfile, profile, navigateTo, }) => {
6
+ return (_jsxs(_Fragment, { children: [_jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: query, selectQuery: selectQuery, selectProfile: selectProfile, closeProfile: () => { }, profileSelection: profile, comparing: false, enforcedProfileName: '' }) }), profile != null ? (_jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: profile.ProfileSource(), navigateTo: navigateTo }) })) : (_jsx(_Fragment, {}))] }));
7
7
  };
8
8
  export default ProfileExplorerSingle;
@@ -6,5 +6,6 @@ interface ProfileExplorerProps {
6
6
  queryParams: any;
7
7
  navigateTo: NavigateFunction;
8
8
  }
9
+ export declare const getExpressionAsAString: (expression: string | []) => string;
9
10
  declare const ProfileExplorer: ({ queryClient, queryParams, navigateTo, }: ProfileExplorerProps) => JSX.Element;
10
11
  export default ProfileExplorer;
@@ -23,7 +23,7 @@ import ProfileExplorerSingle from './ProfileExplorerSingle';
23
23
  const ErrorContent = ({ errorMessage }) => {
24
24
  return (_jsx("div", { className: "relative rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700", role: "alert", children: _jsx("span", { className: "block sm:inline", children: errorMessage }) }));
25
25
  };
26
- const getExpressionAsAString = (expression) => {
26
+ export const getExpressionAsAString = (expression) => {
27
27
  const x = Array.isArray(expression) ? expression.join() : expression;
28
28
  return x;
29
29
  };
@@ -168,32 +168,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
168
168
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
169
169
  });
170
170
  };
171
- const compareProfile = () => {
172
- let compareQuery = {
173
- compare_a: 'true',
174
- expression_a: encodeURIComponent(queryA.expression),
175
- from_a: queryA.from.toString(),
176
- to_a: queryA.to.toString(),
177
- time_selection_a: queryA.timeSelection,
178
- compare_b: 'true',
179
- expression_b: encodeURIComponent(queryA.expression),
180
- from_b: queryA.from.toString(),
181
- to_b: queryA.to.toString(),
182
- time_selection_b: queryA.timeSelection,
183
- };
184
- if (profileA != null) {
185
- compareQuery = {
186
- ...SuffixParams(profileA.HistoryParams(), '_a'),
187
- ...compareQuery,
188
- };
189
- }
190
- void navigateTo('/', {
191
- ...compareQuery,
192
- search_string: '',
193
- dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
194
- });
195
- };
196
- return (_jsx(ProfileExplorerSingle, { queryClient: queryClient, query: queryA, profile: profileA, selectQuery: selectQuery, selectProfile: selectProfile, compareProfile: compareProfile, navigateTo: navigateTo }));
171
+ return (_jsx(ProfileExplorerSingle, { queryClient: queryClient, query: queryA, profile: profileA, selectQuery: selectQuery, selectProfile: selectProfile, navigateTo: navigateTo }));
197
172
  }
198
173
  const queryB = {
199
174
  expression: expression_b,
@@ -19,7 +19,6 @@ interface ProfileSelectorProps {
19
19
  enforcedProfileName: string;
20
20
  profileSelection: ProfileSelection | null;
21
21
  comparing: boolean;
22
- onCompareProfile: () => void;
23
22
  }
24
23
  export interface IProfileTypesResult {
25
24
  loading: boolean;
@@ -27,5 +26,5 @@ export interface IProfileTypesResult {
27
26
  error?: RpcError;
28
27
  }
29
28
  export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
30
- declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, onCompareProfile, }: ProfileSelectorProps) => JSX.Element;
29
+ declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, }: ProfileSelectorProps) => JSX.Element;
31
30
  export default ProfileSelector;
@@ -20,7 +20,6 @@ import MatchersInput from '../MatchersInput/index';
20
20
  import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
21
21
  import ProfileMetricsGraph, { ProfileMetricsEmptyState } from '../ProfileMetricsGraph';
22
22
  import ProfileTypeSelector from '../ProfileTypeSelector/index';
23
- import CompareButton from './CompareButton';
24
23
  import { useAutoQuerySelector } from './useAutoQuerySelector';
25
24
  export const useProfileTypes = (client) => {
26
25
  const [result, setResult] = useState(undefined);
@@ -39,7 +38,7 @@ export const useProfileTypes = (client) => {
39
38
  }, [client, metadata, loading]);
40
39
  return { loading, data: result, error };
41
40
  };
42
- const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, onCompareProfile, }) => {
41
+ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, }) => {
43
42
  const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
44
43
  const { heightStyle } = useMetricsGraphDimensions(comparing);
45
44
  const { viewComponent } = useParcaContext();
@@ -138,16 +137,14 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
138
137
  setProfileName,
139
138
  setQueryExpression,
140
139
  });
141
- const handleCompareClick = () => onCompareProfile();
142
140
  const searchDisabled = queryExpressionString === undefined ||
143
141
  queryExpressionString === '' ||
144
142
  queryExpressionString === '{}';
145
- const compareDisabled = selectedProfileName === '' || querySelection.expression === undefined;
146
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsxs("div", { className: "flex w-full flex-wrap content-start items-center justify-between gap-2", children: [_jsxs("div", { className: "pb-6", children: [_jsx("label", { className: "text-xs", children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: error, disabled: viewComponent?.disableProfileTypesDropdown })] }), _jsxs("div", { className: "w-full flex-1 pb-6", children: [_jsxs("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: [_jsx("label", { className: "text-xs", children: "Query" }), (query.matchers.length > 0 || query.inputMatcherString.length > 0) &&
147
- viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsxs(ButtonGroup, { children: [!searchDisabled && (_jsx(_Fragment, { children: !comparing && (_jsx(CompareButton, { disabled: compareDisabled, onClick: handleCompareClick })) })), _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
148
- e.preventDefault();
149
- setQueryExpression(true);
150
- }, id: "h-matcher-search-button", children: "Search" })] })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }), _jsx("div", { className: "rounded bg-white shadow dark:border-gray-500 dark:bg-gray-700", children: _jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== undefined &&
143
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsxs("div", { className: "flex w-full flex-wrap content-start items-center gap-2", children: [_jsxs("div", { className: "pb-6", children: [_jsx("label", { className: "text-xs", children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: error, disabled: viewComponent?.disableProfileTypesDropdown })] }), _jsxs("div", { className: "w-full flex-1 pb-6", children: [_jsxs("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: [_jsx("label", { className: "text-xs", children: "Query" }), (query.matchers.length > 0 || query.inputMatcherString.length > 0) &&
144
+ viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsx(ButtonGroup, { children: _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
145
+ e.preventDefault();
146
+ setQueryExpression(true);
147
+ }, id: "h-matcher-search-button", children: "Search" }) })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }), _jsx("div", { className: "rounded bg-white shadow dark:border-gray-500 dark:bg-gray-700", children: _jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== undefined &&
151
148
  querySelection.expression.length > 0 &&
152
149
  querySelection.from !== undefined &&
153
150
  querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, setTimeRange: (range) => {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Props as CallgraphProps } from './Callgraph';
2
- import ProfileExplorer from './ProfileExplorer';
2
+ import ProfileExplorer, { getExpressionAsAString } from './ProfileExplorer';
3
3
  import ProfileTypeSelector from './ProfileTypeSelector';
4
4
  export * from './ProfileIcicleGraph/IcicleGraph';
5
5
  export * from './ProfileIcicleGraph';
@@ -11,4 +11,4 @@ export * from './ProfileTypeSelector';
11
11
  export * from './SourceView';
12
12
  export { default as Callgraph } from './Callgraph';
13
13
  export type { CallgraphProps };
14
- export { ProfileExplorer, ProfileTypeSelector };
14
+ export { ProfileExplorer, ProfileTypeSelector, getExpressionAsAString };
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
- import ProfileExplorer from './ProfileExplorer';
13
+ import ProfileExplorer, { getExpressionAsAString } from './ProfileExplorer';
14
14
  import ProfileTypeSelector from './ProfileTypeSelector';
15
15
  export * from './ProfileIcicleGraph/IcicleGraph';
16
16
  export * from './ProfileIcicleGraph';
@@ -21,4 +21,4 @@ export * from './utils';
21
21
  export * from './ProfileTypeSelector';
22
22
  export * from './SourceView';
23
23
  export { default as Callgraph } from './Callgraph';
24
- export { ProfileExplorer, ProfileTypeSelector };
24
+ export { ProfileExplorer, ProfileTypeSelector, getExpressionAsAString };
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { RpcMetadata } from '@protobuf-ts/runtime-rpc';
2
2
  import { QueryRequest, QueryServiceClient } from '@parca/client';
3
+ import { type NavigateFunction } from '@parca/utilities';
3
4
  export declare const hexifyAddress: (address?: bigint) => string;
4
5
  export declare const downloadPprof: (request: QueryRequest, queryClient: QueryServiceClient, metadata: RpcMetadata) => Promise<Blob>;
5
6
  export declare const truncateString: (str: string, num: number) => string;
6
7
  export declare const truncateStringReverse: (str: string, num: number) => string;
8
+ export declare const compareProfile: (navigateTo: NavigateFunction, defaultDashboardItems?: string[]) => void;
package/dist/utils.js CHANGED
@@ -11,6 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { QueryRequest_ReportType } from '@parca/client';
14
+ import { parseParams } from '@parca/utilities';
15
+ import { ProfileSelectionFromParams, SuffixParams, getExpressionAsAString } from '.';
14
16
  export const hexifyAddress = (address) => {
15
17
  if (address == null) {
16
18
  return '';
@@ -41,3 +43,47 @@ export const truncateStringReverse = (str, num) => {
41
43
  }
42
44
  return '...' + str.slice(str.length - num);
43
45
  };
46
+ export const compareProfile = (navigateTo, defaultDashboardItems = ['icicle']) => {
47
+ const queryParams = parseParams(window.location.search);
48
+ /* eslint-disable @typescript-eslint/naming-convention */
49
+ const { from_a, to_a, merge_from_a, merge_to_a, time_selection_a, filter_by_function, dashboard_items, } = queryParams;
50
+ // eslint-disable-next-line @typescript-eslint/naming-convention
51
+ const selection_a = getExpressionAsAString(queryParams.selection_a);
52
+ // eslint-disable-next-line @typescript-eslint/naming-convention
53
+ const expression_a = getExpressionAsAString(queryParams.expression_a);
54
+ if (expression_a === undefined || selection_a === undefined) {
55
+ return;
56
+ }
57
+ const mergeFrom = merge_from_a ?? undefined;
58
+ const mergeTo = merge_to_a ?? undefined;
59
+ const profileA = ProfileSelectionFromParams(mergeFrom, mergeTo, selection_a, filter_by_function);
60
+ const queryA = {
61
+ expression: expression_a,
62
+ from: parseInt(from_a),
63
+ to: parseInt(to_a),
64
+ timeSelection: time_selection_a,
65
+ };
66
+ let compareQuery = {
67
+ compare_a: 'true',
68
+ expression_a: encodeURIComponent(queryA.expression),
69
+ from_a: queryA.from.toString(),
70
+ to_a: queryA.to.toString(),
71
+ time_selection_a: queryA.timeSelection,
72
+ compare_b: 'true',
73
+ expression_b: encodeURIComponent(queryA.expression),
74
+ from_b: queryA.from.toString(),
75
+ to_b: queryA.to.toString(),
76
+ time_selection_b: queryA.timeSelection,
77
+ };
78
+ if (profileA != null) {
79
+ compareQuery = {
80
+ ...SuffixParams(profileA.HistoryParams(), '_a'),
81
+ ...compareQuery,
82
+ };
83
+ }
84
+ void navigateTo('/', {
85
+ ...compareQuery,
86
+ search_string: '',
87
+ dashboard_items: dashboard_items ?? defaultDashboardItems,
88
+ });
89
+ };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.390",
3
+ "version": "0.16.391",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
7
7
  "@iconify/react": "^4.0.0",
8
8
  "@parca/client": "0.16.117",
9
- "@parca/components": "0.16.283",
9
+ "@parca/components": "0.16.284",
10
10
  "@parca/dynamicsize": "0.16.65",
11
11
  "@parca/hooks": "0.0.60",
12
12
  "@parca/icons": "0.16.69",
@@ -71,5 +71,5 @@
71
71
  "access": "public",
72
72
  "registry": "https://registry.npmjs.org/"
73
73
  },
74
- "gitHead": "da04231e85bce981a4be8b040e3d319772ea79cb"
74
+ "gitHead": "db964de537aebc04da1d2741dcf14f691d78a6a0"
75
75
  }
@@ -69,7 +69,6 @@ const ProfileExplorerCompare = ({
69
69
  closeProfile={closeProfileA}
70
70
  enforcedProfileName={''}
71
71
  comparing={true}
72
- onCompareProfile={() => {}}
73
72
  />
74
73
  </Card>
75
74
  <Card className="mt-2 p-2">
@@ -82,7 +81,6 @@ const ProfileExplorerCompare = ({
82
81
  closeProfile={closeProfileB}
83
82
  enforcedProfileName={Query.parse(queryA.expression).profileName()}
84
83
  comparing={true}
85
- onCompareProfile={() => {}}
86
84
  />
87
85
  </Card>
88
86
  </div>
@@ -24,7 +24,6 @@ interface ProfileExplorerSingleProps {
24
24
  selectQuery: (query: QuerySelection) => void;
25
25
  selectProfile: (source: ProfileSelection) => void;
26
26
  profile: ProfileSelection | null;
27
- compareProfile: () => void;
28
27
  navigateTo: NavigateFunction;
29
28
  }
30
29
 
@@ -34,7 +33,6 @@ const ProfileExplorerSingle = ({
34
33
  selectQuery,
35
34
  selectProfile,
36
35
  profile,
37
- compareProfile,
38
36
  navigateTo,
39
37
  }: ProfileExplorerSingleProps): JSX.Element => {
40
38
  return (
@@ -48,7 +46,6 @@ const ProfileExplorerSingle = ({
48
46
  closeProfile={() => {}} // eslint-disable-line @typescript-eslint/no-empty-function
49
47
  profileSelection={profile}
50
48
  comparing={false}
51
- onCompareProfile={compareProfile}
52
49
  enforcedProfileName={''} // TODO
53
50
  />
54
51
  </Card>
@@ -42,7 +42,7 @@ const ErrorContent = ({errorMessage}: {errorMessage: string}): JSX.Element => {
42
42
  );
43
43
  };
44
44
 
45
- const getExpressionAsAString = (expression: string | []): string => {
45
+ export const getExpressionAsAString = (expression: string | []): string => {
46
46
  const x = Array.isArray(expression) ? expression.join() : expression;
47
47
  return x;
48
48
  };
@@ -263,35 +263,6 @@ const ProfileExplorerApp = ({
263
263
  });
264
264
  };
265
265
 
266
- const compareProfile = (): void => {
267
- let compareQuery = {
268
- compare_a: 'true',
269
- expression_a: encodeURIComponent(queryA.expression),
270
- from_a: queryA.from.toString(),
271
- to_a: queryA.to.toString(),
272
- time_selection_a: queryA.timeSelection,
273
-
274
- compare_b: 'true',
275
- expression_b: encodeURIComponent(queryA.expression),
276
- from_b: queryA.from.toString(),
277
- to_b: queryA.to.toString(),
278
- time_selection_b: queryA.timeSelection,
279
- };
280
-
281
- if (profileA != null) {
282
- compareQuery = {
283
- ...SuffixParams(profileA.HistoryParams(), '_a'),
284
- ...compareQuery,
285
- };
286
- }
287
-
288
- void navigateTo('/', {
289
- ...compareQuery,
290
- search_string: '',
291
- dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
292
- });
293
- };
294
-
295
266
  return (
296
267
  <ProfileExplorerSingle
297
268
  queryClient={queryClient}
@@ -299,7 +270,6 @@ const ProfileExplorerApp = ({
299
270
  profile={profileA}
300
271
  selectQuery={selectQuery}
301
272
  selectProfile={selectProfile}
302
- compareProfile={compareProfile}
303
273
  navigateTo={navigateTo}
304
274
  />
305
275
  );
@@ -33,7 +33,6 @@ import MatchersInput from '../MatchersInput/index';
33
33
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
34
34
  import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
35
35
  import ProfileTypeSelector from '../ProfileTypeSelector/index';
36
- import CompareButton from './CompareButton';
37
36
  import {useAutoQuerySelector} from './useAutoQuerySelector';
38
37
 
39
38
  export interface QuerySelection {
@@ -54,7 +53,6 @@ interface ProfileSelectorProps {
54
53
  enforcedProfileName: string;
55
54
  profileSelection: ProfileSelection | null;
56
55
  comparing: boolean;
57
- onCompareProfile: () => void;
58
56
  }
59
57
 
60
58
  export interface IProfileTypesResult {
@@ -92,7 +90,6 @@ const ProfileSelector = ({
92
90
  enforcedProfileName,
93
91
  profileSelection,
94
92
  comparing,
95
- onCompareProfile,
96
93
  }: ProfileSelectorProps): JSX.Element => {
97
94
  const {
98
95
  loading: profileTypesLoading,
@@ -219,19 +216,15 @@ const ProfileSelector = ({
219
216
  setQueryExpression,
220
217
  });
221
218
 
222
- const handleCompareClick = (): void => onCompareProfile();
223
-
224
219
  const searchDisabled =
225
220
  queryExpressionString === undefined ||
226
221
  queryExpressionString === '' ||
227
222
  queryExpressionString === '{}';
228
223
 
229
- const compareDisabled = selectedProfileName === '' || querySelection.expression === undefined;
230
-
231
224
  return (
232
225
  <>
233
226
  <div className="mb-2 flex gap-2">
234
- <div className="flex w-full flex-wrap content-start items-center justify-between gap-2">
227
+ <div className="flex w-full flex-wrap content-start items-center gap-2">
235
228
  <div className="pb-6">
236
229
  <label className="text-xs">Profile type</label>
237
230
  <ProfileTypeSelector
@@ -261,13 +254,6 @@ const ProfileSelector = ({
261
254
  range={timeRangeSelection}
262
255
  />
263
256
  <ButtonGroup>
264
- {!searchDisabled && (
265
- <>
266
- {!comparing && (
267
- <CompareButton disabled={compareDisabled} onClick={handleCompareClick} />
268
- )}
269
- </>
270
- )}
271
257
  <Button
272
258
  disabled={searchDisabled}
273
259
  onClick={(e: React.MouseEvent<HTMLElement>) => {
package/src/index.tsx CHANGED
@@ -12,7 +12,7 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import type {Props as CallgraphProps} from './Callgraph';
15
- import ProfileExplorer from './ProfileExplorer';
15
+ import ProfileExplorer, {getExpressionAsAString} from './ProfileExplorer';
16
16
  import ProfileTypeSelector from './ProfileTypeSelector';
17
17
 
18
18
  export * from './ProfileIcicleGraph/IcicleGraph';
@@ -27,4 +27,4 @@ export {default as Callgraph} from './Callgraph';
27
27
 
28
28
  export type {CallgraphProps};
29
29
 
30
- export {ProfileExplorer, ProfileTypeSelector};
30
+ export {ProfileExplorer, ProfileTypeSelector, getExpressionAsAString};
package/src/utils.ts CHANGED
@@ -14,6 +14,9 @@
14
14
  import type {RpcMetadata} from '@protobuf-ts/runtime-rpc';
15
15
 
16
16
  import {QueryRequest, QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
17
+ import {parseParams, type NavigateFunction} from '@parca/utilities';
18
+
19
+ import {ProfileSelectionFromParams, SuffixParams, getExpressionAsAString} from '.';
17
20
 
18
21
  export const hexifyAddress = (address?: bigint): string => {
19
22
  if (address == null) {
@@ -59,3 +62,73 @@ export const truncateStringReverse = (str: string, num: number): string => {
59
62
 
60
63
  return '...' + str.slice(str.length - num);
61
64
  };
65
+
66
+ export const compareProfile = (
67
+ navigateTo: NavigateFunction,
68
+ defaultDashboardItems: string[] = ['icicle']
69
+ ): void => {
70
+ const queryParams = parseParams(window.location.search);
71
+
72
+ /* eslint-disable @typescript-eslint/naming-convention */
73
+ const {
74
+ from_a,
75
+ to_a,
76
+ merge_from_a,
77
+ merge_to_a,
78
+ time_selection_a,
79
+ filter_by_function,
80
+ dashboard_items,
81
+ } = queryParams;
82
+
83
+ // eslint-disable-next-line @typescript-eslint/naming-convention
84
+ const selection_a = getExpressionAsAString(queryParams.selection_a as string | []);
85
+
86
+ // eslint-disable-next-line @typescript-eslint/naming-convention
87
+ const expression_a = getExpressionAsAString(queryParams.expression_a as string | []);
88
+
89
+ if (expression_a === undefined || selection_a === undefined) {
90
+ return;
91
+ }
92
+
93
+ const mergeFrom = merge_from_a ?? undefined;
94
+ const mergeTo = merge_to_a ?? undefined;
95
+ const profileA = ProfileSelectionFromParams(
96
+ mergeFrom as string,
97
+ mergeTo as string,
98
+ selection_a,
99
+ filter_by_function as string
100
+ );
101
+ const queryA = {
102
+ expression: expression_a,
103
+ from: parseInt(from_a as string),
104
+ to: parseInt(to_a as string),
105
+ timeSelection: time_selection_a as string,
106
+ };
107
+
108
+ let compareQuery = {
109
+ compare_a: 'true',
110
+ expression_a: encodeURIComponent(queryA.expression),
111
+ from_a: queryA.from.toString(),
112
+ to_a: queryA.to.toString(),
113
+ time_selection_a: queryA.timeSelection,
114
+
115
+ compare_b: 'true',
116
+ expression_b: encodeURIComponent(queryA.expression),
117
+ from_b: queryA.from.toString(),
118
+ to_b: queryA.to.toString(),
119
+ time_selection_b: queryA.timeSelection,
120
+ };
121
+
122
+ if (profileA != null) {
123
+ compareQuery = {
124
+ ...SuffixParams(profileA.HistoryParams(), '_a'),
125
+ ...compareQuery,
126
+ };
127
+ }
128
+
129
+ void navigateTo('/', {
130
+ ...compareQuery,
131
+ search_string: '',
132
+ dashboard_items: dashboard_items ?? defaultDashboardItems,
133
+ });
134
+ };