@parca/profile 0.16.360 → 0.16.362

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 (48) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/Callgraph/index.d.ts +3 -2
  3. package/dist/Callgraph/index.js +2 -2
  4. package/dist/GraphTooltipArrow/Content.d.ts +3 -2
  5. package/dist/GraphTooltipArrow/Content.js +4 -4
  6. package/dist/GraphTooltipArrow/DockedGraphTooltip/index.d.ts +3 -2
  7. package/dist/GraphTooltipArrow/DockedGraphTooltip/index.js +2 -2
  8. package/dist/GraphTooltipArrow/useGraphTooltip/index.d.ts +4 -2
  9. package/dist/GraphTooltipArrow/useGraphTooltip/index.js +29 -11
  10. package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts +2 -1
  11. package/dist/ProfileIcicleGraph/IcicleGraph/index.js +2 -2
  12. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts +3 -2
  13. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +2 -2
  14. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +3 -0
  15. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +62 -9
  16. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +4 -1
  17. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +6 -3
  18. package/dist/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.d.ts +3 -1
  19. package/dist/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.js +5 -2
  20. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +1 -0
  21. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +3 -0
  22. package/dist/ProfileIcicleGraph/index.d.ts +3 -2
  23. package/dist/ProfileIcicleGraph/index.js +2 -2
  24. package/dist/ProfileView/ProfileViewContext.d.ts +0 -1
  25. package/dist/ProfileView/ProfileViewContext.js +0 -1
  26. package/dist/ProfileView/index.d.ts +1 -2
  27. package/dist/ProfileView/index.js +5 -5
  28. package/dist/ProfileViewWithData.js +1 -13
  29. package/dist/SourceView/Highlighter.js +2 -2
  30. package/dist/Table/index.d.ts +2 -1
  31. package/dist/Table/index.js +6 -6
  32. package/package.json +6 -6
  33. package/src/Callgraph/index.tsx +4 -3
  34. package/src/GraphTooltipArrow/Content.tsx +23 -4
  35. package/src/GraphTooltipArrow/DockedGraphTooltip/index.tsx +4 -3
  36. package/src/GraphTooltipArrow/useGraphTooltip/index.ts +46 -11
  37. package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +4 -3
  38. package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +4 -3
  39. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +75 -4
  40. package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +10 -5
  41. package/src/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.ts +9 -1
  42. package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +11 -0
  43. package/src/ProfileIcicleGraph/index.tsx +5 -4
  44. package/src/ProfileView/ProfileViewContext.tsx +0 -2
  45. package/src/ProfileView/index.tsx +4 -6
  46. package/src/ProfileViewWithData.tsx +0 -14
  47. package/src/SourceView/Highlighter.tsx +7 -2
  48. package/src/Table/index.tsx +10 -7
@@ -2,7 +2,6 @@ import { ReactNode } from 'react';
2
2
  import { ProfileSource } from '../ProfileSource';
3
3
  interface Props {
4
4
  profileSource?: ProfileSource;
5
- sampleUnit: string;
6
5
  compareMode: boolean;
7
6
  }
8
7
  export declare const defaultValue: Props;
@@ -14,7 +14,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
14
14
  import { createContext, useContext } from 'react';
15
15
  export const defaultValue = {
16
16
  profileSource: undefined,
17
- sampleUnit: 'bytes',
18
17
  compareMode: false,
19
18
  };
20
19
  const ProfileViewContext = createContext(defaultValue);
