| 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(); | 
| +} | 
|  |