Skip to content

Commit 6e3901b

Browse files
author
Julien Gilli
committed
Changes according to latest code review
1 parent 20a6656 commit 6e3901b

5 files changed

+148
-42
lines changed

test/common.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,37 @@ exports.fileExists = function(pathname) {
449449
exports.fail = function(msg) {
450450
assert.fail(null, null, msg);
451451
};
452+
453+
// Returns true if the exit code "exitCode" and/or signal name "signal"
454+
// represent the exit code and/or signal name of a node process that aborted,
455+
// false otherwise.
456+
exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) {
457+
// Depending on the compiler used, node will exit with either
458+
// exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT).
459+
var expectedExitCodes = [132, 133, 134];
460+
461+
// On platforms using KSH as the default shell (like SmartOS),
462+
// when a process aborts, KSH exits with an exit code that is
463+
// greater than 256, and thus the exit code emitted with the 'exit'
464+
// event is null and the signal is set to either SIGILL, SIGTRAP,
465+
// or SIGABRT (depending on the compiler).
466+
const expectedSignals = ['SIGILL', 'SIGTRAP', 'SIGABRT'];
467+
468+
// On Windows, v8's base::OS::Abort triggers an access violation,
469+
// which corresponds to exit code 3221225477 (0xC0000005)
470+
if (process.platform === 'win32')
471+
expectedExitCodes = [3221225477];
472+
473+
// When using --abort-on-uncaught-exception, V8 will use
474+
// base::OS::Abort to terminate the process.
475+
// Depending on the compiler used, the shell or other aspects of
476+
// the platform used to build the node binary, this will actually
477+
// make V8 exit by aborting or by raising a signal. In any case,
478+
// one of them (exit code or signal) needs to be set to one of
479+
// the expected exit codes or signals.
480+
if (signal !== null) {
481+
return expectedSignals.indexOf(signal) > -1;
482+
} else {
483+
return expectedExitCodes.indexOf(exitCode) > -1;
484+
}
485+
};

