@parca/profile 0.16.465 → 0.16.467

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.
Files changed (27) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +26 -0
  3. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +1 -0
  4. package/dist/MetricsGraph/UtilizationMetrics/index.js +211 -0
  5. package/dist/MetricsGraph/index.d.ts +5 -0
  6. package/dist/MetricsGraph/index.d.ts.map +1 -1
  7. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.js +2 -2
  8. package/dist/ProfileMetricsGraph/hooks/useQueryRange.d.ts +10 -0
  9. package/dist/ProfileMetricsGraph/hooks/useQueryRange.d.ts.map +1 -0
  10. package/dist/ProfileMetricsGraph/hooks/useQueryRange.js +64 -0
  11. package/dist/ProfileMetricsGraph/index.d.ts +1 -8
  12. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  13. package/dist/ProfileMetricsGraph/index.js +4 -52
  14. package/dist/ProfileSelector/MetricsGraphSection.d.ts +4 -2
  15. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  16. package/dist/ProfileSelector/MetricsGraphSection.js +6 -5
  17. package/dist/ProfileSelector/index.d.ts +13 -1
  18. package/dist/ProfileSelector/index.d.ts.map +1 -1
  19. package/dist/ProfileSelector/index.js +2 -2
  20. package/package.json +2 -2
  21. package/src/MetricsGraph/UtilizationMetrics/index.tsx +512 -0
  22. package/src/MetricsGraph/index.tsx +2 -1
  23. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.tsx +2 -2
  24. package/src/ProfileMetricsGraph/hooks/useQueryRange.ts +94 -0
  25. package/src/ProfileMetricsGraph/index.tsx +6 -84
  26. package/src/ProfileSelector/MetricsGraphSection.tsx +46 -26
  27. package/src/ProfileSelector/index.tsx +17 -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.467](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.466...@parca/profile@0.16.467) (2025-01-22)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.466](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.465...@parca/profile@0.16.466) (2025-01-21)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## 0.16.465 (2025-01-21)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -0,0 +1,26 @@
