| Index: runtime/bin/dbg_connection.cc
|
| ===================================================================
|
| --- runtime/bin/dbg_connection.cc (revision 0)
|
| +++ runtime/bin/dbg_connection.cc (revision 0)
|
| @@ -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();
|
| +}
|
|
|