utils.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import {Span} from '@sentry/types';
  2. import {defined} from 'sentry/utils';
  3. import {FlamegraphFrame} from 'sentry/utils/profiling/flamegraphFrame';
  4. import {Frame} from 'sentry/utils/profiling/frame';
  5. import {CallTreeNode} from '../callTreeNode';
  6. type FrameIndex = Record<string | number, Frame>;
  7. export function createFrameIndex(
  8. frames: Profiling.Schema['shared']['frames']
  9. ): FrameIndex;
  10. export function createFrameIndex(
  11. frames: JSSelfProfiling.Frame[],
  12. trace: JSSelfProfiling.Trace
  13. ): FrameIndex;
  14. export function createFrameIndex(
  15. frames: Profiling.Schema['shared']['frames'] | JSSelfProfiling.Frame[],
  16. trace?: JSSelfProfiling.Trace
  17. ): FrameIndex {
  18. if (trace) {
  19. return (frames as JSSelfProfiling.Frame[]).reduce((acc, frame, index) => {
  20. acc[index] = new Frame(
  21. {
  22. key: index,
  23. resource:
  24. frame.resourceId !== undefined
  25. ? trace.resources[frame.resourceId]
  26. : undefined,
  27. ...frame,
  28. },
  29. 'web'
  30. );
  31. return acc;
  32. }, {});
  33. }
  34. return (frames as Profiling.Schema['shared']['frames']).reduce((acc, frame, index) => {
  35. acc[index] = new Frame({
  36. key: index,
  37. ...frame,
  38. });
  39. return acc;
  40. }, {});
  41. }
  42. type Cache<Arguments extends ReadonlyArray<any> | any, Value> = {
  43. args: Arguments;
  44. value: Value;
  45. };
  46. export function memoizeByReference<Arguments, Value>(
  47. fn: (args: Arguments) => Value
  48. ): (t: Arguments) => Value {
  49. let cache: Cache<Arguments, Value> | null = null;
  50. return function memoizeByReferenceCallback(args: Arguments) {
  51. // If this is the first run then eval the fn and cache the result
  52. if (!cache) {
  53. cache = {args, value: fn(args)};
  54. return cache.value;
  55. }
  56. // If args match by reference, then return cached value
  57. if (cache.args === args && cache.args !== undefined && args !== undefined) {
  58. return cache.value;
  59. }
  60. // Else eval the fn and store the new value
  61. cache.args = args;
  62. cache.value = fn(args);
  63. return cache.value;
  64. };
  65. }
  66. export function memoizeVariadicByReference<Arguments, Value>(
  67. fn: (...args: ReadonlyArray<Arguments>) => Value
  68. ): (...t: ReadonlyArray<Arguments>) => Value {
  69. let cache: Cache<ReadonlyArray<Arguments>, Value> | null = null;
  70. return function memoizeByReferenceCallback(...args: ReadonlyArray<Arguments>) {
  71. // If this is the first run then eval the fn and cache the result
  72. if (!cache) {
  73. cache = {args, value: fn(...args)};
  74. return cache.value;
  75. }
  76. // If args match by reference, then return cached value
  77. if (
  78. cache.args.length === args.length &&
  79. cache.args.length !== 0 &&
  80. args.length !== 0 &&
  81. args.every((arg, i) => arg === cache?.args[i])
  82. ) {
  83. return cache.value;
  84. }
  85. // Else eval the fn and store the new value
  86. cache.args = args;
  87. cache.value = fn(...args);
  88. return cache.value;
  89. };
  90. }
  91. export function wrapWithSpan<T>(parentSpan: Span | undefined, fn: () => T, options): T {
  92. if (!defined(parentSpan)) {
  93. return fn();
  94. }
  95. const sentrySpan = parentSpan.startChild(options);
  96. try {
  97. return fn();
  98. } catch (error) {
  99. sentrySpan.setStatus('internal_error');
  100. throw error;
  101. } finally {
  102. sentrySpan.finish();
  103. }
  104. }
  105. export const isSystemCall = (node: CallTreeNode): boolean => {
  106. return !node.frame.is_application;
  107. };
  108. export const isApplicationCall = (node: CallTreeNode): boolean => {
  109. return !!node.frame.is_application;
  110. };
  111. function indexNodeToParents(
  112. roots: FlamegraphFrame[],
  113. map: Record<string, FlamegraphFrame[]>,
  114. leafs: FlamegraphFrame[]
  115. ) {
  116. // Index each child node to its parent
  117. function indexNode(node: FlamegraphFrame, parent: FlamegraphFrame) {
  118. if (!map[node.key]) {
  119. map[node.key] = [];
  120. }
  121. map[node.key].push(parent);
  122. if (!node.children.length) {
  123. leafs.push(node);
  124. return;
  125. }
  126. for (let i = 0; i < node.children.length; i++) {
  127. indexNode(node.children[i], node);
  128. }
  129. }
  130. // Begin in each root node
  131. for (let i = 0; i < roots.length; i++) {
  132. // If the root is a leaf node, push it to the leafs array
  133. if (!roots[i].children?.length) {
  134. leafs.push(roots[i]);
  135. }
  136. // Init the map for the root in case we havent yet
  137. if (!map[roots[i].key]) {
  138. map[roots[i].key] = [];
  139. }
  140. // descend down to each child and index them
  141. for (let j = 0; j < roots[i].children.length; j++) {
  142. indexNode(roots[i].children[j], roots[i]);
  143. }
  144. }
  145. }
  146. function reverseTrail(
  147. nodes: FlamegraphFrame[],
  148. parentMap: Record<string, FlamegraphFrame[]>
  149. ): FlamegraphFrame[] {
  150. const splits: FlamegraphFrame[] = [];
  151. for (const n of nodes) {
  152. const nc = {
  153. ...n,
  154. parent: null as FlamegraphFrame | null,
  155. children: [] as FlamegraphFrame[],
  156. };
  157. if (!parentMap[n.key]) {
  158. continue;
  159. }
  160. for (const parent of parentMap[n.key]) {
  161. nc.children.push(...reverseTrail([parent], parentMap));
  162. }
  163. splits.push(nc);
  164. }
  165. return splits;
  166. }
  167. export const invertCallTree = (roots: FlamegraphFrame[]): FlamegraphFrame[] => {
  168. const nodeToParentIndex: Record<string, FlamegraphFrame[]> = {};
  169. const leafNodes: FlamegraphFrame[] = [];
  170. indexNodeToParents(roots, nodeToParentIndex, leafNodes);
  171. const reversed = reverseTrail(leafNodes, nodeToParentIndex);
  172. return reversed;
  173. };