summaryrefslogtreecommitdiff
path: root/lib/internal/process
diff options
context:
space:
mode:
authorGireesh Punathil <gpunathi@in.ibm.com>2018-09-05 10:06:59 -0400
committerGireesh Punathil <gpunathi@in.ibm.com>2019-01-18 10:34:04 +0530
commit4f6797378eb6dd290dd5d8dc6417fea22d42ebad (patch)
tree88967037a1ba9aec47faea701efecf4f44007587 /lib/internal/process
parent01cd21973b26a2cbacbe143c5983cb4adf8e7681 (diff)
downloadandroid-node-v8-4f6797378eb6dd290dd5d8dc6417fea22d42ebad.tar.gz
android-node-v8-4f6797378eb6dd290dd5d8dc6417fea22d42ebad.tar.bz2
android-node-v8-4f6797378eb6dd290dd5d8dc6417fea22d42ebad.zip
src: merge into core
Make node-report part of core runtime because: 1. When enabled, node-report significantly helps root cause various types of problems, including support issues sent to the various repos of the Node.js organization. 2. The requirement of explicitly adding the dependency to node-report in user applications often represents a blocker to adoption. Major deviation from the module version of the node-report is that the report is generated in JSON format, as opposed to human readable text. No new functionalities have been added, changes that are required for melding it as a built-in capability has been affected on the module version of node-report (https://github.com/nodejs/node-report) Co-authored-by: Bidisha Pyne <bidipyne@in.ibm.com> Co-authored-by: Howard Hellyer <hhellyer@uk.ibm.com> Co-authored-by: Jeremiah Senkpiel <fishrock123@rocketmail.com> Co-authored-by: Julian Alimin <dmastag@yahoo.com> Co-authored-by: Lakshmi Swetha Gopireddy <lakshmigopireddy@in.ibm.com> Co-authored-by: Manusaporn Treerungroj <m.treerungroj@gmail.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com> Co-authored-by: Richard Chamberlain <richard_chamberlain@uk.ibm.com> Co-authored-by: Richard Lau <riclau@uk.ibm.com> Co-authored-by: Sam Roberts <vieuxtech@gmail.com> Co-authored-by: Vipin Menon <vipinmv1@in.ibm.com> PR-URL: https://github.com/nodejs/node/pull/22712 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <Michael_Dawson@ca.ibm.com> Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Diffstat (limited to 'lib/internal/process')
-rw-r--r--lib/internal/process/execution.js25
-rw-r--r--lib/internal/process/report.js163
2 files changed, 188 insertions, 0 deletions
diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js
index 607829544a..b11f54b35d 100644
--- a/lib/internal/process/execution.js
+++ b/lib/internal/process/execution.js
@@ -98,6 +98,31 @@ function createFatalException() {
// call that threw and was never cleared. So clear it now.
clearDefaultTriggerAsyncId();
+ // If node-report is enabled, call into its handler to see
+ // whether it is interested in handling the situation.
+ // Ignore if the error is scoped inside a domain.
+ // use == in the checks as we want to allow for null and undefined
+ if (er == null || er.domain == null) {
+ try {
+ const report = internalBinding('report');
+ if (report != null) {
+ if (require('internal/options').getOptionValue(
+ '--experimental-report')) {
+ const config = {};
+ report.syncConfig(config, false);
+ if (Array.isArray(config.events) &&
+ config.events.includes('exception')) {
+ if (er) {
+ report.onUnCaughtException(er.stack);
+ } else {
+ report.onUnCaughtException(undefined);
+ }
+ }
+ }
+ }
+ } catch {} // NOOP, node_report unavailable.
+ }
+
if (exceptionHandlerState.captureFn !== null) {
exceptionHandlerState.captureFn(er);
} else if (!process.emit('uncaughtException', er)) {
diff --git a/lib/internal/process/report.js b/lib/internal/process/report.js
new file mode 100644
index 0000000000..2d0d3f3921
--- /dev/null
+++ b/lib/internal/process/report.js
@@ -0,0 +1,163 @@
+'use strict';
+
+const { emitExperimentalWarning } = require('internal/util');
+const {
+ ERR_INVALID_ARG_TYPE,
+ ERR_SYNTHETIC } = require('internal/errors').codes;
+
+exports.setup = function() {
+ const REPORTEVENTS = 1;
+ const REPORTSIGNAL = 2;
+ const REPORTFILENAME = 3;
+ const REPORTPATH = 4;
+ const REPORTVERBOSE = 5;
+ if (internalBinding('config').hasReport) {
+ // 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);
+
+ // 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);
+ },
+
+
+ triggerReport(file, err) {
+ emitExperimentalWarning('report');
+ if (err == null) {
+ if (file == null) {
+ return nr.triggerReport(new ERR_SYNTHETIC(
+ 'JavaScript Callstack').stack);
+ }
+ if (typeof file !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('file', 'String', file);
+ return nr.triggerReport(file, new ERR_SYNTHETIC(
+ 'JavaScript Callstack').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('JavaScript Callstack').stack);
+ } else if (typeof err !== 'object') {
+ throw new ERR_INVALID_ARG_TYPE('err', 'Objct', 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 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);
+ return list;
+ }
+ process.report = report;
+ }
+};