@parca/profile 0.16.498 → 0.16.500

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,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.500](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.499...@parca/profile@0.16.500) (2025-04-28)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.499](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.498...@parca/profile@0.16.499) (2025-04-24)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.498](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.497...@parca/profile@0.16.498) (2025-04-24)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -7,7 +7,8 @@ interface Props {
7
7
  sampleUnit: string;
8
8
  delta: boolean;
9
9
  utilizationMetrics?: boolean;
10
+ valuePrefix?: string;
10
11
  }
11
- declare const MetricsTooltip: ({ x, y, highlighted, contextElement, sampleUnit, delta, utilizationMetrics, }: Props) => JSX.Element;
12
+ declare const MetricsTooltip: ({ x, y, highlighted, contextElement, sampleUnit, delta, utilizationMetrics, valuePrefix, }: Props) => JSX.Element;
12
13
  export default MetricsTooltip;
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/MetricsGraph/MetricsTooltip/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,EAAC,iBAAiB,EAAC,MAAM,KAAK,CAAC;AAEtC,UAAU,KAAK;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,iBAAiB,CAAC;IAC/B,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AA8BD,QAAA,MAAM,cAAc,kFAQjB,KAAK,KAAG,GAAG,CAAC,OAoMd,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/MetricsGraph/MetricsTooltip/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,EAAC,iBAAiB,EAAC,MAAM,KAAK,CAAC;AAEtC,UAAU,KAAK;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,iBAAiB,CAAC;IAC/B,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA8BD,QAAA,MAAM,cAAc,+FASjB,KAAK,KAAG,GAAG,CAAC,OAuMd,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -42,7 +42,7 @@ function generateGetBoundingClientRect(contextElement, x = 0, y = 0) {
42
42
  bottom: domRect.y + y,
43
43
  });
44
44
  }
45
- const MetricsTooltip = ({ x, y, highlighted, contextElement, sampleUnit, delta, utilizationMetrics = false, }) => {
45
+ const MetricsTooltip = ({ x, y, highlighted, contextElement, sampleUnit, delta, utilizationMetrics = false, valuePrefix, }) => {
46
46
  const { timezone } = useParcaContext();
47
47
  const [popperElement, setPopperElement] = useState(null);
48
48
  const { styles, attributes, ...popperProps } = usePopper(virtualElement, popperElement, {
@@ -91,7 +91,7 @@ const MetricsTooltip = ({ x, y, highlighted, contextElement, sampleUnit, delta,
91
91
  }, [x, y, contextElement, update]);
92
92
  const nameLabel = highlighted?.labels.find(e => e.name === '__name__');
93
93
  const highlightedNameLabel = nameLabel !== undefined ? nameLabel : { name: '', value: '' };
94
- return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, className: "z-50", children: _jsx("div", { className: "flex max-w-lg", 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 pr-3", children: "Per\u00A0Second" }), _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, timePattern(timezone), timezone) })] })] }) }) }), _jsx("span", { className: "my-2 block text-gray-500", children: utilizationMetrics ? (_jsxs(_Fragment, { children: [Object.keys(attributesResourceMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Resource Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesResourceMap).map(name => (_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: `${name.replace('attributes.', '')}="${attributesResourceMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesResourceMap[name]}` }) }, name))) }), Object.keys(attributesMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesMap).map(name => (_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: `${name.replace('attributes.', '')}="${attributesMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesMap[name]}` }) }, name))) })] })) : (_jsx(_Fragment, { children: highlighted.labels
94
+ return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, className: "z-50", children: _jsx("div", { className: "flex max-w-lg", 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 pr-3", children: "Per\u00A0Second" }), _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: [_jsxs("td", { className: "w-1/4", children: [valuePrefix ?? '', "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, timePattern(timezone), timezone) })] })] }) }) }), _jsx("span", { className: "my-2 block text-gray-500", children: utilizationMetrics ? (_jsxs(_Fragment, { children: [Object.keys(attributesResourceMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Resource Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesResourceMap).map(name => (_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: `${name.replace('attributes.', '')}="${attributesResourceMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesResourceMap[name]}` }) }, name))) }), Object.keys(attributesMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesMap).map(name => (_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: `${name.replace('attributes.', '')}="${attributesMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesMap[name]}` }) }, name))) })] })) : (_jsx(_Fragment, { children: highlighted.labels
95
95
  .filter((label) => label.name !== '__name__')
96
96
  .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.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." })] })] }) }) }) }) }) }));
97
97
  };
@@ -21,4 +21,4 @@ type Props = CommonProps & {
21
21
  };
22
22
  declare const AreaChart: ({ transmitData, receiveData, addLabelMatcher, setTimeRange, utilizationMetricsLoading, name, humanReadableName, from, to, }: Props) => JSX.Element;
23
23
  export default AreaChart;
24
- //# sourceMappingURL=AreaChart.d.ts.map
24
+ //# sourceMappingURL=Throughput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Throughput.d.ts","sourceRoot":"","sources":["../../../src/MetricsGraph/UtilizationMetrics/Throughput.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAC,aAAa,EAAqD,MAAM,mBAAmB,CAAC;AAIpG,OAAO,EAAC,KAAK,kBAAkB,IAAI,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAiB9E,UAAU,WAAW;IACnB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,WAAW,EAAE,YAAY,EAAE,CAAC;IAC5B,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,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAQD,KAAK,KAAK,GAAG,WAAW,GAAG;IACzB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC,CAAC;AAmeF,QAAA,MAAM,SAAS,gIAUZ,KAAK,KAAG,GAAG,CAAC,OAiCd,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -48,23 +48,7 @@ function transformToSeries(data, isReceive = false) {
48
48
  values: series.values.sort((a, b) => a[0] - b[0]),
49
49
  }));
