async_hooks: skip runtime checks when disabled
PR-URL: https://github.com/nodejs/node/pull/15454 Ref: https://github.com/nodejs/node/pull/14387 Ref: https://github.com/nodejs/node/pull/14722 Ref: https://github.com/nodejs/node/issues/14717 Ref: https://github.com/nodejs/node/issues/15448 Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
f8ef2e2bf9
commit
7c079d1261
@ -208,6 +208,14 @@ added: v2.1.0
|
|||||||
Prints a stack trace whenever synchronous I/O is detected after the first turn
|
Prints a stack trace whenever synchronous I/O is detected after the first turn
|
||||||
of the event loop.
|
of the event loop.
|
||||||
|
|
||||||
|
### `--force-async-hooks-checks`
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
Enables runtime checks for `async_hooks`. These can also be enabled dynamically
|
||||||
|
by enabling one of the `async_hooks` hooks.
|
||||||
|
|
||||||
### `--trace-events-enabled`
|
### `--trace-events-enabled`
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v7.7.0
|
added: v7.7.0
|
||||||
|
@ -152,6 +152,11 @@ Write process warnings to the given file instead of printing to stderr.
|
|||||||
Print a stack trace whenever synchronous I/O is detected after the first turn
|
Print a stack trace whenever synchronous I/O is detected after the first turn
|
||||||
of the event loop.
|
of the event loop.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-force\-async\-hooks\-checks
|
||||||
|
Enables runtime checks for `async_hooks`. These can also be enabled dynamically
|
||||||
|
by enabling one of the `async_hooks` hooks.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-trace\-events\-enabled
|
.BR \-\-trace\-events\-enabled
|
||||||
Enables the collection of trace event tracing information.
|
Enables the collection of trace event tracing information.
|
||||||
|
@ -262,7 +262,15 @@ function _writeRaw(data, encoding, callback) {
|
|||||||
this._flushOutput(conn);
|
this._flushOutput(conn);
|
||||||
} else if (!data.length) {
|
} else if (!data.length) {
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
nextTick(this.socket[async_id_symbol], callback);
|
let socketAsyncId = this.socket[async_id_symbol];
|
||||||
|
// If the socket was set directly it won't be correctly initialized
|
||||||
|
// with an async_id_symbol.
|
||||||
|
// TODO(AndreasMadsen): @trevnorris suggested some more correct
|
||||||
|
// solutions in:
|
||||||
|
// https://github.com/nodejs/node/pull/14389/files#r128522202
|
||||||
|
if (socketAsyncId === undefined) socketAsyncId = null;
|
||||||
|
|
||||||
|
nextTick(socketAsyncId, callback);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ const active_hooks = {
|
|||||||
// async execution. These are tracked so if the user didn't include callbacks
|
// async execution. These are tracked so if the user didn't include callbacks
|
||||||
// for a given step, that step can bail out early.
|
// for a given step, that step can bail out early.
|
||||||
const { kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve,
|
const { kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve,
|
||||||
kExecutionAsyncId, kTriggerAsyncId, kAsyncIdCounter,
|
kCheck, kExecutionAsyncId, kTriggerAsyncId, kAsyncIdCounter,
|
||||||
kInitTriggerAsyncId } = async_wrap.constants;
|
kInitTriggerAsyncId } = async_wrap.constants;
|
||||||
|
|
||||||
// Symbols used to store the respective ids on both AsyncResource instances and
|
// Symbols used to store the respective ids on both AsyncResource instances and
|
||||||
@ -156,8 +156,10 @@ class AsyncHook {
|
|||||||
hook_fields[kPromiseResolve] += +!!this[promise_resolve_symbol];
|
hook_fields[kPromiseResolve] += +!!this[promise_resolve_symbol];
|
||||||
hooks_array.push(this);
|
hooks_array.push(this);
|
||||||
|
|
||||||
if (prev_kTotals === 0 && hook_fields[kTotals] > 0)
|
if (prev_kTotals === 0 && hook_fields[kTotals] > 0) {
|
||||||
enablePromiseHook();
|
enablePromiseHook();
|
||||||
|
hook_fields[kCheck] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -180,8 +182,10 @@ class AsyncHook {
|
|||||||
hook_fields[kPromiseResolve] -= +!!this[promise_resolve_symbol];
|
hook_fields[kPromiseResolve] -= +!!this[promise_resolve_symbol];
|
||||||
hooks_array.splice(index, 1);
|
hooks_array.splice(index, 1);
|
||||||
|
|
||||||
if (prev_kTotals > 0 && hook_fields[kTotals] === 0)
|
if (prev_kTotals > 0 && hook_fields[kTotals] === 0) {
|
||||||
disablePromiseHook();
|
disablePromiseHook();
|
||||||
|
hook_fields[kCheck] -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -243,6 +247,15 @@ function triggerAsyncId() {
|
|||||||
return async_id_fields[kTriggerAsyncId];
|
return async_id_fields[kTriggerAsyncId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateAsyncId(asyncId, type) {
|
||||||
|
// Skip validation when async_hooks is disabled
|
||||||
|
if (async_hook_fields[kCheck] <= 0) return;
|
||||||
|
|
||||||
|
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
||||||
|
fatalError(new errors.RangeError('ERR_INVALID_ASYNC_ID', type, asyncId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Embedder API //
|
// Embedder API //
|
||||||
|
|
||||||
@ -337,10 +350,16 @@ function setInitTriggerId(triggerAsyncId) {
|
|||||||
|
|
||||||
|
|
||||||
function emitInitScript(asyncId, type, triggerAsyncId, resource) {
|
function emitInitScript(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
validateAsyncId(asyncId, 'asyncId');
|
||||||
|
if (triggerAsyncId !== null)
|
||||||
|
validateAsyncId(triggerAsyncId, 'triggerAsyncId');
|
||||||
|
if (async_hook_fields[kCheck] > 0 &&
|
||||||
|
(typeof type !== 'string' || type.length <= 0)) {
|
||||||
|
throw new errors.TypeError('ERR_ASYNC_TYPE', type);
|
||||||
|
}
|
||||||
|
|
||||||
// Short circuit all checks for the common case. Which is that no hooks have
|
// Short circuit all checks for the common case. Which is that no hooks have
|
||||||
// been set. Do this to remove performance impact for embedders (and core).
|
// been set. Do this to remove performance impact for embedders (and core).
|
||||||
// Even though it bypasses all the argument checks. The performance savings
|
|
||||||
// here is critical.
|
|
||||||
if (async_hook_fields[kInit] === 0)
|
if (async_hook_fields[kInit] === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -354,18 +373,6 @@ function emitInitScript(asyncId, type, triggerAsyncId, resource) {
|
|||||||
async_id_fields[kInitTriggerAsyncId] = 0;
|
async_id_fields[kInitTriggerAsyncId] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
|
||||||
throw new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId);
|
|
||||||
}
|
|
||||||
if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) {
|
|
||||||
throw new errors.RangeError('ERR_INVALID_ASYNC_ID',
|
|
||||||
'triggerAsyncId',
|
|
||||||
triggerAsyncId);
|
|
||||||
}
|
|
||||||
if (typeof type !== 'string' || type.length <= 0) {
|
|
||||||
throw new errors.TypeError('ERR_ASYNC_TYPE', type);
|
|
||||||
}
|
|
||||||
|
|
||||||
emitInitNative(asyncId, type, triggerAsyncId, resource);
|
emitInitNative(asyncId, type, triggerAsyncId, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,15 +418,8 @@ function emitBeforeScript(asyncId, triggerAsyncId) {
|
|||||||
// Validate the ids. An id of -1 means it was never set and is visible on the
|
// Validate the ids. An id of -1 means it was never set and is visible on the
|
||||||
// call graph. An id < -1 should never happen in any circumstance. Throw
|
// call graph. An id < -1 should never happen in any circumstance. Throw
|
||||||
// on user calls because async state should still be recoverable.
|
// on user calls because async state should still be recoverable.
|
||||||
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
validateAsyncId(asyncId, 'asyncId');
|
||||||
fatalError(
|
validateAsyncId(triggerAsyncId, 'triggerAsyncId');
|
||||||
new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId));
|
|
||||||
}
|
|
||||||
if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) {
|
|
||||||
fatalError(new errors.RangeError('ERR_INVALID_ASYNC_ID',
|
|
||||||
'triggerAsyncId',
|
|
||||||
triggerAsyncId));
|
|
||||||
}
|
|
||||||
|
|
||||||
pushAsyncIds(asyncId, triggerAsyncId);
|
pushAsyncIds(asyncId, triggerAsyncId);
|
||||||
|
|
||||||
@ -429,10 +429,7 @@ function emitBeforeScript(asyncId, triggerAsyncId) {
|
|||||||
|
|
||||||
|
|
||||||
function emitAfterScript(asyncId) {
|
function emitAfterScript(asyncId) {
|
||||||
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
validateAsyncId(asyncId, 'asyncId');
|
||||||
fatalError(
|
|
||||||
new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (async_hook_fields[kAfter] > 0)
|
if (async_hook_fields[kAfter] > 0)
|
||||||
emitAfterNative(asyncId);
|
emitAfterNative(asyncId);
|
||||||
@ -442,10 +439,7 @@ function emitAfterScript(asyncId) {
|
|||||||
|
|
||||||
|
|
||||||
function emitDestroyScript(asyncId) {
|
function emitDestroyScript(asyncId) {
|
||||||
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
validateAsyncId(asyncId, 'asyncId');
|
||||||
fatalError(
|
|
||||||
new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return early if there are no destroy callbacks, or invalid asyncId.
|
// Return early if there are no destroy callbacks, or invalid asyncId.
|
||||||
if (async_hook_fields[kDestroy] === 0 || asyncId <= 0)
|
if (async_hook_fields[kDestroy] === 0 || asyncId <= 0)
|
||||||
|
@ -281,7 +281,7 @@ function setupNextTick() {
|
|||||||
if (process._exiting)
|
if (process._exiting)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId <= 0) {
|
if (triggerAsyncId === null) {
|
||||||
triggerAsyncId = async_hooks.initTriggerId();
|
triggerAsyncId = async_hooks.initTriggerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,6 +526,7 @@ void AsyncWrap::Initialize(Local<Object> target,
|
|||||||
SET_HOOKS_CONSTANT(kDestroy);
|
SET_HOOKS_CONSTANT(kDestroy);
|
||||||
SET_HOOKS_CONSTANT(kPromiseResolve);
|
SET_HOOKS_CONSTANT(kPromiseResolve);
|
||||||
SET_HOOKS_CONSTANT(kTotals);
|
SET_HOOKS_CONSTANT(kTotals);
|
||||||
|
SET_HOOKS_CONSTANT(kCheck);
|
||||||
SET_HOOKS_CONSTANT(kExecutionAsyncId);
|
SET_HOOKS_CONSTANT(kExecutionAsyncId);
|
||||||
SET_HOOKS_CONSTANT(kTriggerAsyncId);
|
SET_HOOKS_CONSTANT(kTriggerAsyncId);
|
||||||
SET_HOOKS_CONSTANT(kAsyncIdCounter);
|
SET_HOOKS_CONSTANT(kAsyncIdCounter);
|
||||||
|
@ -129,10 +129,19 @@ inline v8::Local<v8::String> Environment::AsyncHooks::provider_string(int idx) {
|
|||||||
return providers_[idx].Get(isolate_);
|
return providers_[idx].Get(isolate_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Environment::AsyncHooks::force_checks() {
|
||||||
|
// fields_ does not have the += operator defined
|
||||||
|
fields_[kCheck] = fields_[kCheck] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
inline void Environment::AsyncHooks::push_async_ids(double async_id,
|
inline void Environment::AsyncHooks::push_async_ids(double async_id,
|
||||||
double trigger_async_id) {
|
double trigger_async_id) {
|
||||||
CHECK_GE(async_id, -1);
|
// Since async_hooks is experimental, do only perform the check
|
||||||
CHECK_GE(trigger_async_id, -1);
|
// when async_hooks is enabled.
|
||||||
|
if (fields_[kCheck] > 0) {
|
||||||
|
CHECK_GE(async_id, -1);
|
||||||
|
CHECK_GE(trigger_async_id, -1);
|
||||||
|
}
|
||||||
|
|
||||||
async_ids_stack_.push({ async_id_fields_[kExecutionAsyncId],
|
async_ids_stack_.push({ async_id_fields_[kExecutionAsyncId],
|
||||||
async_id_fields_[kTriggerAsyncId] });
|
async_id_fields_[kTriggerAsyncId] });
|
||||||
@ -145,9 +154,11 @@ inline bool Environment::AsyncHooks::pop_async_id(double async_id) {
|
|||||||
// stack was multiple MakeCallback()'s deep.
|
// stack was multiple MakeCallback()'s deep.
|
||||||
if (async_ids_stack_.empty()) return false;
|
if (async_ids_stack_.empty()) return false;
|
||||||
|
|
||||||
// Ask for the async_id to be restored as a sanity check that the stack
|
// Ask for the async_id to be restored as a check that the stack
|
||||||
// hasn't been corrupted.
|
// hasn't been corrupted.
|
||||||
if (async_id_fields_[kExecutionAsyncId] != async_id) {
|
// Since async_hooks is experimental, do only perform the check
|
||||||
|
// when async_hooks is enabled.
|
||||||
|
if (fields_[kCheck] > 0 && async_id_fields_[kExecutionAsyncId] != async_id) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Error: async hook stack has become corrupted ("
|
"Error: async hook stack has become corrupted ("
|
||||||
"actual: %.f, expected: %.f)\n",
|
"actual: %.f, expected: %.f)\n",
|
||||||
@ -185,7 +196,9 @@ inline Environment::AsyncHooks::InitScope::InitScope(
|
|||||||
Environment* env, double init_trigger_async_id)
|
Environment* env, double init_trigger_async_id)
|
||||||
: env_(env),
|
: env_(env),
|
||||||
async_id_fields_ref_(env->async_hooks()->async_id_fields()) {
|
async_id_fields_ref_(env->async_hooks()->async_id_fields()) {
|
||||||
CHECK_GE(init_trigger_async_id, -1);
|
if (env_->async_hooks()->fields()[AsyncHooks::kCheck] > 0) {
|
||||||
|
CHECK_GE(init_trigger_async_id, -1);
|
||||||
|
}
|
||||||
env->async_hooks()->push_async_ids(
|
env->async_hooks()->push_async_ids(
|
||||||
async_id_fields_ref_[AsyncHooks::kExecutionAsyncId],
|
async_id_fields_ref_[AsyncHooks::kExecutionAsyncId],
|
||||||
init_trigger_async_id);
|
init_trigger_async_id);
|
||||||
|
@ -387,6 +387,7 @@ class Environment {
|
|||||||
kDestroy,
|
kDestroy,
|
||||||
kPromiseResolve,
|
kPromiseResolve,
|
||||||
kTotals,
|
kTotals,
|
||||||
|
kCheck,
|
||||||
kFieldsCount,
|
kFieldsCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -408,6 +409,8 @@ class Environment {
|
|||||||
|
|
||||||
inline v8::Local<v8::String> provider_string(int idx);
|
inline v8::Local<v8::String> provider_string(int idx);
|
||||||
|
|
||||||
|
inline void force_checks();
|
||||||
|
|
||||||
inline void push_async_ids(double async_id, double trigger_async_id);
|
inline void push_async_ids(double async_id, double trigger_async_id);
|
||||||
inline bool pop_async_id(double async_id);
|
inline bool pop_async_id(double async_id);
|
||||||
inline size_t stack_size();
|
inline size_t stack_size();
|
||||||
|
22
src/node.cc
22
src/node.cc
@ -173,6 +173,7 @@ static bool syntax_check_only = false;
|
|||||||
static bool trace_deprecation = false;
|
static bool trace_deprecation = false;
|
||||||
static bool throw_deprecation = false;
|
static bool throw_deprecation = false;
|
||||||
static bool trace_sync_io = false;
|
static bool trace_sync_io = false;
|
||||||
|
static bool force_async_hooks_checks = false;
|
||||||
static bool track_heap_objects = false;
|
static bool track_heap_objects = false;
|
||||||
static const char* eval_string = nullptr;
|
static const char* eval_string = nullptr;
|
||||||
static std::vector<std::string> preload_modules;
|
static std::vector<std::string> preload_modules;
|
||||||
@ -1440,8 +1441,10 @@ void InternalCallbackScope::Close() {
|
|||||||
|
|
||||||
// Make sure the stack unwound properly. If there are nested MakeCallback's
|
// Make sure the stack unwound properly. If there are nested MakeCallback's
|
||||||
// then it should return early and not reach this code.
|
// then it should return early and not reach this code.
|
||||||
CHECK_EQ(env_->execution_async_id(), 0);
|
if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
|
||||||
CHECK_EQ(env_->trigger_async_id(), 0);
|
CHECK_EQ(env_->execution_async_id(), 0);
|
||||||
|
CHECK_EQ(env_->trigger_async_id(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
Local<Object> process = env_->process_object();
|
Local<Object> process = env_->process_object();
|
||||||
|
|
||||||
@ -1450,8 +1453,10 @@ void InternalCallbackScope::Close() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_EQ(env_->execution_async_id(), 0);
|
if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
|
||||||
CHECK_EQ(env_->trigger_async_id(), 0);
|
CHECK_EQ(env_->execution_async_id(), 0);
|
||||||
|
CHECK_EQ(env_->trigger_async_id(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
|
if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
|
||||||
failed_ = true;
|
failed_ = true;
|
||||||
@ -3894,6 +3899,8 @@ static void PrintHelp() {
|
|||||||
" stderr\n"
|
" stderr\n"
|
||||||
" --trace-sync-io show stack trace when use of sync IO\n"
|
" --trace-sync-io show stack trace when use of sync IO\n"
|
||||||
" is detected after the first tick\n"
|
" is detected after the first tick\n"
|
||||||
|
" --force-async-hooks-checks\n"
|
||||||
|
" enables checks for async_hooks\n"
|
||||||
" --trace-events-enabled track trace events\n"
|
" --trace-events-enabled track trace events\n"
|
||||||
" --trace-event-categories comma separated list of trace event\n"
|
" --trace-event-categories comma separated list of trace event\n"
|
||||||
" categories to record\n"
|
" categories to record\n"
|
||||||
@ -4019,6 +4026,7 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env,
|
|||||||
"--trace-warnings",
|
"--trace-warnings",
|
||||||
"--redirect-warnings",
|
"--redirect-warnings",
|
||||||
"--trace-sync-io",
|
"--trace-sync-io",
|
||||||
|
"--force-async-hooks-checks",
|
||||||
"--trace-events-enabled",
|
"--trace-events-enabled",
|
||||||
"--trace-events-categories",
|
"--trace-events-categories",
|
||||||
"--track-heap-objects",
|
"--track-heap-objects",
|
||||||
@ -4157,6 +4165,8 @@ static void ParseArgs(int* argc,
|
|||||||
trace_deprecation = true;
|
trace_deprecation = true;
|
||||||
} else if (strcmp(arg, "--trace-sync-io") == 0) {
|
} else if (strcmp(arg, "--trace-sync-io") == 0) {
|
||||||
trace_sync_io = true;
|
trace_sync_io = true;
|
||||||
|
} else if (strcmp(arg, "--force-async-hooks-checks") == 0) {
|
||||||
|
force_async_hooks_checks = true;
|
||||||
} else if (strcmp(arg, "--trace-events-enabled") == 0) {
|
} else if (strcmp(arg, "--trace-events-enabled") == 0) {
|
||||||
trace_enabled = true;
|
trace_enabled = true;
|
||||||
} else if (strcmp(arg, "--trace-event-categories") == 0) {
|
} else if (strcmp(arg, "--trace-event-categories") == 0) {
|
||||||
@ -4805,6 +4815,10 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
|
|||||||
|
|
||||||
env.set_abort_on_uncaught_exception(abort_on_uncaught_exception);
|
env.set_abort_on_uncaught_exception(abort_on_uncaught_exception);
|
||||||
|
|
||||||
|
if (force_async_hooks_checks) {
|
||||||
|
env.async_hooks()->force_checks();
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Environment::AsyncCallbackScope callback_scope(&env);
|
Environment::AsyncCallbackScope callback_scope(&env);
|
||||||
env.async_hooks()->push_async_ids(1, 0);
|
env.async_hooks()->push_async_ids(1, 0);
|
||||||
|
@ -2,13 +2,10 @@
|
|||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const spawnSync = require('child_process').spawnSync;
|
||||||
const async_hooks = require('async_hooks');
|
const async_hooks = require('async_hooks');
|
||||||
const initHooks = require('./init-hooks');
|
const initHooks = require('./init-hooks');
|
||||||
|
|
||||||
// Verify that if there is no registered hook, then those invalid parameters
|
|
||||||
// won't be checked.
|
|
||||||
assert.doesNotThrow(() => async_hooks.emitInit());
|
|
||||||
|
|
||||||
const expectedId = async_hooks.newUid();
|
const expectedId = async_hooks.newUid();
|
||||||
const expectedTriggerId = async_hooks.newUid();
|
const expectedTriggerId = async_hooks.newUid();
|
||||||
const expectedType = 'test_emit_init_type';
|
const expectedType = 'test_emit_init_type';
|
||||||
@ -25,24 +22,40 @@ const hooks1 = initHooks({
|
|||||||
|
|
||||||
hooks1.enable();
|
hooks1.enable();
|
||||||
|
|
||||||
assert.throws(() => {
|
switch (process.argv[2]) {
|
||||||
async_hooks.emitInit();
|
case 'test_invalid_async_id':
|
||||||
}, common.expectsError({
|
async_hooks.emitInit();
|
||||||
code: 'ERR_INVALID_ASYNC_ID',
|
return;
|
||||||
type: RangeError,
|
case 'test_invalid_trigger_id':
|
||||||
}));
|
async_hooks.emitInit(expectedId);
|
||||||
assert.throws(() => {
|
return;
|
||||||
async_hooks.emitInit(expectedId);
|
case 'test_invalid_trigger_id_negative':
|
||||||
}, common.expectsError({
|
async_hooks.emitInit(expectedId, expectedType, -2);
|
||||||
code: 'ERR_INVALID_ASYNC_ID',
|
return;
|
||||||
type: RangeError,
|
}
|
||||||
}));
|
assert.ok(!process.argv[2]);
|
||||||
assert.throws(() => {
|
|
||||||
async_hooks.emitInit(expectedId, expectedType, -2);
|
|
||||||
}, common.expectsError({
|
const c1 = spawnSync(process.execPath, [__filename, 'test_invalid_async_id']);
|
||||||
code: 'ERR_INVALID_ASYNC_ID',
|
assert.strictEqual(
|
||||||
type: RangeError,
|
c1.stderr.toString().split(/[\r\n]+/g)[0],
|
||||||
}));
|
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid asyncId value: undefined');
|
||||||
|
assert.strictEqual(c1.status, 1);
|
||||||
|
|
||||||
|
const c2 = spawnSync(process.execPath, [__filename, 'test_invalid_trigger_id']);
|
||||||
|
assert.strictEqual(
|
||||||
|
c2.stderr.toString().split(/[\r\n]+/g)[0],
|
||||||
|
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid triggerAsyncId value: undefined');
|
||||||
|
assert.strictEqual(c2.status, 1);
|
||||||
|
|
||||||
|
const c3 = spawnSync(process.execPath, [
|
||||||
|
__filename, 'test_invalid_trigger_id_negative'
|
||||||
|
]);
|
||||||
|
assert.strictEqual(
|
||||||
|
c3.stderr.toString().split(/[\r\n]+/g)[0],
|
||||||
|
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid triggerAsyncId value: -2');
|
||||||
|
assert.strictEqual(c3.status, 1);
|
||||||
|
|
||||||
|
|
||||||
async_hooks.emitInit(expectedId, expectedType, expectedTriggerId,
|
async_hooks.emitInit(expectedId, expectedType, expectedTriggerId,
|
||||||
expectedResource);
|
expectedResource);
|
||||||
|
25
test/async-hooks/test-force-checks-flag.js
Normal file
25
test/async-hooks/test-force-checks-flag.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const async_hooks = require('async_hooks');
|
||||||
|
const spawnSync = require('child_process').spawnSync;
|
||||||
|
|
||||||
|
// disabling this way will just decrement the kCheck counter. It won't actually
|
||||||
|
// disable the checks, because they were enabled with the flag.
|
||||||
|
common.revert_force_async_hooks_checks();
|
||||||
|
|
||||||
|
switch (process.argv[2]) {
|
||||||
|
case 'test_invalid_async_id':
|
||||||
|
async_hooks.emitInit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert.ok(!process.argv[2]);
|
||||||
|
|
||||||
|
|
||||||
|
const c1 = spawnSync(process.execPath, [
|
||||||
|
'--force-async-hooks-checks', __filename, 'test_invalid_async_id'
|
||||||
|
]);
|
||||||
|
assert.strictEqual(
|
||||||
|
c1.stderr.toString().split(/[\r\n]+/g)[0],
|
||||||
|
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid asyncId value: undefined');
|
||||||
|
assert.strictEqual(c1.status, 1);
|
@ -26,11 +26,6 @@ internal.nextTick(null, common.mustCall(function() {
|
|||||||
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId);
|
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// internal default
|
|
||||||
internal.nextTick(undefined, common.mustCall(function() {
|
|
||||||
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
internal.nextTick(rootAsyncId + 1, common.mustCall(function() {
|
internal.nextTick(rootAsyncId + 1, common.mustCall(function() {
|
||||||
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId + 1);
|
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId + 1);
|
||||||
|
26
test/async-hooks/test-no-assert-when-disabled.js
Normal file
26
test/async-hooks/test-no-assert-when-disabled.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
// Flags: --expose-internals
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
const async_hooks = require('async_hooks');
|
||||||
|
const internal = require('internal/process/next_tick');
|
||||||
|
|
||||||
|
// In tests async_hooks dynamic checks are enabled by default. To verify
|
||||||
|
// that no checks are enabled ordinarily disable the checks in this test.
|
||||||
|
common.revert_force_async_hooks_checks();
|
||||||
|
|
||||||
|
// When async_hooks is diabled (or never enabled), the checks
|
||||||
|
// should be disabled as well. This is important while async_hooks is
|
||||||
|
// experimental and there are still critial bugs to fix.
|
||||||
|
|
||||||
|
// Using undefined as the triggerAsyncId.
|
||||||
|
// Ref: https://github.com/nodejs/node/issues/14386
|
||||||
|
// Ref: https://github.com/nodejs/node/issues/14381
|
||||||
|
// Ref: https://github.com/nodejs/node/issues/14368
|
||||||
|
internal.nextTick(undefined, common.mustCall());
|
||||||
|
|
||||||
|
// Negative asyncIds and invalid type name
|
||||||
|
async_hooks.emitInit(-1, null, -1, {});
|
||||||
|
async_hooks.emitBefore(-1, -1);
|
||||||
|
async_hooks.emitAfter(-1);
|
||||||
|
async_hooks.emitDestroy(-1);
|
@ -63,6 +63,17 @@ exports.projectDir = path.resolve(__dirname, '..', '..');
|
|||||||
|
|
||||||
exports.buildType = process.config.target_defaults.default_configuration;
|
exports.buildType = process.config.target_defaults.default_configuration;
|
||||||
|
|
||||||
|
// Always enable async_hooks checks in tests
|
||||||
|
{
|
||||||
|
const async_wrap = process.binding('async_wrap');
|
||||||
|
const { kCheck } = async_wrap.constants;
|
||||||
|
async_wrap.async_hook_fields[kCheck] += 1;
|
||||||
|
|
||||||
|
exports.revert_force_async_hooks_checks = function() {
|
||||||
|
async_wrap.async_hook_fields[kCheck] -= 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// If env var is set then enable async_hook hooks for all tests.
|
// If env var is set then enable async_hook hooks for all tests.
|
||||||
if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) {
|
if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) {
|
||||||
const destroydIdsList = {};
|
const destroydIdsList = {};
|
||||||
|
@ -7,23 +7,12 @@ const spawn = require('child_process').spawn;
|
|||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const async_wrap = process.binding('async_wrap');
|
||||||
|
const { kTotals } = async_wrap.constants;
|
||||||
|
|
||||||
// Verify that inspector-async-hook is registered
|
assert.strictEqual(async_wrap.async_hook_fields[kTotals], 4);
|
||||||
// by checking that emitInit with invalid arguments
|
|
||||||
// throw an error.
|
|
||||||
// See test/async-hooks/test-emit-init.js
|
|
||||||
assert.throws(
|
|
||||||
() => async_hooks.emitInit(),
|
|
||||||
'inspector async hook should have been enabled initially');
|
|
||||||
|
|
||||||
process._debugEnd();
|
process._debugEnd();
|
||||||
|
assert.strictEqual(async_wrap.async_hook_fields[kTotals], 0);
|
||||||
// Verify that inspector-async-hook is no longer registered,
|
|
||||||
// thus emitInit() ignores invalid arguments
|
|
||||||
// See test/async-hooks/test-emit-init.js
|
|
||||||
assert.doesNotThrow(
|
|
||||||
() => async_hooks.emitInit(),
|
|
||||||
'inspector async hook should have beend disabled by _debugEnd()');
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const args = ['--inspect', '-e', script];
|
const args = ['--inspect', '-e', script];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user