@parca/profile 0.16.341 → 0.16.343
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/MetricsGraph/MetricsTooltip/index.js +2 -2
- package/dist/MetricsGraph/index.js +4 -2
- package/dist/ProfileIcicleGraph/ActionButtons/GroupByDropdown.js +1 -1
- package/dist/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.js +1 -1
- package/dist/ProfileIcicleGraph/ActionButtons/SortBySelect.js +2 -2
- package/dist/ProfileIcicleGraph/index.js +1 -1
- package/dist/ProfileMetricsGraph/index.js +8 -1
- package/dist/ProfileTypeSelector/index.js +1 -1
- package/dist/ProfileView/FilterByFunctionButton.js +1 -1
- package/dist/ProfileView/ViewSelector.d.ts +2 -1
- package/dist/ProfileView/ViewSelector.js +2 -2
- package/dist/ProfileView/VisualizationPanel.js +1 -1
- package/dist/ProfileView/index.js +2 -2
- package/dist/ProfileViewWithData.js +12 -1
- package/dist/components/DiffLegend.js +1 -1
- package/package.json +7 -7
- package/src/MetricsGraph/MetricsTooltip/index.tsx +22 -9
- package/src/MetricsGraph/index.tsx +4 -2
- package/src/ProfileIcicleGraph/ActionButtons/GroupByDropdown.tsx +2 -5
- package/src/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.tsx +2 -5
- package/src/ProfileIcicleGraph/ActionButtons/SortBySelect.tsx +1 -1
- package/src/ProfileIcicleGraph/index.tsx +1 -1
- package/src/ProfileMetricsGraph/index.tsx +9 -1
- package/src/ProfileTypeSelector/index.tsx +1 -1
- package/src/ProfileView/FilterByFunctionButton.tsx +1 -1
- package/src/ProfileView/ViewSelector.tsx +4 -0
- package/src/ProfileView/VisualizationPanel.tsx +6 -1
- package/src/ProfileView/index.tsx +2 -1
- package/src/ProfileViewWithData.tsx +12 -1
- package/src/components/DiffLegend.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.16.343](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.342...@parca/profile@0.16.343) (2024-02-20)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.342](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.341...@parca/profile@0.16.342) (2024-02-14)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
6
14
|
## [0.16.341](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.340...@parca/profile@0.16.341) (2024-02-13)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
// Copyright 2022 The Parca Authors
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
@@ -73,7 +73,7 @@ const MetricsTooltip = ({ x, y, highlighted, contextElement, sampleUnit, delta,
|
|
|
73
73
|
}, [x, y, contextElement, update]);
|
|
74
74
|
const nameLabel = highlighted?.labels.find(e => e.name === '__name__');
|
|
75
75
|
const highlightedNameLabel = nameLabel !== undefined ? nameLabel : { name: '', value: '' };
|
|
76
|
-
return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, className: "z-10", children: _jsx("div", { className: "flex max-w-md", children: _jsx("div", { className: "m-auto", children: _jsx("div", { className: "rounded-lg border-gray-300 bg-gray-50 p-3 opacity-90 shadow-lg dark:border-gray-500 dark:bg-gray-900", style: { borderWidth: 1 }, children: _jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "ml-2 mr-6", children: [_jsx("span", { className: "font-semibold", children: highlightedNameLabel.value }), _jsx("span", { className: "my-2 block text-gray-700 dark:text-gray-300", children: _jsx("table", { className: "table-auto", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "
|
|
76
|
+
return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, className: "z-10", children: _jsx("div", { className: "flex max-w-md", children: _jsx("div", { className: "m-auto", children: _jsx("div", { className: "rounded-lg border-gray-300 bg-gray-50 p-3 opacity-90 shadow-lg dark:border-gray-500 dark:bg-gray-900", style: { borderWidth: 1 }, children: _jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "ml-2 mr-6", children: [_jsx("span", { className: "font-semibold", children: highlightedNameLabel.value }), _jsx("span", { className: "my-2 block text-gray-700 dark:text-gray-300", children: _jsx("table", { className: "table-auto", children: _jsxs("tbody", { children: [delta ? (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Per Second" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.valuePerSecond, sampleUnit === 'nanoseconds' ? 'CPU Cores' : sampleUnit, 5) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Total" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.value, sampleUnit, 2) })] })] })) : (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Value" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.valuePerSecond, sampleUnit, 5) })] })), highlighted.duration > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Duration" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.duration, 'nanoseconds', 2) })] })), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "At" }), _jsx("td", { className: "w-3/4", children: formatDate(highlighted.timestamp, timeFormat) })] })] }) }) }), _jsx("span", { className: "my-2 block text-gray-500", children: highlighted.labels
|
|
77
77
|
.filter((label) => label.name !== '__name__')
|
|
78
78
|
.map((label) => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: _jsx(TextWithTooltip, { text: `${label.name}="${label.value}"`, maxTextLength: 37, id: `tooltip-${label.name}-${label.value}` }) }, label.name))) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to add labels to query." })] })] }) }) }) }) }) }));
|
|
79
79
|
};
|
|
@@ -241,9 +241,11 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
|
|
|
241
241
|
};
|
|
242
242
|
const isDeltaType = profile !== null ? profile?.query.profType.delta : false;
|
|
243
243
|
let yAxisLabel = sampleUnit;
|
|
244
|
+
let yAxisUnit = sampleUnit;
|
|
244
245
|
if (isDeltaType) {
|
|
245
|
-
if (
|
|
246
|
+
if (sampleUnit === 'nanoseconds') {
|
|
246
247
|
yAxisLabel = 'CPU Cores';
|
|
248
|
+
yAxisUnit = '';
|
|
247
249
|
}
|
|
248
250
|
if (sampleUnit === 'bytes') {
|
|
249
251
|
yAxisLabel = 'Bytes per Second';
|
|
@@ -260,7 +262,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLab
|
|
|
260
262
|
}
|
|
261
263
|
return (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
|
|
262
264
|
/* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
|
|
263
|
-
transform: `translate(0, ${yScale(d)})`, children: [_jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x2: -6 }), _jsx("text", { fill: "currentColor", x: -9, dy: '0.32em', children: valueFormatter(d,
|
|
265
|
+
transform: `translate(0, ${yScale(d)})`, children: [_jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x2: -6 }), _jsx("text", { fill: "currentColor", x: -9, dy: '0.32em', children: valueFormatter(d, yAxisUnit, decimals) })] }, `tick-${i}`), _jsx("g", { children: _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(from), x2: xScale(to), y1: yScale(d), y2: yScale(d) }) }, `grid-${i}`)] }, `${i.toString()}-${d.toString()}`));
|
|
264
266
|
}), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: 0, x2: 0, y1: 0, y2: height - margin }), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(to), x2: xScale(to), y1: 0, y2: height - margin }), _jsx("g", { transform: `translate(${-margin}, ${(height - margin) / 2}) rotate(270)`, children: _jsx("text", { fill: "currentColor", dy: "-0.7em", className: "text-sm capitalize", textAnchor: "middle", children: yAxisLabel }) })] }), _jsxs("g", { className: "x axis", fill: "none", fontSize: "10", textAnchor: "middle", transform: `translate(0,${height - margin})`, children: [xScale.ticks(5).map((d, i) => (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
|
|
265
267
|
/* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
|
|
266
268
|
transform: `translate(${xScale(d)}, 0)`, children: [_jsx("line", { y2: 6, className: "stroke-gray-300 dark:stroke-gray-500" }), _jsx("text", { fill: "currentColor", dy: ".71em", y: 9, children: formatDate(d, formatForTimespan(from, to)) })] }, `tick-${i}`), _jsx("g", { children: _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(d), x2: xScale(d), y1: 0, y2: -height + margin }) }, `grid-${i}`)] }, `${i.toString()}-${d.toString()}`))), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(from), x2: xScale(to), y1: 0, y2: 0 }), _jsx("g", { transform: `translate(${(width - 2.5 * margin) / 2}, ${margin / 2})`, children: _jsx("text", { fill: "currentColor", dy: ".71em", y: 5, className: "text-sm", children: "Time" }) })] }), _jsx("g", { className: "lines fill-transparent", children: series.map((s, i) => (_jsx("g", { className: "line", children: _jsx(MetricsSeries, { data: s, line: l, color: color(i.toString()), strokeWidth: hovering && highlighted != null && i === highlighted.seriesIndex
|
|
@@ -52,6 +52,6 @@ const GroupByDropdown = ({ groupBy, toggleGroupBy, }) => {
|
|
|
52
52
|
: groupBy.length === 1
|
|
53
53
|
? groupByOptions.find(option => option.value === groupBy[0])?.label
|
|
54
54
|
: 'Multiple';
|
|
55
|
-
return (_jsxs("div", { className: "relative", children: [_jsx("label", { className: "text-sm", children: "Group" }), _jsxs(Menu, { as: "div", className: "relative text-left",
|
|
55
|
+
return (_jsxs("div", { className: "relative", children: [_jsx("label", { className: "text-sm", children: "Group" }), _jsxs(Menu, { as: "div", className: "relative text-left", id: "h-group-by-filter", children: [_jsxs(Menu.Button, { className: "relative w-max cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] 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 flex items-center pr-2 text-gray-400", children: _jsx(Icon, { icon: "heroicons:chevron-down-20-solid", "aria-hidden": "true" }) })] }), _jsx(Transition, { as: "div", 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))) }) }) }) }) })] })] }));
|
|
56
56
|
};
|
|
57
57
|
export default GroupByDropdown;
|
|
@@ -18,6 +18,6 @@ const RuntimeToggle = ({ id, state, toggle, label, description, }) => {
|
|
|
18
18
|
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));
|
|
19
19
|
};
|
|
20
20
|
const RuntimeFilterDropdown = ({ showRuntimeRuby, toggleShowRuntimeRuby, showRuntimePython, toggleShowRuntimePython, showInterpretedOnly, toggleShowInterpretedOnly, }) => {
|
|
21
|
-
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-[1.7rem] 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",
|
|
21
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Runtimes" }), _jsxs(Menu, { as: "div", className: "relative text-left", id: "h-runtimes-filter", children: [_jsx("div", { children: _jsxs(Menu.Button, { className: "relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] 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." })] }) }) }) }) })] })] }));
|
|
22
22
|
};
|
|
23
23
|
export default RuntimeFilterDropdown;
|
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
14
14
|
import { Select } from '@parca/components';
|
|
15
15
|
import { FIELD_CUMULATIVE, FIELD_DIFF, FIELD_FUNCTION_NAME } from '../IcicleGraphArrow';
|
|
16
16
|
const SortBySelect = ({ sortBy, setSortBy, compareMode, }) => {
|
|
17
|
-
return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Sort" }), _jsx(Select, {
|
|
17
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "text-sm", children: "Sort" }), _jsx(Select, { className: "!px-3", items: [
|
|
18
18
|
{
|
|
19
19
|
key: FIELD_FUNCTION_NAME,
|
|
20
20
|
disabled: false,
|
|
@@ -39,6 +39,6 @@ const SortBySelect = ({ sortBy, setSortBy, compareMode, }) => {
|
|
|
39
39
|
expanded: (_jsx(_Fragment, { children: _jsx("span", { children: "Diff" }) })),
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
|
-
], selectedKey: sortBy, onSelection: key => setSortBy(key), placeholder: 'Sort By', primary: false, disabled: false })] }));
|
|
42
|
+
], selectedKey: sortBy, onSelection: key => setSortBy(key), placeholder: 'Sort By', primary: false, disabled: false, id: "h-sort-by-filter" })] }));
|
|
43
43
|
};
|
|
44
44
|
export default SortBySelect;
|
|
@@ -134,6 +134,6 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, to
|
|
|
134
134
|
if (isTrimmed) {
|
|
135
135
|
console.info(`Trimmed ${trimmedFormatted} (${trimmedPercentage}%) too small values.`);
|
|
136
136
|
}
|
|
137
|
-
return (_jsx(AnimatePresence, { children: _jsxs(motion.div, { className: "relative h-full w-full", initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.5 }, children: [compareMode ? _jsx(DiffLegend, {}) : null, _jsxs("div", { className: "min-h-48", children: [graph !== undefined && (_jsx(IcicleGraph, { width: width, graph: graph, total: total, filtered: filtered, curPath: curPath, setCurPath: setNewCurPath, sampleUnit: sampleUnit, navigateTo: navigateTo })), arrow !== undefined && (_jsx(IcicleGraphArrow, { width: width, arrow: arrow, total: total, filtered: filtered, curPath: curPath, setCurPath: setNewCurPath, sampleUnit: sampleUnit, navigateTo: navigateTo, sortBy: storeSortBy }))] }), _jsxs("p", { className: "my-2 text-xs", children: ["Showing ", totalFormatted, ' ', isFiltered ? (_jsxs("span", { children: ["(", filteredPercentage, "%) filtered of ", totalUnfilteredFormatted, ' '] })) : (_jsx(_Fragment, {})), "values.", ' '] })] }, "icicle-graph-loaded") }));
|
|
137
|
+
return (_jsx(AnimatePresence, { children: _jsxs(motion.div, { className: "relative h-full w-full", initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.5 }, children: [compareMode ? _jsx(DiffLegend, {}) : null, _jsxs("div", { className: "min-h-48", id: "h-icicle-graph", children: [graph !== undefined && (_jsx(IcicleGraph, { width: width, graph: graph, total: total, filtered: filtered, curPath: curPath, setCurPath: setNewCurPath, sampleUnit: sampleUnit, navigateTo: navigateTo })), arrow !== undefined && (_jsx(IcicleGraphArrow, { width: width, arrow: arrow, total: total, filtered: filtered, curPath: curPath, setCurPath: setNewCurPath, sampleUnit: sampleUnit, navigateTo: navigateTo, sortBy: storeSortBy }))] }), _jsxs("p", { className: "my-2 text-xs", children: ["Showing ", totalFormatted, ' ', isFiltered ? (_jsxs("span", { children: ["(", filteredPercentage, "%) filtered of ", totalUnfilteredFormatted, ' '] })) : (_jsx(_Fragment, {})), "values.", ' '] })] }, "icicle-graph-loaded") }));
|
|
138
138
|
};
|
|
139
139
|
export default ProfileIcicleGraph;
|
|
@@ -91,7 +91,14 @@ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to,
|
|
|
91
91
|
const handleSampleClick = (timestamp, _value, labels) => {
|
|
92
92
|
onPointClick(timestamp, labels, queryExpression);
|
|
93
93
|
};
|
|
94
|
-
|
|
94
|
+
let sampleUnit = '';
|
|
95
|
+
if (series.every((val, i, arr) => val?.sampleType?.unit === arr[0]?.sampleType?.unit)) {
|
|
96
|
+
sampleUnit = series[0]?.sampleType?.unit ?? '';
|
|
97
|
+
}
|
|
98
|
+
if (sampleUnit === '') {
|
|
99
|
+
sampleUnit = Query.parse(queryExpression).profileType().sampleUnit;
|
|
100
|
+
}
|
|
101
|
+
return (_jsx(AnimatePresence, { children: _jsx(motion.div, { className: "h-full w-full", initial: { display: 'none', opacity: 0 }, animate: { display: 'block', opacity: 1 }, transition: { duration: 0.5 }, children: _jsx(MetricsGraph, { data: series, from: from, to: to, profile: profile, setTimeRange: setTimeRange, onSampleClick: handleSampleClick, addLabelMatcher: addLabelMatcher, sampleUnit: sampleUnit, height: height, width: width, margin: margin }) }, "metrics-graph-loaded") }));
|
|
95
102
|
}
|
|
96
103
|
return _jsx(ProfileMetricsEmptyState, { message: "No data found. Try a different query." });
|
|
97
104
|
};
|
|
@@ -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" }));
|
|
125
|
+
return (_jsx(Select, { items: profileLabels, selectedKey: selectedKey, onSelection: onSelection, placeholder: "Select profile type...", loading: loading, className: "bg-white h-profile-type-dropdown" }));
|
|
126
126
|
};
|
|
127
127
|
export default ProfileTypeSelector;
|
|
@@ -29,6 +29,6 @@ const FilterByFunctionButton = ({ navigateTo, }) => {
|
|
|
29
29
|
setStoreValue(localValue);
|
|
30
30
|
}
|
|
31
31
|
}, [localValue, isClearAction, setStoreValue]);
|
|
32
|
-
return (_jsx(Input, { placeholder: "Filter by function",
|
|
32
|
+
return (_jsx(Input, { placeholder: "Filter by function", className: "text-sm", onAction: onAction, onChange: e => setLocalValue(e.target.value), value: localValue ?? '', onBlur: () => setLocalValue(storeValue), actionIcon: isClearAction ? _jsx(Icon, { icon: "ep:circle-close" }) : _jsx(Icon, { icon: "ep:arrow-right" }), id: "h-filter-by-function" }));
|
|
33
33
|
};
|
|
34
34
|
export default FilterByFunctionButton;
|
|
@@ -9,6 +9,7 @@ interface Props {
|
|
|
9
9
|
addView?: boolean;
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
icon?: JSX.Element;
|
|
12
|
+
id?: string;
|
|
12
13
|
}
|
|
13
|
-
declare const ViewSelector: ({ defaultValue, navigateTo, position, placeholderText, primary, addView, disabled, icon, }: Props) => JSX.Element;
|
|
14
|
+
declare const ViewSelector: ({ defaultValue, navigateTo, position, placeholderText, primary, addView, disabled, icon, id, }: Props) => JSX.Element;
|
|
14
15
|
export default ViewSelector;
|
|
@@ -13,7 +13,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { Select, useParcaContext, useURLState } from '@parca/components';
|
|
15
15
|
import { useUIFeatureFlag } from '@parca/hooks';
|
|
16
|
-
const ViewSelector = ({ defaultValue, navigateTo, position, placeholderText, primary = false, addView = false, disabled = false, icon, }) => {
|
|
16
|
+
const ViewSelector = ({ defaultValue, navigateTo, position, placeholderText, primary = false, addView = false, disabled = false, icon, id, }) => {
|
|
17
17
|
const [callgraphEnabled] = useUIFeatureFlag('callgraph');
|
|
18
18
|
const [dashboardItems = ['icicle'], setDashboardItems] = useURLState({
|
|
19
19
|
param: 'dashboard_items',
|
|
@@ -65,6 +65,6 @@ const ViewSelector = ({ defaultValue, navigateTo, position, placeholderText, pri
|
|
|
65
65
|
: [dashboardItems[0], value];
|
|
66
66
|
setDashboardItems(newDashboardItems);
|
|
67
67
|
};
|
|
68
|
-
return (_jsx(Select, { items: items, selectedKey: defaultValue, onSelection: onSelection, placeholder: placeholderText ?? 'Select view type...', primary: primary, disabled: disabled, icon: icon }));
|
|
68
|
+
return (_jsx(Select, { className: "h-view-selector", items: items, selectedKey: defaultValue, onSelection: onSelection, placeholder: placeholderText ?? 'Select view type...', primary: primary, disabled: disabled, icon: icon, id: id }));
|
|
69
69
|
};
|
|
70
70
|
export default ViewSelector;
|
|
@@ -20,7 +20,7 @@ import ViewSelector from './ViewSelector';
|
|
|
20
20
|
export const VisualizationPanel = React.memo(function VisualizationPanel({ dashboardItem, index, isMultiPanelView, handleClosePanel, navigateTo, dragHandleProps, getDashboardItemByType, }) {
|
|
21
21
|
const [actionButtons, setActionButtons] = useState(_jsx(_Fragment, {}));
|
|
22
22
|
const { flamegraphHint } = useParcaContext();
|
|
23
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full items-center justify-end gap-2 pb-2 min-h-[78px]", children: [_jsxs("div", { className: cx('flex w-full justify-between flex-col-reverse md:flex-row', isMultiPanelView && dashboardItem === 'icicle' ? 'items-end gap-x-2' : 'items-end'), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: cx(isMultiPanelView ? '' : 'hidden', 'flex items-center'), ...dragHandleProps, children: _jsx(Icon, { className: "text-xl", icon: "material-symbols:drag-indicator" }) }), _jsx("div", { className: "flex gap-2", children: actionButtons })] }), _jsxs("div", { className: cx('flex flex-row items-center gap-4', isMultiPanelView && dashboardItem === 'icicle' && 'pb-[10px]'), children: [_jsx(ViewSelector, { defaultValue: dashboardItem, navigateTo: navigateTo, position: index }), dashboardItem === 'icicle' && flamegraphHint != null ? (_jsx("div", { className: "px-2", children: flamegraphHint })) : null] })] }), isMultiPanelView && (_jsx(IconButton, { className: "py-0", onClick: () => handleClosePanel(dashboardItem), icon: _jsx(CloseIcon, {}) }))] }), getDashboardItemByType({
|
|
23
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full items-center justify-end gap-2 pb-2 min-h-[78px]", children: [_jsxs("div", { className: cx('flex w-full justify-between flex-col-reverse md:flex-row', isMultiPanelView && dashboardItem === 'icicle' ? 'items-end gap-x-2' : 'items-end'), children: [_jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: cx(isMultiPanelView ? '' : 'hidden', 'flex items-center'), ...dragHandleProps, children: _jsx(Icon, { className: "text-xl", icon: "material-symbols:drag-indicator" }) }), _jsx("div", { className: "flex gap-2", children: actionButtons })] }), _jsxs("div", { className: cx('flex flex-row items-center gap-4', isMultiPanelView && dashboardItem === 'icicle' && 'pb-[10px]'), children: [_jsx(ViewSelector, { id: "h-switch-viz", defaultValue: dashboardItem, navigateTo: navigateTo, position: index }), dashboardItem === 'icicle' && flamegraphHint != null ? (_jsx("div", { className: "px-2", children: flamegraphHint })) : null] })] }), isMultiPanelView && (_jsx(IconButton, { className: "py-0", onClick: () => handleClosePanel(dashboardItem), icon: _jsx(CloseIcon, {}) }))] }), getDashboardItemByType({
|
|
24
24
|
type: dashboardItem,
|
|
25
25
|
isHalfScreen: isMultiPanelView,
|
|
26
26
|
setActionButtons,
|
|
@@ -156,10 +156,10 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
|
|
|
156
156
|
'items-center': hasProfileSource,
|
|
157
157
|
}), children: [_jsxs("div", { children: [hasProfileSource && (_jsxs("div", { className: "max-w-[300px]", children: [_jsx("div", { className: "text-sm font-medium capitalize", children: headerParts.length > 0 ? headerParts[0].replace(/"/g, '') : '' }), _jsx("div", { className: "text-xs", children: headerParts.length > 1
|
|
158
158
|
? headerParts[headerParts.length - 1].replace(/"/g, '')
|
|
159
|
-
: '' })] })), profileViewExternalMainActions != null ? profileViewExternalMainActions : null] }), _jsxs("div", { className: "lg:flex flex-wrap items-center gap-2 md:justify-end hidden", children: [_jsx(FilterByFunctionButton, { navigateTo: navigateTo }), profileViewExternalSubActions != null ? profileViewExternalSubActions : null, _jsx(UserPreferences, { customButton: _jsxs(Button, { className: "gap-2", variant: "neutral", id: "h-viz-preferences", children: ["Preferences", _jsx(Icon, { icon: "pajamas:preferences", width: 20 })] }) }), profileSource !== undefined && queryClient !== undefined ? (_jsx(ProfileShareButton, { queryRequest: profileSource.QueryRequest(), queryClient: queryClient })) : null, _jsxs(Button, {
|
|
159
|
+
: '' })] })), profileViewExternalMainActions != null ? profileViewExternalMainActions : null] }), _jsxs("div", { className: "lg:flex flex-wrap items-center gap-2 md:justify-end hidden", children: [_jsx(FilterByFunctionButton, { navigateTo: navigateTo }), profileViewExternalSubActions != null ? profileViewExternalSubActions : null, _jsx(UserPreferences, { customButton: _jsxs(Button, { className: "gap-2", variant: "neutral", id: "h-viz-preferences", children: ["Preferences", _jsx(Icon, { icon: "pajamas:preferences", width: 20 })] }) }), profileSource !== undefined && queryClient !== undefined ? (_jsx(ProfileShareButton, { queryRequest: profileSource.QueryRequest(), queryClient: queryClient })) : null, _jsxs(Button, { className: "gap-2", variant: "neutral", onClick: e => {
|
|
160
160
|
e.preventDefault();
|
|
161
161
|
onDownloadPProf();
|
|
162
|
-
}, disabled: pprofDownloading, children: [pprofDownloading != null && pprofDownloading ? 'Downloading...' : 'Download pprof', _jsx(Icon, { icon: "material-symbols:download", width: 20 })] }), _jsx(ViewSelector, { defaultValue: "", navigateTo: navigateTo, position: -1, placeholderText: "Add panel", icon: _jsx(Icon, { icon: "material-symbols:add", width: 20 }), addView: true, disabled: isMultiPanelView || dashboardItems.length < 1 })] })] }), _jsx("div", { className: "w-full", ref: ref, children: _jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: "droppable", direction: "horizontal", children: provided => (_jsx("div", { ref: provided.innerRef, className: cx('grid w-full gap-2', isMultiPanelView ? 'grid-cols-2' : 'grid-cols-1'), ...provided.droppableProps, children: dashboardItems.map((dashboardItem, index) => {
|
|
162
|
+
}, disabled: pprofDownloading, id: "h-download-pprof", children: [pprofDownloading != null && pprofDownloading ? 'Downloading...' : 'Download pprof', _jsx(Icon, { icon: "material-symbols:download", width: 20 })] }), _jsx(ViewSelector, { defaultValue: "", navigateTo: navigateTo, position: -1, placeholderText: "Add panel", icon: _jsx(Icon, { icon: "material-symbols:add", width: 20 }), addView: true, disabled: isMultiPanelView || dashboardItems.length < 1, id: "h-add-panel" })] })] }), _jsx("div", { className: "w-full", ref: ref, children: _jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: "droppable", direction: "horizontal", children: provided => (_jsx("div", { ref: provided.innerRef, className: cx('grid w-full gap-2', isMultiPanelView ? 'grid-cols-2' : 'grid-cols-1'), ...provided.droppableProps, children: dashboardItems.map((dashboardItem, index) => {
|
|
163
163
|
return (_jsx(Draggable, { draggableId: dashboardItem, index: index, isDragDisabled: !isMultiPanelView, children: (provided, snapshot) => (_createElement("div", { ref: provided.innerRef, ...provided.draggableProps, key: dashboardItem, className: cx('w-full rounded p-2 shadow dark:border dark:border-gray-700 dark:bg-gray-700 min-h-96', snapshot.isDragging
|
|
164
164
|
? 'bg-gray-200 dark:bg-gray-500'
|
|
165
165
|
: 'bg-white dark:bg-gray-700') },
|
|
@@ -87,7 +87,18 @@ export const ProfileViewWithData = ({ queryClient, profileSource, navigateTo, })
|
|
|
87
87
|
sourceResponse,
|
|
88
88
|
perf,
|
|
89
89
|
]);
|
|
90
|
-
|
|
90
|
+
// Default to the unit of the selected profile type. This is a heuristic, and
|
|
91
|
+
// only a fallback, the unit should be returned by the backend.
|
|
92
|
+
let sampleUnit = profileSource.ProfileType().sampleUnit;
|
|
93
|
+
if (flamegraphResponse?.report.oneofKind === 'flamegraphArrow') {
|
|
94
|
+
sampleUnit = flamegraphResponse.report.flamegraphArrow.unit;
|
|
95
|
+
}
|
|
96
|
+
if (tableResponse?.report.oneofKind === 'tableArrow') {
|
|
97
|
+
sampleUnit = tableResponse.report.tableArrow.unit;
|
|
98
|
+
}
|
|
99
|
+
if (sourceResponse?.report.oneofKind === 'source') {
|
|
100
|
+
sampleUnit = sourceResponse.report.source.unit;
|
|
101
|
+
}
|
|
91
102
|
const downloadPProfClick = async () => {
|
|
92
103
|
if (profileSource == null || queryClient == null) {
|
|
93
104
|
return;
|
|
@@ -45,6 +45,6 @@ const DiffLegend = () => {
|
|
|
45
45
|
const handleMouseLeave = () => {
|
|
46
46
|
setShowLegendTooltip(false);
|
|
47
47
|
};
|
|
48
|
-
return (_jsxs("div", { className: "mt-1 mb-2 hidden md:block", children: [_jsxs("div", { ref: setReferenceElement, className: "flex items-center justify-center", children: [_jsx("span", { children: "Better" }), _jsx(DiffLegendBar, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }), _jsx("span", { children: "Worse" })] }), _jsx(Popover, { className: "relative", children: () => (_jsx(Transition, { show: showLegendTooltip, as: Fragment, enter: "transition ease-out duration-200", enterFrom: "opacity-0 translate-y-1", enterTo: "opacity-100 translate-y-0", leave: "transition ease-in duration-150", leaveFrom: "opacity-100 translate-y-0", leaveTo: "opacity-0 translate-y-1", children: _jsx(Popover.Panel, { ref: setPopperElement, style: styles.popper, ...attributes.popper, children: _jsx("div", { className: "overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5", children: _jsxs("div", { className: "bg-gray-50 p-4 dark:bg-gray-800", children: [_jsx("div", { className: "flex items-center justify-center" }), _jsx("span", { className: "block text-sm text-gray-500 dark:text-gray-50", children: "This is a differential icicle graph, where a purple-colored node means unchanged, and the darker the red, the worse the node got, and the darker the green, the better the node got." })] }) }) }) })) })] }));
|
|
48
|
+
return (_jsxs("div", { className: "mt-1 mb-2 hidden md:block", id: "h-diff-legend", children: [_jsxs("div", { ref: setReferenceElement, className: "flex items-center justify-center", children: [_jsx("span", { children: "Better" }), _jsx(DiffLegendBar, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }), _jsx("span", { children: "Worse" })] }), _jsx(Popover, { className: "relative", children: () => (_jsx(Transition, { show: showLegendTooltip, as: Fragment, enter: "transition ease-out duration-200", enterFrom: "opacity-0 translate-y-1", enterTo: "opacity-100 translate-y-0", leave: "transition ease-in duration-150", leaveFrom: "opacity-100 translate-y-0", leaveTo: "opacity-0 translate-y-1", children: _jsx(Popover.Panel, { ref: setPopperElement, style: styles.popper, ...attributes.popper, children: _jsx("div", { className: "overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5", children: _jsxs("div", { className: "bg-gray-50 p-4 dark:bg-gray-800", children: [_jsx("div", { className: "flex items-center justify-center" }), _jsx("span", { className: "block text-sm text-gray-500 dark:text-gray-50", children: "This is a differential icicle graph, where a purple-colored node means unchanged, and the darker the red, the worse the node got, and the darker the green, the better the node got." })] }) }) }) })) })] }));
|
|
49
49
|
};
|
|
50
50
|
export default DiffLegend;
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.343",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@parca/client": "^0.16.
|
|
7
|
-
"@parca/components": "^0.16.
|
|
6
|
+
"@parca/client": "^0.16.103",
|
|
7
|
+
"@parca/components": "^0.16.253",
|
|
8
8
|
"@parca/dynamicsize": "^0.16.60",
|
|
9
|
-
"@parca/hooks": "^0.0.
|
|
9
|
+
"@parca/hooks": "^0.0.41",
|
|
10
10
|
"@parca/parser": "^0.16.68",
|
|
11
|
-
"@parca/store": "^0.16.
|
|
12
|
-
"@parca/utilities": "^0.0.
|
|
11
|
+
"@parca/store": "^0.16.129",
|
|
12
|
+
"@parca/utilities": "^0.0.57",
|
|
13
13
|
"@tanstack/react-query": "^4.0.5",
|
|
14
14
|
"@types/react-beautiful-dnd": "^13.1.3",
|
|
15
15
|
"apache-arrow": "^12.0.0",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"access": "public",
|
|
52
52
|
"registry": "https://registry.npmjs.org/"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "c5b8204dc83fb3f120c2536fa15de23171cb04b9"
|
|
55
55
|
}
|
|
@@ -117,17 +117,30 @@ const MetricsTooltip = ({
|
|
|
117
117
|
<span className="my-2 block text-gray-700 dark:text-gray-300">
|
|
118
118
|
<table className="table-auto">
|
|
119
119
|
<tbody>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
{delta ? (
|
|
121
|
+
<>
|
|
122
|
+
<tr>
|
|
123
|
+
<td className="w-1/4">Per Second</td>
|
|
124
|
+
<td className="w-3/4">
|
|
125
|
+
{valueFormatter(
|
|
126
|
+
highlighted.valuePerSecond,
|
|
127
|
+
sampleUnit === 'nanoseconds' ? 'CPU Cores' : sampleUnit,
|
|
128
|
+
5
|
|
129
|
+
)}
|
|
130
|
+
</td>
|
|
131
|
+
</tr>
|
|
132
|
+
<tr>
|
|
133
|
+
<td className="w-1/4">Total</td>
|
|
134
|
+
<td className="w-3/4">
|
|
135
|
+
{valueFormatter(highlighted.value, sampleUnit, 2)}
|
|
136
|
+
</td>
|
|
137
|
+
</tr>
|
|
138
|
+
</>
|
|
139
|
+
) : (
|
|
127
140
|
<tr>
|
|
128
|
-
<td className="w-1/4">
|
|
141
|
+
<td className="w-1/4">Value</td>
|
|
129
142
|
<td className="w-3/4">
|
|
130
|
-
{valueFormatter(highlighted.
|
|
143
|
+
{valueFormatter(highlighted.valuePerSecond, sampleUnit, 5)}
|
|
131
144
|
</td>
|
|
132
145
|
</tr>
|
|
133
146
|
)}
|
|
@@ -392,9 +392,11 @@ export const RawMetricsGraph = ({
|
|
|
392
392
|
const isDeltaType = profile !== null ? profile?.query.profType.delta : false;
|
|
393
393
|
|
|
394
394
|
let yAxisLabel = sampleUnit;
|
|
395
|
+
let yAxisUnit = sampleUnit;
|
|
395
396
|
if (isDeltaType) {
|
|
396
|
-
if (
|
|
397
|
+
if (sampleUnit === 'nanoseconds') {
|
|
397
398
|
yAxisLabel = 'CPU Cores';
|
|
399
|
+
yAxisUnit = '';
|
|
398
400
|
}
|
|
399
401
|
if (sampleUnit === 'bytes') {
|
|
400
402
|
yAxisLabel = 'Bytes per Second';
|
|
@@ -477,7 +479,7 @@ export const RawMetricsGraph = ({
|
|
|
477
479
|
>
|
|
478
480
|
<line className="stroke-gray-300 dark:stroke-gray-500" x2={-6} />
|
|
479
481
|
<text fill="currentColor" x={-9} dy={'0.32em'}>
|
|
480
|
-
{valueFormatter(d,
|
|
482
|
+
{valueFormatter(d, yAxisUnit, decimals)}
|
|
481
483
|
</text>
|
|
482
484
|
</g>
|
|
483
485
|
<g key={`grid-${i}`}>
|
|
@@ -72,11 +72,8 @@ const GroupByDropdown = ({
|
|
|
72
72
|
return (
|
|
73
73
|
<div className="relative">
|
|
74
74
|
<label className="text-sm">Group</label>
|
|
75
|
-
<Menu as="div" className="relative text-left">
|
|
76
|
-
<Menu.Button
|
|
77
|
-
id="h-group-by-filter"
|
|
78
|
-
className="relative w-max cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] 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"
|
|
79
|
-
>
|
|
75
|
+
<Menu as="div" className="relative text-left" id="h-group-by-filter">
|
|
76
|
+
<Menu.Button className="relative w-max cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] 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">
|
|
80
77
|
<span className="block overflow-x-hidden text-ellipsis">{label}</span>
|
|
81
78
|
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 text-gray-400">
|
|
82
79
|
<Icon icon="heroicons:chevron-down-20-solid" aria-hidden="true" />
|
|
@@ -69,12 +69,9 @@ const RuntimeFilterDropdown = ({
|
|
|
69
69
|
return (
|
|
70
70
|
<div>
|
|
71
71
|
<label className="text-sm">Runtimes</label>
|
|
72
|
-
<Menu as="div" className="relative text-left">
|
|
72
|
+
<Menu as="div" className="relative text-left" id="h-runtimes-filter">
|
|
73
73
|
<div>
|
|
74
|
-
<Menu.Button
|
|
75
|
-
className="relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] 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"
|
|
76
|
-
id="h-runtimes-filter"
|
|
77
|
-
>
|
|
74
|
+
<Menu.Button className="relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] 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">
|
|
78
75
|
<span className="block overflow-x-hidden text-ellipsis">Runtimes</span>
|
|
79
76
|
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400">
|
|
80
77
|
<Icon icon="heroicons:chevron-down-20-solid" aria-hidden="true" />
|
|
@@ -28,7 +28,6 @@ const SortBySelect = ({
|
|
|
28
28
|
<div>
|
|
29
29
|
<label className="text-sm">Sort</label>
|
|
30
30
|
<Select
|
|
31
|
-
id="h-sort-by-filter"
|
|
32
31
|
className="!px-3"
|
|
33
32
|
items={[
|
|
34
33
|
{
|
|
@@ -73,6 +72,7 @@ const SortBySelect = ({
|
|
|
73
72
|
placeholder={'Sort By'}
|
|
74
73
|
primary={false}
|
|
75
74
|
disabled={false}
|
|
75
|
+
id="h-sort-by-filter"
|
|
76
76
|
/>
|
|
77
77
|
</div>
|
|
78
78
|
);
|
|
@@ -325,7 +325,7 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
|
|
|
325
325
|
transition={{duration: 0.5}}
|
|
326
326
|
>
|
|
327
327
|
{compareMode ? <DiffLegend /> : null}
|
|
328
|
-
<div className="min-h-48">
|
|
328
|
+
<div className="min-h-48" id="h-icicle-graph">
|
|
329
329
|
{graph !== undefined && (
|
|
330
330
|
<IcicleGraph
|
|
331
331
|
width={width}
|
|
@@ -172,6 +172,14 @@ const ProfileMetricsGraph = ({
|
|
|
172
172
|
onPointClick(timestamp, labels, queryExpression);
|
|
173
173
|
};
|
|
174
174
|
|
|
175
|
+
let sampleUnit = '';
|
|
176
|
+
if (series.every((val, i, arr) => val?.sampleType?.unit === arr[0]?.sampleType?.unit)) {
|
|
177
|
+
sampleUnit = series[0]?.sampleType?.unit ?? '';
|
|
178
|
+
}
|
|
179
|
+
if (sampleUnit === '') {
|
|
180
|
+
sampleUnit = Query.parse(queryExpression).profileType().sampleUnit;
|
|
181
|
+
}
|
|
182
|
+
|
|
175
183
|
return (
|
|
176
184
|
<AnimatePresence>
|
|
177
185
|
<motion.div
|
|
@@ -189,7 +197,7 @@ const ProfileMetricsGraph = ({
|
|
|
189
197
|
setTimeRange={setTimeRange}
|
|
190
198
|
onSampleClick={handleSampleClick}
|
|
191
199
|
addLabelMatcher={addLabelMatcher}
|
|
192
|
-
sampleUnit={
|
|
200
|
+
sampleUnit={sampleUnit}
|
|
193
201
|
height={height}
|
|
194
202
|
width={width}
|
|
195
203
|
margin={margin}
|
|
@@ -42,13 +42,13 @@ const FilterByFunctionButton = ({
|
|
|
42
42
|
return (
|
|
43
43
|
<Input
|
|
44
44
|
placeholder="Filter by function"
|
|
45
|
-
id="h-filter-by-function"
|
|
46
45
|
className="text-sm"
|
|
47
46
|
onAction={onAction}
|
|
48
47
|
onChange={e => setLocalValue(e.target.value)}
|
|
49
48
|
value={localValue ?? ''}
|
|
50
49
|
onBlur={() => setLocalValue(storeValue as string)}
|
|
51
50
|
actionIcon={isClearAction ? <Icon icon="ep:circle-close" /> : <Icon icon="ep:arrow-right" />}
|
|
51
|
+
id="h-filter-by-function"
|
|
52
52
|
/>
|
|
53
53
|
);
|
|
54
54
|
};
|
|
@@ -24,6 +24,7 @@ interface Props {
|
|
|
24
24
|
addView?: boolean;
|
|
25
25
|
disabled?: boolean;
|
|
26
26
|
icon?: JSX.Element;
|
|
27
|
+
id?: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const ViewSelector = ({
|
|
@@ -35,6 +36,7 @@ const ViewSelector = ({
|
|
|
35
36
|
addView = false,
|
|
36
37
|
disabled = false,
|
|
37
38
|
icon,
|
|
39
|
+
id,
|
|
38
40
|
}: Props): JSX.Element => {
|
|
39
41
|
const [callgraphEnabled] = useUIFeatureFlag('callgraph');
|
|
40
42
|
const [dashboardItems = ['icicle'], setDashboardItems] = useURLState({
|
|
@@ -110,6 +112,7 @@ const ViewSelector = ({
|
|
|
110
112
|
|
|
111
113
|
return (
|
|
112
114
|
<Select
|
|
115
|
+
className="h-view-selector"
|
|
113
116
|
items={items}
|
|
114
117
|
selectedKey={defaultValue}
|
|
115
118
|
onSelection={onSelection}
|
|
@@ -117,6 +120,7 @@ const ViewSelector = ({
|
|
|
117
120
|
primary={primary}
|
|
118
121
|
disabled={disabled}
|
|
119
122
|
icon={icon}
|
|
123
|
+
id={id}
|
|
120
124
|
/>
|
|
121
125
|
);
|
|
122
126
|
};
|
|
@@ -73,7 +73,12 @@ export const VisualizationPanel = React.memo(function VisualizationPanel({
|
|
|
73
73
|
isMultiPanelView && dashboardItem === 'icicle' && 'pb-[10px]'
|
|
74
74
|
)}
|
|
75
75
|
>
|
|
76
|
-
<ViewSelector
|
|
76
|
+
<ViewSelector
|
|
77
|
+
id="h-switch-viz"
|
|
78
|
+
defaultValue={dashboardItem}
|
|
79
|
+
navigateTo={navigateTo}
|
|
80
|
+
position={index}
|
|
81
|
+
/>
|
|
77
82
|
|
|
78
83
|
{dashboardItem === 'icicle' && flamegraphHint != null ? (
|
|
79
84
|
<div className="px-2">{flamegraphHint}</div>
|
|
@@ -383,7 +383,6 @@ export const ProfileView = ({
|
|
|
383
383
|
/>
|
|
384
384
|
) : null}
|
|
385
385
|
<Button
|
|
386
|
-
id="h-download-pprof"
|
|
387
386
|
className="gap-2"
|
|
388
387
|
variant="neutral"
|
|
389
388
|
onClick={e => {
|
|
@@ -391,6 +390,7 @@ export const ProfileView = ({
|
|
|
391
390
|
onDownloadPProf();
|
|
392
391
|
}}
|
|
393
392
|
disabled={pprofDownloading}
|
|
393
|
+
id="h-download-pprof"
|
|
394
394
|
>
|
|
395
395
|
{pprofDownloading != null && pprofDownloading ? 'Downloading...' : 'Download pprof'}
|
|
396
396
|
<Icon icon="material-symbols:download" width={20} />
|
|
@@ -403,6 +403,7 @@ export const ProfileView = ({
|
|
|
403
403
|
icon={<Icon icon="material-symbols:add" width={20} />}
|
|
404
404
|
addView={true}
|
|
405
405
|
disabled={isMultiPanelView || dashboardItems.length < 1}
|
|
406
|
+
id="h-add-panel"
|
|
406
407
|
/>
|
|
407
408
|
</div>
|
|
408
409
|
</div>
|
|
@@ -135,7 +135,18 @@ export const ProfileViewWithData = ({
|
|
|
135
135
|
perf,
|
|
136
136
|
]);
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
// Default to the unit of the selected profile type. This is a heuristic, and
|
|
139
|
+
// only a fallback, the unit should be returned by the backend.
|
|
140
|
+
let sampleUnit = profileSource.ProfileType().sampleUnit;
|
|
141
|
+
if (flamegraphResponse?.report.oneofKind === 'flamegraphArrow') {
|
|
142
|
+
sampleUnit = flamegraphResponse.report.flamegraphArrow.unit;
|
|
143
|
+
}
|
|
144
|
+
if (tableResponse?.report.oneofKind === 'tableArrow') {
|
|
145
|
+
sampleUnit = tableResponse.report.tableArrow.unit;
|
|
146
|
+
}
|
|
147
|
+
if (sourceResponse?.report.oneofKind === 'source') {
|
|
148
|
+
sampleUnit = sourceResponse.report.source.unit;
|
|
149
|
+
}
|
|
139
150
|
|
|
140
151
|
const downloadPProfClick = async (): Promise<void> => {
|
|
141
152
|
if (profileSource == null || queryClient == null) {
|
|
@@ -74,7 +74,7 @@ const DiffLegend = (): JSX.Element => {
|
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
return (
|
|
77
|
-
<div className="mt-1 mb-2 hidden md:block">
|
|
77
|
+
<div className="mt-1 mb-2 hidden md:block" id="h-diff-legend">
|
|
78
78
|
<div ref={setReferenceElement} className="flex items-center justify-center">
|
|
79
79
|
<span>Better</span>
|
|
80
80
|
<DiffLegendBar onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} />
|