Skip to content

Commit

Permalink
feat: graphviz visualization support (#1759)
Browse files Browse the repository at this point in the history
  • Loading branch information
petethepig authored Jan 23, 2023
1 parent f581aee commit ca855d2
Show file tree
Hide file tree
Showing 11 changed files with 751 additions and 21 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
transformIgnorePatterns: [
// force us to not transpile these dependencies
// https://stackoverflow.com/a/69150188
'node_modules/(?!(true-myth|d3|d3-array|internmap|d3-scale|react-notifications-component))',
'node_modules/(?!(true-myth|d3|d3-array|internmap|d3-scale|react-notifications-component|graphviz-react))',
],
globals: {
'ts-jest': {
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
"webpack-plugin-hash-output": "^3.2.1"
},
"dependencies": {
"graphviz-react": "^1.2.5",
"@babel/plugin-transform-runtime": "^7.16.4",
"@babel/preset-env": "^7.10.4",
"@babel/preset-react": "^7.12.10",
Expand Down Expand Up @@ -277,6 +278,8 @@
"resolutions": {
"react": "16.14.0",
"react-dom": "16.14.0",
"jquery": "3.6.0"
"jquery": "3.6.0",
"d3-graphviz": "5.0.2",
"d3-selection": "3.0.0"
}
}
1 change: 1 addition & 0 deletions packages/pyroscope-flamegraph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0",
"graphviz-react": "^1.2.5",
"true-myth": "^5.1.2"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
export type ViewTypes = 'flamegraph' | 'both' | 'table' | 'sandwich';
export type ViewTypes =
| 'flamegraph'
| 'both'
| 'table'
| 'sandwich'
| 'graphviz';
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react/destructuring-assignment */
/* eslint-disable no-nested-ternary */
/* eslint-disable global-require */

import React, { Dispatch, SetStateAction, ReactNode, Component } from 'react';
import clsx from 'clsx';
import { Maybe } from 'true-myth';
import { createFF, Flamebearer, Profile } from '@pyroscope/models/src';
import NoData from '@pyroscope/webapp/javascript/ui/NoData';

import Graph from './FlameGraphComponent';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: let's move this to typescript some time in the future
Expand All @@ -19,13 +21,31 @@ import {
calleesProfile,
callersProfile,
} from '../convert/sandwichViewProfiles';
import toGraphviz from '../convert/toGraphviz';
import { DefaultPalette } from './FlameGraphComponent/colorPalette';
import styles from './FlamegraphRenderer.module.scss';
import PyroscopeLogo from '../logo-v3-small.svg';
import decode from './decode';
import { FitModes } from '../fitMode/fitMode';
import { ViewTypes } from './FlameGraphComponent/viewTypes';

interface IGraphvizProps {
dot: string;
options?: object;
className?: string;
}

// this is to make sure that graphviz-react is not used in node.js
let Graphviz = (obj: IGraphvizProps) => {
if (obj) {
return null;
}
return null;
};
if (typeof process === 'undefined') {
Graphviz = require('graphviz-react').Graphviz;
}

// Still support old flamebearer format
// But prefer the new 'profile' one
function mountFlamebearer(p: { profile?: Profile; flamebearer?: Flamebearer }) {
Expand Down Expand Up @@ -511,13 +531,83 @@ class FlameGraphRenderer extends Component<
);
})();

// export type Flamebearer = {
// /**
// * List of names
// */
// names: string[];
// /**
// * List of level
// *
// * This is NOT the same as in the flamebearer
// * that we receive from the server.
// * As in there are some transformations required
// * (see deltaDiffWrapper)
// */
// levels: number[][];
// numTicks: number;
// maxSelf: number;

// /**
// * Sample Rate, used in text information
// */
// sampleRate: number;
// units: Units;

// spyName: SpyName;
// // format: 'double' | 'single';
// // leftTicks?: number;
// // rightTicks?: number;
// } & addTicks;

const graphvizPane = (() => {
// TODO(@petethepig): I don't understand what's going on with types here
// need to fix at some point
const flamebearer = this.state.flamebearer as ShamefulAny;
// flamebearer
const dot =
flamebearer.metadata?.format && flamebearer.flamebearer?.levels
? toGraphviz(flamebearer)
: null;

// Graphviz doesn't update position and scale value on rerender
// so image sometimes moves out of the screen
// to fix it we remounting graphViz component by updating key
const key = `graphviz-pane-${
flamebearer?.appName || String(new Date().valueOf())
}`;

return (
<div className={styles.graphVizPane} key={key}>
{dot ? (
<Graphviz
// options https://github.com/magjac/d3-graphviz#supported-options
options={{
zoom: true,
width: '150%',
height: '100%',
scale: 1,
// 'true' by default, but causes warning
// https://github.com/magjac/d3-graphviz/blob/master/README.md#defining-the-hpcc-jswasm-script-tag
useWorker: false,
}}
dot={dot}
/>
) : (
<div>NO DATA</div>
)}
</div>
);
})();

