Skip to content

Commit

Permalink
process: move process mutation into bootstrap/node.js
Browse files Browse the repository at this point in the history
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: #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>
  • Loading branch information
joyeecheung authored and targos committed Feb 10, 2019
1 parent 1d76ba1 commit c8bf432
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 140 deletions.
13 changes: 12 additions & 1 deletion lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,18 @@ if (process.env.NODE_V8_COVERAGE) {
}

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() {
Expand Down
276 changes: 137 additions & 139 deletions lib/internal/process/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,158 +3,156 @@
const { emitExperimentalWarning } = require('internal/util');
const {
ERR_INVALID_ARG_TYPE,
ERR_SYNTHETIC } = require('internal/errors').codes;
ERR_SYNTHETIC
} = require('internal/errors').codes;

exports.setup = function() {
const REPORTEVENTS = 1;
const REPORTSIGNAL = 2;
const REPORTFILENAME = 3;
const REPORTPATH = 4;
const REPORTVERBOSE = 5;
const REPORTEVENTS = 1;
const REPORTSIGNAL = 2;
const REPORTFILENAME = 3;
const REPORTPATH = 4;
const REPORTVERBOSE = 5;

// If report is enabled, extract the binding and
// wrap the APIs with thin layers, with some error checks.
// user options can come in from CLI / ENV / API.
// CLI and ENV is intercepted in C++ and the API call here (JS).
// So sync up with both sides as appropriate - initially from
// C++ to JS and from JS to C++ whenever the API is called.
// Some events are controlled purely from JS (signal | exception)
// and some from C++ (fatalerror) so this sync-up is essential for
// correct behavior and alignment with the supplied tunables.
const nr = internalBinding('report');
// If report is enabled, extract the binding and
// wrap the APIs with thin layers, with some error checks.
// user options can come in from CLI / ENV / API.
// CLI and ENV is intercepted in C++ and the API call here (JS).
// So sync up with both sides as appropriate - initially from
// C++ to JS and from JS to C++ whenever the API is called.
// Some events are controlled purely from JS (signal | exception)
// and some from C++ (fatalerror) so this sync-up is essential for
// correct behavior and alignment with the supplied tunables.
const nr = internalBinding('report');

// Keep it un-exposed; lest programs play with it
// leaving us with a lot of unwanted sanity checks.
let config = {
events: [],
signal: 'SIGUSR2',
filename: '',
path: '',
verbose: false
};
const report = {
setDiagnosticReportOptions(options) {
emitExperimentalWarning('report');
// Reuse the null and undefined checks. Save
// space when dealing with large number of arguments.
const list = parseOptions(options);
// Keep it un-exposed; lest programs play with it
// leaving us with a lot of unwanted sanity checks.
let config = {
events: [],
signal: 'SIGUSR2',
filename: '',
path: '',
verbose: false
};
const report = {
setDiagnosticReportOptions(options) {
emitExperimentalWarning('report');
// Reuse the null and undefined checks. Save
// space when dealing with large number of arguments.
const list = parseOptions(options);

// Flush the stale entries from report, as
// we are refreshing it, items that the users did not
// touch may be hanging around stale otherwise.
config = {};
// Flush the stale entries from report, as
// we are refreshing it, items that the users did not
// touch may be hanging around stale otherwise.
config = {};

// The parseOption method returns an array that include
// the indices at which valid params are present.
list.forEach((i) => {
switch (i) {
case REPORTEVENTS:
if (Array.isArray(options.events))
config.events = options.events;
else
throw new ERR_INVALID_ARG_TYPE('events',
'Array',
options.events);
break;
case REPORTSIGNAL:
if (typeof options.signal !== 'string') {
throw new ERR_INVALID_ARG_TYPE('signal',
'String',
options.signal);
}
process.removeListener(config.signal, handleSignal);
if (config.events.includes('signal'))
process.on(options.signal, handleSignal);
config.signal = options.signal;
break;
case REPORTFILENAME:
if (typeof options.filename !== 'string') {
throw new ERR_INVALID_ARG_TYPE('filename',
'String',
options.filename);
}
config.filename = options.filename;
break;
case REPORTPATH:
if (typeof options.path !== 'string')
throw new ERR_INVALID_ARG_TYPE('path', 'String', options.path);
config.path = options.path;
break;
case REPORTVERBOSE:
if (typeof options.verbose !== 'string' &&
typeof options.verbose !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE('verbose',
'Booelan | String' +
' (true|false|yes|no)',
options.verbose);
}
config.verbose = options.verbose;
break;
}
});
// Upload this new config to C++ land
nr.syncConfig(config, true);
},
// The parseOption method returns an array that include
// the indices at which valid params are present.
list.forEach((i) => {
switch (i) {
case REPORTEVENTS:
if (Array.isArray(options.events))
config.events = options.events;
else
throw new ERR_INVALID_ARG_TYPE('events',
'Array',
options.events);
break;
case REPORTSIGNAL:
if (typeof options.signal !== 'string') {
throw new ERR_INVALID_ARG_TYPE('signal',
'String',
options.signal);
}
process.removeListener(config.signal, handleSignal);
if (config.events.includes('signal'))
process.on(options.signal, handleSignal);
config.signal = options.signal;
break;
case REPORTFILENAME:
if (typeof options.filename !== 'string') {
throw new ERR_INVALID_ARG_TYPE('filename',
'String',
options.filename);
}
config.filename = options.filename;
break;
case REPORTPATH:
if (typeof options.path !== 'string')
throw new ERR_INVALID_ARG_TYPE('path', 'String', options.path);
config.path = options.path;
break;
case REPORTVERBOSE:
if (typeof options.verbose !== 'string' &&
typeof options.verbose !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE('verbose',
'Booelan | String' +
' (true|false|yes|no)',
options.verbose);
}
config.verbose = options.verbose;
break;
}
});
// Upload this new config to C++ land
nr.syncConfig(config, true);
},


triggerReport(file, err) {
emitExperimentalWarning('report');
if (err == null) {
if (file == null) {
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);
triggerReport(file, err) {
emitExperimentalWarning('report');
if (err == null) {
if (file == null) {
return nr.triggerReport(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')
throw new ERR_INVALID_ARG_TYPE('file', 'String', file);
return nr.triggerReport(file, 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);
}
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')
throw new ERR_INVALID_ARG_TYPE('file', 'String', file);
return nr.triggerReport(file, 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;
nr.onUserSignal(signo);
}

function parseOptions(obj) {
const list = [];
if (obj == null)
return list;
if (obj.events != null)
list.push(REPORTEVENTS);
if (obj.signal != null)
list.push(REPORTSIGNAL);
if (obj.filename != null)
list.push(REPORTFILENAME);
if (obj.path != null)
list.push(REPORTPATH);
if (obj.verbose != null)
list.push(REPORTVERBOSE);
function parseOptions(obj) {
const list = [];
if (obj == null)
return list;
}
process.report = report;
if (obj.events != null)
list.push(REPORTEVENTS);
if (obj.signal != null)
list.push(REPORTSIGNAL);
if (obj.filename != null)
list.push(REPORTFILENAME);
if (obj.path != null)
list.push(REPORTPATH);
if (obj.verbose != null)
list.push(REPORTVERBOSE);
return list;
}

module.exports = {
config,
handleSignal,
report,
syncConfig: nr.syncConfig
};

0 comments on commit c8bf432

Please sign in to comment.