profileFlamegraph.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {Fragment, useEffect, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import Alert from 'sentry/components/alert';
  4. import LoadingIndicator from 'sentry/components/loadingIndicator';
  5. import {Flamegraph} from 'sentry/components/profiling/flamegraph';
  6. import {ProfileDragDropImportProps} from 'sentry/components/profiling/profileDragDropImport';
  7. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  8. import {t} from 'sentry/locale';
  9. import {DeepPartial} from 'sentry/types/utils';
  10. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  11. import {
  12. decodeFlamegraphStateFromQueryParams,
  13. FlamegraphState,
  14. FlamegraphStateProvider,
  15. FlamegraphStateQueryParamSync,
  16. } from 'sentry/utils/profiling/flamegraph/flamegraphStateProvider/index';
  17. import {FlamegraphThemeProvider} from 'sentry/utils/profiling/flamegraph/flamegraphThemeProvider';
  18. import {ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
  19. import {Profile} from 'sentry/utils/profiling/profile/profile';
  20. import {useLocation} from 'sentry/utils/useLocation';
  21. import useOrganization from 'sentry/utils/useOrganization';
  22. import {useProfileGroup} from './profileGroupProvider';
  23. const LoadingGroup: ProfileGroup = {
  24. name: 'Loading',
  25. traceID: '',
  26. activeProfileIndex: 0,
  27. profiles: [Profile.Empty()],
  28. };
  29. function ProfileFlamegraph(): React.ReactElement {
  30. const location = useLocation();
  31. const organization = useOrganization();
  32. const [profileGroup, setProfileGroup] = useProfileGroup();
  33. useEffect(() => {
  34. trackAdvancedAnalyticsEvent('profiling_views.profile_flamegraph', {
  35. organization,
  36. });
  37. }, [organization]);
  38. const onImport: ProfileDragDropImportProps['onImport'] = profiles => {
  39. setProfileGroup({type: 'resolved', data: profiles});
  40. };
  41. const initialFlamegraphPreferencesState = useMemo((): DeepPartial<FlamegraphState> => {
  42. return decodeFlamegraphStateFromQueryParams(location.query);
  43. // We only want to decode this when our component mounts
  44. // eslint-disable-next-line react-hooks/exhaustive-deps
  45. }, []);
  46. return (
  47. <SentryDocumentTitle
  48. title={t('Profiling \u2014 Flamegraph')}
  49. orgSlug={organization.slug}
  50. >
  51. <FlamegraphStateProvider initialState={initialFlamegraphPreferencesState}>
  52. <FlamegraphThemeProvider>
  53. <FlamegraphStateQueryParamSync />
  54. <FlamegraphContainer>
  55. {profileGroup.type === 'errored' ? (
  56. <Alert type="error" showIcon>
  57. {profileGroup.error}
  58. </Alert>
  59. ) : profileGroup.type === 'loading' ? (
  60. <Fragment>
  61. <Flamegraph onImport={onImport} profiles={LoadingGroup} />
  62. <LoadingIndicatorContainer>
  63. <LoadingIndicator />
  64. </LoadingIndicatorContainer>
  65. </Fragment>
  66. ) : profileGroup.type === 'resolved' ? (
  67. <Flamegraph onImport={onImport} profiles={profileGroup.data} />
  68. ) : null}
  69. </FlamegraphContainer>
  70. </FlamegraphThemeProvider>
  71. </FlamegraphStateProvider>
  72. </SentryDocumentTitle>
  73. );
  74. }
  75. const LoadingIndicatorContainer = styled('div')`
  76. position: absolute;
  77. display: flex;
  78. flex-direction: column;
  79. justify-content: center;
  80. width: 100%;
  81. height: 100%;
  82. `;
  83. const FlamegraphContainer = styled('div')`
  84. display: flex;
  85. flex-direction: column;
  86. flex: 1 1 100%;
  87. /*
  88. * The footer component is a sibling of this div.
  89. * Remove it so the flamegraph can take up the
  90. * entire screen.
  91. */
  92. ~ footer {
  93. display: none;
  94. }
  95. `;
  96. export default ProfileFlamegraph;