1
+ import { DateTimeRange } from '@parca/components';
2
+ interface MetricSeries {
3
+ timestamp: number;
4
+ value: number;
5
+ resource: {
6
+ [key: string]: string;
7
+ };
8
+ attributes: {
9
+ [key: string]: string;
10
+ };
11
+ }
12
+ interface Props {
13
+ data: MetricSeries[];
14
+ addLabelMatcher: (labels: {
15
+ key: string;
16
+ value: string;
17
+ } | Array<{
18
+ key: string;
19
+ value: string;
20
+ }>) => void;
21
+ setTimeRange: (range: DateTimeRange) => void;
22
+ utilizationMetricsLoading?: boolean;
23
+ }
24
+ declare const UtilizationMetrics: ({ data, addLabelMatcher, setTimeRange, utilizationMetricsLoading, }: Props) => JSX.Element;
25
+ export default UtilizationMetrics;
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/MetricsGraph/UtilizationMetrics/index.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAC,aAAa,EAAwC,MAAM,mBAAmB,CAAC;AAUvF,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,UAAU,EAAE;QACV,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;CACH;AAaD,UAAU,KAAK;IACb,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAgaD,QAAA,MAAM,kBAAkB,wEAKrB,KAAK,KAAG,GAAG,CAAC,OA4Bd,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,211 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ import { Fragment, useCallback, useId, useMemo, useRef, useState } from 'react';
15
+ import * as d3 from 'd3';
16
+ import { pointer } from 'd3-selection';
17
+ import { AnimatePresence, motion } from 'framer-motion';
18
+ import throttle from 'lodash.throttle';
19
+ import { useContextMenu } from 'react-contexify';
20
+ import { DateTimeRange, MetricsGraphSkeleton, useParcaContext } from '@parca/components';
21
+ import { formatDate, formatForTimespan, getPrecision, valueFormatter } from '@parca/utilities';
22
+ import MetricsCircle from '../../MetricsCircle';
23
+ import MetricsSeries from '../../MetricsSeries';
24
+ import MetricsContextMenu from '../MetricsContextMenu';
25
+ import MetricsTooltip from '../MetricsTooltip';
26
+ import { useMetricsGraphDimensions } from '../useMetricsGraphDimensions';
27
+ function transformToSeries(data) {
28
+ const groupedData = data.reduce((acc, series) => {
29
+ const resourceKey = Object.entries(series.resource)
30
+ .map(([name, value]) => `${name}=${value}`)
31
+ .join(',');
32
+ if (!Object.hasOwn(acc, resourceKey)) {
33
+ acc[resourceKey] = {
34
+ metric: Object.entries(series.resource).map(([name, value]) => ({ name, value })),
35
+ values: [],
36
+ labelset: resourceKey,
37
+ };
38
+ }
39
+ acc[resourceKey].values.push([series.timestamp, series.value, 0, 0]);
40
+ return acc;
41
+ }, {});
42
+ // Sort values by timestamp for each series
43
+ return Object.values(groupedData).map(series => ({
44
+ ...series,
45
+ values: series.values.sort((a, b) => a[0] - b[0]),
46
+ }));
47
+ }
48
+ const RawUtilizationMetrics = ({ data, addLabelMatcher, setTimeRange, width, height, margin, }) => {
49
+ const { timezone } = useParcaContext();
50
+ const graph = useRef(null);
51
+ const [dragging, setDragging] = useState(false);
52
+ const [hovering, setHovering] = useState(false);
53
+ const [relPos, setRelPos] = useState(-1);
54
+ const [pos, setPos] = useState([0, 0]);
55
+ const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
56
+ const metricPointRef = useRef(null);
57
+ const idForContextMenu = useId();
58
+ const lineStroke = '1px';
59
+ const lineStrokeHover = '2px';
60
+ const graphWidth = width - margin * 1.5 - margin / 2;
61
+ // Calculate the time range from the data
62
+ const timeExtent = d3.extent(data, d => d.timestamp);
63
+ const from = timeExtent[0] ?? 0;
64
+ const to = timeExtent[1] ?? 0;
65
+ // Add a small padding (2%) to the time range to avoid points touching the edges
66
+ const timeRange = to - from;
67
+ const paddedFrom = from - timeRange * 0.02;
68
+ const paddedTo = to + timeRange * 0.02;
69
+ const series = transformToSeries(data);
70
+ const extentsY = series.map(function (s) {
71
+ return d3.extent(s.values, function (d) {
72
+ return d[1];
73
+ });
74
+ });
75
+ const minY = d3.min(extentsY, function (d) {
76
+ return d[0];
77
+ });
78
+ const maxY = d3.max(extentsY, function (d) {
79
+ return d[1];
80
+ });
81
+ // Setup scales with padded time range
82
+ const xScale = d3.scaleUtc().domain([paddedFrom, paddedTo]).range([0, graphWidth]);
83
+ const yScale = d3
84
+ .scaleLinear()
85
+ // tslint:disable-next-line
86
+ .domain([minY, maxY])
87
+ .range([height - margin, 0])
88
+ .nice();
89
+ const throttledSetPos = throttle(setPos, 20);
90
+ const onMouseMove = (e) => {
91
+ if (isContextMenuOpen) {
92
+ return;
93
+ }
94
+ // X/Y coordinate array relative to svg
95
+ const rel = pointer(e);
96
+ const xCoordinate = rel[0];
97
+ const xCoordinateWithoutMargin = xCoordinate - margin;
98
+ const yCoordinate = rel[1];
99
+ const yCoordinateWithoutMargin = yCoordinate - margin;
100
+ throttledSetPos([xCoordinateWithoutMargin, yCoordinateWithoutMargin]);
101
+ };
102
+ const trackVisibility = (isVisible) => {
103
+ setIsContextMenuOpen(isVisible);
104
+ };
105
+ const MENU_ID = `utilizationmetrics-context-menu-${idForContextMenu}`;
106
+ const { show } = useContextMenu({
107
+ id: MENU_ID,
108
+ });
109
+ const displayMenu = useCallback((e) => {
110
+ show({
111
+ event: e,
112
+ });
113
+ }, [show]);
114
+ const color = d3.scaleOrdinal(d3.schemeCategory10);
115
+ const l = d3.line(d => xScale(d[0]), d => yScale(d[1]));
116
+ const highlighted = useMemo(() => {
117
+ // Return the closest point as the highlighted point
118
+ const closestPointPerSeries = series.map(function (s) {
119
+ const distances = s.values.map(d => {
120
+ const x = xScale(d[0]) + margin / 2;
121
+ const y = yScale(d[1]) - margin / 3;
122
+ return Math.sqrt(Math.pow(pos[0] - x, 2) + Math.pow(pos[1] - y, 2));
123
+ });
124
+ const pointIndex = d3.minIndex(distances);
125
+ const minDistance = distances[pointIndex];
126
+ return {
127
+ pointIndex,
128
+ distance: minDistance,
129
+ };
130
+ });
131
+ const closestSeriesIndex = d3.minIndex(closestPointPerSeries, s => s.distance);
132
+ const pointIndex = closestPointPerSeries[closestSeriesIndex].pointIndex;
133
+ const point = series[closestSeriesIndex].values[pointIndex];
134
+ return {
135
+ seriesIndex: closestSeriesIndex,
136
+ labels: series[closestSeriesIndex].metric,
137
+ timestamp: point[0],
138
+ valuePerSecond: point[1],
139
+ value: point[2],
140
+ duration: point[3],
141
+ x: xScale(point[0]),
142
+ y: yScale(point[1]),
143
+ };
144
+ }, [pos, series, xScale, yScale, margin]);
145
+ const onMouseDown = (e) => {
146
+ // only left mouse button
147
+ if (e.button !== 0) {
148
+ return;
149
+ }
150
+ // X/Y coordinate array relative to svg
151
+ const rel = pointer(e);
152
+ const xCoordinate = rel[0];
153
+ const xCoordinateWithoutMargin = xCoordinate - margin;
154
+ if (xCoordinateWithoutMargin >= 0) {
155
+ setRelPos(xCoordinateWithoutMargin);
156
+ setDragging(true);
157
+ }
158
+ e.stopPropagation();
159
+ e.preventDefault();
160
+ };
161
+ const onMouseUp = (e) => {
162
+ setDragging(false);
163
+ if (relPos === -1) {
164
+ // MouseDown happened outside of this element.
165
+ return;
166
+ }
167
+ // This is a normal click. We tolerate tiny movements to still be a
168
+ // click as they can occur when clicking based on user feedback.
169
+ if (Math.abs(relPos - pos[0]) <= 1) {
170
+ setRelPos(-1);
171
+ return;
172
+ }
173
+ let startPos = relPos;
174
+ let endPos = pos[0];
175
+ if (startPos > endPos) {
176
+ startPos = pos[0];
177
+ endPos = relPos;
178
+ }
179
+ const startCorrection = 10;
180
+ const endCorrection = 30;
181
+ const firstTime = xScale.invert(startPos - startCorrection).valueOf();
182
+ const secondTime = xScale.invert(endPos - endCorrection).valueOf();
183
+ setTimeRange(DateTimeRange.fromAbsoluteDates(firstTime, secondTime));
184
+ setRelPos(-1);
185
+ e.stopPropagation();
186
+ e.preventDefault();
187
+ };
188
+ return (_jsxs(_Fragment, { children: [_jsx(MetricsContextMenu, { onAddLabelMatcher: addLabelMatcher, menuId: MENU_ID, highlighted: highlighted, trackVisibility: trackVisibility }), highlighted != null && hovering && !dragging && pos[0] !== 0 && pos[1] !== 0 && (_jsx("div", { onMouseMove: onMouseMove, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false), children: !isContextMenuOpen && (_jsx(MetricsTooltip, { x: pos[0] + margin, y: pos[1] + margin, highlighted: highlighted, contextElement: graph.current, sampleUnit: "%", delta: false })) })), _jsx("div", { ref: graph, onMouseEnter: function () {
189
+ setHovering(true);
190
+ }, onMouseLeave: () => setHovering(false), onContextMenu: displayMenu, children: _jsxs("svg", { width: `${width}px`, height: `${height + margin}px`, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onMouseMove: onMouseMove, children: [_jsx("g", { transform: `translate(${margin}, 0)`, children: dragging && (_jsx("g", { className: "zoom-time-rect", children: _jsx("rect", { className: "bar", x: pos[0] - relPos < 0 ? pos[0] : relPos, y: 0, height: height, width: Math.abs(pos[0] - relPos), fill: 'rgba(0, 0, 0, 0.125)' }) })) }), _jsxs("g", { transform: `translate(${margin * 1.5}, ${margin / 1.5})`, children: [_jsxs("g", { className: "y axis", textAnchor: "end", fontSize: "10", fill: "none", children: [yScale.ticks(5).map((d, i, allTicks) => {
191
+ let decimals = 2;
192
+ const intervalBetweenTicks = allTicks[1] - allTicks[0];
193
+ if (intervalBetweenTicks < 1) {
194
+ const precision = getPrecision(intervalBetweenTicks);
195
+ decimals = precision;
196
+ }
197
+ return (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
198
+ /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
199
+ 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, 'percent', 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()}`));
200
+ }), _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: "Utilization" }) })] }), _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",
201
+ /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
202
+ 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), timezone) })] }, `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: 0, x2: graphWidth, 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
203
+ ? lineStrokeHover
204
+ : lineStroke, xScale: xScale, yScale: yScale }) }, i))) }), hovering && highlighted != null && (_jsx("g", { className: "circle-group", ref: metricPointRef, style: { fill: color(highlighted.seriesIndex.toString()) }, children: _jsx(MetricsCircle, { cx: highlighted.x, cy: highlighted.y }) }))] })] }) })] }));
205
+ };
206
+ const UtilizationMetrics = ({ data, addLabelMatcher, setTimeRange, utilizationMetricsLoading, }) => {
207
+ const { isDarkMode } = useParcaContext();
208
+ const { width, height, margin, heightStyle } = useMetricsGraphDimensions(false);
209
+ return (_jsx(AnimatePresence, { children: _jsx(motion.div, { className: "h-full w-full relative", initial: { display: 'none', opacity: 0 }, animate: { display: 'block', opacity: 1 }, transition: { duration: 0.5 }, children: utilizationMetricsLoading === true ? (_jsx(MetricsGraphSkeleton, { heightStyle: heightStyle, isDarkMode: isDarkMode })) : (_jsx(RawUtilizationMetrics, { data: data, addLabelMatcher: addLabelMatcher, setTimeRange: setTimeRange, width: width, height: height, margin: margin })) }, "utilization-metrics-graph-loaded") }));
210
+ };
211
+ export default UtilizationMetrics;
@@ -31,6 +31,11 @@ export interface HighlightedSeries {
31
31
  x: number;
32
32
  y: number;
33
33
  }
