@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,167 @@
1
+ /* eslint-disable camelcase */
2
+ import Color from 'color';
3
+ import { scaleLinear } from 'd3-scale';
4
+ import type { SpyName } from '../../models';
5
+ import murmurhash3_32_gc from './murmur3';
6
+ import type { FlamegraphPalette } from './colorPalette';
7
+
8
+ export const defaultColor = Color.rgb(148, 142, 142);
9
+ export const diffColorRed = Color.rgb(200, 0, 0);
10
+ export const diffColorGreen = Color.rgb(0, 170, 0);
11
+
12
+ export const highlightColor = Color('#48CE73');
13
+
14
+ export function colorBasedOnDiffPercent(
15
+ palette: FlamegraphPalette,
16
+ leftPercent: number,
17
+ rightPercent: number
18
+ ) {
19
+ const result = diffPercent(leftPercent, rightPercent);
20
+ const color = NewDiffColor(palette);
21
+ return color(result);
22
+ }
23
+
24
+ // TODO move to a different file
25
+ // difference between 2 percents
26
+ export function diffPercent(leftPercent: number, rightPercent: number) {
27
+ if (leftPercent === rightPercent) {
28
+ return 0;
29
+ }
30
+
31
+ if (leftPercent === 0) {
32
+ return 100;
33
+ }
34
+
35
+ // https://en.wikipedia.org/wiki/Relative_change_and_difference
36
+ const result = ((rightPercent - leftPercent) / leftPercent) * 100;
37
+
38
+ if (result > 100) {
39
+ return 100;
40
+ }
41
+ if (result < -100) {
42
+ return -100;
43
+ }
44
+
45
+ return result;
46
+ }
47
+
48
+ export function colorFromPercentage(p: number, alpha: number) {
49
+ // calculated by drawing a line (https://en.wikipedia.org/wiki/Line_drawing_algorithm)
50
+ // where p1 = (0, 180) and p2 = (100, 0)
51
+ // where x is the absolute percentage
52
+ // and y is the color variation
53
+ let v = 180 - 1.8 * Math.abs(p);
54
+
55
+ if (v > 200) {
56
+ v = 200;
57
+ }
58
+
59
+ // red
60
+ if (p > 0) {
61
+ return Color.rgb(200, v, v).alpha(alpha);
62
+ }
63
+ // green
64
+ if (p < 0) {
65
+ return Color.rgb(v, 200, v).alpha(alpha);
66
+ }
67
+ // grey
68
+ return Color.rgb(200, 200, 200).alpha(alpha);
69
+ }
70
+
71
+ export function colorGreyscale(v: number, a: number) {
72
+ return Color.rgb(v, v, v).alpha(a);
73
+ }
74
+
75
+ function spyToRegex(spyName: SpyName): RegExp {
76
+ // eslint-disable-next-line default-case
77
+ switch (spyName) {
78
+ case 'dotnetspy':
79
+ return /^(?<packageName>.+)\.(.+)\.(.+)\(.*\)$/;
80
+ // TODO: come up with a clever heuristic
81
+ case 'ebpfspy':
82
+ return /^(?<packageName>.+)$/;
83
+ // tested with pyroscope stacktraces here: https://regex101.com/r/99KReq/1
84
+ case 'gospy':
85
+ return /^(?<packageName>.*?\/.*?\.|.*?\.|.+)(?<functionName>.*)$/;
86
+ // assume scrape is golang, since that's the only language we support right now
87
+ case 'scrape':
88
+ return /^(?<packageName>.*?\/.*?\.|.*?\.|.+)(?<functionName>.*)$/;
89
+ case 'phpspy':
90
+ return /^(?<packageName>(.*\/)*)(?<filename>.*\.php+)(?<line_info>.*)$/;
91
+ case 'pyspy':
92
+ return /^(?<packageName>(.*\/)*)(?<filename>.*\.py+)(?<line_info>.*)$/;
93
+ case 'rbspy':
94
+ return /^(?<packageName>(.*\/)*)(?<filename>.*\.rb+)(?<line_info>.*)$/;
95
+ case 'nodespy':
96
+ return /^(\.\/node_modules\/)?(?<packageName>[^/]*)(?<filename>.*\.?(jsx?|tsx?)?):(?<functionName>.*):(?<line_info>.*)$/;
97
+ case 'tracing':
98
+ return /^(?<packageName>.+?):.*$/;
99
+ case 'javaspy':
100
+ // TODO: we might want to add ? after groups
101
+ return /^(?<packageName>.+\/)(?<filename>.+\.)(?<functionName>.+)$/;
102
+ case 'pyroscope-rs':
103
+ return /^(?<packageName>[^::]+)/;
104
+ case 'unknown':
105
+ return /^(?<packageName>.+)$/;
106
+ }
107
+
108
+ return /^(?<packageName>.+)$/;
109
+ }
110
+
111
+ // TODO spy names?
112
+ export function getPackageNameFromStackTrace(
113
+ spyName: SpyName,
114
+ stackTrace: string
115
+ ) {
116
+ if (stackTrace.length === 0) {
117
+ return stackTrace;
118
+ }
119
+ const regexp = spyToRegex(spyName);
120
+ const fullStackGroups = stackTrace.match(regexp);
121
+ if (fullStackGroups && fullStackGroups.groups) {
122
+ return fullStackGroups.groups['packageName'] || '';
123
+ }
124
+ return stackTrace;
125
+ }
126
+
127
+ export function colorBasedOnPackageName(
128
+ palette: FlamegraphPalette,
129
+ name: string
130
+ ) {
131
+ const hash = murmurhash3_32_gc(name, 0);
132
+ const colorIndex = hash % palette.colors.length;
133
+ const baseClr = palette.colors[colorIndex];
134
+ if (!baseClr) {
135
+ console.warn('Could not calculate color. Defaulting to the first one');
136
+ // We assert to Color since the first position is always available
137
+ return palette.colors[0];
138
+ }
139
+
140
+ return baseClr;
141
+ }
142
+
143
+ /**
144
+ * NewDiffColor constructs a function that given a number from -100 to 100
145
+ * it returns the color for that number in a linear scale
146
+ * encoded in rgb
147
+ */
148
+ export function NewDiffColor(
149
+ props: Omit<FlamegraphPalette, 'colors'>
150
+ ): (n: number) => InstanceType<typeof Color> {
151
+ const { goodColor, neutralColor, badColor } = props;
152
+
153
+ const color = scaleLinear()
154
+ .domain([-100, 0, 100])
155
+ // TODO types from DefinitelyTyped seem to mismatch
156
+ .range([
157
+ goodColor.rgb().toString(),
158
+ neutralColor.rgb().toString(),
159
+ badColor.rgb().toString(),
160
+ ] as ShamefulAny);
161
+
162
+ return (n: number) => {
163
+ // convert to our Color object
164
+ // since that's what users are expecting to use
165
+ return Color(color(n).toString());
166
+ };
167
+ }
@@ -0,0 +1,58 @@
1
+ import Color from 'color';
2
+
3
+ // FlamegraphPalette represents
4
+ export interface FlamegraphPalette {
5
+ name: string;
6
+ goodColor: InstanceType<typeof Color>;
7
+ neutralColor: InstanceType<typeof Color>;
8
+ badColor: InstanceType<typeof Color>;
9
+
10
+ colors: InstanceType<typeof Color>[];
11
+ }
12
+
13
+ export const DefaultPalette: FlamegraphPalette = {
14
+ name: 'Default',
15
+ // green
16
+ goodColor: Color.rgb(0, 170, 0),
17
+ // grey
18
+ neutralColor: Color.rgb(148, 142, 142),
19
+ // red
20
+ badColor: Color.rgb(200, 0, 0),
21
+
22
+ colors: [
23
+ Color.hsl(24, 69, 60),
24
+ Color.hsl(34, 65, 65),
25
+ Color.hsl(194, 52, 61),
26
+ Color.hsl(163, 45, 55),
27
+ Color.hsl(211, 48, 60),
28
+ Color.hsl(246, 40, 65),
29
+ Color.hsl(305, 63, 79),
30
+ Color.hsl(47, 100, 73),
31
+
32
+ Color.rgb(183, 219, 171),
33
+ Color.rgb(244, 213, 152),
34
+ Color.rgb(78, 146, 249),
35
+ Color.rgb(249, 186, 143),
36
+ Color.rgb(242, 145, 145),
37
+ Color.rgb(130, 181, 216),
38
+ Color.rgb(229, 168, 226),
39
+ Color.rgb(174, 162, 224),
40
+ Color.rgb(154, 196, 138),
41
+ Color.rgb(242, 201, 109),
42
+ Color.rgb(101, 197, 219),
43
+ Color.rgb(249, 147, 78),
44
+ Color.rgb(234, 100, 96),
45
+ Color.rgb(81, 149, 206),
46
+ Color.rgb(214, 131, 206),
47
+ Color.rgb(128, 110, 183),
48
+ ],
49
+ };
50
+
51
+ export const ColorBlindPalette: FlamegraphPalette = {
52
+ ...DefaultPalette,
53
+
54
+ name: 'Color Blind',
55
+ goodColor: Color.rgb(26, 133, 255),
56
+ neutralColor: Color.rgb(148, 142, 142),
57
+ badColor: Color.rgb(220, 50, 32),
58
+ };
@@ -0,0 +1,5 @@
1
+ export const PX_PER_LEVEL = 22;
2
+ export const COLLAPSE_THRESHOLD = 5;
3
+ export const LABEL_THRESHOLD = 20;
4
+ export const GAP = 0.5;
5
+ export const BAR_HEIGHT = PX_PER_LEVEL - GAP;
@@ -0,0 +1,291 @@
1
+ import React from 'react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { render, screen, waitFor } from '@testing-library/react';
4
+ import { Maybe } from 'true-myth';
5
+ import FlamegraphComponent from './index';
6
+ import TestData from './testData';
7
+ import { BAR_HEIGHT } from './constants';
8
+ import { DefaultPalette, FlamegraphPalette } from './colorPalette';
9
+
10
+ // the leaves have already been tested
11
+ // this is just to guarantee code is compiling
12
+ // and the callbacks are being called correctly
13
+ describe('FlamegraphComponent', () => {
14
+ const setPalette = (p: FlamegraphPalette) => {};
15
+ it('renders', () => {
16
+ const onZoom = jest.fn();
17
+ const onReset = jest.fn();
18
+ const isDirty = jest.fn();
19
+ const onFocusOnNode = jest.fn();
20
+
21
+ render(
22
+ <FlamegraphComponent
23
+ updateFitMode={() => ({})}
24
+ selectedItem={Maybe.nothing()}
25
+ setActiveItem={() => ({})}
26
+ showCredit
27
+ fitMode="HEAD"
28
+ zoom={Maybe.nothing()}
29
+ focusedNode={Maybe.nothing()}
30
+ highlightQuery=""
31
+ onZoom={onZoom}
32
+ onFocusOnNode={onFocusOnNode}
33
+ onReset={onReset}
34
+ isDirty={isDirty}
35
+ flamebearer={TestData.SimpleTree}
36
+ palette={DefaultPalette}
37
+ setPalette={setPalette}
38
+ />
39
+ );
40
+ });
41
+
42
+ it('resizes correctly', () => {
43
+ const onZoom = jest.fn();
44
+ const onReset = jest.fn();
45
+ const isDirty = jest.fn();
46
+ const onFocusOnNode = jest.fn();
47
+
48
+ render(
49
+ <FlamegraphComponent
50
+ updateFitMode={() => ({})}
51
+ selectedItem={Maybe.nothing()}
52
+ setActiveItem={() => ({})}
53
+ showCredit
54
+ fitMode="HEAD"
55
+ zoom={Maybe.nothing()}
56
+ focusedNode={Maybe.nothing()}
57
+ highlightQuery=""
58
+ onZoom={onZoom}
59
+ onFocusOnNode={onFocusOnNode}
60
+ onReset={onReset}
61
+ isDirty={isDirty}
62
+ flamebearer={TestData.SimpleTree}
63
+ palette={DefaultPalette}
64
+ setPalette={setPalette}
65
+ />
66
+ );
67
+
68
+ Object.defineProperty(window, 'innerWidth', {
69
+ writable: true,
70
+ configurable: true,
71
+ value: 800,
72
+ });
73
+
74
+ window.dispatchEvent(new Event('resize'));
75
+
76
+ // there's nothing much to assert here
77
+ expect(window.innerWidth).toBe(800);
78
+ });
79
+
80
+ it('zooms on click', () => {
81
+ const onZoom = jest.fn();
82
+ const onReset = jest.fn();
83
+ const isDirty = jest.fn();
84
+ const onFocusOnNode = jest.fn();
85
+
86
+ render(
87
+ <FlamegraphComponent
88
+ updateFitMode={() => ({})}
89
+ selectedItem={Maybe.nothing()}
90
+ setActiveItem={() => ({})}
91
+ showCredit
92
+ fitMode="HEAD"
93
+ zoom={Maybe.nothing()}
94
+ focusedNode={Maybe.nothing()}
95
+ highlightQuery=""
96
+ onZoom={onZoom}
97
+ onFocusOnNode={onFocusOnNode}
98
+ onReset={onReset}
99
+ isDirty={isDirty}
100
+ flamebearer={TestData.SimpleTree}
101
+ palette={DefaultPalette}
102
+ setPalette={setPalette}
103
+ />
104
+ );
105
+
106
+ userEvent.click(screen.getByTestId('flamegraph-canvas'), {
107
+ clientX: 0,
108
+ clientY: BAR_HEIGHT * 3,
109
+ });
110
+
111
+ expect(onZoom).toHaveBeenCalled();
112
+ });
113
+
114
+ describe('context menu', () => {
115
+ it(`enables "reset view" menuitem when it's dirty`, async () => {
116
+ const onZoom = jest.fn();
117
+ const onReset = jest.fn();
118
+ const isDirty = jest.fn();
119
+ const onFocusOnNode = jest.fn();
120
+
121
+ const { rerender } = render(
122
+ <FlamegraphComponent
123
+ updateFitMode={() => ({})}
124
+ selectedItem={Maybe.nothing()}
125
+ setActiveItem={() => ({})}
126
+ showCredit
127
+ fitMode="HEAD"
128
+ zoom={Maybe.nothing()}
129
+ focusedNode={Maybe.nothing()}
130
+ highlightQuery=""
131
+ onZoom={onZoom}
132
+ onFocusOnNode={onFocusOnNode}
133
+ onReset={onReset}
134
+ isDirty={isDirty}
135
+ flamebearer={TestData.SimpleTree}
136
+ palette={DefaultPalette}
137
+ setPalette={setPalette}
138
+ />
139
+ );
140
+
141
+ userEvent.click(screen.getByTestId('flamegraph-canvas'), {
142
+ button: 2,
143
+ clientX: 1,
144
+ clientY: 1,
145
+ });
146
+
147
+ // should not be available unless we zoom
148
+ await waitFor(() =>
149
+ expect(
150
+ screen.queryByRole('menuitem', { name: /Reset View/ })
151
+ ).toHaveAttribute('aria-disabled', 'true')
152
+ );
153
+
154
+ // it's dirty now
155
+ isDirty.mockReturnValue(true);
156
+
157
+ rerender(
158
+ <FlamegraphComponent
159
+ updateFitMode={() => ({})}
160
+ selectedItem={Maybe.nothing()}
161
+ setActiveItem={() => ({})}
162
+ showCredit
163
+ fitMode="HEAD"
164
+ zoom={Maybe.nothing()}
165
+ focusedNode={Maybe.nothing()}
166
+ highlightQuery=""
167
+ onZoom={onZoom}
168
+ onFocusOnNode={onFocusOnNode}
169
+ onReset={onReset}
170
+ isDirty={isDirty}
171
+ flamebearer={TestData.SimpleTree}
172
+ palette={DefaultPalette}
173
+ setPalette={setPalette}
174
+ />
175
+ );
176
+
177
+ userEvent.click(screen.getByTestId('flamegraph-canvas'), {
178
+ button: 2,
179
+ });
180
+
181
+ // should be enabled now
182
+ expect(
183
+ screen.queryByRole('menuitem', { name: /Reset View/ })
184
+ ).not.toHaveAttribute('aria-disabled', 'true');
185
+ });
186
+
187
+ it('triggers a highlight', () => {
188
+ const onZoom = jest.fn();
189
+ const onReset = jest.fn();
190
+ const isDirty = jest.fn();
191
+ const onFocusOnNode = jest.fn();
192
+
193
+ render(
194
+ <FlamegraphComponent
195
+ updateFitMode={() => ({})}
196
+ selectedItem={Maybe.nothing()}
197
+ setActiveItem={() => ({})}
198
+ showCredit
199
+ fitMode="HEAD"
200
+ zoom={Maybe.nothing()}
201
+ focusedNode={Maybe.nothing()}
202
+ highlightQuery=""
203
+ onZoom={onZoom}
204
+ onFocusOnNode={onFocusOnNode}
205
+ onReset={onReset}
206
+ isDirty={isDirty}
207
+ flamebearer={TestData.SimpleTree}
208
+ palette={DefaultPalette}
209
+ setPalette={setPalette}
210
+ />
211
+ );
212
+
213
+ // initially the context highlight is not visible
214
+ expect(
215
+ screen.getByTestId('flamegraph-highlight-contextmenu')
216
+ ).not.toBeVisible();
217
+
218
+ // then we click
219
+ userEvent.click(screen.getByTestId('flamegraph-canvas'), { button: 2 });
220
+
221
+ // should be visible now
222
+ expect(
223
+ screen.getByTestId('flamegraph-highlight-contextmenu')
224
+ ).toBeVisible();
225
+ });
226
+ });
227
+
228
+ describe('header', () => {
229
+ const onZoom = jest.fn();
230
+ const onReset = jest.fn();
231
+ const isDirty = jest.fn();
232
+ const onFocusOnNode = jest.fn();
233
+
234
+ it('renders when type is single', () => {
235
+ render(
236
+ <FlamegraphComponent
237
+ updateFitMode={() => ({})}
238
+ selectedItem={Maybe.nothing()}
239
+ setActiveItem={() => ({})}
240
+ showCredit
241
+ fitMode="HEAD"
242
+ zoom={Maybe.nothing()}
243
+ focusedNode={Maybe.nothing()}
244
+ highlightQuery=""
245
+ onZoom={onZoom}
246
+ onFocusOnNode={onFocusOnNode}
247
+ onReset={onReset}
248
+ isDirty={isDirty}
249
+ flamebearer={TestData.SimpleTree}
250
+ palette={DefaultPalette}
251
+ setPalette={setPalette}
252
+ toolbarVisible
253
+ />
254
+ );
255
+
256
+ expect(screen.queryByRole('heading', { level: 2 })).toHaveTextContent(
257
+ 'Frame width represents CPU time per function'
258
+ );
259
+ });
260
+
261
+ it('renders when type is "double"', () => {
262
+ const flamebearer = TestData.DiffTree;
263
+ render(
264
+ <FlamegraphComponent
265
+ updateFitMode={() => ({})}
266
+ selectedItem={Maybe.nothing()}
267
+ setActiveItem={() => ({})}
268
+ showCredit
269
+ fitMode="HEAD"
270
+ zoom={Maybe.nothing()}
271
+ focusedNode={Maybe.nothing()}
272
+ highlightQuery=""
273
+ onZoom={onZoom}
274
+ onFocusOnNode={onFocusOnNode}
275
+ onReset={onReset}
276
+ isDirty={isDirty}
277
+ flamebearer={flamebearer}
278
+ palette={DefaultPalette}
279
+ setPalette={setPalette}
280
+ toolbarVisible
281
+ />
282
+ );
283
+
284
+ expect(screen.queryByRole('heading', { level: 2 })).toHaveTextContent(
285
+ '(-) RemovedAdded (+)'
286
+ );
287
+
288
+ expect(screen.getByTestId('flamegraph-legend')).toBeInTheDocument();
289
+ });
290
+ });
291
+ });