Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlegendecas <legendecas@gmail.com>2019-08-19 16:03:08 +0300
committerlegendecas <legendecas@gmail.com>2020-01-28 08:52:27 +0300
commit7b7e7bd185aa606cf2845747dd3844a9b12ec9ab (patch)
tree5081e7a080edf45d2b5f82039e0d014c0ab46d96 /src/node_watchdog.cc
parent78743f8e3912fa04670ceab5365f081b3079507b (diff)
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>
Diffstat (limited to 'src/node_watchdog.cc')
-rw-r--r--src/node_watchdog.cc144
1 files changed, 136 insertions, 8 deletions
diff --git a/src/node_watchdog.cc b/src/node_watchdog.cc
index 4ab625ce65b..4cc75c31604 100644
--- a/src/node_watchdog.cc
+++ b/src/node_watchdog.cc
@@ -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);