34
+ export interface Series {
35
+ metric: Label[];
36
+ values: number[][];
37
+ labelset: string;
38
+ }
34
39
  declare const MetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width, height, margin, sumBy, }: Props) => JSX.Element;
35
40
  export default MetricsGraph;
36
41
  export declare const parseValue: (value: string) => number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MetricsGraph/index.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAC,KAAK,EAAiB,aAAa,IAAI,eAAe,EAAC,MAAM,eAAe,CAAC;AACrF,OAAO,EAAC,aAAa,EAAkB,MAAM,mBAAmB,CAAC;AASjE,OAAO,EAAC,sBAAsB,EAAC,MAAM,IAAI,CAAC;AAO1C,UAAU,KAAK;IACb,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACvC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7F,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAQD,QAAA,MAAM,YAAY,yHAaf,KAAK,KAAG,GAAG,CAAC,OA0Bd,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAAM,GAAG,IAKnD,CAAC;AAKF,eAAO,MAAM,eAAe,yHAazB,KAAK,KAAG,GAAG,CAAC,OA4ed,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MetricsGraph/index.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAC,KAAK,EAAiB,aAAa,IAAI,eAAe,EAAC,MAAM,eAAe,CAAC;AACrF,OAAO,EAAC,aAAa,EAAkB,MAAM,mBAAmB,CAAC;AASjE,OAAO,EAAC,sBAAsB,EAAC,MAAM,IAAI,CAAC;AAO1C,UAAU,KAAK;IACb,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACvC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7F,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,QAAA,MAAM,YAAY,yHAaf,KAAK,KAAG,GAAG,CAAC,OA0Bd,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAAM,GAAG,IAKnD,CAAC;AAKF,eAAO,MAAM,eAAe,yHAazB,KAAK,KAAG,GAAG,CAAC,OA6ed,CAAC"}