@@ -39,7 +39,6 @@ export interface ProfileViewProps {
39
39
  topTableData?: TopTableData;
40
40
  callgraphData?: CallgraphData;
41
41
  sourceData?: SourceData;
42
- sampleUnit: string;
43
42
  profileSource?: ProfileSource;
44
43
  queryClient?: QueryServiceClient;
45
44
  navigateTo?: NavigateFunction;
@@ -47,5 +46,5 @@ export interface ProfileViewProps {
47
46
  onDownloadPProf: () => void;
48
47
  pprofDownloading?: boolean;
49
48
  }
50
- export declare const ProfileView: ({ total, filtered, flamegraphData, topTableData, callgraphData, sourceData, sampleUnit, profileSource, queryClient, navigateTo, onDownloadPProf, pprofDownloading, compare, }: ProfileViewProps) => JSX.Element;
49
+ export declare const ProfileView: ({ total, filtered, flamegraphData, topTableData, callgraphData, sourceData, profileSource, queryClient, navigateTo, onDownloadPProf, pprofDownloading, compare, }: ProfileViewProps) => JSX.Element;
51
50
  export {};
@@ -38,7 +38,7 @@ function arrayEquals(a, b) {
38
38
  a.length === b.length &&
39
39
  a.every((val, index) => val === b[index]));
40
40
  }
