@parca/profile 0.16.374 → 0.16.376

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 (41) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.js +1 -1
  3. package/dist/ProfileIcicleGraph/IcicleGraph/utils.d.ts +1 -1
  4. package/dist/ProfileIcicleGraph/IcicleGraph/utils.js +1 -5
  5. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ColorStackLegend.d.ts +4 -4
  6. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ColorStackLegend.js +50 -13
  7. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts +2 -1
  8. package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +3 -2
  9. package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +0 -1
  10. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.d.ts +5 -1
  11. package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +48 -50
  12. package/dist/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.d.ts +1 -2
  13. package/dist/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.js +2 -7
  14. package/dist/ProfileIcicleGraph/IcicleGraphArrow/utils.js +0 -3
  15. package/dist/ProfileIcicleGraph/index.d.ts +3 -1
  16. package/dist/ProfileIcicleGraph/index.js +57 -36
  17. package/dist/ProfileSource.js +2 -0
  18. package/dist/ProfileView/index.d.ts +2 -0
  19. package/dist/ProfileView/index.js +1 -1
  20. package/dist/ProfileViewWithData.js +17 -10
  21. package/dist/Table/index.js +2 -9
  22. package/dist/useQuery.d.ts +1 -3
  23. package/dist/useQuery.js +21 -8
  24. package/package.json +7 -7
  25. package/src/ProfileIcicleGraph/IcicleGraph/useColoredGraph.ts +1 -1
  26. package/src/ProfileIcicleGraph/IcicleGraph/utils.ts +1 -7
  27. package/src/ProfileIcicleGraph/IcicleGraphArrow/ColorStackLegend.tsx +62 -16
  28. package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +21 -1
  29. package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +0 -1
  30. package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +66 -61
  31. package/src/ProfileIcicleGraph/IcicleGraphArrow/useNodeColor.ts +1 -8
  32. package/src/ProfileIcicleGraph/IcicleGraphArrow/utils.ts +0 -5
  33. package/src/ProfileIcicleGraph/index.tsx +142 -96
  34. package/src/ProfileSource.tsx +2 -0
  35. package/src/ProfileView/index.tsx +4 -0
  36. package/src/ProfileViewWithData.tsx +26 -10
  37. package/src/Table/index.tsx +1 -11
  38. package/src/useQuery.tsx +22 -11
  39. package/dist/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.d.ts +0 -9
  40. package/dist/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.js +0 -23
  41. package/src/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.tsx +0 -123
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.376 (2024-05-30)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.375](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.374...@parca/profile@0.16.375) (2024-05-23)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## 0.16.374 (2024-05-21)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -25,7 +25,7 @@ const colorNodes = (nodes, strings, mappings, locations, functions, features) =>
25
25
  if (node.children != null) {
26
26
  coloredNode.children = colorNodes(node.children, strings, mappings, locations, functions, features);
27
27
  }
28
- const feature = extractFeature(node, mappings, locations, strings, functions);
28
+ const feature = extractFeature(node, mappings, locations, strings);
29
29
  coloredNode.feature = feature.name;
30
30
  features[feature.name] = feature.type;
31
31
  return coloredNode;
@@ -3,4 +3,4 @@ import { Location, Mapping, Function as ParcaFunction } from '@parca/client/dist
3
3
  import { type Feature } from '@parca/store';
4
4
  export declare const getBinaryName: (node: FlamegraphNode, mappings: Mapping[], locations: Location[], strings: string[]) => string | undefined;
5
5
  export declare function nodeLabel(node: FlamegraphNode, strings: string[], mappings: Mapping[], locations: Location[], functions: ParcaFunction[], showBinaryName: boolean): string;
6
- export declare const extractFeature: (data: FlamegraphNode, mappings: Mapping[], locations: Location[], strings: string[], functions: ParcaFunction[]) => Feature;
6
+ export declare const extractFeature: (data: FlamegraphNode, mappings: Mapping[], locations: Location[], strings: string[]) => Feature;
@@ -57,11 +57,7 @@ export function nodeLabel(node, strings, mappings, locations, functions, showBin
57
57
  const fallback = `${mappingString}${address}`;
58
58
  return fallback === '' ? '<unknown>' : fallback;
59
59
  }