@@ -36,8 +36,8 @@ export const IcicleChartRootNode = React.memo(function IcicleChartRootNodeNonMem
36
36
  }
37
37
  return (_jsx(_Fragment, { children: childRows.map(row => {
38
38
  const groupByFields = (groupByMetadata?.get(row)).toJSON();
39
- const tsStr = arrowToString(groupByFields.timestamp);
40
- const tsNanos = BigInt(parseInt(tsStr, 10)) * 1000000n;
39
+ const tsStr = arrowToString(groupByFields.time_nanos);
40
+ const tsNanos = BigInt(parseInt(tsStr, 10));
41
41
  const durationStr = arrowToString(groupByFields.duration);
42
42
  const duration = parseInt(durationStr, 10);
43
43
  const x = tsXScale(tsNanos);
@@ -0,0 +1,10 @@
1
+ import { RpcError } from '@protobuf-ts/runtime-rpc';
2
+ import { QueryRangeResponse, QueryServiceClient } from '@parca/client';
3
+ interface IQueryRangeState {
4
+ response: QueryRangeResponse | null;
5
+ isLoading: boolean;
6
+ error: RpcError | null;
7
+ }
8
+ export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number, sumBy: string[], skip?: boolean) => IQueryRangeState;
9
+ export {};
10
+ //# sourceMappingURL=useQueryRange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryRange.d.ts","sourceRoot":"","sources":["../../../src/ProfileMetricsGraph/hooks/useQueryRange.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAW,kBAAkB,EAAE,kBAAkB,EAAY,MAAM,eAAe,CAAC;AAM1F,UAAU,gBAAgB;IACxB,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CACxB;AAYD,eAAO,MAAM,aAAa,WAChB,kBAAkB,mBACT,MAAM,SAChB,MAAM,OACR,MAAM,SACJ,MAAM,EAAE,qBAEd,gBA+CF,CAAC"}
@@ -0,0 +1,64 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { useEffect, useMemo } from 'react';
14
+ import { Duration, Timestamp } from '@parca/client';
15
+ import { useGrpcMetadata, useURLState } from '@parca/components';
16
+ import { getStepDuration } from '@parca/utilities';
17
+ import useGrpcQuery from '../../useGrpcQuery';
18
+ const getStepCountFromScreenWidth = (pixelsPerPoint) => {
19
+ let width =
20
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
21
+ window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
22
+ // subtract the padding around the graph
23
+ width = width - (20 + 24 + 68) * 2;
24
+ return Math.round(width / pixelsPerPoint);
25
+ };
26
+ export const useQueryRange = (client, queryExpression, start, end, sumBy, skip = false) => {
27
+ const metadata = useGrpcMetadata();
28
+ const [stepCountStr, setStepCount] = useURLState('step_count');
29
+ const defaultStepCount = useMemo(() => {
30
+ return getStepCountFromScreenWidth(10);
31
+ }, []);
32
+ const stepCount = useMemo(() => {
33
+ if (stepCountStr != null) {
34
+ return parseInt(stepCountStr, 10);
35
+ }
36
+ return defaultStepCount;
37
+ }, [stepCountStr, defaultStepCount]);
38
+ useEffect(() => {
39
+ if (stepCountStr == null) {
40
+ setStepCount(defaultStepCount.toString());
41
+ }
42
+ }, [stepCountStr, defaultStepCount, setStepCount]);
43
+ const { data, isLoading, error } = useGrpcQuery({
44
+ key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
45
+ queryFn: async () => {
46
+ const stepDuration = getStepDuration(start, end, stepCount);
47
+ const { response } = await client.queryRange({
48
+ query: queryExpression,
49
+ start: Timestamp.fromDate(new Date(start)),
50
+ end: Timestamp.fromDate(new Date(end)),
51
+ step: Duration.create(stepDuration),
52
+ limit: 0,
53
+ sumBy,
54
+ }, { meta: metadata });
55
+ return response;
56
+ },
57
+ options: {
58
+ retry: false,
59
+ enabled: !skip,
60
+ staleTime: 1000 * 60 * 5, // 5 minutes
61
+ },
62
+ });
63
+ return { isLoading, error: error, response: data ?? null };
64
+ };
@@ -1,5 +1,4 @@
1
- import { RpcError } from '@protobuf-ts/runtime-rpc';
2
- import { Label, QueryRangeResponse, QueryServiceClient } from '@parca/client';
1
+ import { Label, QueryServiceClient } from '@parca/client';
3
2
  import { DateTimeRange } from '@parca/components';
4
3
  import { ProfileSelection } from '..';
5
4
  interface ProfileMetricsEmptyStateProps {
@@ -25,12 +24,6 @@ interface ProfileMetricsGraphProps {
25
24
  onPointClick: (timestamp: number, labels: Label[], queryExpression: string, duration: number) => void;
26
25
  comparing?: boolean;
27
26
  }
28
- export interface IQueryRangeState {
29
- response: QueryRangeResponse | null;
30
- isLoading: boolean;
31
- error: RpcError | null;
32
- }
33
- export declare const useQueryRange: (client: QueryServiceClient, queryExpression: string, start: number, end: number, sumBy: string[], skip?: boolean) => IQueryRangeState;
34
27
  declare const ProfileMetricsGraph: ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing, sumBy, sumByLoading, }: ProfileMetricsGraphProps) => JSX.Element;
35
28
  export default ProfileMetricsGraph;
36
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAW,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAY,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAK5D,UAAU,6BAA6B;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,eAAO,MAAM,wBAAwB,gBAAe,6BAA6B,KAAG,GAAG,CAAC,OAMvF,CAAC;AAEF,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,IAAI,CAAC;IACV,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CACxB;AAYD,eAAO,MAAM,aAAa,WAChB,kBAAkB,mBACT,MAAM,SAChB,MAAM,OACR,MAAM,SACJ,MAAM,EAAE,qBAEd,gBA+CF,CAAC;AAEF,QAAA,MAAM,mBAAmB,sIAYtB,wBAAwB,KAAG,GAAG,CAAC,OAsFjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileMetricsGraph/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,KAAK,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACxD,OAAO,EAAC,aAAa,EAAwC,MAAM,mBAAmB,CAAC;AAIvF,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAK5D,UAAU,6BAA6B;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,eAAO,MAAM,wBAAwB,gBAAe,6BAA6B,KAAG,GAAG,CAAC,OAMvF,CAAC;AAEF,UAAU,wBAAwB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CACf,MAAM,EAAE;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,KACvE,IAAI,CAAC;IACV,YAAY,EAAE,CACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,IAAI,CAAC;IACV,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,QAAA,MAAM,mBAAmB,sIAYtB,wBAAwB,KAAG,GAAG,CAAC,OAuFjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -11,68 +11,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
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 { useEffect, useMemo } from 'react';
14
+ import { useEffect } from 'react';
15
15
  import { AnimatePresence, motion } from 'framer-motion';
16
- import { Duration, Timestamp } from '@parca/client';
17
- import { MetricsGraphSkeleton, useGrpcMetadata, useParcaContext, useURLState, } from '@parca/components';
16
+ import { MetricsGraphSkeleton, useParcaContext } from '@parca/components';
18
17
  import { Query } from '@parca/parser';
19
- import { capitalizeOnlyFirstLetter, getStepDuration } from '@parca/utilities';
18
+ import { capitalizeOnlyFirstLetter } from '@parca/utilities';
20
19
  import MetricsGraph from '../MetricsGraph';
21
20
  import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
22
- import useGrpcQuery from '../useGrpcQuery';
21
+ import { useQueryRange } from './hooks/useQueryRange';
23
22
  const ErrorContent = ({ errorMessage }) => {
24
23
  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
24
  };
26
25
  export const ProfileMetricsEmptyState = ({ message }) => {
27
26
  return (_jsx("div", { className: "flex h-full w-full flex-col items-center justify-center", children: _jsx("p", { children: message }) }));
28
27
  };
29
- const getStepCountFromScreenWidth = (pixelsPerPoint) => {
30
- let width =
31
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
32
- window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
33
- // subtract the padding around the graph
34
- width = width - (20 + 24 + 68) * 2;
35
- return Math.round(width / pixelsPerPoint);
36
- };
37
- export const useQueryRange = (client, queryExpression, start, end, sumBy, skip = false) => {
38
- const metadata = useGrpcMetadata();
39
- const [stepCountStr, setStepCount] = useURLState('step_count');
40
- const defaultStepCount = useMemo(() => {
41
- return getStepCountFromScreenWidth(10);
42
- }, []);
43
- const stepCount = useMemo(() => {
44
- if (stepCountStr != null) {
45
- return parseInt(stepCountStr, 10);
46
- }
47
- return defaultStepCount;
48
- }, [stepCountStr, defaultStepCount]);
49
- useEffect(() => {
50
- if (stepCountStr == null) {
51
- setStepCount(defaultStepCount.toString());
52
- }
53
- }, [stepCountStr, defaultStepCount, setStepCount]);
54
- const { data, isLoading, error } = useGrpcQuery({
55
- key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
56
- queryFn: async () => {
57
- const stepDuration = getStepDuration(start, end, stepCount);
58
- const { response } = await client.queryRange({
59
- query: queryExpression,
60
- start: Timestamp.fromDate(new Date(start)),
61
- end: Timestamp.fromDate(new Date(end)),
62
- step: Duration.create(stepDuration),
63
- limit: 0,
64
- sumBy,
65
- }, { meta: metadata });
66
- return response;
67
- },
68
- options: {
69
- retry: false,
70
- enabled: !skip,
71
- staleTime: 1000 * 60 * 5, // 5 minutes
72
- },
73
- });
74
- return { isLoading, error: error, response: data ?? null };
75
- };
76
28
  const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to, setTimeRange, addLabelMatcher, onPointClick, comparing = false, sumBy, sumByLoading, }) => {
77
29
  const { isLoading: metricsGraphLoading, response, error, } = useQueryRange(queryClient, queryExpression, from, to, sumBy, sumByLoading);
78
30
  const { onError, perf, authenticationErrorMessage, isDarkMode } = useParcaContext();
@@ -2,7 +2,7 @@ import { QueryServiceClient } from '@parca/client';
2
2
  import { DateTimeRange } from '@parca/components';
3
3
  import { Query } from '@parca/parser';
4
4
  import { ProfileSelection } from '..';
5
- import { QuerySelection } from './index';
5
+ import { QuerySelection, type UtilizationMetrics as UtilizationMetricsType } from './index';
6
6
  interface MetricsGraphSectionProps {
7
7
  showMetricsGraph: boolean;
8
8
  setDisplayHideMetricsGraphButton: (show: boolean) => void;
@@ -20,7 +20,9 @@ interface MetricsGraphSectionProps {
20
20
  query: Query;
21
21
  setNewQueryExpression: (queryExpression: string) => void;
22
22
  setQueryExpression: (updateTs?: boolean) => void;
23
+ utilizationMetrics?: UtilizationMetricsType[];
24
+ utilizationMetricsLoading?: boolean;
23
25
  }
24
- export declare function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGraphButton, heightStyle, querySelection, profileSelection, comparing, sumBy, defaultSumByLoading, queryClient, queryExpressionString, setTimeRangeSelection, selectQuery, selectProfile, query, setNewQueryExpression, }: MetricsGraphSectionProps): JSX.Element;
26
+ export declare function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGraphButton, heightStyle, querySelection, profileSelection, comparing, sumBy, defaultSumByLoading, queryClient, queryExpressionString, setTimeRangeSelection, selectQuery, selectProfile, query, setNewQueryExpression, utilizationMetrics, utilizationMetricsLoading, }: MetricsGraphSectionProps): JSX.Element;
25
27
  export {};
