-
Notifications
You must be signed in to change notification settings - Fork 623
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enhancement/split flamegraphrenderer (#360)
* split flamegraph rendere into smaller components * use flamegraph renderer from external repo * use flamegraph as local component * disable lint some checks * fix paramsToObject function and tooltip position * add cypress test * fix textformat and color generation * adds a webpack config for grafana panel (#382) * improves license noticies * changes tooltip position to fixed * fixes a key in array bug * improves tooltip positioning Co-authored-by: Dmitry Filimonov <dmitry@pyroscope.io>
- Loading branch information
1 parent
2539b9c
commit 230699f
Showing
31 changed files
with
1,529 additions
and
879 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,9 @@ | ||
const { merge } = require("webpack-merge"); | ||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; | ||
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); | ||
|
||
const common = require("./webpack.common.js"); | ||
|
||
module.exports = merge(common, { | ||
mode: "development", | ||
plugins: [ | ||
new BundleAnalyzerPlugin() | ||
] | ||
plugins: [new BundleAnalyzerPlugin()], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const { merge } = require("webpack-merge"); | ||
|
||
const prod = require("./webpack.prod.js"); | ||
const path = require("path"); | ||
|
||
module.exports = merge(prod, { | ||
entry: { | ||
flamegraphComponent: "./webapp/javascript/components/FlameGraph/FlameGraphComponent/index.jsx", | ||
}, | ||
|
||
output: { | ||
publicPath: "", | ||
path: path.resolve(__dirname, "../../webapp/public/assets"), | ||
filename: "[name].js", | ||
clean: true, | ||
|
||
library: "pyroscope", | ||
libraryTarget: 'umd', | ||
umdNamedDefine: true | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
webapp/javascript/components/FlameGraph/FlameGraphComponent/color.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* eslint-disable camelcase */ | ||
import Color from "color"; | ||
import murmurhash3_32_gc from "./murmur3"; | ||
|
||
const colors = [ | ||
Color.hsl(24, 69, 60), | ||
Color.hsl(34, 65, 65), | ||
Color.hsl(194, 52, 61), | ||
Color.hsl(163, 45, 55), | ||
Color.hsl(211, 48, 60), | ||
Color.hsl(246, 40, 65), | ||
Color.hsl(305, 63, 79), | ||
Color.hsl(47, 100, 73), | ||
|
||
Color.rgb(183, 219, 171), | ||
Color.rgb(244, 213, 152), | ||
Color.rgb(112, 219, 237), | ||
Color.rgb(249, 186, 143), | ||
Color.rgb(242, 145, 145), | ||
Color.rgb(130, 181, 216), | ||
Color.rgb(229, 168, 226), | ||
Color.rgb(174, 162, 224), | ||
Color.rgb(154, 196, 138), | ||
Color.rgb(242, 201, 109), | ||
Color.rgb(101, 197, 219), | ||
Color.rgb(249, 147, 78), | ||
Color.rgb(234, 100, 96), | ||
Color.rgb(81, 149, 206), | ||
Color.rgb(214, 131, 206), | ||
Color.rgb(128, 110, 183), | ||
]; | ||
|
||
export const defaultColor = Color.rgb(148, 142, 142); | ||
export const diffColorRed = Color.rgb(200, 0, 0); | ||
export const diffColorGreen = Color.rgb(0, 170, 0); | ||
|
||
export function colorBasedOnPackageName(name, a) { | ||
const hash = murmurhash3_32_gc(name); | ||
const colorIndex = hash % colors.length; | ||
const baseClr = colors[colorIndex]; | ||
return baseClr.alpha(a); | ||
} | ||
|
||
// assume: left >= 0 && Math.abs(diff) <= left so diff / left is in [0...1] | ||
// if left == 0 || Math.abs(diff) > left, we use the color of 100% | ||
export function colorBasedOnDiff(diff, left, a) { | ||
const v = | ||
!left || Math.abs(diff) > left ? 1 : 200 * Math.sqrt(Math.abs(diff / left)); | ||
if (diff >= 0) return Color.rgb(200, 200 - v, 200 - v).alpha(a); | ||
return Color.rgb(200 - v, 200, 200 - v).alpha(a); | ||
} | ||
|
||
export function colorGreyscale(v, a) { | ||
return Color.rgb(v, v, v).alpha(a); | ||
} |
170 changes: 170 additions & 0 deletions
170
webapp/javascript/components/FlameGraph/FlameGraphComponent/format.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* eslint-disable no-plusplus */ | ||
/* eslint-disable prefer-destructuring */ | ||
/* eslint-disable max-classes-per-file */ | ||
export function numberWithCommas(x) { | ||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | ||
} | ||
|
||
const suffixes = ["K", "M", "G", "T"]; | ||
|
||
export function shortNumber(x) { | ||
let suffix = ""; | ||
|
||
for (let i = 0; x > 1000 && i < suffixes.length; i++) { | ||
suffix = suffixes[i]; | ||
x /= 1000; | ||
} | ||
|
||
return Math.round(x).toString() + suffix; | ||
} | ||
|
||
export function formatPercent(ratio) { | ||
const percent = Math.round(10000 * ratio) / 100; | ||
return `${percent}%`; | ||
} | ||
|
||
const durations = [ | ||
[60, "minute"], | ||
[60, "hour"], | ||
[24, "day"], | ||
[30, "month"], | ||
[12, "year"], | ||
]; | ||
|
||
// this is a class and not a function because we can save some time by | ||
// precalculating divider and suffix and not doing it on each iteration | ||
export class DurationFormatter { | ||
constructor(maxDur) { | ||
this.divider = 1; | ||
this.suffix = "second"; | ||
for (let i = 0; i < durations.length; i++) { | ||
if (maxDur >= durations[i][0]) { | ||
this.divider *= durations[i][0]; | ||
maxDur /= durations[i][0]; | ||
this.suffix = durations[i][1]; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
format(samples, sampleRate) { | ||
let number = samples / sampleRate / this.divider; | ||
if (number >= 0 && number < 0.01) { | ||
number = "< 0.01"; | ||
} else if (number <= 0 && number > -0.01) { | ||
number = "< 0.01"; | ||
} else { | ||
number = number.toFixed(2); | ||
} | ||
return `${number} ${this.suffix}${number === 1 ? "" : "s"}`; | ||
} | ||
} | ||
|
||
const bytes = [ | ||
[1024, "KB"], | ||
[1024, "MB"], | ||
[1024, "GB"], | ||
[1024, "TB"], | ||
[1024, "PB"], | ||
]; | ||
|
||
export class BytesFormatter { | ||
constructor(maxBytes) { | ||
this.divider = 1; | ||
this.suffix = "bytes"; | ||
for (let i = 0; i < bytes.length; i++) { | ||
if (maxBytes >= bytes[i][0]) { | ||
this.divider *= bytes[i][0]; | ||
maxBytes /= bytes[i][0]; | ||
this.suffix = bytes[i][1]; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
format(samples, sampleRate) { | ||
let number = samples / this.divider; | ||
if (number >= 0 && number < 0.01) { | ||
number = "< 0.01"; | ||
} else if (number <= 0 && number > -0.01) { | ||
number = "< 0.01"; | ||
} else { | ||
number = number.toFixed(2); | ||
} | ||
return `${number} ${this.suffix}`; | ||
} | ||
} | ||
|
||
const objects = [ | ||
[1000, "K"], | ||
[1000, "M"], | ||
[1000, "G"], | ||
[1000, "T"], | ||
[1000, "P"], | ||
]; | ||
|
||
export class ObjectsFormatter { | ||
constructor(maxObjects) { | ||
this.divider = 1; | ||
this.suffix = ""; | ||
for (let i = 0; i < objects.length; i++) { | ||
if (maxObjects >= objects[i][0]) { | ||
this.divider *= objects[i][0]; | ||
maxObjects /= objects[i][0]; | ||
this.suffix = objects[i][1]; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
format(samples, sampleRate) { | ||
let number = samples / this.divider; | ||
if (number >= 0 && number < 0.01) { | ||
number = "< 0.01"; | ||
} else if (number <= 0 && number > -0.01) { | ||
number = "< 0.01"; | ||
} else { | ||
number = number.toFixed(2); | ||
} | ||
return `${number} ${this.suffix}`; | ||
} | ||
} | ||
|
||
export function getPackageNameFromStackTrace(spyName, stackTrace) { | ||
// TODO: actually make sure these make sense and add tests | ||
const regexpLookup = { | ||
default: /^(?<packageName>(.*\/)*)(?<filename>.*)(?<line_info>.*)$/, | ||
dotnetspy: /^(?<packageName>.+)\.(.+)\.(.+)\(.*\)$/, | ||
ebpfspy: /^(?<packageName>.+)$/, | ||
gospy: /^(?<packageName>(.*\/)*)(?<filename>.*)(?<line_info>.*)$/, | ||
phpspy: /^(?<packageName>(.*\/)*)(?<filename>.*\.php+)(?<line_info>.*)$/, | ||
pyspy: /^(?<packageName>(.*\/)*)(?<filename>.*\.py+)(?<line_info>.*)$/, | ||
rbspy: /^(?<packageName>(.*\/)*)(?<filename>.*\.rb+)(?<line_info>.*)$/, | ||
}; | ||
|
||
if (stackTrace.length === 0) { | ||
return stackTrace; | ||
} | ||
const regexp = regexpLookup[spyName] || regexpLookup.default; | ||
const fullStackGroups = stackTrace.match(regexp); | ||
if (fullStackGroups) { | ||
return fullStackGroups.groups.packageName; | ||
} | ||
return stackTrace; | ||
} | ||
|
||
export function getFormatter(max, sampleRate, units) { | ||
switch (units) { | ||
case "samples": | ||
return new DurationFormatter(max / sampleRate); | ||
case "objects": | ||
return new ObjectsFormatter(max); | ||
case "bytes": | ||
return new BytesFormatter(max); | ||
default: | ||
return new DurationFormatter(max / sampleRate); | ||
} | ||
} |
Oops, something went wrong.