@parca/profile 0.16.232 → 0.16.233

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 (34) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/MatchersInput/index.js +1 -1
  3. package/dist/MetricsGraph/index.d.ts +2 -3
  4. package/dist/MetricsGraph/index.js +12 -11
  5. package/dist/MetricsGraph/useMetricsGraphDimensions.d.ts +0 -1
  6. package/dist/MetricsGraph/useMetricsGraphDimensions.js +0 -3
  7. package/dist/ProfileExplorer/ProfileExplorerCompare.js +2 -1
  8. package/dist/ProfileExplorer/ProfileExplorerSingle.js +2 -1
  9. package/dist/ProfileIcicleGraph/index.js +1 -1
  10. package/dist/ProfileMetricsGraph/index.js +2 -2
  11. package/dist/ProfileSelector/index.js +38 -37
  12. package/dist/ProfileTypeSelector/index.js +1 -1
  13. package/dist/ProfileView/ViewSelector.d.ts +2 -1
  14. package/dist/ProfileView/ViewSelector.js +2 -2
  15. package/dist/ProfileView/VisualizationPanel.js +1 -1
  16. package/dist/ProfileView/index.js +12 -10
  17. package/dist/TopTable/index.js +1 -1
  18. package/dist/components/ProfileShareButton/index.js +2 -2
  19. package/dist/styles.css +1 -1
  20. package/package.json +3 -3
  21. package/src/MatchersInput/index.tsx +1 -1
  22. package/src/MetricsGraph/index.tsx +83 -42
  23. package/src/MetricsGraph/useMetricsGraphDimensions.ts +0 -4
  24. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +17 -12
  25. package/src/ProfileExplorer/ProfileExplorerSingle.tsx +21 -22
  26. package/src/ProfileIcicleGraph/index.tsx +1 -2
  27. package/src/ProfileMetricsGraph/index.tsx +2 -6
  28. package/src/ProfileSelector/index.tsx +70 -59
  29. package/src/ProfileTypeSelector/index.tsx +2 -1
  30. package/src/ProfileView/ViewSelector.tsx +3 -0
  31. package/src/ProfileView/VisualizationPanel.tsx +9 -6
  32. package/src/ProfileView/index.tsx +101 -93
  33. package/src/TopTable/index.tsx +11 -9
  34. package/src/components/ProfileShareButton/index.tsx +10 -3
@@ -43,7 +43,6 @@ interface Props {
43
43
  width?: number;
44
44
  height?: number;
45
45
  margin?: number;
46
- marginRight?: number;
47
46
  }
48
47
 