50
50
  }
51
- const getYAxisUnit = (name) => {
52
- switch (name) {
53
- case 'gpu_utilization_percent':
54
- return 'percent';
55
- case 'gpu_memory_utilization_percent':
56
- return 'percent';
57
- case 'gpu_power_watt':
58
- return 'watts';
59
- case 'gpu_pcie_throughput_transmit_bytes':
60
- return 'bytes_per_second';
61
- case 'gpu_pcie_throughput_receive_bytes':
62
- return 'bytes_per_second';
63
- default:
64
- return 'percent';
65
- }
66
- };
67
- const RawAreaChart = ({ transmitData, receiveData, addLabelMatcher, setTimeRange, width, height, margin, name, humanReadableName, from, to, }) => {
51
+ const RawAreaChart = ({ transmitData, receiveData, addLabelMatcher, setTimeRange, width, height, margin, humanReadableName, from, to, }) => {
68
52
  const { timezone } = useParcaContext();
69
53
  const graph = useRef(null);
70
54
  const [dragging, setDragging] = useState(false);
@@ -136,21 +120,15 @@ const RawAreaChart = ({ transmitData, receiveData, addLabelMatcher, setTimeRange
136
120
  event: e,
137
121
  });
138
122
  }, [show]);
123
+ const color = d3.scaleOrdinal(d3.schemeCategory10);
139
124
  const getSeriesColor = (series) => {
140
- return series.isReceive === true ? '#EAB308' : '#22C55E'; // Yellow for receive, Green for transmit
125
+ return color(series.labelset);
141
126
  };
142
- // Create area generator for transmit (above zero)
143
- const transmitArea = d3
144
- .area()
145
- .x(d => xScale(d[0]))
146
- .y0(yScale(0)) // Start from zero line
147
- .y1(d => yScale(d[1])); // Top of the area (data point)
148
- // Create area generator for receive (below zero)
149
- const receiveArea = d3
150
- .area()
127
+ // Create line generator for both transmit and receive
128
+ const lineGenerator = d3
129
+ .line()
151
130
  .x(d => xScale(d[0]))
152
- .y0(yScale(0)) // Start from zero line
153
- .y1(d => yScale(d[1])); // Bottom of the area (negative data point)
131
+ .y(d => yScale(d[1]));
154
132
  const highlighted = useMemo(() => {
155
133
  if (series.length === 0) {
156
134
  return null;
@@ -228,47 +206,45 @@ const RawAreaChart = ({ transmitData, receiveData, addLabelMatcher, setTimeRange
228
206
  };
229
207
  return (_jsxs(_Fragment, { children: [_jsx(MetricsContextMenu, { onAddLabelMatcher: addLabelMatcher, menuId: MENU_ID, highlighted: highlighted, trackVisibility: trackVisibility, utilizationMetrics: true }), 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: {
230
208
  ...highlighted,
231
- valuePerSecond: Math.abs(highlighted.valuePerSecond), // Show absolute value in tooltip
232
- }, contextElement: graph.current, sampleUnit: getYAxisUnit(name), delta: false, utilizationMetrics: true })) })), _jsx("div", { ref: graph, onMouseEnter: () => setHovering(true), 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(6).map((d, i, allTicks) => {
209
+ valuePerSecond: Math.abs(highlighted.valuePerSecond),
210
+ }, contextElement: graph.current, sampleUnit: 'bytes_per_second', delta: false, utilizationMetrics: true, valuePrefix: highlighted.seriesIndex >= transmitData.length ? 'Receive ' : 'Transmit ' })) })), _jsx("div", { ref: graph, onMouseEnter: () => setHovering(true), 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(6).map((d, i, allTicks) => {
233
211
  let decimals = 2;
234
212
  const intervalBetweenTicks = allTicks[1] - allTicks[0];
235
213
  if (intervalBetweenTicks < 1) {
236
214
  const precision = getPrecision(intervalBetweenTicks);
237
215
  decimals = precision;
238
216
  }
239
- return (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick", transform: `translate(0, ${yScale(d)})`, children: [_jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x2: -6 }), _jsxs("text", { fill: "currentColor", x: -9, dy: '0.32em', children: [d < 0 ? '-' : '', valueFormatter(Math.abs(d), getYAxisUnit(name), 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()}`));
217
+ return (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick", transform: `translate(0, ${yScale(d)})`, children: [_jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x2: -6 }), _jsxs("text", { fill: "currentColor", x: -9, dy: '0.32em', children: [d < 0 ? '-' : '', valueFormatter(Math.abs(d), 'bytes_per_second', 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()}`));
240
218
  }), _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: humanReadableName }) })] }), _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",
