@parca/profile 0.16.139 → 0.16.141

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 (37) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/Callgraph/index.d.ts +1 -0
  3. package/dist/GraphTooltip/ExpandOnHoverValue.d.ts +1 -0
  4. package/dist/GraphTooltip/index.d.ts +1 -0
  5. package/dist/MatchersInput/SuggestionItem.d.ts +1 -0
  6. package/dist/MatchersInput/SuggestionsList.d.ts +1 -0
  7. package/dist/MatchersInput/index.d.ts +1 -0
  8. package/dist/MetricsCircle/index.d.ts +1 -0
  9. package/dist/MetricsGraph/MetricsTooltip/index.d.ts +3 -1
  10. package/dist/MetricsGraph/MetricsTooltip/index.js +2 -2
  11. package/dist/MetricsGraph/index.d.ts +2 -0
  12. package/dist/MetricsGraph/index.js +4 -2
  13. package/dist/MetricsSeries/index.d.ts +1 -0
  14. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +1 -0
  15. package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +1 -0
  16. package/dist/ProfileExplorer/index.d.ts +1 -0
  17. package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.d.ts +1 -0
  18. package/dist/ProfileIcicleGraph/index.d.ts +1 -0
  19. package/dist/ProfileMetricsGraph/index.d.ts +1 -0
  20. package/dist/ProfileMetricsGraph/index.js +7 -1
  21. package/dist/ProfileSelector/CompareButton.d.ts +1 -0
  22. package/dist/ProfileSelector/index.d.ts +1 -0
  23. package/dist/ProfileSource.d.ts +1 -0
  24. package/dist/ProfileTypeSelector/index.d.ts +1 -0
  25. package/dist/ProfileView/FilterByFunctionButton.d.ts +1 -0
  26. package/dist/ProfileView/ViewSelector.d.ts +1 -0
  27. package/dist/ProfileView/index.d.ts +1 -0
  28. package/dist/ProfileViewWithData.d.ts +1 -0
  29. package/dist/ProfileViewWithData.js +20 -10
  30. package/dist/components/DiffLegend.d.ts +1 -0
  31. package/dist/components/ProfileShareButton/ResultBox.d.ts +1 -0
  32. package/dist/components/ProfileShareButton/index.d.ts +1 -0
  33. package/package.json +6 -6
  34. package/src/MetricsGraph/MetricsTooltip/index.tsx +18 -6
  35. package/src/MetricsGraph/index.tsx +5 -1
  36. package/src/ProfileMetricsGraph/index.tsx +9 -1
  37. package/src/ProfileViewWithData.tsx +22 -12
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.141 (2023-03-20)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.140](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.139...@parca/profile@0.16.140) (2023-03-16)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.139](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.138...@parca/profile@0.16.139) (2023-03-16)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Callgraph as CallgraphType } from '@parca/client';
2
3
  export interface Props {
3
4
  data: CallgraphType;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  interface Props {
2
3
  value: string | number | undefined;
3
4
  displayValue?: string | number | undefined;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { CallgraphNode, CallgraphNodeMeta, FlamegraphNode, FlamegraphNodeMeta, FlamegraphRootNode } from '@parca/client';
2
3
  import { Location, Mapping, Function as ParcaFunction } from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
3
4
  interface ExtendedCallgraphNodeMeta extends CallgraphNodeMeta {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  interface Props {
2
3
  isHighlighted: boolean;
3
4
  onHighlight: () => void;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  export declare class Suggestion {
2
3
  type: string;
3
4
  typeahead: string;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { LabelsResponse, QueryServiceClient } from '@parca/client';
2
3
  import { Query } from '@parca/parser';
3
4
  interface MatchersInputProps {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  interface MetricsCircleProps {
2
3
  cx: number;
3
4
  cy: number;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { HighlightedSeries } from '../';
2
3
  interface Props {
3
4
  x: number;
@@ -6,6 +7,7 @@ interface Props {
6
7
  onLabelClick: (labelName: string, labelValue: string) => void;
7
8
  contextElement: Element | null;
8
9
  sampleUnit: string;
10
+ delta: boolean;
9
11
  }
10
- declare const MetricsTooltip: ({ x, y, highlighted, onLabelClick, contextElement, sampleUnit, }: Props) => JSX.Element;
12
+ declare const MetricsTooltip: ({ x, y, highlighted, onLabelClick, contextElement, sampleUnit, delta, }: Props) => JSX.Element;
11
13
  export default MetricsTooltip;
@@ -68,7 +68,7 @@ function generateGetBoundingClientRect(contextElement, x, y) {
68
68
  };
69
69
  }
70
70
  var MetricsTooltip = function (_a) {
71
- var x = _a.x, y = _a.y, highlighted = _a.highlighted, onLabelClick = _a.onLabelClick, contextElement = _a.contextElement, sampleUnit = _a.sampleUnit;
71
+ var x = _a.x, y = _a.y, highlighted = _a.highlighted, onLabelClick = _a.onLabelClick, contextElement = _a.contextElement, sampleUnit = _a.sampleUnit, delta = _a.delta;
72
72
  var _b = useState(null), popperElement = _b[0], setPopperElement = _b[1];
73
73
  var _c = usePopper(virtualElement, popperElement, {
74
74
  placement: 'auto-start',
@@ -98,7 +98,7 @@ var MetricsTooltip = function (_a) {
98
98
  }, [x, y, contextElement, update]);
99
99
  var nameLabel = highlighted === null || highlighted === void 0 ? void 0 : highlighted.labels.find(function (e) { return e.name === '__name__'; });
100
100
  var highlightedNameLabel = nameLabel !== undefined ? nameLabel : { name: '', value: '' };
101
- return (_jsx("div", __assign({ ref: setPopperElement, style: styles.popper }, attributes.popper, { className: "z-10" }, { children: _jsx("div", __assign({ className: "flex max-w-md" }, { children: _jsx("div", __assign({ className: "m-auto" }, { children: _jsx("div", __assign({ className: "border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg opacity-90", style: { borderWidth: 1 } }, { children: _jsx("div", __assign({ className: "flex flex-row" }, { children: _jsxs("div", __assign({ className: "ml-2 mr-6" }, { children: [_jsx("span", __assign({ className: "font-semibold" }, { children: highlightedNameLabel.value })), _jsx("span", __assign({ className: "block text-gray-700 dark:text-gray-300 my-2" }, { children: _jsx("table", __assign({ className: "table-auto" }, { children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Value" })), _jsx("td", __assign({ className: "w-3/4" }, { children: valueFormatter(highlighted.valuePerSecond, sampleUnit, 5) }))] }), _jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Total" })), _jsx("td", __assign({ className: "w-3/4" }, { children: valueFormatter(highlighted.value, sampleUnit, 2) }))] }), _jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "At" })), _jsx("td", __assign({ className: "w-3/4" }, { children: formatDate(highlighted.timestamp, timeFormat) }))] })] }) })) })), _jsx("span", __assign({ className: "block text-gray-500 my-2" }, { children: highlighted.labels
101
+ return (_jsx("div", __assign({ ref: setPopperElement, style: styles.popper }, attributes.popper, { className: "z-10" }, { children: _jsx("div", __assign({ className: "flex max-w-md" }, { children: _jsx("div", __assign({ className: "m-auto" }, { children: _jsx("div", __assign({ className: "border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg opacity-90", style: { borderWidth: 1 } }, { children: _jsx("div", __assign({ className: "flex flex-row" }, { children: _jsxs("div", __assign({ className: "ml-2 mr-6" }, { children: [_jsx("span", __assign({ className: "font-semibold" }, { children: highlightedNameLabel.value })), _jsx("span", __assign({ className: "block text-gray-700 dark:text-gray-300 my-2" }, { children: _jsx("table", __assign({ className: "table-auto" }, { children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Value" })), _jsx("td", __assign({ className: "w-3/4" }, { children: valueFormatter(highlighted.valuePerSecond, sampleUnit, 5) }))] }), delta && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Total" })), _jsx("td", __assign({ className: "w-3/4" }, { children: valueFormatter(highlighted.value, sampleUnit, 2) }))] })), highlighted.duration > 0 && (_jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "Duration" })), _jsx("td", __assign({ className: "w-3/4" }, { children: valueFormatter(highlighted.duration, 'nanoseconds', 2) }))] })), _jsxs("tr", { children: [_jsx("td", __assign({ className: "w-1/4" }, { children: "At" })), _jsx("td", __assign({ className: "w-3/4" }, { children: formatDate(highlighted.timestamp, timeFormat) }))] })] }) })) })), _jsx("span", __assign({ className: "block text-gray-500 my-2" }, { children: highlighted.labels
102
102
  .filter(function (label) { return label.name !== '__name__'; })
103
103
  .map(function (label) {
104
104
  return (_jsx("button", __assign({ type: "button", className: "inline-block rounded-lg text-gray-700 bg-gray-200 dark:bg-gray-700 dark:text-gray-400 px-2 py-1 text-xs font-bold mr-3", onClick: function () { return onLabelClick(label.name, label.value); } }, { children: _jsx(TextWithTooltip, { text: "".concat(label.name, "=\"").concat(label.value, "\""), maxTextLength: 37, id: "tooltip-".concat(label.name, "-").concat(label.value) }) }), label.name));
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Label, MetricsSeries as MetricsSeriesPb } from '@parca/client';
2
3
  import { DateTimeRange } from '@parca/components';
3
4
  import { MergedProfileSelection } from '..';
@@ -18,6 +19,7 @@ export interface HighlightedSeries {
18
19
  timestamp: number;
19
20
  value: number;
20
21
  valuePerSecond: number;
22
+ duration: number;
21
23
  x: number;
22
24
  y: number;
23
25
  }
@@ -70,7 +70,7 @@ export var RawMetricsGraph = function (_a) {
70
70
  values: s.samples.reduce(function (agg, d) {
71
71
  if (d.timestamp !== undefined && d.valuePerSecond !== undefined) {
72
72
  var t = (+d.timestamp.seconds * 1e9 + d.timestamp.nanos) / 1e6; // https://github.com/microsoft/TypeScript/issues/5710#issuecomment-157886246
73
- agg.push([t, d.valuePerSecond, parseFloat(d.value)]);
73
+ agg.push([t, d.valuePerSecond, parseFloat(d.value), parseFloat(d.duration)]);
74
74
  }
75
75
  return agg;
76
76
  }, []),
@@ -124,6 +124,7 @@ export var RawMetricsGraph = function (_a) {
124
124
  timestamp: point[0],
125
125
  valuePerSecond: point[1],
126
126
  value: point[2],
127
+ duration: point[3],
127
128
  x: xScale(point[0]),
128
129
  y: yScale(point[1]),
129
130
  };
@@ -231,12 +232,13 @@ export var RawMetricsGraph = function (_a) {
231
232
  timestamp: sample[0],
232
233
  valuePerSecond: sample[1],
233
234
  value: sample[2],
235
+ duration: sample[3],
234
236
  x: xScale(sample[0]),
235
237
  y: yScale(sample[1]),
236
238
  };
237
239
  };
238
240
  var selected = findSelectedProfile();
239
- return (_jsxs(_Fragment, { children: [highlighted != null && hovering && !dragging && pos[0] !== 0 && pos[1] !== 0 && (_jsx("div", __assign({ onMouseMove: onMouseMove, onMouseEnter: function () { return setHovering(true); }, onMouseLeave: function () { return setHovering(false); } }, { children: _jsx(MetricsTooltip, { x: pos[0] + margin, y: pos[1] + margin, highlighted: highlighted, onLabelClick: onLabelClick, contextElement: graph.current, sampleUnit: sampleUnit }) }))), _jsx("div", __assign({ ref: graph, onMouseEnter: function () {
241
+ return (_jsxs(_Fragment, { children: [highlighted != null && hovering && !dragging && pos[0] !== 0 && pos[1] !== 0 && (_jsx("div", __assign({ onMouseMove: onMouseMove, onMouseEnter: function () { return setHovering(true); }, onMouseLeave: function () { return setHovering(false); } }, { children: _jsx(MetricsTooltip, { x: pos[0] + margin, y: pos[1] + margin, highlighted: highlighted, onLabelClick: onLabelClick, contextElement: graph.current, sampleUnit: sampleUnit, delta: profile !== null ? profile === null || profile === void 0 ? void 0 : profile.query.profType.delta : false }) }))), _jsx("div", __assign({ ref: graph, onMouseEnter: function () {
240
242
  setHovering(true);
241
243
  }, onMouseLeave: function () { return setHovering(false); } }, { children: _jsxs("svg", __assign({ width: "".concat(width, "px"), height: "".concat(height + margin, "px"), onMouseDown: onMouseDown, onMouseUp: onMouseUp, onMouseMove: onMouseMove }, { children: [_jsx("g", __assign({ transform: "translate(".concat(margin, ", 0)") }, { children: dragging && (_jsx("g", __assign({ 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", __assign({ transform: "translate(".concat(margin, ", ").concat(margin, ")") }, { children: [_jsx("g", __assign({ className: "lines fill-transparent" }, { children: series.map(function (s, i) { return (_jsx("g", __assign({ className: "line" }, { children: _jsx(MetricsSeries, { data: s, line: l, color: color(i.toString()), strokeWidth: hovering && highlighted != null && i === highlighted.seriesIndex
242
244
  ? lineStrokeHover
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import * as d3 from 'd3';
2
3
  interface MetricsSeriesProps {
3
4
  data: any;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { QueryServiceClient } from '@parca/client';
2
3
  import type { NavigateFunction } from '@parca/functions';
3
4
  import { ProfileSelection } from '..';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { QueryServiceClient } from '@parca/client';
2
3
  import type { NavigateFunction } from '@parca/functions';
3
4
  import { ProfileSelection } from '..';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { QueryServiceClient } from '@parca/client';
2
3
  import type { NavigateFunction } from '@parca/functions';
3
4
  interface ProfileExplorerProps {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import type { NavigateFunction } from '@parca/functions';
2
3
  interface Props {
3
4
  navigateTo?: NavigateFunction;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Flamegraph } from '@parca/client';
2
3
  import { type NavigateFunction } from '@parca/functions';
3
4
  export type ResizeHandler = (width: number, height: number) => void;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { RpcError } from '@protobuf-ts/runtime-rpc';
2
3
  import { Label, QueryRangeResponse, QueryServiceClient } from '@parca/client';
3
4
  import { DateTimeRange } from '@parca/components';
@@ -102,12 +102,18 @@ var ProfileMetricsGraph = function (_a) {
102
102
  var queryClient = _a.queryClient, queryExpression = _a.queryExpression, profile = _a.profile, from = _a.from, to = _a.to, setTimeRange = _a.setTimeRange, addLabelMatcher = _a.addLabelMatcher, onPointClick = _a.onPointClick;
103
103
  var _b = useQueryRange(queryClient, queryExpression, from, to), isLoading = _b.isLoading, response = _b.response, error = _b.error;
104
104
  var isLoaderVisible = useDelayedLoader(isLoading);
105
- var _c = useParcaContext(), loader = _c.loader, onError = _c.onError;
105
+ var _c = useParcaContext(), loader = _c.loader, onError = _c.onError, perf = _c.perf;
106
106
  useEffect(function () {
107
107
  if (error !== null) {
108
108
  onError === null || onError === void 0 ? void 0 : onError(error, 'metricsGraph');
109
109
  }
110
110
  }, [error, onError]);
111
+ useEffect(function () {
112
+ if (response === null) {
113
+ return;
114
+ }
115
+ perf === null || perf === void 0 ? void 0 : perf.markInteraction('Metrics graph render', response.series[0].samples.length);
116
+ }, [perf, response]);
111
117
  if (isLoaderVisible) {
112
118
  return _jsx(_Fragment, { children: loader });
113
119
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  declare const CompareButton: ({ disabled, onClick, }: {
2
3
  disabled: boolean;
3
4
  onClick: () => void;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { RpcError } from '@protobuf-ts/runtime-rpc';
2
3
  import { ProfileTypesResponse, QueryServiceClient } from '@parca/client';
3
4
  import { ProfileSelection } from '..';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Label, ProfileDiffSelection, QueryRequest } from '@parca/client';
2
3
  import { ProfileType, Query } from '@parca/parser';
3
4
  export interface ProfileSource {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { RpcError } from '@protobuf-ts/runtime-rpc';
2
3
  import { ProfileType, ProfileTypesResponse } from '@parca/client';
3
4
  interface WellKnownProfile {
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import type { NavigateFunction } from '@parca/functions';
2
3
  declare const FilterByFunctionButton: ({ navigateTo, }: {
3
4
  navigateTo: NavigateFunction | undefined;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import type { NavigateFunction } from '@parca/functions';
2
3
  interface Props {
3
4
  position: number;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { Callgraph as CallgraphType, Flamegraph, QueryServiceClient, Top } from '@parca/client';
2
3
  import { ResizeHandler } from '../ProfileIcicleGraph';
3
4
  import { ProfileSource } from '../ProfileSource';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { QueryServiceClient } from '@parca/client';
2
3
  import { type NavigateFunction } from '@parca/functions';
3
4
  import { ProfileSource } from './ProfileSource';
@@ -82,22 +82,32 @@ export var ProfileViewWithData = function (_a) {
82
82
  nodeTrimThreshold: nodeTrimThreshold,
83
83
  }), flamegraphLoading = _e.isLoading, flamegraphResponse = _e.response, flamegraphError = _e.error;
84
84
  var perf = useParcaContext().perf;
85
- useEffect(function () {
86
- var _a;
87
- if (flamegraphLoading) {
88
- return;
89
- }
90
- if ((flamegraphResponse === null || flamegraphResponse === void 0 ? void 0 : flamegraphResponse.report.oneofKind) !== 'flamegraph') {
91
- return;
92
- }
93
- perf === null || perf === void 0 ? void 0 : perf.markInteraction('Flamegraph Render', (_a = flamegraphResponse === null || flamegraphResponse === void 0 ? void 0 : flamegraphResponse.report) === null || _a === void 0 ? void 0 : _a.flamegraph.total);
94
- }, [flamegraphLoading, flamegraphResponse, perf]);
95
85
  var _f = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
96
86
  skip: !dashboardItems.includes('table'),
97
87
  }), topTableLoading = _f.isLoading, topTableResponse = _f.response, topTableError = _f.error;
98
88
  var _g = useQuery(queryClient, profileSource, QueryRequest_ReportType.CALLGRAPH, {
99
89
  skip: !dashboardItems.includes('callgraph'),
100
90
  }), callgraphLoading = _g.isLoading, callgraphResponse = _g.response, callgraphError = _g.error;
91
+ useEffect(function () {
92
+ var _a, _b;
93
+ if (!flamegraphLoading && (flamegraphResponse === null || flamegraphResponse === void 0 ? void 0 : flamegraphResponse.report.oneofKind) === 'flamegraph') {
94
+ perf === null || perf === void 0 ? void 0 : perf.markInteraction('Flamegraph render', flamegraphResponse.report.flamegraph.total);
95
+ }
96
+ if (!topTableLoading && (topTableResponse === null || topTableResponse === void 0 ? void 0 : topTableResponse.report.oneofKind) === 'top') {
97
+ perf === null || perf === void 0 ? void 0 : perf.markInteraction('Top table render', (_a = topTableResponse === null || topTableResponse === void 0 ? void 0 : topTableResponse.report) === null || _a === void 0 ? void 0 : _a.top.total);
98
+ }
99
+ if (!callgraphLoading && (callgraphResponse === null || callgraphResponse === void 0 ? void 0 : callgraphResponse.report.oneofKind) === 'callgraph') {
100
+ perf === null || perf === void 0 ? void 0 : perf.markInteraction('Callgraph render', (_b = callgraphResponse === null || callgraphResponse === void 0 ? void 0 : callgraphResponse.report) === null || _b === void 0 ? void 0 : _b.callgraph.cumulative);
101
+ }
102
+ }, [
103
+ flamegraphLoading,
104
+ flamegraphResponse,
105
+ callgraphResponse,
106
+ callgraphLoading,
107
+ topTableLoading,
108
+ topTableResponse,
109
+ perf,
110
+ ]);
101
111
  var sampleUnit = profileSource.ProfileType().sampleUnit;
102
112
  var downloadPProfClick = function () { return __awaiter(void 0, void 0, void 0, function () {
103
113
  var blob, error_1;
@@ -1,2 +1,3 @@
1
+ /// <reference types="react" />
1
2
  declare const DiffLegend: () => JSX.Element;
2
3
  export default DiffLegend;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  interface Props {
2
3
  value: string;
3
4
  className?: string;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { QueryRequest, QueryServiceClient } from '@parca/client';
2
3
  interface Props {
3
4
  queryRequest: QueryRequest;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.139",
3
+ "version": "0.16.141",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
- "@parca/client": "^0.16.66",
7
- "@parca/components": "^0.16.114",
6
+ "@parca/client": "^0.16.67",
7
+ "@parca/components": "^0.16.115",
8
8
  "@parca/dynamicsize": "^0.16.54",
9
- "@parca/functions": "^0.16.67",
9
+ "@parca/functions": "^0.16.68",
10
10
  "@parca/parser": "^0.16.55",
11
- "@parca/store": "^0.16.65",
11
+ "@parca/store": "^0.16.66",
12
12
  "@types/react-beautiful-dnd": "^13.1.3",
13
13
  "d3": "7.8.2",
14
14
  "d3-scale": "^4.0.2",
@@ -45,5 +45,5 @@
45
45
  "access": "public",
46
46
  "registry": "https://registry.npmjs.org/"
47
47
  },
48
- "gitHead": "d05b976c14a943c299f3c2b5b027a72922a1ae4d"
48
+ "gitHead": "f3a97ac4b80fa76c06890f1c129b6cece3ee3455"
49
49
  }
@@ -30,6 +30,7 @@ interface Props {
30
30
  onLabelClick: (labelName: string, labelValue: string) => void;
31
31
  contextElement: Element | null;
32
32
  sampleUnit: string;
33
+ delta: boolean;
33
34
  }
34
35
 
35
36
  const virtualElement: VirtualElement = {
@@ -67,6 +68,7 @@ const MetricsTooltip = ({
67
68
  onLabelClick,
68
69
  contextElement,
69
70
  sampleUnit,
71
+ delta,
70
72
  }: Props): JSX.Element => {
71
73
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
72
74
 
@@ -122,12 +124,22 @@ const MetricsTooltip = ({
122
124
  {valueFormatter(highlighted.valuePerSecond, sampleUnit, 5)}
123
125
  </td>
124
126
  </tr>
125
- <tr>
126
- <td className="w-1/4">Total</td>
127
- <td className="w-3/4">
128
- {valueFormatter(highlighted.value, sampleUnit, 2)}
129
- </td>
130
- </tr>
127
+ {delta && (
128
+ <tr>
129
+ <td className="w-1/4">Total</td>
130
+ <td className="w-3/4">
131
+ {valueFormatter(highlighted.value, sampleUnit, 2)}
132
+ </td>
133
+ </tr>
134
+ )}
135
+ {highlighted.duration > 0 && (
136
+ <tr>
137
+ <td className="w-1/4">Duration</td>
138
+ <td className="w-3/4">
139
+ {valueFormatter(highlighted.duration, 'nanoseconds', 2)}
140
+ </td>
141
+ </tr>
142
+ )}
131
143
  <tr>
132
144
  <td className="w-1/4">At</td>
133
145
  <td className="w-3/4">{formatDate(highlighted.timestamp, timeFormat)}</td>
@@ -50,6 +50,7 @@ export interface HighlightedSeries {
50
50
  timestamp: number;
51
51
  value: number;
52
52
  valuePerSecond: number;
53
+ duration: number;
53
54
  x: number;
54
55
  y: number;
55
56
  }
@@ -137,7 +138,7 @@ export const RawMetricsGraph = ({
137
138
  values: s.samples.reduce<number[][]>(function (agg: number[][], d: MetricsSample) {
138
139
  if (d.timestamp !== undefined && d.valuePerSecond !== undefined) {
139
140
  const t = (+d.timestamp.seconds * 1e9 + d.timestamp.nanos) / 1e6; // https://github.com/microsoft/TypeScript/issues/5710#issuecomment-157886246
140
- agg.push([t, d.valuePerSecond, parseFloat(d.value)]);
141
+ agg.push([t, d.valuePerSecond, parseFloat(d.value), parseFloat(d.duration)]);
141
142
  }
142
143
  return agg;
143
144
  }, []),
@@ -205,6 +206,7 @@ export const RawMetricsGraph = ({
205
206
  timestamp: point[0],
206
207
  valuePerSecond: point[1],
207
208
  value: point[2],
209
+ duration: point[3],
208
210
  x: xScale(point[0]),
209
211
  y: yScale(point[1]),
210
212
  };
@@ -329,6 +331,7 @@ export const RawMetricsGraph = ({
329
331
  timestamp: sample[0],
330
332
  valuePerSecond: sample[1],
331
333
  value: sample[2],
334
+ duration: sample[3],
332
335
  x: xScale(sample[0]),
333
336
  y: yScale(sample[1]),
334
337
  };
@@ -350,6 +353,7 @@ export const RawMetricsGraph = ({
350
353
  onLabelClick={onLabelClick}
351
354
  contextElement={graph.current}
352
355
  sampleUnit={sampleUnit}
356
+ delta={profile !== null ? profile?.query.profType.delta : false}
353
357
  />
354
358
  </div>
355
359
  )}
@@ -95,7 +95,7 @@ const ProfileMetricsGraph = ({
95
95
  }: ProfileMetricsGraphProps): JSX.Element => {
96
96
  const {isLoading, response, error} = useQueryRange(queryClient, queryExpression, from, to);
97
97
  const isLoaderVisible = useDelayedLoader(isLoading);
98
- const {loader, onError} = useParcaContext();
98
+ const {loader, onError, perf} = useParcaContext();
99
99
 
100
100
  useEffect(() => {
101
101
  if (error !== null) {
@@ -103,6 +103,14 @@ const ProfileMetricsGraph = ({
103
103
  }
104
104
  }, [error, onError]);
105
105
 
106
+ useEffect(() => {
107
+ if (response === null) {
108
+ return;
109
+ }
110
+
111
+ perf?.markInteraction('Metrics graph render', response.series[0].samples.length);
112
+ }, [perf, response]);
113
+
106
114
  if (isLoaderVisible) {
107
115
  return <>{loader}</>;
108
116
  }
@@ -67,18 +67,6 @@ export const ProfileViewWithData = ({
67
67
  });
68
68
  const {perf} = useParcaContext();
69
69
 
70
- useEffect(() => {
71
- if (flamegraphLoading) {
72
- return;
73
- }
74
-
75
- if (flamegraphResponse?.report.oneofKind !== 'flamegraph') {
76
- return;
77
- }
78
-
79
- perf?.markInteraction('Flamegraph Render', flamegraphResponse?.report?.flamegraph.total);
80
- }, [flamegraphLoading, flamegraphResponse, perf]);
81
-
82
70
  const {
83
71
  isLoading: topTableLoading,
84
72
  response: topTableResponse,
@@ -95,6 +83,28 @@ export const ProfileViewWithData = ({
95
83
  skip: !dashboardItems.includes('callgraph'),
96
84
  });
97
85
 
86
+ useEffect(() => {
87
+ if (!flamegraphLoading && flamegraphResponse?.report.oneofKind === 'flamegraph') {
88
+ perf?.markInteraction('Flamegraph render', flamegraphResponse.report.flamegraph.total);
89
+ }
90
+
91
+ if (!topTableLoading && topTableResponse?.report.oneofKind === 'top') {
92
+ perf?.markInteraction('Top table render', topTableResponse?.report?.top.total);
93
+ }
94
+
95
+ if (!callgraphLoading && callgraphResponse?.report.oneofKind === 'callgraph') {
96
+ perf?.markInteraction('Callgraph render', callgraphResponse?.report?.callgraph.cumulative);
97
+ }
98
+ }, [
99
+ flamegraphLoading,
100
+ flamegraphResponse,
101
+ callgraphResponse,
102
+ callgraphLoading,
103
+ topTableLoading,
104
+ topTableResponse,
105
+ perf,
106
+ ]);
107
+
98
108
  const sampleUnit = profileSource.ProfileType().sampleUnit;
99
109
 
100
110
  const downloadPProfClick = async (): Promise<void> => {