test/parallel/test-domain-no-error-handler-abort-on-uncaught.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,11 @@ if (process.argv[2] === 'child') {
180180

181181
var child = child_process.exec(testCmd);
182182

183-
child.on('exit', function onExit(code, signal) {
184-
assert.ok([132, 133, 134].indexOf(code) !== -1, 'Test at index ' +
185-
testIndex + ' should have aborted but instead exited with code ' +
186-
code + ' and signal ' + signal);
183+
child.on('exit', function onExit(exitCode, signal) {
184+
const errMsg = 'Test at index ' + testIndex + ' should have aborted ' +
185+
'but instead exited with exit code ' + exitCode + ' and signal ' +
186+
signal;
187+
assert(common.nodeProcessAborted(exitCode, signal), errMsg);
187188
});
188189
});
189190
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
'use strict';
2+
3+
// This test makes sure that when throwing an error from a domain, and then
4+
// handling that error in an uncaughtException handler by throwing an error
5+
// again, the exit code, signal and error messages are the ones we expect with
6+
// and without using --abort-on-uncaught-exception.
7+
8+
const common = require('../common');
9+
const assert = require('assert');
10+
const child_process = require('child_process');
11+
const domain = require('domain');
12+
13+
const uncaughtExceptionHandlerErrMsg = 'boom from uncaughtException handler';
14+
const domainErrMsg = 'boom from domain';
15+
16+
const RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE = 42;
17+
18+
if (process.argv[2] === 'child') {
19+
process.on('uncaughtException', common.mustCall(function onUncaught() {
20+
if (process.execArgv.indexOf('--abort-on-uncaught-exception') !== -1) {
21+
// When passing --abort-on-uncaught-exception to the child process,
22+
// we want to make sure that this handler (the process' uncaughtException
23+
// event handler) wasn't called. Unfortunately we can't parse the child
24+
// process' output to do that, since on Windows the standard error output
25+
// is not properly flushed in V8's Isolate::Throw right before the
26+
// process aborts due to an uncaught exception, and thus the error
27+
// message representing the error that was thrown cannot be read by the
28+
// parent process. So instead of parsing the child process' stdandard
29+
// error, the parent process will check that in the case
30+
// --abort-on-uncaught-exception was passed, the process did not exit
31+
// with exit code RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE.
32+
process.exit(RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE);
33+
} else {
34+
// On the other hand, when not passing --abort-on-uncaught-exception to
35+
// the node process, we want to throw in this event handler to make sure
36+
// that the proper error message, exit code and signal are the ones we
37+
// expect.
38+
throw new Error(uncaughtExceptionHandlerErrMsg);
39+
}
40+
}));
41+
42+
const d = domain.create();
43+
d.run(common.mustCall(function() {
44+
throw new Error(domainErrMsg);
45+
}));
46+
} else {
47+
runTestWithoutAbortOnUncaughtException();
48+
runTestWithAbortOnUncaughtException();
49+
}
50+
51+
function runTestWithoutAbortOnUncaughtException() {
52+
child_process.exec(createTestCmdLine(),
53+
function onTestDone(err, stdout, stderr) {
54+
// When _not_ passing --abort-on-uncaught-exception, the process'
55+
// uncaughtException handler _must_ be called, and thus the error
56+
// message must include only the message of the error thrown from the
57+
// process' uncaughtException handler.
58+
assert(stderr.includes(uncaughtExceptionHandlerErrMsg),
59+
'stderr output must include proper uncaughtException handler\'s ' +
60+
'error\'s message');
61+
assert(!stderr.includes(domainErrMsg), 'stderr output must not ' +
62+
'include domain\'s error\'s message');
63+
64+
assert.notEqual(err.code, 0,
65+
'child process should have exited with a non-zero exit code, ' +
66+
'but did not');
67+
});
68+
}
69+
70+
function runTestWithAbortOnUncaughtException() {
71+
child_process.exec(createTestCmdLine({
72+
withAbortOnUncaughtException: true
73+
}), function onTestDone(err, stdout, stderr) {
74+
assert.notEqual(err.code, RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE,
75+
'child process should not have run its uncaughtException event ' +
76+
'handler');
77+
assert(common.nodeProcessAborted(err.code, err.signal),
78+
'process should have aborted, but did not');
79+
});
80+
}
81+
82+
function createTestCmdLine(options) {
83+
var testCmd = '';
84+
85+
if (process.platform !== 'win32') {
86+
// Do not create core files, as it can take a lot of disk space on
87+
// continuous testing and developers' machines
88+
testCmd += 'ulimit -c 0 && ';
89+
}
90+
91+
testCmd += process.argv[0];
92+
93+
if (options && options.withAbortOnUncaughtException) {
94+
testCmd += ' ' + '--abort-on-uncaught-exception';
95+
}
96+
97+
testCmd += ' ' + process.argv[1];
98+
testCmd += ' ' + 'child';
99+
100+
return testCmd;
101+
}

test/parallel/test-domain-uncaught-exception.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/*
44
* The goal of this test is to make sure that errors thrown within domains
5-
* are handled correctly. It checks that the process 'uncaughtException' event
5+
* are handled correctly. It checks that the process' 'uncaughtException' event
66
* is emitted when appropriate, and not emitted when it shouldn't. It also
77
* checks that the proper domain error handlers are called when they should
88
* be called, and not called when they shouldn't.
@@ -56,7 +56,7 @@ function test3() {
5656
/*
5757
* This test creates two nested domains: d3 and d4. d4 doesn't register an
5858
* error handler, but d3 does. The error is handled by the d3 domain and thus
59-
* and 'uncaughtException' event should _not_ be emitted.
59+
* an 'uncaughtException' event should _not_ be emitted.
6060
*/
6161
const d3 = domain.create();
6262
const d4 = domain.create();
@@ -110,7 +110,7 @@ tests.push({
110110

111111
function test5() {
112112
/*
113-
* This test creates two nested domains: d7 and d4. d8 _does_ register an
113+
* This test creates two nested domains: d7 and d8. d8 _does_ register an
114114
* error handler, so throwing within that domain should not emit an uncaught
115115
* exception.
116116
*/
@@ -169,7 +169,7 @@ if (process.argv[2] === 'child') {
169169
} else {
170170
// Run each test's function in a child process. Listen on
171171
// messages sent by each child process and compare expected
172-
// messages defined for each ttest from the actual received messages.
172+
// messages defined for each test with the actual received messages.
173173
tests.forEach(function doTest(test, testIndex) {
174174
const testProcess = child_process.fork(__filename, ['child', testIndex]);
175175

@@ -180,7 +180,7 @@ if (process.argv[2] === 'child') {
180180
test.messagesReceived.push(msg);
181181
});
182182

183-
testProcess.on('exit', function onExit() {
183+
testProcess.on('disconnect', common.mustCall(function onExit() {
184184
// Make sure that all expected messages were sent from the
185185
// child process
186186
test.expectedMessages.forEach(function(expectedMessage) {
@@ -200,6 +200,6 @@ if (process.argv[2] === 'child') {
200200
}
201201
});
202202
}
203-
});
203+
}));
204204
});
205205
}

test/parallel/test-domain-with-abort-on-uncaught-exception.js

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -131,38 +131,8 @@ if (process.argv[2] === 'child') {
131131
// outside of a try/catch block, the process should not exit gracefully
132132
if (!options.useTryCatch && options.throwInDomainErrHandler) {
133133
if (cmdLineOption === '--abort_on_uncaught_exception') {
134-
// If the top-level domain's error handler throws, and only if
135-
// --abort_on_uncaught_exception is passed on the command line,
136-
// the process must abort.
137-
//
138-
// Depending on the compiler used, node will exit with either
139-
// exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT).
140-
expectedExitCodes = [132, 133, 134];
141-
142-
// On platforms using KSH as the default shell (like SmartOS),
143-
// when a process aborts, KSH exits with an exit code that is
144-
// greater than 256, and thus the exit code emitted with the 'exit'
145-
// event is null and the signal is set to either SIGILL, SIGTRAP,
146-
// or SIGABRT (depending on the compiler).
147-
expectedSignals = ['SIGILL', 'SIGTRAP', 'SIGABRT'];
148-
149-
// On Windows, v8's base::OS::Abort triggers an access violation,
150-
// which corresponds to exit code 3221225477 (0xC0000005)
151-
if (process.platform === 'win32')
152-
expectedExitCodes = [3221225477];
153-
154-
// When using --abort-on-uncaught-exception, V8 will use
155-
// base::OS::Abort to terminate the process.
156-
// Depending on the compiler used, the shell or other aspects of
157-
// the platform used to build the node binary, this will actually
158-
// make V8 exit by aborting or by raising a signal. In any case,
159-
// one of them (exit code or signal) needs to be set to one of
160-
// the expected exit codes or signals.
161-
if (signal !== null) {
162-
assert.ok(expectedSignals.indexOf(signal) > -1);
163-
} else {
164-
assert.ok(expectedExitCodes.indexOf(exitCode) > -1);
165-
}
134+
assert(common.nodeProcessAborted(exitCode, signal),
135+
'process should have aborted, but did not');
166136
} else {
167137
// By default, uncaught exceptions make node exit with an exit
168138
// code of 7.

0 commit comments

Comments
 (0)