241
219
  /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
242
- 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: "areas", children: series.map((s, i) => {
243
- let isSelected = false;
244
- if (parsedSelectedSeries != null && parsedSelectedSeries.length > 0) {
245
- isSelected = parsedSelectedSeries.every(m => {
246
- for (let i = 0; i < s.metric.length; i++) {
247
- if (s.metric[i].name === m.key && s.metric[i].value === m.value) {
248
- return true;
220
+ 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" }) })] }), _jsxs("g", { className: "areas", children: [_jsx("line", { x1: xScale(from), x2: xScale(to), y1: yScale(0), y2: yScale(0), stroke: "#64748b", strokeDasharray: "4 2", strokeWidth: 1, opacity: 0.7 }), series.map((s, i) => {
221
+ let isSelected = false;
222
+ if (parsedSelectedSeries != null && parsedSelectedSeries.length > 0) {
223
+ isSelected = parsedSelectedSeries.every(m => {
224
+ for (let i = 0; i < s.metric.length; i++) {
225
+ if (s.metric[i].name === m.key && s.metric[i].value === m.value) {
226
+ return true;
227
+ }
249
228
  }
250
- }
251
- return false;
252
- });
253
- }
254
- const seriesColor = getSeriesColor(s);
255
- const fillOpacity = isSelected ? 0.4 : 0.2;
256
- const strokeOpacity = isSelected ? 1 : 0.8;
257
- const areaGenerator = s.isReceive === true ? receiveArea : transmitArea;
258
- return (_jsx("g", { className: "area cursor-pointer", children: _jsx("path", { d: areaGenerator(s.values) ?? '', fill: seriesColor, fillOpacity: fillOpacity, stroke: seriesColor, strokeWidth: isSelected
259
- ? lineStrokeSelected
260
- : hovering && highlighted != null && i === highlighted.seriesIndex
261
- ? lineStrokeHover
262
- : lineStroke, strokeOpacity: strokeOpacity, onClick: () => {
263
- if (highlighted != null) {
264
- setSelectedSeries(JSON.stringify(highlighted.labels.map(l => ({
265
- key: l.name,
266
- value: l.value,
267
- }))));
268
- setSelectedTimeframe(undefined);
269
- }
270
- } }) }, i));
271
- }) })] })] }) })] }));
229
+ return false;
230
+ });
231
+ }
232
+ const seriesColor = getSeriesColor(s);
233
+ const strokeOpacity = isSelected ? 1 : 0.8;
234
+ return (_jsx("g", { className: "line cursor-pointer", children: _jsx("path", { d: lineGenerator(s.values) ?? '', fill: "none", stroke: seriesColor, strokeWidth: isSelected
235
+ ? lineStrokeSelected
236
+ : hovering && highlighted != null && i === highlighted.seriesIndex
237
+ ? lineStrokeHover
238
+ : lineStroke, strokeOpacity: strokeOpacity, onClick: () => {
239
+ if (highlighted != null) {
240
+ setSelectedSeries(JSON.stringify(highlighted.labels.map(l => ({
241
+ key: l.name,
242
+ value: l.value,
243
+ }))));
244
+ setSelectedTimeframe(undefined);
245
+ }
246
+ } }) }, i));
247
+ })] })] })] }) })] }));
272
248
  };
