mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
src,lib: make ^C print a JS stack trace
If terminating the process with ctrl-c / SIGINT, prints a JS stacktrace leading up to the currently executing code. The feature would be enabled under option `--trace-sigint`. Conditions of no stacktrace on sigint: - has (an) active sigint listener(s); - main thread is idle (i.e. uv polling), a message instead of stacktrace would be printed. PR-URL: https://github.com/nodejs/node/pull/29207 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Christopher Hiller <boneskull@boneskull.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
78743f8e39
commit
7b7e7bd185
20 changed files with 405 additions and 15 deletions
|
@ -779,6 +779,13 @@ added: v13.5.0
|
|||
Prints a stack trace whenever an environment is exited proactively,
|
||||
i.e. invoking `process.exit()`.
|
||||
|
||||
### `--trace-sigint`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Prints a stack trace on SIGINT.
|
||||
|
||||
### `--trace-sync-io`
|
||||
<!-- YAML
|
||||
added: v2.1.0
|
||||
|
@ -1122,6 +1129,7 @@ Node.js options that are allowed are:
|
|||
* `--trace-event-file-pattern`
|
||||
* `--trace-events-enabled`
|
||||
* `--trace-exit`
|
||||
* `--trace-sigint`
|
||||
* `--trace-sync-io`
|
||||
* `--trace-tls`
|
||||
* `--trace-uncaught`
|
||||
|
|
|
@ -367,6 +367,8 @@ Enable the collection of trace event tracing information.
|
|||
.It Fl -trace-exit
|
||||
Prints a stack trace whenever an environment is exited proactively,
|
||||
i.e. invoking `process.exit()`.
|
||||
.It Fl -trace-sigint
|
||||
Prints a stack trace on SIGINT.
|
||||
.
|
||||
.It Fl -trace-sync-io
|
||||
Print a stack trace whenever synchronous I/O is detected after the first turn of the event loop.
|
||||
|
|
|
@ -37,6 +37,9 @@ function prepareMainThreadExecution(expandArgv1 = false) {
|
|||
|
||||
setupDebugEnv();
|
||||
|
||||
// Print stack trace on `SIGINT` if option `--trace-sigint` presents.
|
||||
setupStacktracePrinterOnSigint();
|
||||
|
||||
// Process initial diagnostic reporting configuration, if present.
|
||||
initializeReport();
|
||||
initializeReportSignalHandlers(); // Main-thread-only.
|
||||
|
@ -149,6 +152,16 @@ function setupCoverageHooks(dir) {
|
|||
return coverageDirectory;
|
||||
}
|
||||
|
||||
function setupStacktracePrinterOnSigint() {
|
||||
if (!getOptionValue('--trace-sigint')) {
|
||||
return;
|
||||
}
|
||||
const { SigintWatchdog } = require('internal/watchdog');
|
||||
|
||||
const watchdog = new SigintWatchdog();
|
||||
watchdog.start();
|
||||
}
|
||||
|
||||
function initializeReport() {
|
||||
if (!getOptionValue('--experimental-report')) {
|
||||
return;
|
||||
|
|
59
lib/internal/watchdog.js
Normal file
59
lib/internal/watchdog.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
TraceSigintWatchdog
|
||||
} = internalBinding('watchdog');
|
||||
|
||||
class SigintWatchdog extends TraceSigintWatchdog {
|
||||
_started = false;
|
||||
_effective = false;
|
||||
_onNewListener = (eve) => {
|
||||
if (eve === 'SIGINT' && this._effective) {
|
||||
super.stop();
|
||||
this._effective = false;
|
||||
}
|
||||
};
|
||||
_onRemoveListener = (eve) => {
|
||||
if (eve === 'SIGINT' && process.listenerCount('SIGINT') === 0 &&
|
||||
!this._effective) {
|
||||
super.start();
|
||||
this._effective = true;
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
// Prepend sigint newListener to remove stop watchdog before signal wrap
|
||||
// been activated. Also make sigint removeListener been ran after signal
|
||||
// wrap been stopped.
|
||||
process.prependListener('newListener', this._onNewListener);
|
||||
process.addListener('removeListener', this._onRemoveListener);
|
||||
|
||||
if (process.listenerCount('SIGINT') === 0) {
|
||||
super.start();
|
||||
this._effective = true;
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = false;
|
||||
process.removeListener('newListener', this._onNewListener);
|
||||
process.removeListener('removeListener', this._onRemoveListener);
|
||||
|
||||
if (this._effective) {
|
||||
super.stop();
|
||||
this._effective = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
SigintWatchdog
|
||||
};
|
1
node.gyp
1
node.gyp
|
@ -210,6 +210,7 @@
|
|||
'lib/internal/vm/module.js',
|
||||
'lib/internal/worker.js',
|
||||
'lib/internal/worker/io.js',
|
||||
'lib/internal/watchdog.js',
|
||||
'lib/internal/streams/lazy_transform.js',
|
||||
'lib/internal/streams/async_iterator.js',
|
||||
'lib/internal/streams/buffer_list.js',
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace node {
|
|||
V(TTYWRAP) \
|
||||
V(UDPSENDWRAP) \
|
||||
V(UDPWRAP) \
|
||||
V(SIGINTWATCHDOG) \
|
||||
V(WORKER) \
|
||||
V(WRITEWRAP) \
|
||||
V(ZLIB)
|
||||
|
|
|
@ -81,6 +81,14 @@ void MemoryTracker::TrackFieldWithSize(const char* edge_name,
|
|||
if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
|
||||
}
|
||||
|
||||
void MemoryTracker::TrackInlineFieldWithSize(const char* edge_name,
|
||||
size_t size,
|
||||
const char* node_name) {
|
||||
if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
|
||||
CHECK(CurrentNode());
|
||||
CurrentNode()->size_ -= size;
|
||||
}
|
||||
|
||||
void MemoryTracker::TrackField(const char* edge_name,
|
||||
const MemoryRetainer& value,
|
||||
const char* node_name) {
|
||||
|
@ -248,6 +256,12 @@ void MemoryTracker::TrackField(const char* name,
|
|||
TrackFieldWithSize(name, sizeof(value), "uv_async_t");
|
||||
}
|
||||
|
||||
void MemoryTracker::TrackInlineField(const char* name,
|
||||
const uv_async_t& value,
|
||||
const char* node_name) {
|
||||
TrackInlineFieldWithSize(name, sizeof(value), "uv_async_t");
|
||||
}
|
||||
|
||||
template <class NativeT, class V8T>
|
||||
void MemoryTracker::TrackField(const char* name,
|
||||
const AliasedBufferBase<NativeT, V8T>& value,
|
||||
|
|
|
@ -135,6 +135,10 @@ class MemoryTracker {
|
|||
inline void TrackFieldWithSize(const char* edge_name,
|
||||
size_t size,
|
||||
const char* node_name = nullptr);
|
||||
inline void TrackInlineFieldWithSize(const char* edge_name,
|
||||
size_t size,
|
||||
const char* node_name = nullptr);
|
||||
|
||||
// Shortcut to extract the underlying object out of the smart pointer
|
||||
template <typename T>
|
||||
inline void TrackField(const char* edge_name,
|
||||
|
@ -228,6 +232,9 @@ class MemoryTracker {
|
|||
inline void TrackField(const char* edge_name,
|
||||
const uv_async_t& value,
|
||||
const char* node_name = nullptr);
|
||||
inline void TrackInlineField(const char* edge_name,
|
||||
const uv_async_t& value,
|
||||
const char* node_name = nullptr);
|
||||
template <class NativeT, class V8T>
|
||||
inline void TrackField(const char* edge_name,
|
||||
const AliasedBufferBase<NativeT, V8T>& value,
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
V(v8) \
|
||||
V(wasi) \
|
||||
V(worker) \
|
||||
V(watchdog) \
|
||||
V(zlib)
|
||||
|
||||
#define NODE_BUILTIN_MODULES(V) \
|
||||
|
|
|
@ -758,6 +758,11 @@ PerProcessOptionsParser::PerProcessOptionsParser(
|
|||
&PerProcessOptions::use_largepages,
|
||||
kAllowedInEnvironment);
|
||||
|
||||
AddOption("--trace-sigint",
|
||||
"enable printing JavaScript stacktrace on SIGINT",
|
||||
&PerProcessOptions::trace_sigint,
|
||||
kAllowedInEnvironment);
|
||||
|
||||
Insert(iop, &PerProcessOptions::get_per_isolate_options);
|
||||
}
|
||||
|
||||
|
|
|
@ -237,6 +237,7 @@ class PerProcessOptions : public Options {
|
|||
#endif
|
||||
#endif
|
||||
std::string use_largepages = "off";
|
||||
bool trace_sigint = false;
|
||||
|
||||
#ifdef NODE_REPORT
|
||||
std::vector<std::string> cmdline;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "async_wrap-inl.h"
|
||||
#include "debug_utils-inl.h"
|
||||
#include "env-inl.h"
|
||||
#include "node_errors.h"
|
||||
|
@ -30,6 +31,12 @@
|
|||
|
||||
namespace node {
|
||||
|
||||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::Object;
|
||||
using v8::Value;
|
||||
|
||||
Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms, bool* timed_out)
|
||||
: isolate_(isolate), timed_out_(timed_out) {
|
||||
|
||||
|
@ -106,10 +113,116 @@ SigintWatchdog::~SigintWatchdog() {
|
|||
SigintWatchdogHelper::GetInstance()->Stop();
|
||||
}
|
||||
|
||||
|
||||
void SigintWatchdog::HandleSigint() {
|
||||
SignalPropagation SigintWatchdog::HandleSigint() {
|
||||
*received_signal_ = true;
|
||||
isolate_->TerminateExecution();
|
||||
return SignalPropagation::kStopPropagation;
|
||||
}
|
||||
|
||||
void TraceSigintWatchdog::Init(Environment* env, Local<Object> target) {
|
||||
Local<FunctionTemplate> constructor = env->NewFunctionTemplate(New);
|
||||
constructor->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
Local<v8::String> js_sigint_watch_dog =
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "TraceSigintWatchdog");
|
||||
constructor->SetClassName(js_sigint_watch_dog);
|
||||
constructor->Inherit(HandleWrap::GetConstructorTemplate(env));
|
||||
|
||||
env->SetProtoMethod(constructor, "start", Start);
|
||||
env->SetProtoMethod(constructor, "stop", Stop);
|
||||
|
||||
target
|
||||
->Set(env->context(),
|
||||
js_sigint_watch_dog,
|
||||
constructor->GetFunction(env->context()).ToLocalChecked())
|
||||
.Check();
|
||||
}
|
||||
|
||||
void TraceSigintWatchdog::New(const FunctionCallbackInfo<Value>& args) {
|
||||
// This constructor should not be exposed to public javascript.
|
||||
// Therefore we assert that we are not trying to call this as a
|
||||
// normal function.
|
||||
CHECK(args.IsConstructCall());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
new TraceSigintWatchdog(env, args.This());
|
||||
}
|
||||
|
||||
void TraceSigintWatchdog::Start(const FunctionCallbackInfo<Value>& args) {
|
||||
TraceSigintWatchdog* watchdog;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&watchdog, args.Holder());
|
||||
// Register this watchdog with the global SIGINT/Ctrl+C listener.
|
||||
SigintWatchdogHelper::GetInstance()->Register(watchdog);
|
||||
// Start the helper thread, if that has not already happened.
|
||||
int r = SigintWatchdogHelper::GetInstance()->Start();
|
||||
CHECK_EQ(r, 0);
|
||||
}
|
||||
|
||||
void TraceSigintWatchdog::Stop(const FunctionCallbackInfo<Value>& args) {
|
||||
TraceSigintWatchdog* watchdog;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&watchdog, args.Holder());
|
||||
SigintWatchdogHelper::GetInstance()->Unregister(watchdog);
|
||||
SigintWatchdogHelper::GetInstance()->Stop();
|
||||
}
|
||||
|
||||
TraceSigintWatchdog::TraceSigintWatchdog(Environment* env, Local<Object> object)
|
||||
: HandleWrap(env,
|
||||
object,
|
||||
reinterpret_cast<uv_handle_t*>(&handle_),
|
||||
AsyncWrap::PROVIDER_SIGINTWATCHDOG) {
|
||||
int r = uv_async_init(env->event_loop(), &handle_, [](uv_async_t* handle) {
|
||||
TraceSigintWatchdog* watchdog =
|
||||
ContainerOf(&TraceSigintWatchdog::handle_, handle);
|
||||
watchdog->signal_flag_ = SignalFlags::FromIdle;
|
||||
watchdog->HandleInterrupt();
|
||||
});
|
||||
CHECK_EQ(r, 0);
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(&handle_));
|
||||
}
|
||||
|
||||
SignalPropagation TraceSigintWatchdog::HandleSigint() {
|
||||
/**
|
||||
* In case of uv loop polling, i.e. no JS currently running, activate the
|
||||
* loop to run a piece of JS code to trigger interruption.
|
||||
*/
|
||||
CHECK_EQ(uv_async_send(&handle_), 0);
|
||||
env()->isolate()->RequestInterrupt(
|
||||
[](v8::Isolate* isolate, void* data) {
|
||||
TraceSigintWatchdog* self = static_cast<TraceSigintWatchdog*>(data);
|
||||
if (self->signal_flag_ == SignalFlags::None) {
|
||||
self->signal_flag_ = SignalFlags::FromInterrupt;
|
||||
}
|
||||
self->HandleInterrupt();
|
||||
},
|
||||
this);
|
||||
return SignalPropagation::kContinuePropagation;
|
||||
}
|
||||
|
||||
void TraceSigintWatchdog::HandleInterrupt() {
|
||||
// Do not nest interrupts.
|
||||
if (interrupting) {
|
||||
return;
|
||||
}
|
||||
interrupting = true;
|
||||
if (signal_flag_ == SignalFlags::None) {
|
||||
return;
|
||||
}
|
||||
Environment* env_ = env();
|
||||
// FIXME: Before
|
||||
// https://github.com/nodejs/node/pull/29207#issuecomment-527667993 get
|
||||
// fixed, additional JavaScript code evaluation shall be prevented from
|
||||
// running during interruption.
|
||||
FPrintF(stderr,
|
||||
"KEYBOARD_INTERRUPT: Script execution was interrupted by `SIGINT`\n");
|
||||
if (signal_flag_ == SignalFlags::FromInterrupt) {
|
||||
PrintStackTrace(env_->isolate(),
|
||||
v8::StackTrace::CurrentStackTrace(
|
||||
env_->isolate(), 10, v8::StackTrace::kDetailed));
|
||||
}
|
||||
signal_flag_ = SignalFlags::None;
|
||||
interrupting = false;
|
||||
|
||||
SigintWatchdogHelper::GetInstance()->Unregister(this);
|
||||
SigintWatchdogHelper::GetInstance()->Stop();
|
||||
raise(SIGINT);
|
||||
}
|
||||
|
||||
#ifdef __POSIX__
|
||||
|
@ -163,8 +276,13 @@ bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
|
|||
instance.has_pending_signal_ = true;
|
||||
}
|
||||
|
||||
for (auto it : instance.watchdogs_)
|
||||
it->HandleSigint();
|
||||
for (auto it = instance.watchdogs_.rbegin(); it != instance.watchdogs_.rend();
|
||||
it++) {
|
||||
SignalPropagation wp = (*it)->HandleSigint();
|
||||
if (wp == SignalPropagation::kStopPropagation) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return is_stopping;
|
||||
}
|
||||
|
@ -260,15 +378,13 @@ bool SigintWatchdogHelper::HasPendingSignal() {
|
|||
return has_pending_signal_;
|
||||
}
|
||||
|
||||
|
||||
void SigintWatchdogHelper::Register(SigintWatchdog* wd) {
|
||||
void SigintWatchdogHelper::Register(SigintWatchdogBase* wd) {
|
||||
Mutex::ScopedLock lock(list_mutex_);
|
||||
|
||||
watchdogs_.push_back(wd);
|
||||
}
|
||||
|
||||
|
||||
void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) {
|
||||
void SigintWatchdogHelper::Unregister(SigintWatchdogBase* wd) {
|
||||
Mutex::ScopedLock lock(list_mutex_);
|
||||
|
||||
auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
|
||||
|
@ -303,4 +419,16 @@ SigintWatchdogHelper::~SigintWatchdogHelper() {
|
|||
|
||||
SigintWatchdogHelper SigintWatchdogHelper::instance;
|
||||
|
||||
namespace watchdog {
|
||||
static void Initialize(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
Local<Context> context,
|
||||
void* priv) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
TraceSigintWatchdog::Init(env, target);
|
||||
}
|
||||
} // namespace watchdog
|
||||
|
||||
} // namespace node
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_INTERNAL(watchdog, node::watchdog::Initialize);
|
||||
|
|
|
@ -24,9 +24,12 @@
|
|||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "uv.h"
|
||||
#include "node_mutex.h"
|
||||
#include <vector>
|
||||
#include "handle_wrap.h"
|
||||
#include "memory_tracker-inl.h"
|
||||
#include "node_mutex.h"
|
||||
#include "uv.h"
|
||||
#include "v8.h"
|
||||
|
||||
#ifdef __POSIX__
|
||||
#include <pthread.h>
|
||||
|
@ -34,6 +37,11 @@
|
|||
|
||||
namespace node {
|
||||
|
||||
enum class SignalPropagation {
|
||||
kContinuePropagation,
|
||||
kStopPropagation,
|
||||
};
|
||||
|
||||
class Watchdog {
|
||||
public:
|
||||
explicit Watchdog(v8::Isolate* isolate,
|
||||
|
@ -54,24 +62,56 @@ class Watchdog {
|
|||
bool* timed_out_;
|
||||
};
|
||||
|
||||
class SigintWatchdog {
|
||||
class SigintWatchdogBase {
|
||||
public:
|
||||
virtual ~SigintWatchdogBase() = default;
|
||||
virtual SignalPropagation HandleSigint() = 0;
|
||||
};
|
||||
|
||||
class SigintWatchdog : public SigintWatchdogBase {
|
||||
public:
|
||||
explicit SigintWatchdog(v8::Isolate* isolate,
|
||||
bool* received_signal = nullptr);
|
||||
~SigintWatchdog();
|
||||
v8::Isolate* isolate() { return isolate_; }
|
||||
void HandleSigint();
|
||||
SignalPropagation HandleSigint() override;
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
bool* received_signal_;
|
||||
};
|
||||
|
||||
class TraceSigintWatchdog : public HandleWrap, public SigintWatchdogBase {
|
||||
public:
|
||||
static void Init(Environment* env, Local<v8::Object> target);
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Start(const v8::FunctionCallbackInfo<Value>& args);
|
||||
static void Stop(const v8::FunctionCallbackInfo<Value>& args);
|
||||
|
||||
SignalPropagation HandleSigint() override;
|
||||
|
||||
inline void MemoryInfo(node::MemoryTracker* tracker) const override {
|
||||
tracker->TrackInlineField("handle_", handle_);
|
||||
}
|
||||
SET_MEMORY_INFO_NAME(TraceSigintWatchdog)
|
||||
SET_SELF_SIZE(TraceSigintWatchdog)
|
||||
|
||||
private:
|
||||
enum class SignalFlags { None, FromIdle, FromInterrupt };
|
||||
|
||||
TraceSigintWatchdog(Environment* env, Local<v8::Object> object);
|
||||
void HandleInterrupt();
|
||||
|
||||
bool interrupting = false;
|
||||
uv_async_t handle_;
|
||||
SignalFlags signal_flag_ = SignalFlags::None;
|
||||
};
|
||||
|
||||
class SigintWatchdogHelper {
|
||||
public:
|
||||
static SigintWatchdogHelper* GetInstance() { return &instance; }
|
||||
void Register(SigintWatchdog* watchdog);
|
||||
void Unregister(SigintWatchdog* watchdog);
|
||||
void Register(SigintWatchdogBase* watchdog);
|
||||
void Unregister(SigintWatchdogBase* watchdog);
|
||||
bool HasPendingSignal();
|
||||
|
||||
int Start();
|
||||
|
@ -88,7 +128,7 @@ class SigintWatchdogHelper {
|
|||
|
||||
Mutex mutex_;
|
||||
Mutex list_mutex_;
|
||||
std::vector<SigintWatchdog*> watchdogs_;
|
||||
std::vector<SigintWatchdogBase*> watchdogs_;
|
||||
bool has_pending_signal_;
|
||||
|
||||
#ifdef __POSIX__
|
||||
|
|
39
test/pseudo-tty/test-trace-sigint-disabled.js
Normal file
39
test/pseudo-tty/test-trace-sigint-disabled.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
const { mustCall } = require('../common');
|
||||
const childProcess = require('child_process');
|
||||
const assert = require('assert');
|
||||
|
||||
if (process.env.CHILD === 'true') {
|
||||
main();
|
||||
} else {
|
||||
// Use inherited stdio child process to prevent test tools from determining
|
||||
// the case as crashed from SIGINT
|
||||
const cp = childProcess.spawn(
|
||||
process.execPath,
|
||||
['--trace-sigint', __filename],
|
||||
{
|
||||
env: { ...process.env, CHILD: 'true' },
|
||||
stdio: 'inherit'
|
||||
});
|
||||
cp.on('exit', mustCall((code, signal) => {
|
||||
assert.strictEqual(signal, null);
|
||||
assert.strictEqual(code, 0);
|
||||
}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
// Deactivate colors even if the tty does support colors.
|
||||
process.env.NODE_DISABLE_COLORS = '1';
|
||||
|
||||
const noop = mustCall(() => {
|
||||
process.exit(0);
|
||||
});
|
||||
process.on('SIGINT', noop);
|
||||
// Try testing re-add 'SIGINT' listeners
|
||||
process.removeListener('SIGINT', noop);
|
||||
process.on('SIGINT', noop);
|
||||
|
||||
process.kill(process.pid, 'SIGINT');
|
||||
setTimeout(() => { assert.fail('unreachable path'); }, 10 * 1000);
|
||||
}
|
0
test/pseudo-tty/test-trace-sigint-disabled.out
Normal file
0
test/pseudo-tty/test-trace-sigint-disabled.out
Normal file
30
test/pseudo-tty/test-trace-sigint-on-idle.js
Normal file
30
test/pseudo-tty/test-trace-sigint-on-idle.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
const { mustCall } = require('../common');
|
||||
const childProcess = require('child_process');
|
||||
const assert = require('assert');
|
||||
|
||||
if (process.env.CHILD === 'true') {
|
||||
main();
|
||||
} else {
|
||||
// Use inherited stdio child process to prevent test tools from determining
|
||||
// the case as crashed from SIGINT
|
||||
const cp = childProcess.spawn(
|
||||
process.execPath,
|
||||
['--trace-sigint', __filename],
|
||||
{
|
||||
env: { ...process.env, CHILD: 'true' },
|
||||
stdio: 'inherit'
|
||||
});
|
||||
setTimeout(() => cp.kill('SIGINT'), 1 * 1000);
|
||||
cp.on('exit', mustCall((code, signal) => {
|
||||
assert.strictEqual(signal, 'SIGINT');
|
||||
assert.strictEqual(code, null);
|
||||
}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
// Deactivate colors even if the tty does support colors.
|
||||
process.env.NODE_DISABLE_COLORS = '1';
|
||||
setTimeout(() => {}, 10 * 1000);
|
||||
}
|
1
test/pseudo-tty/test-trace-sigint-on-idle.out
Normal file
1
test/pseudo-tty/test-trace-sigint-on-idle.out
Normal file
|
@ -0,0 +1 @@
|
|||
KEYBOARD_INTERRUPT: Script execution was interrupted by `SIGINT`
|
30
test/pseudo-tty/test-trace-sigint.js
Normal file
30
test/pseudo-tty/test-trace-sigint.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
const { mustCall } = require('../common');
|
||||
const childProcess = require('child_process');
|
||||
const assert = require('assert');
|
||||
|
||||
if (process.env.CHILD === 'true') {
|
||||
main();
|
||||
} else {
|
||||
// Use inherited stdio child process to prevent test tools from determining
|
||||
// the case as crashed from SIGINT
|
||||
const cp = childProcess.spawn(
|
||||
process.execPath,
|
||||
['--trace-sigint', __filename],
|
||||
{
|
||||
env: { ...process.env, CHILD: 'true' },
|
||||
stdio: 'inherit'
|
||||
});
|
||||
cp.on('exit', mustCall((code, signal) => {
|
||||
assert.strictEqual(signal, 'SIGINT');
|
||||
assert.strictEqual(code, null);
|
||||
}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
// Deactivate colors even if the tty does support colors.
|
||||
process.env.NODE_DISABLE_COLORS = '1';
|
||||
process.kill(process.pid, 'SIGINT');
|
||||
while (true) {}
|
||||
}
|
9
test/pseudo-tty/test-trace-sigint.out
Normal file
9
test/pseudo-tty/test-trace-sigint.out
Normal file
|
@ -0,0 +1,9 @@
|
|||
KEYBOARD_INTERRUPT: Script execution was interrupted by `SIGINT`
|
||||
at main (*/test-trace-sigint.js:*)
|
||||
at */test-trace-sigint.js:*
|
||||
at *
|
||||
at *
|
||||
at *
|
||||
at *
|
||||
at *
|
||||
at *
|
|
@ -51,6 +51,7 @@ const { getSystemErrorName } = require('util');
|
|||
delete providers.HTTPCLIENTREQUEST;
|
||||
delete providers.HTTPINCOMINGMESSAGE;
|
||||
delete providers.ELDHISTOGRAM;
|
||||
delete providers.SIGINTWATCHDOG;
|
||||
|
||||
const objKeys = Object.keys(providers);
|
||||
if (objKeys.length > 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue