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:<portnumber>. 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
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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<char*>(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();
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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 <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
@@ -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 <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
class DebuggerConnectionImpl {
|
||||
public:
|
||||
static void StartHandler(int port_number);
|
||||
};
|
||||
|
||||
#endif // BIN_DBG_CONNECTION_LINUX_H_
|
||||
@@ -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 <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/event.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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<uint8_t*>(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);
|
||||
}
|
||||
}
|
||||
@@ -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 <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
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_
|
||||
@@ -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 <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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");
|
||||
}
|
||||
|
||||
@@ -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 <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
class DebuggerConnectionImpl {
|
||||
public:
|
||||
static void StartHandler(int port_number);
|
||||
};
|
||||
|
||||
#endif // BIN_DBG_CONNECTION_WIN_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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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[:<port number>]\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)) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
@@ -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_
|
||||
@@ -9,6 +9,7 @@
|
||||
'c99_support_win.h',
|
||||
'globals.h',
|
||||
'inttypes_support_win.h',
|
||||
'json.h',
|
||||
'thread.h',
|
||||
'thread_linux.h',
|
||||
'thread_macos.h',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
{
|
||||
'sources': [
|
||||
'assert.cc',
|
||||
'json.cc',
|
||||
'thread_linux.cc',
|
||||
'thread_macos.cc',
|
||||
'thread_win.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 {
|
||||
|
||||
@@ -163,8 +163,6 @@
|
||||
'isolate.cc',
|
||||
'isolate.h',
|
||||
'isolate_test.cc',
|
||||
'json.cc',
|
||||
'json.h',
|
||||
'json_test.cc',
|
||||
'longjump.cc',
|
||||
'longjump.h',
|
||||
|
||||
Reference in New Issue
Block a user