@parca/profile 0.16.198 → 0.16.199

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 (57) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/Callgraph/index.d.ts +0 -1
  3. package/dist/GraphTooltip/ExpandOnHoverValue.d.ts +0 -1
  4. package/dist/GraphTooltip/index.d.ts +0 -1
  5. package/dist/GraphTooltipArrow/ExpandOnHoverValue.d.ts +6 -0
  6. package/dist/GraphTooltipArrow/ExpandOnHoverValue.js +4 -0
  7. package/dist/GraphTooltipArrow/index.d.ts +43 -0
  8. package/dist/GraphTooltipArrow/index.js +244 -0
  9. package/dist/MatchersInput/SuggestionItem.d.ts +0 -1
  10. package/dist/MatchersInput/SuggestionsList.d.ts +0 -1
  11. package/dist/MatchersInput/index.d.ts +0 -1
  12. package/dist/MetricsCircle/index.d.ts +0 -1
  13. package/dist/MetricsGraph/MetricsTooltip/index.d.ts +0 -1
  14. package/dist/MetricsGraph/index.d.ts +0 -1
  15. package/dist/MetricsSeries/index.d.ts +0 -1
  16. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +0 -1
  17. package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +0 -1
  18. package/dist/ProfileExplorer/index.d.ts +0 -1
  19. package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.d.ts +0 -1
  20. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ColorStackLegend.d.ts +10 -0
  21. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ColorStackLegend.js +64 -0
  22. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +48 -0
  23. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +150 -0
  24. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +23 -0
  25. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +110 -0
  26. package/dist/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.d.ts +14 -0
  27. package/dist/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.js +26 -0
  28. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.d.ts +4 -0
  29. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +42 -0
  30. package/dist/ProfileIcicleGraph/index.d.ts +4 -3
  31. package/dist/ProfileIcicleGraph/index.js +7 -5
  32. package/dist/ProfileMetricsGraph/index.d.ts +0 -1
  33. package/dist/ProfileSelector/CompareButton.d.ts +0 -1
  34. package/dist/ProfileSelector/index.d.ts +0 -1
  35. package/dist/ProfileSource.d.ts +0 -1
  36. package/dist/ProfileTypeSelector/index.d.ts +0 -1
  37. package/dist/ProfileView/FilterByFunctionButton.d.ts +0 -1
  38. package/dist/ProfileView/ViewSelector.d.ts +0 -1
  39. package/dist/ProfileView/index.d.ts +2 -1
  40. package/dist/ProfileView/index.js +2 -2
  41. package/dist/ProfileViewWithData.d.ts +0 -1
  42. package/dist/ProfileViewWithData.js +15 -6
  43. package/dist/components/DiffLegend.d.ts +0 -1
  44. package/dist/components/ProfileShareButton/ResultBox.d.ts +0 -1
  45. package/dist/components/ProfileShareButton/index.d.ts +0 -1
  46. package/dist/styles.css +1 -1
  47. package/package.json +6 -5
  48. package/src/GraphTooltipArrow/ExpandOnHoverValue.tsx +30 -0
  49. package/src/GraphTooltipArrow/index.tsx +564 -0
  50. package/src/ProfileIcicleGraph/IcicleGraphArrow/ColorStackLegend.tsx +109 -0
  51. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +327 -0
  52. package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +212 -0
  53. package/src/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.ts +52 -0
  54. package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +51 -0
  55. package/src/ProfileIcicleGraph/index.tsx +34 -14
  56. package/src/ProfileView/index.tsx +4 -1
  57. package/src/ProfileViewWithData.tsx +20 -6
@@ -0,0 +1,51 @@
1
+ // Copyright 2022 The Parca Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+
14
+ import {Table} from 'apache-arrow';
15
+
16
+ import {EVERYTHING_ELSE, FEATURE_TYPES, type Feature} from '@parca/store';
17
+ import {getLastItem} from '@parca/utilities';
18
+
19
+ import {hexifyAddress} from '../../utils';
20
+ import {FIELD_FUNCTION_NAME, FIELD_LOCATION_ADDRESS, FIELD_MAPPING_FILE} from './index';
21
+
22
+ export function nodeLabel(table: Table<any>, row: number, showBinaryName: boolean): string {
23
+ const functionName: string | null = table.getChild(FIELD_FUNCTION_NAME)?.get(row);
24
+ if (functionName !== null && functionName !== '') {
25
+ return functionName;
26
+ }
27
+
28
+ let mappingString = '';
29
+ if (showBinaryName) {
30
+ const mappingFile: string | null = table.getChild(FIELD_MAPPING_FILE)?.get(row) ?? '';
31
+ const binary: string | undefined = getLastItem(mappingFile ?? undefined);
32
+ if (binary != null) mappingString = `[${binary}]`;
33
+ }
34
+
35
+ const addressBigInt: bigint = table.getChild(FIELD_LOCATION_ADDRESS)?.get(row);
36
+ const address = hexifyAddress(addressBigInt);
37
+ const fallback = `${mappingString}${address}`;
38
+ return fallback === '' ? '<unknown>' : fallback;
39
+ }
40
+
41
+ export const extractFeature = (mapping: string): Feature => {
42
+ if (mapping.startsWith('runtime') || mapping === 'root') {
43
+ return {name: 'runtime', type: FEATURE_TYPES.Runtime};
44
+ }
45
+
46
+ if (mapping != null && mapping !== '') {
47
+ return {name: mapping, type: FEATURE_TYPES.Binary};
48
+ }
49
+
50
+ return {name: EVERYTHING_ELSE, type: FEATURE_TYPES.Misc};
51
+ };
@@ -13,13 +13,16 @@
13
13
 
