8000
We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
1 parent 219b1b8 commit 549216aCopy full SHA for 549216a
configure.py
@@ -493,6 +493,11 @@
493
dest='without_npm',
494
help='do not install the bundled npm (package manager)')
495
496
+parser.add_option('--without-report',
497
+ action='store_true',
498
+ dest='without_report',
499
+ help='build without report')
500
+
501
# Dummy option for backwards compatibility
502
parser.add_option('--with-snapshot',
503
action='store_true',
@@ -938,6 +943,7 @@ def configure_node(o):
938
943
o['variables']['OS'] = 'android'
939
944
o['variables']['node_prefix'] = options.prefix
940
945
o['variables']['node_install_npm'] = b(not options.without_npm)
946
+ o['variables']['node_report'] = b(not options.without_report)
941
947
o['default_configuration'] = 'Debug' if options.debug else 'Release'
942
948
949
host_arch = host_arch_win() if os.name == 'nt' else host_arch_cc()
lib/internal/bootstrap/node.js
@@ -352,6 +352,10 @@ function startup() {
352
} = perf.constants;
353
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
354
355
+ if (getOptionValue('--experimental-report')) {
356
+ NativeModule.require('internal/process/report').setup();
357
+ }
358
359
if (isMainThread) {
360
return startMainThreadExecution;
361
} else {
lib/internal/errors.js
@@ -709,6 +709,7 @@ E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error);
709
E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error);
710
E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error);
711
E('ERR_INVALID_ADDRESS_FAMILY', 'Invalid address family: %s', RangeError);
712
+E('ERR_SYNTHETIC', 'JavaScript Callstack: %s', Error);
713
E('ERR_INVALID_ARG_TYPE',
714
(name, expected, actual) => {
715
assert(typeof name === 'string', "'name' must be a string");
lib/internal/process/execution.js
@@ -98,6 +98,31 @@ function createFatalException() {
98
// call that threw and was never cleared. So clear it now.
99
clearDefaultTriggerAsyncId();
100
101
+ // If node-report is enabled, call into its handler to see
102
+ // whether it is interested in handling the situation.
103
+ // Ignore if the error is scoped inside a domain.
104
+ // use == in the checks as we want to allow for null and undefined
105
+ if (er == null || er.domain == null) {
106
+ try {
107
+ const report = internalBinding('report');
108
+ if (report != null) {
109
+ if (require('internal/options').getOptionValue(
110
+ '--experimental-report')) {
111
+ const config = {};
112
+ report.syncConfig(config, false);
113
+ if (Array.isArray(config.events) &&
114
+ config.events.includes('exception')) {
115
+ if (er) {
116
+ report.onUnCaughtException(er.stack);
117
+ } else {
118
+ report.onUnCaughtException(undefined);
119
120
121
122
123
+ } catch {} // NOOP, node_report unavailable.
124
125
126
if (exceptionHandlerState.captureFn !== null) {
127
exceptionHandlerState.captureFn(er);
128
} else if (!process.emit('uncaughtException', er)) {
lib/internal/process/report.js
@@ -0,0 +1,163 @@
1
+'use strict';
2
3
+const { emitExperimentalWarning } = require('internal/util');
4
+const {
5
+ ERR_INVALID_ARG_TYPE,
6
+ ERR_SYNTHETIC } = require('internal/errors').codes;
7
8
+exports.setup = function() {
9
+ const REPORTEVENTS = 1;
10
+ const REPORTSIGNAL = 2;
11
+ const REPORTFILENAME = 3;
12
+ const REPORTPATH = 4;
13
+ const REPORTVERBOSE = 5;
14
+ if (internalBinding('config').hasReport) {
15
+ // If report is enabled, extract the binding and
16
+ // wrap the APIs with thin layers, with some error checks.
17
+ // user options can come in from CLI / ENV / API.
18
+ // CLI and ENV is intercepted in C++ and the API call here (JS).
19
+ // So sync up with both sides as appropriate - initially from
20
+ // C++ to JS and from JS to C++ whenever the API is called.
21
+ // Some events are controlled purely from JS (signal | exception)
22
+ // and some from C++ (fatalerror) so this sync-up is essential for
23
+ // correct behavior and alignment with the supplied tunables.
24
+ const nr = internalBinding('report');
25
26
+ // Keep it un-exposed; lest programs play with it
27
+ // leaving us with a lot of unwanted sanity checks.
28
+ let config = {
29
+ events: [],
30
+ signal: 'SIGUSR2',
31
+ filename: '',
32
+ path: '',
33
+ verbose: false
34
+ };
35
+ const report = {
36
+ setDiagnosticReportOptions(options) {
37
+ emitExperimentalWarning('report');
38
+ // Reuse the null and undefined checks. Save
39
+ // space when dealing with large number of arguments.
40
+ const list = parseOptions(options);
41
42
+ // Flush the stale entries from report, as
43
+ // we are refreshing it, items that the users did not
44
+ // touch may be hanging around stale otherwise.
45
+ config = {};
46
47
+ // The parseOption method returns an array that include
48
+ // the indices at which valid params are present.
49
+ list.forEach((i) => {
50
+ switch (i) {
51
+ case REPORTEVENTS:
52
+ if (Array.isArray(options.events))
53
+ config.events = options.events;
54
+ else
55
+ throw new ERR_INVALID_ARG_TYPE('events',
56
+ 'Array',
57
+ options.events);
58
+ break;
59
+ case REPORTSIGNAL:
60
+ if (typeof options.signal !== 'string') {
61
+ throw new ERR_INVALID_ARG_TYPE('signal',
62
+ 'String',
63
+ options.signal);
64
65
+ process.removeListener(config.signal, handleSignal);
66
+ if (config.events.includes('signal'))
67
+ process.on(options.signal, handleSignal);
68
+ config.signal = options.signal;
69
70
+ case REPORTFILENAME:
71
+ if (typeof options.filename !== 'string') {
72
+ throw new ERR_INVALID_ARG_TYPE('filename',
73
74
+ options.filename);
75
76
+ config.filename = options.filename;
77
78
+ case REPORTPATH:
79
+ if (typeof options.path !== 'string')
80
+ throw new ERR_INVALID_ARG_TYPE('path', 'String', options.path);
81
+ config.path = options.path;
82
83
+ case REPORTVERBOSE:
84
+ if (typeof options.verbose !== 'string' &&
85
+ typeof options.verbose !== 'boolean') {
86
+ throw new ERR_INVALID_ARG_TYPE('verbose',
87
+ 'Booelan | String' +
88
+ ' (true|false|yes|no)',
89
+ options.verbose);
90
91
+ config.verbose = options.verbose;
92
93
94
+ });
95
+ // Upload this new config to C++ land
96
+ nr.syncConfig(config, true);
97
+ },
+ triggerReport(file, err) {
+ 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(
+ if (typeof err !== 'object')
+ throw new ERR_INVALID_ARG_TYPE('err', 'Object', err);
+ if (file == null)
+ return nr.triggerReport(err.stack);
+ return nr.triggerReport(file, err.stack);
+ getReport(err) {
+ return nr.getReport(new ERR_SYNTHETIC('JavaScript Callstack').stack);
+ } else if (typeof err !== 'object') {
+ throw new ERR_INVALID_ARG_TYPE('err', 'Objct', err);
+ return nr.getReport(err.stack);
129
130
131
132
+ // Download the CLI / ENV config into JS land.
133
+ nr.syncConfig(config, false);
134
135
+ function handleSignal(signo) {
136
+ if (typeof signo !== 'string')
137
+ signo = config.signal;
138
+ nr.onUserSignal(signo);
139
140
141
+ if (config.events.includes('signal')) {
142
+ process.on(config.signal, handleSignal);
143
144
145
+ function parseOptions(obj) {
146
+ const list = [];
147
+ if (obj == null)
148
+ return list;
149
+ if (obj.events != null)
150
+ list.push(REPORTEVENTS);
151
+ if (obj.signal != null)
152
+ list.push(REPORTSIGNAL);
153
+ if (obj.filename != null)
154
+ list.push(REPORTFILENAME);
155
+ if (obj.path != null)
156
+ list.push(REPORTPATH);
157
+ if (obj.verbose != null)
158
+ list.push(REPORTVERBOSE);
159
160
161
+ process.report = report;
162
163
+};
node.gyp
@@ -155,6 +155,7 @@
'lib/internal/process/stdio.js',
'lib/internal/process/warning.js',
'lib/internal/process/worker_thread_only.js',
+ 'lib/internal/process/report.js',
'lib/internal/querystring.js',
'lib/internal/queue_microtask.js',
'lib/internal/readline.js',
@@ -313,6 +314,29 @@
313
314
# the executable and rename it back to node.exe later
315
'product_name': '<(node_core_target_name)-win',
316
}],
317
+ [ 'node_report=="true"', {
318
+ 'defines': [
319
+ 'NODE_REPORT',
320
+ 'NODE_ARCH="<(target_arch)"',
321
+ 'NODE_PLATFORM="<(OS)"',
322
+ ],
323
+ 'conditions': [
324
+ ['OS=="win"', {
325
+ 'libraries': [
326
+ 'dbghelp.lib',
327
+ 'Netapi32.lib',
328
+ 'PsApi.lib',
329
+ 'Ws2_32.lib',
330
331
+ 'dll_files': [
332
+ 'dbghelp.dll',
333
+ 'Netapi32.dll',
334
+ 'PsApi.dll',
335
+ 'Ws2_32.dll',
336
337
+ }],
338
339
340
],
341
}, # node_core_target_name
342
{
@@ -622,6 +646,34 @@
622
646
'src/tls_wrap.h'
623
647
624
648
649
650
+ 'sources': [
651
+ 'src/node_report.cc',
652
+ 'src/node_report_module.cc',
653
+ 'src/node_report_utils.cc',
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
625
677
[ 'node_use_large_pages=="true" and OS=="linux"', {
626
678
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
627
679
# The current implementation of Large Pages is under Linux.
@@ -963,6 +1015,29 @@
963
1015
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
964
1016
},
965
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
966
1041
967
1042
}, # cctest
968
1043
], # end targets
src/node.cc
@@ -89,6 +89,10 @@
#include <unicode/uvernum.h>
#endif
+#ifdef NODE_REPORT
+#include "node_report.h"
+#endif
#if defined(LEAK_SANITIZER)
#include <sanitizer/lsan_interface.h>
@@ -724,6 +728,12 @@ void RunBootstrapping(Environment* env) {
724
728
return;
725
729
}
726
730
731
732
+ if (env->options()->experimental_report) {
733
+ report::InitializeReport(env->isolate(), env);
734
735
+#endif // NODE_REPORT
736
727
737
// process, loaderExports, isMainThread
738
std::vector<Local<String>> node_params = {
739
env->process_string(),
@@ -960,6 +970,12 @@ int Init(std::vector<std::string>* argv,
960
970
// Make inherited handles noninheritable.
961
971
uv_disable_stdio_inheritance();
962
972
973
974
+ // Cache the original command line to be
975
+ // used in diagnostic reports.
976
+ per_process::cli_options->cmdline = *argv;
977
978
979
#if defined(NODE_V8_OPTIONS)
980
// Should come before the call to V8::SetFlagsFromCommandLine()
981
// so the user can disable a flag --foo at run-time by passing
src/node_binding.cc
@@ -15,6 +15,12 @@
#define NODE_BUILTIN_ICU_MODULES(V)
+#if NODE_REPORT
+#define NODE_BUILTIN_REPORT_MODULES(V) V(report)
+#else
+#define NODE_BUILTIN_REPORT_MODULES(V)
// A list of built-in modules. In order to do module registration
// in node::Init(), need to add built-in modules in the following list.
// Then in binding::RegisterBuiltinModules(), it calls modules' registration
@@ -70,7 +76,8 @@
#define NODE_BUILTIN_MODULES(V) \
NODE_BUILTIN_STANDARD_MODULES(V) \
NODE_BUILTIN_OPENSSL_MODULES(V) \
- NODE_BUILTIN_ICU_MODULES(V)
+ NODE_BUILTIN_ICU_MODULES(V) \
+ NODE_BUILTIN_REPORT_MODULES(V)
// This is used to load built-in modules. Instead of using
// __attribute__((constructor)), we call the _register_<modname>