inspector: Do not crash if the port is n/a

Node process will no longer terminate with an assertion if the
inspector port is not available.

PR-URL: https://github.com/nodejs/node/pull/7874
Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: jasnell - James M Snell <jasnell@gmail.com>
This commit is contained in:
Eugene Ostroukhov 2016-07-25 14:57:43 -07:00 committed by Ali Ijaz Sheikh
parent 7d75338c0e
commit f789eb3106
4 changed files with 55 additions and 47 deletions

View File

@ -1643,8 +1643,9 @@ cases:
source code internal in Node.js's bootstrapping process threw an error source code internal in Node.js's bootstrapping process threw an error
when the bootstrapping function was called. This is extremely rare, when the bootstrapping function was called. This is extremely rare,
and generally can only happen during development of Node.js itself. and generally can only happen during development of Node.js itself.
* `12` **Invalid Debug Argument** - The `--debug` and/or `--debug-brk` * `12` **Invalid Debug Argument** - The `--debug`, `--inspect` and/or
options were set, but an invalid port number was chosen. `--debug-brk` options were set, but the port number chosen was invalid
or unavailable.
* `>128` **Signal Exits** - If Node.js receives a fatal signal such as * `>128` **Signal Exits** - If Node.js receives a fatal signal such as
`SIGKILL` or `SIGHUP`, then its exit code will be `128` plus the `SIGKILL` or `SIGHUP`, then its exit code will be `128` plus the
value of the signal code. This is a standard Unix practice, since value of the signal code. This is a standard Unix practice, since

View File