273
249
  const AreaChart = ({ transmitData, receiveData, addLabelMatcher, setTimeRange, utilizationMetricsLoading, name, humanReadableName, from, to, }) => {
274
250
  const { isDarkMode } = useParcaContext();
@@ -15,7 +15,7 @@ import cx from 'classnames';
15
15
  import { Query } from '@parca/parser';
16
16
  import { MergedProfileSelection } from '..';
17
17
  import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
18
- import AreaChart from '../MetricsGraph/UtilizationMetrics/AreaChart';
18
+ import AreaChart from '../MetricsGraph/UtilizationMetrics/Throughput';
19
19
  import ProfileMetricsGraph, { ProfileMetricsEmptyState } from '../ProfileMetricsGraph';
20
20
  export function MetricsGraphSection({ showMetricsGraph, setDisplayHideMetricsGraphButton, heightStyle, querySelection, profileSelection, comparing, sumBy, defaultSumByLoading, queryClient, queryExpressionString, setTimeRangeSelection, selectQuery, selectProfile, query, setNewQueryExpression, utilizationMetrics, utilizationMetricsLoading, }) {
21
21
  const handleTimeRangeChange = (range) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.498",
3
+ "version": "0.16.500",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
@@ -77,5 +77,5 @@
77
77
  "access": "public",
78
78
  "registry": "https://registry.npmjs.org/"
79
79
  },
80
- "gitHead": "fc0740783d9673d30bf0020d3744bceaba02a345"
80
+ "gitHead": "4fe4798ae9359ce2ca83f9ff5c83ef9868b9ef45"
81
81
  }
@@ -31,6 +31,7 @@ interface Props {
31
31
  sampleUnit: string;
32
32
  delta: boolean;
33
33
  utilizationMetrics?: boolean;
34
+ valuePrefix?: string;
34
35
  }
35
36
 
36
37
  const virtualElement: VirtualElement = {
@@ -69,6 +70,7 @@ const MetricsTooltip = ({
69
70
  sampleUnit,
70
71
  delta,
71
72
  utilizationMetrics = false,
73
+ valuePrefix,
72
74
  }: Props): JSX.Element => {
73
75
  const {timezone} = useParcaContext();
74
76
 
@@ -164,7 +166,10 @@ const MetricsTooltip = ({
164
166
  </>
165
167
  ) : (
166
168
  <tr>
167
- <td className="w-1/4">Value</td>
169
+ <td className="w-1/4">
170
+ {valuePrefix ?? ''}
171
+ Value
172
+ </td>
168
173
  <td className="w-3/4">
169
174
  {valueFormatter(highlighted.valuePerSecond, sampleUnit, 5)}
170
175
  </td>
@@ -100,23 +100,6 @@ function transformToSeries(data: MetricSeries[], isReceive = false): NetworkSeri
100
100
  }));
101
101
  }
102
102
 
