@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,527 @@
1
+ // src/ProfilerTable.tsx
2
+ import React, { useRef, RefObject, CSSProperties } from 'react';
3
+ import type Color from 'color';
4
+ import cl from 'classnames';
5
+ import type { Maybe } from 'true-myth';
6
+ import { doubleFF, singleFF, Flamebearer } from './models';
7
+ // until ui is moved to its own package this should do it
8
+ // eslint-disable-next-line import/no-extraneous-dependencies
9
+ import TableUI, {
10
+ useTableSort,
11
+ BodyRow,
12
+ TableBodyType,
13
+ } from './shims/Table';
14
+ import TableTooltip from './Tooltip/TableTooltip';
15
+ import { getFormatter, ratioToPercent, diffPercent } from './format/format';
16
+ import {
17
+ colorBasedOnPackageName,
18
+ defaultColor,
19
+ getPackageNameFromStackTrace,
20
+ } from './FlameGraph/FlameGraphComponent/color';
21
+ import { fitIntoTableCell, FitModes } from './fitMode/fitMode';
22
+ import { isMatch } from './search';
23
+ import type { FlamegraphPalette } from './FlameGraph/FlameGraphComponent/colorPalette';
24
+ import {
25
+ useFlamegraphI18n,
26
+ type FlamegraphMessages,
27
+ } from './i18n';
28
+
29
+ const zero = (v?: number) => v || 0;
30
+
31
+ interface SingleCell {
32
+ type: 'single';
33
+ self: number;
34
+ total: number;
35
+ }
36
+
37
+ interface DoubleCell {
38
+ type: 'double';
39
+ self: number;
40
+ total: number;
41
+ selfLeft: number;
42
+ selfRght: number;
43
+ selfDiff: number;
44
+ totalLeft: number;
45
+ totalRght: number;
46
+ totalDiff: number;
47
+ leftTicks: number;
48
+ rightTicks: number;
49
+ }
50
+
51
+ function generateCellSingle(
52
+ ff: typeof singleFF,
53
+ cell: SingleCell,
54
+ level: number[],
55
+ j: number
56
+ ) {
57
+ const c = cell;
58
+
59
+ c.type = 'single';
60
+ c.self = zero(c.self) + ff.getBarSelf(level, j);
61
+ c.total = zero(c.total) + ff.getBarTotal(level, j);
62
+ return c;
63
+ }
64
+
65
+ function generateCellDouble(
66
+ ff: typeof doubleFF,
67
+ cell: DoubleCell,
68
+ level: number[],
69
+ j: number,
70
+ leftTicks: number,
71
+ rightTicks: number
72
+ ) {
73
+ const c = cell;
74
+
75
+ c.type = 'double';
76
+ c.self = zero(c.self) + ff.getBarSelf(level, j);
77
+ c.total = zero(c.total) + ff.getBarTotal(level, j);
78
+ c.selfLeft = zero(c.selfLeft) + ff.getBarSelfLeft(level, j);
79
+ c.selfRght = zero(c.selfRght) + ff.getBarSelfRght(level, j);
80
+ c.selfDiff = zero(c.selfDiff) + ff.getBarSelfDiff(level, j);
81
+ c.totalLeft = zero(c.totalLeft) + ff.getBarTotalLeft(level, j);
82
+ c.totalRght = zero(c.totalRght) + ff.getBarTotalRght(level, j);
83
+ c.totalDiff = zero(c.totalDiff) + ff.getBarTotalDiff(level, j);
84
+ c.leftTicks = leftTicks;
85
+ c.rightTicks = rightTicks;
86
+ return c;
87
+ }
88
+
89
+ // generates a table from data in flamebearer format
90
+ function generateTable(
91
+ flamebearer: Flamebearer
92
+ ): ((SingleCell | DoubleCell) & { name: string })[] {
93
+ const table: ((SingleCell | DoubleCell) & { name: string })[] = [];
94
+ if (!flamebearer) {
95
+ return table;
96
+ }
97
+ const { names, levels, format } = flamebearer;
98
+ const ff = format !== 'double' ? singleFF : doubleFF;
99
+
100
+ const hash = new Map<string, (DoubleCell | SingleCell) & { name: string }>();
101
+ // eslint-disable-next-line no-plusplus
102
+ for (let i = 0; i < levels.length; i++) {
103
+ const level = levels[i];
104
+ for (let j = 0; j < level.length; j += ff.jStep) {
105
+ const key = ff.getBarName(level, j);
106
+ const name = names[key];
107
+
108
+ if (!hash.has(name)) {
109
+ hash.set(name, {
110
+ name: name || '<empty>',
111
+ self: 0,
112
+ total: 0,
113
+ } as SingleCell & { name: string });
114
+ }
115
+
116
+ const cell = hash.get(name);
117
+ // Should not happen
118
+ if (!cell) {
119
+ break;
120
+ }
121
+
122
+ // TODO(eh-am): not the most optimal performance wise
123
+ // but better for type checking
124
+ if (format === 'single') {
125
+ generateCellSingle(singleFF, cell as SingleCell, level, j);
126
+ } else {
127
+ generateCellDouble(
128
+ doubleFF,
129
+ cell as DoubleCell,
130
+ level,
131
+ j,
132
+ flamebearer.leftTicks,
133
+ flamebearer.rightTicks
134
+ );
135
+ }
136
+ }
137
+ }
138
+
139
+ return Array.from(hash.values());
140
+ }
141
+
142
+ // the value must be negative or zero
143
+ function neg(v: number) {
144
+ return Math.min(0, v);
145
+ }
146
+
147
+ function backgroundImageStyle(a: number, b: number, color: any) {
148
+ const w = 148;
149
+ const k = w - (a / b) * w;
150
+ const clr = color.alpha(1.0);
151
+ return {
152
+ backgroundImage: `linear-gradient(${clr}, ${clr})`,
153
+ backgroundPosition: `-${k}px 0px`,
154
+ backgroundRepeat: 'no-repeat',
155
+ };
156
+ }
157
+
158
+ // side: _ | L | R : indicates how to render the diff color
159
+ // - _: render both diff color
160
+ // - L: only render diff color on the left, if the left is longer than the right (better, green)
161
+ // - R: only render diff color on the right, if the right is longer than the left (worse, red)
162
+ export function backgroundImageDiffStyle(
163
+ palette: FlamegraphPalette,
164
+ a: number,
165
+ b: number,
166
+ total: number,
167
+ color: InstanceType<typeof Color>,
168
+ side?: 'L' | 'R'
169
+ ): React.CSSProperties {
170
+ const w = 148;
171
+ const k = w - (Math.min(a, b) / total) * w;
172
+ const kd = w - (Math.max(a, b) / total) * w;
173
+ const clr = color.alpha(1.0);
174
+ const cld =
175
+ b < a ? palette.goodColor.alpha(0.8) : palette.badColor.alpha(0.8);
176
+
177
+ if (side === 'L' && a < b) {
178
+ return {
179
+ backgroundImage: `linear-gradient(${clr}, ${clr})`,
180
+ backgroundPosition: `${neg(-k)}px 0px`,
181
+ backgroundRepeat: 'no-repeat',
182
+ };
183
+ }
184
+ if (side === 'R' && b < a) {
185
+ return {
186
+ backgroundImage: `linear-gradient(${clr}, ${clr})`,
187
+ backgroundPosition: `${neg(-k)}px 0px`,
188
+ backgroundRepeat: 'no-repeat',
189
+ };
190
+ }
191
+
192
+ return {
193
+ backgroundImage: `linear-gradient(${clr}, ${clr}), linear-gradient(${cld}, ${cld})`,
194
+ backgroundPosition: `${neg(-k)}px 0px, ${neg(-kd)}px 0px`,
195
+ backgroundRepeat: 'no-repeat',
196
+ };
197
+ }
198
+
199
+ interface GetTableBodyRowsProps
200
+ extends Omit<ProfilerTableProps, 'tableBodyRef'> {
201
+ sortBy: string;
202
+ sortByDirection: string;
203
+ isDoubles: boolean;
204
+ messages: FlamegraphMessages;
205
+ }
206
+
207
+ const getTableBody = ({
208
+ flamebearer,
209
+ sortBy,
210
+ sortByDirection,
211
+ isDoubles,
212
+ fitMode,
213
+ handleTableItemClick,
214
+ highlightQuery,
215
+ palette,
216
+ selectedItem,
217
+ messages,
218
+ }: GetTableBodyRowsProps): TableBodyType => {
219
+ const { numTicks, maxSelf, sampleRate, spyName, units } = flamebearer;
220
+
221
+ const tableBodyCells = generateTable(flamebearer).sort(
222
+ (a, b) => b.total - a.total
223
+ );
224
+ const m = sortByDirection === 'asc' ? 1 : -1;
225
+ let sorted: typeof tableBodyCells;
226
+
227
+ if (sortBy === 'name') {
228
+ sorted = tableBodyCells.sort(
229
+ (a, b) => m * a[sortBy].localeCompare(b[sortBy])
230
+ );
231
+ } else {
232
+ switch (sortBy) {
233
+ case 'total':
234
+ case 'self': {
235
+ sorted = tableBodyCells.sort((a, b) => m * (a[sortBy] - b[sortBy]));
236
+ break;
237
+ }
238
+ case 'baseline': {
239
+ sorted = (tableBodyCells as (DoubleCell & { name: string })[]).sort(
240
+ (a, b) => m * (a.totalLeft / a.leftTicks - b.totalLeft / b.leftTicks)
241
+ );
242
+ break;
243
+ }
244
+ case 'comparison': {
245
+ sorted = (tableBodyCells as (DoubleCell & { name: string })[]).sort(
246
+ (a, b) =>
247
+ m * (a.totalRght / a.rightTicks - b.totalRght / b.rightTicks)
248
+ );
249
+ break;
250
+ }
251
+ case 'diff': {
252
+ sorted = (tableBodyCells as (DoubleCell & { name: string })[]).sort(
253
+ (a, b) => {
254
+ const totalDiffA = diffPercent(
255
+ ratioToPercent(a.totalLeft / a.leftTicks),
256
+ ratioToPercent(a.totalRght / a.rightTicks)
257
+ );
258
+ const totalDiffB = diffPercent(
259
+ ratioToPercent(b.totalLeft / b.leftTicks),
260
+ ratioToPercent(b.totalRght / b.rightTicks)
261
+ );
262
+
263
+ return m * (totalDiffA - totalDiffB);
264
+ }
265
+ );
266
+ break;
267
+ }
268
+ default:
269
+ sorted = tableBodyCells;
270
+ break;
271
+ }
272
+ }
273
+
274
+ const formatter = getFormatter(numTicks, sampleRate, units);
275
+ const isRowSelected = (name: string) => {
276
+ if (selectedItem.isJust) {
277
+ return name === selectedItem.value;
278
+ }
279
+
280
+ return false;
281
+ };
282
+
283
+ const nameCell = (x: { name: string }, style: CSSProperties) => (
284
+ <button className="table-item-button">
285
+ <span className="color-reference" style={style} />
286
+ <div className="symbol-name" style={fitIntoTableCell(fitMode)}>
287
+ {x.name}
288
+ </div>
289
+ </button>
290
+ );
291
+
292
+ const getSingleRow = (
293
+ x: SingleCell & { name: string },
294
+ color: InstanceType<typeof Color>,
295
+ style: CSSProperties
296
+ ): BodyRow => ({
297
+ 'data-row': `${x.type};${x.name};${x.self};${x.total}`,
298
+ isRowSelected: isRowSelected(x.name),
299
+ onClick: () => handleTableItemClick(x),
300
+ cells: [
301
+ { value: nameCell(x, style) },
302
+ {
303
+ value: formatter.format(x.self, sampleRate),
304
+ style: backgroundImageStyle(x.self, maxSelf, color),
305
+ },
306
+ {
307
+ value: formatter.format(x.total, sampleRate),
308
+ style: backgroundImageStyle(x.total, numTicks, color),
309
+ },
310
+ ],
311
+ });
312
+
313
+ const getDoubleRow = (
314
+ x: DoubleCell & { name: string },
315
+ style: CSSProperties
316
+ ): BodyRow => {
317
+ const leftPercent = ratioToPercent(x.totalLeft / x.leftTicks);
318
+ const rghtPercent = ratioToPercent(x.totalRght / x.rightTicks);
319
+
320
+ const totalDiff = diffPercent(leftPercent, rghtPercent);
321
+
322
+ let diffCellColor = '';
323
+ if (totalDiff > 0) {
324
+ diffCellColor = palette.badColor.rgb().string();
325
+ } else if (totalDiff < 0) {
326
+ diffCellColor = palette.goodColor.rgb().string();
327
+ }
328
+
329
+ let diffValue = '';
330
+ if (!x.totalLeft || totalDiff === Infinity) {
331
+ // this is a new function
332
+ diffValue = messages.diffNew;
333
+ } else if (!x.totalRght) {
334
+ // this function has been removed
335
+ diffValue = messages.diffRemoved;
336
+ } else if (totalDiff > 0) {
337
+ diffValue = `(+${totalDiff.toFixed(2)}%)`;
338
+ } else if (totalDiff < 0) {
339
+ diffValue = `(${totalDiff.toFixed(2)}%)`;
340
+ }
341
+
342
+ return {
343
+ 'data-row': `${x.type};${x.name};${x.totalLeft};${x.leftTicks};${x.totalRght};${x.rightTicks}`,
344
+ isRowSelected: isRowSelected(x.name),
345
+ onClick: () => handleTableItemClick(x),
346
+ cells: [
347
+ { value: nameCell(x, style) },
348
+ { value: `${leftPercent} %` },
349
+ { value: `${rghtPercent} %` },
350
+ {
351
+ value: diffValue,
352
+ style: {
353
+ color: diffCellColor,
354
+ },
355
+ },
356
+ ],
357
+ };
358
+ };
359
+
360
+ const rows = sorted
361
+ .filter((x) => {
362
+ if (!highlightQuery) {
363
+ return true;
364
+ }
365
+
366
+ return isMatch(highlightQuery, x.name);
367
+ })
368
+ .map((x) => {
369
+ const pn = getPackageNameFromStackTrace(spyName, x.name);
370
+ const color = isDoubles
371
+ ? defaultColor
372
+ : colorBasedOnPackageName(palette, pn);
373
+ const style = {
374
+ backgroundColor: color.rgb().toString(),
375
+ };
376
+
377
+ if (x.type === 'double') {
378
+ return getDoubleRow(x as DoubleCell & { name: string }, style);
379
+ }
380
+
381
+ return getSingleRow(x as SingleCell & { name: string }, color, style);
382
+ });
383
+
384
+ return rows.length > 0
385
+ ? { bodyRows: rows, type: 'filled' as const }
386
+ : {
387
+ value: (
388
+ <div className="unsupported-format">{messages.noItemsFound}</div>
389
+ ),
390
+ type: 'not-filled' as const,
391
+ };
392
+ };
393
+
394
+ export interface ProfilerTableProps {
395
+ flamebearer: Flamebearer;
396
+ fitMode: FitModes;
397
+ handleTableItemClick: (tableItem: { name: string }) => void;
398
+ highlightQuery: string;
399
+ palette: FlamegraphPalette;
400
+ selectedItem: Maybe<string>;
401
+
402
+ tableBodyRef: RefObject<HTMLTableSectionElement>;
403
+ }
404
+
405
+ function Table({
406
+ tableBodyRef,
407
+ flamebearer,
408
+ isDoubles,
409
+ fitMode,
410
+ handleTableItemClick,
411
+ highlightQuery,
412
+ selectedItem,
413
+ palette,
414
+ }: ProfilerTableProps & { isDoubles: boolean }) {
415
+ const i18n = useFlamegraphI18n();
416
+
417
+ const tableFormat = React.useMemo(
418
+ () =>
419
+ isDoubles
420
+ ? [
421
+ {
422
+ sortable: 1,
423
+ name: 'name' as const,
424
+ label: i18n.location,
425
+ },
426
+ {
427
+ sortable: 1,
428
+ name: 'baseline' as const,
429
+ label: i18n.baseline,
430
+ default: true,
431
+ },
432
+ {
433
+ sortable: 1,
434
+ name: 'comparison' as const,
435
+ label: i18n.comparison,
436
+ },
437
+ {
438
+ sortable: 1,
439
+ name: 'diff' as const,
440
+ label: i18n.diff,
441
+ },
442
+ ]
443
+ : [
444
+ {
445
+ sortable: 1,
446
+ name: 'name' as const,
447
+ label: i18n.location,
448
+ },
449
+ {
450
+ sortable: 1,
451
+ name: 'self' as const,
452
+ label: i18n.self,
453
+ default: true,
454
+ },
455
+ {
456
+ sortable: 1,
457
+ name: 'total' as const,
458
+ label: i18n.total,
459
+ },
460
+ ],
461
+ [i18n, isDoubles]
462
+ );
463
+
464
+ const tableSortProps = useTableSort(tableFormat as any);
465
+ const table = {
466
+ headRow: tableFormat as any,
467
+ ...getTableBody({
468
+ flamebearer,
469
+ sortBy: tableSortProps.sortBy,
470
+ sortByDirection: tableSortProps.sortByDirection,
471
+ isDoubles,
472
+ fitMode,
473
+ handleTableItemClick,
474
+ highlightQuery,
475
+ palette,
476
+ selectedItem,
477
+ messages: i18n,
478
+ }),
479
+ };
480
+
481
+ return (
482
+ <TableUI
483
+ /* eslint-disable-next-line react/jsx-props-no-spreading */
484
+ {...tableSortProps}
485
+ tableBodyRef={tableBodyRef}
486
+ table={table}
487
+ className={cl('flamegraph-table', {
488
+ 'flamegraph-table-doubles': isDoubles,
489
+ })}
490
+ />
491
+ );
492
+ }
493
+
494
+ const ProfilerTable = React.memo(function ProfilerTable({
495
+ flamebearer,
496
+ fitMode,
497
+ handleTableItemClick,
498
+ highlightQuery,
499
+ palette,
500
+ selectedItem,
501
+ }: Omit<ProfilerTableProps, 'tableBodyRef'>) {
502
+ const tableBodyRef = useRef<HTMLTableSectionElement>(null);
503
+
504
+ return (
505
+ <div data-testid="table-view">
506
+ <Table
507
+ tableBodyRef={tableBodyRef}
508
+ flamebearer={flamebearer}
509
+ isDoubles={flamebearer.format === 'double'}
510
+ fitMode={fitMode}
511
+ highlightQuery={highlightQuery}
512
+ handleTableItemClick={handleTableItemClick}
513
+ palette={palette}
514
+ selectedItem={selectedItem}
515
+ />
516
+ <TableTooltip
517
+ tableBodyRef={tableBodyRef}
518
+ numTicks={flamebearer.numTicks}
519
+ sampleRate={flamebearer.sampleRate}
520
+ units={flamebearer.units}
521
+ palette={palette}
522
+ />
523
+ </div>
524
+ );
525
+ });
526
+
527
+ export default ProfilerTable;
@@ -0,0 +1,82 @@
1
+ .wrapper {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+ flex-grow: 1;
6
+ }
7
+
8
+ .search {
9
+ background: var(--ps-immutable-off-white);
10
+ transition: background-color ease-out 0.1s;
11
+ font: inherit;
12
+ margin-right: 1.5px;
13
+ border: 1px solid var(--ps-ui-border);
14
+ display: flex;
15
+ flex-grow: 1;
16
+ height: 37px;
17
+ width: 100%;
18
+
19
+ &,
20
+ &::placeholder {
21
+ color: var(--ps-immutable-placeholder-text) !important;
22
+ }
23
+ }
24
+
25
+ .searchWithSync {
26
+ @extend .search;
27
+
28
+ border-top-right-radius: 0;
29
+ border-bottom-right-radius: 0;
30
+ border-right: 1px solid var(--ps-ui-border);
31
+ margin-right: 0;
32
+ }
33
+
34
+ .search-synced {
35
+ @extend .search;
36
+
37
+ border-color: var(--ps-immutable-linked-border);
38
+ }
39
+
40
+ .search:focus {
41
+ background: var(--ps-immutable-white);
42
+ }
43
+
44
+ .icon {
45
+ cursor: pointer;
46
+
47
+ & path {
48
+ fill: var(--ps-grey-primary);
49
+ }
50
+ }
51
+
52
+ .checked {
53
+ @extend .icon;
54
+
55
+ path {
56
+ fill: var(--ps-immutable-linked-border);
57
+ }
58
+ }
59
+
60
+ .sync {
61
+ border: none;
62
+ background-color: var(--ps-immutable-off-white);
63
+ cursor: pointer;
64
+ width: 40px;
65
+ border-top-right-radius: 4px;
66
+ border-bottom-right-radius: 4px;
67
+ border: 1px solid var(--ps-ui-border);
68
+ position: relative;
69
+ border-left: none;
70
+ display: flex;
71
+ align-self: stretch;
72
+ align-items: center;
73
+ justify-content: center;
74
+ }
75
+
76
+ .syncSelected {
77
+ @extend .sync;
78
+
79
+ border-top-color: var(--ps-immutable-linked-border);
80
+ border-right-color: var(--ps-immutable-linked-border);
81
+ border-bottom-color: var(--ps-immutable-linked-border);
82
+ }