@parca/profile 0.16.400 → 0.16.402

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 CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.16.402](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.401...@parca/profile@0.16.402) (2024-07-04)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.401](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.400...@parca/profile@0.16.401) (2024-07-03)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.400](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.399...@parca/profile@0.16.400) (2024-07-02)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -43,14 +43,16 @@ export const DockedGraphTooltip = ({ table, total, totalUnfiltered, row, level,
43
43
  if (graphTooltipData === null) {
44
44
  return _jsx(_Fragment, {});
45
45
  }
46
- const { name, cumulativeText, diffText, diff } = graphTooltipData;
46
+ const { name, cumulativeText, cumulativePerSecondText, flatText, flatPerSecondText, diffText, diff, } = graphTooltipData;
47
47
  const labels = labelPairs.map((l) => (_jsx("span", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: `${l[0]}="${l[1]}"` }, l[0])));
48
48
  const isMappingBuildIDAvailable = mappingBuildID !== null && mappingBuildID !== '';
49
49
  const inlinedText = inlined === null ? 'merged' : inlined ? 'yes' : 'no';
50
50
  const addressText = locationAddress !== 0n ? hexifyAddress(locationAddress) : _jsx(NoData, {});
51
+ const cumulativeTextBoth = `${cumulativeText}\n${cumulativePerSecondText}`;
52
+ const flatTextBoth = `${flatText}\n${flatPerSecondText}`;
51
53
  return (_jsxs("div", { className: "fixed bottom-0 z-20 overflow-hidden rounded-t-lg border-l border-r border-t border-gray-400 bg-white bg-opacity-90 px-8 py-3 dark:border-gray-600 dark:bg-black dark:bg-opacity-80", style: { width }, children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { className: "flex justify-between gap-4", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx("p", { children: name !== ''
52
54
  ? name
53
55
  : locationAddress !== 0n
54
56
  ? hexifyAddress(locationAddress)
55
- : 'unknown' })) }), _jsxs("div", { className: "flex justify-between gap-3", children: [_jsx(InfoSection, { title: "Cumulative", value: cumulativeText, minWidth: "w-44" }), diff !== 0n ? _jsx(InfoSection, { title: "Diff", value: diffText, minWidth: "w-44" }) : null, _jsx(InfoSection, { title: "File", value: functionFilename !== '' ? truncateStringReverse(file, 45) : _jsx(NoData, {}), minWidth: 'w-[460px]' }), _jsx(InfoSection, { title: "Address", value: addressText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Inlined", value: inlinedText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Binary", value: (mappingFile != null ? getLastItem(mappingFile) : null) ?? _jsx(NoData, {}), minWidth: "w-44" }), _jsx(InfoSection, { title: "Build ID", value: isMappingBuildIDAvailable ? (_jsx("div", { children: truncateString(mappingBuildID, 28) })) : (_jsx(NoData, {})) })] }), _jsx("div", { children: _jsx("div", { className: "flex h-5 gap-1", children: labels }) })] }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to show context menu" })] })] }));
57
+ : 'unknown' })) }), _jsxs("div", { className: "flex justify-between gap-3", children: [_jsx(InfoSection, { title: "Cumulative", value: cumulativeTextBoth, minWidth: "w-44" }), _jsx(InfoSection, { title: "Flat", value: flatTextBoth, minWidth: "w-44" }), diff !== 0n ? _jsx(InfoSection, { title: "Diff", value: diffText, minWidth: "w-44" }) : null, _jsx(InfoSection, { title: "File", value: functionFilename !== '' ? truncateStringReverse(file, 45) : _jsx(NoData, {}), minWidth: 'w-[460px]' }), _jsx(InfoSection, { title: "Address", value: addressText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Inlined", value: inlinedText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Binary", value: (mappingFile != null ? getLastItem(mappingFile) : null) ?? _jsx(NoData, {}), minWidth: "w-44" }), _jsx(InfoSection, { title: "Build ID", value: isMappingBuildIDAvailable ? (_jsx("div", { children: truncateString(mappingBuildID, 28) })) : (_jsx(NoData, {})) })] }), _jsx("div", { children: _jsx("div", { className: "flex h-5 gap-1", children: labels }) })] }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to show context menu" })] })] }));
56
58
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Card } from '@parca/components';
2
+ import { Card, useURLState } from '@parca/components';
3
3
  import { Query } from '@parca/parser';
