@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,145 @@
1
+ import type { TreeNode } from './flamebearersToTree';
2
+
3
+ export const tree: TreeNode = {
4
+ name: 'name',
5
+ key: '/name',
6
+ self: [0],
7
+ total: [1000],
8
+ children: [
9
+ {
10
+ name: 'specific-function-name',
11
+ key: '/name/specific-function-name',
12
+ self: [0],
13
+ total: [600],
14
+ children: [
15
+ {
16
+ name: 'specific-function-name',
17
+ key: '/name/specific-function-name/specific-function-name',
18
+ self: [200],
19
+ total: [200],
20
+ children: [],
21
+ },
22
+ {
23
+ name: 'wwwwwww',
24
+ key: '/name/specific-function-name/wwwwwww',
25
+ self: [20],
26
+ total: [400],
27
+ children: [
28
+ {
29
+ name: 'name-3-2',
30
+ key: '/name/specific-function-name/wwwwwww/name-3-2',
31
+ self: [380],
32
+ total: [380],
33
+ children: [],
34
+ },
35
+ ],
36
+ },
37
+ ],
38
+ },
39
+ {
40
+ name: 'name-2-2',
41
+ key: '/name/name-2-2',
42
+ self: [0],
43
+ total: [400],
44
+ children: [
45
+ {
46
+ name: 'name-3-1',
47
+ key: '/name/name-2-2/name-3-1',
48
+ self: [100],
49
+ total: [400],
50
+ children: [
51
+ {
52
+ name: 'specific-function-name',
53
+ key: '/name/name-2-2/name-3-1/specific-function-name',
54
+ self: [0],
55
+ total: [300],
56
+ children: [
57
+ {
58
+ name: 'name-5-1',
59
+ key: '/name/name-2-2/name-3-1/specific-function-name/name-5-1',
60
+ self: [150],
61
+ total: [150],
62
+ children: [],
63
+ },
64
+ {
65
+ name: 'name-5-2',
66
+ key: '/name/name-2-2/name-3-1/specific-function-name/name-5-2',
67
+ self: [150],
68
+ total: [150],
69
+ children: [],
70
+ },
71
+ ],
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ },
77
+ ],
78
+ };
79
+
80
+ const singleAppearanceZeroCalleesTree = {
81
+ name: 'name-5-2',
82
+ children: [],
83
+ self: [0],
84
+ total: [150],
85
+ key: '/name-5-2',
86
+ };
87
+ const singleAppearanceSingleCalleeTree = {
88
+ name: 'wwwwwww',
89
+ key: '/wwwwwww',
90
+ self: [0],
91
+ total: [400],
92
+ children: [
93
+ {
94
+ name: 'name-3-2',
95
+ key: '/wwwwwww/name-3-2',
96
+ self: [380],
97
+ total: [380],
98
+ children: [],
99
+ },
100
+ ],
101
+ };
102
+ const singleAppearanceMultipleCalleesTree = {
103
+ name: 'name-2-2',
104
+ key: '/name-2-2',
105
+ total: [400],
106
+ self: [0],
107
+ children: [
108
+ {
109
+ name: 'name-3-1',
110
+ key: '/name-2-2/name-3-1',
111
+ self: [100],
112
+ total: [400],
113
+ children: [
114
+ {
115
+ name: 'specific-function-name',
116
+ key: '/name-2-2/name-3-1/specific-function-name',
117
+ self: [0],
118
+ total: [300],
119
+ children: [
120
+ {
121
+ name: 'name-5-1',
122
+ key: '/name-2-2/name-3-1/specific-function-name/name-5-1',
123
+ self: [150],
124
+ total: [150],
125
+ children: [],
126
+ },
127
+ {
128
+ name: 'name-5-2',
129
+ key: '/name-2-2/name-3-1/specific-function-name/name-5-2',
130
+ self: [150],
131
+ total: [150],
132
+ children: [],
133
+ },
134
+ ],
135
+ },
136
+ ],
137
+ },
138
+ ],
139
+ };
140
+
141
+ export const singleAppearanceTrees = {
142
+ zero: singleAppearanceZeroCalleesTree,
143
+ one: singleAppearanceSingleCalleeTree,
144
+ multiple: singleAppearanceMultipleCalleesTree,
145
+ };
@@ -0,0 +1,485 @@
1
+ import type { Profile } from '../models';
2
+
3
+ import { flamebearersToTree, TreeNode } from './flamebearersToTree';
4
+ import { getFormatter } from '../format/format';
5
+
6
+ const nodeFraction = 0.005;
7
+ const edgeFraction = 0.001;
8
+ const maxNodes = 80;
9
+
10
+ // have to specify font name here, otherwise renderer won't size boxes properly
11
+ // const fontName = "SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier,monospace";
12
+ const fontName = '';
13
+
14
+ function renderLabels(obj: { [key: string]: string | number }): string {
15
+ const labels: string[] = [];
16
+ // for (const key of ) {
17
+ Object.keys(obj).forEach((key) => {
18
+ labels.push(`${key}="${escapeForDot(String(obj[key] || ''))}"`);
19
+ });
20
+ return `[${labels.join(' ')}]`;
21
+ }
22
+
23
+ const baseFontSize = 8;
24
+ const maxFontGrowth = 16;
25
+
26
+ function formatPercent(a: number, b: number): string {
27
+ return `${((a * 100) / b).toFixed(2)}%`;
28
+ }
29
+
30
+ type sampleFormatter = (dur: number) => string;
31
+
32
+ // dotColor returns a color for the given score (between -1.0 and
33
+ // 1.0), with -1.0 colored green, 0.0 colored grey, and 1.0 colored
34
+ // red. If isBackground is true, then a light (low-saturation)
35
+ // color is returned (suitable for use as a background color);
36
+ // otherwise, a darker color is returned (suitable for use as a
37
+ // foreground color).
38
+ function dotColor(score: number, isBackground: boolean): string {
39
+ // A float between 0.0 and 1.0, indicating the extent to which
40
+ // colors should be shifted away from grey (to make positive and
41
+ // negative values easier to distinguish, and to make more use of
42
+ // the color range.)
43
+ const shift = 0.7;
44
+
45
+ // Saturation and value (in hsv colorspace) for background colors.
46
+ const bgSaturation = 0.1;
47
+ const bgValue = 0.93;
48
+
49
+ // Saturation and value (in hsv colorspace) for foreground colors.
50
+ const fgSaturation = 1.0;
51
+ const fgValue = 0.7;
52
+
53
+ // Choose saturation and value based on isBackground.
54
+ let saturation: number;
55
+ let value: number;
56
+ if (isBackground) {
57
+ saturation = bgSaturation;
58
+ value = bgValue;
59
+ } else {
60
+ saturation = fgSaturation;
61
+ value = fgValue;
62
+ }
63
+
64
+ // Limit the score values to the range [-1.0, 1.0].
65
+ score = Math.max(-1.0, Math.min(1.0, score));
66
+
67
+ // Reduce saturation near score=0 (so it is colored grey, rather than yellow).
68
+ if (Math.abs(score) < 0.2) {
69
+ saturation *= Math.abs(score) / 0.2;
70
+ }
71
+
72
+ // Apply 'shift' to move scores away from 0.0 (grey).
73
+ if (score > 0.0) {
74
+ score **= 1.0 - shift;
75
+ }
76
+ if (score < 0.0) {
77
+ score = -((-score) ** (1.0 - shift));
78
+ }
79
+
80
+ let r: number;
81
+ let g: number; // red, green, blue
82
+ if (score < 0.0) {
83
+ g = value;
84
+ r = value * (1 + saturation * score);
85
+ } else {
86
+ r = value;
87
+ g = value * (1 - saturation * score);
88
+ }
89
+ const b: number = value * (1 - saturation);
90
+ return `#${Math.floor(r * 255.0)
91
+ .toString(16)
92
+ .padStart(2, '0')}${Math.floor(g * 255.0)
93
+ .toString(16)
94
+ .padStart(2, '0')}${Math.floor(b * 255.0)
95
+ .toString(16)
96
+ .padStart(2, '0')}`;
97
+ }
98
+
99
+ function renderNode(
100
+ format: sampleFormatter,
101
+ n: GraphNode,
102
+ maxSelf: number,
103
+ maxTotal: number
104
+ ): string {
105
+ const { self } = n;
106
+ const { total } = n;
107
+
108
+ const name = n.name.replace(/"/g, '\\"');
109
+ const dur = format(self);
110
+ const fontsize =
111
+ baseFontSize + Math.ceil(maxFontGrowth * Math.sqrt(self / maxSelf));
112
+ const color = dotColor(total / maxTotal, false);
113
+ const fillcolor = dotColor(total / maxTotal, true);
114
+
115
+ const label = formatNodeLabel(format, name, self, total, maxTotal);
116
+
117
+ const labels = {
118
+ label,
119
+ id: `node${n.index}`,
120
+ fontsize,
121
+ shape: 'box',
122
+ tooltip: `${name} (${dur})`,
123
+ color,
124
+ fontcolor: '#000000',
125
+ style: 'filled',
126
+ fontname: fontName,
127
+ // margin: "0.7,0.055",
128
+ fillcolor,
129
+ };
130
+ return `N${n.index} ${renderLabels(labels)}`;
131
+ }
132
+
133
+ function escapeForDot(str: string) {
134
+ return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
135
+ }
136
+
137
+ function pathBasename(p: string): string {
138
+ return p.replace(/.*\//, '');
139
+ }
140
+
141
+ function formatNodeLabel(
142
+ format: sampleFormatter,
143
+ name: string,
144
+ self: number,
145
+ total: number,
146
+ maxTotal: number
147
+ ): string {
148
+ let label = '';
149
+ label = `${pathBasename(name)}\n`;
150
+
151
+ const selfValue = format(self);
152
+ if (self !== 0) {
153
+ label = `${label + selfValue} (${formatPercent(self, maxTotal)})`;
154
+ } else {
155
+ label += '0';
156
+ }
157
+ let totalValue = selfValue;
158
+ if (total !== self) {
159
+ if (self !== 0) {
160
+ label += '\n';
161
+ } else {
162
+ label += ' ';
163
+ }
164
+ totalValue = format(total);
165
+ label = `${label}of ${totalValue} (${formatPercent(total, maxTotal)})`;
166
+ }
167
+
168
+ return label;
169
+ }
170
+
171
+ function renderEdge(
172
+ sampleFormatter: sampleFormatter,
173
+ edge: GraphEdge,
174
+ maxTotal: number
175
+ ): string {
176
+ const srcName = edge.from.name.replace(/"/g, '\\"');
177
+ const dstName = edge.to.name.replace(/"/g, '\\"');
178
+ const edgeWeight = edge.weight; // TODO
179
+ const dur = sampleFormatter(edge.weight); // TODO
180
+ const weight = 1 + (edgeWeight * 100) / maxTotal;
181
+ const penwidth = 1 + (edgeWeight * 5) / maxTotal;
182
+ const color = dotColor(edgeWeight / maxTotal, false);
183
+ const tooltip = `${srcName} -> ${dstName} (${dur})`;
184
+
185
+ const labels = {
186
+ label: dur,
187
+ weight,
188
+ penwidth,
189
+ tooltip,
190
+ labeltooltip: tooltip,
191
+ fontname: fontName,
192
+ color,
193
+ style: edge.residual ? 'dotted' : '',
194
+ };
195
+ return `N${edge.from.index} -> N${edge.to.index} ${renderLabels(labels)}`;
196
+ }
197
+
198
+ type GraphNode = {
199
+ name: string;
200
+ index: number;
201
+ self: number;
202
+ total: number;
203
+ parents: GraphEdge[];
204
+ children: GraphEdge[];
205
+ };
206
+
207
+ type GraphEdge = {
208
+ from: GraphNode;
209
+ to: GraphNode;
210
+ weight: number;
211
+ residual: boolean;
212
+ };
213
+
214
+ export default function toGraphviz(p: Profile): string {
215
+ if (p.metadata.format === 'double') {
216
+ return 'diff flamegraphs are not supported';
217
+ }
218
+
219
+ const tree = flamebearersToTree(p.flamebearer);
220
+
221
+ const nodes: string[] = [];
222
+ const edges: string[] = [];
223
+
224
+ function calcMaxAndSumValues(
225
+ n: TreeNode,
226
+ maxSelf: number,
227
+ maxTotal: number,
228
+ sumSelf: number,
229
+ sumTotal: number
230
+ ): [number, number, number, number] {
231
+ n.children.forEach((child) => {
232
+ const [newMaxSelf, newMaxTotal] = calcMaxAndSumValues(
233
+ child,
234
+ maxSelf,
235
+ maxTotal,
236
+ sumSelf,
237
+ sumTotal
238
+ );
239
+ maxSelf = Math.max(maxSelf, newMaxSelf);
240
+ maxTotal = Math.max(maxTotal, newMaxTotal);
241
+ });
242
+
243
+ maxSelf = Math.max(maxSelf, n.self[0]);
244
+ maxTotal = Math.max(maxTotal, n.total[0]);
245
+ sumSelf += n.self[0];
246
+ sumTotal += n.total[0];
247
+
248
+ return [maxSelf, maxTotal, sumSelf, sumTotal];
249
+ }
250
+
251
+ const [maxSelf, maxTotal, , sumTotal] = calcMaxAndSumValues(tree, 0, 0, 0, 0);
252
+ const { sampleRate, units } = p.metadata;
253
+ const formatter = getFormatter(maxTotal, sampleRate, units);
254
+
255
+ const formatFunc = (dur: number): string => {
256
+ return formatter.format(dur, sampleRate, true);
257
+ };
258
+
259
+ // we first turn tree into a graph
260
+ let graphNodes: { [key: string]: GraphNode } = {};
261
+ const graphEdges: { [key: string]: GraphEdge } = {};
262
+ let nodesTotal = 0;
263
+ function treeToGraph(n: TreeNode, seenNodes: string[]): GraphNode {
264
+ if (seenNodes.indexOf(n.name) === -1) {
265
+ if (!graphNodes[n.name]) {
266
+ nodesTotal += 1;
267
+ graphNodes[n.name] = {
268
+ index: nodesTotal,
269
+ name: n.name,
270
+ self: n.self[0],
271
+ total: n.total[0],
272
+ parents: [],
273
+ children: [],
274
+ };
275
+ } else {
276
+ graphNodes[n.name].self += n.self[0];
277
+ graphNodes[n.name].total += n.total[0];
278
+ }
279
+ }
280
+
281
+ n.children.forEach((child) => {
282
+ const childNode = treeToGraph(child, seenNodes.concat([n.name]));
283
+ const childKey = `${n.name}/${child.name}`;
284
+ if (child.name !== n.name) {
285
+ if (!graphEdges[childKey]) {
286
+ graphEdges[childKey] = {
287
+ from: graphNodes[n.name],
288
+ to: childNode,
289
+ weight: child.total[0],
290
+ residual: false,
291
+ };
292
+ } else {
293
+ graphEdges[childKey].weight += child.total[0];
294
+ }
295
+ childNode.parents.push(graphEdges[childKey]);
296
+ graphNodes[n.name].children.push(graphEdges[childKey]);
297
+ }
298
+ });
299
+ return graphNodes[n.name];
300
+ }
301
+
302
+ // skip "total" node
303
+ tree.children.forEach((child) => {
304
+ treeToGraph(child, []);
305
+ });
306
+
307
+ // next is we need to trim graph to remove small nodes
308
+ const nodeCutoff = sumTotal * nodeFraction;
309
+ const edgeCutoff = sumTotal * edgeFraction;
310
+
311
+ Object.keys(graphNodes).forEach((key) => {
312
+ if (graphNodes[key].total < nodeCutoff) {
313
+ delete graphNodes[key];
314
+ }
315
+ });
316
+
317
+ // next is we limit total number of nodes
318
+
319
+ function entropyScore(n: GraphNode): number {
320
+ let score = 0;
321
+
322
+ if (n.parents.length === 0) {
323
+ score += 1;
324
+ } else {
325
+ score += edgeEntropyScore(n.parents, 0);
326
+ }
327
+
328
+ if (n.children.length === 0) {
329
+ score += 1;
330
+ } else {
331
+ score += edgeEntropyScore(n.children, n.self);
332
+ }
333
+
334
+ return score * n.total + n.self;
335
+ }
336
+ function edgeEntropyScore(edges: GraphEdge[], self: number) {
337
+ let score = 0;
338
+ let total = self;
339
+ edges.forEach((e) => {
340
+ if (e.weight > 0) {
341
+ total += Math.abs(e.weight);
342
+ }
343
+ });
344
+
345
+ if (total !== 0) {
346
+ edges.forEach((e) => {
347
+ const frac = Math.abs(e.weight) / total;
348
+ score += -frac * Math.log2(frac);
349
+ });
350
+ if (self > 0) {
351
+ const frac = Math.abs(self) / total;
352
+ score += -frac * Math.log2(frac);
353
+ }
354
+ }
355
+ return score;
356
+ }
357
+
358
+ const cachedScores: { [key: string]: number } = {};
359
+ Object.keys(graphNodes).forEach((key) => {
360
+ cachedScores[graphNodes[key].name] = entropyScore(graphNodes[key]);
361
+ });
362
+
363
+ const sortedNodes = Object.values(graphNodes).sort((a, b) => {
364
+ const sa: number = cachedScores[a.name];
365
+ const sb: number = cachedScores[b.name];
366
+ if (sa !== sb) {
367
+ return sb - sa;
368
+ }
369
+ if (a.name !== b.name) {
370
+ return a.name < b.name ? -1 : 1;
371
+ }
372
+ if (a.self !== b.self) {
373
+ return sb - sa;
374
+ }
375
+
376
+ return a.name < b.name ? -1 : 1;
377
+ });
378
+
379
+ const keptNodes: { [key: string]: GraphNode } = {};
380
+ sortedNodes.forEach((n) => {
381
+ keptNodes[n.name] = n;
382
+ });
383
+
384
+ sortedNodes.slice(maxNodes).forEach((n) => {
385
+ delete keptNodes[n.name];
386
+ });
387
+
388
+ // now that we removed nodes we need to create residual edges
389
+ function trimTree(n: TreeNode, lastPresentParent: TreeNode | null) {
390
+ const isNodeDeleted = !keptNodes[n.name];
391
+ n.children.forEach((child) => {
392
+ const isChildNodeDeleted = !keptNodes[child.name];
393
+ trimTree(child, isNodeDeleted ? lastPresentParent : n);
394
+ if (!isChildNodeDeleted && lastPresentParent && isNodeDeleted) {
395
+ const edgeKey = `${lastPresentParent.name}/${child.name}`;
396
+ graphEdges[edgeKey] ||= {
397
+ from: graphNodes[lastPresentParent.name],
398
+ to: graphNodes[child.name],
399
+ weight: 0,
400
+ residual: true,
401
+ };
402
+
403
+ graphEdges[edgeKey].weight += child.total[0];
404
+ graphEdges[edgeKey].residual = true;
405
+ }
406
+ });
407
+ }
408
+
409
+ trimTree(tree, null);
410
+
411
+ graphNodes = keptNodes;
412
+
413
+ function isRedundantEdge(e: GraphEdge) {
414
+ const [src, n] = [e.from, e.to];
415
+ const seen: { [key: string]: boolean } = {};
416
+ const queue = [n];
417
+
418
+ while (queue.length > 0) {
419
+ const n = queue.shift() as GraphNode;
420
+
421
+ for (let i = 0; i < n.parents.length; i += 1) {
422
+ const ie = n.parents[i];
423
+ if (!(e === ie || seen[ie.from.name])) {
424
+ if (ie.from === src) {
425
+ return true;
426
+ }
427
+ seen[ie.from.name] = true;
428
+ queue.push(ie.from);
429
+ }
430
+ }
431
+ }
432
+ return false;
433
+ }
434
+
435
+ // remove redundant edges
436
+ sortedNodes.reverse().forEach((node) => {
437
+ const sortedParentEdges = node.parents.sort((a, b) => b.weight - a.weight);
438
+ const edgesToDelete: GraphEdge[] = [];
439
+ for (let i = 0; i < sortedParentEdges.length; i += 1) {
440
+ const parentEdge = sortedParentEdges[i];
441
+ if (!parentEdge.residual) {
442
+ break;
443
+ }
444
+
445
+ if (isRedundantEdge(parentEdge)) {
446
+ edgesToDelete.push(parentEdge);
447
+ delete graphEdges[`${parentEdge.from.name}/${parentEdge.to.name}`];
448
+ }
449
+ }
450
+ edgesToDelete.forEach((edge) => {
451
+ edge.from.children = edge.from.children.filter((e) => e.to !== edge.to);
452
+ edge.to.parents = edge.to.parents.filter((e) => e.from !== edge.from);
453
+ });
454
+ });
455
+
456
+ // now we clean up edges
457
+ Object.keys(graphEdges).forEach((key) => {
458
+ const e = graphEdges[key];
459
+ // first delete the ones that no longer have nodes
460
+ if (!graphNodes[e.from.name]) {
461
+ delete graphEdges[key];
462
+ }
463
+ if (!graphNodes[e.to.name]) {
464
+ delete graphEdges[key];
465
+ }
466
+ // second delete the ones that are too small
467
+ if (e.weight < edgeCutoff) {
468
+ delete graphEdges[key];
469
+ }
470
+ });
471
+
472
+ Object.keys(graphNodes).forEach((key) => {
473
+ nodes.push(renderNode(formatFunc, graphNodes[key], maxSelf, maxTotal));
474
+ });
475
+
476
+ Object.keys(graphEdges).forEach((key) => {
477
+ edges.push(renderEdge(formatFunc, graphEdges[key], maxTotal));
478
+ });
479
+
480
+ return `digraph "unnamed" {
481
+ fontname= "${fontName}"
482
+ ${nodes.join('\n')}
483
+ ${edges.join('\n')}
484
+ }`;
485
+ }
@@ -0,0 +1,93 @@
1
+ import { fitToCanvasRect } from './fitMode';
2
+
3
+ describe('fitToCanvasRect', () => {
4
+ describe('HEAD', () => {
5
+ it('always returns fullText', () => {
6
+ const mode = 'HEAD';
7
+ const charSize = 1;
8
+ const rectWidth = 10;
9
+ const fullText = 'full_long_text';
10
+ const shortText = 'short_text';
11
+
12
+ expect(
13
+ fitToCanvasRect({
14
+ mode,
15
+ charSize,
16
+ rectWidth,
17
+ fullText,
18
+ shortText,
19
+ })
20
+ ).toMatchObject({
21
+ mode,
22
+ text: fullText,
23
+ marginLeft: 3,
24
+ });
25
+ });
26
+ });
27
+
28
+ describe('TAIL', () => {
29
+ const mode = 'TAIL';
30
+ it('returns full text if it CAN fit', () => {
31
+ const charSize = 1;
32
+ const rectWidth = 99;
33
+ const fullText = 'full_long_text';
34
+ const shortText = 'short_text';
35
+
36
+ expect(
37
+ fitToCanvasRect({
38
+ mode,
39
+ charSize,
40
+ rectWidth,
41
+ fullText,
42
+ shortText,
43
+ })
44
+ ).toMatchObject({
45
+ mode,
46
+ text: fullText,
47
+ marginLeft: 3,
48
+ });
49
+ });
50
+
51
+ it('returns short text with if it CAN fit short text', () => {
52
+ const charSize = 1;
53
+ const rectWidth = 10;
54
+ const fullText = 'full_long_text';
55
+ const shortText = 'short_text';
56
+
57
+ expect(
58
+ fitToCanvasRect({
59
+ mode,
60
+ charSize,
61
+ rectWidth,
62
+ fullText,
63
+ shortText,
64
+ })
65
+ ).toMatchObject({
66
+ mode,
67
+ text: shortText,
68
+ marginLeft: 3,
69
+ });
70
+ });
71
+
72
+ it('returns short text with negative margin BOTH short and long CAN NOT fit', () => {
73
+ const charSize = 1;
74
+ const rectWidth = 10;
75
+ const fullText = 'full_long_text'; // 14
76
+ const shortText = 'short_text_'; // 11
77
+
78
+ expect(
79
+ fitToCanvasRect({
80
+ mode,
81
+ charSize,
82
+ rectWidth,
83
+ fullText,
84
+ shortText,
85
+ })
86
+ ).toMatchObject({
87
+ mode,
88
+ text: shortText,
89
+ marginLeft: -4,
90
+ });
91
+ });
92
+ });
93
+ });