26
28
  //# sourceMappingURL=MetricsGraphSection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MetricsGraphSection.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/MetricsGraphSection.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAQ,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAE5D,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAEvC,UAAU,wBAAwB;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gCAAgC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,KAAK,EAAE,KAAK,CAAC;IACb,qBAAqB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,kBAAkB,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD;AAED,wBAAgB,mBAAmB,CAAC,EAClC,gBAAgB,EAChB,gCAAgC,EAChC,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,KAAK,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,KAAK,EACL,qBAAqB,GACtB,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CAiHxC"}
1
+ {"version":3,"file":"MetricsGraphSection.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/MetricsGraphSection.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAQ,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAG5D,OAAO,EAAC,cAAc,EAAE,KAAK,kBAAkB,IAAI,sBAAsB,EAAC,MAAM,SAAS,CAAC;AAE1F,UAAU,wBAAwB;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gCAAgC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,KAAK,EAAE,KAAK,CAAC;IACb,qBAAqB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,kBAAkB,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,kBAAkB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC9C,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,gBAAgB,EAChB,gCAAgC,EAChC,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,KAAK,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,KAAK,EACL,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,GAC1B,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CAgIxC"}
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx, 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.
@@ -14,8 +14,9 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
14
14
  import cx from 'classnames';
15
15
  import { Query } from '@parca/parser';
