Skip to content

Commit 225b3a0

Browse files
committed
ui: Refactor Flame Graph color mapping logic
1 parent f861a71 commit 225b3a0

3 files changed

Lines changed: 32 additions & 51 deletions

File tree

ui/packages/shared/profile/src/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,6 @@ export const FlameNode = React.memo(
130130
const colorAttribute =
131131
colorBy === 'filename' ? filename : colorBy === 'binary' ? mappingFile : null;
132132

133-
const colorsMap = colors;
134-
135133
const hoveringName =
136134
hoveringRow !== undefined ? arrowToString(functionNameColumn?.get(hoveringRow)) : '';
137135
const shouldBeHighlighted =
@@ -142,7 +140,7 @@ export const FlameNode = React.memo(
142140
compareMode,
143141
cumulative,
144142
diff,
145-
colorsMap,
143+
colorsMap: colors,
146144
colorAttribute,
147145
});
148146

@@ -296,7 +294,9 @@ export const FlameNode = React.memo(
296294
prevProps.hoveringRow === nextProps.hoveringRow &&
297295
prevProps.totalWidth === nextProps.totalWidth &&
298296
prevProps.height === nextProps.height &&
299-
prevProps.effectiveDepth === nextProps.effectiveDepth
297+
prevProps.effectiveDepth === nextProps.effectiveDepth &&
298+
prevProps.colorBy === nextProps.colorBy &&
299+
prevProps.colors === nextProps.colors
300300
);
301301
}
302302
);

ui/packages/shared/profile/src/ProfileFlameGraph/FlameGraphArrow/index.tsx

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ import React, {
2121
useState,
2222
} from 'react';
2323

24-
import {Dictionary, Table, Vector, tableFromIPC} from 'apache-arrow';
24+
import {Table, tableFromIPC} from 'apache-arrow';
2525
import {useContextMenu} from 'react-contexify';
2626

2727
import {FlamegraphArrow} from '@parca/client';
28-
import {useParcaContext, useURLState} from '@parca/components';
28+
import {useParcaContext} from '@parca/components';
2929
import {USER_PREFERENCES, useCurrentColorProfile, useUserPreference} from '@parca/hooks';
3030
import {ProfileType} from '@parca/parser';
3131
import {getColorForFeature, selectDarkMode, useAppSelector} from '@parca/store';
32-
import {getLastItem, type ColorConfig} from '@parca/utilities';
32+
import {type ColorConfig} from '@parca/utilities';
3333

3434
import {ProfileSource} from '../../ProfileSource';
3535
import {useProfileFilters} from '../../ProfileView/components/ProfileFilters/useProfileFilters';
@@ -38,12 +38,10 @@ import ContextMenuWrapper, {ContextMenuWrapperRef} from './ContextMenuWrapper';
3838
import {FlameNode, RowHeight, colorByColors} from './FlameGraphNodes';
3939
import {MemoizedTooltip} from './MemoizedTooltip';
4040
import {TooltipProvider} from './TooltipContext';
41-
import {useFilenamesList} from './useMappingList';
4241
import {useScrollViewport} from './useScrollViewport';
4342
import {useVisibleNodes} from './useVisibleNodes';
4443
import {
4544
CurrentPathFrame,
46-
arrowToString,
4745
extractFeature,
4846
extractFilenameFeature,
4947
getCurrentPathFrameData,
@@ -84,6 +82,8 @@ interface FlameGraphArrowProps {
8482
setCurPath: (path: CurrentPathFrame[]) => void;
8583
isHalfScreen: boolean;
8684
mappingsListFromMetadata: string[];
85+
filenamesListFromMetadata: string[];
86+
colorBy: string;
8787
compareAbsolute: boolean;
8888
isFlameChart?: boolean;
8989
isRenderedAsFlamegraph?: boolean;
@@ -139,6 +139,9 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
139139
tooltipId = 'default',
140140
maxFrameCount,
141141
isExpanded = false,
142+
mappingsListFromMetadata,
143+
filenamesListFromMetadata,
144+
colorBy,
142145
}: FlameGraphArrowProps): React.JSX.Element {
143146
const [highlightSimilarStacksPreference] = useUserPreference<boolean>(
144147
USER_PREFERENCES.HIGHLIGHT_SIMILAR_STACKS.key
@@ -169,49 +172,17 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
169172
const currentColorProfile = useCurrentColorProfile();
170173
const colorForSimilarNodes = currentColorProfile.colorForSimilarNodes;
171174

172-
const [colorBy, _] = useURLState('color_by');
173-
const colorByValue = colorBy === undefined || colorBy === '' ? 'binary' : (colorBy as string);
174-
175-
const filenamesList = useFilenamesList(table);
176-
177-
const mappingsList = useMemo(() => {
178-
// Read the mappings from the dictionary that contains all mapping strings.
179-
// This is great, as might only have a dozen or so mappings,
180-
// and don't need to read through all the rows (potentially thousands).
181-
const mappingsDict: Vector<Dictionary> | null = table.getChild(FIELD_MAPPING_FILE);
182-
const mappings =
183-
mappingsDict?.data
184-
.map(mapping => {
185-
if (mapping.dictionary == null) {
186-
return [];
187-
}
188-
const len = mapping.dictionary.length;
189-
const entries: string[] = [];
190-
for (let i = 0; i < len; i++) {
191-
const fn = arrowToString(mapping.dictionary.get(i));
192-
entries.push(getLastItem(fn) ?? '');
193-
}
194-
return entries;
195-
})
196-
.flat() ?? [];
197-
198-
// We add a EVERYTHING ELSE mapping to the list.
199-
mappings.push('');
200-
201-
// We sort the mappings alphabetically to make sure that the order is always the same.
202-
mappings.sort((a, b) => a.localeCompare(b));
203-
return mappings;
204-
}, [table]);
175+
const colorByValue = colorBy === undefined || colorBy === '' ? 'binary' : colorBy;
205176

206177
const filenameColors = useMemo(() => {
207-
const colors = getFilenameColors(filenamesList, isDarkMode, currentColorProfile);
178+
const colors = getFilenameColors(filenamesListFromMetadata, isDarkMode, currentColorProfile);
208179
return colors;
209-
}, [isDarkMode, filenamesList, currentColorProfile]);
180+
}, [isDarkMode, filenamesListFromMetadata, currentColorProfile]);
210181

