Browse Source

tools: add --prof-process flag to node binary

This change cleans up outstanding comments on #3032. It improves error
handling when no isolate file is provided and adds the --prof-process
flag to the node binary which executes the tick processor on the
provided isolate file.

PR-URL: https://github.com/nodejs/node/pull/4021
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Evan Lucas <evanlucas@me.com>
v4.x
Matt Loring 9 years ago
committed by Myles Borins
parent
commit
0164e12a6c
  1. 1
      .eslintignore
  2. 2
      doc/node.1
  3. 9
      lib/internal/v8_prof_polyfill.js
  4. 44
      lib/internal/v8_prof_processor.js
  5. 13
      node.gyp
  6. 14
      src/node.cc
  7. 3
      src/node.js
  8. 8
      test/parallel/test-tick-processor.js
  9. 44
      tools/install.py
  10. 2
      tools/js2c.py
  11. 4
      tools/rpm/node.spec
  12. 51
      tools/v8-prof/tick-processor.js

1
.eslintignore

@ -1,3 +1,4 @@
lib/internal/v8_prof_polyfill.js
lib/punycode.js lib/punycode.js
test/addons/??_*/ test/addons/??_*/
test/fixtures test/fixtures

2
doc/node.1

@ -64,6 +64,8 @@ and servers.
--track-heap-objects track heap object allocations for heap snapshots --track-heap-objects track heap object allocations for heap snapshots
--prof-process process v8 profiler output generated using --prof
--v8-options print v8 command line options --v8-options print v8 command line options
--tls-cipher-list=list use an alternative default TLS cipher list --tls-cipher-list=list use an alternative default TLS cipher list

9
tools/v8-prof/polyfill.js → lib/internal/v8_prof_polyfill.js

@ -50,7 +50,14 @@ arguments = process.argv.slice(2);
var quit = process.exit; var quit = process.exit;
// Polyfill "readline()". // Polyfill "readline()".
var fd = fs.openSync(arguments[arguments.length - 1], 'r'); var logFile = arguments[arguments.length - 1];
try {
fs.accessSync(logFile);
} catch(e) {
console.error('Please provide a valid isolate file as the final argument.');
process.exit(1);
}
var fd = fs.openSync(logFile, 'r');
var buf = new Buffer(4096); var buf = new Buffer(4096);
var dec = new (require('string_decoder').StringDecoder)('utf-8'); var dec = new (require('string_decoder').StringDecoder)('utf-8');
var line = ''; var line = '';

44
lib/internal/v8_prof_processor.js

@ -0,0 +1,44 @@
'use strict';
var cp = require('child_process');
var fs = require('fs');
var path = require('path');
var scriptFiles = [
'internal/v8_prof_polyfill',
'v8/tools/splaytree',
'v8/tools/codemap',
'v8/tools/csvparser',
'v8/tools/consarray',
'v8/tools/profile',
'v8/tools/profile_view',
'v8/tools/logreader',
'v8/tools/tickprocessor',
'v8/tools/SourceMap',
'v8/tools/tickprocessor-driver'
];
var tempScript = 'tick-processor-tmp-' + process.pid;
var tempNm = 'mac-nm-' + process.pid;
process.on('exit', function() {
try { fs.unlinkSync(tempScript); } catch (e) {}
try { fs.unlinkSync(tempNm); } catch (e) {}
});
process.on('uncaughtException', function(err) {
try { fs.unlinkSync(tempScript); } catch (e) {}
try { fs.unlinkSync(tempNm); } catch (e) {}
throw err;
});
scriptFiles.forEach(function(script) {
fs.appendFileSync(tempScript, process.binding('natives')[script]);
});
var tickArguments = [tempScript];
if (process.platform === 'darwin') {
fs.writeFileSync(tempNm, process.binding('natives')['v8/tools/mac-nm'],
{ mode: 0o555 });
tickArguments.push('--mac', '--nm=' + path.join(process.cwd(), tempNm));
} else if (process.platform === 'win32') {
tickArguments.push('--windows');
}
tickArguments.push.apply(tickArguments, process.argv.slice(1));
cp.spawn(process.execPath, tickArguments, { stdio: 'inherit' });

