Skip to content

Commit

Permalink
phase option, closes davidmarkclements#90
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmarkclements committed Feb 6, 2018
1 parent 55dd175 commit bd575b6
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 22 deletions.
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# v3.2.0
* introduce `--phase` option
* change `--delay` from `300` to `0` - not a breaking change
because `--phase` provides the same result (stripping module loading stacks)
far more reliably.

# v3.1.0

* enhance `--output-dir` option with interpolation feature
Expand Down
3 changes: 3 additions & 0 deletions cli-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@
"delay": {
"type": "number"
},
"phase": {
"type": "number"
},
"d": {
"type": "number"
},
Expand Down
5 changes: 3 additions & 2 deletions cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function cmd (argv, banner = defaultBanner) {
var args = minimist(argv, {
stopEarly: true,
'--': true,
number: ['delay'],
number: ['delay', 'phase'],
boolean: [
'open', 'version', 'help', 'quiet',
'silent', 'jsonStacks', 'svg', 'traceInfo',
Expand Down Expand Up @@ -52,7 +52,8 @@ function cmd (argv, banner = defaultBanner) {
collectOnly: 'collect-only'
},
default: {
delay: 300
delay: 0,
phase: 2
}
})

Expand Down
7 changes: 6 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const {
silence,
stacksToFlamegraph,
createLoggers,
noop
noop,
phases
} = require('./lib/util')

// for thus is how it is pronounced:
Expand All @@ -24,6 +25,9 @@ function zeroEks (args, binary, cb) {
args.status = status
args.ee = new EventEmitter()
if (cb) cb = once(cb)

args.mapFrames = args.mapFrames || phases[args.phase]

isSudo(function (sudo) {
switch (process.platform) {
case 'linux':
Expand All @@ -46,6 +50,7 @@ zeroEks.stacksToFlamegraph = (args, cb) => {
args.log = log
args.status = status
args.ee = new EventEmitter()
args.mapFrames = args.mapFrames || phases[args.phase]
stacksToFlamegraph(args)
if (typeof cb !== 'function') return
args.ee.on('done', cb)
Expand Down
31 changes: 18 additions & 13 deletions lib/stack-convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,33 @@ var eos = require('end-of-stream')
var through = require('through2')
var profLabel = process.platform === 'darwin' ? 'profile-1ms' : 'cpu-clock'
var debug = require('debug')('0x:stack-convert')
function Node (name) {
function Node (name, mapFrames) {
this.name = name
this.value = 0
this.top = 0
this.children = {}
this.mapFrames = mapFrames || ((frames, instance) => frames)
}

Node.prototype.add = function (frames, value, topper) {
Node.prototype.add = function (frames, value, topper, index = 0) {
frames = this.mapFrames(frames, this)

// stops the base block from not being flush with the flamegraph
if (frames === false || frames.length === 1 && frames[0] === profLabel) return

this.value += value
if (frames && frames.length > 0) {
var head = frames[0]
if (frames && frames.length - index > 0) {
var head = frames[index]
var child = this.children[head]

if (!child) {
child = new Node(head)
child = new Node(head, this.mapFrames)
this.children[head] = child
}

if (head === topper) child.top += 1

frames.splice(0, 1)
child.add(frames, value, topper)
child.add(frames, value, topper, index + 1)
}
}

Expand All @@ -47,8 +52,8 @@ Node.prototype.serialize = function () {
return res
}

function Profile () {
this.samples = new Node('root')
function Profile (mapFrames) {
this.samples = new Node('root', mapFrames)
this.stack = null
this.name = profLabel
}
Expand Down Expand Up @@ -80,11 +85,11 @@ Profile.prototype.closeStack = function () {
this.name = profLabel
}

function stream () {
function stream (mapFrames) {
var stackOpenRx = /(.+):(.+): ?$/
var stackCloseRx = /^$/g
var commentRx = /^#/g
var profile = new Profile()
var profile = new Profile(mapFrames)

var s = through(function (line, enc, cb) {
if (commentRx.exec(line)) return cb()
Expand Down Expand Up @@ -112,8 +117,8 @@ function stream () {
return s
}

module.exports = function convert (cb) {
var s = stream()
module.exports = function convert (mapFrames, cb) {
var s = stream(mapFrames)
s.on('pipe', function (src) {
eos(src, function () {
var samples = s.profile.samples
Expand Down
28 changes: 24 additions & 4 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ const debug = require('debug')('0x')
const sll = require('single-line-log')
const launch = require('opn')

const phases = [
(frames) => frames,
(frames) => {
var moduleRunMain = frames.find((frame) => /Module.runMain module.js/.test(frame))
if (moduleRunMain) {
const startupIndex = frames.findIndex((frame) => /startup bootstrap_node/.test(frame))
if (startupIndex !== -1) frames.splice(startupIndex, 1)
return frames
}

var startup = frames.find((frame) => /startup bootstrap_node/.test(frame))
if (startup) return false
return frames
},
(frames) => {
if (frames.find((frame) => /startup bootstrap_node/.test(frame))) return false
return frames
}
]

module.exports = {
determineOutputDir: determineOutputDir,
ensureDirExists: ensureDirExists,
Expand All @@ -29,7 +49,8 @@ module.exports = {
isSudo: isSudo,
noop: noop,
createLoggers: createLoggers,
stacksToFlamegraph
stacksToFlamegraph,
phases
}

function createLoggers (args) {
Expand Down Expand Up @@ -150,12 +171,12 @@ function stacksToFlamegraphStream (args, {pid, folder}, cb, preClear) {
const {
log, tiers, langs, theme, name, open,
exclude, include, jsonStacks, svg, ee,
collectOnly, visualizeOnly
collectOnly, visualizeOnly, mapFrames
} = args


debug('begin rendering')
return pumpify(split(), convert(function (err, json) {
return pumpify(split(), convert(mapFrames, function (err, json) {
if (err) return ee.emit('error', err)
debug('converted stacks to intermediate format')
var title = args.title
Expand Down Expand Up @@ -254,4 +275,3 @@ function isSudo (cb) {
}

function noop () {}

30 changes: 29 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,29 @@ outputs to a file `{name}.html` in the current folder.
Generates an `flamegraph.svg` file in the artifact output directory,
in addition to the `flamegraph.html` file.

### --phase

Stage in initialization to begin aggregating stacks.

**Phase 0** visualizes from the very start, this includes bootstrapping
stacks and loading the application module tree (these can dominate the flamegraph).

**Phase 1** excludes core bootstrapping stacks, except the end of the boostrapping process
where the application module tree is loaded from the entry point.

**Phase 2** excludes all initialization, this renders the most pragmatic flamegraph for most
use cases.

Default: 2

### --delay | -d

Milliseconds. Delay before tracing begins (or before stacks are processed in the Linux case), allows us to ignore
initialisation stacks (e.g. module loading).

Example: `0x -d 2000 my-app.js`

Default: 300
Default: 0

### --langs | -l

Expand Down Expand Up @@ -485,6 +500,19 @@ See [`--output-html`](#--output-html---f)

See [`--title`](#--title)

#### `phase` (number)

See [`--phase`](#--phase)

#### `mapFrames` (function)

Will override phase. A custom mapping function that receives
an array of frames and an instance of the Profiler (see `stack-convert.js`).

Takes the form `(frames, profiler) => Array|false`. Return false to remove
the whole stack from the output, or return a modified array to change
the output.

#### `delay` (number)

See [`--delay`](#--delay---d)
Expand Down
14 changes: 13 additions & 1 deletion usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,22 @@
--open | -o Automatically open after finishing
Default: false

--phase Stage in initialization to begin aggregating
stacks. Phase 0 visualizes from the very start,
this includes bootstrapping stacks and loading
the application module tree (these can dominate
the flamegraph). Phase 1 excludes core bootstrapping
stacks, except the end of the boostrapping process
where the application module tree is loaded from
the entry point. Phase 2 excludes all initialization,
this renders the most pragmatic flamegraph for most
use cases.
Default: 2

--delay | -d Delay before tracing begins, allows us to ignore
initialisation stacks (e.g. module loading).
Milliseconds
Default: 300
Default: 0

--output-dir | -D Specify artifact output directory.
Template variables {outputDir}, {pid}, {timestamp}, {cwd}
Expand Down

0 comments on commit bd575b6

Please sign in to comment.