211182
const mappingColors = useMemo(() => {
212-
const colors = getMappingColors(mappingsList, isDarkMode, currentColorProfile);
183+
const colors = getMappingColors(mappingsListFromMetadata, isDarkMode, currentColorProfile);
213184
return colors;
214-
}, [isDarkMode, mappingsList, currentColorProfile]);
185+
}, [isDarkMode, mappingsListFromMetadata, currentColorProfile]);
215186

216187
const colorByList = {
217188
filename: filenameColors,

ui/packages/shared/profile/src/ProfileFlameGraph/index.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
3131
import {MergedProfileSource, ProfileSource} from '../ProfileSource';
3232
import DiffLegend from '../ProfileView/components/DiffLegend';
3333
import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
34+
import {useProfileMetadata} from '../ProfileView/hooks/useProfileMetadata';
35+
import {useVisualizationState} from '../ProfileView/hooks/useVisualizationState';
3436
import {TimelineGuide} from '../TimelineGuide';
3537
import {FlameGraphArrow} from './FlameGraphArrow';
36-
import useMappingList from './FlameGraphArrow/useMappingList';
3738
import {CurrentPathFrame, boundsFromProfileSource} from './FlameGraphArrow/utils';
3839

3940
const numberFormatter = new Intl.NumberFormat('en-US');
@@ -100,11 +101,13 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
100101
tooltipId,
101102
maxFrameCount,
102103
isExpanded = false,
104+
metadataLoading = false,
103105
}: ProfileFlameGraphProps): JSX.Element {
104106
const {onError, authenticationErrorMessage, isDarkMode, flamechartHelpText} = useParcaContext();
105107
const {compareMode} = useProfileViewContext();
106108
const [isLoading, setIsLoading] = useState<boolean>(true);
107109
const [flameChartRef, {height: flameChartHeight}] = useMeasure();
110+
const {colorBy, setColorBy} = useVisualizationState();
108111

109112
// Create local state for paths when in sandwich view to avoid URL updates
110113
const [localCurPathArrow, setLocalCurPathArrow] = useState<CurrentPathFrame[]>([]);
@@ -123,9 +126,12 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
123126
// Determine which paths to use based on isInSandwichView flag
124127
const effectiveCurPathArrow = isInSandwichView ? localCurPathArrow : curPathArrow;
125128

126-
const mappingsList = useMappingList(metadataMappingFiles);
127-
128-
const [colorBy, setColorBy] = useURLState('color_by');
129+
const {mappingsList, filenamesList} = useProfileMetadata({
130+
flamegraphArrow: arrow,
131+
metadataMappingFiles,
132+
metadataLoading,
133+
colorBy,
134+
});
129135

130136
// By default, we want delta profiles (CPU) to be relatively compared.
131137
// For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
@@ -279,6 +285,7 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
279285
profileType={profileType}
280286
isHalfScreen={isHalfScreen}
281287
mappingsListFromMetadata={mappingsList}
288+
filenamesListFromMetadata={filenamesList}
282289
compareAbsolute={isCompareAbsolute}
283290
isFlameChart={isFlameChart}
284291
profileSource={profileSource}
@@ -287,6 +294,7 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
287294
tooltipId={tooltipId}
288295
maxFrameCount={maxFrameCount}
289296
isExpanded={isExpanded}
297+
colorBy={colorBy}
290298
/>
291299
</div>
292300
</div>
@@ -302,7 +310,6 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
302310
profileType,
303311
isHalfScreen,
304312
isDarkMode,
305-
mappingsList,
306313
isCompareAbsolute,
307314
isFlameChart,
308315
profileSource,
@@ -316,6 +323,9 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
316323
tooltipId,
317324
maxFrameCount,
318325
isExpanded,
326+
mappingsList,
327+
filenamesList,
328+
colorBy,
319329
]);
320330

321331
useEffect(() => {

0 commit comments

Comments
 (0)