Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Grafana flamegraph component #2346

Merged
merged 12 commits into from
Sep 27, 2023
Next Next commit
Add grafana flamegraph
  • Loading branch information
aocenas committed Aug 31, 2023
commit 2108632aa15bbb364dcbfc2b316671d92a58d333
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@
"@fortawesome/free-regular-svg-icons": "~5.15.2",
"@fortawesome/free-solid-svg-icons": "~5.14.0",
"@fortawesome/react-fontawesome": "~0.1.11",
"@grafana/data": "10.1.0",
"@grafana/flamegraph": "^10.2.0-pre.4",
"@grafana/ui": "^10.1.0",
"@hookform/resolvers": "^2.9.8",
"@mui/base": "5.0.0-alpha.98",
"@mui/material": "5.10.11",
Expand Down
26 changes: 17 additions & 9 deletions public/app/components/ExportData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ export class PprofRequest extends Message<PprofRequest> {
end: number;
}

type ExportDataProps = exportPprof &
type ExportDataProps = {
buttonEl?: React.ComponentType<{
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
}>;
} & exportPprof &
exportHTML &
exportFlamegraphDotCom &
exportPNG &
Expand Down Expand Up @@ -292,14 +296,18 @@ function ExportData(props: ExportDataProps) {
return (
<div className={styles.dropdownContainer}>
<OutsideClickHandler onOutsideClick={() => setToggleMenu(false)}>
<Tooltip placement="top" title="Export Data">
<Button
className={styles.toggleMenuButton}
onClick={handleToggleMenu}
>
<FontAwesomeIcon icon={faShareSquare} />
</Button>
</Tooltip>
{props.buttonEl ? (
<props.buttonEl onClick={handleToggleMenu} />
) : (
<Tooltip placement="top" title="Export Data">
<Button
className={styles.toggleMenuButton}
onClick={handleToggleMenu}
>
<FontAwesomeIcon icon={faShareSquare} />
</Button>
</Tooltip>
)}
<div className={toggleMenu ? styles.menuShow : styles.menuHide}>
{exportPNG && (
<button
Expand Down
132 changes: 132 additions & 0 deletions public/app/pages/ContinuousSingleView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import React, { useEffect } from 'react';
import 'react-dom';

import {
createTheme,
DataFrameDTO,
FieldType,
createDataFrame,
} from '@grafana/data';
import { FlameGraph } from '@grafana/flamegraph';
import { Button, Tooltip } from '@grafana/ui';

import { useAppDispatch, useAppSelector } from '@pyroscope/redux/hooks';
import Box from '@pyroscope/ui/Box';
import { FlamegraphRenderer } from '@pyroscope/legacy/flamegraph/FlamegraphRenderer';
Expand Down Expand Up @@ -79,10 +88,52 @@ function ContinuousSingleView() {
};
const exportToFlamegraphDotComFn = useExportToFlamegraphDotCom(getRaw());

const newFlamegraph = true;
const flamegraphRenderer = (() => {
switch (singleView.type) {
case 'loaded':
case 'reloading': {
if (newFlamegraph) {
const dataFrame = diffFlamebearerToDataFrameDTO(
singleView.profile?.flamebearer.levels,
singleView.profile?.flamebearer.names
);
return (
<FlameGraph
getTheme={() => createTheme({ colors: { mode: 'dark' } })}
data={dataFrame}
extraHeaderElements={
<ExportData
flamebearer={singleView.profile}
exportPNG
exportJSON
exportPprof
exportHTML
exportFlamegraphDotCom={isExportToFlamegraphDotComEnabled}
exportFlamegraphDotComFn={exportToFlamegraphDotComFn}
buttonEl={({ onClick }) => {
return (
<Tooltip content={'Export Data'}>
<Button
// Ugly hack to go around globally defined line height messing up sizing of the button.
// Not sure why it happens even if everything is display: Block. To override it would
// need changes in Flamegraph which would be weird so this seems relatively sensible.
style={{ marginTop: -7 }}
icon={'download-alt'}
size={'sm'}
variant={'secondary'}
fill={'outline'}
onClick={onClick}
/>
</Tooltip>
);
}}
/>
}
/>
);
}

return (
<FlamegraphRenderer
showCredit={false}
Expand Down Expand Up @@ -269,4 +320,85 @@ function prepareTimelineTooltipContent(
);
}

function getNodes(level: number[], names: string[]) {
const nodes = [];
for (let i = 0; i < level.length; i += 4) {
nodes.push({
level: 0,
label: names[level[i + 3]],
val: level[i + 1],
self: level[i + 2],
offset: level[i],
children: [],
});
}
return nodes;
}

function diffFlamebearerToDataFrameDTO(levels: number[][], names: string[]) {
const nodeLevels: any[][] = [];
for (let i = 0; i < levels.length; i++) {
nodeLevels[i] = [];
for (const node of getNodes(levels[i], names)) {
node.level = i;
nodeLevels[i].push(node);
if (i > 0) {
const prevNodesInLevel = nodeLevels[i].slice(0, -1);
const currentNodeStart =
prevNodesInLevel.reduce(
(acc: number, n: any) => n.offset + n.val + acc,
0
) + node.offset;

const prevLevel = nodeLevels[i - 1];
let prevLevelOffset = 0;
for (const prevLevelNode of prevLevel) {
const parentNodeStart = prevLevelOffset + prevLevelNode.offset;
const parentNodeEnd = parentNodeStart + prevLevelNode.val;

if (
parentNodeStart <= currentNodeStart &&
parentNodeEnd > currentNodeStart
) {
prevLevelNode.children.push(node);
break;
} else {
prevLevelOffset += prevLevelNode.offset + prevLevelNode.val;
}
}
}
}
}

const root = nodeLevels[0][0];
const stack = [root];

const labelValues = [];
const levelValues = [];
const selfValues = [];
const valueValues = [];

while (stack.length) {
const node = stack.shift();
labelValues.push(node.label);
levelValues.push(node.level);
selfValues.push(node.self);
valueValues.push(node.val);
stack.unshift(...node.children);
}

const frame: DataFrameDTO = {
name: 'response',
meta: { preferredVisualisationType: 'flamegraph' },
fields: [
{ name: 'level', values: levelValues },
{ name: 'label', values: labelValues, type: FieldType.string },
{ name: 'self', values: selfValues },
{ name: 'value', values: valueValues },
],
};

return createDataFrame(frame);
}

export default ContinuousSingleView;
4 changes: 4 additions & 0 deletions public/app/sass/profile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ html {
width: 100vw;
}

a {
text-decoration: none;
}

body {
font: 400 16px/1.7 -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica,
Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
Expand Down
Loading