Skip to content

Commit 549216a

Browse files
gireeshpunathiltargos
authored andcommitted
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: #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>
1 parent 219b1b8 commit 549216a

16 files changed

Lines changed: 1995 additions & 2 deletions

‎configure.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,11 @@
493493
dest='without_npm',
494494
help='do not install the bundled npm (package manager)')
495495

496+
parser.add_option('--without-report',
497+
action='store_true',
498+
dest='without_report',
499+
help='build without report')
500+
496501
# Dummy option for backwards compatibility
497502
parser.add_option('--with-snapshot',
498503
action='store_true',
@@ -938,6 +943,7 @@ def configure_node(o):
938943
o['variables']['OS'] ='android'
939944
o['variables']['node_prefix'] =options.prefix
940945
o['variables']['node_install_npm'] =b(notoptions.without_npm)
946+
o['variables']['node_report'] =b(notoptions.without_report)
941947
o['default_configuration'] ='Debug'ifoptions.debugelse'Release'
942948

943949
host_arch=host_arch_win() ifos.name=='nt'elsehost_arch_cc()

‎lib/internal/bootstrap/node.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ function startup() {
352352
}=perf.constants;
353353
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
354354

355+
if(getOptionValue('--experimental-report')){
356+
NativeModule.require('internal/process/report').setup();
357+
}
358+
355359
if(isMainThread){
356360
returnstartMainThreadExecution;
357361
}else{

‎lib/internal/errors.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error);
709709
E('ERR_INSPECTOR_NOT_AVAILABLE','Inspector is not available',Error);
710710
E('ERR_INSPECTOR_NOT_CONNECTED','Session is not connected',Error);
711711
E('ERR_INVALID_ADDRESS_FAMILY','Invalid address family: %s',RangeError);
712+
E('ERR_SYNTHETIC','JavaScript Callstack: %s',Error);
712713
E('ERR_INVALID_ARG_TYPE',
713714
(name,expected,actual)=>{
714715
assert(typeofname==='string',"'name' must be a string");

‎lib/internal/process/execution.js‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,31 @@ function createFatalException() {
9898
// call that threw and was never cleared. So clear it now.
9999
clearDefaultTriggerAsyncId();
100100

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+
constreport=internalBinding('report');
108+
if(report!=null){
109+
if(require('internal/options').getOptionValue(
110+
'--experimental-report')){
111+
constconfig={};
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+
101126
if(exceptionHandlerState.captureFn!==null){
102127
exceptionHandlerState.captureFn(er);
103128
}elseif(!process.emit('uncaughtException',er)){

‎lib/internal/process/report.js‎

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
constREPORTEVENTS=1;
10+
constREPORTSIGNAL=2;
11+
constREPORTFILENAME=3;
12+
constREPORTPATH=4;
13+
constREPORTVERBOSE=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+
constnr=internalBinding('report');
25+
26+
// Keep it un-exposed; lest programs play with it
27+
// leaving us with a lot of unwanted sanity checks.
28+
letconfig={
29+
events: [],
30+
signal: 'SIGUSR2',
31+
filename: '',
32+
path: '',
33+
verbose: false
34+
};
35+
constreport={
36+
setDiagnosticReportOptions(options){
37+
emitExperimentalWarning('report');
38+
// Reuse the null and undefined checks. Save
39+
// space when dealing with large number of arguments.
40+
constlist=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+
caseREPORTEVENTS:
52+
if(Array.isArray(options.events))
53+
config.events=options.events;
54+
else
55+
thrownewERR_INVALID_ARG_TYPE('events',
56+
'Array',
57+
options.events);
58+
break;
59+
caseREPORTSIGNAL:
60+
if(typeofoptions.signal!=='string'){
61+
thrownewERR_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+
break;
70+
caseREPORTFILENAME:
71+
if(typeofoptions.filename!=='string'){
72+
thrownewERR_INVALID_ARG_TYPE('filename',
73+
'String',
74+
options.filename);
75+
}
76+
config.filename=options.filename;
77+
break;
78+
caseREPORTPATH:
79+
if(typeofoptions.path!=='string')
80+
thrownewERR_INVALID_ARG_TYPE('path','String',options.path);
81+
config.path=options.path;
82+
break;
83+
caseREPORTVERBOSE:
84+
if(typeofoptions.verbose!=='string'&&
85+
typeofoptions.verbose!=='boolean'){
86+
thrownewERR_INVALID_ARG_TYPE('verbose',
87+
'Booelan | String'+
88+
' (true|false|yes|no)',
89+
options.verbose);
90+
}
91+
config.verbose=options.verbose;
92+
break;
93+
}
94+
});
95+
// Upload this new config to C++ land
96+
nr.syncConfig(config,true);
97+
},
98+
99+
100+
triggerReport(file,err){
101+
emitExperimentalWarning('report');
102+
if(err==null){
103+
if(file==null){
104+
returnnr.triggerReport(newERR_SYNTHETIC(
105+
'JavaScript Callstack').stack);
106+
}
107+
if(typeoffile!=='string')
108+
thrownewERR_INVALID_ARG_TYPE('file','String',file);
109+
returnnr.triggerReport(file,newERR_SYNTHETIC(
110+
'JavaScript Callstack').stack);
111+
}
112+
if(typeoferr!=='object')
113+
thrownewERR_INVALID_ARG_TYPE('err','Object',err);
114+
if(file==null)
115+
returnnr.triggerReport(err.stack);
116+
if(typeoffile!=='string')
117+
thrownewERR_INVALID_ARG_TYPE('file','String',file);
118+
returnnr.triggerReport(file,err.stack);
119+
},
120+
getReport(err){
121+
emitExperimentalWarning('report');
122+
if(err==null){
123+
returnnr.getReport(newERR_SYNTHETIC('JavaScript Callstack').stack);
124+
}elseif(typeoferr!=='object'){
125+
thrownewERR_INVALID_ARG_TYPE('err','Objct',err);
126+
}else{
127+
returnnr.getReport(err.stack);
128+
}
129+
}
130+
};
131+
132+
// Download the CLI / ENV config into JS land.
133+
nr.syncConfig(config,false);
134+
135+
functionhandleSignal(signo){
136+
if(typeofsigno!=='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+
functionparseOptions(obj){
146+
constlist=[];
147+
if(obj==null)
148+
returnlist;
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+
returnlist;
160+
}
161+
process.report=report;
162+
}
163+
};

‎node.gyp‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
'lib/internal/process/stdio.js',
156156
'lib/internal/process/warning.js',
157157
'lib/internal/process/worker_thread_only.js',
158+
'lib/internal/process/report.js',
158159
'lib/internal/querystring.js',
159160
'lib/internal/queue_microtask.js',
160161
'lib/internal/readline.js',
@@ -313,6 +314,29 @@
313314
# the executable and rename it back to node.exe later
314315
'product_name': '<(node_core_target_name)-win',
315316
}],
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+
}],
316340
],
317341
}, # node_core_target_name
318342
{
@@ -622,6 +646,34 @@
622646
'src/tls_wrap.h'
623647
],
624648
}],
649+
[ 'node_report=="true"', {
650+
'sources': [
651+
'src/node_report.cc',
652+
'src/node_report_module.cc',
653+
'src/node_report_utils.cc',
654+
],
655+
'defines': [
656+
'NODE_REPORT',
657+
'NODE_ARCH="<(target_arch)"',
658+
'NODE_PLATFORM="<(OS)"',
659+
],
660+
'conditions': [
661+
['OS=="win"', {
662+
'libraries': [
663+
'dbghelp.lib',
664+
'Netapi32.lib',
665+
'PsApi.lib',
666+
'Ws2_32.lib',
667+
],
668+
'dll_files': [
669+
'dbghelp.dll',
670+
'Netapi32.dll',
671+
'PsApi.dll',
672+
'Ws2_32.dll',
673+
],
674+
}],
675+
],
676+
}],
625677
[ 'node_use_large_pages=="true" and OS=="linux"', {
626678
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
627679
# The current implementation of Large Pages is under Linux.
@@ -963,6 +1015,29 @@
9631015
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
9641016
},
9651017
}],
1018+
[ 'node_report=="true"', {
1019+
'defines': [
1020+
'NODE_REPORT',
1021+
'NODE_ARCH="<(target_arch)"',
1022+
'NODE_PLATFORM="<(OS)"',
1023+
],
1024+
'conditions': [
1025+
['OS=="win"', {
1026+
'libraries': [
1027+
'dbghelp.lib',
1028+
'Netapi32.lib',
1029+
'PsApi.lib',
1030+
'Ws2_32.lib',
1031+
],
1032+
'dll_files': [
1033+
'dbghelp.dll',
1034+
'Netapi32.dll',
1035+
'PsApi.dll',
1036+
'Ws2_32.dll',
1037+
],
1038+
}],
1039+
],
1040+
}],
9661041
],
9671042
}, # cctest
9681043
], # end targets