4
4
  import { ProfileDiffSource, ProfileViewWithData } from '..';
5
5
  import ProfileSelector from '../ProfileSelector';
@@ -10,6 +10,7 @@ const ProfileExplorerCompare = ({ queryClient, queryA, queryB, profileA, profile
10
10
  const closeProfileB = () => {
11
11
  closeProfile('B');
12
12
  };
13
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex justify-between gap-2", children: [_jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true, navigateTo: navigateTo }) }), _jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true, navigateTo: navigateTo }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { children: _jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileViewWithData, { navigateTo: navigateTo, queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource()) }) }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
13
+ const [compareAbsolute] = useURLState({ param: 'compare_absolute', navigateTo });
14
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex justify-between gap-2", children: [_jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true, navigateTo: navigateTo }) }), _jsx(Card, { className: "mt-2 p-2", children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true, navigateTo: navigateTo }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { children: _jsx(Card, { className: "mt-2 px-6 py-4", children: _jsx(ProfileViewWithData, { navigateTo: navigateTo, queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource(), '', compareAbsolute === 'true') }) }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
14
15
  };
15
16
  export default ProfileExplorerCompare;
@@ -93,6 +93,15 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, to
93
93
  navigateTo,
94
94
  });
95
95
  const isInvert = invertStack === 'true';
96
+ // By default, we want delta profiles (CPU) to be relatively compared.
97
+ // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
98
+ const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
99
+ const [compareAbsolute = compareAbsoluteDefault, setCompareAbsolute] = useURLState({
100
+ param: 'compare_absolute',
101
+ navigateTo,
102
+ withURLUpdate: true,
103
+ });
104
+ const isCompareAbsolute = compareAbsolute === 'true';
96
105
  const [totalFormatted, totalUnfilteredFormatted, isTrimmed, trimmedFormatted, trimmedPercentage, isFiltered, filteredPercentage,] = useMemo(() => {
97
106
  if (graph === undefined && arrow === undefined) {
98
107
  return ['0', '0', false, '0', '0', false, '0', '0'];
@@ -112,7 +121,7 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, to
112
121
  ];
113
122
  }, [graph, arrow, filtered, total]);