@ -165,16 +165,17 @@ class AgentImpl {
~AgentImpl(); ~AgentImpl();
// Start the inspector agent thread // Start the inspector agent thread
void Start(v8::Platform* platform, int port, bool wait); bool Start(v8::Platform* platform, int port, bool wait);
// Stop the inspector agent // Stop the inspector agent
void Stop(); void Stop();
bool IsStarted(); bool IsStarted();
bool IsConnected() { return connected_; } bool IsConnected() { return state_ == State::kConnected; }
void WaitForDisconnect(); void WaitForDisconnect();
private: private:
using MessageQueue = std::vector<std::pair<int, String16>>; using MessageQueue = std::vector<std::pair<int, String16>>;
enum class State { kNew, kAccepting, kConnected, kDone, kError };
static void ThreadCbIO(void* agent); static void ThreadCbIO(void* agent);
static void OnSocketConnectionIO(uv_stream_t* server, int status); static void OnSocketConnectionIO(uv_stream_t* server, int status);
@ -195,6 +196,7 @@ class AgentImpl {
const String16& message); const String16& message);
void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2); void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2);
void PostIncomingMessage(const String16& message); void PostIncomingMessage(const String16& message);
State ToState(State state);
uv_sem_t start_sem_; uv_sem_t start_sem_;
ConditionVariable pause_cond_; ConditionVariable pause_cond_;
@ -205,8 +207,8 @@ class AgentImpl {
int port_; int port_;
bool wait_; bool wait_;
bool connected_;
bool shutting_down_; bool shutting_down_;
State state_;
node::Environment* parent_env_; node::Environment* parent_env_;
uv_async_t data_written_; uv_async_t data_written_;
@ -314,8 +316,8 @@ class V8NodeInspector : public blink::V8Inspector {
AgentImpl::AgentImpl(Environment* env) : port_(0), AgentImpl::AgentImpl(Environment* env) : port_(0),
wait_(false), wait_(false),
connected_(false),
shutting_down_(false), shutting_down_(false),
state_(State::kNew),
parent_env_(env), parent_env_(env),
client_socket_(nullptr), client_socket_(nullptr),
inspector_(nullptr), inspector_(nullptr),
@ -334,17 +336,11 @@ AgentImpl::~AgentImpl() {
uv_close(reinterpret_cast<uv_handle_t*>(&data_written_), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(&data_written_), nullptr);
} }
void AgentImpl::Start(v8::Platform* platform, int port, bool wait) { bool AgentImpl::Start(v8::Platform* platform, int port, bool wait) {
auto env = parent_env_; auto env = parent_env_;
inspector_ = new V8NodeInspector(this, env, platform); inspector_ = new V8NodeInspector(this, env, platform);
int err;
platform_ = platform; platform_ = platform;
int err = uv_async_init(env->event_loop(), &data_written_, nullptr);
err = uv_loop_init(&child_loop_);
CHECK_EQ(err, 0);
err = uv_async_init(env->event_loop(), &data_written_, nullptr);
CHECK_EQ(err, 0); CHECK_EQ(err, 0);
uv_unref(reinterpret_cast<uv_handle_t*>(&data_written_)); uv_unref(reinterpret_cast<uv_handle_t*>(&data_written_));
@ -356,21 +352,20 @@ void AgentImpl::Start(v8::Platform* platform, int port, bool wait) {
CHECK_EQ(err, 0); CHECK_EQ(err, 0);
uv_sem_wait(&start_sem_); uv_sem_wait(&start_sem_);
if (state_ == State::kError) {
Stop();
return false;
}
state_ = State::kAccepting;
if (wait) { if (wait) {
DispatchMessages(); DispatchMessages();
} }
return true;
} }
void AgentImpl::Stop() { void AgentImpl::Stop() {
// TODO(repenaxa): hop on the right thread.
DisconnectAndDisposeIO(client_socket_);
int err = uv_thread_join(&thread_); int err = uv_thread_join(&thread_);
CHECK_EQ(err, 0); CHECK_EQ(err, 0);
uv_run(&child_loop_, UV_RUN_NOWAIT);
err = uv_loop_close(&child_loop_);
CHECK_EQ(err, 0);
delete inspector_; delete inspector_;
} }
@ -429,7 +424,6 @@ void AgentImpl::OnRemoteDataIO(inspector_socket_t* socket,
Mutex::ScopedLock scoped_lock(pause_lock_); Mutex::ScopedLock scoped_lock(pause_lock_);
if (read > 0) { if (read > 0) {
String16 str = String16::fromUTF8(buf->base, read); String16 str = String16::fromUTF8(buf->base, read);
PostIncomingMessage(str);
// TODO(pfeldman): Instead of blocking execution while debugger // TODO(pfeldman): Instead of blocking execution while debugger
// engages, node should wait for the run callback from the remote client // engages, node should wait for the run callback from the remote client
// and initiate its startup. This is a change to node.cc that should be // and initiate its startup. This is a change to node.cc that should be
@ -438,11 +432,7 @@ void AgentImpl::OnRemoteDataIO(inspector_socket_t* socket,
wait_ = false; wait_ = false;
uv_sem_post(&start_sem_); uv_sem_post(&start_sem_);
} }
PostIncomingMessage(str);
platform_->CallOnForegroundThread(parent_env_->isolate(),
new DispatchOnInspectorBackendTask(this));
parent_env_->isolate()->RequestInterrupt(InterruptCallback, this);
uv_async_send(&data_written_);
} else if (read <= 0) { } else if (read <= 0) {
// EOF // EOF
if (client_socket_ == socket) { if (client_socket_ == socket) {
@ -477,8 +467,10 @@ void AgentImpl::WriteCbIO(uv_async_t* async) {
void AgentImpl::WorkerRunIO() { void AgentImpl::WorkerRunIO() {
sockaddr_in addr; sockaddr_in addr;
uv_tcp_t server; uv_tcp_t server;
int err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO); int err = uv_loop_init(&child_loop_);
CHECK_EQ(0, err); CHECK_EQ(err, 0);
err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO);
CHECK_EQ(err, 0);
io_thread_req_.data = this; io_thread_req_.data = this;
uv_tcp_init(&child_loop_, &server); uv_tcp_init(&child_loop_, &server);
uv_ip4_addr("0.0.0.0", port_, &addr); uv_ip4_addr("0.0.0.0", port_, &addr);
@ -489,19 +481,26 @@ void AgentImpl::WorkerRunIO() {
err = uv_listen(reinterpret_cast<uv_stream_t*>(&server), 1, err = uv_listen(reinterpret_cast<uv_stream_t*>(&server), 1,
OnSocketConnectionIO); OnSocketConnectionIO);
} }
if (err == 0) { if (err != 0) {
PrintDebuggerReadyMessage(port_);
} else {
fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err)); fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err));
ABORT(); state_ = State::kError; // Safe, main thread is waiting on semaphore
uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server), nullptr);
uv_loop_close(&child_loop_);
uv_sem_post(&start_sem_);
return;
} }
PrintDebuggerReadyMessage(port_);
if (!wait_) { if (!wait_) {
uv_sem_post(&start_sem_); uv_sem_post(&start_sem_);
} }
uv_run(&child_loop_, UV_RUN_DEFAULT); uv_run(&child_loop_, UV_RUN_DEFAULT);
uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(&server), nullptr);
uv_run(&child_loop_, UV_RUN_DEFAULT); DisconnectAndDisposeIO(client_socket_);
uv_run(&child_loop_, UV_RUN_NOWAIT);
err = uv_loop_close(&child_loop_);
CHECK_EQ(err, 0);
} }
void AgentImpl::AppendMessage(MessageQueue* queue, int session_id, void AgentImpl::AppendMessage(MessageQueue* queue, int session_id,
@ -544,16 +543,19 @@ void AgentImpl::DispatchMessages() {
for (const MessageQueue::value_type& pair : tasks) { for (const MessageQueue::value_type& pair : tasks) {
const String16& message = pair.second; const String16& message = pair.second;
if (message == TAG_CONNECT) { if (message == TAG_CONNECT) {
CHECK_EQ(false, connected_); CHECK_EQ(State::kAccepting, state_);
backend_session_id_++; backend_session_id_++;
connected_ = true; state_ = State::kConnected;
fprintf(stderr, "Debugger attached.\n"); fprintf(stderr, "Debugger attached.\n");
inspector_->connectFrontend(new ChannelImpl(this)); inspector_->connectFrontend(new ChannelImpl(this));
} else if (message == TAG_DISCONNECT) { } else if (message == TAG_DISCONNECT) {
CHECK(connected_); CHECK_EQ(State::kConnected, state_);
connected_ = false; if (shutting_down_) {
if (!shutting_down_) state_ = State::kDone;
} else {
PrintDebuggerReadyMessage(port_); PrintDebuggerReadyMessage(port_);
state_ = State::kAccepting;
}
inspector_->quitMessageLoopOnPause(); inspector_->quitMessageLoopOnPause();
inspector_->disconnectFrontend(); inspector_->disconnectFrontend();
} else { } else {
@ -577,8 +579,8 @@ Agent::~Agent() {
delete impl; delete impl;
} }
void Agent::Start(v8::Platform* platform, int port, bool wait) { bool Agent::Start(v8::Platform* platform, int port, bool wait) {
impl->Start(platform, port, wait); return impl->Start(platform, port, wait);
} }
void Agent::Stop() { void Agent::Stop() {

View File

@ -25,7 +25,7 @@ class Agent {
~Agent(); ~Agent();
// Start the inspector agent thread // Start the inspector agent thread
void Start(v8::Platform* platform, int port, bool wait); bool Start(v8::Platform* platform, int port, bool wait);
// Stop the inspector agent // Stop the inspector agent
void Stop(); void Stop();

View File

@ -208,9 +208,11 @@ static struct {
platform_ = nullptr; platform_ = nullptr;
} }
void StartInspector(Environment *env, int port, bool wait) { bool StartInspector(Environment *env, int port, bool wait) {
#if HAVE_INSPECTOR #if HAVE_INSPECTOR
env->inspector_agent()->Start(platform_, port, wait); return env->inspector_agent()->Start(platform_, port, wait);
#else
return true;
#endif // HAVE_INSPECTOR #endif // HAVE_INSPECTOR
} }
@ -3756,8 +3758,7 @@ static void DispatchMessagesDebugAgentCallback(Environment* env) {
static void StartDebug(Environment* env, bool wait) { static void StartDebug(Environment* env, bool wait) {
CHECK(!debugger_running); CHECK(!debugger_running);
if (use_inspector) { if (use_inspector) {
v8_platform.StartInspector(env, inspector_port, wait); debugger_running = v8_platform.StartInspector(env, inspector_port, wait);
debugger_running = true;
} else { } else {
env->debugger_agent()->set_dispatch_handler( env->debugger_agent()->set_dispatch_handler(
DispatchMessagesDebugAgentCallback); DispatchMessagesDebugAgentCallback);
@ -4383,8 +4384,12 @@ static void StartNodeInstance(void* arg) {
ShouldAbortOnUncaughtException); ShouldAbortOnUncaughtException);
// Start debug agent when argv has --debug // Start debug agent when argv has --debug
if (instance_data->use_debug_agent()) if (instance_data->use_debug_agent()) {
StartDebug(&env, debug_wait_connect); StartDebug(&env, debug_wait_connect);
if (use_inspector && !debugger_running) {
exit(12);
}
}
{ {
Environment::AsyncCallbackScope callback_scope(&env); Environment::AsyncCallbackScope callback_scope(&env);