@kylincloud/flamegraph 0.35.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/CHANGELOG.md +1211 -0
  2. package/LICENSE +202 -0
  3. package/README.md +251 -0
  4. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts +2 -0
  5. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts.map +1 -0
  6. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts +17 -0
  7. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts.map +1 -0
  8. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts +14 -0
  9. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts.map +1 -0
  10. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts +9 -0
  11. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts.map +1 -0
  12. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts +8 -0
  13. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts.map +1 -0
  14. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts +96 -0
  15. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts.map +1 -0
  16. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts +27 -0
  17. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts.map +1 -0
  18. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts +7 -0
  19. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts.map +1 -0
  20. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts +12 -0
  21. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts.map +1 -0
  22. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts +18 -0
  23. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts.map +1 -0
  24. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts +2 -0
  25. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts.map +1 -0
  26. package/dist/FlameGraph/FlameGraphComponent/color.d.ts +20 -0
  27. package/dist/FlameGraph/FlameGraphComponent/color.d.ts.map +1 -0
  28. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts +11 -0
  29. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts.map +1 -0
  30. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts +6 -0
  31. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts.map +1 -0
  32. package/dist/FlameGraph/FlameGraphComponent/index.d.ts +37 -0
  33. package/dist/FlameGraph/FlameGraphComponent/index.d.ts.map +1 -0
  34. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts +2 -0
  35. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts.map +1 -0
  36. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts +53 -0
  37. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts.map +1 -0
  38. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts +6 -0
  39. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts.map +1 -0
  40. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts +2 -0
  41. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts.map +1 -0
  42. package/dist/FlameGraph/FlameGraphRenderer.d.ts +86 -0
  43. package/dist/FlameGraph/FlameGraphRenderer.d.ts.map +1 -0
  44. package/dist/FlameGraph/decode.d.ts +27 -0
  45. package/dist/FlameGraph/decode.d.ts.map +1 -0
  46. package/dist/FlameGraph/normalize.d.ts +6 -0
  47. package/dist/FlameGraph/normalize.d.ts.map +1 -0
  48. package/dist/FlameGraph/uniqueness.d.ts +3 -0
  49. package/dist/FlameGraph/uniqueness.d.ts.map +1 -0
  50. package/dist/FlamegraphRenderer.d.ts +19 -0
  51. package/dist/FlamegraphRenderer.d.ts.map +1 -0
  52. package/dist/Icons.d.ts +9 -0
  53. package/dist/Icons.d.ts.map +1 -0
  54. package/dist/ProfilerTable.d.ts +21 -0
  55. package/dist/ProfilerTable.d.ts.map +1 -0
  56. package/dist/SharedQueryInput.d.ts +10 -0
  57. package/dist/SharedQueryInput.d.ts.map +1 -0
  58. package/dist/Toolbar.d.ts +31 -0
  59. package/dist/Toolbar.d.ts.map +1 -0
  60. package/dist/Tooltip/FlamegraphTooltip.d.ts +59 -0
  61. package/dist/Tooltip/FlamegraphTooltip.d.ts.map +1 -0
  62. package/dist/Tooltip/LeftClickIcon.d.ts +2 -0
  63. package/dist/Tooltip/LeftClickIcon.d.ts.map +1 -0
  64. package/dist/Tooltip/RightClickIcon.d.ts +2 -0
  65. package/dist/Tooltip/RightClickIcon.d.ts.map +1 -0
  66. package/dist/Tooltip/TableTooltip.d.ts +12 -0
  67. package/dist/Tooltip/TableTooltip.d.ts.map +1 -0
  68. package/dist/Tooltip/Tooltip.d.ts +29 -0
  69. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  70. package/dist/convert/convertJaegerTraceToProfile.d.ts +3 -0
  71. package/dist/convert/convertJaegerTraceToProfile.d.ts.map +1 -0
  72. package/dist/convert/diffTwoProfiles.d.ts +3 -0
  73. package/dist/convert/diffTwoProfiles.d.ts.map +1 -0
  74. package/dist/convert/flamebearersToTree.d.ts +11 -0
  75. package/dist/convert/flamebearersToTree.d.ts.map +1 -0
  76. package/dist/convert/sandwichViewProfiles.d.ts +14 -0
  77. package/dist/convert/sandwichViewProfiles.d.ts.map +1 -0
  78. package/dist/convert/subtract.d.ts +3 -0
  79. package/dist/convert/subtract.d.ts.map +1 -0
  80. package/dist/convert/testData.d.ts +50 -0
  81. package/dist/convert/testData.d.ts.map +1 -0
  82. package/dist/convert/toGraphviz.d.ts +3 -0
  83. package/dist/convert/toGraphviz.d.ts.map +1 -0
  84. package/dist/fitMode/fitMode.d.ts +42 -0
  85. package/dist/fitMode/fitMode.d.ts.map +1 -0
  86. package/dist/format/format.d.ts +42 -0
  87. package/dist/format/format.d.ts.map +1 -0
  88. package/dist/i18n.d.ts +55 -0
  89. package/dist/i18n.d.ts.map +1 -0
  90. package/dist/index.cjs.css +792 -0
  91. package/dist/index.cjs.js +5087 -0
  92. package/dist/index.d.ts +4 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.esm.css +792 -0
  95. package/dist/index.esm.js +5079 -0
  96. package/dist/index.node.d.ts +9 -0
  97. package/dist/index.node.d.ts.map +1 -0
  98. package/dist/logo-v3-small-T5VXIMRR.svg +32 -0
  99. package/dist/models/decode.d.ts +3 -0
  100. package/dist/models/decode.d.ts.map +1 -0
  101. package/dist/models/flamebearer.d.ts +63 -0
  102. package/dist/models/flamebearer.d.ts.map +1 -0
  103. package/dist/models/groups.d.ts +37 -0
  104. package/dist/models/groups.d.ts.map +1 -0
  105. package/dist/models/index.d.ts +8 -0
  106. package/dist/models/index.d.ts.map +1 -0
  107. package/dist/models/profile.d.ts +152 -0
  108. package/dist/models/profile.d.ts.map +1 -0
  109. package/dist/models/spyName.d.ts +8 -0
  110. package/dist/models/spyName.d.ts.map +1 -0
  111. package/dist/models/trace.d.ts +357 -0
  112. package/dist/models/trace.d.ts.map +1 -0
  113. package/dist/models/units.d.ts +6 -0
  114. package/dist/models/units.d.ts.map +1 -0
  115. package/dist/search.d.ts +2 -0
  116. package/dist/search.d.ts.map +1 -0
  117. package/dist/shims/Box.d.ts +38 -0
  118. package/dist/shims/Box.d.ts.map +1 -0
  119. package/dist/shims/Button.d.ts +26 -0
  120. package/dist/shims/Button.d.ts.map +1 -0
  121. package/dist/shims/Dropdown.d.ts +30 -0
  122. package/dist/shims/Dropdown.d.ts.map +1 -0
  123. package/dist/shims/Input.d.ts +19 -0
  124. package/dist/shims/Input.d.ts.map +1 -0
  125. package/dist/shims/LoadingSpinner.d.ts +7 -0
  126. package/dist/shims/LoadingSpinner.d.ts.map +1 -0
  127. package/dist/shims/Menu.d.ts +4 -0
  128. package/dist/shims/Menu.d.ts.map +1 -0
  129. package/dist/shims/NoData.d.ts +2 -0
  130. package/dist/shims/NoData.d.ts.map +1 -0
  131. package/dist/shims/Table.d.ts +52 -0
  132. package/dist/shims/Table.d.ts.map +1 -0
  133. package/dist/shims/Tooltip.d.ts +9 -0
  134. package/dist/shims/Tooltip.d.ts.map +1 -0
  135. package/package.json +84 -0
  136. package/src/FlameGraph/FlameGraphComponent/CheckIcon.tsx +27 -0
  137. package/src/FlameGraph/FlameGraphComponent/ContextMenu.module.scss +10 -0
  138. package/src/FlameGraph/FlameGraphComponent/ContextMenu.spec.tsx +84 -0
  139. package/src/FlameGraph/FlameGraphComponent/ContextMenu.tsx +86 -0
  140. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.module.css +8 -0
  141. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.tsx +47 -0
  142. package/src/FlameGraph/FlameGraphComponent/DiffLegend.module.css +21 -0
  143. package/src/FlameGraph/FlameGraphComponent/DiffLegend.tsx +52 -0
  144. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.module.css +40 -0
  145. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx +129 -0
  146. package/src/FlameGraph/FlameGraphComponent/Flamegraph.spec.ts +552 -0
  147. package/src/FlameGraph/FlameGraphComponent/Flamegraph.ts +446 -0
  148. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.spec.tsx +233 -0
  149. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.ts +478 -0
  150. package/src/FlameGraph/FlameGraphComponent/GraphVizPane.tsx +56 -0
  151. package/src/FlameGraph/FlameGraphComponent/GraphVizPanel.module.scss +55 -0
  152. package/src/FlameGraph/FlameGraphComponent/Header.module.css +27 -0
  153. package/src/FlameGraph/FlameGraphComponent/Header.tsx +71 -0
  154. package/src/FlameGraph/FlameGraphComponent/Highlight.module.css +7 -0
  155. package/src/FlameGraph/FlameGraphComponent/Highlight.spec.tsx +53 -0
  156. package/src/FlameGraph/FlameGraphComponent/Highlight.tsx +94 -0
  157. package/src/FlameGraph/FlameGraphComponent/LogoLink.module.scss +10 -0
  158. package/src/FlameGraph/FlameGraphComponent/LogoLink.tsx +101 -0
  159. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-collapses-small-blocks-into-one-1-snap.png +0 -0
  160. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-diff-mode-1-snap.png +0 -0
  161. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-highlighted-flamegraph-1-snap.png +0 -0
  162. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-normal-flamegraph-1-snap.png +0 -0
  163. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-selected-node-1-snap.png +0 -0
  164. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-also-zooms-1-snap.png +0 -0
  165. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-in-the-beginning-1-snap.png +0 -0
  166. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-when-node-is-not-in-the-beginning-1-snap.png +0 -0
  167. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-zoom-top-level-1-snap.png +0 -0
  168. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-complex-flamegraph-1-snap.png +0 -0
  169. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-double-diff-flamegraph-1-snap.png +0 -0
  170. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-double-flamegraph-1-snap.png +0 -0
  171. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-flamegraph-1-snap.png +0 -0
  172. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-flamegraph-1-snap.png +0 -0
  173. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-tree-1-snap.png +0 -0
  174. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-flamegraph-1-snap.png +0 -0
  175. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-with-fit-mode-tail-1-snap.png +0 -0
  176. package/src/FlameGraph/FlameGraphComponent/canvas.module.css +6 -0
  177. package/src/FlameGraph/FlameGraphComponent/color.spec.ts +308 -0
  178. package/src/FlameGraph/FlameGraphComponent/color.ts +167 -0
  179. package/src/FlameGraph/FlameGraphComponent/colorPalette.ts +58 -0
  180. package/src/FlameGraph/FlameGraphComponent/constants.ts +5 -0
  181. package/src/FlameGraph/FlameGraphComponent/index.spec.tsx +291 -0
  182. package/src/FlameGraph/FlameGraphComponent/index.tsx +411 -0
  183. package/src/FlameGraph/FlameGraphComponent/murmur3.ts +97 -0
  184. package/src/FlameGraph/FlameGraphComponent/styles.module.scss +10 -0
  185. package/src/FlameGraph/FlameGraphComponent/testData.ts +427 -0
  186. package/src/FlameGraph/FlameGraphComponent/utils.ts +31 -0
  187. package/src/FlameGraph/FlameGraphComponent/viewTypes.ts +6 -0
  188. package/src/FlameGraph/FlameGraphRenderer.tsx +603 -0
  189. package/src/FlameGraph/FlamegraphRenderer.module.scss +93 -0
  190. package/src/FlameGraph/decode.ts +78 -0
  191. package/src/FlameGraph/normalize.spec.ts +76 -0
  192. package/src/FlameGraph/normalize.ts +60 -0
  193. package/src/FlameGraph/testData.json +423 -0
  194. package/src/FlameGraph/uniqueness.spec.ts +16 -0
  195. package/src/FlameGraph/uniqueness.ts +84 -0
  196. package/src/FlamegraphRenderer.tsx +61 -0
  197. package/src/Icons.tsx +74 -0
  198. package/src/ProfilerTable.tsx +527 -0
  199. package/src/SharedQueryInput.module.scss +82 -0
  200. package/src/SharedQueryInput.tsx +127 -0
  201. package/src/Toolbar.module.scss +117 -0
  202. package/src/Toolbar.spec.tsx +217 -0
  203. package/src/Toolbar.tsx +471 -0
  204. package/src/Tooltip/FlamegraphTooltip.spec.tsx +81 -0
  205. package/src/Tooltip/FlamegraphTooltip.tsx +257 -0
  206. package/src/Tooltip/LeftClickIcon.tsx +18 -0
  207. package/src/Tooltip/RightClickIcon.tsx +18 -0
  208. package/src/Tooltip/TableTooltip.spec.tsx +44 -0
  209. package/src/Tooltip/TableTooltip.tsx +145 -0
  210. package/src/Tooltip/Tooltip.module.scss +71 -0
  211. package/src/Tooltip/Tooltip.spec.tsx +395 -0
  212. package/src/Tooltip/Tooltip.tsx +336 -0
  213. package/src/__snapshots__/Toolbar.spec.tsx.snap +297 -0
  214. package/src/convert/convertJaegerTraceToProfile.ts +97 -0
  215. package/src/convert/diffTwoProfiles.ts +81 -0
  216. package/src/convert/flamebearersToTree.ts +78 -0
  217. package/src/convert/sandwichViewProfiles.spec.ts +65 -0
  218. package/src/convert/sandwichViewProfiles.ts +191 -0
  219. package/src/convert/subtract.ts +87 -0
  220. package/src/convert/testData.ts +145 -0
  221. package/src/convert/toGraphviz.ts +485 -0
  222. package/src/fitMode/fitMode.spec.ts +93 -0
  223. package/src/fitMode/fitMode.ts +122 -0
  224. package/src/format/format.spec.ts +291 -0
  225. package/src/format/format.ts +303 -0
  226. package/src/globals.d.ts +13 -0
  227. package/src/i18n.tsx +293 -0
  228. package/src/index.node.ts +19 -0
  229. package/src/index.spec.tsx +383 -0
  230. package/src/index.tsx +10 -0
  231. package/src/logo-v3-small.svg +32 -0
  232. package/src/models/decode.ts +45 -0
  233. package/src/models/flamebearer.ts +86 -0
  234. package/src/models/groups.ts +14 -0
  235. package/src/models/index.ts +7 -0
  236. package/src/models/profile.spec.ts +32 -0
  237. package/src/models/profile.ts +48 -0
  238. package/src/models/spyName.spec.ts +18 -0
  239. package/src/models/spyName.ts +32 -0
  240. package/src/models/trace.ts +45 -0
  241. package/src/models/units.spec.ts +21 -0
  242. package/src/models/units.ts +24 -0
  243. package/src/sass/_common.scss +206 -0
  244. package/src/sass/_css-variables.scss +201 -0
  245. package/src/sass/_mixins.scss +15 -0
  246. package/src/sass/_sanitize.scss +407 -0
  247. package/src/sass/_variables.scss +53 -0
  248. package/src/sass/flamegraph.scss +18 -0
  249. package/src/search.spec.ts +11 -0
  250. package/src/search.ts +4 -0
  251. package/src/shameful-any.d.ts +2 -0
  252. package/src/shims/Box.module.scss +57 -0
  253. package/src/shims/Box.tsx +105 -0
  254. package/src/shims/Button.module.scss +129 -0
  255. package/src/shims/Button.tsx +128 -0
  256. package/src/shims/Dropdown.module.scss +63 -0
  257. package/src/shims/Dropdown.tsx +96 -0
  258. package/src/shims/Input.module.scss +15 -0
  259. package/src/shims/Input.tsx +55 -0
  260. package/src/shims/LoadingSpinner.tsx +19 -0
  261. package/src/shims/Menu.tsx +9 -0
  262. package/src/shims/NoData.module.scss +6 -0
  263. package/src/shims/NoData.tsx +11 -0
  264. package/src/shims/Table.module.scss +82 -0
  265. package/src/shims/Table.spec.tsx +121 -0
  266. package/src/shims/Table.tsx +252 -0
  267. package/src/shims/Tooltip.tsx +51 -0
