eventedProfile.spec.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import {EventedProfile} from 'sentry/utils/profiling/profile/eventedProfile';
  2. import {createFrameIndex} from 'sentry/utils/profiling/profile/utils';
  3. import {firstCallee, makeTestingBoilerplate} from './profile.spec';
  4. describe('EventedProfile', () => {
  5. it('imports the base properties', () => {
  6. const trace: Profiling.EventedProfile = {
  7. name: 'profile',
  8. startValue: 0,
  9. endValue: 1000,
  10. unit: 'milliseconds',
  11. threadID: 0,
  12. type: 'evented',
  13. events: [],
  14. };
  15. const profile = EventedProfile.FromProfile(trace, createFrameIndex([]));
  16. expect(profile.duration).toBe(1000);
  17. expect(profile.name).toBe(trace.name);
  18. expect(profile.threadId).toBe(trace.threadID);
  19. expect(profile.startedAt).toBe(0);
  20. expect(profile.endedAt).toBe(1000);
  21. });
  22. it('rebuilds the stack', () => {
  23. const trace: Profiling.EventedProfile = {
  24. name: 'profile',
  25. startValue: 0,
  26. endValue: 1000,
  27. unit: 'milliseconds',
  28. threadID: 0,
  29. type: 'evented',
  30. events: [
  31. {type: 'O', at: 0, frame: 0},
  32. {type: 'O', at: 1, frame: 1},
  33. {type: 'C', at: 2, frame: 1},
  34. {type: 'C', at: 4, frame: 0},
  35. ],
  36. };
  37. const {open, close, openSpy, closeSpy, timings} = makeTestingBoilerplate();
  38. const profile = EventedProfile.FromProfile(
  39. trace,
  40. createFrameIndex([{name: 'f0'}, {name: 'f1'}])
  41. );
  42. profile.forEach(open, close);
  43. expect(timings).toEqual([
  44. ['f0', 'open'],
  45. ['f1', 'open'],
  46. ['f1', 'close'],
  47. ['f0', 'close'],
  48. ]);
  49. expect(openSpy).toHaveBeenCalledTimes(2);
  50. expect(closeSpy).toHaveBeenCalledTimes(2);
  51. const root = firstCallee(profile.appendOrderStack[0]);
  52. expect(root.totalWeight).toEqual(4);
  53. expect(firstCallee(root).totalWeight).toEqual(1);
  54. expect(root.selfWeight).toEqual(3);
  55. expect(firstCallee(root).selfWeight).toEqual(1);
  56. });
  57. it('marks direct recursion', () => {
  58. const trace: Profiling.EventedProfile = {
  59. name: 'profile',
  60. startValue: 0,
  61. endValue: 1000,
  62. unit: 'milliseconds',
  63. threadID: 0,
  64. type: 'evented',
  65. events: [
  66. {type: 'O', at: 0, frame: 0},
  67. {type: 'O', at: 1, frame: 0},
  68. {type: 'C', at: 1, frame: 0},
  69. {type: 'C', at: 1, frame: 0},
  70. ],
  71. };
  72. const profile = EventedProfile.FromProfile(trace, createFrameIndex([{name: 'f0'}]));
  73. expect(firstCallee(firstCallee(profile.appendOrderTree)).isRecursive()).toBe(true);
  74. });
  75. it('marks indirect recursion', () => {
  76. const trace: Profiling.EventedProfile = {
  77. name: 'profile',
  78. startValue: 0,
  79. endValue: 1000,
  80. unit: 'milliseconds',
  81. threadID: 0,
  82. type: 'evented',
  83. events: [
  84. {type: 'O', at: 0, frame: 0},
  85. {type: 'O', at: 1, frame: 1},
  86. {type: 'O', at: 2, frame: 0},
  87. {type: 'C', at: 3, frame: 0},
  88. {type: 'C', at: 2, frame: 1},
  89. {type: 'C', at: 2, frame: 0},
  90. ],
  91. };
  92. const profile = EventedProfile.FromProfile(
  93. trace,
  94. createFrameIndex([{name: 'f0'}, {name: 'f1'}])
  95. );
  96. expect(
  97. firstCallee(firstCallee(firstCallee(profile.appendOrderTree))).isRecursive()
  98. ).toBe(true);
  99. });
  100. it('tracks minFrameDuration', () => {
  101. const trace: Profiling.EventedProfile = {
  102. name: 'profile',
  103. startValue: 0,
  104. endValue: 1000,
  105. unit: 'milliseconds',
  106. threadID: 0,
  107. type: 'evented',
  108. events: [
  109. {type: 'O', at: 0, frame: 0},
  110. {type: 'O', at: 5, frame: 1},
  111. {type: 'C', at: 5.5, frame: 1},
  112. {type: 'C', at: 10, frame: 0},
  113. ],
  114. };
  115. const profile = EventedProfile.FromProfile(
  116. trace,
  117. createFrameIndex([{name: 'f0'}, {name: 'f1'}, {name: 'f2'}])
  118. );
  119. expect(profile.minFrameDuration).toBe(0.5);
  120. });
  121. it('throws if samples are our of order', () => {
  122. const trace: Profiling.EventedProfile = {
  123. name: 'profile',
  124. startValue: 0,
  125. endValue: 1000,
  126. unit: 'milliseconds',
  127. threadID: 0,
  128. type: 'evented',
  129. events: [
  130. {type: 'O', at: 5, frame: 0},
  131. {type: 'O', at: 2, frame: 1},
  132. {type: 'C', at: 5.5, frame: 1},
  133. {type: 'C', at: 5.5, frame: 1},
  134. // Simulate unclosed frame
  135. ],
  136. };
  137. expect(() =>
  138. EventedProfile.FromProfile(
  139. trace,
  140. createFrameIndex([{name: 'f0'}, {name: 'f1'}, {name: 'f2'}])
  141. )
  142. ).toThrow('Sample delta cannot be negative, samples may be corrupt or out of order');
  143. });
  144. it('throws on unbalanced stack', () => {
  145. const trace: Profiling.EventedProfile = {
  146. name: 'profile',
  147. startValue: 0,
  148. endValue: 1000,
  149. unit: 'milliseconds',
  150. threadID: 0,
  151. type: 'evented',
  152. events: [
  153. {type: 'O', at: 0, frame: 0},
  154. {type: 'O', at: 5, frame: 1},
  155. {type: 'C', at: 5.5, frame: 1},
  156. // Simulate unclosed frame
  157. ],
  158. };
  159. expect(() =>
  160. EventedProfile.FromProfile(
  161. trace,
  162. createFrameIndex([{name: 'f0'}, {name: 'f1'}, {name: 'f2'}])
  163. )
  164. ).toThrow('Unbalanced append order stack');
  165. });
  166. });