16
16
  import { MergedProfileSelection } from '..';
17
+ import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
17
18
  import ProfileMetricsGraph, { ProfileMetricsEmptyState } from '../ProfileMetricsGraph';
18
- export function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGraphButton, heightStyle, querySelection, profileSelection, comparing, sumBy, defaultSumByLoading, queryClient, queryExpressionString, setTimeRangeSelection, selectQuery, selectProfile, query, setNewQueryExpression, }) {
19
+ export function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGraphButton, heightStyle, querySelection, profileSelection, comparing, sumBy, defaultSumByLoading, queryClient, queryExpressionString, setTimeRangeSelection, selectQuery, selectProfile, query, setNewQueryExpression, utilizationMetrics, utilizationMetricsLoading, }) {
19
20
  const handleTimeRangeChange = (range) => {
20
21
  const from = range.getFromMs();
21
22
  const to = range.getToMs();
@@ -74,7 +75,7 @@ export function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGra
74
75
  const mergeTo = query.profileType().delta ? mergeFrom + durationInMilliseconds : mergeFrom;
75
76
  selectProfile(new MergedProfileSelection(mergeFrom, mergeTo, query));
76
77
  };
77
- return (_jsxs("div", { className: cx('relative', { 'py-4': !showMetricsGraph }), children: [_jsxs("button", { onClick: () => setDisplayHideMetricsGraphButton(!showMetricsGraph), className: cx('hidden z-10 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900', showMetricsGraph && 'absolute right-0 bottom-3 !flex', !showMetricsGraph && 'relative !flex ml-auto'), children: [showMetricsGraph ? 'Hide' : 'Show', " Metrics Graph"] }), showMetricsGraph && (_jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== '' &&
78
- querySelection.from !== undefined &&
79
- querySelection.to !== undefined ? (_jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? sumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: handleTimeRangeChange, addLabelMatcher: addLabelMatcher, onPointClick: handlePointClick })) : (profileSelection === null && (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsEmptyState, { message: "Please select a profile type and click 'Search' to begin." }) }))) }))] }));
78
+ return (_jsxs("div", { className: cx('relative', { 'py-4': !showMetricsGraph }), children: [_jsxs("button", { onClick: () => setDisplayHideMetricsGraphButton(!showMetricsGraph), className: cx('hidden z-10 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900', showMetricsGraph && 'absolute right-0 bottom-3 !flex', !showMetricsGraph && 'relative !flex ml-auto'), children: [showMetricsGraph ? 'Hide' : 'Show', " Metrics Graph"] }), showMetricsGraph && (_jsx(_Fragment, { children: _jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== '' &&
79
+ querySelection.from !== undefined &&
80
+ querySelection.to !== undefined ? (_jsx(_Fragment, { children: utilizationMetrics !== undefined ? (_jsx(UtilizationMetricsGraph, { data: utilizationMetrics, addLabelMatcher: addLabelMatcher, setTimeRange: handleTimeRangeChange, utilizationMetricsLoading: utilizationMetricsLoading })) : (_jsx(_Fragment, { children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? sumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: handleTimeRangeChange, addLabelMatcher: addLabelMatcher, onPointClick: handlePointClick }) })) })) : (profileSelection === null && (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsEmptyState, { message: "Please select a profile type and click 'Search' to begin." }) }))) }) }))] }));
80
81
  }