41
- export const ProfileView = ({ total, filtered, flamegraphData, topTableData, callgraphData, sourceData, sampleUnit, profileSource, queryClient, navigateTo, onDownloadPProf, pprofDownloading, compare, }) => {
41
+ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, callgraphData, sourceData, profileSource, queryClient, navigateTo, onDownloadPProf, pprofDownloading, compare, }) => {
42
42
  const { timezone } = useParcaContext();
43
43
  const { ref, dimensions } = useContainerDimensions();
44
44
  const [curPath, setCurPath] = useState([]);
@@ -107,7 +107,7 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
107
107
  return (_jsx(ConditionalWrapper, { condition: perf?.onRender != null, WrapperComponent: Profiler, wrapperProps: {
108
108
  id: 'icicleGraph',
109
109
  onRender: perf?.onRender,
110
- }, children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, arrow: flamegraphData?.arrow, graph: flamegraphData?.data, total: total, filtered: filtered, sampleUnit: sampleUnit, navigateTo: navigateTo, loading: flamegraphData.loading, setActionButtons: setActionButtons, error: flamegraphData.error, isHalfScreen: isHalfScreen, width: dimensions?.width !== undefined
110
+ }, children: _jsx(ProfileIcicleGraph, { curPath: curPath, setNewCurPath: setNewCurPath, arrow: flamegraphData?.arrow, graph: flamegraphData?.data, total: total, filtered: filtered, profileType: profileSource?.ProfileType(), navigateTo: navigateTo, loading: flamegraphData.loading, setActionButtons: setActionButtons, error: flamegraphData.error, isHalfScreen: isHalfScreen, width: dimensions?.width !== undefined
111
111
  ? isHalfScreen
112
112
  ? (dimensions.width - 40) / 2
113
113
  : dimensions.width - 16
@@ -116,10 +116,10 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
116
116
  case 'callgraph': {
117
117
  return callgraphData?.data !== undefined &&
118
118
  callgraphSVG !== undefined &&
119
- dimensions?.width !== undefined ? (_jsx(Callgraph, { data: callgraphData.data, svgString: callgraphSVG, sampleUnit: sampleUnit, width: isHalfScreen ? dimensions?.width / 2 : dimensions?.width })) : (_jsx(_Fragment, {}));
119
+ dimensions?.width !== undefined ? (_jsx(Callgraph, { data: callgraphData.data, svgString: callgraphSVG, profileType: profileSource?.ProfileType(), width: isHalfScreen ? dimensions?.width / 2 : dimensions?.width })) : (_jsx(_Fragment, {}));
120
120
  }
121
121
  case 'table': {
122
- return topTableData != null ? (_jsx(Table, { total: total, filtered: filtered, loading: topTableData.loading, data: topTableData.arrow?.record, sampleUnit: sampleUnit, navigateTo: navigateTo, setActionButtons: setActionButtons, currentSearchString: currentSearchString, isHalfScreen: isHalfScreen })) : (_jsx(_Fragment, {}));
122
+ return topTableData != null ? (_jsx(Table, { total: total, filtered: filtered, loading: topTableData.loading, data: topTableData.arrow?.record, profileType: profileSource?.ProfileType(), navigateTo: navigateTo, setActionButtons: setActionButtons, currentSearchString: currentSearchString, isHalfScreen: isHalfScreen })) : (_jsx(_Fragment, {}));
123
123
  }
124
124
  case 'source': {
125
125
  return sourceData != null ? (_jsx(SourceView, { loading: sourceData.loading, data: sourceData.data, total: total, filtered: filtered, setActionButtons: setActionButtons })) : (_jsx(_Fragment, {}));
@@ -150,7 +150,7 @@ export const ProfileView = ({ total, filtered, flamegraphData, topTableData, cal
150
150
  const headerParts = profileSourceString?.split('"') ?? [];
151
151
  const compareMode = compare === true ||
152
152
  (selectQueryParam('compare_a') === 'true' && selectQueryParam('compare_b') === 'true');
153
- return (_jsx(KeyDownProvider, { children: _jsxs(ProfileViewContextProvider, { value: { profileSource, sampleUnit, compareMode }, children: [_jsxs("div", { className: cx('mb-4 flex w-full', hasProfileSource || profileViewExternalMainActions != null
153
+ return (_jsx(KeyDownProvider, { children: _jsxs(ProfileViewContextProvider, { value: { profileSource, compareMode }, children: [_jsxs("div", { className: cx('mb-4 flex w-full', hasProfileSource || profileViewExternalMainActions != null
154
154
  ? 'justify-between'
155
155
  : 'justify-end', {
156
156
  'items-end': !hasProfileSource && profileViewExternalMainActions != null,
@@ -87,18 +87,6 @@ export const ProfileViewWithData = ({ queryClient, profileSource, navigateTo, })
87
87
  sourceResponse,
88
88
  perf,
89
89
  ]);
90
- // Default to the unit of the selected profile type. This is a heuristic, and
91
- // only a fallback, the unit should be returned by the backend.
92
- let sampleUnit = profileSource.ProfileType().sampleUnit;
93
- if (flamegraphResponse?.report.oneofKind === 'flamegraphArrow') {
94
- sampleUnit = flamegraphResponse.report.flamegraphArrow.unit;
95
- }
96
- if (tableResponse?.report.oneofKind === 'tableArrow') {
97
- sampleUnit = tableResponse.report.tableArrow.unit;
98
- }
99
- if (sourceResponse?.report.oneofKind === 'source') {
100
- sampleUnit = sourceResponse.report.source.unit;
101
- }
102
90
  const downloadPProfClick = async () => {
103
91
  if (profileSource == null || queryClient == null) {
104
92
  return;
@@ -163,6 +151,6 @@ export const ProfileViewWithData = ({ queryClient, profileSource, navigateTo, })
163
151
  ? sourceResponse?.report?.source
164
152
  : undefined,
165
153
  error: sourceError,
166
- }, sampleUnit: sampleUnit, profileSource: profileSource, queryClient: queryClient, navigateTo: navigateTo, onDownloadPProf: () => void downloadPProfClick(), pprofDownloading: pprofDownloading }));
154
+ }, profileSource: profileSource, queryClient: queryClient, navigateTo: navigateTo, onDownloadPProf: () => void downloadPProfClick(), pprofDownloading: pprofDownloading }));
167
155
  };
168
156
  export default ProfileViewWithData;
@@ -45,13 +45,13 @@ const intensityScale = scaleLinear().domain([0, 99]).range([0.05, 0.75]);
45
45
  const LineProfileMetadata = ({ value, total, filtered, }) => {
46
46
  const commonClasses = 'w-[52px] shrink-0';
47
47
  const id = useId();
48
- const { sampleUnit } = useProfileViewContext();
48
+ const { profileSource } = useProfileViewContext();
49
49
  if (value === 0n) {
50
50
  return _jsx("div", { className: cx(commonClasses) });
51
51
  }
52
52
  const unfilteredPercent = (Number(value) / Number(total + filtered)) * 100;
53
53
  const filteredPercent = (Number(value) / Number(total)) * 100;
54
- const valueWithUnit = valueFormatter(value, sampleUnit, 1, true);
54
+ const valueWithUnit = valueFormatter(value, profileSource?.ProfileType().sampleUnit ?? '', 1, true);
55
55
  return (_jsxs(_Fragment, { children: [_jsx("p", { className: cx('w- flex justify-end overflow-hidden text-ellipsis whitespace-nowrap', commonClasses), style: { backgroundColor: `rgba(236, 151, 6, ${intensityScale(unfilteredPercent)})` }, "data-tooltip-id": id, "data-tooltip-content": `${valueWithUnit} (${unfilteredPercent.toFixed(2)}%${filtered > 0n ? ` / ${filteredPercent.toFixed(2)}%` : ''})`, children: valueWithUnit }), _jsx(Tooltip, { id: id })] }));
56
56
  };
57
57
  const charsToWidth = (chars) => {
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Vector } from 'apache-arrow';
3
+ import { ProfileType } from '@parca/parser';
3
4
  import { type NavigateFunction } from '@parca/utilities';
4
5
  export interface ColumnDef {
5
6
  id: string;
@@ -17,7 +18,7 @@ interface TableProps {
17
18
  data?: Uint8Array;
18
19
  total: bigint;
19
20
  filtered: bigint;
20
- sampleUnit: string;
21
+ profileType?: ProfileType;
21
22
  navigateTo?: NavigateFunction;
22
23
  loading: boolean;
23
24
  currentSearchString?: string;
@@ -28,7 +28,7 @@ const FIELD_FLAT = 'flat';
28
28
  const FIELD_FLAT_DIFF = 'flat_diff';
29
29
  const FIELD_CUMULATIVE = 'cumulative';
30
30
  const FIELD_CUMULATIVE_DIFF = 'cumulative_diff';
31
- export const Table = React.memo(function Table({ data, total, filtered, sampleUnit: unit, navigateTo, loading, currentSearchString, setActionButtons, isHalfScreen, }) {
31
+ export const Table = React.memo(function Table({ data, total, filtered, profileType, navigateTo, loading, currentSearchString, setActionButtons, isHalfScreen, }) {
32
32
  const router = parseParams(window?.location.search);
33
33
  const [rawDashboardItems] = useURLState({ param: 'dashboard_items' });
34
34
  const [filterByFunctionInput] = useURLState({ param: 'filter_by_function' });
@@ -59,7 +59,7 @@ export const Table = React.memo(function Table({ data, total, filtered, sampleUn
59
59
  id: 'flat',
60
60
  accessorKey: 'flat',
61
61
  header: 'Flat',
62
- cell: info => valueFormatter(info.getValue(), unit, 2),
62
+ cell: info => valueFormatter(info.getValue(), profileType?.sampleUnit ?? '', 2),
63
63
  size: 80,
64
64
  meta: {
65
65
  align: 'right',
@@ -81,7 +81,7 @@ export const Table = React.memo(function Table({ data, total, filtered, sampleUn
81
81
  id: 'flatDiff',
82
82
  accessorKey: 'flatDiff',
83
83
  header: 'Flat Diff',
84
- cell: info => addPlusSign(valueFormatter(info.getValue(), unit, 2)),
84
+ cell: info => addPlusSign(valueFormatter(info.getValue(), profileType?.sampleUnit ?? '', 2)),
85
85
  size: 120,
86
86
  meta: {
87
87
  align: 'right',
@@ -103,7 +103,7 @@ export const Table = React.memo(function Table({ data, total, filtered, sampleUn
103
103
  id: 'cumulative',
104
104
  accessorKey: 'cumulative',
105
105
  header: 'Cumulative',
106
- cell: info => valueFormatter(info.getValue(), unit, 2),
106
+ cell: info => valueFormatter(info.getValue(), profileType?.sampleUnit ?? '', 2),
107
107
  size: 150,
108
108
  meta: {
109
109
  align: 'right',
@@ -125,7 +125,7 @@ export const Table = React.memo(function Table({ data, total, filtered, sampleUn
125
125
  id: 'cumulativeDiff',
126
126
  accessorKey: 'cumulativeDiff',
127
127
  header: 'Cumulative Diff',
128
- cell: info => addPlusSign(valueFormatter(info.getValue(), unit, 2)),
128
+ cell: info => addPlusSign(valueFormatter(info.getValue(), profileType?.sampleUnit ?? '', 2)),
129
129
  size: 170,
130
130
  meta: {
131
131
  align: 'right',
@@ -166,7 +166,7 @@ export const Table = React.memo(function Table({ data, total, filtered, sampleUn
166
166
  },
167
167
  ];
168
168
  // eslint-disable-next-line react-hooks/exhaustive-deps
169
- }, [unit]);
169
+ }, [profileType]);
170
170
  const [columnVisibility, setColumnVisibility] = useState(() => {
171
171
  return {
172
172
  flat: true,
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.360",
3
+ "version": "0.16.362",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@parca/client": "^0.16.107",
7
- "@parca/components": "^0.16.266",
7
+ "@parca/components": "^0.16.267",
8
8
  "@parca/dynamicsize": "^0.16.61",
9
- "@parca/hooks": "^0.0.47",
9
+ "@parca/hooks": "^0.0.48",
10
10
  "@parca/parser": "^0.16.69",
11
- "@parca/store": "^0.16.136",
12
- "@parca/utilities": "^0.0.64",
11
+ "@parca/store": "^0.16.137",
12
+ "@parca/utilities": "^0.0.65",
13
13
  "@tanstack/react-query": "^4.0.5",
14
14
  "@types/react-beautiful-dnd": "^13.1.8",
15
15
  "apache-arrow": "^12.0.0",
@@ -50,5 +50,5 @@
50
50
  "access": "public",
51
51
  "registry": "https://registry.npmjs.org/"
52
52
  },
53
- "gitHead": "6430a8027b0edb84336feea5c774e409595f40a6"
53
+ "gitHead": "b9faf0a1236107753bda612b90d67c7d82e9583c"
54
54
  }
@@ -20,6 +20,7 @@ import {MapInteractionCSS} from 'react-map-interaction';
20
20
 
21
21
  import {CallgraphEdge, Callgraph as CallgraphType} from '@parca/client';
22
22
  import {Button, useKeyDown, useURLState} from '@parca/components';
23
+ import {ProfileType} from '@parca/parser';
23
24
  import {selectDarkMode, setHoveringNode, useAppDispatch, useAppSelector} from '@parca/store';
24
25
  import {getNewSpanColor} from '@parca/utilities';
25
26
 
@@ -28,7 +29,7 @@ import GraphTooltip from '../GraphTooltip';
28
29
  export interface Props {
29
30
  data: CallgraphType;
30
31
  svgString: string;
31
- sampleUnit: string;
32
+ profileType: ProfileType | undefined;
32
33
  width: number;
33
34
  }
34
35
 
@@ -37,7 +38,7 @@ interface View {
37
38
  translation: {x: number; y: number};
38
39
  }
39
40
 
40
- const Callgraph = ({data, svgString, sampleUnit, width}: Props): JSX.Element => {
41
+ const Callgraph = ({data, svgString, profileType, width}: Props): JSX.Element => {
41
42
  const originalView = {
42
43
  scale: 1,
43
44
  translation: {x: 0, y: 0},
@@ -147,7 +148,7 @@ const Callgraph = ({data, svgString, sampleUnit, width}: Props): JSX.Element =>
147
148
  {svgRef.current !== null && (
148
149
  <GraphTooltip
149
150
  type="callgraph"
150
- unit={sampleUnit}
151
+ unit={profileType?.sampleUnit ?? ''}
151
152
  total={data.cumulative}
152
153
  totalUnfiltered={data.cumulative}
153
154
  contextElement={containerRef.current}
@@ -16,6 +16,7 @@ import React from 'react';
16
16
  import {Icon} from '@iconify/react';
17
17
  import {Table} from 'apache-arrow';
18
18
 
19
+ import {ProfileType} from '@parca/parser';
19
20
  import {getLastItem, type NavigateFunction} from '@parca/utilities';
20
21
 
21
22
  import {hexifyAddress, truncateString, truncateStringReverse} from '../utils';
@@ -25,7 +26,7 @@ import {useGraphTooltipMetaInfo} from './useGraphTooltipMetaInfo';
25
26
 
26
27
  interface GraphTooltipArrowContentProps {
27
28
  table: Table<any>;
28
- unit: string;
29
+ profileType?: ProfileType;
29
30
  total: bigint;
30
31
  totalUnfiltered: bigint;
31
32
  row: number | null;
@@ -40,7 +41,7 @@ const NoData = (): React.JSX.Element => {
40
41
 
41
42
  const GraphTooltipArrowContent = ({
42
43
  table,
43
- unit,
44
+ profileType,
44
45
  total,
45
46
  totalUnfiltered,
46
47
  row,
@@ -50,7 +51,7 @@ const GraphTooltipArrowContent = ({
50
51
  }: GraphTooltipArrowContentProps): React.JSX.Element => {
51
52
  const graphTooltipData = useGraphTooltip({
52
53
  table,
53
- unit,
54
+ profileType,
54
55
  total,
55
56
  totalUnfiltered,
56
57
  row,
@@ -61,7 +62,15 @@ const GraphTooltipArrowContent = ({
61
62
  return <></>;
62
63
  }
63
64
 
64
- const {name, locationAddress, cumulativeText, diffText, diff, row: rowNumber} = graphTooltipData;
65
+ const {
66
+ name,
67
+ locationAddress,
68
+ cumulativeText,
69
+ cumulativePerSecondText,
70
+ diffText,
71
+ diff,
72
+ row: rowNumber,
73
+ } = graphTooltipData;
65
74
 
66
75
  return (
67
76
  <div className={`flex text-sm ${isFixed ? 'w-full' : ''}`}>
@@ -84,6 +93,16 @@ const GraphTooltipArrowContent = ({
84
93
  </div>
85
94
  <table className="my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300">
86
95
  <tbody>
96
+ {profileType?.delta ?? false ? (
97
+ <tr>
98
+ <td className="w-1/4">Per Second</td>
99
+ <td className="w-3/4">
100
+ <div>{cumulativePerSecondText}</div>
101
+ </td>
102
+ </tr>
103
+ ) : (
104
+ <></>
105
+ )}
87
106
  <tr>
88
107
  <td className="w-1/4">Cumulative</td>
89
108
 
@@ -17,6 +17,7 @@ import cx from 'classnames';
17
17
  import {useWindowSize} from 'react-use';
18
18
 
19
19
  import {useParcaContext} from '@parca/components';
20
+ import {ProfileType} from '@parca/parser';
20
21
  import {getLastItem} from '@parca/utilities';
21
22
 
22
23
  import {hexifyAddress, truncateString, truncateStringReverse} from '../../utils';
@@ -25,11 +26,11 @@ import {useGraphTooltipMetaInfo} from '../useGraphTooltipMetaInfo';
25
26
 
26
27
  interface Props {
27
28
  table: Table<any>;
28
- unit: string;
29
29
  total: bigint;
30
30
  totalUnfiltered: bigint;
31
31
  row: number | null;
32
32
  level: number;
33
+ profileType?: ProfileType;
33
34
  }
34
35
 
35
36
  const InfoSection = ({
@@ -55,11 +56,11 @@ const NoData = (): React.JSX.Element => {
55
56
 
56
57
  export const DockedGraphTooltip = ({
57
58
  table,
58
- unit,
59
59
  total,
60
60
  totalUnfiltered,
61
61
  row,
62
62
  level,
63
+ profileType,
63
64
  }: Props): JSX.Element => {
64
65
  let {width} = useWindowSize();
65
66
  const {profileExplorer, navigateTo} = useParcaContext();
@@ -68,7 +69,7 @@ export const DockedGraphTooltip = ({
68
69
 
69
70
  const graphTooltipData = useGraphTooltip({
70
71
  table,
71
- unit,
72
+ profileType,
72
73
  total,
73
74
  totalUnfiltered,
74
75
  row,
@@ -13,18 +13,25 @@
13
13
 
14
14
  import {Table} from 'apache-arrow';
15
15
 
16
+ import {ProfileType} from '@parca/parser';
16
17
  import {divide, valueFormatter} from '@parca/utilities';
17
18
 
18
19
  import {
19
20
  FIELD_CUMULATIVE,
21
+ FIELD_CUMULATIVE_PER_SECOND,
20
22
  FIELD_DIFF,
23
+ FIELD_DIFF_PER_SECOND,
21
24
  FIELD_LOCATION_ADDRESS,
22
25
  } from '../../ProfileIcicleGraph/IcicleGraphArrow';
23
- import {getTextForCumulative, nodeLabel} from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
26
+ import {
27
+ getTextForCumulative,
28
+ getTextForCumulativePerSecond,
29
+ nodeLabel,
30
+ } from '../../ProfileIcicleGraph/IcicleGraphArrow/utils';
24
31
 
25
32
  interface Props {
26
33
  table: Table<any>;
27
- unit: string;
34
+ profileType?: ProfileType;
28
35
  total: bigint;
29
36
  totalUnfiltered: bigint;
30
37
  row: number | null;
@@ -35,6 +42,7 @@ interface GraphTooltipData {
35
42
  name: string;
36
43
  locationAddress: bigint;
37
44
  cumulativeText: string;
45
+ cumulativePerSecondText: string;
38
46
  diffText: string;
39
47
  diff: bigint;
40
48
  row: number;
@@ -42,13 +50,13 @@ interface GraphTooltipData {
42
50
 
43
51
  export const useGraphTooltip = ({
44
52
  table,
45
- unit,
53
+ profileType,
46
54
  total,
47
55
  totalUnfiltered,
48
56
  row,
49
57
  level,
50
58
  }: Props): GraphTooltipData | null => {
51
- if (row === null) {
59
+ if (row === null || profileType === undefined) {
52
60
  return null;
53
61
  }
54
62
 
@@ -58,24 +66,51 @@ export const useGraphTooltip = ({
58
66
  table.getChild(FIELD_CUMULATIVE)?.get(row) !== null
59
67
  ? BigInt(table.getChild(FIELD_CUMULATIVE)?.get(row))
60
68
  : 0n;
69
+ const cumulativePerSecond: number =
70
+ table.getChild(FIELD_CUMULATIVE_PER_SECOND)?.get(row) !== null
71
+ ? table.getChild(FIELD_CUMULATIVE_PER_SECOND)?.get(row)
72
+ : 0;
61
73
  const diff: bigint =
62
74
  table.getChild(FIELD_DIFF)?.get(row) !== null
63
75
  ? BigInt(table.getChild(FIELD_DIFF)?.get(row))
64
76
  : 0n;
77
+ const diffPerSecond: number =
78
+ table.getChild(FIELD_DIFF_PER_SECOND)?.get(row) !== null
79
+ ? table.getChild(FIELD_DIFF_PER_SECOND)?.get(row)
80
+ : 0;
65
81
 
66
- const prevValue = cumulative - diff;
67
- const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
68
- const diffSign = diff > 0 ? '+' : '';
69
- const diffValueText = diffSign + valueFormatter(diff, unit, 1);
70
- const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
71
- const diffText = `${diffValueText} (${diffPercentageText})`;
82
+ let diffText = '';
83
+ if (profileType?.delta ?? false) {
84
+ const prevValue = cumulativePerSecond - diffPerSecond;
85
+ const diffRatio = diffPerSecond !== 0 ? diffPerSecond / prevValue : 0;
86
+ const diffSign = diffPerSecond > 0 ? '+' : '';
87
+ const diffValueText = diffSign + valueFormatter(diffPerSecond, 'CPU Cores', 5);
88
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
89
+ diffText = `${diffValueText} (${diffPercentageText})`;
90
+ } else {
91
+ const prevValue = cumulative - diff;
92
+ const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
93
+ const diffSign = diff > 0 ? '+' : '';
94
+ const diffValueText = diffSign + valueFormatter(diff, profileType?.sampleUnit ?? '', 1);
95
+ const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
96
+ diffText = `${diffValueText} (${diffPercentageText})`;
97
+ }
72
98
 
73
99
  const name = nodeLabel(table, row, level, false);
74
100
 
75
101
  return {
76
102
  name,
77
103
  locationAddress,
78
- cumulativeText: getTextForCumulative(cumulative, totalUnfiltered, total, unit),
104
+ cumulativeText: getTextForCumulative(
105
+ cumulative,
106
+ totalUnfiltered,
107
+ total,
108
+ profileType?.periodUnit ?? ''
109
+ ),
110
+ cumulativePerSecondText: getTextForCumulativePerSecond(
111
+ cumulativePerSecond,
112
+ profileType?.periodUnit ?? 'CPU Cores'
113
+ ),
79
114
  diffText,
80
115
  diff,
81
116
  row,
@@ -14,6 +14,7 @@
14
14
  import {memo, useEffect, useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import {Flamegraph} from '@parca/client';
17
+ import {ProfileType} from '@parca/parser';
17
18
  import {setHoveringNode, useAppDispatch} from '@parca/store';
18
19
  import {scaleLinear, selectQueryParam, type NavigateFunction} from '@parca/utilities';
19
20
 
@@ -27,7 +28,7 @@ interface IcicleGraphProps {
27
28
  graph: Flamegraph;
28
29
  total: bigint;
29
30
  filtered: bigint;
30
- sampleUnit: string;
31
+ profileType?: ProfileType;
31
32
  width?: number;
32
33
  curPath: string[];
33
34
  setCurPath: (path: string[]) => void;
@@ -41,7 +42,7 @@ export const IcicleGraph = memo(function IcicleGraph({
41
42
  width,
42
43
  setCurPath,
43
44
  curPath,
44
- sampleUnit,
45
+ profileType,
45
46
  navigateTo,
46
47
  }: IcicleGraphProps): JSX.Element {
47
48
  const dispatch = useAppDispatch();
@@ -77,7 +78,7 @@ export const IcicleGraph = memo(function IcicleGraph({
77
78
  <ColorStackLegend navigateTo={navigateTo} compareMode={compareMode} />
78
79
  )}
79
80
  <GraphTooltip
80
- unit={sampleUnit}
81
+ unit={profileType?.sampleUnit ?? ''}
81
82
  total={total}
82
83
  totalUnfiltered={total + filtered}
83
84
  contextElement={svg.current}
@@ -18,6 +18,7 @@ import {Tooltip} from 'react-tooltip';
18
18
 
19
19
  import {useParcaContext} from '@parca/components';
20
20
  import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
21
+ import {ProfileType} from '@parca/parser';
21
22
  import {type NavigateFunction} from '@parca/utilities';
22
23
 
23
24
  import {useGraphTooltip} from '../../GraphTooltipArrow/useGraphTooltip';
@@ -27,7 +28,7 @@ import {hexifyAddress, truncateString} from '../../utils';
27
28
  interface ContextMenuProps {
28
29
  menuId: string;
29
30
  table: Table<any>;
30
- unit: string;
31
+ profileType?: ProfileType;
31
32
  total: bigint;
32
33
  totalUnfiltered: bigint;
33
34
  row: number;
@@ -42,7 +43,6 @@ interface ContextMenuProps {
42
43
  const ContextMenu = ({
43
44
  menuId,
44
45
  table,
45
- unit,
46
46
  total,
47
47
  totalUnfiltered,
48
48
  row,
@@ -52,6 +52,7 @@ const ContextMenu = ({
52
52
  curPath,
53
53
  setCurPath,
54
54
  hideMenu,
55
+ profileType,
55
56
  }: ContextMenuProps): JSX.Element => {
56
57
  const {isDarkMode} = useParcaContext();
57
58
  const {enableSourcesView} = useParcaContext();
@@ -60,7 +61,7 @@ const ContextMenu = ({
60
61
  );
61
62
  const contextMenuData = useGraphTooltip({
62
63
  table,
63
- unit,
64
+ profileType,
64
65
  total,
65
66
  totalUnfiltered,
66
67
  row,