spanChart.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import {Rect} from 'sentry/utils/profiling/gl/utils';
  2. import {SpanTree, SpanTreeNode} from 'sentry/utils/profiling/spanTree';
  3. import {
  4. makeFormatter,
  5. makeFormatTo,
  6. makeTimelineFormatter,
  7. } from 'sentry/utils/profiling/units/units';
  8. import {Profile} from './profile/profile';
  9. export interface SpanChartNode {
  10. children: SpanChartNode[];
  11. depth: number;
  12. duration: number;
  13. end: number;
  14. node: SpanTree['root'];
  15. parent: SpanChartNode | null;
  16. start: number;
  17. }
  18. class SpanChart {
  19. spans: SpanChartNode[];
  20. root: SpanChartNode = {
  21. parent: null,
  22. node: SpanTreeNode.Root(),
  23. duration: 0,
  24. depth: -1,
  25. start: 0,
  26. end: 0,
  27. children: [],
  28. };
  29. spanTree: SpanTree;
  30. depth: number = 0;
  31. minSpanDuration: number = Number.POSITIVE_INFINITY;
  32. configSpace: Rect;
  33. toFinalUnit = makeFormatTo('milliseconds', 'milliseconds');
  34. formatter = makeFormatter('milliseconds');
  35. timelineFormatter: (value: number) => string;
  36. constructor(
  37. spanTree: SpanTree,
  38. options: {unit: Profile['unit']; configSpace?: Rect} = {unit: 'milliseconds'}
  39. ) {
  40. // Units need to be init before any profile iteration is done
  41. this.toFinalUnit = makeFormatTo('seconds', options.unit);
  42. this.timelineFormatter = makeTimelineFormatter(options.unit);
  43. this.formatter = makeFormatter(options.unit);
  44. this.spanTree = spanTree;
  45. this.spans = this.collectSpanNodes();
  46. const duration = this.toFinalUnit(
  47. this.spanTree.root.span.timestamp - this.spanTree.root.span.start_timestamp
  48. );
  49. this.configSpace = new Rect(0, 0, duration, this.depth);
  50. this.root.end = duration;
  51. this.root.duration = duration;
  52. }
  53. // Bfs over the span tree while keeping track of level depth and calling the cb fn
  54. forEachSpan(cb: (node: SpanChartNode) => void) {
  55. const transactionStart = this.spanTree.root.span.start_timestamp;
  56. const queue: [SpanChartNode | null, SpanTreeNode][] = [[null, this.spanTree.root]];
  57. let depth = 0;
  58. while (queue.length) {
  59. let children_at_depth = queue.length;
  60. while (children_at_depth-- !== 0) {
  61. const [parent, node] = queue.shift()!;
  62. const duration = node.span.timestamp - node.span.start_timestamp;
  63. const start = node.span.start_timestamp - transactionStart;
  64. const end = start + duration;
  65. const spanChartNode: SpanChartNode = {
  66. duration: this.toFinalUnit(duration),
  67. start: this.toFinalUnit(start),
  68. end: this.toFinalUnit(end),
  69. node,
  70. depth,
  71. parent,
  72. children: [],
  73. };
  74. cb(spanChartNode);
  75. if (parent) {
  76. parent.children.push(spanChartNode);
  77. } else {
  78. this.root.children.push(spanChartNode);
  79. }
  80. queue.push(
  81. ...node.children.map(
  82. // @todo use satisfies here when available
  83. child => [spanChartNode, child] as [SpanChartNode, SpanTreeNode]
  84. )
  85. );
  86. }
  87. depth++;
  88. }
  89. }
  90. collectSpanNodes(): SpanChartNode[] {
  91. const nodes: SpanChartNode[] = [];
  92. const visit = (node: SpanChartNode): void => {
  93. this.depth = Math.max(this.depth, node.depth);
  94. this.minSpanDuration = Math.min(this.minSpanDuration, node.duration);
  95. nodes.push(node);
  96. };
  97. this.forEachSpan(visit);
  98. return nodes;
  99. }
  100. }
  101. export {SpanChart};