profileGroupProvider.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import {createContext, useContext, useMemo} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import type {Frame} from 'sentry/utils/profiling/frame';
  4. import type {
  5. ContinuousProfileGroup,
  6. ProfileGroup,
  7. } from 'sentry/utils/profiling/profile/importProfile';
  8. import {importProfile} from 'sentry/utils/profiling/profile/importProfile';
  9. type ProfileGroupContextValue = ContinuousProfileGroup | ProfileGroup;
  10. const ProfileGroupContext = createContext<ProfileGroupContextValue | null>(null);
  11. function assertContinuousProfileGroup(
  12. input: ProfileGroupContextValue | null
  13. ): asserts input is ContinuousProfileGroup {
  14. if (input && input.type !== 'loading' && input.type !== 'continuous') {
  15. throw new Error('ProfileGroup is not of continuous profile type.');
  16. }
  17. }
  18. function assertTransactionProfileGroup(
  19. input: ProfileGroupContextValue | null
  20. ): asserts input is ProfileGroup {
  21. if (input && input.type !== 'loading' && input.type !== 'transaction') {
  22. throw new Error('ProfileGroup is not of transaction profile type.');
  23. }
  24. }
  25. export function useProfileGroup(): ProfileGroup {
  26. const context = useContext(ProfileGroupContext);
  27. if (!context) {
  28. throw new Error('useProfileGroup was called outside of ProfileGroupProvider');
  29. }
  30. assertTransactionProfileGroup(context);
  31. return context;
  32. }
  33. export function useContinuousProfileGroup(): ContinuousProfileGroup {
  34. const context = useContext(ProfileGroupContext);
  35. if (!context) {
  36. throw new Error(
  37. 'useContinuousProfileGroup was called outside of ProfileGroupProvider'
  38. );
  39. }
  40. assertContinuousProfileGroup(context);
  41. return context;
  42. }
  43. export const LOADING_PROFILE_GROUP: Readonly<ProfileGroup> = {
  44. name: 'Loading',
  45. activeProfileIndex: 0,
  46. transactionID: null,
  47. metadata: {},
  48. measurements: {},
  49. traceID: '',
  50. profiles: [],
  51. type: 'loading',
  52. };
  53. interface ProfileGroupProviderProps {
  54. children: React.ReactNode;
  55. input: Readonly<Profiling.ProfileInput> | null;
  56. traceID: string;
  57. type: 'flamegraph' | 'flamechart';
  58. frameFilter?: (frame: Frame) => boolean;
  59. }
  60. export function ProfileGroupProvider(props: ProfileGroupProviderProps) {
  61. const profileGroup = useMemo(() => {
  62. if (!props.input) {
  63. return LOADING_PROFILE_GROUP;
  64. }
  65. const qs = new URLSearchParams(window.location.search);
  66. const threadId = qs.get('tid');
  67. try {
  68. return importProfile(
  69. props.input,
  70. props.traceID,
  71. threadId,
  72. props.type,
  73. props.frameFilter
  74. );
  75. } catch (err) {
  76. Sentry.captureException(err);
  77. return LOADING_PROFILE_GROUP;
  78. }
  79. }, [props.input, props.traceID, props.type, props.frameFilter]);
  80. return (
  81. <ProfileGroupContext.Provider value={profileGroup}>
  82. {props.children}
  83. </ProfileGroupContext.Provider>
  84. );
  85. }