flamegraph
Version:
Generates flamegraphs with Node.js or in the browser
1,263 lines (1,105 loc) • 117 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.flamegraph = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
var svg = require('./lib/svg')
var defaultOpts = require('./lib/default-opts')
var defaultOptsMeta = require('./lib/default-opts-meta')
var cpuprofilify = require('cpuprofilify')
var cpuprofileProcessor = require('./lib/cpuprofile-processor')
function processCpuProfile(cpuprofile, opts) {
return cpuprofileProcessor(cpuprofile, opts).process()
}
function fromCpuProfile(cpuprofile, opts) {
var processed = processCpuProfile(cpuprofile, opts)
return svg(processed, opts)
}
exports = module.exports =
/**
* Converts an array of call graph lines into an svg document.
*
* @name flamegraph
* @function
* @param {Array.<string>} arr input lines to render svg for
* @param {Object} opts objects that affect the visualization
* @param {Object} opts.profile options passed to cpuprofilify @see [cpuprofilify.convert params](https://github.com/thlorenz/cpuprofilify#parameters)
* @param {string} opts.fonttype type of font to use default: `'Verdana'`
* @param {number} opts.fontsize base text size default: `12`
* @param {number} opts.imagewidth max width, pixels default: `1200`
* @param {number} opts.frameheight max height is dynamic default: `16.0`
* @param {number} opts.fontwidth avg width relative to fontsize default: `0.59`
* @param {number} opts.minwidth min function width, pixels default: `0.1`
* @param {string} opts.countname what are the counts in the data? default: `'samples'`
* @param {string} opts.colors color theme default: `'hot'`
* @param {string} opts.bgcolor1 background color gradient start default: `'#eeeeee'`
* @param {string} opts.bgcolor2 background color gradient stop default: `'#eeeeb0'`
* @param {number} opts.timemax (override the) sum of the counts default: `Infinity`
* @param {number} opts.factor factor to scale counts by default: `1`
* @param {boolean} opts.hash color by function name default: `true`
* @param {string} opts.titletext centered heading default: `'Flame Graph'`
* @param {string} opts.nametype what are the names in the data? default: `'Function:'`
* @return {string} svg the rendered svg
*/
function flamegraph(arr, opts) {
var profile
if (!Array.isArray(arr)) throw new TypeError('First arg needs to be an array of lines.')
opts = opts || {}
try {
profile = cpuprofilify().convert(arr, opts.profile)
} catch (err) {
// not a valid input to cpuprofilify -- maybe it's an actual cpuprofile already?
try {
profile = JSON.parse(arr.join('\n'))
} catch (parseErr) {
// if not throw the original cpuprofilify error
throw err
}
}
// at this point we have a cpuprofile
return fromCpuProfile(profile, opts)
}
exports.svg = svg
exports.defaultOpts = defaultOpts
exports.defaultOptsMeta = defaultOptsMeta
exports.processCpuProfile = processCpuProfile
exports.fromCpuProfile = fromCpuProfile
},{"./lib/cpuprofile-processor":4,"./lib/default-opts":6,"./lib/default-opts-meta":5,"./lib/svg":9,"cpuprofilify":15}],2:[function(require,module,exports){
var format = require('util').format
function scalarReverse(s) {
return s.split('').reverse().join('')
}
function nameHash(name) {
// Generate a vector hash for the name string, weighting early over
// later characters. We want to pick the same colors for function
// names across different flame graphs.
var vector = 0
var weight = 1
var max = 1
var mod = 10
var ord
// if module name present, trunc to 1st char
name = name.replace(/.(.*?)`/, '')
var splits = name.split('')
for (var i = 0; i < splits.length; i++) {
ord = splits[i].charCodeAt(0) % mod
vector += (ord / (mod++ - 1)) * weight
max += weight
weight *= 0.70
if (mod > 12) break
}
return (1 - vector / max)
}
function color(type, hash, name) {
var v1, v2, v3, r, g, b
if (!type) return 'rgb(0, 0, 0)'
if (hash) {
v1 = nameHash(name)
v2 = v3 = nameHash(scalarReverse(name))
} else {
v1 = Math.random() + 1
v2 = Math.random() + 1
v3 = Math.random() + 1
}
switch (type) {
case 'hot':
r = 205 + Math.round(50 * v3)
g = 0 + Math.round(230 * v1)
b = 0 + Math.round(55 * v2)
return format('rgb(%s, %s, %s)', r, g, b)
case 'mem':
r = 0
g = 190 + Math.round(50 * v2)
b = 0 + Math.round(210 * v1)
return format('rgb(%s, %s, %s)', r, g, b)
case 'io':
r = 80 + Math.round(60 * v1)
g = r
b = 190 + Math.round(55 * v2)
return format('rgb(%s, %s, %s)', r, g, b)
default:
throw new Error('Unknown type ' + type)
}
}
module.exports =
/**
* Maps a function name to a color, while trying to create same colors for similar functions.
*
* @name colorMap
* @function
* @private
* @param {Object.<string, string>} paletteMap current map of colors `func: color`
* @param {string} colorTheme theme of colors to be used `hot | mem | io`
* @param {boolean} hash if true colors will be created from name hash, otherwise they are random
* @param {string} func the function name for which to select a color
* @return {string} containing an rgb color, i.e. `'rgb(1, 2, 3)'`
*/
function colorMap(paletteMap, colorTheme, hash, func) {
if (paletteMap[func]) return paletteMap[func]
paletteMap[func] = color(colorTheme, hash, func)
return paletteMap[func]
}
},{"util":14}],3:[function(require,module,exports){
var xtend = require('xtend')
var format = require('util').format
var colorMap = require('./color-map')
function oneDecimal(x) {
return (Math.round(x * 10) / 10)
}
function htmlEscape(s) {
return s
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
}
module.exports =
/**
* Extracts a context object from the parsed callgraph @see `stackparse.js`.
* This context can then be used to generate the svg file via a template.
*
* @name contextify
* @private
* @function
* @param {Object} parsed nodes
* @param {Object} opts options that affect visual and how the nodes are filtered
*/
// Contextifier proto
function contextify(parsed, opts) {
var time = parsed.time
var timeMax = opts.timemax
var ypadTop = opts.fontsize * 4 // pad top, include title
var ypadBottom = opts.fontsize * 2 + 10 // pad bottom, include labels
var xpad = 10 // pad left and right
var depthMax = 0
var frameHeight = opts.frameheight
var paletteMap = {}
if (timeMax < time && timeMax / time > 0.02) {
console.error('Specified timemax %d is less than actual total %d, so it will be ignored', timeMax, time)
timeMax = Infinity
}
timeMax = Math.min(time, timeMax)
var widthPerTime = (opts.imagewidth - 2 * xpad) / timeMax
var minWidthTime = opts.minwidth / widthPerTime
function markNarrowBlocks(nodes) {
function mark(k) {
var val = parsed.nodes[k]
if (typeof val.stime !== 'number') throw new Error('Missing start for ' + k)
if ((val.etime - val.stime) < minWidthTime) {
val.narrow = true
return
}
val.narrow = false
depthMax = Math.max(val.depth, depthMax)
}
Object.keys(nodes).forEach(mark)
}
// NodeProcessor proto
function processNode(node) {
var func = node.func
var depth = node.depth
var etime = node.etime
var stime = node.stime
var factor = opts.factor
var countName = opts.countname
var isRoot = !func.length && depth === 0
if (isRoot) etime = timeMax
var samples = Math.round((etime - stime * factor) * 10) / 10
var samplesTxt = samples.toLocaleString()
var pct
var pctTxt
var escapedFunc
var name
var sampleInfo
if (isRoot) {
name = 'all'
sampleInfo = format('(%s %s, 100%)', samplesTxt, countName)
} else {
pct = Math.round((100 * samples) / (timeMax * factor) * 10) / 10
pctTxt = pct.toLocaleString()
escapedFunc = htmlEscape(func)
name = escapedFunc
sampleInfo = format('(%s %s), %s%%)', samplesTxt, countName, pctTxt)
}
var x1 = oneDecimal(xpad + stime * widthPerTime)
var x2 = oneDecimal(xpad + etime * widthPerTime)
var y1 = oneDecimal(imageHeight - ypadBottom - (depth + 1) * frameHeight + 1)
var y2 = oneDecimal(imageHeight - ypadBottom - depth * frameHeight)
var chars = (x2 - x1) / (opts.fontsize * opts.fontwidth)
var showText = false
var text
if (chars >= 3) { // enough room to display function name?
showText = true
text = func.slice(0, chars)
if (chars < func.length) text = text.slice(0, chars - 2) + '..'
text = htmlEscape(text)
}
return {
name : name
, search : name.toLowerCase()
, samples : sampleInfo
, rect_x : x1
, rect_y : y1
, rect_w : x2 - x1
, rect_h : y2 - y1
, rect_fill : colorMap(paletteMap, opts.colors, opts.hash, func)
, text : text
, text_x : x1 + (showText ? 3 : 0)
, text_y : 3 + (y1 + y2) / 2
, narrow : node.narrow
, func : htmlEscape(func)
}
}
function processNodes(nodes) {
var keys = Object.keys(nodes)
var acc = new Array(keys.length)
for (var i = 0; i < keys.length; i++) {
acc[i] = processNode(nodes[keys[i]])
}
return acc
}
markNarrowBlocks(parsed.nodes)
var imageHeight = (depthMax * frameHeight) + ypadTop + ypadBottom
var ctx = xtend(opts, {
imageheight : imageHeight
, xpad : xpad
, titleX : opts.imagewidth / 2
, detailsY : imageHeight - (frameHeight / 2)
})
ctx.nodes = processNodes(parsed.nodes)
return ctx
}
},{"./color-map":2,"util":14,"xtend":38}],4:[function(require,module,exports){
function funcName(node) {
var n = node.functionName
if (node.url) n += ' ' + node.url + ':' + node.lineNumber
return n
}
function byFramesLexically(a, b) {
var i = 0
var framesA = a.frames
var framesB = b.frames
while (true) {
if (!framesA[i]) return -1
if (!framesB[i]) return 1
if (framesA[i] < framesB[i]) return -1
if (framesB[i] < framesA[i]) return 1
i++
}
}
function sort(functions) {
return functions.sort(byFramesLexically)
}
function CpuProfileProcessor(cpuprofile) {
if (!(this instanceof CpuProfileProcessor)) return new CpuProfileProcessor(cpuprofile)
this._profile = cpuprofile
this._paths = []
this._time = 0
this._last = []
this._tmp = {}
this._nodes = {}
}
var proto = CpuProfileProcessor.prototype
module.exports = CpuProfileProcessor
proto._explorePaths = function _explorePaths(node, stack) {
stack.push(funcName(node))
if (node.hitCount) this._paths.push({ frames: stack.slice(), hitCount: node.hitCount })
for (var i = 0; i < node.children.length; i++) {
this._explorePaths(node.children[i], stack)
}
stack.pop()
}
proto._flow = function _flow(frames) {
var lenLast = this._last.length - 1
var lenFrames = frames.length - 1
var i
var lenSame
var k
for (i = 0; i <= lenLast; i++) {
if (i > lenFrames) break
if (this._last[i] !== frames[i]) break
}
lenSame = i
for (i = lenLast; i >= lenSame; i--) {
k = this._last[i] + ';' + i
// a unique ID is constructed from "func;depth;etime"
// func-depth isn't unique, it may be repeated later.
this._nodes[k + ';' + this._time] = { func: this._last[i], depth: i, etime: this._time, stime: this._tmp[k].stime }
this._tmp[k] = null
}
for (i = lenSame; i <= lenFrames; i++) {
k = frames[i] + ';' + i
this._tmp[k] = { stime: this._time }
}
}
proto._processPath = function _processPath(path) {
this._flow(path.frames)
this._time += path.hitCount
this._last = path.frames
}
proto._processPaths = function _processPaths() {
sort(this._paths)
for (var i = 0; i < this._paths.length; i++) {
this._processPath(this._paths[i])
}
this._flow([])
}
proto.process = function process() {
this._explorePaths(this._profile.head, [])
this._processPaths()
return { nodes: this._nodes, time: this._time }
}
},{}],5:[function(require,module,exports){
/* eslint-disable standard/object-curly-even-spacing, comma-spacing */
module.exports = {
fonttype : { type : 'string' , description : 'Font Type' }
, fontsize : { type : 'range' , description : 'Font Size' , min: 6, max: 22, step: 0.1 }
, imagewidth : { type : 'range' , description : 'Image Width' , min: 200, max: 2400, step: 5 }
, frameheight : { type : 'range' , description : 'Frame Height', min: 6, max: 40, step: 0.1 }
, fontwidth : { type : 'range' , description : 'Font Width', min: 0.2, max: 1.0, step: 0.05 }
, minwidth : { type : 'range' , description : 'Min Function Width', min: 0.1, max: 30, step: 0.1 }
, countname : { type : 'string' , description : 'Count Name' }
, colors : { type : 'string' , description : 'Color Theme' }
, bgcolor1 : { type : 'color' , description : 'Gradient start' }
, bgcolor2 : { type : 'color' , description : 'Gradient stop' }
, timemax : { type : 'number' , description : 'Time Max' }
, factor : { type : 'number' , description : 'Scaling Factor' }
, hash : { type : 'boolean', description : 'Color by Function Name' }
, titlestring : { type : 'string' , description : 'Title' }
, nametype : { type : 'string' , description : 'Name' }
// turns on all internals inside profile: {}, passed to cpuprofilify
, internals: { type: 'checkbox' , description: 'Show Internals', checked: '' }
}
},{}],6:[function(require,module,exports){
module.exports = {
fonttype : 'Verdana' // font type
, fontsize : 12 // base text size
, imagewidth : 1200 // max width, pixels
, frameheight : 16.0 // max height is dynamic
, fontwidth : 0.59 // avg width relative to fontsize
, minwidth : 0.1 // min function width, pixels
, countname : 'samples' // what are the counts in the data?
, colors : 'hot' // color theme
, bgcolor1 : '#eeeeee' // background color gradient start
, bgcolor2 : '#eeeeb0' // background color gradient stop
, timemax : Infinity // (override the) sum of the counts
, factor : 1 // factor to scale counts by
, hash : true // color by function name
, titletext : 'Flame Graph' // centered heading
, nametype : 'Function:' // what are the names in the data?
// below are not supported at this point
, palette : false // if we use consistent palettes (default off)
, palette_map : {} // palette map hash
, pal_file : 'palette.map' // palette map file name
, removenarrows : true // removes narrow functions instead of adding a 'hidden' class
, profile: {
shortStack : true
, unresolveds : false
, v8internals : false
, v8gc : true
, sysinternals : false
}
}
},{}],7:[function(require,module,exports){
// resolved via hbsfy transform
module.exports = require('./svg.hbs')
},{"./svg.hbs":8}],8:[function(require,module,exports){
// hbsfy compiled Handlebars template
var Handlebars = require('hbsfy/runtime');
module.exports = Handlebars.template({"1":function(depth0,helpers,partials,data,depths) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, lambda=this.lambda, buffer = "<g class=\"func_g "
+ escapeExpression(((helper = (helper = helpers['class'] || (depth0 != null ? depth0['class'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"class","hash":{},"data":data}) : helper)))
+ "\" onmouseover=\"typeof s === 'function' && s('";
stack1 = ((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += " ";
stack1 = ((helper = (helper = helpers.samples || (depth0 != null ? depth0.samples : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"samples","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += "')\" onmouseout=\"typeof c === 'function' && c()\" data-search=\"";
stack1 = ((helper = (helper = helpers.search || (depth0 != null ? depth0.search : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"search","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += "\" data-funcname=\"";
stack1 = ((helper = (helper = helpers.func || (depth0 != null ? depth0.func : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"func","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += "\">\n <title>";
stack1 = ((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += " ";
stack1 = ((helper = (helper = helpers.samples || (depth0 != null ? depth0.samples : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"samples","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += "</title>\n <rect x=\""
+ escapeExpression(((helper = (helper = helpers.rect_x || (depth0 != null ? depth0.rect_x : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_x","hash":{},"data":data}) : helper)))
+ "\" data-x=\""
+ escapeExpression(((helper = (helper = helpers.rect_x || (depth0 != null ? depth0.rect_x : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_x","hash":{},"data":data}) : helper)))
+ "\" y=\""
+ escapeExpression(((helper = (helper = helpers.rect_y || (depth0 != null ? depth0.rect_y : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_y","hash":{},"data":data}) : helper)))
+ "\" width=\""
+ escapeExpression(((helper = (helper = helpers.rect_w || (depth0 != null ? depth0.rect_w : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_w","hash":{},"data":data}) : helper)))
+ "\" data-width=\""
+ escapeExpression(((helper = (helper = helpers.rect_w || (depth0 != null ? depth0.rect_w : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_w","hash":{},"data":data}) : helper)))
+ "\" height=\""
+ escapeExpression(((helper = (helper = helpers.rect_h || (depth0 != null ? depth0.rect_h : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_h","hash":{},"data":data}) : helper)))
+ "\" data-height=\""
+ escapeExpression(((helper = (helper = helpers.rect_h || (depth0 != null ? depth0.rect_h : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_h","hash":{},"data":data}) : helper)))
+ "\" fill=\""
+ escapeExpression(((helper = (helper = helpers.rect_fill || (depth0 != null ? depth0.rect_fill : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"rect_fill","hash":{},"data":data}) : helper)))
+ "\" rx=\"2\" ry=\"2\"></rect>\n <text data-x=\""
+ escapeExpression(((helper = (helper = helpers.text_x || (depth0 != null ? depth0.text_x : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"text_x","hash":{},"data":data}) : helper)))
+ "\" x=\""
+ escapeExpression(((helper = (helper = helpers.text_x || (depth0 != null ? depth0.text_x : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"text_x","hash":{},"data":data}) : helper)))
+ "\" y=\""
+ escapeExpression(((helper = (helper = helpers.text_y || (depth0 != null ? depth0.text_y : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"text_y","hash":{},"data":data}) : helper)))
+ "\" font-size=\""
+ escapeExpression(lambda((depths[1] != null ? depths[1].fontsize : depths[1]), depth0))
+ "\" font-family=\""
+ escapeExpression(lambda((depths[1] != null ? depths[1].fonttype : depths[1]), depth0))
+ "\" fill=\"rgb(0,0,0)\">";
stack1 = ((helper = (helper = helpers.text || (depth0 != null ? depth0.text : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"text","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
return buffer + "</text>\n</g>\n";
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data,depths) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<?xml version=\"1.0\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n\n<svg version=\"1.1\" id=\"flamegraph-svg\" \n data-width=\""
+ escapeExpression(((helper = (helper = helpers.imagewidth || (depth0 != null ? depth0.imagewidth : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imagewidth","hash":{},"data":data}) : helper)))
+ "\" width=\""
+ escapeExpression(((helper = (helper = helpers.imagewidth || (depth0 != null ? depth0.imagewidth : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imagewidth","hash":{},"data":data}) : helper)))
+ "\" \n height=\""
+ escapeExpression(((helper = (helper = helpers.imageheight || (depth0 != null ? depth0.imageheight : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imageheight","hash":{},"data":data}) : helper)))
+ "\" data-height=\""
+ escapeExpression(((helper = (helper = helpers.imageheight || (depth0 != null ? depth0.imageheight : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imageheight","hash":{},"data":data}) : helper)))
+ "\"\n onload=\"init(evt)\" \n viewBox=\"0 0 "
+ escapeExpression(((helper = (helper = helpers.imagewidth || (depth0 != null ? depth0.imagewidth : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imagewidth","hash":{},"data":data}) : helper)))
+ " "
+ escapeExpression(((helper = (helper = helpers.imageheight || (depth0 != null ? depth0.imageheight : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imageheight","hash":{},"data":data}) : helper)))
+ "\" \n xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<defs>\n <linearGradient id=\"background\" y1=\"0\" y2=\"1\" x1=\"0\" x2=\"0\">\n <stop stop-color=\""
+ escapeExpression(((helper = (helper = helpers.bgcolor1 || (depth0 != null ? depth0.bgcolor1 : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"bgcolor1","hash":{},"data":data}) : helper)))
+ "\" offset=\"5%\" />\n <stop stop-color=\""
+ escapeExpression(((helper = (helper = helpers.bgcolor2 || (depth0 != null ? depth0.bgcolor2 : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"bgcolor2","hash":{},"data":data}) : helper)))
+ "\" offset=\"95%\" />\n </linearGradient>\n</defs>\n<style type=\"text/css\">\n .func_g:hover { stroke:black; stroke-width:0.5; }\n</style>\n<script type=\"text/javascript\">\n var details;\n function init(evt) { details = document.getElementById(\"details\").firstChild; }\n function s(info) { details.nodeValue = \""
+ escapeExpression(((helper = (helper = helpers.nametype || (depth0 != null ? depth0.nametype : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nametype","hash":{},"data":data}) : helper)))
+ ": \" + info; }\n function c() { details.nodeValue = ' '; }\n</script>\n\n<rect x=\"0.0\" y=\"0\" id=\"svg-background\" width=\""
+ escapeExpression(((helper = (helper = helpers.imagewidth || (depth0 != null ? depth0.imagewidth : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imagewidth","hash":{},"data":data}) : helper)))
+ "\" height=\""
+ escapeExpression(((helper = (helper = helpers.imageheight || (depth0 != null ? depth0.imageheight : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"imageheight","hash":{},"data":data}) : helper)))
+ "\" fill=\"url(#background)\" />\n<!--<text text-anchor=\"middle\" x=\""
+ escapeExpression(((helper = (helper = helpers.titleX || (depth0 != null ? depth0.titleX : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"titleX","hash":{},"data":data}) : helper)))
+ "\" y=\"24\" font-size=\"17\" font-family=\""
+ escapeExpression(((helper = (helper = helpers.fonttype || (depth0 != null ? depth0.fonttype : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"fonttype","hash":{},"data":data}) : helper)))
+ "\" fill=\"rgb(0,0,0)\">";
stack1 = ((helper = (helper = helpers.titletext || (depth0 != null ? depth0.titletext : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"titletext","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
buffer += "</text>-->\n<text text-anchor=\"left\" x=\""
+ escapeExpression(((helper = (helper = helpers.xpad || (depth0 != null ? depth0.xpad : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"xpad","hash":{},"data":data}) : helper)))
+ "\" y=\""
+ escapeExpression(((helper = (helper = helpers.detailsY || (depth0 != null ? depth0.detailsY : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"detailsY","hash":{},"data":data}) : helper)))
+ "\" font-size=\""
+ escapeExpression(((helper = (helper = helpers.fontsize || (depth0 != null ? depth0.fontsize : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"fontsize","hash":{},"data":data}) : helper)))
+ "\" font-family=\""
+ escapeExpression(((helper = (helper = helpers.fonttype || (depth0 != null ? depth0.fonttype : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"fonttype","hash":{},"data":data}) : helper)))
+ "\" fill=\"rgb(0,0,0)\" id=\"details\"> </text>\n\n";
stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.nodes : depth0), {"name":"each","hash":{},"fn":this.program(1, data, depths),"inverse":this.noop,"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer + "\n</svg>\n";
},"useData":true,"useDepths":true});
},{"hbsfy/runtime":36}],9:[function(require,module,exports){
var xtend = require('xtend')
var contextify = require('./contextify')
var svgTemplate = require('./svg-template')
var defaultOpts = require('./default-opts')
function narrowify(context, opts) {
function processNode(n) {
n.class = n.narrow ? 'hidden' : ''
}
function filterNode(n) {
return !n.narrow
}
if (opts.removenarrows) context.nodes = context.nodes.filter(filterNode)
else context.nodes.forEach(processNode)
}
module.exports =
/**
* Creates a context from a call graph that has been collapsed (`stackcollapse-*`) and renders svg from it.
*
* @name flamegraph::svg
* @function
* @param {Array.<string>} collapsedLines callgraph that has been collapsed
* @param {Object} opts options
* @return {string} svg
*/
function svg(processedCpuProfile, opts) {
opts = xtend(defaultOpts, opts)
var context = contextify(processedCpuProfile, opts)
narrowify(context, opts)
return svgTemplate(context)
}
},{"./contextify":3,"./default-opts":6,"./svg-template":7,"xtend":38}],10:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var objectCreate = Object.create || objectCreatePolyfill
var objectKeys = Object.keys || objectKeysPolyfill
var bind = Function.prototype.bind || functionBindPolyfill
function EventEmitter() {
if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
this._events = objectCreate(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;
var hasDefineProperty;
try {
var o = {};
if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
hasDefineProperty = o.x === 0;
} catch (err) { hasDefineProperty = false }
if (hasDefineProperty) {
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
// check whether the input is a positive number (whose value is zero or
// greater and not a NaN).
if (typeof arg !== 'number' || arg < 0 || arg !== arg)
throw new TypeError('"defaultMaxListeners" must be a positive number');
defaultMaxListeners = arg;
}
});
} else {
EventEmitter.defaultMaxListeners = defaultMaxListeners;
}
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || isNaN(n))
throw new TypeError('"n" argument must be a positive number');
this._maxListeners = n;
return this;
};
function $getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return $getMaxListeners(this);
};
// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
if (isFn)
handler.call(self);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self);
}
}
function emitOne(handler, isFn, self, arg1) {
if (isFn)
handler.call(self, arg1);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1);
}
}
function emitTwo(handler, isFn, self, arg1, arg2) {
if (isFn)
handler.call(self, arg1, arg2);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2);
}
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
if (isFn)
handler.call(self, arg1, arg2, arg3);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2, arg3);
}
}
function emitMany(handler, isFn, self, args) {
if (isFn)
handler.apply(self, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(self, args);
}
}
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, events;
var doError = (type === 'error');
events = this._events;
if (events)
doError = (doError && events.error == null);
else if (!doError)
return false;
// If there is no 'error' event listener then throw.
if (doError) {
if (arguments.length > 1)
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Unhandled "error" event. (' + er + ')');
err.context = er;
throw err;
}
return false;
}
handler = events[type];
if (!handler)
return false;
var isFn = typeof handler === 'function';
len = arguments.length;
switch (len) {
// fast cases
case 1:
emitNone(handler, isFn, this);
break;
case 2:
emitOne(handler, isFn, this, arguments[1]);
break;
case 3:
emitTwo(handler, isFn, this, arguments[1], arguments[2]);
break;
case 4:
emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
break;
// slower
default:
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
emitMany(handler, isFn, this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = target._events;
if (!events) {
events = target._events = objectCreate(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (!existing) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
} else {
// If we've already got an array, just append.
if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
}
// Check for listener leak
if (!existing.warned) {
m = $getMaxListeners(target);
if (m && m > 0 && existing.length > m) {
existing.warned = true;
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' "' + String(type) + '" listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit.');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
if (typeof console === 'object' && console.warn) {
console.warn('%s: %s', w.name, w.message);
}
}
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
switch (arguments.length) {
case 0:
return this.listener.call(this.target);
case 1:
return this.listener.call(this.target, arguments[0]);
case 2:
return this.listener.call(this.target, arguments[0], arguments[1]);
case 3:
return this.listener.call(this.target, arguments[0], arguments[1],
arguments[2]);
default:
var args = new Array(arguments.length);
for (var i = 0; i < args.length; ++i)
args[i] = arguments[i];
this.listener.apply(this.target, args);
}
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = bind.call(onceWrapper, state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = this._events;
if (!events)
return this;
list = events[type];
if (!list)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = objectCreate(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else
spliceOne(list, position);
if (list.length === 1)
events[type] = list[0];
if (events.removeListener)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (!events)
return this;
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (arguments.length === 0) {
this._events = objectCreate(null);
this._eventsCount = 0;
} else if (events[type]) {
if (--this._eventsCount === 0)
this._events = objectCreate(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = objectKeys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = objectCreate(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (!events)
return [];
var evlistener = events[type];
if (!evlistener)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};
// About 1.5x faster than the two-arg version of Array#splice().
function spliceOne(list, index) {
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
list[i] = list[k];
list.pop();
}
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function objectCreatePolyfill(proto) {
var F = function() {};
F.prototype = proto;
return new F;
}
function objectKeysPolyfill(obj) {
var keys = [];
for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
return k;
}
function functionBindPolyfill(context) {
var fn = this;
return function () {
return fn.apply(context, arguments);
};
}
},{}],11:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],12:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (