@parca/profile 0.16.369 → 0.16.370

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.370](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.369...@parca/profile@0.16.370) (2024-05-14)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## [0.16.369](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.368...@parca/profile@0.16.369) (2024-05-14)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { Fragment, useCallback, useMemo, useRef, useState } from 'react';
14
+ import { Fragment, useCallback, useId, useMemo, useRef, useState } from 'react';
15
15
  import * as d3 from 'd3';
16
16
  import { pointer } from 'd3-selection';
17
17
  import throttle from 'lodash.throttle';
@@ -45,6 +45,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
45
45
  const [pos, setPos] = useState([0, 0]);
46
46
  const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
47
47
  const metricPointRef = useRef(null);
48
+ const idForContextMenu = useId();
48
49
  // the time of the selected point is the start of the merge window
49
50
  const time = parseFloat(profile?.HistoryParams().merge_from);
50
51
  if (width === undefined || width == null) {
@@ -228,7 +229,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
228
229
  };
229
230
  };
230
231
  const selected = findSelectedProfile();
231
- const MENU_ID = 'metrics-context-menu';
232
+ const MENU_ID = `metrics-context-menu-${idForContextMenu}`;
232
233
  const { show } = useContextMenu({
233
234
  id: MENU_ID,
234
235
  });
@@ -0,0 +1,5 @@
1
+ declare const GroupByDropdown: ({ groupBy, toggleGroupBy, }: {
2
+ groupBy: string[];
3
+ toggleGroupBy: (key: string) => void;
4
+ }) => React.JSX.Element;
5
+ export default GroupByDropdown;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Menu, Transition } from '@headlessui/react';
3
+ const GroupByDropdown = ({ groupBy, toggleGroupBy, }) => {
4
+ const label = groupBy.length === 0
5
+ ? 'Nothing'
6
+ : groupBy.length === 1
7
+ ? groupByOptions.find(option => option.value === groupBy[0])?.label
8
+ : 'Multiple';
9
+ return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Group" }), _jsxs(Menu, { as: "div", className: "relative text-left", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm", children: [_jsx("span", { className: "block overflow-x-hidden text-ellipsis", children: label }), _jsx("span", { className: "pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400", children: _jsx(Icon, { icon: "heroicons:chevron-down-20-solid", "aria-hidden": "true" }) })] }) }), _jsx(Transition, { as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsx(Menu.Items, { className: "absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm", children: _jsx("div", { className: "p-4", children: _jsx("fieldset", { children: _jsx("div", { className: "space-y-5", children: groupByOptions.map(({ value, label, description, disabled }) => (_jsxs("div", { className: "relative flex items-start", children: [_jsx("div", { className: "flex h-6 items-center", children: _jsx("input", { id: value, name: value, type: "checkbox", disabled: disabled, className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", checked: groupBy.includes(value), onChange: () => toggleGroupBy(value) }) }), _jsxs("div", { className: "ml-3 text-sm leading-6", children: [_jsx("label", { htmlFor: value, className: "font-medium text-gray-900 dark:text-gray-200", children: label }), _jsx("p", { className: "text-gray-500 dark:text-gray-400", children: description })] })] }, value))) }) }) }) }) })] })] }));
10
+ };
11
+ export default GroupByDropdown;
@@ -0,0 +1,9 @@
1
+ declare const RuntimeFilterDropdown: ({ showRuntimeRuby, toggleShowRuntimeRuby, showRuntimePython, toggleShowRuntimePython, showInterpretedOnly, toggleShowInterpretedOnly, }: {
2
+ showRuntimeRuby: boolean;
3
+ toggleShowRuntimeRuby: () => void;
4
+ showRuntimePython: boolean;
5
+ toggleShowRuntimePython: () => void;
6
+ showInterpretedOnly: boolean;
7
+ toggleShowInterpretedOnly: () => void;
8
+ }) => React.JSX.Element;
9
+ export default RuntimeFilterDropdown;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ import { Menu, Transition } from '@headlessui/react';
4
+ import { Icon } from '@iconify/react';
5
+ const RuntimeToggle = ({ id, state, toggle, label, description, }) => {
6
+ return (_jsxs("div", { className: "relative flex items-start", children: [_jsx("div", { className: "flex h-6 items-center", children: _jsx("input", { id: id, name: id, type: "checkbox", className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600", checked: state, onChange: () => toggle() }) }), _jsxs("div", { className: "ml-3 text-sm leading-6", children: [_jsx("label", { htmlFor: id, className: "font-medium text-gray-900 dark:text-gray-200", children: label }), _jsx("p", { className: "text-gray-500 dark:text-gray-400", children: description })] })] }, id));
7
+ };
8
+ const RuntimeFilterDropdown = ({ showRuntimeRuby, toggleShowRuntimeRuby, showRuntimePython, toggleShowRuntimePython, showInterpretedOnly, toggleShowInterpretedOnly, }) => {
9
+ return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Runtimes" }), _jsxs(Menu, { as: "div", className: "relative text-left", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm", children: [_jsx("span", { className: "block overflow-x-hidden text-ellipsis", children: "Runtimes" }), _jsx("span", { className: "pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400", children: _jsx(Icon, { icon: "heroicons:chevron-down-20-solid", "aria-hidden": "true" }) })] }) }), _jsx(Transition, { as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0", children: _jsx(Menu.Items, { className: "absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm", children: _jsx("div", { className: "p-4", children: _jsx("fieldset", { children: _jsxs("div", { className: "space-y-5", children: [_jsx(RuntimeToggle, { id: "show-runtime-ruby", state: showRuntimeRuby, toggle: toggleShowRuntimeRuby, label: "Ruby", description: "Show Ruby runtime functions." }), _jsx(RuntimeToggle, { id: "show-runtime-python", state: showRuntimePython, toggle: toggleShowRuntimePython, label: "Python", description: "Show Python runtime functions." }), _jsx(RuntimeToggle, { id: "show-interpreted-only", state: showInterpretedOnly, toggle: toggleShowInterpretedOnly, label: "Interpreted Only", description: "Show only interpreted functions." })] }) }) }) }) })] })] }));
10
+ };
11
+ export default RuntimeFilterDropdown;
@@ -0,0 +1,6 @@
1
+ declare const SortBySelect: ({ sortBy, setSortBy, compareMode, }: {
2
+ sortBy: string;
3
+ setSortBy: (key: string) => void;
4
+ compareMode: boolean;
5
+ }) => React.JSX.Element;
6
+ export default SortBySelect;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_NAME, } from './IcicleGraphArrow';
3
+ const SortBySelect = ({ sortBy, setSortBy, compareMode, }) => {
4
+ return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Sort" }), _jsx(Select, { items: [
5
+ {
6
+ key: FIELD_FUNCTION_NAME,
7
+ disabled: false,
8
+ element: {
9
+ active: _jsx(_Fragment, { children: "Function" }),
10
+ expanded: (_jsx(_Fragment, { children: _jsx("span", { children: "Function" }) })),
11
+ },
12
+ },
13
+ {
14
+ key: FIELD_CUMULATIVE,
15
+ disabled: false,
16
+ element: {
17
+ active: _jsx(_Fragment, { children: "Cumulative" }),
18
+ expanded: (_jsx(_Fragment, { children: _jsx("span", { children: "Cumulative" }) })),
19
+ },
20
+ },
21
+ {
22
+ key: FIELD_DIFF,
23
+ disabled: !compareMode,
24
+ element: {
25
+ active: _jsx(_Fragment, { children: "Diff" }),
26
+ expanded: (_jsx(_Fragment, { children: _jsx("span", { children: "Diff" }) })),
27
+ },
28
+ },
29
+ ], selectedKey: sortBy, onSelection: key => setSortBy(key), placeholder: 'Sort By', primary: false, disabled: false })] }));
30
+ };
31
+ export default SortBySelect;
@@ -143,7 +143,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
143
143
  queryExpressionString === '' ||
144
144
  queryExpressionString === '{}';
145
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 })] }), _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) &&
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
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
148
  e.preventDefault();
149
149
  setQueryExpression(true);
@@ -21,6 +21,7 @@ interface Props {
21
21
  selectedKey: string | undefined;
22
22
  flexibleKnownProfilesDetection?: boolean;
23
23
  onSelection: (value: string | undefined) => void;
24
+ disabled?: boolean;
24
25
  }
25
- declare const ProfileTypeSelector: ({ profileTypesData, loading, error, selectedKey, onSelection, flexibleKnownProfilesDetection, }: Props) => JSX.Element;
26
+ declare const ProfileTypeSelector: ({ profileTypesData, loading, error, selectedKey, onSelection, flexibleKnownProfilesDetection, disabled, }: Props) => JSX.Element;
26
27
  export default ProfileTypeSelector;
@@ -110,7 +110,7 @@ export const normalizeProfileTypesData = (types) => {
110
110
  return a.localeCompare(b);
111
111
  });
112
112
  };
113
- const ProfileTypeSelector = ({ profileTypesData, loading = false, error, selectedKey, onSelection, flexibleKnownProfilesDetection = false, }) => {
113
+ const ProfileTypeSelector = ({ profileTypesData, loading = false, error, selectedKey, onSelection, flexibleKnownProfilesDetection = false, disabled, }) => {
114
114
  const profileNames = useMemo(() => {
115
115
  return (error === undefined || error == null) &&
116
116
  profileTypesData !== undefined &&
@@ -122,6 +122,6 @@ const ProfileTypeSelector = ({ profileTypesData, loading = false, error, selecte
122
122
  key: name,
123
123
  element: profileSelectElement(name, flexibleKnownProfilesDetection),
124
124
  }));
125
- return (_jsx(Select, { items: profileLabels, selectedKey: selectedKey, onSelection: onSelection, placeholder: "Select profile type...", loading: loading, className: "bg-white h-profile-type-dropdown" }));
125
+ return (_jsx(Select, { items: profileLabels, selectedKey: selectedKey, onSelection: onSelection, placeholder: "Select profile type...", loading: loading, className: "bg-white h-profile-type-dropdown", disabled: disabled }));
126
126
  };
127
127
  export default ProfileTypeSelector;
@@ -0,0 +1,12 @@
1
+ import { QueryServiceClient } from '@parca/client';
2
+ import { DateTimeRange } from '@parca/components';
3
+ import { QuerySelection } from '../ProfileSelector';
4
+ interface Props {
5
+ queryClient: QueryServiceClient;
6
+ selectQuery: (query: QuerySelection) => void;
7
+ enforcedProfileName: string;
8
+ timeRangeSelection: DateTimeRange;
9
+ querySelection: QuerySelection;
10
+ }
11
+ declare const QueryBrowser: ({ queryClient, enforcedProfileName, timeRangeSelection, selectQuery, querySelection, }: Props) => import("react/jsx-runtime").JSX.Element;
12
+ export default QueryBrowser;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Query } from '@parca/parser';
4
+ import MatchersInput from '../MatchersInput';
5
+ const QueryBrowser = ({ queryClient, enforcedProfileName, timeRangeSelection, selectQuery, querySelection, }) => {
6
+ const [queryExpressionString, setQueryExpressionString] = useState(querySelection.expression);
7
+ useEffect(() => {
8
+ if (enforcedProfileName !== '') {
9
+ const [q, changed] = Query.parse(querySelection.expression).setProfileName(enforcedProfileName);
10
+ if (changed) {
11
+ setQueryExpressionString(q.toString());
12
+ return;
13
+ }
14
+ }
15
+ setQueryExpressionString(querySelection.expression);
16
+ }, [enforcedProfileName, querySelection.expression]);
17
+ const enforcedProfileNameQuery = () => {
18
+ const pq = Query.parse(queryExpressionString);
19
+ const [q] = pq.setProfileName(enforcedProfileName);
20
+ return q;
21
+ };
22
+ const setMatchersString = (matchers) => {
23
+ const newExpressionString = `${''}{${matchers}}`;
24
+ setQueryExpressionString(newExpressionString);
25
+ };
26
+ const setNewQueryExpression = (expr) => {
27
+ const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
28
+ const delta = query.profileType().delta;
29
+ const from = timeRangeSelection.getFromMs();
30
+ const to = timeRangeSelection.getToMs();
31
+ const mergeParams = delta
32
+ ? {
33
+ mergeFrom: from,
34
+ mergeTo: to,
35
+ }
36
+ : {};
37
+ selectQuery({
38
+ expression: expr,
39
+ from,
40
+ to,
41
+ timeSelection: timeRangeSelection.getRangeKey(),
42
+ ...mergeParams,
43
+ });
44
+ };
45
+ const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(queryExpressionString);
46
+ const setQueryExpression = () => {
47
+ setNewQueryExpression(query.toString());
48
+ };
49
+ return (_jsx(_Fragment, { children: _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query }) }));
50
+ };
51
+ export default QueryBrowser;
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.369",
3
+ "version": "0.16.370",
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.113",
9
- "@parca/components": "^0.16.274",
9
+ "@parca/components": "^0.16.275",
10
10
  "@parca/dynamicsize": "^0.16.64",
11
- "@parca/hooks": "^0.0.54",
11
+ "@parca/hooks": "^0.0.55",
12
12
  "@parca/icons": "^0.16.68",
13
13
  "@parca/parser": "^0.16.73",
14
- "@parca/store": "^0.16.143",
15
- "@parca/utilities": "^0.0.71",
14
+ "@parca/store": "^0.16.144",
15
+ "@parca/utilities": "^0.0.72",
16
16
  "@popperjs/core": "^2.11.8",
17
17
  "@protobuf-ts/runtime-rpc": "^2.5.0",
18
18
  "@tanstack/react-query": "^4.0.5",
@@ -71,5 +71,5 @@
71
71
  "access": "public",
72
72
  "registry": "https://registry.npmjs.org/"
73
73
  },
74
- "gitHead": "704de2373c6b4ac3de52bd666f88e51981700f92"
74
+ "gitHead": "6fb2ee469c00ba81c6b1317771127fdf5806dced"
75
75
  }
@@ -11,7 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {Fragment, useCallback, useMemo, useRef, useState} from 'react';
14
+ import React, {Fragment, useCallback, useId, useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import * as d3 from 'd3';
17
17
  import {pointer} from 'd3-selection';
@@ -140,6 +140,7 @@ export const RawMetricsGraph = ({
140
140
  const [pos, setPos] = useState([0, 0]);
141
141
  const [isContextMenuOpen, setIsContextMenuOpen] = useState<boolean>(false);
142
142
  const metricPointRef = useRef(null);
143
+ const idForContextMenu = useId();
143
144
 
144
145
  // the time of the selected point is the start of the merge window
145
146
  const time: number = parseFloat(profile?.HistoryParams().merge_from);
@@ -372,7 +373,7 @@ export const RawMetricsGraph = ({
372
373
 
373
374
  const selected = findSelectedProfile();
374
375
 
375
- const MENU_ID = 'metrics-context-menu';
376
+ const MENU_ID = `metrics-context-menu-${idForContextMenu}`;
376
377
 
377
378
  const {show} = useContextMenu({
378
379
  id: MENU_ID,
@@ -240,6 +240,7 @@ const ProfileSelector = ({
240
240
  selectedKey={selectedProfileName}
241
241
  onSelection={setProfileName}
242
242
  error={error}
243
+ disabled={viewComponent?.disableProfileTypesDropdown}
243
244
  />
244
245
  </div>
245
246
  <div className="w-full flex-1 pb-6">
@@ -147,6 +147,7 @@ interface Props {
147
147
  selectedKey: string | undefined;
148
148
  flexibleKnownProfilesDetection?: boolean;
149
149
  onSelection: (value: string | undefined) => void;
150
+ disabled?: boolean;
150
151
  }
151
152
 
152
153
  const ProfileTypeSelector = ({
@@ -156,6 +157,7 @@ const ProfileTypeSelector = ({
156
157
  selectedKey,
157
158
  onSelection,
158
159
  flexibleKnownProfilesDetection = false,
160
+ disabled,
159
161
  }: Props): JSX.Element => {
160
162
  const profileNames = useMemo(() => {
161
163
  return (error === undefined || error == null) &&
@@ -178,6 +180,7 @@ const ProfileTypeSelector = ({
178
180
  placeholder="Select profile type..."
179
181
  loading={loading}
180
182
  className="bg-white h-profile-type-dropdown"
183
+ disabled={disabled}
181
184
  />
182
185
  );
183
186
  };