@@ -19,6 +19,16 @@ interface ProfileSelectorFeatures {
19
19
  disableExplorativeQuerying?: boolean;
20
20
  disableProfileTypesDropdown?: boolean;
21
21
  }
22
+ export interface UtilizationMetrics {
23
+ timestamp: number;
24
+ value: number;
25
+ resource: {
26
+ [key: string]: string;
27
+ };
28
+ attributes: {
29
+ [key: string]: string;
30
+ };
31
+ }
22
32
  interface ProfileSelectorProps extends ProfileSelectorFeatures {
23
33
  queryClient: QueryServiceClient;
24
34
  querySelection: QuerySelection;
@@ -31,6 +41,8 @@ interface ProfileSelectorProps extends ProfileSelectorFeatures {
31
41
  navigateTo: NavigateFunction;
32
42
  setDisplayHideMetricsGraphButton: Dispatch<SetStateAction<boolean>>;
33
43
  suffix?: string;
44
+ utilizationMetrics?: UtilizationMetrics[];
45
+ utilizationMetricsLoading?: boolean;
34
46
  }
35
47
  export interface IProfileTypesResult {
36
48
  loading: boolean;
@@ -38,6 +50,6 @@ export interface IProfileTypesResult {
38
50
  error?: RpcError;
39
51
  }
40
52
  export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
41
- declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph, showSumBySelector, showProfileTypeSelector, disableExplorativeQuerying, setDisplayHideMetricsGraphButton, }: ProfileSelectorProps) => JSX.Element;
53
+ declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph, showSumBySelector, showProfileTypeSelector, disableExplorativeQuerying, setDisplayHideMetricsGraphButton, utilizationMetrics, utilizationMetricsLoading, }: ProfileSelectorProps) => JSX.Element;
42
54
  export default ProfileSelector;
