@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,603 @@
1
+ /* eslint-disable react/no-unused-state */
2
+ /* eslint-disable no-bitwise */
3
+ /* eslint-disable react/no-access-state-in-setstate */
4
+ /* eslint-disable react/jsx-props-no-spreading */
5
+ /* eslint-disable react/destructuring-assignment */
6
+ /* eslint-disable no-nested-ternary */
7
+ /* eslint-disable global-require */
8
+
9
+ import React, { Dispatch, SetStateAction, ReactNode, Component } from 'react';
10
+ import clsx from 'clsx';
11
+ import { Maybe } from 'true-myth';
12
+ import { createFF, Flamebearer, Profile } from '../models';
13
+ import NoData from '../shims/NoData';
14
+
15
+ import Graph from './FlameGraphComponent';
16
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
17
+ // @ts-ignore: let's move this to typescript some time in the future
18
+ import ProfilerTable from '../ProfilerTable';
19
+ import Toolbar, { ProfileHeaderProps } from '../Toolbar';
20
+ import {
21
+ calleesProfile,
22
+ callersProfile,
23
+ } from '../convert/sandwichViewProfiles';
24
+ import { DefaultPalette } from './FlameGraphComponent/colorPalette';
25
+ import styles from './FlamegraphRenderer.module.scss';
26
+ import PyroscopeLogo from '../logo-v3-small.svg';
27
+ import { FitModes } from '../fitMode/fitMode';
28
+ import { ViewTypes } from './FlameGraphComponent/viewTypes';
29
+ import { GraphVizPane } from './FlameGraphComponent/GraphVizPane';
30
+ import { isSameFlamebearer } from './uniqueness';
31
+ import { normalize } from './normalize';
32
+
33
+ // Refers to a node in the flamegraph
34
+ interface Node {
35
+ i: number;
36
+ j: number;
37
+ }
38
+
39
+ export interface FlamegraphRendererProps {
40
+ profile?: Profile;
41
+
42
+ /** in case you ONLY want to display a specific visualization mode. It will also disable the dropdown that allows you to change mode. */
43
+ onlyDisplay?: ViewTypes;
44
+ showToolbar?: boolean;
45
+
46
+ /** whether to display the panes (table and flamegraph) side by side ('horizontal') or one on top of the other ('vertical') */
47
+ panesOrientation?: 'horizontal' | 'vertical';
48
+ showPyroscopeLogo?: boolean;
49
+ showCredit?: boolean;
50
+ ExportData?: ProfileHeaderProps['ExportData'];
51
+
52
+ /** @deprecated prefer Profile */
53
+ flamebearer?: Flamebearer;
54
+ sharedQuery?: {
55
+ searchQuery?: string;
56
+ onQueryChange: Dispatch<SetStateAction<string | undefined>>;
57
+ syncEnabled: string | boolean;
58
+ toggleSync: Dispatch<SetStateAction<boolean | string>>;
59
+ id: string;
60
+ };
61
+
62
+ children?: ReactNode;
63
+ }
64
+
65
+ interface FlamegraphRendererState {
66
+ /** A dirty flamegraph refers to a flamegraph where its original state can be reset */
67
+ isFlamegraphDirty: boolean;
68
+
69
+ view: NonNullable<FlamegraphRendererProps['onlyDisplay']>;
70
+ panesOrientation: NonNullable<FlamegraphRendererProps['panesOrientation']>;
71
+
72
+ fitMode: 'HEAD' | 'TAIL';
73
+ flamebearer: NonNullable<FlamegraphRendererProps['flamebearer']>;
74
+
75
+ /** Query searched in the input box.
76
+ * It's used to filter data in the table AND highlight items in the flamegraph */
77
+ searchQuery: string;
78
+ /** Triggered when an item is clicked on the table. It overwrites the searchQuery */
79
+ selectedItem: Maybe<string>;
80
+
81
+ flamegraphConfigs: {
82
+ focusedNode: Maybe<Node>;
83
+ zoom: Maybe<Node>;
84
+ };
85
+
86
+ palette: typeof DefaultPalette;
87
+ }
88
+
89
+ class FlameGraphRenderer extends Component<
90
+ FlamegraphRendererProps,
91
+ FlamegraphRendererState
92
+ > {
93
+ resetFlamegraphState = {
94
+ focusedNode: Maybe.nothing<Node>(),
95
+ zoom: Maybe.nothing<Node>(),
96
+ };
97
+
98
+ // TODO: At some point the initial state may be set via the user
99
+ // Eg when sharing a specific node
100
+ initialFlamegraphState = this.resetFlamegraphState;
101
+
102
+ // eslint-disable-next-line react/static-property-placement
103
+ static defaultProps = {
104
+ showCredit: true,
105
+ };
106
+
107
+ constructor(props: FlamegraphRendererProps) {
108
+ super(props);
109
+
110
+ this.state = {
111
+ isFlamegraphDirty: false,
112
+ view: this.props.onlyDisplay ? this.props.onlyDisplay : 'both',
113
+ fitMode: 'HEAD',
114
+ flamebearer: normalize(props),
115
+
116
+ // Default to horizontal since it's the most common case
117
+ panesOrientation: props.panesOrientation
118
+ ? props.panesOrientation
119
+ : 'horizontal',
120
+
121
+ // query used in the 'search' checkbox
122
+ searchQuery: '',
123
+ selectedItem: Maybe.nothing(),
124
+
125
+ flamegraphConfigs: this.initialFlamegraphState,
126
+
127
+ // TODO make this come from the redux store?
128
+ palette: DefaultPalette,
129
+ };
130
+ }
131
+
132
+ componentDidUpdate(
133
+ prevProps: FlamegraphRendererProps,
134
+ prevState: FlamegraphRendererState
135
+ ) {
136
+ // TODO: this is a slow operation
137
+ const prevFlame = normalize(prevProps);
138
+ const currFlame = normalize(this.props);
139
+
140
+ if (!this.isSameFlamebearer(prevFlame, currFlame)) {
141
+ const newConfigs = this.calcNewConfigs(prevFlame, currFlame);
142
+
143
+ // Batch these updates to not do unnecessary work
144
+ // eslint-disable-next-line react/no-did-update-set-state
145
+ this.setState({
146
+ flamebearer: currFlame,
147
+ flamegraphConfigs: {
148
+ ...this.state.flamegraphConfigs,
149
+ ...newConfigs,
150
+ },
151
+ selectedItem: Maybe.nothing(),
152
+ });
153
+ return;
154
+ }
155
+
156
+ // flamegraph configs changed
157
+ if (prevState.flamegraphConfigs !== this.state.flamegraphConfigs) {
158
+ this.updateFlamegraphDirtiness();
159
+ }
160
+ }
161
+
162
+ // Calculate what should be the new configs
163
+ // It checks if the zoom/selectNode still points to the same node
164
+ // If not, it resets to the resetFlamegraphState
165
+ calcNewConfigs = (prevFlame: Flamebearer, currFlame: Flamebearer) => {
166
+ const newConfigs = this.state.flamegraphConfigs;
167
+
168
+ // This is a simple heuristic based on the name
169
+ // It does not account for eg recursive calls
170
+ const isSameNode = (f: Flamebearer, f2: Flamebearer, s: Maybe<Node>) => {
171
+ // TODO: don't use createFF directly
172
+ const getBarName = (f: Flamebearer, i: number, j: number) => {
173
+ return f.names[createFF(f.format).getBarName(f.levels[i], j)];
174
+ };
175
+
176
+ // No node is technically the same node
177
+ if (s.isNothing) {
178
+ return true;
179
+ }
180
+
181
+ // if the bar doesn't exist, it will throw an error
182
+ try {
183
+ const sAny = s as any
184
+ const barName1 = getBarName(f, sAny.value.i, sAny.value.j)
185
+ const barName2 = getBarName(f2, sAny.value.i, sAny.value.j)
186
+ return barName1 === barName2;
187
+ } catch {
188
+ return false;
189
+ }
190
+ };
191
+
192
+ // Reset zoom
193
+ const currZoom = this.state.flamegraphConfigs.zoom;
194
+ if (!isSameNode(prevFlame, currFlame, currZoom)) {
195
+ newConfigs.zoom = this.resetFlamegraphState.zoom;
196
+ }
197
+
198
+ // Reset focused node
199
+ const currFocusedNode = this.state.flamegraphConfigs.focusedNode;
200
+ if (!isSameNode(prevFlame, currFlame, currFocusedNode)) {
201
+ newConfigs.focusedNode = this.resetFlamegraphState.focusedNode;
202
+ }
203
+
204
+ return newConfigs;
205
+ };
206
+
207
+ onSearchChange = (e: string) => {
208
+ this.setState({ searchQuery: e });
209
+ };
210
+
211
+ isSameFlamebearer = (prevFlame: Flamebearer, currFlame: Flamebearer) => {
212
+ return isSameFlamebearer(prevFlame, currFlame);
213
+ // TODO: come up with a less resource intensive operation
214
+ // keep in mind naive heuristics may provide bad behaviours like (https://github.com/pyroscope-io/pyroscope/issues/1192)
215
+ // return JSON.stringify(prevFlame) === JSON.stringify(currFlame);
216
+ };
217
+
218
+ onReset = () => {
219
+ this.setState({
220
+ ...this.state,
221
+ flamegraphConfigs: {
222
+ ...this.state.flamegraphConfigs,
223
+ ...this.initialFlamegraphState,
224
+ },
225
+ selectedItem: Maybe.nothing(),
226
+ });
227
+ };
228
+
229
+ onFlamegraphZoom = (bar: Maybe<Node>) => {
230
+ // zooming on the topmost bar is equivalent to resetting to the original state
231
+ if (bar.isJust && bar.value.i === 0 && bar.value.j === 0) {
232
+ this.onReset();
233
+ return;
234
+ }
235
+
236
+ // otherwise just pass it up to the state
237
+ // doesn't matter if it's some or none
238
+ this.setState({
239
+ ...this.state,
240
+ flamegraphConfigs: {
241
+ ...this.state.flamegraphConfigs,
242
+ zoom: bar,
243
+ },
244
+ });
245
+ };
246
+
247
+ onFocusOnNode = (i: number, j: number) => {
248
+ if (i === 0 && j === 0) {
249
+ this.onReset();
250
+ return;
251
+ }
252
+
253
+ let flamegraphConfigs = { ...this.state.flamegraphConfigs };
254
+
255
+ // reset zoom if we are focusing below the zoom
256
+ // or the same one we were zoomed
257
+ const { zoom } = this.state.flamegraphConfigs;
258
+ if (zoom.isJust) {
259
+ if (zoom.value.i <= i) {
260
+ flamegraphConfigs = {
261
+ ...flamegraphConfigs,
262
+ zoom: this.initialFlamegraphState.zoom,
263
+ };
264
+ }
265
+ }
266
+
267
+ this.setState({
268
+ ...this.state,
269
+ flamegraphConfigs: {
270
+ ...flamegraphConfigs,
271
+ focusedNode: Maybe.just({ i, j }),
272
+ },
273
+ });
274
+ };
275
+
276
+ setActiveItem = (item: { name: string }) => {
277
+ const { name } = item;
278
+
279
+ // if clicking on the same item, undo the search
280
+ if (this.state.selectedItem.isJust) {
281
+ if (name === this.state.selectedItem.value) {
282
+ this.setState({
283
+ selectedItem: Maybe.nothing(),
284
+ });
285
+ return;
286
+ }
287
+ }
288
+
289
+ // clicking for the first time
290
+ this.setState({
291
+ selectedItem: Maybe.just(name),
292
+ });
293
+ };
294
+
295
+ getHighlightQuery = () => {
296
+ // prefer table selected
297
+ if (this.state.selectedItem.isJust) {
298
+ return this.state.selectedItem.value;
299
+ }
300
+
301
+ return this.state.searchQuery;
302
+ };
303
+
304
+ updateView = (newView: ViewTypes) => {
305
+ if (newView === 'sandwich') {
306
+ this.setState({
307
+ searchQuery: '',
308
+ flamegraphConfigs: this.resetFlamegraphState,
309
+ });
310
+ }
311
+
312
+ this.setState({
313
+ view: newView,
314
+ });
315
+ };
316
+
317
+ updateFlamegraphDirtiness = () => {
318
+ // TODO(eh-am): find a better approach
319
+ const isDirty = this.isDirty();
320
+
321
+ this.setState({
322
+ isFlamegraphDirty: isDirty,
323
+ });
324
+ };
325
+
326
+ updateFitMode = (newFitMode: FitModes) => {
327
+ this.setState({
328
+ fitMode: newFitMode,
329
+ });
330
+ };
331
+
332
+ // used as a variable instead of keeping in the state
333
+ // so that the flamegraph doesn't rerender unnecessarily
334
+ isDirty = () => {
335
+ return (
336
+ this.state.selectedItem.isJust ||
337
+ JSON.stringify(this.initialFlamegraphState) !==
338
+ JSON.stringify(this.state.flamegraphConfigs)
339
+ );
340
+ };
341
+
342
+ shouldShowToolbar() {
343
+ // default to true
344
+ return this.props.showToolbar !== undefined ? this.props.showToolbar : true;
345
+ }
346
+
347
+ render = () => {
348
+ // This is necessary because the order switches depending on single vs comparison view
349
+ const tablePane = (
350
+ <div
351
+ key="table-pane"
352
+ className={clsx(
353
+ styles.tablePane,
354
+ this.state.panesOrientation === 'vertical'
355
+ ? styles.vertical
356
+ : styles.horizontal
357
+ )}
358
+ >
359
+ <ProfilerTable
360
+ data-testid="table-view"
361
+ flamebearer={this.state.flamebearer}
362
+ fitMode={this.state.fitMode}
363
+ highlightQuery={this.state.searchQuery}
364
+ selectedItem={this.state.selectedItem}
365
+ handleTableItemClick={this.setActiveItem}
366
+ palette={this.state.palette}
367
+ />
368
+ </div>
369
+ );
370
+
371
+ const toolbarVisible = this.shouldShowToolbar();
372
+
373
+ const flameGraphPane = (
374
+ <Graph
375
+ key="flamegraph-pane"
376
+ // data-testid={flamegraphDataTestId}
377
+ showCredit={this.props.showCredit as boolean}
378
+ flamebearer={this.state.flamebearer}
379
+ highlightQuery={this.getHighlightQuery()}
380
+ setActiveItem={this.setActiveItem}
381
+ selectedItem={this.state.selectedItem}
382
+ updateView={this.props.onlyDisplay ? undefined : this.updateView}
383
+ fitMode={this.state.fitMode}
384
+ updateFitMode={this.updateFitMode}
385
+ zoom={this.state.flamegraphConfigs.zoom}
386
+ focusedNode={this.state.flamegraphConfigs.focusedNode}
387
+ onZoom={this.onFlamegraphZoom}
388
+ onFocusOnNode={this.onFocusOnNode}
389
+ onReset={this.onReset}
390
+ isDirty={this.isDirty}
391
+ palette={this.state.palette}
392
+ toolbarVisible={toolbarVisible}
393
+ setPalette={(p) =>
394
+ this.setState({
395
+ palette: p,
396
+ })
397
+ }
398
+ />
399
+ );
400
+
401
+ const sandwichPane = (() => {
402
+ if (this.state.selectedItem.isNothing) {
403
+ return (
404
+ <div className={styles.sandwichPane} key="sandwich-pane">
405
+ <div
406
+ className={clsx(
407
+ styles.sandwichPaneInfo,
408
+ this.state.panesOrientation === 'vertical'
409
+ ? styles.vertical
410
+ : styles.horizontal
411
+ )}
412
+ >
413
+ <div className={styles.arrow} />
414
+ Select a function to view callers/callees sandwich view
415
+ </div>
416
+ </div>
417
+ );
418
+ }
419
+
420
+ const callersFlamebearer = callersProfile(
421
+ this.state.flamebearer,
422
+ (this.state.selectedItem as any).value
423
+ );
424
+ const calleesFlamebearer = calleesProfile(
425
+ this.state.flamebearer,
426
+ (this.state.selectedItem as any).value
427
+ );
428
+ const sandwitchGraph = (myCustomParams: {
429
+ flamebearer: Flamebearer;
430
+ headerVisible?: boolean;
431
+ showSingleLevel?: boolean;
432
+ }) => (
433
+ <Graph
434
+ disableClick
435
+ showCredit={this.props.showCredit as boolean}
436
+ highlightQuery=""
437
+ setActiveItem={this.setActiveItem}
438
+ selectedItem={this.state.selectedItem}
439
+ fitMode={this.state.fitMode}
440
+ updateFitMode={this.updateFitMode}
441
+ zoom={this.state.flamegraphConfigs.zoom}
442
+ focusedNode={this.state.flamegraphConfigs.focusedNode}
443
+ onZoom={this.onFlamegraphZoom}
444
+ onFocusOnNode={this.onFocusOnNode}
445
+ onReset={this.onReset}
446
+ isDirty={this.isDirty}
447
+ palette={this.state.palette}
448
+ toolbarVisible={toolbarVisible}
449
+ setPalette={(p) =>
450
+ this.setState({
451
+ palette: p,
452
+ })
453
+ }
454
+ {...myCustomParams}
455
+ />
456
+ );
457
+
458
+ return (
459
+ <div className={styles.sandwichPane} key="sandwich-pane">
460
+ <div className={styles.sandwichTop}>
461
+ <span className={styles.name}>Callers</span>
462
+ {/* todo(dogfrogfog): to allow left/right click on the node we should
463
+ store Graph component we clicking and append action only on to
464
+ this component
465
+ will be implemented i nnext PR */}
466
+ {sandwitchGraph({ flamebearer: callersFlamebearer })}
467
+ </div>
468
+ <div className={styles.sandwichBottom}>
469
+ <span className={styles.name}>Callees</span>
470
+ {sandwitchGraph({
471
+ flamebearer: calleesFlamebearer,
472
+ headerVisible: false,
473
+ showSingleLevel: true,
474
+ })}
475
+ </div>
476
+ </div>
477
+ );
478
+ })();
479
+
480
+ // export type Flamebearer = {
481
+ // /**
482
+ // * List of names
483
+ // */
484
+ // names: string[];
485
+ // /**
486
+ // * List of level
487
+ // *
488
+ // * This is NOT the same as in the flamebearer
489
+ // * that we receive from the server.
490
+ // * As in there are some transformations required
491
+ // * (see deltaDiffWrapper)
492
+ // */
493
+ // levels: number[][];
494
+ // numTicks: number;
495
+ // maxSelf: number;
496
+
497
+ // /**
498
+ // * Sample Rate, used in text information
499
+ // */
500
+ // sampleRate: number;
501
+ // units: Units;
502
+
503
+ // spyName: SpyName;
504
+ // // format: 'double' | 'single';
505
+ // // leftTicks?: number;
506
+ // // rightTicks?: number;
507
+ // } & addTicks;
508
+
509
+ const dataUnavailable =
510
+ !this.state.flamebearer || this.state.flamebearer.names.length <= 1;
511
+ const panes = decidePanesOrder(
512
+ this.state.view,
513
+ flameGraphPane,
514
+ tablePane,
515
+ sandwichPane,
516
+ <GraphVizPane flamebearer={this.state.flamebearer} />
517
+ );
518
+
519
+ return (
520
+ <div>
521
+ <div>
522
+ {toolbarVisible && (
523
+ <Toolbar
524
+ sharedQuery={this.props.sharedQuery}
525
+ enableChangingDisplay={!this.props.onlyDisplay}
526
+ flamegraphType={this.state.flamebearer.format}
527
+ view={this.state.view}
528
+ handleSearchChange={this.onSearchChange}
529
+ reset={this.onReset}
530
+ updateView={this.updateView}
531
+ updateFitMode={this.updateFitMode}
532
+ fitMode={this.state.fitMode}
533
+ isFlamegraphDirty={this.isDirty()}
534
+ selectedNode={this.state.flamegraphConfigs.zoom}
535
+ highlightQuery={this.state.searchQuery}
536
+ onFocusOnSubtree={this.onFocusOnNode}
537
+ ExportData={this.props.ExportData}
538
+ />
539
+ )}
540
+ {this.props.children}
541
+ <div
542
+ className={`${styles.flamegraphContainer} ${clsx(
543
+ this.state.panesOrientation === 'vertical'
544
+ ? styles.vertical
545
+ : styles.horizontal,
546
+ styles[this.state.panesOrientation],
547
+ styles.panesWrapper
548
+ )}`}
549
+ >
550
+ {dataUnavailable ? <NoData /> : panes.map((pane) => pane)}
551
+ </div>
552
+ </div>
553
+
554
+ {this.props.showPyroscopeLogo && (
555
+ <div className={styles.createdBy}>
556
+ Created by
557
+ <a
558
+ href="https://twitter.com/PyroscopeIO"
559
+ rel="noreferrer"
560
+ target="_blank"
561
+ >
562
+ <PyroscopeLogo width="30" height="30" />
563
+ @PyroscopeIO
564
+ </a>
565
+ </div>
566
+ )}
567
+ </div>
568
+ );
569
+ };
570
+ }
571
+
572
+ function decidePanesOrder(
573
+ view: FlamegraphRendererState['view'],
574
+ flamegraphPane: JSX.Element | null,
575
+ tablePane: JSX.Element,
576
+ sandwichPane: JSX.Element,
577
+ graphvizPane: JSX.Element
578
+ ) {
579
+ switch (view) {
580
+ case 'table': {
581
+ return [tablePane];
582
+ }
583
+ case 'flamegraph': {
584
+ return [flamegraphPane];
585
+ }
586
+ case 'sandwich': {
587
+ return [tablePane, sandwichPane];
588
+ }
589
+
590
+ case 'both': {
591
+ return [tablePane, flamegraphPane];
592
+ }
593
+
594
+ case 'graphviz': {
595
+ return [graphvizPane];
596
+ }
597
+ default: {
598
+ throw new Error(`Invalid view '${view}'`);
599
+ }
600
+ }
601
+ }
602
+
603
+ export default FlameGraphRenderer;
@@ -0,0 +1,93 @@
1
+ .flamegraphContainer {
2
+ width: 100%;
3
+ }
4
+
5
+ /* We take a gamble here and assume only 2 items will exist */
6
+ .flamegraphContainer > *:first-child {
7
+ margin-right: 8px;
8
+ }
9
+ .flamegraphContainer > *:nth-child(2) {
10
+ margin-left: 8px;
11
+ /* width: 50%; */
12
+ }
13
+
14
+ .createdBy {
15
+ color: white;
16
+ text-align: center;
17
+ margin: 10px 0;
18
+ }
19
+ .createdBy a {
20
+ color: #199bef !important;
21
+ }
22
+
23
+ .horizontal {
24
+ flex-direction: row;
25
+ }
26
+
27
+ .vertical {
28
+ /* reverse, since by default the table is on the left in the DOM */
29
+ flex-direction: column-reverse;
30
+ }
31
+
32
+ .panesWrapper {
33
+ display: flex;
34
+ }
35
+
36
+ .tablePane,
37
+ .sandwichPane {
38
+ flex: 1;
39
+ }
40
+
41
+ .sandwichPane {
42
+ .sandwichPaneInfo {
43
+ display: inline-block;
44
+ position: relative;
45
+ margin: 30px 0 0 10px;
46
+ padding: 8px;
47
+ border-radius: 5px;
48
+ background-color: var(--ps-sandwich-pane-info-bg);
49
+ color: var(--ps-neutral-1);
50
+
51
+ &.vertical {
52
+ margin: 30px 0;
53
+
54
+ .arrow {
55
+ top: initial;
56
+ bottom: -15px;
57
+ left: 20px;
58
+ transform: rotate(-90deg);
59
+ }
60
+ }
61
+
62
+ .arrow {
63
+ position: absolute;
64
+ top: 14px;
65
+ left: -20px;
66
+ width: 0;
67
+ height: 0;
68
+ border-style: solid;
69
+ border-width: 8px 20px 8px 0;
70
+ border-color: transparent var(--ps-sandwich-pane-info-bg) transparent
71
+ transparent;
72
+ }
73
+ }
74
+
75
+ .sandwichTop {
76
+ margin-bottom: 10px;
77
+ }
78
+
79
+ .sandwichTop,
80
+ .sandwichBottom {
81
+ display: flex;
82
+
83
+ .name {
84
+ writing-mode: vertical-rl;
85
+ transform: rotate(-180deg);
86
+ padding: 5px 0;
87
+ }
88
+ }
89
+
90
+ .sandwichBottom span {
91
+ text-align: right;
92
+ }
93
+ }