@@ -0,0 +1,97 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import groupBy from 'lodash.groupby';
3
+ import map from 'lodash.map';
4
+ import type { Profile, Trace, TraceSpan } from '../models';
5
+ import { deltaDiffWrapperReverse } from '../FlameGraph/decode';
6
+
7
+ interface Span extends TraceSpan {
8
+ children: Span[];
9
+ total: number;
10
+ self: number;
11
+ }
12
+
13
+ export function convertJaegerTraceToProfile(trace: Trace): Profile {
14
+ const resultFlamebearer = {
15
+ numTicks: 0,
16
+ maxSelf: 0,
17
+ names: [] as string[],
18
+ levels: [] as number[][],
19
+ };
20
+
21
+ // Step 1: converting spans to a tree
22
+
23
+ const spans: Record<string, Span> = {};
24
+ const root: Span = { children: [] } as unknown as Span;
25
+ (trace.spans as Span[]).forEach((span) => {
26
+ span.children = [];
27
+ spans[span.spanID] = span;
28
+ });
29
+
30
+ (trace.spans as Span[]).forEach((span) => {
31
+ let node = root;
32
+ if (span.references && span.references.length > 0) {
33
+ node = spans[span.references[0].spanID] || root;
34
+ }
35
+
36
+ node.children.push(span);
37
+ });
38
+
39
+ // Step 2: group spans with same name
40
+
41
+ function groupSpans(span: Span, d: number) {
42
+ (span.children || []).forEach((x) => groupSpans(x, d + 1));
43
+
44
+ let childrenDur = 0;
45
+ const groups = groupBy(span.children || [], (x) => x.operationName);
46
+ span.children = map(groups, (group) => {
47
+ const res = group[0];
48
+ for (let i = 1; i < group.length; i += 1) {
49
+ res.duration += group[i].duration;
50
+ }
51
+ childrenDur += res.duration;
52
+ return res;
53
+ });
54
+ span.total = span.duration || childrenDur;
55
+ span.self = Math.max(0, span.total - childrenDur);
56
+ }
57
+ groupSpans(root, 0);
58
+
59
+ // Step 3: traversing the tree
60
+
61
+ function processNode(span: Span, level: number, offset: number) {
62
+ resultFlamebearer.numTicks ||= span.total;
63
+ resultFlamebearer.levels[level] ||= [];
64
+ resultFlamebearer.levels[level].push(offset);
65
+ resultFlamebearer.levels[level].push(span.total);
66
+ resultFlamebearer.levels[level].push(span.self);
67
+ resultFlamebearer.names.push(
68
+ (span.processID
69
+ ? `${trace.processes[span.processID].serviceName}: `
70
+ : '') + (span.operationName || 'total')
71
+ );
72
+ resultFlamebearer.levels[level].push(resultFlamebearer.names.length - 1);
73
+
74
+ (span.children || []).forEach((x) => {
75
+ offset += processNode(x, level + 1, offset);
76
+ });
77
+ return span.total;
78
+ }
79
+
80
+ processNode(root, 0, 0);
81
+
82
+ resultFlamebearer.levels = deltaDiffWrapperReverse(
83
+ 'single',
84
+ resultFlamebearer.levels
85
+ );
86
+
87
+ return {
88
+ version: 1,
89
+ flamebearer: resultFlamebearer,
90
+ metadata: {
91
+ format: 'single',
92
+ units: 'trace_samples',
93
+ spyName: 'tracing',
94
+ sampleRate: 1000000,
95
+ },
96
+ };
97
+ }
@@ -0,0 +1,81 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
+ // @ts-nocheck
4
+
5
+ import type { Profile, Flamebearer } from './models';
6
+ import {
7
+ deltaDiffWrapper,
8
+ deltaDiffWrapperReverse,
9
+ } from '../FlameGraph/decode';
10
+ import { flamebearersToTree } from './flamebearersToTree';
11
+
12
+ function diffFlamebearer(f1: Flamebearer, f2: Flamebearer): Flamebearer {
13
+ const result: Flamebearer = {
14
+ format: 'double',
15
+ numTicks: f1.numTicks + f2.numTicks,
16
+ leftTicks: f1.numTicks,
17
+ rightTicks: f2.numTicks,
18
+ maxSelf: 100,
19
+ sampleRate: f1.sampleRate,
20
+ names: [],
21
+ levels: [],
22
+ units: f1.units,
23
+ spyName: f1.spyName,
24
+ };
25
+
26
+ const tree = flamebearersToTree(f1, f2);
27
+ const processNode = (
28
+ node,
29
+ level: number,
30
+ offsetLeft: number,
31
+ offsetRight: number
32
+ ) => {
33
+ const { name, children, self, total } = node;
34
+ result.names.push(name);
35
+ result.levels[level] ||= [];
36
+ result.maxSelf = Math.max(result.maxSelf, self[0] || 0, self[1] || 0);
37
+ result.levels[level] = result.levels[level].concat([
38
+ offsetLeft,
39
+ total[0] || 0,
40
+ self[0] || 0,
41
+ offsetRight,
42
+ total[1] || 0,
43
+ self[1] || 0,
44
+ result.names.length - 1,
45
+ ]);
46
+ for (let i = 0; i < children.length; i += 1) {
47
+ const [ol, or] = processNode(
48
+ children[i],
49
+ level + 1,
50
+ offsetLeft,
51
+ offsetRight
52
+ );
53
+ offsetLeft += ol;
54
+ offsetRight += or;
55
+ }
56
+ return [total[0] || 0, total[1] || 0];
57
+ };
58
+
59
+ processNode(tree, 0, 0, 0);
60
+
61
+ return result;
62
+ }
63
+
64
+ export function diffTwoProfiles(p1: Profile, p2: Profile): Profile {
65
+ p1.flamebearer.levels = deltaDiffWrapper('single', p1.flamebearer.levels);
66
+ p2.flamebearer.levels = deltaDiffWrapper('single', p2.flamebearer.levels);
67
+ const resultFlamebearer = diffFlamebearer(p1.flamebearer, p2.flamebearer);
68
+ resultFlamebearer.levels = deltaDiffWrapperReverse(
69
+ 'double',
70
+ resultFlamebearer.levels
71
+ );
72
+ const metadata = { ...p1.metadata };
73
+ metadata.format = 'double';
74
+ return {
75
+ version: 1,
76
+ flamebearer: resultFlamebearer,
77
+ metadata,
78
+ leftTicks: p1.flamebearer.numTicks,
79
+ rightTicks: p2.flamebearer.numTicks,
80
+ };
81
+ }
@@ -0,0 +1,78 @@
1
+ import type { Profile } from '../models';
2
+
3
+ export interface TreeNode {
4
+ name: string;
5
+ key: string;
6
+ self: number[];
7
+ total: number[];
8
+ offset?: number;
9
+ children: TreeNode[];
10
+ }
11
+
12
+ export function flamebearersToTree(
13
+ f1: Profile['flamebearer'],
14
+ f2?: Profile['flamebearer']
15
+ ): TreeNode {
16
+ const globalLookup: { [key: string]: TreeNode } = {};
17
+ const treeSpecificLookup: { [key: string]: TreeNode } = {};
18
+ let root: TreeNode = {
19
+ name: 'total',
20
+ children: [],
21
+ self: [],
22
+ total: [],
23
+ key: '/total',
24
+ };
25
+
26
+ (f2 ? [f1, f2] : [f1]).forEach((f, fi) => {
27
+ for (let i = 0; i < f.levels.length; i += 1) {
28
+ for (let j = 0; j < f.levels[i].length; j += 4) {
29
+ const treeSpecificKey: string = [fi, i, j].join('/');
30
+ const name: string = f.names[f.levels[i][j + 3]];
31
+ const offset: number = f.levels[i][j + 0];
32
+ const total: number = f.levels[i][j + 1];
33
+ const self: number = f.levels[i][j + 2];
34
+ let parentGlobalKey = '';
35
+ // searching for parent node
36
+ if (i !== 0) {
37
+ const pi = i - 1;
38
+ const parentLevel = f.levels[pi];
39
+ for (let k = 0; k < parentLevel.length; k += 4) {
40
+ const parentOffset = parentLevel[k + 0];
41
+ const total = parentLevel[k + 1];
42
+ if (offset >= parentOffset && offset < parentOffset + total) {
43
+ const parentTreeSpecificKey = [fi, pi, k].join('/');
44
+ const parentObj = treeSpecificLookup[parentTreeSpecificKey];
45
+ parentGlobalKey = parentObj.key;
46
+ break;
47
+ }
48
+ }
49
+ }
50
+
51
+ const globalKey = [parentGlobalKey || '', name].join('/');
52
+ const isNewObject = !globalLookup[globalKey];
53
+ globalLookup[globalKey] ||= {
54
+ name,
55
+ children: [],
56
+ self: [],
57
+ total: [],
58
+ key: globalKey,
59
+ } as TreeNode;
60
+ const obj: TreeNode = globalLookup[globalKey];
61
+ obj.total[fi] ||= 0;
62
+ obj.total[fi] += total;
63
+ obj.self[fi] ||= 0;
64
+ obj.self[fi] += self;
65
+ treeSpecificLookup[treeSpecificKey] = obj;
66
+
67
+ if (parentGlobalKey && isNewObject) {
68
+ globalLookup[parentGlobalKey].children.push(obj);
69
+ }
70
+ if (i === 0) {
71
+ root = obj;
72
+ }
73
+ }
74
+ }
75
+ });
76
+
77
+ return root;
78
+ }
@@ -0,0 +1,65 @@
1
+ import { treeToFlamebearer, calleesFlamebearer } from './sandwichViewProfiles';
2
+ import { flamebearersToTree } from './flamebearersToTree';
3
+
4
+ import { tree, singleAppearanceTrees } from './testData';
5
+ import { Flamebearer } from '../models';
6
+
7
+ const flamebearersProps = {
8
+ spyName: 'gospy',
9
+ units: 'samples',
10
+ format: 'single',
11
+ numTicks: 400,
12
+ maxSelf: 150,
13
+ sampleRate: 100,
14
+ };
15
+
16
+ describe('Sandwich view profiles', () => {
17
+ describe('when target function has single tree appearance', () => {
18
+ it('return correct flamebearer with 0 callees', () => {
19
+ const f = treeToFlamebearer(tree);
20
+
21
+ const resultCalleesFlamebearer = calleesFlamebearer(
22
+ { ...f, ...flamebearersProps } as Flamebearer,
23
+ 'name-5-2'
24
+ );
25
+
26
+ const treeToMatchOriginalTree = flamebearersToTree(
27
+ resultCalleesFlamebearer
28
+ );
29
+
30
+ expect(treeToMatchOriginalTree).toMatchObject(singleAppearanceTrees.zero);
31
+ });
32
+ it('return correct flamebearer with 1 callee', () => {
33
+ const f = treeToFlamebearer(tree);
34
+
35
+ const resultCalleesFlamebearer = calleesFlamebearer(
36
+ { ...f, ...flamebearersProps } as Flamebearer,
37
+ 'wwwwwww'
38
+ );
39
+
40
+ const treeToMatchOriginalTree = flamebearersToTree(
41
+ resultCalleesFlamebearer
42
+ );
43
+
44
+ expect(treeToMatchOriginalTree).toMatchObject(singleAppearanceTrees.one);
45
+ });
46
+ it('return correct flamebearer with multiple callees', () => {
47
+ const f = treeToFlamebearer(tree);
48
+
49
+ const resultCalleesFlamebearer = calleesFlamebearer(
50
+ { ...f, ...flamebearersProps } as Flamebearer,
51
+ 'name-2-2'
52
+ );
53
+
54
+ const treeToMatchOriginalTree = flamebearersToTree(
55
+ resultCalleesFlamebearer
56
+ );
57
+
58
+ expect(treeToMatchOriginalTree).toMatchObject(
59
+ singleAppearanceTrees.multiple
60
+ );
61
+ });
62
+ });
63
+
64
+ // todo(dogfrogfog): add tests for callers flamegraph when remove top lvl empty node
65
+ });
@@ -0,0 +1,191 @@
1
+ import type { Flamebearer } from '../models';
2
+ import { flamebearersToTree, TreeNode } from './flamebearersToTree';
3
+
4
+ interface FlamebearerData {
5
+ maxSelf: number;
6
+ levels: number[][];
7
+ names: string[];
8
+ }
9
+
10
+ export const treeToFlamebearer = (tree: TreeNode): FlamebearerData => {
11
+ const flamebearerData: FlamebearerData = {
12
+ maxSelf: 100,
13
+ names: [],
14
+ levels: [],
15
+ };
16
+
17
+ const processNode = (node: TreeNode, level: number, offsetLeft: number) => {
18
+ const { name, children, self, total, offset } = node;
19
+ flamebearerData.names.push(name);
20
+ flamebearerData.levels[level] ||= [];
21
+ flamebearerData.maxSelf = Math.max(flamebearerData.maxSelf, self[0] || 0);
22
+ flamebearerData.levels[level] = flamebearerData.levels[level].concat([
23
+ offsetLeft,
24
+ total[0] || 0,
25
+ self[0] || 0,
26
+ flamebearerData.names.length - 1,
27
+ ]);
28
+
29
+ for (let i = 0; i < children.length; i += 1) {
30
+ const ol = processNode(children[i], level + 1, offsetLeft);
31
+ offsetLeft += ol;
32
+ }
33
+ return offset || total[0] || 0;
34
+ };
35
+
36
+ processNode(tree, 0, 0);
37
+
38
+ return flamebearerData;
39
+ };
40
+
41
+ const arrayToTree = (nodesArray: TreeNode[], total: number): TreeNode => {
42
+ const result = {} as TreeNode;
43
+ let nestedObj = result;
44
+
45
+ nodesArray.forEach(({ name, ...rest }) => {
46
+ const nextNode = { name, ...rest, total: [total] };
47
+ nestedObj.children = [nextNode];
48
+ nestedObj = nextNode;
49
+ });
50
+
51
+ return result.children[0];
52
+ };
53
+
54
+ function dedupTree(node: TreeNode) {
55
+ const childrenMap = new Map<string, TreeNode>();
56
+ for (let i = 0; i < node.children.length; i += 1) {
57
+ if (!childrenMap.has(node.children[i].name)) {
58
+ childrenMap.set(node.children[i].name, node.children[i]);
59
+ }
60
+ }
61
+
62
+ for (let i = 0; i < node.children.length; i += 1) {
63
+ const currentNode = node.children[i];
64
+ const existingNode = childrenMap.get(node.children[i].name);
65
+ if (existingNode && existingNode !== currentNode) {
66
+ existingNode.total[0] += currentNode.total[0];
67
+ existingNode.self[0] += currentNode.self[0];
68
+ existingNode.children = existingNode.children.concat(
69
+ currentNode.children
70
+ );
71
+ }
72
+ }
73
+ node.children = Array.from(childrenMap.values());
74
+ for (let i = 0; i < node.children.length; i += 1) {
75
+ dedupTree(node.children[i]);
76
+ }
77
+ }
78
+
79
+ export function calleesFlamebearer(
80
+ f: Flamebearer,
81
+ nodeName: string
82
+ ): Flamebearer {
83
+ const tree = flamebearersToTree(f);
84
+ const result: Flamebearer = {
85
+ format: 'single',
86
+ numTicks: 0,
87
+ maxSelf: 100,
88
+ sampleRate: 100,
89
+ names: [],
90
+ levels: [],
91
+ units: f.units,
92
+ spyName: f.spyName,
93
+ };
94
+
95
+ const totalNode = {
96
+ name: nodeName,
97
+ key: `/${nodeName}`,
98
+ total: [],
99
+ self: [0],
100
+ children: [],
101
+ } as TreeNode;
102
+ const processTree = (node: TreeNode) => {
103
+ if (node.name === nodeName) {
104
+ result.numTicks += node.total[0];
105
+
106
+ totalNode.total = [result.numTicks];
107
+ totalNode.children = totalNode.children.concat(node.children);
108
+ }
109
+ for (let i = 0; i < node.children.length; i += 1) {
110
+ processTree(node.children[i]);
111
+ }
112
+ };
113
+ processTree(tree);
114
+ dedupTree(totalNode);
115
+
116
+ return { ...result, ...treeToFlamebearer(totalNode) };
117
+ }
118
+
119
+ export function callersFlamebearer(
120
+ f: Flamebearer,
121
+ nodeName: string
122
+ ): Flamebearer {
123
+ const tree = flamebearersToTree(f);
124
+ const result: Flamebearer = {
125
+ format: 'single',
126
+ maxSelf: 100,
127
+ sampleRate: 100,
128
+ numTicks: 0,
129
+ names: [],
130
+ levels: [],
131
+ units: f.units,
132
+ spyName: f.spyName,
133
+ };
134
+
135
+ const targetFunctionTotals: number[] = [];
136
+ const subtrees: TreeNode[][] = [];
137
+
138
+ const totalNode = {
139
+ name: nodeName,
140
+ key: `/${nodeName}`,
141
+ total: [0],
142
+ self: [0],
143
+ children: [],
144
+ } as TreeNode;
145
+ const processTree = (node: TreeNode, parentNodes: TreeNode[] = []) => {
146
+ const currentSubtree = parentNodes.concat([
147
+ { ...node, children: [] } as TreeNode,
148
+ ]);
149
+
150
+ if (node.name === nodeName) {
151
+ subtrees.push(currentSubtree);
152
+ targetFunctionTotals.push(node.total[0]);
153
+ result.numTicks += node.total[0];
154
+ }
155
+
156
+ for (let i = 0; i < node.children.length; i += 1) {
157
+ processTree(node.children[i], currentSubtree);
158
+ }
159
+ };
160
+ processTree(tree);
161
+
162
+ // 1. we first make a regular tree
163
+ subtrees.forEach((v, i) => {
164
+ totalNode.children.push(arrayToTree(v.reverse(), targetFunctionTotals[i]));
165
+ });
166
+
167
+ // 2. that allows us to use the same dedup function
168
+ dedupTree(totalNode);
169
+
170
+ const flamebearer = treeToFlamebearer(totalNode);
171
+
172
+ // 3. then we reverse levels so that tree goes from bottom to top
173
+ flamebearer.levels = flamebearer.levels.reverse().slice(0, -1);
174
+
175
+ return { ...result, ...flamebearer };
176
+ }
177
+
178
+ export function calleesProfile(f: Flamebearer, nodeName: string): Flamebearer {
179
+ const copy = JSON.parse(JSON.stringify(f));
180
+ const calleesResultFlamebearer = calleesFlamebearer(copy, nodeName);
181
+
182
+ return calleesResultFlamebearer;
183
+ }
184
+
185
+ export function callersProfile(f: Flamebearer, nodeName: string): Flamebearer {
186
+ const copy = JSON.parse(JSON.stringify(f));
187
+
188
+ const callersResultFlamebearer = callersFlamebearer(copy, nodeName);
189
+
190
+ return callersResultFlamebearer;
191
+ }
@@ -0,0 +1,87 @@
1
+ import type { Profile } from '../models';
2
+ import {
3
+ decodeFlamebearer,
4
+ deltaDiffWrapperReverse,
5
+ } from '../FlameGraph/decode';
6
+ import { flamebearersToTree, TreeNode } from './flamebearersToTree';
7
+
8
+ function subtractFlamebearer(
9
+ f1: Profile['flamebearer'],
10
+ f2: Profile['flamebearer']
11
+ ): Profile['flamebearer'] {
12
+ const result: Profile['flamebearer'] = {
13
+ numTicks: 0,
14
+ maxSelf: 0,
15
+ names: [],
16
+ levels: [],
17
+ };
18
+
19
+ const tree = flamebearersToTree(f1, f2);
20
+
21
+ const updateNumbers = (node: TreeNode): number => {
22
+ // self is easy
23
+ node.self[0] = Math.max((node.self[0] || 0) - (node.self[1] || 0), 0);
24
+ result.numTicks += node.self[0];
25
+
26
+ // total needs to be recalculated using children
27
+ if (node.children.length === 0) {
28
+ node.total[0] = Math.max((node.total[0] || 0) - (node.total[1] || 0), 0);
29
+ } else {
30
+ let total = node.self[0];
31
+ for (let i = 0; i < node.children.length; i += 1) {
32
+ total += updateNumbers(node.children[i]);
33
+ }
34
+ node.total[0] = total;
35
+ }
36
+
37
+ return node.total[0];
38
+ };
39
+
40
+ updateNumbers(tree);
41
+
42
+ const processNode = (node: TreeNode, level: number, offset: number) => {
43
+ const { name, children, self, total } = node;
44
+ result.levels[level] ||= [];
45
+ const newSelf = self[0];
46
+ const newTotal = total[0];
47
+ result.maxSelf = Math.max(result.maxSelf, newSelf);
48
+ if (newTotal === 0) {
49
+ return 0;
50
+ }
51
+ result.names.push(name);
52
+ result.levels[level] = result.levels[level].concat([
53
+ offset,
54
+ newTotal,
55
+ newSelf,
56
+ result.names.length - 1,
57
+ ]);
58
+ for (let i = 0; i < children.length; i += 1) {
59
+ const offsetAddition = processNode(children[i], level + 1, offset);
60
+ offset += offsetAddition;
61
+ }
62
+ return newTotal;
63
+ };
64
+
65
+ processNode(tree, 0, 0);
66
+ return result;
67
+ }
68
+
69
+ // this functions expects two compressed (before delta diff) profiles,
70
+ // and returns a compressed profile
71
+ export function subtract(p1: Profile, p2: Profile): Profile {
72
+ p1 = decodeFlamebearer(p1);
73
+ p2 = decodeFlamebearer(p2);
74
+
75
+ const resultFlamebearer = subtractFlamebearer(p1.flamebearer, p2.flamebearer);
76
+ resultFlamebearer.levels = deltaDiffWrapperReverse(
77
+ 'single',
78
+ resultFlamebearer.levels
79
+ );
80
+ const metadata = { ...p1.metadata };
81
+ metadata.format = 'single';
82
+ return {
83
+ version: 1,
84
+ flamebearer: resultFlamebearer,
85
+ metadata,
86
+ };
87
+ }