@parca/profile 0.16.456 → 0.16.458

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 (59) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MetricsGraph/MetricsTooltip/index.js +1 -1
  3. package/dist/MetricsGraphStrips/index.d.ts +1 -0
  4. package/dist/MetricsGraphStrips/index.d.ts.map +1 -1
  5. package/dist/MetricsGraphStrips/index.js +3 -3
  6. package/dist/ProfileIcicleGraph/IcicleGraph/index.js +1 -1
  7. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.d.ts +9 -0
  8. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.d.ts.map +1 -0
  9. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.js +51 -0
  10. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +10 -1
  11. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
  12. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +2 -2
  13. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +5 -0
  14. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
  15. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +9 -1
  16. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +3 -0
  17. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts.map +1 -1
  18. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +18 -0
  19. package/dist/ProfileIcicleGraph/index.d.ts +4 -1
  20. package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
  21. package/dist/ProfileIcicleGraph/index.js +6 -4
  22. package/dist/ProfileSelector/QueryControls.d.ts.map +1 -1
  23. package/dist/ProfileSelector/QueryControls.js +6 -6
  24. package/dist/ProfileView/components/DashboardItems/index.d.ts +2 -1
  25. package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
  26. package/dist/ProfileView/components/DashboardItems/index.js +7 -1
  27. package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
  28. package/dist/ProfileView/components/ViewSelector/index.js +4 -1
  29. package/dist/ProfileView/index.d.ts +1 -1
  30. package/dist/ProfileView/index.d.ts.map +1 -1
  31. package/dist/ProfileView/index.js +3 -2
  32. package/dist/ProfileView/types/visualization.d.ts +2 -4
  33. package/dist/ProfileView/types/visualization.d.ts.map +1 -1
  34. package/dist/ProfileViewWithData.d.ts +1 -4
  35. package/dist/ProfileViewWithData.d.ts.map +1 -1
  36. package/dist/ProfileViewWithData.js +30 -7
  37. package/dist/TimelineGuide/index.d.ts +4 -3
  38. package/dist/TimelineGuide/index.d.ts.map +1 -1
  39. package/dist/TimelineGuide/index.js +9 -7
  40. package/dist/styles.css +1 -1
  41. package/dist/utils.d.ts +1 -0
  42. package/dist/utils.d.ts.map +1 -1
  43. package/package.json +7 -7
  44. package/src/MetricsGraph/MetricsTooltip/index.tsx +2 -2
  45. package/src/MetricsGraphStrips/index.tsx +3 -3
  46. package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +1 -1
  47. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.tsx +157 -0
  48. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +3 -3
  49. package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +58 -0
  50. package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +29 -1
  51. package/src/ProfileIcicleGraph/index.tsx +16 -6
  52. package/src/ProfileSelector/QueryControls.tsx +5 -4
  53. package/src/ProfileView/components/DashboardItems/index.tsx +27 -0
  54. package/src/ProfileView/components/ViewSelector/index.tsx +5 -1
  55. package/src/ProfileView/index.tsx +3 -3
  56. package/src/ProfileView/types/visualization.ts +2 -4
  57. package/src/ProfileViewWithData.tsx +36 -11
  58. package/src/TimelineGuide/index.tsx +41 -31
  59. package/src/utils.ts +1 -0
@@ -32,8 +32,10 @@ import {getLastItem, scaleLinear, type ColorConfig} from '@parca/utilities';
32
32
  import GraphTooltipArrow from '../../GraphTooltipArrow';
33
33
  import GraphTooltipArrowContent from '../../GraphTooltipArrow/Content';
34
34
  import {DockedGraphTooltip} from '../../GraphTooltipArrow/DockedGraphTooltip';
35
+ import {ProfileSource} from '../../ProfileSource';
35
36
  import {useProfileViewContext} from '../../ProfileView/context/ProfileViewContext';
36
37
  import ContextMenu from './ContextMenu';