114
123
  useEffect(() => {
115
- setActionButtons?.(_jsx("div", { className: "flex w-full justify-end gap-2 pb-2", children: _jsxs("div", { className: "ml-2 flex w-full flex-col items-start justify-between gap-2 md:flex-row md:items-end", children: [_jsx(GroupAndSortActionButtons, { navigateTo: navigateTo }), isHalfScreen ? (_jsx(IconButton, { icon: isInvert ? 'ph:sort-ascending' : 'ph:sort-descending', toolTipText: isInvert ? 'Original Call Stack' : 'Invert Call Stack', onClick: () => setInvertStack(isInvert ? '' : 'true'), className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center py-2 px-3 cursor-pointer min-h-[38px]" })) : (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setInvertStack(isInvert ? '' : 'true'), children: [isInvert ? 'Original Call Stack' : 'Invert Call Stack', _jsx(Icon, { icon: isInvert ? 'ph:sort-ascending' : 'ph:sort-descending', width: 20 })] })), _jsx(ShowHideLegendButton, { isHalfScreen: isHalfScreen, navigateTo: navigateTo }), isHalfScreen ? (_jsx(IconButton, { icon: "system-uicons:reset", disabled: curPath.length === 0, toolTipText: "Reset View", onClick: () => setNewCurPath([]), className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center py-2 px-3 cursor-pointer min-h-[38px]" })) : (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setNewCurPath([]), disabled: curPath.length === 0, children: ["Reset View", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }))] }) }));
124
+ setActionButtons?.(_jsx("div", { className: "flex w-full justify-end gap-2 pb-2", children: _jsxs("div", { className: "ml-2 flex w-full flex-col items-start justify-between gap-2 md:flex-row md:items-end", children: [_jsx(GroupAndSortActionButtons, { navigateTo: navigateTo }), isHalfScreen ? (_jsx(IconButton, { icon: isInvert ? 'ph:sort-ascending' : 'ph:sort-descending', toolTipText: isInvert ? 'Original Call Stack' : 'Invert Call Stack', onClick: () => setInvertStack(isInvert ? '' : 'true'), className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center py-2 px-3 cursor-pointer min-h-[38px]" })) : (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setInvertStack(isInvert ? '' : 'true'), children: [isInvert ? 'Original Call Stack' : 'Invert Call Stack', _jsx(Icon, { icon: isInvert ? 'ph:sort-ascending' : 'ph:sort-descending', width: 20 })] })), _jsx(ShowHideLegendButton, { isHalfScreen: isHalfScreen, navigateTo: navigateTo }), compareMode && (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setCompareAbsolute(isCompareAbsolute ? '' : 'true'), children: [isCompareAbsolute ? 'Compare Relative' : 'Compare Absolute', _jsx(Icon, { icon: isCompareAbsolute ? 'fluent-mdl2:compare' : 'fluent-mdl2:compare-uneven', width: 20 })] })), isHalfScreen ? (_jsx(IconButton, { icon: "system-uicons:reset", disabled: curPath.length === 0, toolTipText: "Reset View", onClick: () => setNewCurPath([]), className: "rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 items-center flex border border-gray-200 dark:border-gray-600 dark:text-white justify-center py-2 px-3 cursor-pointer min-h-[38px]" })) : (_jsxs(Button, { variant: "neutral", className: "gap-2 w-max", onClick: () => setNewCurPath([]), disabled: curPath.length === 0, children: ["Reset View", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }))] }) }));
116
125
  }, [
117
126
  navigateTo,
118
127
  isInvert,
@@ -124,6 +133,9 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({ graph, arrow, to
124
133
  loading,
125
134
  isHalfScreen,
126
135
  isLoading,
136
+ compareMode,
137
+ isCompareAbsolute,
138
+ setCompareAbsolute,
127
139
  ]);
128
140
  const loadingState = !loading && (arrow !== undefined || graph !== undefined) && mappings !== undefined;
129
141
  useEffect(() => {
@@ -42,7 +42,8 @@ export declare class ProfileDiffSource implements ProfileSource {
42
42
  b: ProfileSource;
43
43
  filterQuery: string | undefined;
44
44
  profileType: ProfileType;
45
- constructor(a: ProfileSource, b: ProfileSource, filterQuery?: string);
45
+ absolute?: boolean;
46
+ constructor(a: ProfileSource, b: ProfileSource, filterQuery?: string, absolute?: boolean);
46
47
  DiffSelection(): ProfileDiffSelection;
47
48
  QueryRequest(): QueryRequest;
48
49
  ProfileType(): ProfileType;
@@ -73,11 +73,12 @@ export class MergedProfileSelection {
73
73
  }
74
74
  }
75
75
  export class ProfileDiffSource {
76
- constructor(a, b, filterQuery) {
76
+ constructor(a, b, filterQuery, absolute) {
77
77
  this.a = a;
78
78
  this.b = b;
79
79
  this.filterQuery = filterQuery;
80
80
  this.profileType = a.ProfileType();
81
+ this.absolute = absolute;
81
82
  }
82
83
  DiffSelection() {
83
84
  throw new Error('Method not implemented.');
@@ -89,9 +90,10 @@ export class ProfileDiffSource {
89
90
  diff: {
90
91
  a: this.a.DiffSelection(),
91
92
  b: this.b.DiffSelection(),
93
+ absolute: this.absolute,
92
94
  },
93
95
  },
94
- reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
96
+ reportType: QueryRequest_ReportType.FLAMEGRAPH_ARROW,
95
97
  mode: QueryRequest_Mode.DIFF,
96
98
  filterQuery: this.filterQuery,
97
99
  filter: [],
@@ -143,7 +145,7 @@ export class MergedProfileSource {
143
145
  query: this.query.toString(),
144
146
  },
145
147
  },
146
- reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
148
+ reportType: QueryRequest_ReportType.FLAMEGRAPH_ARROW,
147
149
  mode: QueryRequest_Mode.MERGE,
148
150
  filterQuery: this.filterQuery,
149
151
  filter: [],
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.400",
3
+ "version": "0.16.402",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
7
7
  "@iconify/react": "^4.0.0",
8
- "@parca/client": "0.16.119",
9
- "@parca/components": "0.16.289",
8
+ "@parca/client": "0.16.120",
9
+ "@parca/components": "0.16.290",
10
10
  "@parca/dynamicsize": "0.16.65",
11
- "@parca/hooks": "0.0.64",
11
+ "@parca/hooks": "0.0.65",
12
12
  "@parca/icons": "0.16.70",
13
13
  "@parca/parser": "0.16.77",
14
- "@parca/store": "0.16.153",
15
- "@parca/utilities": "0.0.80",
14
+ "@parca/store": "0.16.154",
15
+ "@parca/utilities": "0.0.81",
16
16
  "@popperjs/core": "^2.11.8",
17
17
  "@protobuf-ts/runtime-rpc": "^2.5.0",
18
18
  "@tanstack/react-query": "^4.0.5",
@@ -73,5 +73,5 @@
73
73
  "access": "public",
74
74
  "registry": "https://registry.npmjs.org/"
75
75
  },
76
- "gitHead": "9d13687d6648e70412369d67999b77633d384199"
76
+ "gitHead": "25851d40d2630dbb542747d3a09718363212ad0e"
77
77
  }
@@ -93,7 +93,15 @@ export const DockedGraphTooltip = ({
93
93
  return <></>;
94
94
  }
95
95
 
96
- const {name, cumulativeText, diffText, diff} = graphTooltipData;
96
+ const {
97
+ name,
98
+ cumulativeText,
99
+ cumulativePerSecondText,
100
+ flatText,
101
+ flatPerSecondText,
102
+ diffText,
103
+ diff,
104
+ } = graphTooltipData;
97
105
 
98
106
  const labels = labelPairs.map(
99
107
  (l): React.JSX.Element => (
@@ -110,6 +118,9 @@ export const DockedGraphTooltip = ({
110
118
  const inlinedText = inlined === null ? 'merged' : inlined ? 'yes' : 'no';
111
119
  const addressText = locationAddress !== 0n ? hexifyAddress(locationAddress) : <NoData />;
112
120
 
121
+ const cumulativeTextBoth = `${cumulativeText}\n${cumulativePerSecondText}`;
122
+ const flatTextBoth = `${flatText}\n${flatPerSecondText}`;
123
+
113
124
  return (
114
125
  <div
115
126
  className="fixed bottom-0 z-20 overflow-hidden rounded-t-lg border-l border-r border-t border-gray-400 bg-white bg-opacity-90 px-8 py-3 dark:border-gray-600 dark:bg-black dark:bg-opacity-80"
@@ -130,7 +141,8 @@ export const DockedGraphTooltip = ({
130
141
  )}
131
142
  </div>
132
143
  <div className="flex justify-between gap-3">
133
- <InfoSection title="Cumulative" value={cumulativeText} minWidth="w-44" />
144
+ <InfoSection title="Cumulative" value={cumulativeTextBoth} minWidth="w-44" />
145
+ <InfoSection title="Flat" value={flatTextBoth} minWidth="w-44" />
134
146
  {diff !== 0n ? <InfoSection title="Diff" value={diffText} minWidth="w-44" /> : null}
135
147
  <InfoSection
136
148
  title="File"
@@ -12,7 +12,7 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {QueryServiceClient} from '@parca/client';
15
- import {Card} from '@parca/components';
15
+ import {Card, useURLState} from '@parca/components';
16
16
  import {Query} from '@parca/parser';
17
17
  import type {NavigateFunction} from '@parca/utilities';
18
18
 
@@ -56,6 +56,8 @@ const ProfileExplorerCompare = ({
56
56
  closeProfile('B');
57
57
  };
58
58
 
59
+ const [compareAbsolute] = useURLState({param: 'compare_absolute', navigateTo});
60
+
59
61
  return (
60
62
  <>
61
63
  <div className="flex justify-between gap-2">
@@ -94,7 +96,12 @@ const ProfileExplorerCompare = ({
94
96
  navigateTo={navigateTo}
95
97
  queryClient={queryClient}
96
98
  profileSource={
97
- new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource())
99
+ new ProfileDiffSource(
100
+ profileA.ProfileSource(),
101
+ profileB.ProfileSource(),
102
+ '',
103
+ compareAbsolute === 'true'
104
+ )
98
105
  }
99
106
  />
100
107
  </Card>
@@ -238,9 +238,19 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
238
238
  param: 'invert_call_stack',
239
239
  navigateTo,
240
240
  });
241
-
242
241
  const isInvert = invertStack === 'true';
243
242
 
243
+ // By default, we want delta profiles (CPU) to be relatively compared.
244
+ // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
245
+ const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
246
+
247
+ const [compareAbsolute = compareAbsoluteDefault, setCompareAbsolute] = useURLState({
248
+ param: 'compare_absolute',
249
+ navigateTo,
250
+ withURLUpdate: true,
251
+ });
252
+ const isCompareAbsolute = compareAbsolute === 'true';
253
+
244
254
  const [
245
255
  totalFormatted,
246
256
  totalUnfilteredFormatted,
@@ -294,6 +304,19 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
294
304
  </Button>
295
305
  )}
296
306
  <ShowHideLegendButton isHalfScreen={isHalfScreen} navigateTo={navigateTo} />
307
+ {compareMode && (
308
+ <Button
309
+ variant="neutral"
310
+ className="gap-2 w-max"
311
+ onClick={() => setCompareAbsolute(isCompareAbsolute ? '' : 'true')}
312
+ >
313
+ {isCompareAbsolute ? 'Compare Relative' : 'Compare Absolute'}
314
+ <Icon
315
+ icon={isCompareAbsolute ? 'fluent-mdl2:compare' : 'fluent-mdl2:compare-uneven'}
316
+ width={20}
317
+ />
318
+ </Button>
319
+ )}
297
320
  {isHalfScreen ? (
298
321
  <IconButton
299
322
  icon="system-uicons:reset"
@@ -327,6 +350,9 @@ const ProfileIcicleGraph = function ProfileIcicleGraphNonMemo({
327
350
  loading,
328
351
  isHalfScreen,
329
352
  isLoading,
353
+ compareMode,
354
+ isCompareAbsolute,
355
+ setCompareAbsolute,
330
356
  ]);
331
357
 
332
358
  const loadingState =
@@ -136,12 +136,14 @@ export class ProfileDiffSource implements ProfileSource {
136
136
  b: ProfileSource;
137
137
  filterQuery: string | undefined;
138
138
  profileType: ProfileType;
139
+ absolute?: boolean;
139
140
 
140
- constructor(a: ProfileSource, b: ProfileSource, filterQuery?: string) {
141
+ constructor(a: ProfileSource, b: ProfileSource, filterQuery?: string, absolute?: boolean) {
141
142
  this.a = a;
142
143
  this.b = b;
143
144
  this.filterQuery = filterQuery;
144
145
  this.profileType = a.ProfileType();
146
+ this.absolute = absolute;
145
147
  }
146
148
 
147
149
  DiffSelection(): ProfileDiffSelection {
@@ -155,9 +157,10 @@ export class ProfileDiffSource implements ProfileSource {
155
157
  diff: {
156
158
  a: this.a.DiffSelection(),
157
159
  b: this.b.DiffSelection(),
160
+ absolute: this.absolute,
158
161
  },
159
162
  },
160
- reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
163
+ reportType: QueryRequest_ReportType.FLAMEGRAPH_ARROW,
161
164
  mode: QueryRequest_Mode.DIFF,
162
165
  filterQuery: this.filterQuery,
163
166
  filter: [],
@@ -227,7 +230,7 @@ export class MergedProfileSource implements ProfileSource {
227
230
  query: this.query.toString(),
228
231
  },
229
232
  },
230
- reportType: QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED,
233
+ reportType: QueryRequest_ReportType.FLAMEGRAPH_ARROW,
231
234
  mode: QueryRequest_Mode.MERGE,
232
235
  filterQuery: this.filterQuery,
233
236
  filter: [],