43
55
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAErF,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAUvE,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQpC,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,UAAU,oBAAqB,SAAQ,uBAAuB;IAC5D,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,gCAAgC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,yQAelB,oBAAoB,KAAG,GAAG,CAAC,OAyM7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAErF,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAUvE,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQpC,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,UAAU,EAAE;QACV,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;CACH;AAED,UAAU,oBAAqB,SAAQ,uBAAuB;IAC5D,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,gCAAgC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC1C,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,wTAiBlB,oBAAoB,KAAG,GAAG,CAAC,OA0M7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -38,7 +38,7 @@ export const useProfileTypes = (client) => {
38
38
  }, [client, metadata, loading]);
39
39
  return { loading, data: result, error };
40
40
  };
41
- const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph = true, showSumBySelector = true, showProfileTypeSelector = true, disableExplorativeQuerying = false, setDisplayHideMetricsGraphButton, }) => {
41
+ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, showMetricsGraph = true, showSumBySelector = true, showProfileTypeSelector = true, disableExplorativeQuerying = false, setDisplayHideMetricsGraphButton, utilizationMetrics, utilizationMetricsLoading, }) => {
42
42
  const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
43
43
  const { heightStyle } = useMetricsGraphDimensions(comparing);
44
44
  const { viewComponent } = useParcaContext();
@@ -139,6 +139,6 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
139
139
  queryExpressionString === '{}';
140
140
  const queryBrowserRef = useRef(null);
141
141
  const sumByRef = useRef(null);
142
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, disableExplorativeQuerying: disableExplorativeQuerying, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: setTimeRangeSelection, searchDisabled: searchDisabled, queryBrowserMode: queryBrowserMode, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: sumBySelection ?? [], setUserSumBySelection: setUserSumBySelection, profileType: profileType, profileTypesError: error }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], defaultSumByLoading: defaultSumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: setTimeRangeSelection, selectQuery: selectQuery, selectProfile: selectProfile, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setNewQueryExpression })] }));
142
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, disableExplorativeQuerying: disableExplorativeQuerying, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: setTimeRangeSelection, searchDisabled: searchDisabled, queryBrowserMode: queryBrowserMode, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: sumBySelection ?? [], setUserSumBySelection: setUserSumBySelection, profileType: profileType, profileTypesError: error }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], defaultSumByLoading: defaultSumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: setTimeRangeSelection, selectQuery: selectQuery, selectProfile: selectProfile, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setNewQueryExpression, utilizationMetrics: utilizationMetrics, utilizationMetricsLoading: utilizationMetricsLoading })] }));
143
143
  };
144
144
  export default ProfileSelector;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.465",
3
+ "version": "0.16.467",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
@@ -75,5 +75,5 @@
75
75
  "access": "public",
76
76
  "registry": "https://registry.npmjs.org/"
77
77
  },
78
- "gitHead": "249ea57ecb9338451d0bf9a34f345c139accaa8d"
78
+ "gitHead": "2515639e6593c345f1ed3499da31cfcb0c8b932e"
79
79
  }