38
+ import {IcicleChartRootNode} from './IcicleChartRootNode';
37
39
  import {IcicleNode, RowHeight, colorByColors} from './IcicleGraphNodes';
38
40
  import {useFilenamesList} from './useMappingList';
39
41
  import {arrowToString, extractFeature, extractFilenameFeature} from './utils';
@@ -45,6 +47,8 @@ export const FIELD_LOCATION_ADDRESS = 'location_address';
45
47
  export const FIELD_LOCATION_LINE = 'location_line';
46
48
  export const FIELD_INLINED = 'inlined';
47
49
  export const FIELD_TIMESTAMP = 'timestamp';
50
+ export const FIELD_DURATION = 'duration';
51
+ export const FIELD_GROUPBY_METADATA = 'groupby_metadata';
48
52
  export const FIELD_FUNCTION_NAME = 'function_name';
49
53
  export const FIELD_FUNCTION_SYSTEM_NAME = 'function_system_name';
50
54
  export const FIELD_FUNCTION_FILE_NAME = 'function_file_name';
@@ -60,6 +64,7 @@ interface IcicleGraphArrowProps {
60
64
  total: bigint;
61
65
  filtered: bigint;
62
66
  profileType?: ProfileType;
67
+ profileSource?: ProfileSource;
63
68
  width?: number;
64
69
  curPath: string[];
65
70
  setCurPath: (path: string[]) => void;
@@ -68,6 +73,7 @@ interface IcicleGraphArrowProps {
68
73
  isHalfScreen: boolean;
69
74
  mappingsListFromMetadata: string[];
70
75
  compareAbsolute: boolean;
76
+ isIcicleChart?: boolean;
71
77
  }
72
78
 
73
79
  export const getMappingColors = (
@@ -108,10 +114,12 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
108
114
  setCurPath,
109
115
  curPath,
110
116
  profileType,
117
+ profileSource,
111
118
  sortBy,
112
119
  flamegraphLoading,
113
120
  mappingsListFromMetadata,
114
121
  compareAbsolute,
122
+ isIcicleChart = false,
115
123
  }: IcicleGraphArrowProps): React.JSX.Element {
116
124
  const [isContextMenuOpen, setIsContextMenuOpen] = useState<boolean>(false);
117
125
  const dispatch = useAppDispatch();
@@ -252,6 +260,54 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
252
260
 
253
261
  // useMemo for the root graph as it otherwise renders the whole graph if the hoveringRow changes.
254
262
  const root = useMemo(() => {
263
+ if (isIcicleChart) {
264
+ return (
265
+ <svg
266
+ className="font-robotoMono"
267
+ width={width}
268
+ height={height}
269
+ preserveAspectRatio="xMinYMid"
270
+ ref={svg}
271
+ onContextMenu={displayMenu}
272
+ >
273
+ <g ref={ref}>
274
+ <g transform={'translate(0, 0)'}>
275
+ <IcicleChartRootNode
276
+ table={table}
277
+ row={0}
278
+ colors={colorByColors}
279
+ colorBy={colorByValue}
280
+ x={0}
281
+ y={0}
282
+ totalWidth={width ?? 1}
283
+ height={RowHeight}
284
+ setCurPath={setCurPath}
285
+ curPath={curPath}
286
+ total={total}
287
+ xScale={xScale}
288
+ path={path}
289
+ level={0}
290
+ isRoot={true}
291
+ searchString={(currentSearchString as string) ?? ''}
292
+ setHoveringRow={setHoveringRow}
293
+ setHoveringLevel={setHoveringLevel}
294
+ sortBy={sortBy}
295
+ darkMode={isDarkMode}
296
+ compareMode={compareMode}
297
+ profileType={profileType}
298
+ isContextMenuOpen={isContextMenuOpen}
299
+ hoveringName={highlightSimilarStacksName}
300
+ setHoveringName={highlightSimilarStacksSetName}
301
+ hoveringRow={highlightSimilarStacksRow}
302
+ colorForSimilarNodes={colorForSimilarNodes}
303
+ highlightSimilarStacksPreference={highlightSimilarStacksPreference}
304
+ profileSource={profileSource}
305
+ />
306
+ </g>
307
+ </g>
308
+ </svg>
309
+ );
310
+ }
255
311
  return (
256
312
  <svg
257
313
  className="font-robotoMono"
@@ -320,6 +376,8 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({
320
376
  highlightSimilarStacksPreference,
321
377
  path,
322
378
  highlightSimilarStacksSetName,
379
+ isIcicleChart,
380
+ profileSource,
323
381
  ]);
324
382
 
325
383
  return (
@@ -22,7 +22,8 @@ import {
22
22
  } from '@parca/store';
23
23
  import {divide, getLastItem, valueFormatter} from '@parca/utilities';
24
24
 
25
- import {hexifyAddress} from '../../utils';
25
+ import {MergedProfileSource, ProfileSource} from '../../ProfileSource';
26
+ import {BigIntDuo, hexifyAddress} from '../../utils';
26
27
  import {
27
28
  FIELD_FUNCTION_NAME,
28
29
  FIELD_LABELS_ONLY,
@@ -121,3 +122,30 @@ export const arrowToString = (buffer: any): string | null => {
121
122
  }
122
123
  return '';
123
124
  };
125
+
126
+ export const boundsFromProfileSource = (profileSource?: ProfileSource): BigIntDuo => {
127
+ if (profileSource === undefined) {
128
+ return [0n, 1n];
129
+ }
130
+
131
+ if (!(profileSource instanceof MergedProfileSource)) {
132
+ return [0n, 1n];
133
+ }
134
+
135
+ const request = profileSource.QueryRequest();
136
+
137
+ if (
138
+ request.options.oneofKind !== 'merge' ||
139
+ request.options.merge.start === undefined ||
140
+ request.options.merge.end === undefined
141
+ ) {
142
+ return [0n, 1n];
143
+ }
144
+
145
+ const start =
146
+ request.options.merge.start.seconds * 1000000000n + BigInt(request.options.merge.start.nanos);
147
+ const end =
148
+ request.options.merge.end.seconds * 1000000000n + BigInt(request.options.merge.end.nanos);
149
+
150
+ return [start, end];
151
+ };
@@ -20,12 +20,14 @@ import {IcicleGraphSkeleton, useParcaContext, useURLState} from '@parca/componen
20
20
  import {ProfileType} from '@parca/parser';
21
21
  import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
22
22
 
23
+ import {ProfileSource} from '../ProfileSource';
23
24
  import DiffLegend from '../ProfileView/components/DiffLegend';
24
25
  import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
25
26
  import {TimelineGuide} from '../TimelineGuide';
26
27
  import {IcicleGraph} from './IcicleGraph';
27
28
  import {FIELD_FUNCTION_NAME, IcicleGraphArrow} from './IcicleGraphArrow';
28
29
  import useMappingList from './IcicleGraphArrow/useMappingList';
30
+ import {boundsFromProfileSource} from './IcicleGraphArrow/utils';
29
31
 
30
32
  const numberFormatter = new Intl.NumberFormat('en-US');
31
33
 
@@ -38,6 +40,7 @@ interface ProfileIcicleGraphProps {
38
40
  total: bigint;
39
41
  filtered: bigint;
40
42
  profileType?: ProfileType;
43
+ profileSource?: ProfileSource;
41
44
  curPath: string[] | [];
42
45
  setNewCurPath: (path: string[]) => void;
43
46
  loading: boolean;
@@ -46,6 +49,7 @@ interface ProfileIcicleGraphProps {
46
49
  isHalfScreen: boolean;
47
50
  metadataMappingFiles?: string[];
48
51
  metadataLoading?: boolean;
52
+ isIcicleChart?: boolean;
49
53
  }
50
54
 
51
55
  const ErrorContent = ({errorMessage}: {errorMessage: string}): JSX.Element => {
@@ -65,9 +69,11 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
65
69
  width,
66
70
  isHalfScreen,
67
71
  metadataMappingFiles,
72
+ isIcicleChart = false,
73
+ profileSource,
68
74
  }: ProfileIcicleGraphProps): JSX.Element {
69
75
  const {onError, authenticationErrorMessage, isDarkMode} = useParcaContext();
70
- const {compareMode, timelineGuide} = useProfileViewContext();
76
+ const {compareMode} = useProfileViewContext();
71
77
  const [isLoading, setIsLoading] = useState<boolean>(true);
72
78
 
73
79
  const mappingsList = useMappingList(metadataMappingFiles);
@@ -167,15 +173,16 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
167
173
  if (arrow !== undefined)
168
174
  return (
169
175
  <div className="relative">
170
- {timelineGuide?.show === true && (
176
+ {isIcicleChart ? (
171
177
  <TimelineGuide
172
- bounds={timelineGuide.props.bounds}
178
+ bounds={boundsFromProfileSource(profileSource)}
173
179
  width={width}
174
180
  height={1000}
175
181
  margin={0}
176
- ticks={60000 / 10000}
182
+ ticks={12}
183
+ timeUnit="nanoseconds"
177
184
  />
178
- )}
185
+ ) : null}
179
186
  <IcicleGraphArrow
180
187
  width={width}
181
188
  arrow={arrow}
@@ -189,6 +196,8 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
189
196
  isHalfScreen={isHalfScreen}
190
197
  mappingsListFromMetadata={mappingsList}
191
198
  compareAbsolute={isCompareAbsolute}
199
+ isIcicleChart={isIcicleChart}
200
+ profileSource={profileSource}
192
201
  />
193
202
  </div>
194
203
  );
@@ -208,7 +217,8 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
208
217
  isDarkMode,
209
218
  mappingsList,
210
219
  isCompareAbsolute,
211
- timelineGuide,
220
+ isIcicleChart,
221
+ profileSource,
212
222
  ]);
213
223
 
214
224
  if (error != null) {
@@ -16,7 +16,7 @@ import {RpcError} from '@protobuf-ts/runtime-rpc';
16
16
  import Select, {type SelectInstance} from 'react-select';
17
17
 
18
18
  import {ProfileTypesResponse, QueryServiceClient} from '@parca/client';
19
- import {Button, ButtonGroup, DateTimeRange, DateTimeRangePicker} from '@parca/components';
19
+ import {Button, DateTimeRange, DateTimeRangePicker} from '@parca/components';
20
20
  import {ProfileType, Query} from '@parca/parser';
21
21
 
22
22
  import MatchersInput from '../MatchersInput';
@@ -90,7 +90,7 @@ export function QueryControls({
90
90
  profileTypesError,
91
91
  }: QueryControlsProps): JSX.Element {
92
92
  return (
93
- <div className="flex w-full flex-wrap items-end gap-2">
93
+ <div className="flex w-full flex-wrap items-start gap-2">
94
94
  {showProfileTypeSelector && (
95
95
  <div>
96
96
  <label className="text-xs">Profile type</label>
@@ -216,7 +216,8 @@ export function QueryControls({
216
216
 
217
217
  <DateTimeRangePicker onRangeSelection={setTimeRangeSelection} range={timeRangeSelection} />
218
218
 
219
- <ButtonGroup>
219
+ <div>
220
+ <label className="text-xs">&nbsp;</label>
220
221
  <Button
221
222
  disabled={searchDisabled}
222
223
  onClick={(e: React.MouseEvent<HTMLElement>) => {
@@ -227,7 +228,7 @@ export function QueryControls({
227
228
  >
228
229
  Search
229
230
  </Button>
230
- </ButtonGroup>
231
+ </div>
231
232
  </div>
232
233
  );
233
234
  }
@@ -33,6 +33,7 @@ interface GetDashboardItemProps {
33
33
  isHalfScreen: boolean;
34
34
  dimensions: DOMRect | undefined;
35
35
  flamegraphData: FlamegraphData;
36
+ flamechartData: FlamegraphData;
36
37
  topTableData?: TopTableData;
37
38
  callgraphData?: CallgraphData;
38
39
  sourceData?: SourceData;
@@ -54,6 +55,7 @@ export const getDashboardItem = ({
54
55
  isHalfScreen,
55
56
  dimensions,
56
57
  flamegraphData,
58
+ flamechartData,
57
59
  topTableData,
58
60
  callgraphData,
59
61
  sourceData,
@@ -101,6 +103,31 @@ export const getDashboardItem = ({
101
103
  />
102
104
  </ConditionalWrapper>
103
105
  );
106
+ case 'iciclechart':
107
+ return (
108
+ <ProfileIcicleGraph
109
+ curPath={curPath}
110
+ setNewCurPath={setNewCurPath}
111
+ arrow={flamechartData?.arrow}
112
+ total={total}
113
+ filtered={filtered}
114
+ profileType={profileSource?.ProfileType()}
115
+ loading={flamechartData.loading}
116
+ error={flamechartData.error}
117
+ isHalfScreen={isHalfScreen}
118
+ width={
119
+ dimensions?.width !== undefined
120
+ ? isHalfScreen
121
+ ? (dimensions.width - 54) / 2
122
+ : dimensions.width - 16
123
+ : 0
124
+ }
125
+ metadataMappingFiles={flamechartData.metadataMappingFiles}
126
+ metadataLoading={flamechartData.metadataLoading}
127
+ profileSource={profileSource}
128
+ isIcicleChart={true}
129
+ />
130
+ );
104
131
  case 'callgraph':
105
132
  return callgraphData?.data !== undefined &&
106
133
  callgraphSVG !== undefined &&
@@ -22,12 +22,16 @@ const ViewSelector = (): JSX.Element => {
22
22
  alwaysReturnArray: true,
23
23
  }
24
24
  );
25
- const {enableSourcesView} = useParcaContext();
25
+ const {enableSourcesView, enableIciclechartView} = useParcaContext();
26
26
 
27
27
  const allItems: Array<{key: string; canBeSelected: boolean; supportingText?: string}> = [
28
28
  {key: 'table', canBeSelected: !dashboardItems.includes('table')},
29
29
  {key: 'icicle', canBeSelected: !dashboardItems.includes('icicle')},
30
30
  ];
31
+ if (enableIciclechartView === true) {
32
+ allItems.push({key: 'iciclechart', canBeSelected: !dashboardItems.includes('iciclechart')});
33
+ }
34
+
31
35
  if (enableSourcesView === true) {
32
36
  allItems.push({key: 'source', canBeSelected: false});
33
37
  }
@@ -33,6 +33,7 @@ export const ProfileView = ({
33
33
  total,
34
34
  filtered,
35
35
  flamegraphData,
36
+ flamechartData,
36
37
  topTableData,
37
38
  callgraphData,
38
39
  sourceData,
@@ -42,7 +43,6 @@ export const ProfileView = ({
42
43
  pprofDownloading,
43
44
  compare,
44
45
  showVisualizationSelector,
45
- timelineGuide,
46
46
  }: ProfileViewProps): JSX.Element => {
47
47
  const {
48
48
  timezone,
@@ -98,6 +98,7 @@ export const ProfileView = ({
98
98
  isHalfScreen,
99
99
  dimensions,
100
100
  flamegraphData,
101
+ flamechartData,
101
102
  topTableData,
102
103
  callgraphData,
103
104
  sourceData,
@@ -130,14 +131,13 @@ export const ProfileView = ({
130
131
 
131
132
  return (
132
133
  <KeyDownProvider>
133
- <ProfileViewContextProvider value={{profileSource, compareMode, timelineGuide}}>
134
+ <ProfileViewContextProvider value={{profileSource, compareMode}}>
134
135
  <DashboardProvider>
135
136
  <ProfileHeader
136
137
  profileSourceString={profileSource?.toString(timezone)}
137
138
  hasProfileSource={hasProfileSource}
138
139
  externalMainActions={profileViewExternalMainActions}
139
140
  />
140
-
141
141
  <VisualisationToolbar
142
142
  groupBy={groupBy}
143
143
  toggleGroupBy={toggleGroupBy}
@@ -21,7 +21,6 @@ import {
21
21
  } from '@parca/client';
22
22
 
23
23
  import {ProfileSource} from '../../ProfileSource';
24
- import {TimelineGuideData} from '../context/ProfileViewContext';
25
24
 
26
25
  export interface FlamegraphData {
27
26
  loading: boolean;
@@ -58,12 +57,13 @@ export interface SourceData {
58
57
  error?: any;
59
58
  }
60
59
 
61
- export type VisualizationType = 'icicle' | 'callgraph' | 'table' | 'source';
60
+ export type VisualizationType = 'icicle' | 'callgraph' | 'table' | 'source' | 'iciclechart';
62
61
 
63
62
  export interface ProfileViewProps {
64
63
  total: bigint;
65
64
  filtered: bigint;
66
65
  flamegraphData: FlamegraphData;
66
+ flamechartData: FlamegraphData;
67
67
  topTableData?: TopTableData;
68
68
  callgraphData?: CallgraphData;
69
69
  sourceData?: SourceData;
@@ -73,6 +73,4 @@ export interface ProfileViewProps {
73
73
  onDownloadPProf: () => void;
74
74
  pprofDownloading?: boolean;
75
75
  showVisualizationSelector?: boolean;
76
- showTimelineGuide?: boolean;
77
- timelineGuide?: TimelineGuideData;
78
76
  }
@@ -17,10 +17,9 @@ import {QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
17
17
  import {useGrpcMetadata, useParcaContext, useURLState} from '@parca/components';
18
18
  import {saveAsBlob} from '@parca/utilities';
19
19
 
20
- import {FIELD_FUNCTION_NAME, FIELD_TIMESTAMP} from './ProfileIcicleGraph/IcicleGraphArrow';
20
+ import {FIELD_FUNCTION_NAME} from './ProfileIcicleGraph/IcicleGraphArrow';
21
21
  import {ProfileSource} from './ProfileSource';
22
22
  import {ProfileView} from './ProfileView';
23
- import {TimelineGuideData} from './ProfileView/context/ProfileViewContext';
24
23
  import {useQuery} from './useQuery';
25
24
  import {downloadPprof} from './utils';
26
25
 
@@ -29,16 +28,12 @@ interface ProfileViewWithDataProps {
29
28
  profileSource: ProfileSource;
30
29
  compare?: boolean;
31
30
  showVisualizationSelector?: boolean;
32
- isGroupByTimestamp?: boolean;
33
- timelineGuide?: TimelineGuideData;
34
31
  }
35
32
 
36
33
  export const ProfileViewWithData = ({
37
34
  queryClient,
38
35
  profileSource,
39
36
  showVisualizationSelector,
40
- isGroupByTimestamp,
41
- timelineGuide,
42
37
  }: ProfileViewWithDataProps): JSX.Element => {
43
38
  const metadata = useGrpcMetadata();
44
39
  const [dashboardItems] = useURLState<string[]>('dashboard_items', {
@@ -47,10 +42,7 @@ export const ProfileViewWithData = ({
47
42
  const [sourceBuildID] = useURLState<string>('source_buildid');
48
43
  const [sourceFilename] = useURLState<string>('source_filename');
49
44
  const [groupBy] = useURLState<string[]>('group_by', {
50
- defaultValue: [
51
- isGroupByTimestamp === true ? FIELD_TIMESTAMP : (null as unknown as string),
52
- FIELD_FUNCTION_NAME,
53
- ].filter(Boolean),
45
+ defaultValue: [FIELD_FUNCTION_NAME],
54
46
  alwaysReturnArray: true,
55
47
  });
56
48
 
@@ -86,6 +78,18 @@ export const ProfileViewWithData = ({
86
78
  binaryFrameFilter,
87
79
  });
88
80
 
81
+ const {
82
+ isLoading: flamechartLoading,
83
+ response: flamechartResponse,
84
+ error: flamechartError,
85
+ } = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMECHART, {
86
+ skip: !dashboardItems.includes('iciclechart'),
87
+ nodeTrimThreshold,
88
+ groupBy,
89
+ invertCallStack,
90
+ binaryFrameFilter,
91
+ });
92
+
89
93
  const {isLoading: profileMetadataLoading, response: profileMetadataResponse} = useQuery(
90
94
  queryClient,
91
95
  profileSource,
@@ -188,6 +192,9 @@ export const ProfileViewWithData = ({
188
192
  } else if (sourceResponse !== null) {
189
193
  total = BigInt(sourceResponse.total);
190
194
  filtered = BigInt(sourceResponse.filtered);
195
+ } else if (flamechartResponse !== null) {
196
+ total = BigInt(flamechartResponse.total);
197
+ filtered = BigInt(flamechartResponse.filtered);
191
198
  }
192
199
 
193
200
  return (
@@ -217,6 +224,25 @@ export const ProfileViewWithData = ({
217
224
  : undefined,
218
225
  metadataLoading: profileMetadataLoading,
219
226
  }}
227
+ flamechartData={{
228
+ loading: flamechartLoading && profileMetadataLoading,
229
+ arrow:
230
+ flamechartResponse?.report.oneofKind === 'flamegraphArrow'
231
+ ? flamechartResponse?.report?.flamegraphArrow
232
+ : undefined,
233
+ total: BigInt(flamechartResponse?.total ?? '0'),
234
+ filtered: BigInt(flamechartResponse?.filtered ?? '0'),
235
+ error: flamechartError,
236
+ metadataMappingFiles:
237
+ profileMetadataResponse?.report.oneofKind === 'profileMetadata'
238
+ ? profileMetadataResponse?.report?.profileMetadata?.mappingFiles
239
+ : undefined,
240
+ metadataLabels:
241
+ profileMetadataResponse?.report.oneofKind === 'profileMetadata'
242
+ ? profileMetadataResponse?.report?.profileMetadata?.labels
243
+ : undefined,
244
+ metadataLoading: profileMetadataLoading,
245
+ }}
220
246
  topTableData={{
221
247
  loading: tableLoading,
222
248
  arrow:
@@ -250,7 +276,6 @@ export const ProfileViewWithData = ({
250
276
  onDownloadPProf={() => void downloadPProfClick()}
251
277
  pprofDownloading={pprofDownloading}
252
278
  showVisualizationSelector={showVisualizationSelector}
253
- timelineGuide={timelineGuide}
254
279
  />
255
280
  );
256
281
  };
@@ -13,19 +13,20 @@
13
13
 
14
14
  import {Fragment} from 'react';
15
15
 
16
- import * as d3 from 'd3';
16
+ import {scaleLinear, valueFormatter} from '@parca/utilities';
17
17
 
18
- import {NumberDuo} from '../utils';
18
+ import {BigIntDuo} from '../utils';
19
19
 
20
20
  interface Props {
21
21
  width: number;
22
22
  height: number;
23
23
  margin: number;
24
- bounds: NumberDuo;
24
+ bounds: BigIntDuo;
25
25
  ticks?: number;
26
+ timeUnit?: string;
26
27
  }
27
28
 
28
- const alignBeforeAxisCorrection = (val: number): number => {
29
+ const alignBeforeAxisCorrection = (val: bigint): number => {
29
30
  if (val < 10000) {
30
31
  return -24;
31
32
  }
@@ -36,11 +37,18 @@ const alignBeforeAxisCorrection = (val: number): number => {
36
37
  return 0;
37
38
  };
38
39
 
39
- export const TimelineGuide = ({bounds, width, height, margin, ticks}: Props): JSX.Element => {
40
- const xScale = d3.scaleLinear().domain(bounds).range([0, width]);
40
+ export const TimelineGuide = ({
41
+ bounds,
42
+ width,
43
+ height,
44
+ margin,
45
+ ticks,
46
+ timeUnit = 'milliseconds',
47
+ }: Props): JSX.Element => {
48
+ const xScale = scaleLinear(bounds, [0, width]);
41
49
 
42
50
  return (
43
- <div className="relative h-4">
51
+ <div className="relative h-5">
44
52
  <div className="absolute" style={{width, height}}>
45
53
  <svg style={{width: '100%', height: '100%'}} className="z-[5]">
46
54
  <g
@@ -50,30 +58,32 @@ export const TimelineGuide = ({bounds, width, height, margin, ticks}: Props): JS
50
58
  textAnchor="middle"
51
59
  transform={`translate(0,${height - margin})`}
52
60
  >
53
- {xScale.ticks(ticks).map((d, i) => (
54
- <Fragment key={`${i.toString()}-${d.toString()}`}>
55
- <g
56
- key={`tick-${i}`}
57
- className="tick"
58
- /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
59
- transform={`translate(${xScale(d) + alignBeforeAxisCorrection(d)}, ${-height})`}
60
- >
61
- {/* <line y2={6} className="stroke-gray-300 dark:stroke-gray-500" /> */}
62
- <text fill="currentColor" dy=".71em" y={9}>
63
- {d} ms
64
- </text>
65
- </g>
66
- <g key={`grid-${i}`}>
67
- <line
68
- className="stroke-gray-300 dark:stroke-gray-500"
69
- x1={xScale(d)}
70
- x2={xScale(d)}
71
- y1={0}
72
- y2={-height + margin}
73
- />
74
- </g>
75
- </Fragment>
76
- ))}
61
+ {xScale.ticks(ticks).map((d, i) => {
62
+ return (
63
+ <Fragment key={`${i.toString()}-${d.toString()}`}>
64
+ <g
65
+ key={`tick-${i}`}
66
+ className="tick"
67
+ /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
68
+ transform={`translate(${xScale(d) + alignBeforeAxisCorrection(d)}, ${-height})`}
69
+ >
70
+ {/* <line y2={6} className="stroke-gray-300 dark:stroke-gray-500" /> */}
71
+ <text fill="currentColor" dy=".71em" y={9}>
72
+ {valueFormatter(d - bounds[0], timeUnit, 2, true).toString()}
73
+ </text>
74
+ </g>
75
+ <g key={`grid-${i}`}>
76
+ <line
77
+ className="stroke-gray-300 dark:stroke-gray-500"
78
+ x1={xScale(d)}
79
+ x2={xScale(d)}
80
+ y1={0}
81
+ y2={-height + margin}
82
+ />
83
+ </g>
84
+ </Fragment>
85
+ );
86
+ })}
77
87
  <line
78
88
  className="stroke-gray-300 dark:stroke-gray-500"
79
89
  x1={0}
package/src/utils.ts CHANGED
@@ -61,3 +61,4 @@ export const truncateStringReverse = (str: string, num: number): string => {
61
61
  };
62
62
 
63
63
  export type NumberDuo = [number, number];
64
+ export type BigIntDuo = [bigint, bigint];