103
- const getYAxisUnit = (name: string): string => {
104
- switch (name) {
105
- case 'gpu_utilization_percent':
106
- return 'percent';
107
- case 'gpu_memory_utilization_percent':
108
- return 'percent';
109
- case 'gpu_power_watt':
110
- return 'watts';
111
- case 'gpu_pcie_throughput_transmit_bytes':
112
- return 'bytes_per_second';
113
- case 'gpu_pcie_throughput_receive_bytes':
114
- return 'bytes_per_second';
115
- default:
116
- return 'percent';
117
- }
118
- };
119
-
120
103
  const RawAreaChart = ({
121
104
  transmitData,
122
105
  receiveData,
@@ -125,7 +108,6 @@ const RawAreaChart = ({
125
108
  width,
126
109
  height,
127
110
  margin,
128
- name,
129
111
  humanReadableName,
130
112
  from,
131
113
  to,
@@ -225,23 +207,17 @@ const RawAreaChart = ({
225
207
  [show]
226
208
  );
227
209
 
210
+ const color = d3.scaleOrdinal(d3.schemeCategory10);
211
+
228
212
  const getSeriesColor = (series: NetworkSeries): string => {
229
- return series.isReceive === true ? '#EAB308' : '#22C55E'; // Yellow for receive, Green for transmit
213
+ return color(series.labelset);
230
214
  };
231
215
 
232
- // Create area generator for transmit (above zero)
233
- const transmitArea = d3
234
- .area<number[]>()
216
+ // Create line generator for both transmit and receive
217
+ const lineGenerator = d3
218
+ .line<number[]>()
235
219
  .x(d => xScale(d[0]))
236
- .y0(yScale(0)) // Start from zero line
237
- .y1(d => yScale(d[1])); // Top of the area (data point)
238
-
239
- // Create area generator for receive (below zero)
240
- const receiveArea = d3
241
- .area<number[]>()
242
- .x(d => xScale(d[0]))
243
- .y0(yScale(0)) // Start from zero line
244
- .y1(d => yScale(d[1])); // Bottom of the area (negative data point)
220
+ .y(d => yScale(d[1]));
245
221
 
246
222
  const highlighted = useMemo(() => {
247
223
  if (series.length === 0) {
@@ -360,12 +336,15 @@ const RawAreaChart = ({
360
336
  y={pos[1] + margin}
361
337
  highlighted={{
362
338
  ...highlighted,
363
- valuePerSecond: Math.abs(highlighted.valuePerSecond), // Show absolute value in tooltip
339
+ valuePerSecond: Math.abs(highlighted.valuePerSecond),
364
340
  }}
365
341
  contextElement={graph.current}
366
- sampleUnit={getYAxisUnit(name)}
342
+ sampleUnit={'bytes_per_second'}
367
343
  delta={false}
368
344
  utilizationMetrics={true}
345
+ valuePrefix={
346
+ highlighted.seriesIndex >= transmitData.length ? 'Receive ' : 'Transmit '
347
+ }
369
348
  />
370
349
  )}
371
350
  </div>
@@ -414,7 +393,7 @@ const RawAreaChart = ({
414
393
  <line className="stroke-gray-300 dark:stroke-gray-500" x2={-6} />
415
394
  <text fill="currentColor" x={-9} dy={'0.32em'}>
416
395
  {d < 0 ? '-' : ''}
417
- {valueFormatter(Math.abs(d), getYAxisUnit(name), decimals)}
396
+ {valueFormatter(Math.abs(d), 'bytes_per_second', decimals)}
418
397
  </text>
419
398
  </g>
420
399
  <g key={`grid-${i}`}>
@@ -499,6 +478,17 @@ const RawAreaChart = ({
499
478
  </g>
500
479
  </g>
501
480
  <g className="areas">
481
+ {/* Draw baseline at y=0 */}
482
+ <line
483
+ x1={xScale(from)}
484
+ x2={xScale(to)}
485
+ y1={yScale(0)}
486
+ y2={yScale(0)}
487
+ stroke="#64748b"
488
+ strokeDasharray="4 2"
489
+ strokeWidth={1}
490
+ opacity={0.7}
491
+ />
502
492
  {series.map((s, i) => {
503
493
  let isSelected = false;
504
494
  if (parsedSelectedSeries != null && parsedSelectedSeries.length > 0) {
@@ -513,16 +503,13 @@ const RawAreaChart = ({
513
503
  }
514
504
 
515
505
  const seriesColor = getSeriesColor(s);
516
- const fillOpacity = isSelected ? 0.4 : 0.2;
517
506
  const strokeOpacity = isSelected ? 1 : 0.8;
518
- const areaGenerator = s.isReceive === true ? receiveArea : transmitArea;
519
507
 
520
508
  return (
521
- <g key={i} className="area cursor-pointer">
509
+ <g key={i} className="line cursor-pointer">
522
510
  <path
523
- d={areaGenerator(s.values) ?? ''}
524
- fill={seriesColor}
525
- fillOpacity={fillOpacity}
511
+ d={lineGenerator(s.values) ?? ''}
512
+ fill="none"
526
513
  stroke={seriesColor}
527
514
  strokeWidth={
528
515
  isSelected
@@ -19,7 +19,7 @@ import {Query} from '@parca/parser';
19
19
 
20
20
  import {MergedProfileSelection, ProfileSelection} from '..';
21
21
  import UtilizationMetricsGraph from '../MetricsGraph/UtilizationMetrics';
22
- import AreaChart from '../MetricsGraph/UtilizationMetrics/AreaChart';
22
+ import AreaChart from '../MetricsGraph/UtilizationMetrics/Throughput';
23
23
  import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
24
24
  import {QuerySelection, type UtilizationMetrics as UtilizationMetricsType} from './index';
25
25
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"AreaChart.d.ts","sourceRoot":"","sources":["../../../src/MetricsGraph/UtilizationMetrics/AreaChart.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAC,aAAa,EAAqD,MAAM,mBAAmB,CAAC;AAIpG,OAAO,EAAC,KAAK,kBAAkB,IAAI,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAiB9E,UAAU,WAAW;IACnB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,WAAW,EAAE,YAAY,EAAE,CAAC;IAC5B,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,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAQD,KAAK,KAAK,GAAG,WAAW,GAAG;IACzB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC,CAAC;AAgfF,QAAA,MAAM,SAAS,gIAUZ,KAAK,KAAG,GAAG,CAAC,OAiCd,CAAC;AAEF,eAAe,SAAS,CAAC"}