49
48
  export interface HighlightedSeries {
@@ -75,7 +74,6 @@ const MetricsGraph = ({
75
74
  width = 0,
76
75
  height = 0,
77
76
  margin = 0,
78
- marginRight = 0,
79
77
  }: Props): JSX.Element => {
80
78
  return (
81
79
  <RawMetricsGraph
@@ -90,7 +88,6 @@ const MetricsGraph = ({
90
88
  width={width}
91
89
  height={height}
92
90
  margin={margin}
93
- marginRight={marginRight}
94
91
  />
95
92
  );
96
93
  };
@@ -118,7 +115,6 @@ export const RawMetricsGraph = ({
118
115
  width,
119
116
  height = 50,
120
117
  margin = 0,
121
- marginRight = 0,
122
118
  sampleUnit,
123
119
  }: Props): JSX.Element => {
124
120
  const graph = useRef(null);
@@ -174,13 +170,14 @@ export const RawMetricsGraph = ({
174
170
  const xScale = d3
175
171
  .scaleUtc()
176
172
  .domain([from, to])
177
- .range([0, width - margin - marginRight]);
173
+ .range([0, width - 2 * margin]);
178
174
 
179
175
  const yScale = d3
180
176
  .scaleLinear()
181
177
  // tslint:disable-next-line
182
178
  .domain([minY, maxY] as Iterable<d3.NumberValue>)
183
- .range([height - margin, 0]);
179
+ .range([height - margin, 0])
180
+ .nice();
184
181
 
185
182
  const color = d3.scaleOrdinal(d3.schemeCategory10);
186
183
 
@@ -360,6 +357,7 @@ export const RawMetricsGraph = ({
360
357
  };
361
358
 
362
359
  const selected = findSelectedProfile();
360
+
363
361
  return (
364
362
  <>
365
363
  {highlighted != null && hovering && !dragging && pos[0] !== 0 && pos[1] !== 0 && (
@@ -408,6 +406,85 @@ export const RawMetricsGraph = ({
408
406
  )}
409
407
  </g>
410
408
  <g transform={`translate(${margin}, ${margin})`}>
409
+ <g className="y axis" textAnchor="end" fontSize="10" fill="none">
410
+ {yScale.ticks(5).map((d, i) => (
411
+ <>
412
+ <g
413
+ key={`tick-${i}`}
414
+ className="tick"
415
+ /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
416
+ transform={`translate(0, ${yScale(d)})`}
417
+ >
418
+ <line className="stroke-gray-300 dark:stroke-gray-500" x2={-6} />
419
+ <text fill="currentColor" x={-9} dy={'0.32em'}>
420
+ {valueFormatter(d, sampleUnit, 1)}
421
+ </text>
422
+ </g>
423
+ <g key={`grid-${i}`}>
424
+ <line
425
+ className="stroke-gray-300 dark:stroke-gray-500"
426
+ x1={xScale(from)}
427
+ x2={xScale(to)}
428
+ y1={yScale(d)}
429
+ y2={yScale(d)}
430
+ />
431
+ </g>
432
+ </>
433
+ ))}
434
+ <line
435
+ className="stroke-gray-300 dark:stroke-gray-500"
436
+ x1={0}
437
+ x2={0}
438
+ y1={0}
439
+ y2={height - margin}
440
+ />
441
+ <line
442
+ className="stroke-gray-300 dark:stroke-gray-500"
443
+ x1={xScale(to)}
444
+ x2={xScale(to)}
445
+ y1={0}
446
+ y2={height - margin}
447
+ />
448
+ </g>
449
+ <g
450
+ className="x axis"
451
+ fill="none"
452
+ fontSize="10"
453
+ textAnchor="middle"
454
+ transform={`translate(0,${height - margin})`}
455
+ >
456
+ {xScale.ticks(5).map((d, i) => (
457
+ <>
458
+ <g
459
+ key={`tick-${i}`}
460
+ className="tick"
461
+ /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
462
+ transform={`translate(${xScale(d)}, 0)`}
463
+ >
464
+ <line y2={6} className="stroke-gray-300 dark:stroke-gray-500" />
465
+ <text fill="currentColor" dy=".71em" y={9}>
466
+ {formatDate(d, formatForTimespan(from, to))}
467
+ </text>
468
+ </g>
469
+ <g key={`grid-${i}`}>
470
+ <line
471
+ className="stroke-gray-300 dark:stroke-gray-500"
472
+ x1={xScale(d)}
473
+ x2={xScale(d)}
474
+ y1={0}
475
+ y2={-height + margin}
476
+ />
477
+ </g>
478
+ </>
479
+ ))}
480
+ <line
481
+ className="stroke-gray-300 dark:stroke-gray-500"
482
+ x1={xScale(from)}
483
+ x2={xScale(to)}
484
+ y1={0}
485
+ y2={0}
486
+ />
487
+ </g>
411
488
  <g className="lines fill-transparent">
412
489
  {series.map((s, i) => (
413
490
  <g key={i} className="line">
@@ -447,42 +524,6 @@ export const RawMetricsGraph = ({
447
524
  <MetricsCircle cx={selected.x} cy={selected.y} radius={5} />
448
525
  </g>
449
526
  )}
450
- <g
451
- className="x axis"
452
- fill="none"
453
- fontSize="10"
454
- textAnchor="middle"
455
- transform={`translate(0,${height - margin})`}
456
- >
457
- {xScale.ticks(5).map((d, i) => (
458
- <g
459
- key={i}
460
- className="tick"
461
- /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
462
- transform={`translate(${xScale(d)}, 0)`}
463
- >
464
- <line y2={6} stroke="currentColor" />
465
- <text fill="currentColor" dy=".71em" y={9}>
466
- {formatDate(d, formatForTimespan(from, to))}
467
- </text>
468
- </g>
469
- ))}
470
- </g>
471
- <g className="y axis" textAnchor="end" fontSize="10" fill="none">
472
- {yScale.ticks(3).map((d, i) => (
473
- <g
474
- key={i}
475
- className="tick"
476
- /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
477
- transform={`translate(0, ${yScale(d)})`}
478
- >
479
- <line stroke="currentColor" x2={-6} />
480
- <text fill="currentColor" x={-9} dy={'0.32em'}>
481
- {valueFormatter(d, sampleUnit, 1)}
482
- </text>
483
- </g>
484
- ))}
485
- </g>
486
527
  </g>
487
528
  </svg>
488
529
  </div>
@@ -20,7 +20,6 @@ interface MetricsGraphDimensions {
20
20
  height: number;
21
21
  heightStyle: string;
22
22
  margin: number;
23
- marginRight: number;
24
23
  }
25
24
 
26
25
  const maxHeight = 402;
@@ -35,7 +34,6 @@ export const useMetricsGraphDimensions = (comparing: boolean): MetricsGraphDimen
35
34
  height: 0,
36
35
  heightStyle: '0',
37
36
  margin: 0,
38
- marginRight: 0,
39
37
  };
40
38
  }
41
39
  width = width - profileExplorer.PaddingX;
@@ -43,7 +41,6 @@ export const useMetricsGraphDimensions = (comparing: boolean): MetricsGraphDimen
43
41
  width = width / 2 - 32;
44
42
  }
45
43
  const height = Math.min(width / 2.5, maxHeight);
46
- const marginRight = 20;
47
44
  const heightStyle = `min(${maxHeight + margin}px, ${
48
45
  comparing
49
46
  ? profileExplorer.metricsGraph.maxHeightStyle.compareMode
@@ -54,6 +51,5 @@ export const useMetricsGraphDimensions = (comparing: boolean): MetricsGraphDimen
54
51
  height,
55
52
  heightStyle,
56
53
  margin,
57
- marginRight,
58
54
  };
59
55
  };
@@ -12,6 +12,7 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {QueryServiceClient} from '@parca/client';
15
+ import {Card} from '@parca/components';
15
16
  import {Query} from '@parca/parser';
16
17
  import type {NavigateFunction} from '@parca/utilities';
17
18
 
@@ -57,8 +58,8 @@ const ProfileExplorerCompare = ({
57
58
 
58
59
  return (
59
60
  <>
60
- <div className="grid grid-cols-2">
61
- <div className="pr-2">
61
+ <div className="flex justify-between gap-2">
62
+ <Card className="p-2">
62
63
  <ProfileSelector
63
64
  queryClient={queryClient}
64
65
  querySelection={queryA}
@@ -70,8 +71,8 @@ const ProfileExplorerCompare = ({
70
71
  comparing={true}
71
72
  onCompareProfile={() => {}}
72
73
  />
73
- </div>
74
- <div className="pl-2">
74
+ </Card>
75
+ <Card className="p-2">
75
76
  <ProfileSelector
76
77
  queryClient={queryClient}
77
78
  querySelection={queryB}
@@ -83,17 +84,21 @@ const ProfileExplorerCompare = ({
83
84
  comparing={true}
84
85
  onCompareProfile={() => {}}
85
86
  />
86
- </div>
87
+ </Card>
87
88
  </div>
88
89
  <div className="grid grid-cols-1">
89
90
  {profileA != null && profileB != null ? (
90
- <ProfileViewWithData
91
- navigateTo={navigateTo}
92
- queryClient={queryClient}
93
- profileSource={
94
- new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource())
95
- }
96
- />
91
+ <div className="mt-2">
92
+ <Card className="px-6 py-4">
93
+ <ProfileViewWithData
94
+ navigateTo={navigateTo}
95
+ queryClient={queryClient}
96
+ profileSource={
97
+ new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource())
98
+ }
99
+ />
100
+ </Card>
101
+ </div>
97
102
  ) : (
98
103
  <div>
99
104
  <div className="my-20 text-center">
@@ -12,6 +12,7 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {QueryServiceClient} from '@parca/client';
15
+ import {Card} from '@parca/components';
15
16
  import type {NavigateFunction} from '@parca/utilities';
16
17
 
17
18
  import {ProfileSelection, ProfileViewWithData} from '..';
@@ -38,34 +39,32 @@ const ProfileExplorerSingle = ({
38
39
  }: ProfileExplorerSingleProps): JSX.Element => {
39
40
  return (
40
41
  <>
41
- <div className="grid grid-cols-1">
42
- <div>
43
- <ProfileSelector
44
- queryClient={queryClient}
45
- querySelection={query}
46
- selectQuery={selectQuery}
47
- selectProfile={selectProfile}
48
- closeProfile={() => {}} // eslint-disable-line @typescript-eslint/no-empty-function
49
- profileSelection={profile}
50
- comparing={false}
51
- onCompareProfile={compareProfile}
52
- enforcedProfileName={''} // TODO
53
- />
54
- </div>
55
- </div>
56
- <div className="grid grid-cols-1">
57
- <div>
58
- {profile != null ? (
42
+ <Card className="px-6 py-4">
43
+ <ProfileSelector
44
+ queryClient={queryClient}
45
+ querySelection={query}
46
+ selectQuery={selectQuery}
47
+ selectProfile={selectProfile}
48
+ closeProfile={() => {}} // eslint-disable-line @typescript-eslint/no-empty-function
49
+ profileSelection={profile}
50
+ comparing={false}
51
+ onCompareProfile={compareProfile}
52
+ enforcedProfileName={''} // TODO
53
+ />
54
+ </Card>
55
+ {profile != null ? (
56
+ <div className="mt-2">
57
+ <Card className="px-6 py-4">
59
58
  <ProfileViewWithData
60
59
  queryClient={queryClient}
61
60
  profileSource={profile.ProfileSource()}
62
61
  navigateTo={navigateTo}
63
62
  />
64
- ) : (
65
- <></>
66
- )}
63
+ </Card>
67
64
  </div>
68
- </div>
65
+ ) : (
66
+ <></>
67
+ )}
69
68
  </>
70
69
  );
71
70
  };
@@ -158,10 +158,9 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
158
158
  }
159
159
  setActionButtons(
160
160
  <div className="flex w-full justify-end gap-2 pb-2">
161
- <div className="flex w-full items-center justify-between space-x-2">
161
+ <div className="ml-2 flex w-full items-end justify-between gap-2">
162
162
  {table !== undefined && <GroupAndSortActionButtons navigateTo={navigateTo} />}
163
163
  <div>
164
- <label className="inline-block"></label>
165
164
  <Button
166
165
  color="neutral"
167
166
  onClick={() => setNewCurPath([])}
@@ -115,7 +115,7 @@ const ProfileMetricsGraph = ({
115
115
  const {isLoading, response, error} = useQueryRange(queryClient, queryExpression, from, to);
116
116
  const isLoaderVisible = useDelayedLoader(isLoading);
117
117
  const {loader, onError, perf} = useParcaContext();
118
- const {width, height, margin, marginRight} = useMetricsGraphDimensions(comparing);
118
+ const {width, height, margin} = useMetricsGraphDimensions(comparing);
119
119
 
120
120
  useEffect(() => {
121
121
  if (error !== null) {
@@ -156,10 +156,7 @@ const ProfileMetricsGraph = ({
156
156
  };
157
157
 
158
158
  return (
159
- <div
160
- className="h-full w-full rounded border-gray-300 dark:border-gray-500 dark:bg-gray-700"
161
- style={{borderWidth: 1}}
162
- >
159
+ <div className="h-full w-full">
163
160
  <MetricsGraph
164
161
  data={series}
165
162
  from={from}
@@ -172,7 +169,6 @@ const ProfileMetricsGraph = ({
172
169
  height={height}
173
170
  width={width}
174
171
  margin={margin}
175
- marginRight={marginRight}
176
172
  />
177
173
  </div>
178
174
  );
@@ -19,7 +19,6 @@ import {ProfileTypesResponse, QueryServiceClient} from '@parca/client';
19
19
  import {
20
20
  Button,
21
21
  ButtonGroup,
22
- Card,
23
22
  DateTimeRange,
24
23
  DateTimeRangePicker,
25
24
  IconButton,
@@ -198,59 +197,69 @@ const ProfileSelector = ({
198
197
 
199
198
  const compareDisabled = selectedProfileName === '' || querySelection.expression === undefined;
200
199
 
201
- return (
202
- <Card className="overflow-visible">
203
- <Card.Header className="flex !items-center space-x-2">
204
- <div className="flex w-full flex-wrap items-center justify-start space-x-2 space-y-1">
205
- <div className="ml-2 mt-1">
206
- <ProfileTypeSelector
207
- profileTypesData={profileTypesData}
208
- loading={profileTypesLoading}
209
- selectedKey={selectedProfileName}
210
- onSelection={setProfileName}
211
- error={error}
212
- />
213
- </div>
214
- <div className="w-full flex-1">
215
- <MatchersInput
216
- queryClient={queryClient}
217
- setMatchersString={setMatchersString}
218
- runQuery={setQueryExpression}
219
- currentQuery={query}
220
- />
221
- </div>
200
+ const Header = (): JSX.Element => (
201
+ <div className="mb-2 flex">
202
+ <div className="flex w-full flex-wrap content-start items-end justify-between gap-2">
203
+ <div>
204
+ <label className="text-xs">Profile type</label>
205
+ <ProfileTypeSelector
206
+ profileTypesData={profileTypesData}
207
+ loading={profileTypesLoading}
208
+ selectedKey={selectedProfileName}
209
+ onSelection={setProfileName}
210
+ error={error}
211
+ />
212
+ </div>
213
+
214
+ <div className="w-full flex-1">
215
+ <label className="text-xs">Query</label>
216
+ <MatchersInput
217
+ queryClient={queryClient}
218
+ setMatchersString={setMatchersString}
219
+ runQuery={setQueryExpression}
220
+ currentQuery={query}
221
+ />
222
+ </div>
223
+ <div>
224
+ <label className="text-xs">Period</label>
222
225
  <DateTimeRangePicker
223
226
  onRangeSelection={setTimeRangeSelection}
224
227
  range={timeRangeSelection}
225
228
  />
226
- <ButtonGroup>
227
- {!searchDisabled && (
228
- <>
229
- {!comparing && (
230
- <CompareButton disabled={compareDisabled} onClick={handleCompareClick} />
231
- )}
232
- </>
233
- )}
234
- <Button
235
- disabled={searchDisabled}
236
- onClick={(e: React.MouseEvent<HTMLElement>) => {
237
- e.preventDefault();
238
- setQueryExpression();
239
- }}
240
- >
241
- Search
242
- </Button>
243
- </ButtonGroup>
244
229
  </div>
245
- <div>{comparing && <IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />}</div>
246
- </Card.Header>
247
- {
248
- <Card.Body>
249
- <div style={{height: heightStyle}}>
250
- {querySelection.expression !== undefined &&
251
- querySelection.expression.length > 0 &&
252
- querySelection.from !== undefined &&
253
- querySelection.to !== undefined ? (
230
+ <ButtonGroup>
231
+ {!searchDisabled && (
232
+ <>
233
+ {!comparing && (
234
+ <CompareButton disabled={compareDisabled} onClick={handleCompareClick} />
235
+ )}
236
+ </>
237
+ )}
238
+ <Button
239
+ disabled={searchDisabled}
240
+ onClick={(e: React.MouseEvent<HTMLElement>) => {
241
+ e.preventDefault();
242
+ setQueryExpression();
243
+ }}
244
+ >
245
+ Search
246
+ </Button>
247
+ </ButtonGroup>
248
+ </div>
249
+ <div>{comparing && <IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />}</div>
250
+ </div>
251
+ );
252
+
253
+ return (
254
+ <>
255
+ <Header />
256
+ <div className="rounded bg-white shadow dark:border-gray-500 dark:bg-gray-700">
257
+ <div style={{height: heightStyle}}>
258
+ {querySelection.expression !== undefined &&
259
+ querySelection.expression.length > 0 &&
260
+ querySelection.from !== undefined &&
261
+ querySelection.to !== undefined ? (
262
+ <div className="p-2">
254
263
  <ProfileMetricsGraph
255
264
  queryClient={queryClient}
256
265
  queryExpression={querySelection.expression}
@@ -294,19 +303,21 @@ const ProfileSelector = ({
294
303
  selectProfile(new MergedProfileSelection(mergeFrom, mergeTo, query));
295
304
  }}
296
305
  />
297
- ) : (
298
- <>
299
- {profileSelection == null ? (
306
+ </div>
307
+ ) : (
308
+ <>
309
+ {profileSelection == null ? (
310
+ <div className="p-2">
300
311
  <ProfileMetricsEmptyState
301
312
  message={`Please select a profile type and click "Search" to begin.`}
302
313
  />
303
- ) : null}
304
- </>
305
- )}
306
- </div>
307
- </Card.Body>
308
- }
309
- </Card>
314
+ </div>
315
+ ) : null}
316
+ </>
317
+ )}
318
+ </div>
319
+ </div>
320
+ </>
310
321
  );
311
322
  };
312
323
 
@@ -175,8 +175,9 @@ const ProfileTypeSelector = ({
175
175
  items={profileLabels}
176
176
  selectedKey={selectedKey}
177
177
  onSelection={onSelection}
178
- placeholder="Select profile..."
178
+ placeholder="Select profile type..."
179
179
  loading={loading}
180
+ className="bg-white"
180
181
  />
181
182
  );
182
183
  };
@@ -23,6 +23,7 @@ interface Props {
23
23
  primary?: boolean;
24
24
  addView?: boolean;
25
25
  disabled?: boolean;
26
+ icon?: JSX.Element;
26
27
  }
27
28
 
28
29
  const ViewSelector = ({
@@ -33,6 +34,7 @@ const ViewSelector = ({
33
34
  primary = false,
34
35
  addView = false,
35
36
  disabled = false,
37
+ icon,
36
38
  }: Props): JSX.Element => {
37
39
  const [callgraphEnabled] = useUIFeatureFlag('callgraph');
38
40
  const [dashboardItems = ['icicle'], setDashboardItems] = useURLState({
@@ -114,6 +116,7 @@ const ViewSelector = ({
114
116
  placeholder={placeholderText ?? 'Select view type...'}
115
117
  primary={primary}
116
118
  disabled={disabled}
119
+ icon={icon}
117
120
  />
118
121
  );
119
122
  };
@@ -50,22 +50,25 @@ export const VisualizationPanel = React.memo(function VisualizationPanel({
50
50
 
51
51
  return (
52
52
  <>
53
- <div className="flex w-full justify-end gap-2 pb-2">
54
- <div className="flex w-full items-center justify-between">
55
- <div className="flex">
53
+ <div className="flex w-full items-start justify-end gap-2 pb-2">
54
+ <div className="flex w-full items-start justify-between">
55
+ <div className="flex items-start">
56
56
  <div
57
57
  className={cx(isMultiPanelView ? 'visible' : 'invisible', 'flex items-center')}
58
58
  {...dragHandleProps}
59
59
  >
60
60
  <Icon className="text-xl" icon="material-symbols:drag-indicator" />
61
61
  </div>
62
- <>{actionButtons}</>
62
+ <div>{actionButtons}</div>
63
63
  </div>
64
64
  <ViewSelector defaultValue={dashboardItem} navigateTo={navigateTo} position={index} />
65
65
  </div>
66
-
67
66
  {isMultiPanelView && (
68
- <IconButton onClick={() => handleClosePanel(dashboardItem)} icon={<CloseIcon />} />
67
+ <IconButton
68
+ className="py-0"
69
+ onClick={() => handleClosePanel(dashboardItem)}
70
+ icon={<CloseIcon />}
71
+ />
69
72
  )}
70
73
  </div>
71
74
  {getDashboardItemByType({