Skip to content

Commit

Permalink
rm phases, include init tier
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmarkclements committed Mar 21, 2018
1 parent 06ef99a commit f4aa18f
Show file tree
Hide file tree
Showing 17 changed files with 108 additions and 123 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Expand Up @@ -52,6 +52,8 @@
* API: removed `open` (still part of CLI)
* CLI: add `--on-port` flag
* API: add `onPort` option
* CLI: removed `--phase` flag
* API: removed `phase` option

# v3.4.1
* break out the ui and stack converter portions into separate modules: d3-fg and stacks-to-json-tree.
Expand Down
4 changes: 0 additions & 4 deletions cmd.js
Expand Up @@ -30,7 +30,6 @@ async function cmd (argv, banner = defaultBanner) {
var args = minimist(argv, {
stopEarly: true,
'--': true,
number: ['phase'],
boolean: [
'open', 'version', 'help', 'quiet',
'silent', 'treeDebug', 'kernelTracingDebug',
Expand All @@ -53,9 +52,6 @@ async function cmd (argv, banner = defaultBanner) {
treeDebug: 'tree-debug',
onPort: 'on-port',
P: 'onPort'
},
default: {
phase: 2
}
})

Expand Down
17 changes: 1 addition & 16 deletions docs/api.md
Expand Up @@ -59,24 +59,9 @@ with relevant outputs.

Default: false

#### `phase` (number)

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

#### `mapFrames` (function)

Will override phase. A custom mapping function that receives
A custom mapping function that receives
an array of frames and an instance of the Profiler (see [stacks-to-json-stack-tree](http://github.com/davidmarkclements/stacks-to-json-stack-tree)).

Takes the form `(frames, profiler) => Array|false`.
Expand Down
35 changes: 30 additions & 5 deletions docs/ui.md
Expand Up @@ -102,6 +102,10 @@ in an Unmerged flamegraph.
Inlinable blocks are functions that are captured during profiling, which later become inlined
into their parent calling function. These blocks include the tag [INLINABLE].

Inlinable blocks take precedent over app, deps and core blocks. This means if a
frame is app, deps or core but also inlinable, it will be shown when inlinable is
enabled regardless of whether app, deps or core are enabled.

Enabled by default.

#### native
Expand All @@ -116,11 +120,11 @@ will also appear as native frames, such functions have the function name `[eval]

Disabled by default.

#### regexp
#### rx

Regular expressions are also captured as "frames". In this case the regular expression
notation fills in as the "function name" portion of the block label. This can be useful
in identifying slow regular expressions
Rx stands to Regular Expressions. Regular expressions are also captured as "frames".
In this case the regular expression notation fills in as the "function name" portion
of the block label. This can be useful in identifying slow regular expressions
(in particular [exponential time regular expressions](https://perlgeek.de/blog-en.cgi/perl-tips/in-search-of-an-exponetial-regexp.html)).

These will have the tag `[CODE:RegExp]`.
Expand All @@ -138,4 +142,25 @@ V8 frames pertain to V8 runtime operations, tags can include `[CODE:LoadGlobalIC
These are C++ frames that are called by the V8 layer, they do not include C++ frames
that may be called in Node, Libuv or third party modules. In `--kernel-tracing` mode,
however, non-V8 C++ frames *are* included. These frames can include the tags
`[CPP]` and `[SHARED_LIB]`.
`[CPP]` and `[SHARED_LIB]`.

#### init

The init category describes core functions that are either:

* internal module system functions which are repeated frequently
as the dependency tree is loaded
* other initialization functions that aren't related to the (public) module system

Filtering out these frames reduces generally redundant initialization
noise.

These frames include the `[INIT]` tag, but may also include any of the other tags.

Any core frames that are also init frames are controlled by the init type
(e.g hiding core frames wont hide any core frames that are also init frames).

However, any cpp frames that are also init frames are controlled by the cpp
type (e.g. hiding init frames wont hide cpp frames that are also init frames).

Disabled by default.
4 changes: 0 additions & 4 deletions index.js
Expand Up @@ -8,7 +8,6 @@ const validate = require('./lib/validate')(require('./schema.json'))
const traceStacksToTicks = require('./lib/trace-stacks-to-ticks')
const v8LogToTicks = require('./lib/v8-log-to-ticks')
const ticksToTree = require('./lib/ticks-to-tree')
const phases = require('./lib/phases')
const render = require('./lib/render')

const platform = process.platform
Expand All @@ -28,7 +27,6 @@ async function zeroEks (args) {
if (visualizeOnly) return visualize(args)

args.title = args.title || 'node ' + args.argv.join(' ')
args.mapFrames = args.mapFrames || phases[args.phase]
const binary = args.pathToNodeBinary
var { ticks, pid, folder, inlined } = await startProcessAndCollectTraceData(args, binary)

Expand Down Expand Up @@ -109,7 +107,6 @@ async function visualize ({ visualizeOnly, treeDebug, workingDir, title, mapFram
title = title || meta.title
name = name || meta.name

mapFrames = mapFrames || phases['phase' in meta ? meta.phase : phase]
const ticks = (srcType === 'v8')
? await v8LogToTicks(src)
: traceStacksToTicks(src)
Expand All @@ -126,7 +123,6 @@ async function visualize ({ visualizeOnly, treeDebug, workingDir, title, mapFram
title,
name,
mapFrames,
phase,
open,
ticks,
inlined,
Expand Down
22 changes: 0 additions & 22 deletions lib/phases.js

This file was deleted.

32 changes: 32 additions & 0 deletions lib/ticks-to-tree.js
Expand Up @@ -4,6 +4,35 @@ const debug = require('debug')('0x: ticks-to-tree')

module.exports = ticksToTree

function labelInitFrames (frames) {
const startupBootstrapNodeIndex = frames.findIndex(({name}, ix) => {
if (frames[ix + 1] && /Module.runMain module\.js/.test(frames[ix + 1].name)) return false
return /startup bootstrap_node\.js/.test(name)
})

if (startupBootstrapNodeIndex !== -1) {
frames.slice(startupBootstrapNodeIndex + 1).forEach((frame) => {
if (frame.isInit) return
frame.name += ' [INIT]'
frame.isInit = true
})
}

const moduleRunMainIndex = frames.findIndex(({name}, ix) => {
return /Module.runMain module\.js/.test(name)
})

if (moduleRunMainIndex !== -1) {
frames.slice(moduleRunMainIndex + 1).forEach((frame) => {
if (frame.isInit) return
if (/.+ (internal\/)?module\.js/.test(frame.name)) frame.name += '[INIT]'
frame.isInit = true
})
}

return frames
}

function ticksToTree (ticks, mapFrames, inlined) {
const merged = {
name: 'all stacks',
Expand All @@ -20,6 +49,7 @@ function ticksToTree (ticks, mapFrames, inlined) {
const inlineInfo = typeof inlined === 'object'

ticks.forEach((stack) => {
// stack = stack.filter(({name}, ix) => name !== stack[ix -1].name)
if (typeof mapFrames === 'function') stack = mapFrames(stack)
if (!stack) return
stack = stack.map(({name, kind, type}, ix) => {
Expand Down Expand Up @@ -69,6 +99,8 @@ function ticksToTree (ticks, mapFrames, inlined) {
return {S, name, value: 0, top: 0}
})

stack = labelInitFrames(stack)

addToMergedTree(stack.map(({S, name, value, top}) => ({S, name, value, top})))
// mutate original (save another loop over stack + extra objects)
addToUnmergedTree(stack)
Expand Down
21 changes: 1 addition & 20 deletions lib/v8-log-to-ticks.js
Expand Up @@ -55,7 +55,7 @@ function v8LogToTicks (isolateLogPath) {
close()
return reject(err)
}
resolve(ticks)
resolve(ticks.filter(Boolean))
})

if (isJson === false) {
Expand All @@ -65,22 +65,3 @@ function v8LogToTicks (isolateLogPath) {
}
})
}

// function v8LogToTicks (isolateLogPath) {
// const { stdout, stderr, status } = spawnSync(process.argv[0], [
// '--prof-process', '--preprocess', '-j', isolateLogPath
// ])
// if (status !== 0) {
// throw Error('v8 log conversion failed: ', stderr + '', stdout + '')
// }

// const data = JSON.parse(stdout)
// const ticks = data.ticks.map((tick, i) => {
// const addr = tick.s.filter((n, i) => i % 2 === 0)
// const offsets = tick.s.filter((n, i) => i % 2 === 1)
// var stack = addr.map((n) => data.code[n]).filter(Boolean)
// return stack.reverse()
// })

// return ticks
// }
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -27,7 +27,7 @@
"ajv": "^6.0.1",
"browserify": "^13.0.0",
"concat-stream": "^1.5.2",
"d3-fg": "^6.0.2",
"d3-fg": "file:../d3-fg",
"debounce": "^1.1.0",
"debug": "^2.2.0",
"end-of-stream": "^1.1.0",
Expand Down
12 changes: 5 additions & 7 deletions platform/v8.js
Expand Up @@ -37,20 +37,18 @@ async function v8 (args, binary) {
if (onPort) status('Profiling\n')
else status('Profiling')

const whenPort = spawnOnPort(onPort, await when(proc.stdio[3], 'data'))
const whenPort = onPort && spawnOnPort(onPort, await when(proc.stdio[3], 'data'))

const code = await Promise.race([
new Promise((resolve) => process.once('SIGINT', resolve)),
new Promise((resolve) => proc.once('exit', (code) => {
resolve(code)
})),
new Promise((resolve, reject) => {
new Promise((resolve) => proc.once('exit', (code) => resolve(code))),
...(onPort ? [new Promise((resolve, reject) => {
whenPort.then(() => proc.kill('SIGINT'))
whenPort.catch((err) => {
proc.kill()
reject(err)
})
})
})] : [])
])

if (code|0 !== 0) {
Expand Down
15 changes: 0 additions & 15 deletions readme.md
Expand Up @@ -199,21 +199,6 @@ See [docs/kernel-tracing.md](docs/kernel-tracing.md) for more information.

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

### --quiet | -q

Limit output, the only output will be fatal errors or
Expand Down
3 changes: 0 additions & 3 deletions schema.json
Expand Up @@ -68,9 +68,6 @@
"name": {
"type": "string"
},
"phase": {
"type": "number"
},
"visualize-only": {
"type": "string"
},
Expand Down
32 changes: 16 additions & 16 deletions usage.txt
@@ -1,4 +1,5 @@
--open | -o Automatically open after finishing

Default: false

--on-port | -P Run a given command and then generate
Expand All @@ -16,33 +17,25 @@

Default: ''

--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

-q | --quiet Only output flamegraph URI, and fatal errors.

Default: false

-s | --silent Complete silence, 0x will not output anything,
other than fatal errors.

Default: false

--kernel-tracing Use an OS kernel tracing tool (perf on Linux or
dtrace on macOs and Solaris). This will capture
native stack frames (C++ modules and Libuv I/O),
but may result in missing stacks on Node 8.

--kernel-tracing-debug Show output from dtrace or perf tools.

Default: false



--output-dir | -D Specify artifact output directory.
Template variables {outputDir}, {pid}, {timestamp}, {cwd}
(current working directory) and {name}
Expand All @@ -61,14 +54,20 @@
If either this flag or --name is set to - then the HTML
will go to STDOUT.

                       Default: '{outputDir}/{name}.html'
                       Default: '{outputDir}/{name}.html'

--kernel-tracing-debug Show output from dtrace or perf tools.

Default: false

--tree-debug Output a JSON file of stacks as {outputDir}/stacks.{pid}.json

Default: false

--collect-only Do not process captured stacks into a flamegraph.

--visualize-only <folder> Build or rebuild flamegraph using the output dir.
Counterpart to --collect-only.
--visualize-only <dir> Build or rebuild flamegraph using the output dir.
Counterpart to --collect-only.

--name The name of the HTML file, without the .html extension
Can be set to - to write HTML to STDOUT (note
Expand All @@ -81,6 +80,7 @@
Default: flamegraph

--title Set the title to display in the flamegraph UI

Default: node [nodeFlags] script.js

-v | --version Output the 0x version
Expand Down

0 comments on commit f4aa18f

Please sign in to comment.