13
node.gyp

@ -77,7 +77,20 @@
'lib/internal/socket_list.js', 'lib/internal/socket_list.js',
'lib/internal/repl.js', 'lib/internal/repl.js',
'lib/internal/util.js', 'lib/internal/util.js',
'lib/internal/v8_prof_polyfill.js',
'lib/internal/v8_prof_processor.js',
'lib/internal/streams/lazy_transform.js', 'lib/internal/streams/lazy_transform.js',
'deps/v8/tools/splaytree.js',
'deps/v8/tools/codemap.js',
'deps/v8/tools/consarray.js',
'deps/v8/tools/csvparser.js',
'deps/v8/tools/profile.js',
'deps/v8/tools/profile_view.js',
'deps/v8/tools/logreader.js',
'deps/v8/tools/tickprocessor.js',
'deps/v8/tools/SourceMap.js',
'deps/v8/tools/tickprocessor-driver.js',
'deps/v8/tools/mac-nm',
], ],
}, },

14
src/node.cc

@ -141,6 +141,7 @@ static const char** preload_modules = nullptr;
static bool use_debug_agent = false; static bool use_debug_agent = false;
static bool debug_wait_connect = false; static bool debug_wait_connect = false;
static int debug_port = 5858; static int debug_port = 5858;
static bool prof_process = false;
static bool v8_is_profiling = false; static bool v8_is_profiling = false;
static bool node_is_initialized = false; static bool node_is_initialized = false;
static node_module* modpending; static node_module* modpending;
@ -2972,6 +2973,11 @@ void SetupProcessObject(Environment* env,
READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate())); READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate()));
} }
// --prof-process
if (prof_process) {
READONLY_PROPERTY(process, "profProcess", True(env->isolate()));
}
// --trace-deprecation // --trace-deprecation
if (trace_deprecation) { if (trace_deprecation) {
READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate())); READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate()));
@ -3218,6 +3224,8 @@ static void PrintHelp() {
" is detected after the first tick\n" " is detected after the first tick\n"
" --track-heap-objects track heap object allocations for heap " " --track-heap-objects track heap object allocations for heap "
"snapshots\n" "snapshots\n"
" --prof-process process v8 profiler output generated\n"
" using --prof\n"
" --v8-options print v8 command line options\n" " --v8-options print v8 command line options\n"
#if HAVE_OPENSSL #if HAVE_OPENSSL
" --tls-cipher-list=val use an alternative default TLS cipher list\n" " --tls-cipher-list=val use an alternative default TLS cipher list\n"
@ -3289,7 +3297,8 @@ static void ParseArgs(int* argc,
new_argv[0] = argv[0]; new_argv[0] = argv[0];
unsigned int index = 1; unsigned int index = 1;
while (index < nargs && argv[index][0] == '-') { bool short_circuit = false;
while (index < nargs && argv[index][0] == '-' && !short_circuit) {
const char* const arg = argv[index]; const char* const arg = argv[index];
unsigned int args_consumed = 1; unsigned int args_consumed = 1;
@ -3353,6 +3362,9 @@ static void ParseArgs(int* argc,
} else if (strncmp(arg, "--security-revert=", 18) == 0) { } else if (strncmp(arg, "--security-revert=", 18) == 0) {
const char* cve = arg + 18; const char* cve = arg + 18;
Revert(cve); Revert(cve);
} else if (strcmp(arg, "--prof-process") == 0) {
prof_process = true;
short_circuit = true;
} else if (strcmp(arg, "--v8-options") == 0) { } else if (strcmp(arg, "--v8-options") == 0) {
new_v8_argv[new_v8_argc] = "--help"; new_v8_argv[new_v8_argc] = "--help";
new_v8_argc += 1; new_v8_argc += 1;

3
src/node.js

@ -68,6 +68,9 @@
// Start the debugger agent // Start the debugger agent
NativeModule.require('_debug_agent').start(); NativeModule.require('_debug_agent').start();
} else if (process.profProcess) {
NativeModule.require('internal/v8_prof_processor');
} else { } else {
// There is user code to be run // There is user code to be run

8
test/parallel/test-tick-processor.js

@ -16,8 +16,6 @@ if (common.isAix) {
common.refreshTmpDir(); common.refreshTmpDir();
process.chdir(common.tmpDir); process.chdir(common.tmpDir);
var processor =
path.join(common.testDir, '..', 'tools', 'v8-prof', 'tick-processor.js');
// Unknown checked for to prevent flakiness, if pattern is not found, // Unknown checked for to prevent flakiness, if pattern is not found,
// then a large number of unknown ticks should be present // then a large number of unknown ticks should be present
runTest(/LazyCompile.*\[eval\]:1|.*% UNKNOWN/, runTest(/LazyCompile.*\[eval\]:1|.*% UNKNOWN/,
@ -54,9 +52,9 @@ function runTest(pattern, code) {
assert.fail(null, null, 'There should be a single log file.'); assert.fail(null, null, 'There should be a single log file.');
} }
var log = matches[0]; var log = matches[0];
var out = cp.execSync(process.execPath + ' ' + processor + var out = cp.execSync(process.execPath +
' --call-graph-size=10 ' + log, ' --prof-process --call-graph-size=10 ' + log,
{encoding: 'utf8'}); {encoding: 'utf8'});
assert(out.match(pattern)); assert(pattern.test(out));
fs.unlinkSync(log); fs.unlinkSync(log);
} }

44
tools/install.py

@ -127,48 +127,6 @@ def subdir_files(path, dest, action):
for subdir, files in ret.items(): for subdir, files in ret.items():
action(files, subdir + '/') action(files, subdir + '/')
def build_tick_processor(action):
tmp_script = 'out/Release/tick-processor'
if action == install:
# construct script
scripts = [
'tools/v8-prof/polyfill.js',
'deps/v8/tools/splaytree.js',
'deps/v8/tools/codemap.js',
'deps/v8/tools/csvparser.js',
'deps/v8/tools/consarray.js',
'deps/v8/tools/csvparser.js',
'deps/v8/tools/consarray.js',
'deps/v8/tools/profile.js',
'deps/v8/tools/profile_view.js',
'deps/v8/tools/logreader.js',
'deps/v8/tools/tickprocessor.js',
'deps/v8/tools/SourceMap.js',
'deps/v8/tools/tickprocessor-driver.js']
args = []
if sys.platform == 'win32':
args.append('--windows')
elif sys.platform == 'darwin':
args.append('--nm=' + abspath(install_path, 'share/doc/node') + '/mac-nm')
args.append('--mac')
with open(tmp_script, 'w') as out_file:
# Add #! line to run with node
out_file.write('#! ' + abspath(install_path, 'bin/node') + '\n')
# inject arguments
for arg in args:
out_file.write('process.argv.splice(2, 0, \'' + arg + '\');\n')
# cat in source files
for script in scripts:
with open(script) as in_file:
shutil.copyfileobj(in_file, out_file)
# make executable
st = os.stat(tmp_script)
os.chmod(tmp_script, st.st_mode | stat.S_IEXEC)
# perform installations
action([tmp_script], 'share/doc/node/')
if sys.platform == 'darwin':
action(['deps/v8/tools/mac-nm'], 'share/doc/node/')
def files(action): def files(action):
is_windows = sys.platform == 'win32' is_windows = sys.platform == 'win32'
@ -183,8 +141,6 @@ def files(action):
action(['deps/v8/tools/gdbinit'], 'share/doc/node/') action(['deps/v8/tools/gdbinit'], 'share/doc/node/')
build_tick_processor(action)
if 'freebsd' in sys.platform or 'openbsd' in sys.platform: if 'freebsd' in sys.platform or 'openbsd' in sys.platform:
action(['doc/node.1'], 'man/man1/') action(['doc/node.1'], 'man/man1/')
else: else:

2
tools/js2c.py

@ -310,7 +310,7 @@ def JS2C(source, target):
else: else:
ids.append((id, len(lines))) ids.append((id, len(lines)))
escaped_id = id.replace('/', '_') escaped_id = id.replace('-', '_').replace('/', '_')
source_lines.append(SOURCE_DECLARATION % { source_lines.append(SOURCE_DECLARATION % {
'id': id, 'id': id,
'escaped_id': escaped_id, 'escaped_id': escaped_id,

4
tools/rpm/node.spec

@ -94,7 +94,6 @@ done
/usr/include/* /usr/include/*
/usr/lib/node_modules/ /usr/lib/node_modules/
/usr/share/doc/node/gdbinit /usr/share/doc/node/gdbinit
/usr/share/doc/node/tick-processor
/usr/share/man/man1/node.1.gz /usr/share/man/man1/node.1.gz
/usr/share/systemtap/tapset/node.stp /usr/share/systemtap/tapset/node.stp
%{_datadir}/%{name}/ %{_datadir}/%{name}/
@ -102,9 +101,6 @@ done
%changelog %changelog
* Tue Sep 22 2015 Matt Loring <mattloring@google.com>
- Added tick processor.
* Tue Jul 7 2015 Ali Ijaz Sheikh <ofrobots@google.com> * Tue Jul 7 2015 Ali Ijaz Sheikh <ofrobots@google.com>
- Added gdbinit. - Added gdbinit.

51
tools/v8-prof/tick-processor.js

@ -1,51 +0,0 @@
'use strict';
var cp = require('child_process');
var fs = require('fs');
var path = require('path');
var toolsPath = path.join(__dirname, '..', '..', 'deps', 'v8', 'tools');
var scriptFiles = [
path.join(__dirname, 'polyfill.js'),
path.join(toolsPath, 'splaytree.js'),
path.join(toolsPath, 'codemap.js'),
path.join(toolsPath, 'csvparser.js'),
path.join(toolsPath, 'consarray.js'),
path.join(toolsPath, 'csvparser.js'),
path.join(toolsPath, 'consarray.js'),
path.join(toolsPath, 'profile.js'),
path.join(toolsPath, 'profile_view.js'),
path.join(toolsPath, 'logreader.js'),
path.join(toolsPath, 'tickprocessor.js'),
path.join(toolsPath, 'SourceMap.js'),
path.join(toolsPath, 'tickprocessor-driver.js')];
var tempScript = path.join(__dirname, 'tick-processor-tmp-' + process.pid);
process.on('exit', function() {
try { fs.unlinkSync(tempScript); } catch (e) {}
});
process.on('uncaughtException', function(err) {
try { fs.unlinkSync(tempScript); } catch (e) {}
throw err;
});
var inStreams = scriptFiles.map(function(f) {
return fs.createReadStream(f);
});
var outStream = fs.createWriteStream(tempScript);
inStreams.reduce(function(prev, curr, i) {
prev.on('end', function() {
curr.pipe(outStream, { end: i === inStreams.length - 1});
});
return curr;
});
inStreams[0].pipe(outStream, { end: false });
outStream.on('close', function() {
var tickArguments = [tempScript];
if (process.platform === 'darwin') {
tickArguments.push('--mac', '--nm=' + path.join(toolsPath, 'mac-nm'));
} else if (process.platform === 'win32') {
tickArguments.push('--windows');
}
tickArguments.push.apply(tickArguments, process.argv.slice(2));
var processTicks = cp.spawn(process.execPath, tickArguments, { stdio: 'inherit' });
});
Loading…
Cancel
Save