const dataUnavailable =
!this.state.flamebearer || this.state.flamebearer.names.length <= 1;
const panes = decidePanesOrder(
this.state.view,
flameGraphPane,
tablePane,
sandwichPane
sandwichPane,
graphvizPane
);

return (
Expand Down Expand Up @@ -577,7 +667,8 @@ function decidePanesOrder(
view: FlamegraphRendererState['view'],
flamegraphPane: JSX.Element | null,
tablePane: JSX.Element,
sandwichPane: JSX.Element
sandwichPane: JSX.Element,
graphvizPane: JSX.Element
) {
switch (view) {
case 'table': {
Expand All @@ -593,6 +684,10 @@ function decidePanesOrder(
case 'both': {
return [tablePane, flamegraphPane];
}

case 'graphviz': {
return [graphvizPane];
}
default: {
throw new Error(`Invalid view '${view}'`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,59 @@
text-align: right;
}
}

.graphVizPane {
display: flex;
flex: 1;

// hacky better than !important
&#{&} {
margin-right: 0;
border: 1px solid var(--ps-ui-border);
}

div[id^='graphviz'] {
width: 100%;
overflow: hidden;

:global {
.graph > polygon {
// graphviz overlay
fill: none;
}

.node {
polygon {
// node box
// stroke: var(--ps-fl-toolbar-btn-bg);
}

text[text-anchor='middle'] {
// node caption
// fill: var(--ps-toolbar-icon-color);
}
}

.edge {
text[text-anchor='middle'] {
// edge caption
fill: var(--ps-toolbar-icon-color);
// fill: red;
}

a {
path {
// arrow body
// stroke: var(--ps-fl-toolbar-btn-bg);
}

polygon {
// arrow head
// stroke: var(--ps-fl-toolbar-btn-bg);
// fill: var(--ps-fl-toolbar-btn-bg);
}
}
}
}
}
}
9 changes: 8 additions & 1 deletion packages/pyroscope-flamegraph/src/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React, {
import classNames from 'classnames/bind';
import { faUndo } from '@fortawesome/free-solid-svg-icons/faUndo';
import { faCompressAlt } from '@fortawesome/free-solid-svg-icons/faCompressAlt';
import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons/faProjectDiagram';
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons/faEllipsisV';
import { Maybe } from 'true-myth';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Expand Down Expand Up @@ -182,6 +183,7 @@ const Toolbar = memo(
),
width: TOOLBAR_SQUARE_WIDTH + DIVIDER_WIDTH,
};

const viewSectionItem = enableChangingDisplay
? {
el: (
Expand All @@ -192,7 +194,7 @@ const Toolbar = memo(
/>
),
// sandwich view is hidden in diff view
width: TOOLBAR_SQUARE_WIDTH * (flamegraphType === 'single' ? 4 : 3), // 1px is to display divider
width: TOOLBAR_SQUARE_WIDTH * (flamegraphType === 'single' ? 5 : 3), // 1px is to display divider
}
: null;
const exportDataItem = isValidElement(ExportData)
Expand Down Expand Up @@ -403,6 +405,11 @@ const getViewOptions = (
Icon: FlamegraphIcon,
},
{ label: 'Sandwich', value: 'sandwich', Icon: SandwichIcon },
{
label: 'GraphViz',
value: 'graphviz',
Icon: () => <FontAwesomeIcon icon={faProjectDiagram} />,
},
]
: [
{ label: 'Table', value: 'table', Icon: TableIcon },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ exports[`ProfileHeader should render toolbar correctly 1`] = `
</div>
<div
class="item"
style="width: 176px;"
style="width: 220px;"
>
<div
class="viewType"
Expand Down Expand Up @@ -264,6 +264,29 @@ exports[`ProfileHeader should render toolbar correctly 1`] = `
</g>
</svg>
</button>
<button
aria-label="GraphViz"
class="button default toggleViewButton noIcon"
data-mui-internal-clone-element="true"
data-testid="graphviz"
type="button"
>
<svg
aria-hidden="true"
class="svg-inline--fa fa-project-diagram fa-w-20 "
data-icon="project-diagram"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M384 320H256c-17.67 0-32 14.33-32 32v128c0 17.67 14.33 32 32 32h128c17.67 0 32-14.33 32-32V352c0-17.67-14.33-32-32-32zM192 32c0-17.67-14.33-32-32-32H32C14.33 0 0 14.33 0 32v128c0 17.67 14.33 32 32 32h95.72l73.16 128.04C211.98 300.98 232.4 288 256 288h.28L192 175.51V128h224V64H192V32zM608 0H480c-17.67 0-32 14.33-32 32v128c0 17.67 14.33 32 32 32h128c17.67 0 32-14.33 32-32V32c0-17.67-14.33-32-32-32z"
fill="currentColor"
/>
</svg>
</button>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit ca855d2

Please sign in to comment.