process: move process mutation into bootstrap/node.js

This patch moves the part in the report initialization
that mutates the process object into bootstrap/node.js
so it's easier to tell the side effect of the initialization
on the global state during bootstrap.

PR-URL: https://github.com/nodejs/node/pull/25821
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
Joyee Cheung 2019-01-31 00:35:57 +08:00
parent c2359bdad6
commit 406329de57
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
2 changed files with 154 additions and 145 deletions

View File

@ -309,7 +309,18 @@ if (process.env.NODE_V8_COVERAGE) {
} }
if (getOptionValue('--experimental-report')) { if (getOptionValue('--experimental-report')) {
NativeModule.require('internal/process/report').setup(); const {
config,
handleSignal,
report,
syncConfig
} = NativeModule.require('internal/process/report');
process.report = report;
// Download the CLI / ENV config into JS land.
syncConfig(config, false);
if (config.events.includes('signal')) {
process.on(config.signal, handleSignal);
}
} }
function setupTraceCategoryState() { function setupTraceCategoryState() {

View File

@ -3,158 +3,156 @@
const { emitExperimentalWarning } = require('internal/util'); const { emitExperimentalWarning } = require('internal/util');
const { const {
ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_TYPE,
ERR_SYNTHETIC } = require('internal/errors').codes; ERR_SYNTHETIC
} = require('internal/errors').codes;
exports.setup = function() { const REPORTEVENTS = 1;
const REPORTEVENTS = 1; const REPORTSIGNAL = 2;
const REPORTSIGNAL = 2; const REPORTFILENAME = 3;
const REPORTFILENAME = 3; const REPORTPATH = 4;
const REPORTPATH = 4; const REPORTVERBOSE = 5;
const REPORTVERBOSE = 5;
// If report is enabled, extract the binding and // If report is enabled, extract the binding and
// wrap the APIs with thin layers, with some error checks. // wrap the APIs with thin layers, with some error checks.
// user options can come in from CLI / ENV / API. // user options can come in from CLI / ENV / API.
// CLI and ENV is intercepted in C++ and the API call here (JS). // CLI and ENV is intercepted in C++ and the API call here (JS).
// So sync up with both sides as appropriate - initially from // So sync up with both sides as appropriate - initially from
// C++ to JS and from JS to C++ whenever the API is called. // C++ to JS and from JS to C++ whenever the API is called.
// Some events are controlled purely from JS (signal | exception) // Some events are controlled purely from JS (signal | exception)
// and some from C++ (fatalerror) so this sync-up is essential for // and some from C++ (fatalerror) so this sync-up is essential for
// correct behavior and alignment with the supplied tunables. // correct behavior and alignment with the supplied tunables.
const nr = internalBinding('report'); const nr = internalBinding('report');
// Keep it un-exposed; lest programs play with it // Keep it un-exposed; lest programs play with it
// leaving us with a lot of unwanted sanity checks. // leaving us with a lot of unwanted sanity checks.
let config = { let config = {
events: [], events: [],
signal: 'SIGUSR2', signal: 'SIGUSR2',
filename: '', filename: '',
path: '', path: '',
verbose: false verbose: false
}; };
const report = { const report = {
setDiagnosticReportOptions(options) { setDiagnosticReportOptions(options) {
emitExperimentalWarning('report'); emitExperimentalWarning('report');
// Reuse the null and undefined checks. Save // Reuse the null and undefined checks. Save
// space when dealing with large number of arguments. // space when dealing with large number of arguments.
const list = parseOptions(options); const list = parseOptions(options);
// Flush the stale entries from report, as // Flush the stale entries from report, as
// we are refreshing it, items that the users did not // we are refreshing it, items that the users did not
// touch may be hanging around stale otherwise. // touch may be hanging around stale otherwise.
config = {}; config = {};
// The parseOption method returns an array that include // The parseOption method returns an array that include
// the indices at which valid params are present. // the indices at which valid params are present.
list.forEach((i) => { list.forEach((i) => {
switch (i) { switch (i) {
case REPORTEVENTS: case REPORTEVENTS:
if (Array.isArray(options.events)) if (Array.isArray(options.events))
config.events = options.events; config.events = options.events;
else else
throw new ERR_INVALID_ARG_TYPE('events', throw new ERR_INVALID_ARG_TYPE('events',
'Array', 'Array',
options.events); options.events);
break; break;
case REPORTSIGNAL: case REPORTSIGNAL:
if (typeof options.signal !== 'string') { if (typeof options.signal !== 'string') {
throw new ERR_INVALID_ARG_TYPE('signal', throw new ERR_INVALID_ARG_TYPE('signal',
'String', 'String',
options.signal); options.signal);
} }
process.removeListener(config.signal, handleSignal); process.removeListener(config.signal, handleSignal);
if (config.events.includes('signal')) if (config.events.includes('signal'))
process.on(options.signal, handleSignal); process.on(options.signal, handleSignal);
config.signal = options.signal; config.signal = options.signal;
break; break;
case REPORTFILENAME: case REPORTFILENAME:
if (typeof options.filename !== 'string') { if (typeof options.filename !== 'string') {
throw new ERR_INVALID_ARG_TYPE('filename', throw new ERR_INVALID_ARG_TYPE('filename',
'String', 'String',
options.filename); options.filename);
} }
config.filename = options.filename; config.filename = options.filename;
break; break;
case REPORTPATH: case REPORTPATH:
if (typeof options.path !== 'string') if (typeof options.path !== 'string')
throw new ERR_INVALID_ARG_TYPE('path', 'String', options.path); throw new ERR_INVALID_ARG_TYPE('path', 'String', options.path);
config.path = options.path; config.path = options.path;
break; break;
case REPORTVERBOSE: case REPORTVERBOSE:
if (typeof options.verbose !== 'string' && if (typeof options.verbose !== 'string' &&
typeof options.verbose !== 'boolean') { typeof options.verbose !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE('verbose', throw new ERR_INVALID_ARG_TYPE('verbose',
'Booelan | String' + 'Booelan | String' +
' (true|false|yes|no)', ' (true|false|yes|no)',
options.verbose); options.verbose);
} }
config.verbose = options.verbose; config.verbose = options.verbose;
break; break;
} }
}); });
// Upload this new config to C++ land // Upload this new config to C++ land
nr.syncConfig(config, true); nr.syncConfig(config, true);
}, },
triggerReport(file, err) { triggerReport(file, err) {
emitExperimentalWarning('report'); emitExperimentalWarning('report');
if (err == null) { if (err == null) {
if (file == null) { if (file == null) {
return nr.triggerReport(new ERR_SYNTHETIC().stack); return nr.triggerReport(new ERR_SYNTHETIC().stack);
}
if (typeof file !== 'string')
throw new ERR_INVALID_ARG_TYPE('file', 'String', file);
return nr.triggerReport(file, new ERR_SYNTHETIC().stack);
} }
if (typeof err !== 'object')
throw new ERR_INVALID_ARG_TYPE('err', 'Object', err);
if (file == null)
return nr.triggerReport(err.stack);
if (typeof file !== 'string') if (typeof file !== 'string')
throw new ERR_INVALID_ARG_TYPE('file', 'String', file); throw new ERR_INVALID_ARG_TYPE('file', 'String', file);
return nr.triggerReport(file, err.stack); return nr.triggerReport(file, new ERR_SYNTHETIC().stack);
}, }
getReport(err) { if (typeof err !== 'object')
emitExperimentalWarning('report'); throw new ERR_INVALID_ARG_TYPE('err', 'Object', err);
if (err == null) { if (file == null)
return nr.getReport(new ERR_SYNTHETIC().stack); return nr.triggerReport(err.stack);
} else if (typeof err !== 'object') { if (typeof file !== 'string')
throw new ERR_INVALID_ARG_TYPE('err', 'Object', err); throw new ERR_INVALID_ARG_TYPE('file', 'String', file);
} else { return nr.triggerReport(file, err.stack);
return nr.getReport(err.stack); },
} getReport(err) {
emitExperimentalWarning('report');
if (err == null) {
return nr.getReport(new ERR_SYNTHETIC().stack);
} else if (typeof err !== 'object') {
throw new ERR_INVALID_ARG_TYPE('err', 'Object', err);
} else {
return nr.getReport(err.stack);
} }
};
// Download the CLI / ENV config into JS land.
nr.syncConfig(config, false);
function handleSignal(signo) {
if (typeof signo !== 'string')
signo = config.signal;
nr.onUserSignal(signo);
} }
};
if (config.events.includes('signal')) {
process.on(config.signal, handleSignal); function handleSignal(signo) {
} if (typeof signo !== 'string')
signo = config.signal;
function parseOptions(obj) { nr.onUserSignal(signo);
const list = []; }
if (obj == null)
return list; function parseOptions(obj) {
if (obj.events != null) const list = [];
list.push(REPORTEVENTS); if (obj == null)
if (obj.signal != null) return list;
list.push(REPORTSIGNAL); if (obj.events != null)
if (obj.filename != null) list.push(REPORTEVENTS);
list.push(REPORTFILENAME); if (obj.signal != null)
if (obj.path != null) list.push(REPORTSIGNAL);
list.push(REPORTPATH); if (obj.filename != null)
if (obj.verbose != null) list.push(REPORTFILENAME);
list.push(REPORTVERBOSE); if (obj.path != null)
return list; list.push(REPORTPATH);
} if (obj.verbose != null)
process.report = report; list.push(REPORTVERBOSE);
return list;
}
module.exports = {
config,
handleSignal,
report,
syncConfig: nr.syncConfig
}; };