flamegraph.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import {Fragment, ReactElement, useMemo} 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 {
  10. ProfileDragDropImport,
  11. ProfileDragDropImportProps,
  12. } from 'sentry/components/profiling/profileDragDropImport';
  13. import {ThreadMenuSelector} from 'sentry/components/profiling/threadSelector';
  14. import {CanvasPoolManager} from 'sentry/utils/profiling/canvasScheduler';
  15. import {Flamegraph as FlamegraphModel} from 'sentry/utils/profiling/flamegraph';
  16. import {FlamegraphTheme} from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
  17. import {useFlamegraphPreferences} from 'sentry/utils/profiling/flamegraph/useFlamegraphPreferences';
  18. import {useFlamegraphProfiles} from 'sentry/utils/profiling/flamegraph/useFlamegraphProfiles';
  19. import {useFlamegraphTheme} from 'sentry/utils/profiling/flamegraph/useFlamegraphTheme';
  20. import {Rect} from 'sentry/utils/profiling/gl/utils';
  21. import {ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
  22. import {Profile} from 'sentry/utils/profiling/profile/profile';
  23. function getTransactionConfigSpace(profiles: Profile[]): Rect {
  24. const startedAt = Math.min(...profiles.map(p => p.startedAt));
  25. const endedAt = Math.max(...profiles.map(p => p.endedAt));
  26. return new Rect(startedAt, 0, endedAt - startedAt, 0);
  27. }
  28. interface FlamegraphProps {
  29. onImport: ProfileDragDropImportProps['onImport'];
  30. profiles: ProfileGroup;
  31. }
  32. function Flamegraph(props: FlamegraphProps): ReactElement {
  33. const flamegraphTheme = useFlamegraphTheme();
  34. const [{sorting, view, xAxis}, dispatch] = useFlamegraphPreferences();
  35. const [{activeProfileIndex}, dispatchActiveProfileIndex] = useFlamegraphProfiles();
  36. const canvasPoolManager = useMemo(() => new CanvasPoolManager(), []);
  37. const flamegraph = useMemo(() => {
  38. if (
  39. !props.profiles.profiles[activeProfileIndex ?? props.profiles.activeProfileIndex]
  40. ) {
  41. // This could happen if activeProfileIndex was initialized from query string, but for some
  42. // reason the profile was removed from the list of profiles.
  43. return FlamegraphModel.Empty();
  44. }
  45. // if the activeProfileIndex is null, use the activeProfileIndex from the profile group
  46. return new FlamegraphModel(
  47. props.profiles.profiles[activeProfileIndex ?? props.profiles.activeProfileIndex],
  48. activeProfileIndex ?? props.profiles.activeProfileIndex,
  49. {
  50. inverted: view === 'bottom up',
  51. leftHeavy: sorting === 'left heavy',
  52. configSpace:
  53. xAxis === 'transaction'
  54. ? getTransactionConfigSpace(props.profiles.profiles)
  55. : undefined,
  56. }
  57. );
  58. }, [props.profiles, activeProfileIndex, sorting, xAxis, view]);
  59. return (
  60. <Fragment>
  61. <FlamegraphToolbar>
  62. <ThreadMenuSelector
  63. profileGroup={props.profiles}
  64. activeProfileIndex={flamegraph.profileIndex}
  65. onProfileIndexChange={index =>
  66. dispatchActiveProfileIndex({type: 'set active profile index', payload: index})
  67. }
  68. />
  69. <FlamegraphViewSelectMenu
  70. view={view}
  71. sorting={sorting}
  72. onSortingChange={s => {
  73. dispatch({type: 'set sorting', payload: s});
  74. }}
  75. onViewChange={v => {
  76. dispatch({type: 'set view', payload: v});
  77. }}
  78. />
  79. <FlamegraphSearch
  80. flamegraphs={[flamegraph]}
  81. canvasPoolManager={canvasPoolManager}
  82. />
  83. <FlamegraphOptionsMenu canvasPoolManager={canvasPoolManager} />
  84. </FlamegraphToolbar>
  85. <FlamegraphZoomViewMinimapContainer height={flamegraphTheme.SIZES.MINIMAP_HEIGHT}>
  86. <FlamegraphZoomViewMinimap
  87. flamegraph={flamegraph}
  88. canvasPoolManager={canvasPoolManager}
  89. />
  90. </FlamegraphZoomViewMinimapContainer>
  91. <FlamegraphZoomViewContainer>
  92. <ProfileDragDropImport onImport={props.onImport}>
  93. <FlamegraphZoomView
  94. flamegraph={flamegraph}
  95. canvasPoolManager={canvasPoolManager}
  96. />
  97. </ProfileDragDropImport>
  98. </FlamegraphZoomViewContainer>
  99. </Fragment>
  100. );
  101. }
  102. const FlamegraphZoomViewMinimapContainer = styled('div')<{
  103. height: FlamegraphTheme['SIZES']['MINIMAP_HEIGHT'];
  104. }>`
  105. position: relative;
  106. height: ${p => p.height}px;
  107. flex-shrink: 0;
  108. `;
  109. const FlamegraphZoomViewContainer = styled('div')`
  110. position: relative;
  111. flex: 1 1 100%;
  112. `;
  113. export {Flamegraph};