Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "bin/dbg_connection.h" | |
| 6 #include "bin/dartutils.h" | |
| 7 #include "bin/fdutils.h" | |
| 8 #include "bin/socket.h" | |
| 9 #include "bin/thread.h" | |
| 10 #include "bin/utils.h" | |
| 11 | |
| 12 #include "platform/globals.h" | |
| 13 #include "platform/json.h" | |
| 14 #include "platform/thread.h" | |
| 15 #include "platform/utils.h" | |
| 16 | |
| 17 #include "include/dart_api.h" | |
| 18 | |
| 19 | |
| 20 int DebuggerConnectionHandler::listener_fd_ = -1; | |
| 21 int DebuggerConnectionHandler::debugger_fd_ = -1; | |
| 22 MessageBuffer* DebuggerConnectionHandler::msgbuf_ = NULL; | |
| 23 | |
| 24 bool DebuggerConnectionHandler::handler_started_ = false; | |
| 25 | |
| 26 | |
| 27 // TODO(hausner): Need better error handling. | |
| 28 #define ASSERT_NOT_ERROR(handle) \ | |
| 29 ASSERT(!Dart_IsError(handle)) | |
| 30 | |
| 31 | |
| 32 class MessageBuffer { | |
| 33 public: | |
| 34 explicit MessageBuffer(int fd); | |
| 35 ~MessageBuffer(); | |
| 36 void ReadData(); | |
| 37 bool IsValidMessage() const; | |
| 38 void PopMessage(); | |
| 39 int MessageId() const; | |
| 40 char* buf() const { return buf_; } | |
| 41 bool Alive() const { return connection_is_alive_; } | |
| 42 | |
| 43 private: | |
| 44 static const int kInitialBufferSize = 256; | |
| 45 char* buf_; | |
| 46 int buf_length_; | |
| 47 int fd_; | |
| 48 int data_length_; | |
| 49 bool connection_is_alive_; | |
|
siva
2012/05/03 22:52:08
The DISALLOW stuff ....
hausner
2012/05/03 23:52:00
Done.
| |
| 50 }; | |
| 51 | |
| 52 | |
| 53 MessageBuffer::MessageBuffer(int fd) | |
| 54 : buf_(NULL), | |
| 55 buf_length_(0), | |
| 56 fd_(fd), | |
| 57 data_length_(0), | |
| 58 connection_is_alive_(true) { | |
| 59 buf_ = reinterpret_cast<char*>(malloc(kInitialBufferSize)); | |
| 60 if (buf_ == NULL) { | |
| 61 FATAL("Failed to allocate message buffer\n"); | |
| 62 } | |
| 63 buf_length_ = kInitialBufferSize; | |
| 64 buf_[0] = '\0'; | |
| 65 data_length_ = 0; | |
| 66 } | |
| 67 | |
| 68 | |
| 69 MessageBuffer::~MessageBuffer() { | |
| 70 free(buf_); | |
| 71 buf_ = NULL; | |
| 72 fd_ = -1; | |
| 73 } | |
| 74 | |
| 75 | |
| 76 bool MessageBuffer::IsValidMessage() const { | |
| 77 if (data_length_ == 0) { | |
| 78 return false; | |
| 79 } | |
| 80 dart::JSONReader msg_reader(buf_); | |
| 81 return msg_reader.EndOfObject() != NULL; | |
| 82 } | |
| 83 | |
| 84 | |
| 85 int MessageBuffer::MessageId() const { | |
| 86 dart::JSONReader r(buf_); | |
| 87 r.Seek("id"); | |
| 88 if (r.Type() == dart::JSONReader::kInteger) { | |
| 89 return atoi(r.ValueChars()); | |
| 90 } else { | |
| 91 return -1; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 | |
| 96 void MessageBuffer::ReadData() { | |
| 97 ASSERT(data_length_ >= 0); | |
| 98 ASSERT(data_length_ < buf_length_); | |
| 99 int max_read = buf_length_ - data_length_ - 1; | |
| 100 if (max_read == 0) { | |
| 101 // TODO(hausner): | |
| 102 // Buffer is full. What should we do if there is no valid message | |
| 103 // in the buffer? This might be possible if the client sends a message | |
| 104 // that's larger than the buffer, of if the client sends malformed | |
| 105 // messages that keep piling up. | |
| 106 ASSERT(IsValidMessage()); | |
| 107 return; | |
| 108 } | |
| 109 int bytes_read = Socket::Read(fd_, buf_ + data_length_, max_read); | |
|
siva
2012/05/03 22:52:08
Socket:;Read could also return -1 on error conditi
hausner
2012/05/03 23:52:00
Done.
| |
| 110 if (bytes_read == 0) { | |
|
siva
2012/05/03 22:52:08
Socket::Read seems to have some weird code in ther
hausner
2012/05/03 23:52:00
Yes I think on non-blocking sockets they treat the
| |
| 111 connection_is_alive_ = false; | |
| 112 return; | |
| 113 } | |
| 114 ASSERT(bytes_read > 0); | |
| 115 data_length_ += bytes_read; | |
| 116 ASSERT(data_length_ < buf_length_); | |
| 117 buf_[data_length_] = '\0'; | |
| 118 } | |
| 119 | |
| 120 | |
| 121 void MessageBuffer::PopMessage() { | |
| 122 dart::JSONReader msg_reader(buf_); | |
| 123 const char* end = msg_reader.EndOfObject(); | |
| 124 if (end != NULL) { | |
| 125 ASSERT(*end == '}'); | |
| 126 end++; | |
| 127 data_length_ = 0; | |
| 128 while (*end != '\0') { | |
| 129 buf_[data_length_] = *end++; | |
| 130 data_length_++; | |
| 131 } | |
| 132 buf_[data_length_] = '\0'; | |
| 133 ASSERT(data_length_ < buf_length_); | |
|
siva
2012/05/03 22:52:08
This assert should be inside the loop where data_l
hausner
2012/05/03 23:52:00
True, but rather than slowing things down too much
| |
| 134 } | |
| 135 } | |
| 136 | |
| 137 | |
| 138 static bool IsValidJSON(const char* msg) { | |
| 139 dart::JSONReader r(msg); | |
| 140 return r.EndOfObject() != NULL; | |
| 141 } | |
| 142 | |
| 143 | |
| 144 void DebuggerConnectionHandler::HandleResumeCmd() { | |
| 145 int msg_id = msgbuf_->MessageId(); | |
| 146 dart::TextBuffer msg(64); | |
| 147 msg.Printf("{ \"id\": %d }", msg_id); | |
| 148 FDUtils::WriteToBlocking(debugger_fd_, msg.buf(), msg.length()); | |
|
siva
2012/05/03 22:52:08
Need to check for return value from WriteToBlockin
hausner
2012/05/03 23:52:00
Added a TODO for error checking.
On 2012/05/03 22
| |
| 149 } | |
| 150 | |
| 151 | |
| 152 void DebuggerConnectionHandler::HandleMessages() { | |
| 153 for (;;) { | |
| 154 while (!msgbuf_->IsValidMessage() && msgbuf_->Alive()) { | |
| 155 msgbuf_->ReadData(); | |
| 156 } | |
| 157 if (!msgbuf_->Alive()) { | |
| 158 return; | |
| 159 } | |
| 160 dart::JSONReader r(msgbuf_->buf()); | |
| 161 bool found = r.Seek("command"); | |
| 162 if (r.Error()) { | |
| 163 FATAL("Illegal JSON message received"); | |
| 164 } | |
| 165 if (!found) { | |
| 166 printf("'command' not found in JSON message: '%s'\n", msgbuf_->buf()); | |
| 167 msgbuf_->PopMessage(); | |
| 168 } else if (r.IsStringLiteral("resume")) { | |
| 169 HandleResumeCmd(); | |
| 170 msgbuf_->PopMessage(); | |
| 171 return; | |
| 172 } else { | |
| 173 printf("unrecognized command received: '%s'\n", msgbuf_->buf()); | |
| 174 msgbuf_->PopMessage(); | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 | |
| 180 void DebuggerConnectionHandler::SendBreakpointEvent(Dart_Breakpoint bpt, | |
| 181 Dart_StackTrace trace) { | |
| 182 dart::TextBuffer msg(128); | |
| 183 intptr_t trace_len = 0; | |
| 184 Dart_Handle res = Dart_StackTraceLength(trace, &trace_len); | |
| 185 ASSERT_NOT_ERROR(res); | |
| 186 msg.Printf("{ \"command\" : \"paused\", \"params\" : "); | |
| 187 msg.Printf("{ \"callFrames\" : [ "); | |
| 188 for (int i = 0; i < trace_len; i++) { | |
| 189 Dart_ActivationFrame frame; | |
| 190 res = Dart_GetActivationFrame(trace, i, &frame); | |
| 191 ASSERT_NOT_ERROR(res); | |
| 192 Dart_Handle func_name; | |
| 193 Dart_Handle script_url; | |
| 194 intptr_t line_number = 0; | |
| 195 res = Dart_ActivationFrameInfo( | |
| 196 frame, &func_name, &script_url, &line_number); | |
| 197 ASSERT_NOT_ERROR(res); | |
| 198 ASSERT(Dart_IsString(func_name)); | |
| 199 const char* func_name_chars; | |
| 200 Dart_StringToCString(func_name, &func_name_chars); | |
| 201 msg.Printf("%s { \"functionName\" : \"%s\" , ", | |
| 202 i > 0 ? "," : "", | |
| 203 func_name_chars); | |
| 204 ASSERT(Dart_IsString(script_url)); | |
| 205 const char* script_url_chars; | |
| 206 Dart_StringToCString(script_url, &script_url_chars); | |
| 207 msg.Printf("\"location\": { \"scriptId\": \"%s\", \"lineNumber\": %d }}", | |
| 208 script_url_chars, line_number); | |
| 209 } | |
| 210 msg.Printf("]}}"); | |
| 211 printf("Breakpoint event message: '%s'\n", msg.buf()); | |
| 212 ASSERT(IsValidJSON(msg.buf())); | |
| 213 } | |
| 214 | |
| 215 | |
| 216 void DebuggerConnectionHandler::BreakpointHandler(Dart_Breakpoint bpt, | |
| 217 Dart_StackTrace trace) { | |
| 218 while (!IsConnected()) { | |
| 219 printf("Waiting for debugger connection\n"); | |
| 220 sleep(1); | |
| 221 } | |
|
siva
2012/05/03 22:52:08
I am wondering if this loop instead of sitting her
hausner
2012/05/03 23:52:00
True, but fairly soon I plan to support the "pause
| |
| 222 SendBreakpointEvent(bpt, trace); | |
| 223 HandleMessages(); | |
| 224 if (!msgbuf_->Alive()) { | |
| 225 CloseDbgConnection(); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 | |
| 230 void DebuggerConnectionHandler::AcceptDbgConnection(int debugger_fd) { | |
| 231 debugger_fd_ = debugger_fd; | |
| 232 ASSERT(msgbuf_ == NULL); | |
| 233 msgbuf_ = new MessageBuffer(debugger_fd_); | |
| 234 } | |
| 235 | |
| 236 void DebuggerConnectionHandler::CloseDbgConnection() { | |
| 237 if (debugger_fd_ >= 0) { | |
| 238 // TODO(hausner): need a Socket::Close() function. | |
| 239 } | |
| 240 if (msgbuf_ != NULL) { | |
| 241 delete msgbuf_; | |
| 242 msgbuf_ = NULL; | |
| 243 } | |
|
siva
2012/05/03 22:52:08
What about the debugger state (breakpoints set etc
hausner
2012/05/03 23:52:00
Good point. I was thinking of removing all breakpo
| |
| 244 } | |
| 245 | |
| 246 void DebuggerConnectionHandler::StartHandler(int port_number) { | |
| 247 if (handler_started_) { | |
| 248 return; | |
| 249 } | |
| 250 ASSERT(listener_fd_ == -1); | |
| 251 listener_fd_ = ServerSocket::CreateBindListen("127.0.0.1", port_number, 1); | |
|
siva
2012/05/03 22:52:08
instead of hard coding the ip address why not acce
hausner
2012/05/03 23:52:00
I moved this default IP address to main.cc and add
| |
| 252 | |
| 253 handler_started_ = true; | |
| 254 DebuggerConnectionImpl::StartHandler(port_number); | |
| 255 Dart_SetBreakpointHandler(BreakpointHandler); | |
| 256 } | |
| 257 | |
| 258 | |
| 259 DebuggerConnectionHandler::~DebuggerConnectionHandler() { | |
| 260 CloseDbgConnection(); | |
| 261 } | |
| OLD | NEW |