chromeTraceProfile.spec.tsx 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import {
  2. ChromeTraceProfile,
  3. collapseSamples,
  4. parseTypescriptChromeTraceArrayFormat,
  5. splitEventsByProcessAndTraceId,
  6. } from 'sentry/utils/profiling/profile/chromeTraceProfile';
  7. describe('splitEventsByProcessAndTraceId', () => {
  8. it('splits by thread id', () => {
  9. const trace: ChromeTrace.ArrayFormat = [
  10. {
  11. ph: 'B',
  12. tid: 0,
  13. pid: 0,
  14. cat: '',
  15. name: '',
  16. ts: 0,
  17. args: [],
  18. },
  19. {
  20. ph: 'B',
  21. tid: 1,
  22. pid: 0,
  23. cat: '',
  24. name: '',
  25. ts: 0,
  26. args: [],
  27. },
  28. ];
  29. expect(splitEventsByProcessAndTraceId(trace).get(0)?.get(0)).toEqual([trace[0]]);
  30. expect(splitEventsByProcessAndTraceId(trace).get(0)?.get(1)).toEqual([trace[1]]);
  31. });
  32. });
  33. describe('parseTypescriptChromeTraceArrayFormat', () => {
  34. it('returns typescript profile', () => {
  35. expect(
  36. parseTypescriptChromeTraceArrayFormat(
  37. [
  38. {
  39. ph: 'M',
  40. ts: 0,
  41. cat: '',
  42. pid: 0,
  43. tid: 0,
  44. name: 'process_name',
  45. args: {name: 'Process Name'},
  46. },
  47. {
  48. ph: 'B',
  49. ts: 0,
  50. cat: 'program',
  51. pid: 0,
  52. tid: 0,
  53. name: 'createProgram',
  54. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  55. },
  56. ],
  57. ''
  58. ).profiles[0]
  59. ).toBeInstanceOf(ChromeTraceProfile);
  60. });
  61. it('marks process name', () => {
  62. expect(
  63. parseTypescriptChromeTraceArrayFormat(
  64. [
  65. {
  66. ph: 'M',
  67. ts: 0,
  68. cat: '',
  69. pid: 0,
  70. tid: 0,
  71. name: 'process_name',
  72. args: {name: 'Process Name'},
  73. },
  74. {
  75. ph: 'B',
  76. ts: 0,
  77. cat: 'program',
  78. pid: 0,
  79. tid: 0,
  80. name: 'createProgram',
  81. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  82. },
  83. ],
  84. ''
  85. ).profiles[0].name
  86. ).toBe('Process Name (0): tid (0)');
  87. });
  88. it('marks thread name', () => {
  89. expect(
  90. parseTypescriptChromeTraceArrayFormat(
  91. [
  92. {
  93. ph: 'M',
  94. ts: 0,
  95. cat: '',
  96. pid: 0,
  97. tid: 0,
  98. name: 'thread_name',
  99. args: {name: 'Thread Name'},
  100. },
  101. {
  102. ph: 'B',
  103. ts: 0,
  104. cat: 'program',
  105. pid: 0,
  106. tid: 0,
  107. name: 'createProgram',
  108. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  109. },
  110. ],
  111. ''
  112. ).profiles[0].name
  113. ).toBe('pid (0): Thread Name (0)');
  114. });
  115. it('imports a simple trace', () => {
  116. const trace = parseTypescriptChromeTraceArrayFormat(
  117. [
  118. {
  119. ph: 'B',
  120. ts: 1000,
  121. cat: 'program',
  122. pid: 0,
  123. tid: 0,
  124. name: 'createProgram',
  125. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  126. },
  127. {
  128. ph: 'E',
  129. ts: 2000,
  130. cat: 'program',
  131. pid: 0,
  132. tid: 0,
  133. name: 'createProgram',
  134. args: {configFilePath: '/Users/jonasbadalic/Work/sentry/tsconfig.json'},
  135. },
  136. ],
  137. ''
  138. );
  139. expect(trace.profiles[0].startedAt).toBe(1000);
  140. expect(trace.profiles[0].endedAt).toBe(2000);
  141. expect(trace.profiles[0].duration).toBe(1000);
  142. expect(trace.profiles[0].appendOrderTree.children[0].totalWeight).toBe(1000);
  143. });
  144. it('closes unclosed events', () => {
  145. const trace = parseTypescriptChromeTraceArrayFormat(
  146. [
  147. {
  148. ph: 'B',
  149. ts: 0,
  150. cat: 'program',
  151. pid: 0,
  152. tid: 0,
  153. name: 'createProgram',
  154. args: {frame: '0'},
  155. },
  156. {
  157. ph: 'B',
  158. ts: 1000,
  159. cat: 'program',
  160. pid: 0,
  161. tid: 0,
  162. name: 'createProgram',
  163. args: {frame: '1'},
  164. },
  165. {
  166. ph: 'E',
  167. ts: 2000,
  168. cat: 'program',
  169. pid: 0,
  170. tid: 0,
  171. name: 'createProgram',
  172. args: {frame: '1'},
  173. },
  174. ],
  175. ''
  176. );
  177. expect(trace.profiles[0].duration).toBe(2000);
  178. expect(trace.profiles[0].appendOrderTree.children[0].selfWeight).toBe(1000);
  179. expect(trace.profiles[0].appendOrderTree.children[0].totalWeight).toBe(2000);
  180. expect(trace.profiles[0].appendOrderTree.children[0].children[0].selfWeight).toBe(
  181. 1000
  182. );
  183. });
  184. it('handles out of order E events', () => {
  185. const trace = parseTypescriptChromeTraceArrayFormat(
  186. [
  187. {
  188. ph: 'B',
  189. ts: 0,
  190. cat: '',
  191. pid: 0,
  192. tid: 0,
  193. name: '',
  194. args: {frame: '0'},
  195. },
  196. {
  197. ph: 'B',
  198. ts: 1,
  199. cat: '',
  200. pid: 0,
  201. tid: 0,
  202. name: '',
  203. args: {frame: '1'},
  204. },
  205. {
  206. ph: 'E',
  207. ts: 2,
  208. cat: '',
  209. pid: 0,
  210. tid: 0,
  211. name: '',
  212. args: {frame: '0'},
  213. },
  214. {
  215. ph: 'E',
  216. ts: 2,
  217. cat: '',
  218. pid: 0,
  219. tid: 0,
  220. name: '',
  221. args: {frame: '1'},
  222. },
  223. ],
  224. ''
  225. );
  226. expect(trace.profiles[0].duration).toBe(2);
  227. expect(trace.profiles[0].appendOrderTree.children[0].selfWeight).toBe(1);
  228. expect(trace.profiles[0].appendOrderTree.children[0].totalWeight).toBe(2);
  229. expect(trace.profiles[0].appendOrderTree.children[0].frame.name).toBe(
  230. 'Unknown {"frame":"0"}'
  231. );
  232. expect(trace.profiles[0].appendOrderTree.children[0].children[0].frame.name).toBe(
  233. 'Unknown {"frame":"1"}'
  234. );
  235. expect(trace.profiles[0].appendOrderTree.children[0].children[0].selfWeight).toBe(1);
  236. expect(trace.profiles[0].appendOrderTree.children[0].children[0].totalWeight).toBe(1);
  237. });
  238. it('handles out of order B events', () => {
  239. const trace = parseTypescriptChromeTraceArrayFormat(
  240. [
  241. {
  242. ph: 'B',
  243. ts: 0,
  244. cat: '',
  245. pid: 0,
  246. tid: 0,
  247. name: '',
  248. args: {frame: '0'},
  249. },
  250. {
  251. ph: 'B',
  252. ts: 1,
  253. cat: '',
  254. pid: 0,
  255. tid: 0,
  256. name: '',
  257. args: {frame: '1'},
  258. },
  259. {
  260. ph: 'E',
  261. ts: 2,
  262. cat: '',
  263. pid: 0,
  264. tid: 0,
  265. name: '',
  266. args: {frame: '0'},
  267. },
  268. {
  269. ph: 'E',
  270. ts: 2,
  271. cat: '',
  272. pid: 0,
  273. tid: 0,
  274. name: '',
  275. args: {frame: '1'},
  276. },
  277. ],
  278. ''
  279. );
  280. expect(trace.profiles[0].duration).toBe(2);
  281. expect(trace.profiles[0].appendOrderTree.children[0].selfWeight).toBe(1);
  282. expect(trace.profiles[0].appendOrderTree.children[0].totalWeight).toBe(2);
  283. expect(trace.profiles[0].appendOrderTree.children[0].frame.name).toBe(
  284. 'Unknown {"frame":"0"}'
  285. );
  286. expect(trace.profiles[0].appendOrderTree.children[0].children[0].frame.name).toBe(
  287. 'Unknown {"frame":"1"}'
  288. );
  289. expect(trace.profiles[0].appendOrderTree.children[0].children[0].selfWeight).toBe(1);
  290. expect(trace.profiles[0].appendOrderTree.children[0].children[0].totalWeight).toBe(1);
  291. });
  292. it('handles X trace with tdur', () => {
  293. const trace = parseTypescriptChromeTraceArrayFormat(
  294. [
  295. {
  296. ph: 'X',
  297. ts: 0,
  298. cat: '',
  299. pid: 0,
  300. tid: 0,
  301. tdur: 100,
  302. name: '',
  303. args: {frame: '0'},
  304. },
  305. ],
  306. ''
  307. );
  308. expect(trace.profiles[0].duration).toBe(100);
  309. });
  310. it('handles X trace with dur', () => {
  311. const trace = parseTypescriptChromeTraceArrayFormat(
  312. [
  313. {
  314. ph: 'X',
  315. ts: 0,
  316. cat: '',
  317. pid: 0,
  318. tid: 0,
  319. dur: 100,
  320. name: '',
  321. args: {frame: '0'},
  322. },
  323. ],
  324. ''
  325. );
  326. expect(trace.profiles[0].duration).toBe(100);
  327. });
  328. });
  329. describe('collapseSamples', () => {
  330. it.each([
  331. {
  332. samples: [1, 1],
  333. timeDeltas: [0, 1],
  334. expectedSamples: [1, 1],
  335. expectedTimeDeltas: [0, 1],
  336. },
  337. {
  338. samples: [1, 1, 1],
  339. timeDeltas: [0, 1, 1],
  340. expectedSamples: [1, 1],
  341. expectedTimeDeltas: [0, 2],
  342. },
  343. {
  344. samples: [1, 2, 1],
  345. timeDeltas: [0, 1, 2],
  346. expectedSamples: [1, 2, 1],
  347. expectedTimeDeltas: [0, 1, 3],
  348. },
  349. {
  350. samples: [1, 2, 3, 4],
  351. timeDeltas: [0, 1, 1, 1],
  352. expectedSamples: [1, 2, 3, 4],
  353. expectedTimeDeltas: [0, 1, 2, 3],
  354. },
  355. ])('collapses sample', test => {
  356. const result = collapseSamples({
  357. startTime: 0,
  358. endTime: 100,
  359. samples: test.samples,
  360. timeDeltas: test.timeDeltas,
  361. nodes: [],
  362. });
  363. expect(result.samples).toEqual(test.expectedSamples);
  364. expect(result.sampleTimes).toEqual(test.expectedTimeDeltas);
  365. });
  366. it('guards from negative samples', () => {
  367. const result = collapseSamples({
  368. startTime: 0,
  369. endTime: 100,
  370. samples: [1, 2, 3],
  371. timeDeltas: [1, -1, 1],
  372. nodes: [],
  373. });
  374. expect(result.samples).toEqual([1, 2, 3]);
  375. expect(result.sampleTimes).toEqual([1, 1, 2]);
  376. });
  377. it('guards from negative samples when they are being collapsed', () => {
  378. const result = collapseSamples({
  379. startTime: 0,
  380. endTime: 100,
  381. samples: [1, 1, 1],
  382. timeDeltas: [1, -1, 2],
  383. nodes: [],
  384. });
  385. expect(result.samples).toEqual([1, 1]);
  386. expect(result.sampleTimes).toEqual([1, 3]);
  387. });
  388. });