| 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/dbg_message.h" | |
| 7 #include "bin/dartutils.h" | |
| 8 #include "bin/lockers.h" | |
| 9 #include "bin/log.h" | |
| 10 #include "bin/socket.h" | |
| 11 #include "bin/thread.h" | |
| 12 #include "bin/utils.h" | |
| 13 | |
| 14 #include "platform/globals.h" | |
| 15 #include "platform/json.h" | |
| 16 #include "platform/utils.h" | |
| 17 | |
| 18 #include "include/dart_api.h" | |
| 19 | |
| 20 | |
| 21 namespace dart { | |
| 22 namespace bin { | |
| 23 | |
| 24 bool trace_debug_protocol = false; | |
| 25 | |
| 26 intptr_t DebuggerConnectionHandler::listener_fd_ = -1; | |
| 27 Monitor* DebuggerConnectionHandler::handler_lock_ = new Monitor(); | |
| 28 | |
| 29 // TODO(asiva): Remove this once we have support for multiple debugger | |
| 30 // connections. For now we just store the single debugger connection | |
| 31 // handler in a static variable. | |
| 32 static DebuggerConnectionHandler* singleton_handler = NULL; | |
| 33 | |
| 34 // The maximum message length to print when --trace_debug_protocol is | |
| 35 // specified. | |
| 36 static const int kMaxPrintMessageLen = 1024; | |
| 37 | |
| 38 class MessageBuffer { | |
| 39 public: | |
| 40 explicit MessageBuffer(intptr_t fd); | |
| 41 ~MessageBuffer(); | |
| 42 void ReadData(); | |
| 43 bool IsValidMessage() const; | |
| 44 void PopMessage(); | |
| 45 int MessageId() const; | |
| 46 char* buf() const { return buf_; } | |
| 47 bool Alive() const { return connection_is_alive_; } | |
| 48 | |
| 49 private: | |
| 50 static const int kInitialBufferSize = 256; | |
| 51 char* buf_; | |
| 52 int buf_length_; | |
| 53 intptr_t fd_; | |
| 54 int data_length_; | |
| 55 bool connection_is_alive_; | |
| 56 | |
| 57 DISALLOW_COPY_AND_ASSIGN(MessageBuffer); | |
| 58 }; | |
| 59 | |
| 60 | |
| 61 MessageBuffer::MessageBuffer(intptr_t fd) | |
| 62 : buf_(NULL), | |
| 63 buf_length_(0), | |
| 64 fd_(fd), | |
| 65 data_length_(0), | |
| 66 connection_is_alive_(true) { | |
| 67 buf_ = reinterpret_cast<char*>(malloc(kInitialBufferSize)); | |
| 68 if (buf_ == NULL) { | |
| 69 FATAL("Failed to allocate message buffer\n"); | |
| 70 } | |
| 71 buf_length_ = kInitialBufferSize; | |
| 72 buf_[0] = '\0'; | |
| 73 data_length_ = 0; | |
| 74 } | |
| 75 | |
| 76 | |
| 77 MessageBuffer::~MessageBuffer() { | |
| 78 free(buf_); | |
| 79 buf_ = NULL; | |
| 80 fd_ = -1; | |
| 81 } | |
| 82 | |
| 83 | |
| 84 bool MessageBuffer::IsValidMessage() const { | |
| 85 if (data_length_ == 0) { | |
| 86 return false; | |
| 87 } | |
| 88 dart::JSONReader msg_reader(buf_); | |
| 89 return msg_reader.EndOfObject() != NULL; | |
| 90 } | |
| 91 | |
| 92 | |
| 93 int MessageBuffer::MessageId() const { | |
| 94 dart::JSONReader r(buf_); | |
| 95 r.Seek("id"); | |
| 96 if (r.Type() == dart::JSONReader::kInteger) { | |
| 97 return atoi(r.ValueChars()); | |
| 98 } else { | |
| 99 return -1; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 | |
| 104 void MessageBuffer::ReadData() { | |
| 105 ASSERT(data_length_ >= 0); | |
| 106 ASSERT(data_length_ < buf_length_); | |
| 107 int max_read = buf_length_ - data_length_ - 1; | |
| 108 if (max_read == 0) { | |
| 109 // TODO(hausner): | |
| 110 // Buffer is full. What should we do if there is no valid message | |
| 111 // in the buffer? This might be possible if the client sends a message | |
| 112 // that's larger than the buffer, of if the client sends malformed | |
| 113 // messages that keep piling up. | |
| 114 ASSERT(IsValidMessage()); | |
| 115 return; | |
| 116 } | |
| 117 // TODO(hausner): Handle error conditions returned by Read. We may | |
| 118 // want to close the debugger connection if we get any errors. | |
| 119 int bytes_read = | |
| 120 DebuggerConnectionImpl::Receive(fd_, buf_ + data_length_, max_read); | |
| 121 if (bytes_read == 0) { | |
| 122 connection_is_alive_ = false; | |
| 123 return; | |
| 124 } | |
| 125 ASSERT(bytes_read > 0); | |
| 126 data_length_ += bytes_read; | |
| 127 ASSERT(data_length_ < buf_length_); | |
| 128 buf_[data_length_] = '\0'; | |
| 129 } | |
| 130 | |
| 131 | |
| 132 void MessageBuffer::PopMessage() { | |
| 133 dart::JSONReader msg_reader(buf_); | |
| 134 const char* end = msg_reader.EndOfObject(); | |
| 135 if (end != NULL) { | |
| 136 ASSERT(*end == '}'); | |
| 137 end++; | |
| 138 data_length_ = 0; | |
| 139 while (*end != '\0') { | |
| 140 buf_[data_length_] = *end++; | |
| 141 data_length_++; | |
| 142 } | |
| 143 buf_[data_length_] = '\0'; | |
| 144 ASSERT(data_length_ < buf_length_); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 | |
| 149 static bool IsValidJSON(const char* msg) { | |
| 150 dart::JSONReader r(msg); | |
| 151 return r.CheckMessage(); | |
| 152 } | |
| 153 | |
| 154 | |
| 155 DebuggerConnectionHandler::DebuggerConnectionHandler(intptr_t debug_fd) | |
| 156 : debug_fd_(debug_fd), msgbuf_(NULL) { | |
| 157 msgbuf_ = new MessageBuffer(debug_fd_); | |
| 158 } | |
| 159 | |
| 160 | |
| 161 DebuggerConnectionHandler::~DebuggerConnectionHandler() { | |
| 162 CloseDbgConnection(); | |
| 163 DebuggerConnectionHandler::RemoveDebuggerConnection(debug_fd_); | |
| 164 } | |
| 165 | |
| 166 | |
| 167 int DebuggerConnectionHandler::MessageId() { | |
| 168 ASSERT(msgbuf_ != NULL); | |
| 169 return msgbuf_->MessageId(); | |
| 170 } | |
| 171 | |
| 172 | |
| 173 void DebuggerConnectionHandler::HandleUnknownMsg() { | |
| 174 int msg_id = msgbuf_->MessageId(); | |
| 175 ASSERT(msg_id >= 0); | |
| 176 SendError(debug_fd_, msg_id, "unknown debugger command"); | |
| 177 } | |
| 178 | |
| 179 | |
| 180 typedef void (*CommandHandler)(DbgMessage* msg); | |
| 181 | |
| 182 struct JSONDebuggerCommand { | |
| 183 const char* cmd_string; | |
| 184 CommandHandler handler_function; | |
| 185 }; | |
| 186 | |
| 187 | |
| 188 void DebuggerConnectionHandler::HandleMessages() { | |
| 189 static JSONDebuggerCommand generic_debugger_commands[] = { | |
| 190 { "interrupt", HandleInterruptCmd }, | |
| 191 { "getIsolateIds", HandleIsolatesListCmd }, | |
| 192 { NULL, NULL } | |
| 193 }; | |
| 194 | |
| 195 for (;;) { | |
| 196 // Read a message. | |
| 197 while (!msgbuf_->IsValidMessage() && msgbuf_->Alive()) { | |
| 198 msgbuf_->ReadData(); | |
| 199 } | |
| 200 if (!msgbuf_->Alive()) { | |
| 201 if (trace_debug_protocol) { | |
| 202 Log::Print("Debugger is exiting HandleMessages loop.\n"); | |
| 203 } | |
| 204 return; | |
| 205 } | |
| 206 | |
| 207 if (trace_debug_protocol) { | |
| 208 dart::JSONReader r(msgbuf_->buf()); | |
| 209 const char* msg_end = r.EndOfObject(); | |
| 210 if (msg_end != NULL) { | |
| 211 intptr_t msg_len = msg_end - msgbuf_->buf(); | |
| 212 int print_len = ((msg_len > kMaxPrintMessageLen) | |
| 213 ? kMaxPrintMessageLen : msg_len); | |
| 214 Log::Print("[<<<] Receiving message from debug fd %" Pd ":\n%.*s%s\n", | |
| 215 debug_fd_, print_len, msgbuf_->buf(), | |
| 216 ((msg_len > print_len) ? "..." : "")); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 // Parse out the command portion from the message. | |
| 221 dart::JSONReader r(msgbuf_->buf()); | |
| 222 bool found = r.Seek("command"); | |
| 223 if (r.Error()) { | |
| 224 FATAL("Illegal JSON message received"); | |
| 225 } | |
| 226 if (!found) { | |
| 227 Log::Print("'command' not found in JSON message: '%s'\n", | |
| 228 msgbuf_->buf()); | |
| 229 msgbuf_->PopMessage(); | |
| 230 } | |
| 231 | |
| 232 // Check if this is a generic command (not isolate specific). | |
| 233 int i = 0; | |
| 234 bool is_handled = false; | |
| 235 while (generic_debugger_commands[i].cmd_string != NULL) { | |
| 236 if (r.IsStringLiteral(generic_debugger_commands[i].cmd_string)) { | |
| 237 DbgMessage* msg = new DbgMessage(i, | |
| 238 msgbuf_->buf(), | |
| 239 r.EndOfObject(), | |
| 240 debug_fd_); | |
| 241 (*generic_debugger_commands[i].handler_function)(msg); | |
| 242 is_handled = true; | |
| 243 msgbuf_->PopMessage(); | |
| 244 delete msg; | |
| 245 break; | |
| 246 } | |
| 247 i++; | |
| 248 } | |
| 249 if (!is_handled) { | |
| 250 // Check if this is an isolate specific command. | |
| 251 int32_t cmd_idx = DbgMsgQueueList::LookupIsolateCommand(r.ValueChars(), | |
| 252 r.ValueLen()); | |
| 253 if (cmd_idx != DbgMsgQueueList::kInvalidCommand) { | |
| 254 const char* start = msgbuf_->buf(); | |
| 255 const char* end = r.EndOfObject(); | |
| 256 // Get debug message queue corresponding to isolate. | |
| 257 MessageParser msg_parser(start, (end - start)); | |
| 258 Dart_IsolateId isolate_id = msg_parser.GetInt64Param("isolateId"); | |
| 259 if (!DbgMsgQueueList::AddIsolateMessage(isolate_id, | |
| 260 cmd_idx, | |
| 261 msgbuf_->buf(), | |
| 262 r.EndOfObject(), | |
| 263 debug_fd_)) { | |
| 264 SendError(debug_fd_, MessageId(), "Invalid isolate specified"); | |
| 265 } | |
| 266 msgbuf_->PopMessage(); | |
| 267 continue; | |
| 268 } | |
| 269 | |
| 270 // This is an unrecognized command, report error and move on to next. | |
| 271 Log::Print("unrecognized command received: '%s'\n", msgbuf_->buf()); | |
| 272 HandleUnknownMsg(); | |
| 273 msgbuf_->PopMessage(); | |
| 274 } | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 | |
| 279 void DebuggerConnectionHandler::SendError(intptr_t debug_fd, | |
| 280 int msg_id, | |
| 281 const char* err_msg) { | |
| 282 dart::TextBuffer msg(64); | |
| 283 msg.Printf("{\"id\": %d, \"error\": \"Error: ", msg_id); | |
| 284 msg.AddEscapedString(err_msg); | |
| 285 msg.Printf("\"}"); | |
| 286 SendMsg(debug_fd, &msg); | |
| 287 } | |
| 288 | |
| 289 | |
| 290 void DebuggerConnectionHandler::CloseDbgConnection() { | |
| 291 if (debug_fd_ >= 0) { | |
| 292 Socket::Close(debug_fd_); | |
| 293 } | |
| 294 if (msgbuf_ != NULL) { | |
| 295 delete msgbuf_; | |
| 296 msgbuf_ = NULL; | |
| 297 } | |
| 298 // TODO(hausner): Need to tell the VM debugger object to remove all | |
| 299 // breakpoints. | |
| 300 } | |
| 301 | |
| 302 | |
| 303 // The vm service relies on certain debugger functionality. | |
| 304 void DebuggerConnectionHandler::InitForVmService() { | |
| 305 MonitorLocker ml(handler_lock_); | |
| 306 DbgMsgQueueList::Initialize(); | |
| 307 } | |
| 308 | |
| 309 | |
| 310 int DebuggerConnectionHandler::StartHandler(const char* address, | |
| 311 int port_number) { | |
| 312 ASSERT(handler_lock_ != NULL); | |
| 313 MonitorLocker ml(handler_lock_); | |
| 314 if (IsListening()) { | |
| 315 // The debugger connection handler was already started. | |
| 316 return Socket::GetPort(listener_fd_); | |
| 317 } | |
| 318 | |
| 319 // First setup breakpoint, exception and delayed breakpoint handlers. | |
| 320 DbgMsgQueueList::Initialize(); | |
| 321 | |
| 322 // Initialize the socket implementation. | |
| 323 if (!Socket::Initialize()) { | |
| 324 FATAL("Failed initializing socket implementation."); | |
| 325 } | |
| 326 | |
| 327 // Now setup a listener socket and start a thread which will | |
| 328 // listen, accept connections from debuggers, read and handle/dispatch | |
| 329 // debugger commands received on these connections. | |
| 330 ASSERT(listener_fd_ == -1); | |
| 331 OSError *os_error; | |
| 332 AddressList<SocketAddress>* addresses = | |
| 333 Socket::LookupAddress(address, -1, &os_error); | |
| 334 RawAddr addr = addresses->GetAt(0)->addr(); | |
| 335 SocketAddress::SetAddrPort(&addr, port_number); | |
| 336 listener_fd_ = ServerSocket::CreateBindListen(addr, 1); | |
| 337 delete addresses; | |
| 338 if (listener_fd_ < 0) { | |
| 339 fprintf(stderr, "%s", "Could not initialize debug socket\n"); | |
| 340 fflush(stderr); | |
| 341 exit(255); | |
| 342 } | |
| 343 | |
| 344 port_number = Socket::GetPort(listener_fd_); | |
| 345 DebuggerConnectionImpl::StartHandler(port_number); | |
| 346 return port_number; | |
| 347 } | |
| 348 | |
| 349 | |
| 350 void DebuggerConnectionHandler::StopHandler() { | |
| 351 if (IsConnected()) { | |
| 352 DebuggerConnectionImpl::StopHandler(singleton_handler->debug_fd()); | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 | |
| 357 void DebuggerConnectionHandler::WaitForConnection() { | |
| 358 ASSERT(handler_lock_ != NULL); | |
| 359 MonitorLocker ml(handler_lock_); | |
| 360 if (!IsListening()) { | |
| 361 // If we are only running the vm service, don't wait for | |
| 362 // connections. | |
| 363 return; | |
| 364 } | |
| 365 while (!IsConnected()) { | |
| 366 Monitor::WaitResult res = ml.Wait(); | |
| 367 ASSERT(res == Monitor::kNotified); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 | |
| 372 void DebuggerConnectionHandler::SendMsg(intptr_t debug_fd, | |
| 373 dart::TextBuffer* msg) { | |
| 374 ASSERT(handler_lock_ != NULL); | |
| 375 MonitorLocker ml(handler_lock_); | |
| 376 SendMsgHelper(debug_fd, msg); | |
| 377 } | |
| 378 | |
| 379 | |
| 380 void DebuggerConnectionHandler::BroadcastMsg(dart::TextBuffer* msg) { | |
| 381 ASSERT(handler_lock_ != NULL); | |
| 382 MonitorLocker ml(handler_lock_); | |
| 383 if (!IsListening()) { | |
| 384 // If we are only running the vm service, don't try to broadcast | |
| 385 // to debugger clients. | |
| 386 return; | |
| 387 } | |
| 388 // TODO(asiva): Once we support connection to multiple debuggers | |
| 389 // we need to send the message to all of them. | |
| 390 ASSERT(singleton_handler != NULL); | |
| 391 SendMsgHelper(singleton_handler->debug_fd(), msg); | |
| 392 } | |
| 393 | |
| 394 | |
| 395 void DebuggerConnectionHandler::SendMsgHelper(intptr_t debug_fd, | |
| 396 dart::TextBuffer* msg) { | |
| 397 ASSERT(debug_fd >= 0); | |
| 398 ASSERT(IsValidJSON(msg->buf())); | |
| 399 | |
| 400 if (trace_debug_protocol) { | |
| 401 int print_len = ((msg->length() > kMaxPrintMessageLen) | |
| 402 ? kMaxPrintMessageLen : msg->length()); | |
| 403 Log::Print("[>>>] Sending message to debug fd %" Pd ":\n%.*s%s\n", | |
| 404 debug_fd, print_len, msg->buf(), | |
| 405 ((msg->length() > print_len) ? "..." : "")); | |
| 406 } | |
| 407 | |
| 408 // Sending messages in short pieces can be used to stress test the | |
| 409 // debugger front-end's message handling code. | |
| 410 const bool send_in_pieces = false; | |
| 411 if (send_in_pieces) { | |
| 412 intptr_t remaining = msg->length(); | |
| 413 intptr_t sent = 0; | |
| 414 const intptr_t max_piece_len = 122; // Pretty arbitrary, not a power of 2. | |
| 415 Monitor sleep; | |
| 416 while (remaining > 0) { | |
| 417 intptr_t piece_len = remaining; | |
| 418 if (piece_len > max_piece_len) { | |
| 419 piece_len = max_piece_len; | |
| 420 } | |
| 421 intptr_t written = | |
| 422 DebuggerConnectionImpl::Send(debug_fd, msg->buf() + sent, piece_len); | |
| 423 ASSERT(written == piece_len); | |
| 424 sent += written; | |
| 425 remaining -= written; | |
| 426 // Wait briefly so the OS does not coalesce message fragments. | |
| 427 { | |
| 428 MonitorLocker ml(&sleep); | |
| 429 ml.Wait(10); | |
| 430 } | |
| 431 } | |
| 432 return; | |
| 433 } | |
| 434 intptr_t bytes_written = | |
| 435 DebuggerConnectionImpl::Send(debug_fd, msg->buf(), msg->length()); | |
| 436 ASSERT(msg->length() == bytes_written); | |
| 437 // TODO(hausner): Error checking. Probably just shut down the debugger | |
| 438 // session if we there is an error while writing. | |
| 439 } | |
| 440 | |
| 441 | |
| 442 void DebuggerConnectionHandler::AcceptDbgConnection(intptr_t debug_fd) { | |
| 443 Socket::SetNoDelay(debug_fd, true); | |
| 444 AddNewDebuggerConnection(debug_fd); | |
| 445 { | |
| 446 ASSERT(handler_lock_ != NULL); | |
| 447 MonitorLocker ml(handler_lock_); | |
| 448 ml.NotifyAll(); | |
| 449 } | |
| 450 // TODO(asiva): Once we implement support for multiple connections | |
| 451 // we should have a different callback for wakeups on fds which | |
| 452 // are not the listener_fd_. | |
| 453 // In that callback we would lookup the handler object | |
| 454 // corresponding to that fd and invoke HandleMessages on it. | |
| 455 // For now we run that code here. | |
| 456 DebuggerConnectionHandler* handler = GetDebuggerConnectionHandler(debug_fd); | |
| 457 if (handler != NULL) { | |
| 458 handler->HandleMessages(); | |
| 459 delete handler; | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 | |
| 464 void DebuggerConnectionHandler::HandleInterruptCmd(DbgMessage* in_msg) { | |
| 465 MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); | |
| 466 int msg_id = msg_parser.MessageId(); | |
| 467 Dart_IsolateId isolate_id = msg_parser.GetInt64Param("isolateId"); | |
| 468 if (isolate_id == ILLEGAL_ISOLATE_ID || Dart_GetIsolate(isolate_id) == NULL) { | |
| 469 in_msg->SendErrorReply(msg_id, "Invalid isolate specified"); | |
| 470 return; | |
| 471 } | |
| 472 if (!DbgMsgQueueList::InterruptIsolate(isolate_id)) { | |
| 473 in_msg->SendErrorReply(msg_id, "Invalid isolate specified"); | |
| 474 return; | |
| 475 } | |
| 476 dart::TextBuffer msg(64); | |
| 477 msg.Printf("{ \"id\": %d }", msg_id); | |
| 478 in_msg->SendReply(&msg); | |
| 479 } | |
| 480 | |
| 481 | |
| 482 void DebuggerConnectionHandler::HandleIsolatesListCmd(DbgMessage* in_msg) { | |
| 483 MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); | |
| 484 int msg_id = msg_parser.MessageId(); | |
| 485 ASSERT(msg_id >= 0); | |
| 486 dart::TextBuffer msg(64); | |
| 487 msg.Printf("{ \"id\": %d, \"result\": { \"isolateIds\": [", msg_id); | |
| 488 DbgMsgQueueList::ListIsolateIds(&msg); | |
| 489 msg.Printf("]}}"); | |
| 490 in_msg->SendReply(&msg); | |
| 491 } | |
| 492 | |
| 493 | |
| 494 void DebuggerConnectionHandler::AddNewDebuggerConnection(intptr_t debug_fd) { | |
| 495 // TODO(asiva): Support multiple debugger connections, for now we just | |
| 496 // create one handler, store it in a static variable and use it. | |
| 497 ASSERT(singleton_handler == NULL); | |
| 498 singleton_handler = new DebuggerConnectionHandler(debug_fd); | |
| 499 } | |
| 500 | |
| 501 | |
| 502 void DebuggerConnectionHandler::RemoveDebuggerConnection(intptr_t debug_fd) { | |
| 503 // TODO(asiva): Support multiple debugger connections, for now we just | |
| 504 // set the static handler back to NULL. | |
| 505 ASSERT(singleton_handler != NULL); | |
| 506 singleton_handler = NULL; | |
| 507 } | |
| 508 | |
| 509 | |
| 510 DebuggerConnectionHandler* | |
| 511 DebuggerConnectionHandler::GetDebuggerConnectionHandler(intptr_t debug_fd) { | |
| 512 // TODO(asiva): Support multiple debugger connections, for now we just | |
| 513 // return the one static handler that was created. | |
| 514 ASSERT(singleton_handler != NULL); | |
| 515 return singleton_handler; | |
| 516 } | |
| 517 | |
| 518 | |
| 519 bool DebuggerConnectionHandler::IsConnected() { | |
| 520 // TODO(asiva): Support multiple debugger connections. | |
| 521 // Return true if a connection has been established. | |
| 522 return singleton_handler != NULL; | |
| 523 } | |
| 524 | |
| 525 } // namespace bin | |
| 526 } // namespace dart | |
| OLD | NEW |