flamegraph.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import {Fragment, ReactElement, useCallback, useMemo, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {FlamegraphOptionsMenu} from 'sentry/components/profiling/flamegraphOptionsMenu';
  4. import {FlamegraphSearch} from 'sentry/components/profiling/flamegraphSearch';
  5. import {FlamegraphToolbar} from 'sentry/components/profiling/flamegraphToolbar';
  6. import {FlamegraphViewSelectMenu} from 'sentry/components/profiling/flamegraphViewSelectMenu';
  7. import {FlamegraphZoomView} from 'sentry/components/profiling/flamegraphZoomView';
  8. import {FlamegraphZoomViewMinimap} from 'sentry/components/profiling/flamegraphZoomViewMinimap';
  9. import {ProfileDragDropImport} from 'sentry/components/profiling/profileDragDropImport';
  10. import {ThreadMenuSelector} from 'sentry/components/profiling/threadSelector';
  11. import {CanvasPoolManager} from 'sentry/utils/profiling/canvasScheduler';
  12. import {Flamegraph as FlamegraphModel} from 'sentry/utils/profiling/flamegraph';
  13. import {FlamegraphTheme} from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
  14. import {useFlamegraphPreferences} from 'sentry/utils/profiling/flamegraph/useFlamegraphPreferences';
  15. import {useFlamegraphTheme} from 'sentry/utils/profiling/flamegraph/useFlamegraphTheme';
  16. import {Rect} from 'sentry/utils/profiling/gl/utils';
  17. import {ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
  18. import {Profile} from 'sentry/utils/profiling/profile/profile';
  19. function getTransactionConfigSpace(profiles: Profile[]): Rect {
  20. const startedAt = Math.min(...profiles.map(p => p.startedAt));
  21. const endedAt = Math.max(...profiles.map(p => p.endedAt));
  22. return new Rect(startedAt, 0, endedAt - startedAt, 0);
  23. }
  24. interface FlamegraphProps {
  25. profiles: ProfileGroup;
  26. }
  27. function Flamegraph(props: FlamegraphProps): ReactElement {
  28. const flamegraphTheme = useFlamegraphTheme();
  29. const [{sorting, view, synchronizeXAxisWithTransaction}, dispatch] =
  30. useFlamegraphPreferences();
  31. const canvasPoolManager = useMemo(() => new CanvasPoolManager(), []);
  32. const [activeProfileIndex, setActiveProfileIndex] = useState<number | null>(null);
  33. const [importedProfiles, setImportedProfiles] = useState<ProfileGroup | null>(null);
  34. // once an import occurs, it will always take precedence over the profile in the props
  35. const profiles = importedProfiles ?? props.profiles;
  36. const flamegraph = useMemo(() => {
  37. // if the activeProfileIndex is null, use the activeProfileIndex from the profile group
  38. const profileIndex = activeProfileIndex ?? profiles.activeProfileIndex;
  39. const flamegraphModel = new FlamegraphModel(
  40. profiles.profiles[profileIndex],
  41. profileIndex,
  42. {
  43. inverted: view === 'bottom up',
  44. leftHeavy: sorting === 'left heavy',
  45. configSpace: synchronizeXAxisWithTransaction
  46. ? getTransactionConfigSpace(profiles.profiles)
  47. : undefined,
  48. }
  49. );
  50. return flamegraphModel;
  51. }, [profiles, activeProfileIndex, sorting, synchronizeXAxisWithTransaction, view]);
  52. const onImport = useCallback((profile: ProfileGroup) => {
  53. setActiveProfileIndex(null);
  54. setImportedProfiles(profile);
  55. }, []);
  56. return (
  57. <Fragment>
  58. <FlamegraphToolbar>
  59. <ThreadMenuSelector
  60. profileGroup={props.profiles}
  61. activeProfileIndex={flamegraph.profileIndex}
  62. onProfileIndexChange={setActiveProfileIndex}
  63. />
  64. <FlamegraphViewSelectMenu
  65. view={view}
  66. sorting={sorting}
  67. onSortingChange={s => {
  68. dispatch({type: 'set sorting', payload: s});
  69. }}
  70. onViewChange={v => {
  71. dispatch({type: 'set view', payload: v});
  72. }}
  73. />
  74. <FlamegraphOptionsMenu canvasPoolManager={canvasPoolManager} />
  75. </FlamegraphToolbar>
  76. <FlamegraphZoomViewMinimapContainer height={flamegraphTheme.SIZES.MINIMAP_HEIGHT}>
  77. <FlamegraphZoomViewMinimap
  78. flamegraph={flamegraph}
  79. canvasPoolManager={canvasPoolManager}
  80. />
  81. </FlamegraphZoomViewMinimapContainer>
  82. <FlamegraphZoomViewContainer>
  83. <ProfileDragDropImport onImport={onImport}>
  84. <FlamegraphZoomView
  85. key={`${profiles.traceID}-${flamegraph.profileIndex}`}
  86. flamegraph={flamegraph}
  87. canvasPoolManager={canvasPoolManager}
  88. />
  89. <FlamegraphSearch
  90. placement="top"
  91. flamegraphs={[flamegraph]}
  92. canvasPoolManager={canvasPoolManager}
  93. />
  94. </ProfileDragDropImport>
  95. </FlamegraphZoomViewContainer>
  96. </Fragment>
  97. );
  98. }
  99. const FlamegraphZoomViewMinimapContainer = styled('div')<{
  100. height: FlamegraphTheme['SIZES']['MINIMAP_HEIGHT'];
  101. }>`
  102. position: relative;
  103. height: ${p => p.height}px;
  104. flex-shrink: 0;
  105. `;
  106. const FlamegraphZoomViewContainer = styled('div')`
  107. position: relative;
  108. flex: 1 1 100%;
  109. `;
  110. export {Flamegraph};