‎src/node.cc‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@
8989
#include<unicode/uvernum.h>
9090
#endif
9191

92+
#ifdef NODE_REPORT
93+
#include"node_report.h"
94+
#endif
95+
9296
#if defined(LEAK_SANITIZER)
9397
#include<sanitizer/lsan_interface.h>
9498
#endif
@@ -724,6 +728,12 @@ void RunBootstrapping(Environment* env) {
724728
return;
725729
}
726730

731+
#ifdef NODE_REPORT
732+
if (env->options()->experimental_report) {
733+
report::InitializeReport(env->isolate(), env);
734+
}
735+
#endif// NODE_REPORT
736+
727737
// process, loaderExports, isMainThread
728738
std::vector<Local<String>> node_params = {
729739
env->process_string(),
@@ -960,6 +970,12 @@ int Init(std::vector<std::string>* argv,
960970
// Make inherited handles noninheritable.
961971
uv_disable_stdio_inheritance();
962972

973+
#ifdef NODE_REPORT
974+
// Cache the original command line to be
975+
// used in diagnostic reports.
976+
per_process::cli_options->cmdline = *argv;
977+
#endif// NODE_REPORT
978+
963979
#if defined(NODE_V8_OPTIONS)
964980
// Should come before the call to V8::SetFlagsFromCommandLine()
965981
// so the user can disable a flag --foo at run-time by passing

‎src/node_binding.cc‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
#defineNODE_BUILTIN_ICU_MODULES(V)
1616
#endif
1717

18+
#if NODE_REPORT
19+
#defineNODE_BUILTIN_REPORT_MODULES(V) V(report)
20+
#else
21+
#defineNODE_BUILTIN_REPORT_MODULES(V)
22+
#endif
23+
1824
// A list of built-in modules. In order to do module registration
1925
// in node::Init(), need to add built-in modules in the following list.
2026
// Then in binding::RegisterBuiltinModules(), it calls modules' registration
@@ -70,7 +76,8 @@
7076
#defineNODE_BUILTIN_MODULES(V) \
7177
NODE_BUILTIN_STANDARD_MODULES(V) \
7278
NODE_BUILTIN_OPENSSL_MODULES(V) \
73-
NODE_BUILTIN_ICU_MODULES(V)
79+
NODE_BUILTIN_ICU_MODULES(V) \
80+
NODE_BUILTIN_REPORT_MODULES(V)
7481

7582
// This is used to load built-in modules. Instead of using
7683
// __attribute__((constructor)), we call the _register_<modname>

0 commit comments

Comments
 (0)