From 95be46105a0db3b7756ada64cc5bc3de416af798 Mon Sep 17 00:00:00 2001 From: "hausner@google.com" Date: Fri, 4 May 2012 15:25:49 +0000 Subject: [PATCH] Beginnings of a debugger wire protocol The debugger wire handler is implemented similarly to the io event handler. A dedicated thread monitors the debugger port for incoming connection requests. When a debugger is connected, the VM sends events messages over the wire and handles debugger requests. To start the VM with a debugger connection, use the option --debug:. The VM pauses at the beginning of main() and waits for a debugger to connect. Subsequent changes will implement debugger commands one by one. With this change, the VM only understands "resume" commands. Review URL: https://chromiumcodereview.appspot.com//10357003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@7330 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/bin/builtin_impl_sources.gypi | 8 + runtime/bin/dbg_connection.cc | 273 +++++++++++++++++++++++++ runtime/bin/dbg_connection.h | 67 ++++++ runtime/bin/dbg_connection_linux.cc | 18 ++ runtime/bin/dbg_connection_linux.h | 18 ++ runtime/bin/dbg_connection_macos.cc | 158 ++++++++++++++ runtime/bin/dbg_connection_macos.h | 44 ++++ runtime/bin/dbg_connection_win.cc | 17 ++ runtime/bin/dbg_connection_win.h | 18 ++ runtime/bin/fdutils.h | 1 + runtime/bin/fdutils_linux.cc | 14 +- runtime/bin/fdutils_macos.cc | 14 +- runtime/bin/main.cc | 40 ++++ runtime/lib/mirrors.cc | 2 +- runtime/{vm => platform}/json.cc | 24 ++- runtime/{vm => platform}/json.h | 11 +- runtime/platform/platform_headers.gypi | 1 + runtime/platform/platform_sources.gypi | 1 + runtime/vm/json_test.cc | 2 +- runtime/vm/vm_sources.gypi | 2 - 20 files changed, 719 insertions(+), 14 deletions(-) create mode 100644 runtime/bin/dbg_connection.cc create mode 100644 runtime/bin/dbg_connection.h create mode 100644 runtime/bin/dbg_connection_linux.cc create mode 100644 runtime/bin/dbg_connection_linux.h create mode 100644 runtime/bin/dbg_connection_macos.cc create mode 100644 runtime/bin/dbg_connection_macos.h create mode 100644 runtime/bin/dbg_connection_win.cc create mode 100644 runtime/bin/dbg_connection_win.h rename runtime/{vm => platform}/json.cc (93%) rename runtime/{vm => platform}/json.h (91%) diff --git a/runtime/bin/builtin_impl_sources.gypi b/runtime/bin/builtin_impl_sources.gypi index e5790f3361d..81fe80acf47 100644 --- a/runtime/bin/builtin_impl_sources.gypi +++ b/runtime/bin/builtin_impl_sources.gypi @@ -8,6 +8,14 @@ 'sources': [ 'dartutils.cc', 'dartutils.h', + 'dbg_connection.cc', + 'dbg_connection.h', + 'dbg_connection_linux.cc', + 'dbg_connection_linux.h', + 'dbg_connection_macos.cc', + 'dbg_connection_macos.h', + 'dbg_connection_win.cc', + 'dbg_connection_win.h', 'directory.cc', 'directory.h', 'directory_posix.cc', diff --git a/runtime/bin/dbg_connection.cc b/runtime/bin/dbg_connection.cc new file mode 100644 index 00000000000..e9a6e11c70f --- /dev/null +++ b/runtime/bin/dbg_connection.cc @@ -0,0 +1,273 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "bin/dbg_connection.h" +#include "bin/dartutils.h" +#include "bin/fdutils.h" +#include "bin/socket.h" +#include "bin/thread.h" +#include "bin/utils.h" + +#include "platform/globals.h" +#include "platform/json.h" +#include "platform/thread.h" +#include "platform/utils.h" + +#include "include/dart_api.h" + + +int DebuggerConnectionHandler::listener_fd_ = -1; +int DebuggerConnectionHandler::debugger_fd_ = -1; +MessageBuffer* DebuggerConnectionHandler::msgbuf_ = NULL; + +bool DebuggerConnectionHandler::handler_started_ = false; + + +// TODO(hausner): Need better error handling. +#define ASSERT_NOT_ERROR(handle) \ + ASSERT(!Dart_IsError(handle)) + + +class MessageBuffer { + public: + explicit MessageBuffer(int fd); + ~MessageBuffer(); + void ReadData(); + bool IsValidMessage() const; + void PopMessage(); + int MessageId() const; + char* buf() const { return buf_; } + bool Alive() const { return connection_is_alive_; } + + private: + static const int kInitialBufferSize = 256; + char* buf_; + int buf_length_; + int fd_; + int data_length_; + bool connection_is_alive_; + + DISALLOW_COPY_AND_ASSIGN(MessageBuffer); +}; + + +MessageBuffer::MessageBuffer(int fd) + : buf_(NULL), + buf_length_(0), + fd_(fd), + data_length_(0), + connection_is_alive_(true) { + buf_ = reinterpret_cast(malloc(kInitialBufferSize)); + if (buf_ == NULL) { + FATAL("Failed to allocate message buffer\n"); + } + buf_length_ = kInitialBufferSize; + buf_[0] = '\0'; + data_length_ = 0; +} + + +MessageBuffer::~MessageBuffer() { + free(buf_); + buf_ = NULL; + fd_ = -1; +} + + +bool MessageBuffer::IsValidMessage() const { + if (data_length_ == 0) { + return false; + } + dart::JSONReader msg_reader(buf_); + return msg_reader.EndOfObject() != NULL; +} + + +int MessageBuffer::MessageId() const { + dart::JSONReader r(buf_); + r.Seek("id"); + if (r.Type() == dart::JSONReader::kInteger) { + return atoi(r.ValueChars()); + } else { + return -1; + } +} + + +void MessageBuffer::ReadData() { + ASSERT(data_length_ >= 0); + ASSERT(data_length_ < buf_length_); + int max_read = buf_length_ - data_length_ - 1; + if (max_read == 0) { + // TODO(hausner): + // Buffer is full. What should we do if there is no valid message + // in the buffer? This might be possible if the client sends a message + // that's larger than the buffer, of if the client sends malformed + // messages that keep piling up. + ASSERT(IsValidMessage()); + return; + } + // TODO(hausner): Handle error conditions returned by Read. We may + // want to close the debugger connection if we get any errors. + int bytes_read = Socket::Read(fd_, buf_ + data_length_, max_read); + if (bytes_read == 0) { + connection_is_alive_ = false; + return; + } + ASSERT(bytes_read > 0); + data_length_ += bytes_read; + ASSERT(data_length_ < buf_length_); + buf_[data_length_] = '\0'; +} + + +void MessageBuffer::PopMessage() { + dart::JSONReader msg_reader(buf_); + const char* end = msg_reader.EndOfObject(); + if (end != NULL) { + ASSERT(*end == '}'); + end++; + data_length_ = 0; + while (*end != '\0') { + buf_[data_length_] = *end++; + data_length_++; + } + buf_[data_length_] = '\0'; + ASSERT(data_length_ < buf_length_); + } +} + + +static bool IsValidJSON(const char* msg) { + dart::JSONReader r(msg); + return r.EndOfObject() != NULL; +} + + +void DebuggerConnectionHandler::HandleResumeCmd() { + int msg_id = msgbuf_->MessageId(); + dart::TextBuffer msg(64); + msg.Printf("{ \"id\": %d }", msg_id); + FDUtils::WriteToBlocking(debugger_fd_, msg.buf(), msg.length()); + // TODO(hausner): Error checking. Probably just shut down the debugger + // session if we there is an error while writing. +} + + +void DebuggerConnectionHandler::HandleMessages() { + for (;;) { + while (!msgbuf_->IsValidMessage() && msgbuf_->Alive()) { + msgbuf_->ReadData(); + } + if (!msgbuf_->Alive()) { + return; + } + dart::JSONReader r(msgbuf_->buf()); + bool found = r.Seek("command"); + if (r.Error()) { + FATAL("Illegal JSON message received"); + } + if (!found) { + printf("'command' not found in JSON message: '%s'\n", msgbuf_->buf()); + msgbuf_->PopMessage(); + } else if (r.IsStringLiteral("resume")) { + HandleResumeCmd(); + msgbuf_->PopMessage(); + return; + } else { + printf("unrecognized command received: '%s'\n", msgbuf_->buf()); + msgbuf_->PopMessage(); + } + } +} + + +void DebuggerConnectionHandler::SendBreakpointEvent(Dart_Breakpoint bpt, + Dart_StackTrace trace) { + dart::TextBuffer msg(128); + intptr_t trace_len = 0; + Dart_Handle res = Dart_StackTraceLength(trace, &trace_len); + ASSERT_NOT_ERROR(res); + msg.Printf("{ \"command\" : \"paused\", \"params\" : "); + msg.Printf("{ \"callFrames\" : [ "); + for (int i = 0; i < trace_len; i++) { + Dart_ActivationFrame frame; + res = Dart_GetActivationFrame(trace, i, &frame); + ASSERT_NOT_ERROR(res); + Dart_Handle func_name; + Dart_Handle script_url; + intptr_t line_number = 0; + res = Dart_ActivationFrameInfo( + frame, &func_name, &script_url, &line_number); + ASSERT_NOT_ERROR(res); + ASSERT(Dart_IsString(func_name)); + const char* func_name_chars; + Dart_StringToCString(func_name, &func_name_chars); + msg.Printf("%s { \"functionName\" : \"%s\" , ", + i > 0 ? "," : "", + func_name_chars); + ASSERT(Dart_IsString(script_url)); + const char* script_url_chars; + Dart_StringToCString(script_url, &script_url_chars); + msg.Printf("\"location\": { \"scriptId\": \"%s\", \"lineNumber\": %d }}", + script_url_chars, line_number); + } + msg.Printf("]}}"); + Socket::Write(debugger_fd_, msg.buf(), msg.length()); + ASSERT(IsValidJSON(msg.buf())); +} + + +void DebuggerConnectionHandler::BreakpointHandler(Dart_Breakpoint bpt, + Dart_StackTrace trace) { + // TODO(hausner): rather than busy-waiting, block on the pipe to the + // debugger thread and wait until a debugger connection has been + // established. + while (!IsConnected()) { + printf("Waiting for debugger connection\n"); + sleep(1); + } + SendBreakpointEvent(bpt, trace); + HandleMessages(); + if (!msgbuf_->Alive()) { + CloseDbgConnection(); + } +} + + +void DebuggerConnectionHandler::AcceptDbgConnection(int debugger_fd) { + debugger_fd_ = debugger_fd; + ASSERT(msgbuf_ == NULL); + msgbuf_ = new MessageBuffer(debugger_fd_); +} + +void DebuggerConnectionHandler::CloseDbgConnection() { + if (debugger_fd_ >= 0) { + // TODO(hausner): need a Socket::Close() function. + } + if (msgbuf_ != NULL) { + delete msgbuf_; + msgbuf_ = NULL; + } + // TODO(hausner): Need to tell the VM debugger object to remove all + // breakpoints. +} + +void DebuggerConnectionHandler::StartHandler(const char* address, + int port_number) { + if (handler_started_) { + return; + } + ASSERT(listener_fd_ == -1); + listener_fd_ = ServerSocket::CreateBindListen(address, port_number, 1); + + handler_started_ = true; + DebuggerConnectionImpl::StartHandler(port_number); + Dart_SetBreakpointHandler(BreakpointHandler); +} + + +DebuggerConnectionHandler::~DebuggerConnectionHandler() { + CloseDbgConnection(); +} diff --git a/runtime/bin/dbg_connection.h b/runtime/bin/dbg_connection.h new file mode 100644 index 00000000000..0cddacc08f8 --- /dev/null +++ b/runtime/bin/dbg_connection.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_DBG_CONNECTION_H_ +#define BIN_DBG_CONNECTION_H_ + +#include "bin/builtin.h" +#include "bin/utils.h" + +#include "include/dart_debugger_api.h" + +#include "platform/globals.h" +#include "platform/thread.h" +// Declare the OS-specific types ahead of defining the generic class. +#if defined(TARGET_OS_LINUX) +#include "bin/dbg_connection_linux.h" +#elif defined(TARGET_OS_MACOS) +#include "bin/dbg_connection_macos.h" +#elif defined(TARGET_OS_WINDOWS) +#include "bin/dbg_connection_win.h" +#else +#error Unknown target os. +#endif + + +class MessageBuffer; + +class DebuggerConnectionHandler { + public: + ~DebuggerConnectionHandler(); + static void StartHandler(const char* address, int port_number); + + static bool IsConnected() { + return debugger_fd_ >= 0; + } + + private: + static void SendBreakpointEvent(Dart_Breakpoint bpt, Dart_StackTrace trace); + static void BreakpointHandler(Dart_Breakpoint bpt, Dart_StackTrace trace); + + static void AcceptDbgConnection(int debug_fd); + static void CloseDbgConnection(); + + static void HandleMessages(); + static void HandleResumeCmd(); + + static bool handler_started_; + + // The socket that is listening for incoming debugger connections. + // This descriptor is created and closed by a VM thread. + static int listener_fd_; + + // The socket that connects with the debugger client. + // The descriptor is created by the debugger connection thread and + // closed by a VM thread. + static int debugger_fd_; + + static MessageBuffer* msgbuf_; + + friend class DebuggerConnectionImpl; + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(DebuggerConnectionHandler); +}; + +#endif // BIN_DBG_CONNECTION_H_ diff --git a/runtime/bin/dbg_connection_linux.cc b/runtime/bin/dbg_connection_linux.cc new file mode 100644 index 00000000000..49208826572 --- /dev/null +++ b/runtime/bin/dbg_connection_linux.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "bin/dbg_connection.h" +#include "bin/fdutils.h" +#include "bin/socket.h" + + +void DebuggerConnectionImpl::StartHandler(int port_number) { + FATAL("Debugger wire protocol not yet implemented on Linux\n"); +} diff --git a/runtime/bin/dbg_connection_linux.h b/runtime/bin/dbg_connection_linux.h new file mode 100644 index 00000000000..203226a44a5 --- /dev/null +++ b/runtime/bin/dbg_connection_linux.h @@ -0,0 +1,18 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_DBG_CONNECTION_LINUX_H_ +#define BIN_DBG_CONNECTION_LINUX_H_ + +#include +#include +#include + + +class DebuggerConnectionImpl { + public: + static void StartHandler(int port_number); +}; + +#endif // BIN_DBG_CONNECTION_LINUX_H_ diff --git a/runtime/bin/dbg_connection_macos.cc b/runtime/bin/dbg_connection_macos.cc new file mode 100644 index 00000000000..6538be29a2f --- /dev/null +++ b/runtime/bin/dbg_connection_macos.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +#include "bin/dartutils.h" +#include "bin/dbg_connection.h" +#include "bin/fdutils.h" +#include "bin/socket.h" +#include "platform/thread.h" +#include "platform/utils.h" + + +#define INVALID_FD -1 + +int DebuggerConnectionImpl::kqueue_fd_ = INVALID_FD; +int DebuggerConnectionImpl::wakeup_fds_[2] = {INVALID_FD, INVALID_FD}; + + +// Used by VM threads to send a message to the debugger connetion +// handler thread. +void DebuggerConnectionImpl::SendMessage(MessageType id) { + ASSERT(wakeup_fds_[1] != INVALID_FD); + struct Message msg; + msg.msg_id = id; + int result = FDUtils::WriteToBlocking(wakeup_fds_[1], &msg, sizeof(msg)); + if (result != sizeof(msg)) { + if (result == -1) { + perror("Wakeup message failure: "); + } + FATAL1("Wakeup message failure. Wrote %d bytes.", result); + } +} + + +// Used by the debugger connection handler to read the messages sent +// by the VM. +bool DebuggerConnectionImpl::ReceiveMessage(Message* msg) { + int total_read = 0; + int bytes_read = 0; + int remaining = sizeof(Message); + uint8_t* buf = reinterpret_cast(msg); + while (remaining > 0) { + bytes_read = + TEMP_FAILURE_RETRY(read(wakeup_fds_[0], buf + total_read, remaining)); + if ((bytes_read < 0) && (total_read == 0)) { + return false; + } + if (bytes_read > 0) { + total_read += bytes_read; + remaining -= bytes_read; + } + } + ASSERT(remaining >= 0); + return remaining == 0; +} + + +void DebuggerConnectionImpl::HandleEvent(struct kevent* event) { + int ident = event->ident; + if (ident == DebuggerConnectionHandler::listener_fd_) { + if (DebuggerConnectionHandler::IsConnected()) { + FATAL("Cannot connect to more than one debugger.\n"); + } + int fd = ServerSocket::Accept(ident); + if (fd < 0) { + FATAL("Accepting new debugger connection failed.\n"); + } + FDUtils::SetBlocking(fd); + DebuggerConnectionHandler::AcceptDbgConnection(fd); + + /* For now, don't poll the debugger connection. + struct kevent ev_add; + EV_SET(&ev_add, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); + int status = + TEMP_FAILURE_RETRY(kevent(kqueue_fd_, &ev_add, 1, NULL, 0, NULL)); + if (status == -1) { + FATAL1("Failed adding debugger socket to kqueue: %s\n", strerror(errno)); + } + */ + } else if (ident == DebuggerConnectionHandler::debugger_fd_) { + printf("unexpected: receiving debugger connection event.\n"); + UNIMPLEMENTED(); + } else { + Message msg; + if (ReceiveMessage(&msg)) { + printf("Received sync message id %d.\n", msg.msg_id); + } + } +} + + +void DebuggerConnectionImpl::Handler(uword args) { + static const intptr_t kMaxEvents = 4; + struct kevent events[kMaxEvents]; + + while (1) { + // Wait indefinitely for an event. + int result = TEMP_FAILURE_RETRY( + kevent(kqueue_fd_, NULL, 0, events, kMaxEvents, NULL)); + if (result == -1) { + FATAL1("kevent failed %s\n", strerror(errno)); + } else { + ASSERT(result <= kMaxEvents); + for (int i = 0; i < result; i++) { + HandleEvent(&events[i]); + } + } + } + printf("shutting down debugger thread\n"); +} + + +void DebuggerConnectionImpl::SetupPollQueue() { + int result; + result = TEMP_FAILURE_RETRY(pipe(wakeup_fds_)); + if (result != 0) { + FATAL1("Pipe creation failed with error %d\n", result); + } + FDUtils::SetNonBlocking(wakeup_fds_[0]); + + kqueue_fd_ = TEMP_FAILURE_RETRY(kqueue()); + if (kqueue_fd_ == -1) { + FATAL("Failed creating kqueue\n"); + } + // Register the wakeup_fd_ with the kqueue. + struct kevent event; + EV_SET(&event, wakeup_fds_[0], EVFILT_READ, EV_ADD, 0, 0, NULL); + int status = TEMP_FAILURE_RETRY(kevent(kqueue_fd_, &event, 1, NULL, 0, NULL)); + if (status == -1) { + FATAL1("Failed adding wakeup pipe fd to kqueue: %s\n", strerror(errno)); + } + + // Register the listening socket. + EV_SET(&event, DebuggerConnectionHandler::listener_fd_, + EVFILT_READ, EV_ADD, 0, 0, NULL); + status = TEMP_FAILURE_RETRY(kevent(kqueue_fd_, &event, 1, NULL, 0, NULL)); + if (status == -1) { + FATAL1("Failed adding listener socket to kqueue: %s\n", strerror(errno)); + } +} + + +void DebuggerConnectionImpl::StartHandler(int port_number) { + ASSERT(DebuggerConnectionHandler::listener_fd_ != -1); + SetupPollQueue(); + int result = + dart::Thread::Start(&DebuggerConnectionImpl::Handler, 0); + if (result != 0) { + FATAL1("Failed to start debugger connection handler thread: %d\n", result); + } +} diff --git a/runtime/bin/dbg_connection_macos.h b/runtime/bin/dbg_connection_macos.h new file mode 100644 index 00000000000..0bd8634055f --- /dev/null +++ b/runtime/bin/dbg_connection_macos.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_DBG_CONNECTION_MACOS_H_ +#define BIN_DBG_CONNECTION_MACOS_H_ + +#include +#include +#include + + +class DebuggerConnectionImpl { + public: + static void StartHandler(int port_number); + + private: + enum MessageType { + kAddDbgFd = 1, + kRemoveDbgFd, + kQuit + }; + + struct Message { + MessageType msg_id; + }; + + static void SendMessage(MessageType id); + static bool ReceiveMessage(Message* msg); + + static void SetupPollQueue(); + static void HandleEvent(struct kevent* event); + static void Handler(uword args); + + + + // File descriptors for pipes used to communicate with the debugger thread. + static int wakeup_fds_[2]; + // File descriptor for the polling queue. + static int kqueue_fd_; +}; + + +#endif // BIN_DBG_CONNECTION_MACOS_H_ diff --git a/runtime/bin/dbg_connection_win.cc b/runtime/bin/dbg_connection_win.cc new file mode 100644 index 00000000000..86b06866a80 --- /dev/null +++ b/runtime/bin/dbg_connection_win.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "bin/fdutils.h" +#include "bin/socket.h" + +void DebuggerConnectionImpl::StartHandler(int port_number) { + FATAL("Debugger wire protocol not yet implemented on Linux\n"); +} + diff --git a/runtime/bin/dbg_connection_win.h b/runtime/bin/dbg_connection_win.h new file mode 100644 index 00000000000..2b961dc1066 --- /dev/null +++ b/runtime/bin/dbg_connection_win.h @@ -0,0 +1,18 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef BIN_DBG_CONNECTION_WIN_H_ +#define BIN_DBG_CONNECTION_WIN_H_ + +#include +#include +#include + + +class DebuggerConnectionImpl { + public: + static void StartHandler(int port_number); +}; + +#endif // BIN_DBG_CONNECTION_WIN_H_ diff --git a/runtime/bin/fdutils.h b/runtime/bin/fdutils.h index 4ada94b0ffe..1cb82e36f23 100644 --- a/runtime/bin/fdutils.h +++ b/runtime/bin/fdutils.h @@ -11,6 +11,7 @@ class FDUtils { public: static bool SetNonBlocking(intptr_t fd); + static bool SetBlocking(intptr_t fd); // Checks whether the file descriptor is blocking. If the function // returns true the value pointed to by is_blocking will be set to diff --git a/runtime/bin/fdutils_linux.cc b/runtime/bin/fdutils_linux.cc index 0fe89db9fc1..7e8f72b9097 100644 --- a/runtime/bin/fdutils_linux.cc +++ b/runtime/bin/fdutils_linux.cc @@ -10,14 +10,14 @@ #include "bin/fdutils.h" -bool FDUtils::SetNonBlocking(intptr_t fd) { +static bool SetBlockingHelper(intptr_t fd, bool blocking) { intptr_t status; status = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)); if (status < 0) { perror("fcntl F_GETFL failed"); return false; } - status = (status | O_NONBLOCK); + status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK); if (TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, status)) < 0) { perror("fcntl F_SETFL failed"); return false; @@ -26,6 +26,16 @@ bool FDUtils::SetNonBlocking(intptr_t fd) { } +bool FDUtils::SetNonBlocking(intptr_t fd) { + return SetBlockingHelper(fd, false); +} + + +bool FDUtils::SetBlocking(intptr_t fd) { + return SetBlockingHelper(fd, true); +} + + bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { intptr_t status; status = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)); diff --git a/runtime/bin/fdutils_macos.cc b/runtime/bin/fdutils_macos.cc index ade4bbe15e0..b9ea0493bd1 100644 --- a/runtime/bin/fdutils_macos.cc +++ b/runtime/bin/fdutils_macos.cc @@ -10,14 +10,14 @@ #include "bin/fdutils.h" -bool FDUtils::SetNonBlocking(intptr_t fd) { +static bool SetBlockingHelper(intptr_t fd, bool blocking) { intptr_t status; status = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)); if (status < 0) { perror("fcntl F_GETFL failed"); return false; } - status = (status | O_NONBLOCK); + status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK); if (TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, status)) < 0) { perror("fcntl F_SETFL failed"); return false; @@ -26,6 +26,16 @@ bool FDUtils::SetNonBlocking(intptr_t fd) { } +bool FDUtils::SetNonBlocking(intptr_t fd) { + return SetBlockingHelper(fd, false); +} + + +bool FDUtils::SetBlocking(intptr_t fd) { + return SetBlockingHelper(fd, true); +} + + bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { intptr_t status; status = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)); diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index 0b3f389d2a1..2db7280a108 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -11,6 +11,7 @@ #include "bin/builtin.h" #include "bin/dartutils.h" +#include "bin/dbg_connection.h" #include "bin/directory.h" #include "bin/eventhandler.h" #include "bin/extensions.h" @@ -46,6 +47,15 @@ static const char* generate_pprof_symbols_filename = NULL; static const char* breakpoint_at = NULL; +// Global state that indicates whether we should open a connection +// and listen for a debugger to connect. +static bool start_debugger = false; +static const int DEFAULT_DEBUG_PORT = 5858; +static const char* DEFAULT_DEBUG_IP = "127.0.0.1"; +static const char* debug_ip = DEFAULT_DEBUG_IP; +static int debug_port = 0; + + // Value of the --package-root flag. // (This pointer points into an argv buffer and does not need to be // free'd.) @@ -84,6 +94,28 @@ static void ProcessCompileAllOption(const char* compile_all) { } +static void ProcessDebugOption(const char* port) { + // TODO(hausner): Add support for specifying an IP address on which + // the debugger should listen. + ASSERT(port != NULL); + debug_port = 0; + if (*port == '\0') { + debug_port = DEFAULT_DEBUG_PORT; + } else { + if ((*port == '=') || (*port == ':')) { + debug_port = atoi(port + 1); + } + } + if (debug_port == 0) { + fprintf(stderr, "unrecognized --debug option syntax. " + "Use --debug[:]\n"); + return; + } + breakpoint_at = "main"; + start_debugger = true; +} + + static void ProcessPprofOption(const char* filename) { ASSERT(filename != NULL); generate_pprof_symbols_filename = filename; @@ -102,6 +134,7 @@ static struct { } main_options[] = { { "--break_at=", ProcessBreakpointOption }, { "--compile_all", ProcessCompileAllOption }, + { "--debug", ProcessDebugOption }, { "--generate_pprof_symbols=", ProcessPprofOption }, { "--import_map=", ProcessImportMapOption }, { "--package-root=", ProcessPackageRootOption }, @@ -613,6 +646,13 @@ int main(int argc, char** argv) { Dart_GetError(result)); } } + + // Start the debugger wire protocol handler if necessary. + if (start_debugger) { + ASSERT(debug_port != 0); + DebuggerConnectionHandler::StartHandler(debug_ip, debug_port); + } + // Lookup and invoke the top level main function. result = Dart_Invoke(library, Dart_NewString("main"), 0, NULL); if (Dart_IsError(result)) { diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc index af57cfe6524..16a58ddaaa3 100644 --- a/runtime/lib/mirrors.cc +++ b/runtime/lib/mirrors.cc @@ -4,9 +4,9 @@ #include "vm/bootstrap_natives.h" +#include "platform/json.h" #include "vm/dart_entry.h" #include "vm/exceptions.h" -#include "vm/json.h" #include "vm/message.h" #include "vm/port.h" #include "vm/resolver.h" diff --git a/runtime/vm/json.cc b/runtime/platform/json.cc similarity index 93% rename from runtime/vm/json.cc rename to runtime/platform/json.cc index 7b862d35531..58c758ac062 100644 --- a/runtime/vm/json.cc +++ b/runtime/platform/json.cc @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -#include "vm/json.h" +#include "platform/json.h" #include "platform/assert.h" #include "platform/utils.h" @@ -212,6 +212,7 @@ void JSONReader::Set(const char* json_object) { bool JSONReader::Seek(const char* name) { + error_ = false; scanner_.SetText(json_object_); scanner_.Scan(); if (scanner_.CurrentToken() != JSONScanner::TokenLBrace) { @@ -219,6 +220,9 @@ bool JSONReader::Seek(const char* name) { return false; } scanner_.Scan(); + if (scanner_.CurrentToken() == JSONScanner::TokenRBrace) { + return false; + } while (scanner_.CurrentToken() == JSONScanner::TokenString) { bool found = scanner_.IsStringLiteral(name); scanner_.Scan(); @@ -261,16 +265,30 @@ bool JSONReader::Seek(const char* name) { scanner_.Scan(); // Value or closing brace or bracket. if (scanner_.CurrentToken() == JSONScanner::TokenComma) { scanner_.Scan(); + } else if (scanner_.CurrentToken() == JSONScanner::TokenRBrace) { + return false; } else { - // End of json object or malformed object. Value not found. error_ = true; - break; + return false; } } + error_ = true; return false; } +const char* JSONReader::EndOfObject() { + bool found = Seek("***"); // Look for illegally named value. + ASSERT(!found); + if (!found && !error_) { + const char* s = scanner_.TokenChars(); + ASSERT(*s == '}'); + return s; + } + return NULL; +} + + JSONReader::JSONType JSONReader::Type() const { if (error_) { return kNone; diff --git a/runtime/vm/json.h b/runtime/platform/json.h similarity index 91% rename from runtime/vm/json.h rename to runtime/platform/json.h index 0b23b959192..d70a0ab2e99 100644 --- a/runtime/vm/json.h +++ b/runtime/platform/json.h @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -#ifndef VM_JSON_H_ -#define VM_JSON_H_ +#ifndef PLATFORM_JSON_H_ +#define PLATFORM_JSON_H_ #include "vm/allocation.h" #include "vm/globals.h" @@ -77,6 +77,10 @@ class JSONReader : ValueObject { // Returns true if a syntax error was found. bool Error() const { return error_; } + // Returns a pointer to the matching closing brace if the text starts + // with a valid JSON object. Returns NULL otherwise. + const char* EndOfObject(); + JSONType Type() const; const char* ValueChars() const { return (Type() != kNone) ? scanner_.TokenChars() : NULL; @@ -115,6 +119,7 @@ class TextBuffer : ValueObject { void Clear(); char* buf() { return buf_; } + intptr_t length() { return msg_len_; } private: void GrowBuffer(intptr_t len); @@ -125,4 +130,4 @@ class TextBuffer : ValueObject { } // namespace dart -#endif // VM_JSON_H_ +#endif // PLATFORM_JSON_H_ diff --git a/runtime/platform/platform_headers.gypi b/runtime/platform/platform_headers.gypi index 29eed6e4edc..922cb27a26b 100644 --- a/runtime/platform/platform_headers.gypi +++ b/runtime/platform/platform_headers.gypi @@ -9,6 +9,7 @@ 'c99_support_win.h', 'globals.h', 'inttypes_support_win.h', + 'json.h', 'thread.h', 'thread_linux.h', 'thread_macos.h', diff --git a/runtime/platform/platform_sources.gypi b/runtime/platform/platform_sources.gypi index 408720fe8b0..7c36652b480 100644 --- a/runtime/platform/platform_sources.gypi +++ b/runtime/platform/platform_sources.gypi @@ -6,6 +6,7 @@ { 'sources': [ 'assert.cc', + 'json.cc', 'thread_linux.cc', 'thread_macos.cc', 'thread_win.cc', diff --git a/runtime/vm/json_test.cc b/runtime/vm/json_test.cc index 3b1b98fce7e..90c9fdfb436 100644 --- a/runtime/vm/json_test.cc +++ b/runtime/vm/json_test.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/assert.h" -#include "vm/json.h" +#include "platform/json.h" #include "vm/unit_test.h" namespace dart { diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi index 3208097c290..50163c31644 100644 --- a/runtime/vm/vm_sources.gypi +++ b/runtime/vm/vm_sources.gypi @@ -163,8 +163,6 @@ 'isolate.cc', 'isolate.h', 'isolate_test.cc', - 'json.cc', - 'json.h', 'json_test.cc', 'longjump.cc', 'longjump.h',