Skip to content

Commit

Permalink
--prof-only
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmarkclements committed Feb 11, 2018
1 parent 8355e97 commit 9017add
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 17 deletions.
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# v3.4.0
* introduce experimental `--prof-only` flag, generates flamegraph based on internal v8 profiling data, without performing kernel tracing at the same time (as `--prof-viz` does)

# v3.3.0

* introduce experimental `--prof-viz` flag, generates additional flamegraph based on internal v8 profiling data
* linux fixes & tidy up

# 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)
Expand Down
6 changes: 6 additions & 0 deletions cli-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@
"profViz": {
"type": "boolean"
},
"prof-only": {
"type": "boolean"
},
"profOnly": {
"type": "boolean"
},
"--": {
"type": "array",
"items": {}
Expand Down
14 changes: 10 additions & 4 deletions cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function cmd (argv, banner = defaultBanner) {
boolean: [
'open', 'version', 'help', 'quiet',
'silent', 'jsonStacks', 'svg', 'traceInfo',
'collectOnly', 'timestampProfiles', 'profViz'
'collectOnly', 'timestampProfiles', 'profViz', 'profOnly'
],
alias: {
silent: 's',
Expand All @@ -50,16 +50,22 @@ function cmd (argv, banner = defaultBanner) {
logOutput: 'log-output',
visualizeOnly: 'visualize-only',
collectOnly: 'collect-only',
profViz: 'prof-viz'
profViz: 'prof-viz',
profOnly: 'prof-only'
},
default: {
delay: 0,
phase: 2
}
})

if (args.profViz && process.version.substr(0, 3) === 'v6.') {
console.error('0x: The --prof-viz flag is only supported in Node 8 and above')
if ((args.profViz || args.profOnly) && process.version.substr(0, 3) === 'v6.') {
console.error('0x: The --prof-viz/--prof-only flag is only supported in Node 8 and above')
process.exit(1)
}

if (args.profViz && args.profOnly) {
console.error('\n 0x: --prof-viz and --prof-only cannot be used together')
process.exit(1)
}

Expand Down
24 changes: 13 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { sun, linux, windows } = require('./platform')
const { sun, linux, windows, v8 } = require('./platform')
const { execSync } = require('child_process')
const { EventEmitter } = require('events')
const once = require('once')
Expand All @@ -27,17 +27,19 @@ function zeroEks (args, binary, cb) {
if (cb) cb = once(cb)

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

const platform = args.profOnly ? 'v8' : process.platform

isSudo(function (sudo) {
switch (process.platform) {
case 'linux':
return linux(args, sudo, binary)
case 'win32':
return windows(args, sudo, binary)
default:
return sun(args, sudo, binary)
}
})
switch (platform) {
case 'v8':
return v8(args, binary)
case 'linux':
return isSudo((sudo) => linux(args, sudo, binary))
case 'win32':
return windows(args, sudo, binary)
default:
return isSudo((sudo) => sun(args, sudo, binary))
}
if (typeof cb !== 'function') return
args.ee.on('done', cb)
args.ee.on('error', cb)
Expand Down
5 changes: 4 additions & 1 deletion lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,13 @@ function v8ProfFlamegraph (args, {folder, pid}, cb) {
const isolateLogPath = path.join(folder, isolateLog)

fs.renameSync(path.join(args.workingDir, isolateLog), isolateLogPath)
const stacksOut = path.join(folder, 'v8-prof-stacks.' + pid + '.out')
const stacksOut = path.join(folder, args.profOnly ? 'stacks.' + pid + '.out' : 'v8-prof-stacks.' + pid + '.out')
const stream = fs.createWriteStream(stacksOut)
profLogConvert({pid, isolateLogPath, stream}, args)

stream.on('finish', () => {
if (args.profOnly) return cb && cb()

const fg = fs.openSync(path.join(folder, 'v8-prof-' + path.basename(determineHtmlPath(args, {pid, folder}))), 'w')
// the stdout pipe, redirect to a file works though
const sp = spawn(
Expand Down
3 changes: 2 additions & 1 deletion platform/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
const linux = require('./linux')
const sun = require('./sun')
const windows = require('./windows')
const v8 = require('./v8')

module.exports = { linux, sun, windows }
module.exports = { linux, sun, windows, v8 }
119 changes: 119 additions & 0 deletions platform/v8.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use strict'
const fs = require('fs')
const path = require('path')
const spawn = require('child_process').spawn
const pump = require('pump')
const split = require('split2')
const through = require('through2')
const debug = require('debug')('0x')

const {
determineOutputDir,
ensureDirExists,
stacksToFlamegraphStream,
tidy,
pathTo,
notFound,
v8ProfFlamegraph
} = require('../lib/util')

module.exports = v8

function v8 (args, binary) {
const { log, status, ee } = args

var node = !binary || binary === 'node' ? pathTo(args, 'node') : binary
var delay = args.delay
delay = parseInt(delay, 10)
if (isNaN(delay)) { delay = 0 }

var proc = spawn(node, [
'--prof',
`--logfile=%p-v8.log`,
'-r', path.join(__dirname, '..', 'lib', 'soft-exit')
].filter(Boolean).concat(args.argv), {
stdio: ['ignore', 'inherit', 'inherit']
}).on('exit', function (code) {
if (code !== 0) {
tidy(args)
const err = Error('0x Target subprocess error, code: ' + code)
err.code = code
ee.emit('error', err, code)
return
}
analyze(true)
})

var folder = determineOutputDir(args, proc)
ensureDirExists(folder)

setTimeout(status, delay || 0, 'Profiling')

if (process.stdin.isPaused()) {
process.stdin.resume()
log('\u001b[?25l')
}

process.once('SIGINT', analyze)

function analyze (manual) {
if (analyze.called) { return }
analyze.called = true

if (!manual) {
debug('Caught SIGINT, generating flamegraph')
status('Caught SIGINT, generating flamegraph')
proc.on('exit', generate)
} else {
debug('Process exited, generating flamegraph')
status('Process exited, generating flamegraph')
generate()
}

try { process.kill(proc.pid, 'SIGINT') } catch (e) {}

function generate () {
v8ProfFlamegraph(args, {pid: proc.pid, folder}, next)

function next() {
if (delay > 0) {
pump(
fs.createReadStream(folder + '/stacks.' + proc.pid + '.out'),
split(),
filterBeforeDelay(delay),
stacksToFlamegraphStream(args, {pid: proc.pid, folder}, null, () => status(''))
)
} else {
pump(
fs.createReadStream(folder + '/stacks.' + proc.pid + '.out'),
stacksToFlamegraphStream(args, {pid: proc.pid, folder}, null, () => status(''))
)
}
}
}
}
}

function filterBeforeDelay (delay) {
delay *= 1000 // ms -> ns
var start
var pastDelay = false

return through(function (line, enc, cb) {
var diff
line += ''
if (/cpu-clock:/.test(line)) {
if (!start) {
start = parseInt(parseFloat(line.match(/[0-9]+[0-9]+:/)[0], 10), 10)
} else {
diff = parseInt(parseFloat(line.match(/[0-9]+[0-9]+:/)[0], 10), 10) - start
pastDelay = (diff > delay)
}
}
if (pastDelay) {
cb(null, line + '\n')
} else {
cb()
}
})
}
10 changes: 10 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ is set to - then flamegraph HTML will be streamed to STDOUT.

Default: false

### --prof-only

Experimental. Only create the prof-viz flamegraph.

Default: false

### --phase

Stage in initialization to begin aggregating stacks.
Expand Down Expand Up @@ -519,6 +525,10 @@ See [`--title`](#--title)

See [`--prof-viz`](#--prof-viz)

### `profOnly`

See [`--prof-only`](#--prof-only)

#### `phase` (number)

See [`--phase`](#--phase)
Expand Down
2 changes: 2 additions & 0 deletions usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
is set to - then flamegraph HTML will be streamed
to STDOUT.

--prof-only Experimental. Only create the prof-viz flamegraph

--phase Stage in initialization to begin aggregating
stacks. Phase 0 visualizes from the very start,
this includes bootstrapping stacks and loading
Expand Down

0 comments on commit 9017add

Please sign in to comment.