UNPKG

flamegraph

Version:

Generates flamegraphs with Node.js or in the browser

1,263 lines (1,105 loc) 117 kB
(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, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') } 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 (