eventedProfile.spec.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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('mobile', []));
  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('tracks discarded samples', () => {
  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: 'C', at: 0, frame: 0},
  33. ],
  34. };
  35. const profile = EventedProfile.FromProfile(
  36. trace,
  37. createFrameIndex('mobile', [{name: 'f0'}])
  38. );
  39. expect(profile.stats.discardedSamplesCount).toBe(1);
  40. });
  41. it('tracks negative samples', () => {
  42. const trace: Profiling.EventedProfile = {
  43. name: 'profile',
  44. startValue: 0,
  45. endValue: 1000,
  46. unit: 'milliseconds',
  47. threadID: 0,
  48. type: 'evented',
  49. events: [
  50. {type: 'O', at: 0, frame: 0},
  51. {type: 'C', at: -1, frame: 0},
  52. ],
  53. };
  54. const profile = EventedProfile.FromProfile(
  55. trace,
  56. createFrameIndex('mobile', [{name: 'f0'}])
  57. );
  58. expect(profile.stats.negativeSamplesCount).toBe(1);
  59. });
  60. it('rebuilds the stack', () => {
  61. const trace: Profiling.EventedProfile = {
  62. name: 'profile',
  63. startValue: 0,
  64. endValue: 1000,
  65. unit: 'milliseconds',
  66. threadID: 0,
  67. type: 'evented',
  68. events: [
  69. {type: 'O', at: 0, frame: 0},
  70. {type: 'O', at: 1, frame: 1},
  71. {type: 'C', at: 2, frame: 1},
  72. {type: 'C', at: 4, frame: 0},
  73. ],
  74. };
  75. const {open, close, openSpy, closeSpy, timings} = makeTestingBoilerplate();
  76. const profile = EventedProfile.FromProfile(
  77. trace,
  78. createFrameIndex('mobile', [{name: 'f0'}, {name: 'f1'}])
  79. );
  80. profile.forEach(open, close);
  81. expect(timings).toEqual([
  82. ['f0', 'open'],
  83. ['f1', 'open'],
  84. ['f1', 'close'],
  85. ['f0', 'close'],
  86. ]);
  87. expect(openSpy).toHaveBeenCalledTimes(2);
  88. expect(closeSpy).toHaveBeenCalledTimes(2);
  89. const root = firstCallee(profile.appendOrderStack[0]);
  90. expect(root.totalWeight).toEqual(4);
  91. expect(firstCallee(root).totalWeight).toEqual(1);
  92. expect(root.selfWeight).toEqual(3);
  93. expect(firstCallee(root).selfWeight).toEqual(1);
  94. });
  95. it('marks direct recursion', () => {
  96. const trace: Profiling.EventedProfile = {
  97. name: 'profile',
  98. startValue: 0,
  99. endValue: 1000,
  100. unit: 'milliseconds',
  101. threadID: 0,
  102. type: 'evented',
  103. events: [
  104. {type: 'O', at: 0, frame: 0},
  105. {type: 'O', at: 1, frame: 0},
  106. {type: 'C', at: 1, frame: 0},
  107. {type: 'C', at: 1, frame: 0},
  108. ],
  109. };
  110. const profile = EventedProfile.FromProfile(
  111. trace,
  112. createFrameIndex('mobile', [{name: 'f0'}])
  113. );
  114. expect(firstCallee(firstCallee(profile.appendOrderTree)).isRecursive()).toBe(true);
  115. });
  116. it('marks indirect recursion', () => {
  117. const trace: Profiling.EventedProfile = {
  118. name: 'profile',
  119. startValue: 0,
  120. endValue: 1000,
  121. unit: 'milliseconds',
  122. threadID: 0,
  123. type: 'evented',
  124. events: [
  125. {type: 'O', at: 0, frame: 0},
  126. {type: 'O', at: 1, frame: 1},
  127. {type: 'O', at: 2, frame: 0},
  128. {type: 'C', at: 3, frame: 0},
  129. {type: 'C', at: 2, frame: 1},
  130. {type: 'C', at: 2, frame: 0},
  131. ],
  132. };
  133. const profile = EventedProfile.FromProfile(
  134. trace,
  135. createFrameIndex('mobile', [{name: 'f0'}, {name: 'f1'}])
  136. );
  137. expect(
  138. firstCallee(firstCallee(firstCallee(profile.appendOrderTree))).isRecursive()
  139. ).toBe(true);
  140. });
  141. it('tracks minFrameDuration', () => {
  142. const trace: Profiling.EventedProfile = {
  143. name: 'profile',
  144. startValue: 0,
  145. endValue: 1000,
  146. unit: 'milliseconds',
  147. threadID: 0,
  148. type: 'evented',
  149. events: [
  150. {type: 'O', at: 0, frame: 0},
  151. {type: 'O', at: 5, frame: 1},
  152. {type: 'C', at: 5.5, frame: 1},
  153. {type: 'C', at: 10, frame: 0},
  154. ],
  155. };
  156. const profile = EventedProfile.FromProfile(
  157. trace,
  158. createFrameIndex('mobile', [{name: 'f0'}, {name: 'f1'}, {name: 'f2'}])
  159. );
  160. expect(profile.minFrameDuration).toBe(0.5);
  161. });
  162. it('throws if samples are our of order', () => {
  163. const trace: Profiling.EventedProfile = {
  164. name: 'profile',
  165. startValue: 0,
  166. endValue: 1000,
  167. unit: 'milliseconds',
  168. threadID: 0,
  169. type: 'evented',
  170. events: [
  171. {type: 'O', at: 5, frame: 0},
  172. {type: 'O', at: 2, frame: 1},
  173. {type: 'C', at: 5.5, frame: 1},
  174. {type: 'C', at: 5.5, frame: 1},
  175. // Simulate unclosed frame
  176. ],
  177. };
  178. expect(() =>
  179. EventedProfile.FromProfile(
  180. trace,
  181. createFrameIndex('mobile', [{name: 'f0'}, {name: 'f1'}, {name: 'f2'}])
  182. )
  183. ).toThrow('Sample delta cannot be negative, samples may be corrupt or out of order');
  184. });
  185. it('throws on unbalanced stack', () => {
  186. const trace: Profiling.EventedProfile = {
  187. name: 'profile',
  188. startValue: 0,
  189. endValue: 1000,
  190. unit: 'milliseconds',
  191. threadID: 0,
  192. type: 'evented',
  193. events: [
  194. {type: 'O', at: 0, frame: 0},
  195. {type: 'O', at: 5, frame: 1},
  196. {type: 'C', at: 5.5, frame: 1},
  197. // Simulate unclosed frame
  198. ],
  199. };
  200. expect(() =>
  201. EventedProfile.FromProfile(
  202. trace,
  203. createFrameIndex('mobile', [{name: 'f0'}, {name: 'f1'}, {name: 'f2'}])
  204. )
  205. ).toThrow('Unbalanced append order stack');
  206. });
  207. });