60
- export const extractFeature = (data, mappings, locations, strings, functions) => {
61
- const name = nodeLabel(data, strings, mappings, locations, functions, false).trim();
62
- if (name.startsWith('runtime') || name === 'root') {
63
- return { name: 'runtime', type: FEATURE_TYPES.Runtime };
64
- }
60
+ export const extractFeature = (data, mappings, locations, strings) => {
65
61
  const binaryName = getBinaryName(data, mappings, locations, strings);
66
62
  if (binaryName != null) {
67
63
  return { name: binaryName, type: FEATURE_TYPES.Binary };
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
- import type { NavigateFunction } from '@parca/utilities';
3
- import { mappingColors } from './IcicleGraphNodes';
2
+ import { type NavigateFunction } from '@parca/utilities';
4
3
  interface Props {
5
- mappingColors: mappingColors;
4
+ mappings?: string[];
5
+ mappingsLoading?: boolean;
6
6
  navigateTo?: NavigateFunction;
7
7
  compareMode?: boolean;
8
8
  }
9
- declare const ColorStackLegend: ({ mappingColors, navigateTo, compareMode, }: Props) => React.JSX.Element;
9
+ declare const ColorStackLegend: ({ mappings, navigateTo, compareMode, mappingsLoading, }: Props) => React.JSX.Element;
10
10
  export default ColorStackLegend;
@@ -15,11 +15,37 @@ import { useMemo } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
17
  import { useURLState } from '@parca/components';
18
- import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
19
- import { EVERYTHING_ELSE } from '@parca/store';
20
- const ColorStackLegend = ({ mappingColors, navigateTo, compareMode = false, }) => {
18
+ import { USER_PREFERENCES, useCurrentColorProfile, useUserPreference } from '@parca/hooks';
19
+ import { EVERYTHING_ELSE, selectDarkMode, useAppSelector } from '@parca/store';
20
+ import { getLastItem } from '@parca/utilities';
21
+ import { getMappingColors } from '.';
22
+ const ColorStackLegend = ({ mappings, navigateTo, compareMode = false, mappingsLoading, }) => {
23
+ const isDarkMode = useAppSelector(selectDarkMode);
24
+ const currentColorProfile = useCurrentColorProfile();
21
25
  const [colorProfileName] = useUserPreference(USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key);
22
- const [currentSearchString, setSearchString] = useURLState({ param: 'search_string', navigateTo });
26
+ const [currentSearchString, setSearchString] = useURLState({
27
+ param: 'binary_frame_filter',
28
+ navigateTo,
29
+ });
30
+ const mappingsList = useMemo(() => {
31
+ if (mappings === undefined) {
32
+ return [];
33
+ }
34
+ const list = mappings
35
+ ?.map(mapping => {
36
+ return getLastItem(mapping);
37
+ })
38
+ .flat() ?? [];
39
+ // We add a EVERYTHING ELSE mapping to the list.
40
+ list.push('');
41
+ // We sort the mappings alphabetically to make sure that the order is always the same.
42
+ list.sort((a, b) => a.localeCompare(b));
43
+ return list;
44
+ }, [mappings]);
45
+ const mappingColors = useMemo(() => {
46
+ const colors = getMappingColors(mappingsList, isDarkMode, currentColorProfile);
47
+ return colors;
48
+ }, [isDarkMode, mappingsList, currentColorProfile]);
23
49
  const stackColorArray = useMemo(() => {
24
50
  return Object.entries(mappingColors).sort(([featureA], [featureB]) => {
25
51
  if (featureA === EVERYTHING_ELSE) {
@@ -31,7 +57,7 @@ const ColorStackLegend = ({ mappingColors, navigateTo, compareMode = false, }) =
31
57
  return featureA?.localeCompare(featureB ?? '') ?? 0;
32
58
  });
33
59
  }, [mappingColors]);
34
- if (mappingColors === undefined) {
60
+ if (mappingColors === undefined && mappingsLoading === false) {
35
61
  return _jsx(_Fragment, {});
36
62
  }
37
63
  if (Object.entries(mappingColors).length === 0) {
@@ -42,21 +68,32 @@ const ColorStackLegend = ({ mappingColors, navigateTo, compareMode = false, }) =
42
68
  }
43
69
  return (_jsx("div", { className: "my-4 flex w-full flex-wrap justify-start", children: stackColorArray.map(([feature, color]) => {
44
70
  const filteringAllowed = feature !== EVERYTHING_ELSE;
45
- const isHighlighted = currentSearchString === feature;
71
+ const isHighlighted = currentSearchString !== undefined ? currentSearchString.includes(feature) : false;
46
72
  return (_jsxs("div", { className: cx('flex-no-wrap mb-1 flex w-1/5 items-center justify-between text-ellipsis p-1', {
47
73
  'cursor-pointer': filteringAllowed,
48
74
  'bg-gray-200 dark:bg-gray-800': isHighlighted,
49
75
  }), onClick: () => {
50
- if (!filteringAllowed) {
76
+ if (!filteringAllowed || isHighlighted) {
51
77
  return;
52
78
  }
53
- if (isHighlighted) {
54
- setSearchString('');
55
- return;
56
- }
57
- setSearchString(feature);
79
+ // Check if the current search string is defined and an array
80
+ const updatedSearchString = Array.isArray(currentSearchString)
81
+ ? [...currentSearchString, feature] // If array, append the feature
82
+ : // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
83
+ currentSearchString // If not array, preserve current value
84
+ ? currentSearchString.split(',') // If string, split by commas
85
+ : [feature]; // If undefined, initialize array with feature
86
+ setSearchString(updatedSearchString);
58
87
  }, children: [_jsxs("div", { className: "flex w-11/12 items-center justify-start", children: [_jsx("div", { className: "flex w-5 items-center", children: _jsx("div", { className: "mr-1 inline-block h-4 w-4", style: { backgroundColor: color } }) }), _jsx("div", { className: "shrink overflow-hidden text-ellipsis whitespace-nowrap text-sm hover:whitespace-normal", children: feature })] }), _jsx("div", { className: "flex w-1/12 justify-end", children: isHighlighted && (_jsx(Icon, { icon: "radix-icons:cross-circled", onClick: e => {
59
- setSearchString('');
88
+ let searchString = [];
89
+ if (typeof currentSearchString === 'string') {
90
+ searchString.push(currentSearchString);
91
+ }
92
+ else {
93
+ searchString = currentSearchString;
94
+ }
95
+ // remove the current feature from the search string array of strings
96
+ setSearchString(searchString.filter((f) => f !== feature));
60
97
  e.stopPropagation();
61
98
  } })) })] }, feature));
62
99
  }) }));
@@ -15,6 +15,7 @@ interface ContextMenuProps {
15
15
  curPath: string[];
16
16
  setCurPath: (path: string[]) => void;
17
17
  hideMenu: () => void;
18
+ hideBinary: (binaryToRemove: string) => void;
18
19
  }
19
- declare const ContextMenu: ({ menuId, table, total, totalUnfiltered, row, level, navigateTo, trackVisibility, curPath, setCurPath, hideMenu, profileType, }: ContextMenuProps) => JSX.Element;
20
+ declare const ContextMenu: ({ menuId, table, total, totalUnfiltered, row, level, navigateTo, trackVisibility, curPath, setCurPath, hideMenu, profileType, hideBinary, }: ContextMenuProps) => JSX.Element;
20
21
  export default ContextMenu;
@@ -16,10 +16,11 @@ import { Item, Menu, Separator, Submenu } from 'react-contexify';
16
16
  import { Tooltip } from 'react-tooltip';
17
17
  import { useParcaContext } from '@parca/components';
18
18
  import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
19
+ import { getLastItem } from '@parca/utilities';
19
20
  import { useGraphTooltip } from '../../GraphTooltipArrow/useGraphTooltip';
20
21
  import { useGraphTooltipMetaInfo } from '../../GraphTooltipArrow/useGraphTooltipMetaInfo';
21
22
  import { hexifyAddress, truncateString } from '../../utils';
22
- const ContextMenu = ({ menuId, table, total, totalUnfiltered, row, level, navigateTo, trackVisibility, curPath, setCurPath, hideMenu, profileType, }) => {
23
+ const ContextMenu = ({ menuId, table, total, totalUnfiltered, row, level, navigateTo, trackVisibility, curPath, setCurPath, hideMenu, profileType, hideBinary, }) => {
23
24
  const { isDarkMode } = useParcaContext();
24
25
  const { enableSourcesView } = useParcaContext();
25
26
  const [isGraphTooltipDocked, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
@@ -75,6 +76,6 @@ const ContextMenu = ({ menuId, table, total, totalUnfiltered, row, level, naviga
75
76
  { id: 'Build Id', value: buildIdText },
76
77
  ];
77
78
  const nonEmptyValuesToCopy = valuesToCopy.filter(({ value }) => value !== '');
78
- return (_jsxs(Menu, { id: menuId, onVisibilityChange: trackVisibility, theme: isDarkMode ? 'dark' : '', children: [_jsxs(Item, { id: "view-source-file", onClick: handleViewSourceFile, disabled: enableSourcesView === false || !isSourceAvailable, children: [_jsx("div", { "data-tooltip-id": "view-source-file-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "wpf:view-file" }), _jsx("div", { children: "View source file" })] }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "view-source-file-help" }) : null] }), _jsx(Item, { id: "reset-view", onClick: handleResetView, disabled: curPath.length === 0, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "system-uicons:reset" }), _jsx("div", { children: "Reset view" })] }) }), _jsx(Submenu, { label: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "ph:copy" }), _jsx("div", { children: "Copy" })] }), children: _jsx("div", { className: "max-h-[300px] overflow-scroll", children: nonEmptyValuesToCopy.map(({ id, value }) => (_jsx(Item, { id: id, onClick: () => handleCopyItem(value), className: "dark:bg-gray-800", children: _jsxs("div", { className: "flex flex-col dark:text-gray-300 hover:dark:text-gray-100", children: [_jsx("div", { className: "text-sm", children: id }), _jsx("div", { className: "text-xs", children: truncateString(value, 30) })] }) }, id))) }) }), _jsx(Separator, {}), _jsx(Item, { id: "dock-tooltip", onClick: handleDockTooltip, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "bx:dock-bottom" }), isGraphTooltipDocked ? 'Undock tooltip' : 'Dock tooltip'] }) })] }));
79
+ return (_jsxs(Menu, { id: menuId, onVisibilityChange: trackVisibility, theme: isDarkMode ? 'dark' : '', children: [_jsxs(Item, { id: "view-source-file", onClick: handleViewSourceFile, disabled: enableSourcesView === false || !isSourceAvailable, children: [_jsx("div", { "data-tooltip-id": "view-source-file-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "wpf:view-file" }), _jsx("div", { children: "View source file" })] }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "view-source-file-help" }) : null] }), _jsx(Item, { id: "reset-view", onClick: handleResetView, disabled: curPath.length === 0, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "system-uicons:reset" }), _jsx("div", { children: "Reset view" })] }) }), _jsxs(Item, { id: "hide-binary", onClick: () => hideBinary(getLastItem(mappingFile)), disabled: mappingFile === null || mappingFile === '', children: [_jsx("div", { "data-tooltip-id": "hide-binary-help", "data-tooltip-content": "Hide all frames for this binary", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "bx:bxs-hide" }), _jsxs("div", { children: ["Hide Binary ", mappingFile !== null && `(${getLastItem(mappingFile)})`] })] }) }), _jsx(Tooltip, { place: "left", id: "hide-binary-help" })] }), _jsx(Submenu, { label: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "ph:copy" }), _jsx("div", { children: "Copy" })] }), children: _jsx("div", { className: "max-h-[300px] overflow-scroll", children: nonEmptyValuesToCopy.map(({ id, value }) => (_jsx(Item, { id: id, onClick: () => handleCopyItem(value), className: "dark:bg-gray-800", children: _jsxs("div", { className: "flex flex-col dark:text-gray-300 hover:dark:text-gray-100", children: [_jsx("div", { className: "text-sm", children: id }), _jsx("div", { className: "text-xs", children: truncateString(value, 30) })] }) }, id))) }) }), _jsx(Separator, {}), _jsx(Item, { id: "dock-tooltip", onClick: handleDockTooltip, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "bx:dock-bottom" }), isGraphTooltipDocked ? 'Undock tooltip' : 'Dock tooltip'] }) })] }));
79
80
  };
80
81
  export default ContextMenu;
@@ -178,7 +178,6 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({ table, row, map
178
178
  diffPerSecond,
179
179
  mappingColors,
180
180
  mappingFile,
181
- functionName,
182
181
  });
183
182
  const name = useMemo(() => {
184
183
  return isRoot ? 'root' : nodeLabel(table, row, level, binaries.length > 1);
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { FlamegraphArrow } from '@parca/client';
3
3
  import { ProfileType } from '@parca/parser';
4
- import { type NavigateFunction } from '@parca/utilities';
4
+ import { type ColorConfig, type NavigateFunction } from '@parca/utilities';
5
+ import { mappingColors } from './IcicleGraphNodes';
5
6
  export declare const FIELD_LABELS_ONLY = "labels_only";
6
7
  export declare const FIELD_MAPPING_FILE = "mapping_file";
7
8
  export declare const FIELD_MAPPING_BUILD_ID = "mapping_build_id";
@@ -28,6 +29,9 @@ interface IcicleGraphArrowProps {
28
29
  setCurPath: (path: string[]) => void;
29
30
  navigateTo?: NavigateFunction;
30
31
  sortBy: string;
32
+ flamegraphLoading: boolean;
33
+ isHalfScreen: boolean;
31
34
  }
35
+ export declare const getMappingColors: (mappingsList: string[], isDarkMode: boolean, currentColorProfile: ColorConfig) => mappingColors;
32
36
  export declare const IcicleGraphArrow: React.NamedExoticComponent<IcicleGraphArrowProps>;
33
37
  export default IcicleGraphArrow;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // Copyright 2022 The Parca Authors
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
14
14
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
15
15
  import { tableFromIPC } from 'apache-arrow';
16
16
  import { useContextMenu } from 'react-contexify';
17
+ import { useURLState } from '@parca/components';
17
18
  import { USER_PREFERENCES, useCurrentColorProfile, useUserPreference } from '@parca/hooks';
18
19
  import { getColorForFeature, selectDarkMode, setHoveringNode, useAppDispatch, useAppSelector, } from '@parca/store';
19
- import { getLastItem, scaleLinear, selectQueryParam } from '@parca/utilities';
20
+ import { getLastItem, scaleLinear, selectQueryParam, } from '@parca/utilities';
20
21
  import GraphTooltipArrow from '../../GraphTooltipArrow';
21
22
  import GraphTooltipArrowContent from '../../GraphTooltipArrow/Content';
22
23
  import { DockedGraphTooltip } from '../../GraphTooltipArrow/DockedGraphTooltip';
23
24
  import { useProfileViewContext } from '../../ProfileView/ProfileViewContext';
24
- import ColorStackLegend from './ColorStackLegend';
25
25
  import ContextMenu from './ContextMenu';
26
26
  import { IcicleNode, RowHeight } from './IcicleGraphNodes';
27
27
  import { arrowToString, extractFeature } from './utils';
@@ -41,7 +41,15 @@ export const FIELD_CUMULATIVE = 'cumulative';
41
41
  export const FIELD_CUMULATIVE_PER_SECOND = 'cumulative_per_second';
42
42
  export const FIELD_DIFF = 'diff';
43
43
  export const FIELD_DIFF_PER_SECOND = 'diff_per_second';
44
- export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, profileType, navigateTo, sortBy, }) {
44
+ export const getMappingColors = (mappingsList, isDarkMode, currentColorProfile) => {
45
+ const mappingFeatures = mappingsList.map(mapping => extractFeature(mapping));
46
+ const colors = {};
47
+ Object.entries(mappingFeatures).forEach(([_, feature]) => {
48
+ colors[feature.name] = getColorForFeature(feature.name, isDarkMode, currentColorProfile.colors);
49
+ });
50
+ return colors;
51
+ };
52
+ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, filtered, width, setCurPath, curPath, profileType, navigateTo, sortBy, flamegraphLoading, }) {
45
53
  const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
46
54
  const dispatch = useAppDispatch();
47
55
  const [highlightSimilarStacksPreference] = useUserPreference(USER_PREFERENCES.HIGHLIGHT_SIMILAR_STACKS.key);
@@ -56,12 +64,16 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, f
56
64
  const [hoveringName, setHoveringName] = useState(null);
57
65
  const svg = useRef(null);
58
66
  const ref = useRef(null);
67
+ const [binaryFrameFilter, setBinaryFrameFilter] = useURLState({
68
+ param: 'binary_frame_filter',
69
+ navigateTo,
70
+ });
59
71
  const currentSearchString = selectQueryParam('search_string') ?? '';
60
72
  const { compareMode } = useProfileViewContext();
61
- const isColorStackLegendEnabled = selectQueryParam('color_stack_legend') === 'true';
73
+ // const isColorStackLegendEnabled = selectQueryParam('color_stack_legend') === 'true';
62
74
  const currentColorProfile = useCurrentColorProfile();
63
75
  const colorForSimilarNodes = currentColorProfile.colorForSimilarNodes;
64
- const mappings = useMemo(() => {
76
+ const mappingsList = useMemo(() => {
65
77
  // Read the mappings from the dictionary that contains all mapping strings.
66
78
  // This is great, as might only have a dozen or so mappings,
67
79
  // and don't need to read through all the rows (potentially thousands).
@@ -82,46 +94,23 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, f
82
94
  .flat() ?? [];
83
95
  // We add a EVERYTHING ELSE mapping to the list.
84
96
  mappings.push('');
85
- // We look through the function names to find out if there's a runtime function.
86
- // Again, we only read through the dictionary, which is much faster than reading through all the rows.
87
- // We stop as soon as we find a runtime function.
88
- const functionNamesDict = table.getChild(FIELD_FUNCTION_NAME);
89
- functionNamesDict?.data.forEach(fn => {
90
- if (fn.dictionary == null) {
91
- return;
92
- }
93
- const len = fn.dictionary.length;
94
- for (let i = 0; i < len; i++) {
95
- const fn = arrowToString(functionNamesDict?.get(i));
96
- if (fn?.startsWith('runtime') === true) {
97
- mappings.push('runtime');
98
- break;
99
- }
100
- }
101
- });
102
97
  // We sort the mappings alphabetically to make sure that the order is always the same.
103
98
  mappings.sort((a, b) => a.localeCompare(b));
104
99
  return mappings;
105
100
  }, [table]);
106
- // TODO: Somehow figure out how to add runtime to this, if stacks are present.
107
- // Potentially read the function name dictionary and check if it contains strings starting with runtime.
108
- const mappingFeatures = useMemo(() => {
109
- return mappings.map(mapping => extractFeature(mapping));
110
- }, [mappings]);
111
- // TODO: Unify with mappingFeatures
112
101
  const mappingColors = useMemo(() => {
113
- const colors = {};
114
- Object.entries(mappingFeatures).forEach(([_, feature]) => {
115
- colors[feature.name] = getColorForFeature(feature.name, isDarkMode, currentColorProfile.colors);
116
- });
102
+ const colors = getMappingColors(mappingsList, isDarkMode, currentColorProfile);
117
103
  return colors;
118
- }, [isDarkMode, mappingFeatures, currentColorProfile]);
104
+ }, [isDarkMode, mappingsList, currentColorProfile]);
119
105
  useEffect(() => {
120
106
  if (ref.current != null) {
121
107
  setHeight(ref?.current.getBoundingClientRect().height);
122
108
  }
123
- }, [width]);
109
+ }, [width, flamegraphLoading]);
124
110
  const xScale = useMemo(() => {
111
+ if (total === 0n) {
112
+ return () => 0;
113
+ }
125
114
  if (width === undefined) {
126
115
  return () => 0;
127
116
  }
@@ -139,33 +128,42 @@ export const IcicleGraphArrow = memo(function IcicleGraphArrow({ arrow, total, f
139
128
  const trackVisibility = (isVisible) => {
140
129
  setIsContextMenuOpen(isVisible);
141
130
  };
131
+ const hideBinary = (binaryToRemove) => {
132
+ // second/subsequent time filtering out a binary i.e. a binary has already been hidden
133
+ // and we want to hide more binaries, we simply remove the binary from the binaryFrameFilter array in the URL.
134
+ if (Array.isArray(binaryFrameFilter) && binaryFrameFilter.length > 0) {
135
+ const newMappingsList = binaryFrameFilter.filter(mapping => mapping !== binaryToRemove);
136
+ setBinaryFrameFilter(newMappingsList);
137
+ return;
138
+ }
139
+ // first time hiding a binary
140
+ const newMappingsList = mappingsList.filter(mapping => mapping !== binaryToRemove);
141
+ setBinaryFrameFilter(newMappingsList);
142
+ };
142
143
  // useMemo for the root graph as it otherwise renders the whole graph if the hoveringRow changes.
143
144
  const root = useMemo(() => {
144
145
  return (_jsx("svg", { className: "font-robotoMono", width: width, height: height, preserveAspectRatio: "xMinYMid", ref: svg, onContextMenu: displayMenu, children: _jsx("g", { ref: ref, children: _jsx("g", { transform: 'translate(0, 0)', children: _jsx(IcicleNode, { table: table, row: 0, mappingColors: mappingColors, x: 0, y: 0, totalWidth: width ?? 1, height: RowHeight, setCurPath: setCurPath, curPath: curPath, total: total, xScale: xScale, path: [], level: 0, isRoot: true, searchString: currentSearchString, setHoveringRow: setHoveringRow, setHoveringLevel: setHoveringLevel, sortBy: sortBy, darkMode: isDarkMode, compareMode: compareMode, profileType: profileType, isContextMenuOpen: isContextMenuOpen, hoveringName: hoveringName, setHoveringName: setHoveringName, hoveringRow: hoveringRow, colorForSimilarNodes: colorForSimilarNodes, highlightSimilarStacksPreference: highlightSimilarStacksPreference }) }) }) }));
145
146
  }, [
146
- compareMode,
147
- curPath,
148
- currentSearchString,
147
+ width,
149
148
  height,
150
- isDarkMode,
151
- profileType,
149
+ displayMenu,
150
+ table,
152
151
  mappingColors,
153
152
  setCurPath,
154
- sortBy,
155
- table,
153
+ curPath,
156
154
  total,
157
- width,
158
155
  xScale,
156
+ currentSearchString,
157
+ sortBy,
158
+ isDarkMode,
159
+ compareMode,
160
+ profileType,
159
161
  isContextMenuOpen,
160
- displayMenu,
161
- colorForSimilarNodes,
162
- highlightSimilarStacksPreference,
163
162
  hoveringName,
164
163
  hoveringRow,
164
+ colorForSimilarNodes,
165
+ highlightSimilarStacksPreference,
165
166
  ]);
166
- if (table.numRows === 0 || width === undefined) {
167
- return _jsx(_Fragment, {});
168
- }
169
- return (_jsx(_Fragment, { children: _jsxs("div", { onMouseLeave: () => dispatch(setHoveringNode(undefined)), children: [_jsx(ContextMenu, { menuId: MENU_ID, table: table, row: hoveringRow ?? 0, level: hoveringLevel ?? 0, total: total, totalUnfiltered: total + filtered, profileType: profileType, navigateTo: navigateTo, trackVisibility: trackVisibility, curPath: curPath, setCurPath: setCurPath, hideMenu: hideAll }), isColorStackLegendEnabled && (_jsx(ColorStackLegend, { mappingColors: mappingColors, navigateTo: navigateTo, compareMode: compareMode })), dockedMetainfo ? (_jsx(DockedGraphTooltip, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, total: total, totalUnfiltered: total + filtered, profileType: profileType })) : (!isContextMenuOpen && (_jsx(GraphTooltipArrow, { contextElement: svg.current, isContextMenuOpen: isContextMenuOpen, children: _jsx(GraphTooltipArrowContent, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, isFixed: false, total: total, totalUnfiltered: total + filtered, profileType: profileType, navigateTo: navigateTo }) }))), root] }) }));
167
+ return (_jsx(_Fragment, { children: _jsxs("div", { onMouseLeave: () => dispatch(setHoveringNode(undefined)), children: [_jsx(ContextMenu, { menuId: MENU_ID, table: table, row: hoveringRow ?? 0, level: hoveringLevel ?? 0, total: total, totalUnfiltered: total + filtered, profileType: profileType, navigateTo: navigateTo, trackVisibility: trackVisibility, curPath: curPath, setCurPath: setCurPath, hideMenu: hideAll, hideBinary: hideBinary }), dockedMetainfo ? (_jsx(DockedGraphTooltip, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, total: total, totalUnfiltered: total + filtered, profileType: profileType })) : (!isContextMenuOpen && (_jsx(GraphTooltipArrow, { contextElement: svg.current, isContextMenuOpen: isContextMenuOpen, children: _jsx(GraphTooltipArrowContent, { table: table, row: hoveringRow, level: hoveringLevel ?? 0, isFixed: false, total: total, totalUnfiltered: total + filtered, profileType: profileType, navigateTo: navigateTo }) }))), root] }) }));
170
168
  });
171
169
  export default IcicleGraphArrow;
@@ -9,8 +9,7 @@ interface Props {
9
9
  diff: bigint | null;
10
10
  diffPerSecond: number | null;
11
11
  mappingColors: mappingColors;
12
- functionName: string | null;
13
12
  mappingFile: string | null;
14
13
  }
15
- declare const useNodeColor: ({ isDarkMode, compareMode, cumulative, cumulativePerSecond, diff, diffPerSecond, mappingColors, functionName, mappingFile, }: Props) => string;
14
+ declare const useNodeColor: ({ isDarkMode, compareMode, cumulative, cumulativePerSecond, diff, diffPerSecond, mappingColors, mappingFile, }: Props) => string;
16
15
  export default useNodeColor;
@@ -12,18 +12,13 @@
12
12
  // limitations under the License.
13
13
  import { EVERYTHING_ELSE } from '@parca/store';
14
14
  import { diffColor, diffColorPerSecond, getLastItem } from '@parca/utilities';
15
- const useNodeColor = ({ isDarkMode, compareMode, cumulative, cumulativePerSecond, diff, diffPerSecond, mappingColors, functionName, mappingFile, }) => {
15
+ const useNodeColor = ({ isDarkMode, compareMode, cumulative, cumulativePerSecond, diff, diffPerSecond, mappingColors, mappingFile, }) => {
16
16
  if (compareMode) {
17
17
  if (cumulativePerSecond !== null && diffPerSecond !== null) {
18
18
  return diffColorPerSecond(diffPerSecond, cumulativePerSecond, isDarkMode);
19
19
  }
20
20
  return diffColor(diff ?? 0n, cumulative, isDarkMode);
21
21
  }
22
- // To get the color we first check if the function name starts with 'runtime'.
23
- // If it does, we color it as runtime. Otherwise, we check the mapping file.
24
- // If there is no mapping file, we color it as 'everything else'.
25
- return functionName?.startsWith('runtime') === true
26
- ? mappingColors.runtime
27
- : mappingColors[getLastItem(mappingFile ?? '') ?? EVERYTHING_ELSE];
22
+ return mappingColors[getLastItem(mappingFile ?? '') ?? EVERYTHING_ELSE];
28
23
  };
29
24
  export default useNodeColor;
@@ -45,9 +45,6 @@ export function nodeLabel(table, row, level, showBinaryName) {
45
45
  return fallback === '' ? '<unknown>' : fallback;
46
46
  }
47
47
  export const extractFeature = (mapping) => {
48
- if (mapping === 'runtime' || mapping === 'root') {
49
- return { name: 'runtime', type: FEATURE_TYPES.Runtime };
50
- }
51
48
  if (mapping != null && mapping !== '') {
52
49
  return { name: mapping, type: FEATURE_TYPES.Binary };
53
50
  }
@@ -17,6 +17,8 @@ interface ProfileIcicleGraphProps {
17
17
  setActionButtons?: (buttons: React.JSX.Element) => void;
18
18
  error?: any;
19
19
  isHalfScreen: boolean;
20
+ mappings?: string[];
21
+ mappingsLoading?: boolean;
20
22
  }
21
- declare const ProfileIcicleGraph: ({ graph, arrow, total, filtered, curPath, setNewCurPath, profileType, navigateTo, loading, setActionButtons, error, width, isHalfScreen, }: ProfileIcicleGraphProps) => JSX.Element;
23
+ declare const ProfileIcicleGraph: ({ graph, arrow, total, filtered, curPath, setNewCurPath, profileType, navigateTo, loading, setActionButtons, error, width, isHalfScreen, mappings, mappingsLoading, }: ProfileIcicleGraphProps) => JSX.Element;
22
24
  export default ProfileIcicleGraph;