Revert "Merge pull request #165 from BridgeAR/filter-init"

This reverts commit be8e703b03ed86d0252d3a32399fef17a415a485, reversing
changes made to 2bbc814beeee89693f2a55764a5c41ce22d54a61.
This commit is contained in:
Matteo Collina 2018-09-18 15:59:54 +02:00
parent e88214ce22
commit 0a5eb02a0e
8 changed files with 125 additions and 92 deletions

3
.gitignore vendored
View File

@ -5,5 +5,4 @@ node_modules
win
package-lock.json
todo
.vscode
*.0x
.vscode

View File

@ -23,7 +23,6 @@ function ticksToTree (ticks, mapFrames, inlined) {
stack = removeInstrumentationFrames(stack)
if (typeof mapFrames === 'function') stack = mapFrames(stack)
if (!stack) return
stack = stack.map(({name, kind, type}, ix) => {
name = name.replace(/ (:[0-9]+:[0-9]+)/, (_, loc) => ` [eval]${loc}`)
// 0 no info
@ -65,15 +64,14 @@ function ticksToTree (ticks, mapFrames, inlined) {
}
}
if (kind === 'Unopt') S += 1
else if (kind === 'Opt') S += 2
if (kind === 'Opt') S += 2
}
if (type && type !== 'JS') name += ' [' + type + (kind ? ':' + kind : '') + ']'
return {S, name, value: 0, top: 0}
})
labelInitFrames(stack)
stack = labelInitFrames(stack)
addToMergedTree(stack.map(({S, name, value, top}) => ({S, name, value, top})))
// mutate original (save another loop over stack + extra objects)
@ -118,9 +116,9 @@ function ticksToTree (ticks, mapFrames, inlined) {
if (child === undefined) {
frame.fn = frame.name
if (frame.S === 1) frame.name = '~' + frame.name
else if (frame.S === 2) frame.name = '*' + frame.name
else if (frame.S === 3) frame.name = '~' + frame.name + ' [INLINABLE]'
else if (frame.S === 4) frame.name = '*' + frame.name + ' [INLINABLE]'
if (frame.S === 2) frame.name = '*' + frame.name
if (frame.S === 3) frame.name = '~' + frame.name + ' [INLINABLE]'
if (frame.S === 4) frame.name = '*' + frame.name + ' [INLINABLE]'
children.push(frame)
} else frame = child
@ -135,14 +133,51 @@ function ticksToTree (ticks, mapFrames, inlined) {
return { merged, unmerged }
}
function labelInitFrames (frames) {
frames.findIndex((frame) => {
if (/^.?\(anonymous\) \/.+\.m?js:[0-9]+:[0-9]+/.test(frame.name)) {
return true
}
frame.name += ' [INIT]'
frame.isInit = true
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
})
}
// if there's so many modules to load, the module requiring may
// actually go into another tick, so far that's been observed where Module.load
// is the first function, but there could be variation...
const partOfModuleLoadingCycle = frames.findIndex(({name}, ix) => {
return /(Module\.load|Module\._load|tryModuleLoad|Module\._extensions.+|Module\._compile|Module.require|require internal.+) module\.js/.test(name)
})
if (partOfModuleLoadingCycle === 0) {
frames.forEach((frame) => {
if (frame.isInit) return
if (/.+ (internal\/)?module\.js/.test(frame.name)) frame.name += ' [INIT]'
frame.isInit = true
})
}
return frames
}
function removeInstrumentationFrames (frames) {

View File

@ -45,7 +45,6 @@ function linux (args, sudo, binary, cb) {
'--',
node,
'--perf-basic-prof',
'-r', path.join(__dirname, '..', 'lib', 'preload', 'no-cluster'),
'-r', path.join(__dirname, '..', 'lib', 'preload', 'soft-exit'),
...(onPort ? ['-r', path.join(__dirname, '..', 'lib', 'preload', 'detect-port.js')] : [])
].filter(Boolean).concat(args.argv), {

View File

@ -33,7 +33,6 @@ function sun (args, sudo, binary, cb) {
args = Object.assign([
'--perf-basic-prof',
'-r', path.join(__dirname, '..', 'lib', 'preload', 'no-cluster'),
'-r', path.join(__dirname, '..', 'lib', 'preload', 'soft-exit'),
...(onPort ? ['-r', path.join(__dirname, '..', 'lib', 'preload', 'detect-port.js')] : [])
].concat(args.argv), args)
@ -52,6 +51,7 @@ function sun (args, sudo, binary, cb) {
})
var folder
var prof
var profExited = false
function start () {
prof = spawn('sudo', [profile, '-p', proc.pid])
@ -80,7 +80,7 @@ function sun (args, sudo, binary, cb) {
else status('Profiling')
start()
if (onPort) when(proc.stdio[5], 'data').then((port) => {
const whenPort = spawnOnPort(onPort, port)
whenPort.then(() => proc.kill('SIGINT'))
@ -122,11 +122,11 @@ function sun (args, sudo, binary, cb) {
try { process.kill(prof.pid, 'SIGKILL') } catch (e) {}
}
try {
translate = sym({silent: true, pid: proc.pid})
try {
translate = sym({silent: true, pid: proc.pid})
capture(attempts, translate)
} catch (e) {
setTimeout(capture, 300, attempts - 1)
setTimeout(capture, 300, attempts - 1)
}
} else {
status('Unable to find map file!\n')
@ -137,7 +137,7 @@ function sun (args, sudo, binary, cb) {
}
return
}
translate = translate || sym({silent: true, pid: proc.pid})
if (!translate) {

View File

@ -150,12 +150,12 @@ function collectInliningInfo (sp) {
const [ match, inlinedFn ] = /INLINE \((.*)\)/.exec(s) || [ false ]
// shouldn't not match though..
if (match === false) return cb()
if (lastOptimizedFrame === null) return cb()
if (lastOptimizedFrame === null) return cb()
const { fn, file } = lastOptimizedFrame
// could be a big problem if the fn doesn't match
if (fn !== inlinedFn) return cb()
const key = `${fn} ${file}`
inlined[key] = inlined[key] || []
inlined[key].push(lastOptimizedFrame)
@ -177,7 +177,7 @@ function collectInliningInfo (sp) {
if (ix === '-1') root = {file, fn, id, ix, pos, key: `${fn} ${file}`}
else {
lastOptimizedFrame = {file, fn, id, ix, pos, caller: root}
}
}
} else process.stdout.write(s)
}

View File

@ -16,7 +16,7 @@ on any platform which Node runs on (macOs, Linux, Windows, Android...).
* Node v8.5.0 and above
* Default usage supports any Operating System that Node runs on!
* Chrome
* Other browsers may open flamegraphs in a degraded, but functional form
* Other browsers may open flamegraphs in a degraded, but functional form
## Legacy
@ -82,12 +82,12 @@ generates a profile folder (`<pid>.0x`), containing `flamegraph.html`.
## The UI
The `flamegraph.html` file contains the 0x UI, which is explained in
The `flamegraph.html` file contains the 0x UI, which is explained in
[docs/ui.md](docs/ui.md).
## Production Servers
A lightweight, production server friendly, approach to generating a
A lightweight, production server friendly, approach to generating a
flamegraph is described in [docs/production-servers.md](docs/production-servers.md).
## The Profile Folder
@ -125,30 +125,30 @@ Print usage info.
Open the flamegraph in the browser using `open` or `xdg-open` (see
https://www.npmjs.com/package/open for details).
### --on-port | -P
### --on-port | -P
Run a given command and then generate the flamegraph.
The command as specified has access to a `$PORT` variable.
The `$PORT` variable is set according to the first port that
profiled process opens.
Run a given command and then generate the flamegraph.
The command as specified has access to a `$PORT` variable.
The `$PORT` variable is set according to the first port that
profiled process opens.
For instance, here's an example of using [autocannon](http://npm.im/autocannon)
For instance, here's an example of using [autocannon](http://npm.im/autocannon)
to load-test the process:
```sh
0x -P 'autocannon localhost:$PORT' app.js
```
When the load-test completes, the profiled processed will be
sent a SIGINT and the flamegraph will be automatically generated.
When the load-test completes, the profiled processed will be
sent a SIGINT and the flamegraph will be automatically generated.
Remember to use single quotes to avoid bash interpolation,
or else escape variable (e.g. `0x -P "autocannon localhost:$PORT" app.js`
won't work wheras `0x -P "autocannon localhost:\$PORT" app.js` will).
Note: On Windows interpolation usually occurs with `%PORT%`, however
in this case the dollar-prefix `$PORT` is the correct syntax
(because the interpolation is not shell based).
in this case the dollar-prefix `$PORT` is the correct syntax
(because the interpolation is not shell based).
Default: ''
@ -156,24 +156,24 @@ Default: ''
The name of the HTML file, without the .html extension
Can be set to - to write HTML to STDOUT (note
due to the nature of CLI argument parsing, this must be set using `=`,
due to the nature of CLI argument parsing, this must be set using `=`,
e.g. `--name=-`).
If either this flag or `--output-html-file` is set to `-`
If either this flag or `--output-html-file` is set to `-`
then the HTML will go to STDOUT.
Default: flamegraph
### ---title
### ---title
Set the title to display in the flamegraph UI.
Default: the command that 0x ran to start the process
Default: the command that 0x ran to start the process
### --output-dir | -D
Specify artifact output directory. This can be specified in template
form with possible variables being `{pid}`, `{timestamp}`, `{name}`
form with possible variables being `{pid}`, `{timestamp}`, `{name}`
(based on the `--name` flag) and `{outputDir}`(variables
must be specified without whitespace, e.g. `{ pid }` is not supported).
@ -181,34 +181,34 @@ Default: `{pid}.0x`
### --output-html | -F
Specify destination of the generated flamegraph HTML file.
This can be specified in template form with possible variables
being `{pid}`, `{timestamp}`, `{name}` (based on the `--name` flag) and
`{outputDir}` (variables must be specified without whitespace,
e.g. `{ pid }` is not supported). It can also be set to `-` to
Specify destination of the generated flamegraph HTML file.
This can be specified in template form with possible variables
being `{pid}`, `{timestamp}`, `{name}` (based on the `--name` flag) and
`{outputDir}` (variables must be specified without whitespace,
e.g. `{ pid }` is not supported). It can also be set to `-` to
send the HTML output to STDOUT (note
due to the nature of CLI argument parsing, this must be set using `=`,
due to the nature of CLI argument parsing, this must be set using `=`,
e.g. `--output-html=-`).
If either this flag or `--name` is set to `-`
If either this flag or `--name` is set to `-`
then the HTML will go to STDOUT.
Default: `{outputDir}/{name}.html`
### --kernel-tracing
Use an OS kernel tracing tool (perf on Linux or
dtrace on macOS and SmartOS). This will capture
native stack frames (C++ modules and Libuv I/O),
Use an OS kernel tracing tool (perf on Linux or
dtrace on macOS and SmartOS). This will capture
native stack frames (C++ modules and Libuv I/O),
but may result in missing stacks on Node 8.
See [docs/kernel-tracing.md](docs/kernel-tracing.md) for more information.
Default: false
Default: false
### --quiet | -q
### --quiet | -q
Limit output, the only output will be fatal errors or
Limit output, the only output will be fatal errors or
the path to the `flamegraph.html` upon successful generation.
Default: false
@ -226,9 +226,9 @@ with relevant outputs.
Default: false
### --visualize-only
### --visualize-only
Supply a path to a profile folder to build or rebuild visualization
Supply a path to a profile folder to build or rebuild visualization
from original stacks.
Default: undefined
@ -246,7 +246,7 @@ file.
Default: false
## Programmatic API
## Programmatic API
0x can also be required as a Node module and scripted:
@ -268,6 +268,7 @@ async function capture () {
}
capture()
```
The Programmatic API is detailed in [docs/api.md](docs/api.md).
@ -276,9 +277,9 @@ The Programmatic API is detailed in [docs/api.md](docs/api.md).
### Memory Issues
Very complex applications with lots of stacks may hit memory issues.
Very complex applications with lots of stacks may hit memory issues.
The `--stack-size` flag can be used to set the memory to the maximum 8GB
The `--stack-size` flag can be used to set the memory to the maximum 8GB
in order to work around this when profiling:
```
@ -287,7 +288,7 @@ node --stack-size=8024 $(which 0x) my-app.js
There may still be a problem opening the flamegraph in Chrome. The same work
around can be used by opening Chrome from the command line (platform dependent)
and nesting the `--stack-size` flag within the `--js-flags` flag:
and nesting the `--stack-size` flag within the `--js-flags` flag:
`--js-flags="--stack-size 8024"`.
## Debugging
@ -305,7 +306,7 @@ and nesting the `--stack-size` flag within the `--js-flags` flag:
Sponsored by [nearForm](http://nearform.com)
This tool is inspired from various info and code sources
and would have taken much longer without the following people and
and would have taken much longer without the following people and
their Open Source/Info Sharing efforts:
* Thorsten Lorenz (<http://thlorenz.com/>)

View File

@ -1,15 +1,15 @@
--open | -o Automatically open after finishing
Default: false
--on-port | -P Run a given command and then generate
the flamegraph. The command as specified
has access to a $PORT variable.
has access to a $PORT variable.
The $PORT variable is set according
to the first port that profiled process
opens.
to the first port that profiled process
opens.
Example:
Example:
0x -P 'autocannon localhost:$PORT' app.js
Note: Remember to use single quotes or else
@ -19,49 +19,49 @@
-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),
--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.
Default: false
--output-dir | -D Specify artifact output directory.
Template variables {outputDir}, {pid}, {timestamp}, {cwd}
(current working directory) and {name}
(current working directory) and {name}
(based on the --name flag) are supported.
Default: '{pid}.0x'
--output-html | -F Specify destination path for the flamegraph HTML file.
Template variables {outputDir}, {pid}, {timestamp}, {cwd}
(current working directory) and {name}
(based on the --name flag) are supported.
(current working directory) and {name}
(based on the --name flag) are supported.
May also be set to - to send HTML file to STDOUT (note
due to the nature of CLI argument parsing, this must be
due to the nature of CLI argument parsing, this must be
set using =, e.g. --output-html=-).
If either this flag or --name is set to - then the HTML
will go to STDOUT.
If either this flag or --name is set to - then the HTML
will go to STDOUT.
                       Default: '{outputDir}/{name}.html'
--kernel-tracing-debug Show output from dtrace or perf tools.
--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.
@ -71,16 +71,16 @@
--name The name of the HTML file, without the .html extension
Can be set to - to write HTML to STDOUT (note
due to the nature of CLI argument parsing, this must
due to the nature of CLI argument parsing, this must
be set using =, e.g. --name=-)
If either this flag or --output-html is set to -
If either this flag or --output-html is set to -
then the HTML will go to STDOUT.
Default: flamegraph
--title Set the title to display in the flamegraph UI
Default: node [nodeFlags] script.js
-v | --version Output the 0x version

View File

@ -15,12 +15,11 @@ module.exports = function (trees, opts) {
const chart = graph()
const tree = trees.unmerged // default view
const categorizer = !kernelTracing && graph.v8cats
const flamegraph = fg({
categorizer,
tree,
exclude: Array.from(exclude),
categorizer,
tree,
exclude: Array.from(exclude),
element: chart,
topOffset: 55
})