@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.
- package/CHANGELOG.md +8 -0
- package/dist/MetricsGraph/MetricsTooltip/index.js +1 -1
- package/dist/MetricsGraphStrips/index.d.ts +1 -0
- package/dist/MetricsGraphStrips/index.d.ts.map +1 -1
- package/dist/MetricsGraphStrips/index.js +3 -3
- package/dist/ProfileIcicleGraph/IcicleGraph/index.js +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.d.ts +9 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.d.ts.map +1 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.js +51 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +10 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +2 -2
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +5 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +9 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +3 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +18 -0
- package/dist/ProfileIcicleGraph/index.d.ts +4 -1
- package/dist/ProfileIcicleGraph/index.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/index.js +6 -4
- package/dist/ProfileSelector/QueryControls.d.ts.map +1 -1
- package/dist/ProfileSelector/QueryControls.js +6 -6
- package/dist/ProfileView/components/DashboardItems/index.d.ts +2 -1
- package/dist/ProfileView/components/DashboardItems/index.d.ts.map +1 -1
- package/dist/ProfileView/components/DashboardItems/index.js +7 -1
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/index.js +4 -1
- package/dist/ProfileView/index.d.ts +1 -1
- package/dist/ProfileView/index.d.ts.map +1 -1
- package/dist/ProfileView/index.js +3 -2
- package/dist/ProfileView/types/visualization.d.ts +2 -4
- package/dist/ProfileView/types/visualization.d.ts.map +1 -1
- package/dist/ProfileViewWithData.d.ts +1 -4
- package/dist/ProfileViewWithData.d.ts.map +1 -1
- package/dist/ProfileViewWithData.js +30 -7
- package/dist/TimelineGuide/index.d.ts +4 -3
- package/dist/TimelineGuide/index.d.ts.map +1 -1
- package/dist/TimelineGuide/index.js +9 -7
- package/dist/styles.css +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/MetricsGraph/MetricsTooltip/index.tsx +2 -2
- package/src/MetricsGraphStrips/index.tsx +3 -3
- package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +1 -1
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleChartRootNode.tsx +157 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +3 -3
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +58 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +29 -1
- package/src/ProfileIcicleGraph/index.tsx +16 -6
- package/src/ProfileSelector/QueryControls.tsx +5 -4
- package/src/ProfileView/components/DashboardItems/index.tsx +27 -0
- package/src/ProfileView/components/ViewSelector/index.tsx +5 -1
- package/src/ProfileView/index.tsx +3 -3
- package/src/ProfileView/types/visualization.ts +2 -4
- package/src/ProfileViewWithData.tsx +36 -11
- package/src/TimelineGuide/index.tsx +41 -31
- 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 {
|
|
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
|
|
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
|
-
{
|
|
176
|
+
{isIcicleChart ? (
|
|
171
177
|
<TimelineGuide
|
|
172
|
-
bounds={
|
|
178
|
+
bounds={boundsFromProfileSource(profileSource)}
|
|
173
179
|
width={width}
|
|
174
180
|
height={1000}
|
|
175
181
|
margin={0}
|
|
176
|
-
ticks={
|
|
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
|
-
|
|
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,
|
|
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-
|
|
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
|
-
<
|
|
219
|
+
<div>
|
|
220
|
+
<label className="text-xs"> </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
|
-
</
|
|
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
|
|
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
|
|
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
|
|
16
|
+
import {scaleLinear, valueFormatter} from '@parca/utilities';
|
|
17
17
|
|
|
18
|
-
import {
|
|
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:
|
|
24
|
+
bounds: BigIntDuo;
|
|
25
25
|
ticks?: number;
|
|
26
|
+
timeUnit?: string;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const alignBeforeAxisCorrection = (val:
|
|
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 = ({
|
|
40
|
-
|
|
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-
|
|
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
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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