14
14
  import {useEffect, useMemo} from 'react';
15
15
 
16
+ import {Table} from 'apache-arrow';
17
+
16
18
  import {Flamegraph} from '@parca/client';
17
19
  import {Button} from '@parca/components';
18
20
  import {useContainerDimensions} from '@parca/hooks';
19
21
  import {divide, selectQueryParam, type NavigateFunction} from '@parca/utilities';
20
22
 
21
23
  import DiffLegend from '../components/DiffLegend';
22
- import {IcicleGraph} from './IcicleGraph';
24
+ import IcicleGraph from './IcicleGraph';
25
+ import IcicleGraphArrow from './IcicleGraphArrow';
23
26
 
24
27
  const numberFormatter = new Intl.NumberFormat('en-US');
25
28
 
@@ -27,7 +30,8 @@ export type ResizeHandler = (width: number, height: number) => void;
27
30
 
28
31
  interface ProfileIcicleGraphProps {
29
32
  width?: number;
30
- graph: Flamegraph | undefined;
33
+ graph?: Flamegraph;
34
+ table?: Table<any>;
31
35
  total: bigint;
32
36
  filtered: bigint;
33
37
  sampleUnit: string;
@@ -40,6 +44,7 @@ interface ProfileIcicleGraphProps {
40
44
 
41
45
  const ProfileIcicleGraph = ({
42
46
  graph,
47
+ table,
43
48
  total,
44
49
  filtered,
45
50
  curPath,
@@ -66,7 +71,8 @@ const ProfileIcicleGraph = ({
66
71
  return ['0', '0', false, '0', '0', false, '0', '0'];
67
72
  }
68
73
 
69
- const trimmed = graph.trimmed;
74
+ // const trimmed = graph.trimmed;
75
+ const trimmed = 0n;
70
76
 
71
77
  const totalUnfiltered = total + filtered;
72
78
  // safeguard against division by zero
@@ -101,7 +107,7 @@ const ProfileIcicleGraph = ({
101
107
  );
102
108
  }, [setNewCurPath, curPath, setActionButtons]);
103
109
 
104
- if (graph === undefined) return <div>no data...</div>;
110
+ if (graph === undefined && table === undefined) return <div>no data...</div>;
105
111
 
106
112
  if (total === 0n && !loading) return <>Profile has no samples</>;
107
113
 
@@ -113,16 +119,30 @@ const ProfileIcicleGraph = ({
113
119
  <div className="relative">
114
120
  {compareMode && <DiffLegend />}
115
121
  <div ref={ref}>
116
- <IcicleGraph
117
- width={dimensions?.width}
118
- graph={graph}
119
- total={total}
120
- filtered={filtered}
121
- curPath={curPath}
122
- setCurPath={setNewCurPath}
123
- sampleUnit={sampleUnit}
124
- navigateTo={navigateTo}
125
- />
122
+ {graph !== undefined && (
123
+ <IcicleGraph
124
+ width={dimensions?.width}
125
+ graph={graph}
126
+ total={total}
127
+ filtered={filtered}
128
+ curPath={curPath}
129
+ setCurPath={setNewCurPath}
130
+ sampleUnit={sampleUnit}
131
+ navigateTo={navigateTo}
132
+ />
133
+ )}
134
+ {table !== undefined && (
135
+ <IcicleGraphArrow
136
+ width={dimensions?.width}
137
+ table={table}
138
+ total={total}
139
+ filtered={filtered}
140
+ curPath={curPath}
141
+ setCurPath={setNewCurPath}
142
+ sampleUnit={sampleUnit}
143
+ navigateTo={navigateTo}
144
+ />
145
+ )}
126
146
  </div>
127
147
  <p className="my-2 text-xs">
128
148
  Showing {totalFormatted}{' '}
@@ -13,6 +13,7 @@
13
13
 
14
14
  import {Profiler, ProfilerProps, useEffect, useMemo, useState} from 'react';
15
15
 
16
+ import {Table} from 'apache-arrow';
16
17
  import cx from 'classnames';
17
18
  import {scaleLinear} from 'd3';
18
19
  import graphviz from 'graphviz-wasm';
@@ -53,6 +54,7 @@ type NavigateFunction = (path: string, queryParams: any, options?: {replace?: bo
53
54
  export interface FlamegraphData {
54
55
  loading: boolean;
55
56
  data?: Flamegraph;
57
+ table?: Table<any>;
56
58
  total?: bigint;
57
59
  filtered?: bigint;
58
60
  error?: any;
@@ -230,7 +232,7 @@ export const ProfileView = ({
230
232
  }): JSX.Element => {
231
233
  switch (type) {
232
234
  case 'icicle': {
233
- return flamegraphData?.data != null ? (
235
+ return flamegraphData?.table !== undefined || flamegraphData.data !== undefined ? (
234
236
  <ConditionalWrapper<ProfilerProps>
235
237
  condition={perf?.onRender != null}
236
238
  WrapperComponent={Profiler}
@@ -242,6 +244,7 @@ export const ProfileView = ({
242
244
  <ProfileIcicleGraph
243
245
  curPath={curPath}
244
246
  setNewCurPath={setNewCurPath}
247
+ table={flamegraphData.table}
245
248
  graph={flamegraphData.data}
246
249
  total={total}
247
250
  filtered={filtered}
@@ -13,9 +13,11 @@
13
13
 
14
14
  import {useEffect, useMemo, useState} from 'react';
15
15
 
16
+ import {tableFromIPC} from 'apache-arrow';
17
+
16
18
  import {QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
17
19
  import {useGrpcMetadata, useParcaContext, useURLState} from '@parca/components';
18
- import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
20
+ import {USER_PREFERENCES, useUIFeatureFlag, useUserPreference} from '@parca/hooks';
19
21
  import {saveAsBlob, type NavigateFunction} from '@parca/utilities';
20
22
 
21
23
  import {ProfileSource} from './ProfileSource';
@@ -38,6 +40,7 @@ export const ProfileViewWithData = ({
38
40
  const metadata = useGrpcMetadata();
39
41
  const [dashboardItems] = useURLState({param: 'dashboard_items', navigateTo});
40
42
  const [enableTrimming] = useUserPreference<boolean>(USER_PREFERENCES.ENABLE_GRAPH_TRIMMING.key);
43
+ const [arrowFlamegraphEnabled] = useUIFeatureFlag('flamegraph-arrow');
41
44
  const [pprofDownloading, setPprofDownloading] = useState<boolean>(false);
42
45
 
43
46
  const nodeTrimThreshold = useMemo(() => {
@@ -53,11 +56,15 @@ export const ProfileViewWithData = ({
53
56
  return (1 / width) * 100;
54
57
  }, [enableTrimming]);
55
58
 
59
+ const reportType = arrowFlamegraphEnabled
60
+ ? QueryRequest_ReportType.FLAMEGRAPH_ARROW
61
+ : QueryRequest_ReportType.FLAMEGRAPH_TABLE;
62
+
56
63
  const {
57
64
  isLoading: flamegraphLoading,
58
65
  response: flamegraphResponse,
59
66
  error: flamegraphError,
60
- } = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_TABLE, {
67
+ } = useQuery(queryClient, profileSource, reportType, {
61
68
  skip: !dashboardItems.includes('icicle'),
62
69
  nodeTrimThreshold,
63
70
  });
@@ -80,16 +87,19 @@ export const ProfileViewWithData = ({
80
87
  });
81
88
 
82
89
  useEffect(() => {
83
- if (!flamegraphLoading && flamegraphResponse?.report.oneofKind === 'flamegraph') {
84
- perf?.markInteraction('Flamegraph render', flamegraphResponse.report.flamegraph.total);
90
+ if (
91
+ (!flamegraphLoading && flamegraphResponse?.report.oneofKind === 'flamegraph') ||
92
+ flamegraphResponse?.report.oneofKind === 'flamegraphArrow'
93
+ ) {
94
+ perf?.markInteraction('Flamegraph render', flamegraphResponse.total);
85
95
  }
86
96
 
87
97
  if (!topTableLoading && topTableResponse?.report.oneofKind === 'top') {
88
- perf?.markInteraction('Top table render', topTableResponse?.report?.top.total);
98
+ perf?.markInteraction('Top table render', topTableResponse.total);
89
99
  }
90
100
 
91
101
  if (!callgraphLoading && callgraphResponse?.report.oneofKind === 'callgraph') {
92
- perf?.markInteraction('Callgraph render', callgraphResponse?.report?.callgraph.cumulative);
102
+ perf?.markInteraction('Callgraph render', callgraphResponse.total);
93
103
  }
94
104
  }, [
95
105
  flamegraphLoading,
@@ -144,6 +154,10 @@ export const ProfileViewWithData = ({
144
154
  flamegraphResponse?.report.oneofKind === 'flamegraph'
145
155
  ? flamegraphResponse?.report?.flamegraph
146
156
  : undefined,
157
+ table:
158
+ flamegraphResponse?.report.oneofKind === 'flamegraphArrow'
159
+ ? tableFromIPC(flamegraphResponse?.report?.flamegraphArrow.record)
160
+ : undefined,
147
161
  total: BigInt(flamegraphResponse?.total ?? '0'),
148
162
  filtered: BigInt(flamegraphResponse?.filtered ?? '0'),
149
163
  error: flamegraphError,