| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "bin/dbg_connection.h" | 5 #include "bin/dbg_connection.h" |
| 6 #include "bin/dbg_message.h" |
| 6 #include "bin/dartutils.h" | 7 #include "bin/dartutils.h" |
| 7 #include "bin/socket.h" | 8 #include "bin/socket.h" |
| 8 #include "bin/thread.h" | 9 #include "bin/thread.h" |
| 9 #include "bin/utils.h" | 10 #include "bin/utils.h" |
| 10 | 11 |
| 11 #include "platform/globals.h" | 12 #include "platform/globals.h" |
| 12 #include "platform/json.h" | 13 #include "platform/json.h" |
| 13 #include "platform/thread.h" | 14 #include "platform/thread.h" |
| 14 #include "platform/utils.h" | 15 #include "platform/utils.h" |
| 15 | 16 |
| 16 #include "include/dart_api.h" | 17 #include "include/dart_api.h" |
| 17 | 18 |
| 18 | 19 |
| 19 int DebuggerConnectionHandler::listener_fd_ = -1; | 20 int DebuggerConnectionHandler::listener_fd_ = -1; |
| 20 int DebuggerConnectionHandler::debugger_fd_ = -1; | 21 dart::Monitor DebuggerConnectionHandler::handler_lock_; |
| 21 dart::Monitor DebuggerConnectionHandler::is_connected_; | |
| 22 MessageBuffer* DebuggerConnectionHandler::msgbuf_ = NULL; | |
| 23 | 22 |
| 24 bool DebuggerConnectionHandler::handler_started_ = false; | 23 // TODO(asiva): Remove this once we have support for multiple debugger |
| 25 bool DebuggerConnectionHandler::request_resume_ = false; | 24 // connections. For now we just store the single debugger connection |
| 26 | 25 // handler in a static variable. |
| 27 dart::TextBuffer DebuggerConnectionHandler::queued_messages_(64); | 26 static DebuggerConnectionHandler* singleton_handler = NULL; |
| 28 | |
| 29 | |
| 30 // TODO(hausner): Need better error handling. | |
| 31 #define ASSERT_NOT_ERROR(handle) \ | |
| 32 ASSERT(!Dart_IsError(handle)) | |
| 33 | |
| 34 #define RETURN_IF_ERROR(handle) \ | |
| 35 if (Dart_IsError(handle)) { \ | |
| 36 return Dart_GetError(handle); \ | |
| 37 } | |
| 38 | |
| 39 | |
| 40 typedef void (*CommandHandler)(const char* json_cmd); | |
| 41 | |
| 42 struct JSONDebuggerCommand { | |
| 43 const char* cmd_string; | |
| 44 CommandHandler handler_function; | |
| 45 }; | |
| 46 | 27 |
| 47 | 28 |
| 48 class MessageBuffer { | 29 class MessageBuffer { |
| 49 public: | 30 public: |
| 50 explicit MessageBuffer(int fd); | 31 explicit MessageBuffer(int fd); |
| 51 ~MessageBuffer(); | 32 ~MessageBuffer(); |
| 52 void ReadData(); | 33 void ReadData(); |
| 53 bool IsValidMessage() const; | 34 bool IsValidMessage() const; |
| 54 void PopMessage(); | 35 void PopMessage(); |
| 55 int MessageId() const; | 36 int MessageId() const; |
| 56 const char* Params() const; | |
| 57 intptr_t GetIntParam(const char* name) const; | |
| 58 intptr_t GetOptIntParam(const char* name, intptr_t default_val) const; | |
| 59 // GetStringParam mallocs the buffer that it returns. Caller must free. | |
| 60 char* GetStringParam(const char* name) const; | |
| 61 char* buf() const { return buf_; } | 37 char* buf() const { return buf_; } |
| 62 bool Alive() const { return connection_is_alive_; } | 38 bool Alive() const { return connection_is_alive_; } |
| 63 | 39 |
| 64 private: | 40 private: |
| 65 static const int kInitialBufferSize = 256; | 41 static const int kInitialBufferSize = 256; |
| 66 char* buf_; | 42 char* buf_; |
| 67 int buf_length_; | 43 int buf_length_; |
| 68 int fd_; | 44 int fd_; |
| 69 int data_length_; | 45 int data_length_; |
| 70 bool connection_is_alive_; | 46 bool connection_is_alive_; |
| 71 | 47 |
| 72 DISALLOW_COPY_AND_ASSIGN(MessageBuffer); | 48 DISALLOW_COPY_AND_ASSIGN(MessageBuffer); |
| 73 }; | 49 }; |
| 74 | 50 |
| 75 | 51 |
| 76 MessageBuffer::MessageBuffer(int fd) | 52 MessageBuffer::MessageBuffer(int fd) |
| 77 : buf_(NULL), | 53 : buf_(NULL), |
| 78 buf_length_(0), | 54 buf_length_(0), |
| 79 fd_(fd), | 55 fd_(fd), |
| 80 data_length_(0), | 56 data_length_(0), |
| 81 connection_is_alive_(true) { | 57 connection_is_alive_(true) { |
| 82 buf_ = reinterpret_cast<char*>(malloc(kInitialBufferSize)); | 58 buf_ = reinterpret_cast<char*>(malloc(kInitialBufferSize)); |
| 83 if (buf_ == NULL) { | 59 if (buf_ == NULL) { |
| 84 FATAL("Failed to allocate message buffer\n"); | 60 FATAL("Failed to allocate message buffer\n"); |
| 85 } | 61 } |
| 86 buf_length_ = kInitialBufferSize; | 62 buf_length_ = kInitialBufferSize; |
| 87 buf_[0] = '\0'; | 63 buf_[0] = '\0'; |
| 88 data_length_ = 0; | 64 data_length_ = 0; |
| 89 } | 65 } |
| 90 | 66 |
| 91 | 67 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 109 dart::JSONReader r(buf_); | 85 dart::JSONReader r(buf_); |
| 110 r.Seek("id"); | 86 r.Seek("id"); |
| 111 if (r.Type() == dart::JSONReader::kInteger) { | 87 if (r.Type() == dart::JSONReader::kInteger) { |
| 112 return atoi(r.ValueChars()); | 88 return atoi(r.ValueChars()); |
| 113 } else { | 89 } else { |
| 114 return -1; | 90 return -1; |
| 115 } | 91 } |
| 116 } | 92 } |
| 117 | 93 |
| 118 | 94 |
| 119 const char* MessageBuffer::Params() const { | |
| 120 dart::JSONReader r(buf_); | |
| 121 r.Seek("params"); | |
| 122 if (r.Type() == dart::JSONReader::kObject) { | |
| 123 return r.ValueChars(); | |
| 124 } else { | |
| 125 return NULL; | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 | |
| 130 intptr_t MessageBuffer::GetIntParam(const char* name) const { | |
| 131 const char* params = Params(); | |
| 132 ASSERT(params != NULL); | |
| 133 dart::JSONReader r(params); | |
| 134 r.Seek(name); | |
| 135 ASSERT(r.Type() == dart::JSONReader::kInteger); | |
| 136 return strtol(r.ValueChars(), NULL, 10); | |
| 137 } | |
| 138 | |
| 139 | |
| 140 intptr_t MessageBuffer::GetOptIntParam(const char* name, | |
| 141 intptr_t default_val) const { | |
| 142 const char* params = Params(); | |
| 143 ASSERT(params != NULL); | |
| 144 dart::JSONReader r(params); | |
| 145 r.Seek(name); | |
| 146 if (r.Type() == dart::JSONReader::kInteger) { | |
| 147 return strtol(r.ValueChars(), NULL, 10); | |
| 148 } else { | |
| 149 return default_val; | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 | |
| 154 char* MessageBuffer::GetStringParam(const char* name) const { | |
| 155 const char* params = Params(); | |
| 156 ASSERT(params != NULL); | |
| 157 dart::JSONReader pr(params); | |
| 158 pr.Seek(name); | |
| 159 if (pr.Type() != dart::JSONReader::kString) { | |
| 160 return NULL; | |
| 161 } | |
| 162 intptr_t buflen = pr.ValueLen() + 1; | |
| 163 char* param_chars = reinterpret_cast<char*>(malloc(buflen)); | |
| 164 pr.GetValueChars(param_chars, buflen); | |
| 165 // TODO(hausner): Decode escape sequences. | |
| 166 return param_chars; | |
| 167 } | |
| 168 | |
| 169 void MessageBuffer::ReadData() { | 95 void MessageBuffer::ReadData() { |
| 170 ASSERT(data_length_ >= 0); | 96 ASSERT(data_length_ >= 0); |
| 171 ASSERT(data_length_ < buf_length_); | 97 ASSERT(data_length_ < buf_length_); |
| 172 int max_read = buf_length_ - data_length_ - 1; | 98 int max_read = buf_length_ - data_length_ - 1; |
| 173 if (max_read == 0) { | 99 if (max_read == 0) { |
| 174 // TODO(hausner): | 100 // TODO(hausner): |
| 175 // Buffer is full. What should we do if there is no valid message | 101 // Buffer is full. What should we do if there is no valid message |
| 176 // in the buffer? This might be possible if the client sends a message | 102 // in the buffer? This might be possible if the client sends a message |
| 177 // that's larger than the buffer, of if the client sends malformed | 103 // that's larger than the buffer, of if the client sends malformed |
| 178 // messages that keep piling up. | 104 // messages that keep piling up. |
| (...skipping 30 matching lines...) Expand all Loading... |
| 209 } | 135 } |
| 210 } | 136 } |
| 211 | 137 |
| 212 | 138 |
| 213 static bool IsValidJSON(const char* msg) { | 139 static bool IsValidJSON(const char* msg) { |
| 214 dart::JSONReader r(msg); | 140 dart::JSONReader r(msg); |
| 215 return r.EndOfObject() != NULL; | 141 return r.EndOfObject() != NULL; |
| 216 } | 142 } |
| 217 | 143 |
| 218 | 144 |
| 219 void DebuggerConnectionHandler::SendMsg(dart::TextBuffer* msg) { | 145 DebuggerConnectionHandler::DebuggerConnectionHandler(int debug_fd) |
| 220 ASSERT(debugger_fd_ >= 0); | 146 : debug_fd_(debug_fd), msgbuf_(NULL) { |
| 221 ASSERT(IsValidJSON(msg->buf())); | 147 msgbuf_ = new MessageBuffer(debug_fd_); |
| 222 // Sending messages in short pieces can be used to stress test the | |
| 223 // debugger front-end's message handling code. | |
| 224 const bool send_in_pieces = false; | |
| 225 if (send_in_pieces) { | |
| 226 intptr_t remaining = msg->length(); | |
| 227 intptr_t sent = 0; | |
| 228 const intptr_t max_piece_len = 122; // Pretty arbitrary, not a power of 2. | |
| 229 dart::Monitor sleep; | |
| 230 while (remaining > 0) { | |
| 231 intptr_t piece_len = remaining; | |
| 232 if (piece_len > max_piece_len) { | |
| 233 piece_len = max_piece_len; | |
| 234 } | |
| 235 intptr_t written = | |
| 236 Socket::Write(debugger_fd_, msg->buf() + sent, piece_len); | |
| 237 ASSERT(written == piece_len); | |
| 238 sent += written; | |
| 239 remaining -= written; | |
| 240 // Wait briefly so the OS does not coalesce message fragments. | |
| 241 { | |
| 242 MonitorLocker ml(&sleep); | |
| 243 ml.Wait(10); | |
| 244 } | |
| 245 } | |
| 246 return; | |
| 247 } | |
| 248 intptr_t bytes_written = | |
| 249 Socket::Write(debugger_fd_, msg->buf(), msg->length()); | |
| 250 ASSERT(msg->length() == bytes_written); | |
| 251 // TODO(hausner): Error checking. Probably just shut down the debugger | |
| 252 // session if we there is an error while writing. | |
| 253 } | 148 } |
| 254 | 149 |
| 255 | 150 |
| 256 void DebuggerConnectionHandler::QueueMsg(dart::TextBuffer* msg) { | 151 DebuggerConnectionHandler::~DebuggerConnectionHandler() { |
| 257 queued_messages_.Printf("%s", msg->buf()); | 152 CloseDbgConnection(); |
| 153 DebuggerConnectionHandler::RemoveDebuggerConnection(debug_fd_); |
| 258 } | 154 } |
| 259 | 155 |
| 260 | 156 |
| 261 void DebuggerConnectionHandler::SendQueuedMsgs() { | 157 int DebuggerConnectionHandler::MessageId() { |
| 262 if (queued_messages_.length() > 0) { | 158 ASSERT(msgbuf_ != NULL); |
| 263 SendMsg(&queued_messages_); | 159 return msgbuf_->MessageId(); |
| 264 queued_messages_.Clear(); | |
| 265 } | |
| 266 } | 160 } |
| 267 | 161 |
| 268 | 162 |
| 269 void DebuggerConnectionHandler::SendError(int msg_id, | 163 void DebuggerConnectionHandler::HandleUnknownMsg() { |
| 270 const char* err_msg) { | 164 int msg_id = msgbuf_->MessageId(); |
| 271 dart::TextBuffer msg(64); | 165 ASSERT(msg_id >= 0); |
| 272 msg.Printf("{\"id\": %d, \"error\": \"Error: %s\"}", msg_id, err_msg); | 166 SendError(debug_fd_, msg_id, "unknown debugger command"); |
| 273 SendMsg(&msg); | |
| 274 } | 167 } |
| 275 | 168 |
| 276 | 169 |
| 277 static const char* GetStringChars(Dart_Handle str) { | 170 typedef void (*CommandHandler)(DebuggerConnectionHandler* handler); |
| 278 ASSERT(Dart_IsString(str)); | |
| 279 const char* chars; | |
| 280 Dart_Handle res = Dart_StringToCString(str, &chars); | |
| 281 ASSERT(!Dart_IsError(res)); | |
| 282 return chars; | |
| 283 } | |
| 284 | 171 |
| 285 | 172 struct JSONDebuggerCommand { |
| 286 static int GetIntValue(Dart_Handle int_handle) { | 173 const char* cmd_string; |
| 287 int64_t int64_val = -1; | 174 CommandHandler handler_function; |
| 288 ASSERT(Dart_IsInteger(int_handle)); | 175 }; |
| 289 Dart_Handle res = Dart_IntegerToInt64(int_handle, &int64_val); | |
| 290 ASSERT_NOT_ERROR(res); | |
| 291 // TODO(hausner): Range check. | |
| 292 return int64_val; | |
| 293 } | |
| 294 | |
| 295 | |
| 296 void DebuggerConnectionHandler::HandleResumeCmd(const char* json_msg) { | |
| 297 int msg_id = msgbuf_->MessageId(); | |
| 298 dart::TextBuffer msg(64); | |
| 299 msg.Printf("{ \"id\": %d }", msg_id); | |
| 300 SendMsg(&msg); | |
| 301 request_resume_ = true; | |
| 302 } | |
| 303 | |
| 304 | |
| 305 void DebuggerConnectionHandler::HandleStepIntoCmd(const char* json_msg) { | |
| 306 Dart_Handle res = Dart_SetStepInto(); | |
| 307 ASSERT_NOT_ERROR(res); | |
| 308 HandleResumeCmd(json_msg); | |
| 309 } | |
| 310 | |
| 311 | |
| 312 void DebuggerConnectionHandler::HandleStepOutCmd(const char* json_msg) { | |
| 313 Dart_Handle res = Dart_SetStepOut(); | |
| 314 ASSERT_NOT_ERROR(res); | |
| 315 HandleResumeCmd(json_msg); | |
| 316 } | |
| 317 | |
| 318 | |
| 319 void DebuggerConnectionHandler::HandleStepOverCmd(const char* json_msg) { | |
| 320 Dart_Handle res = Dart_SetStepOver(); | |
| 321 ASSERT_NOT_ERROR(res); | |
| 322 HandleResumeCmd(json_msg); | |
| 323 } | |
| 324 | |
| 325 | |
| 326 static void FormatEncodedString(dart::TextBuffer* buf, Dart_Handle str) { | |
| 327 intptr_t str_len = 0; | |
| 328 Dart_Handle res = Dart_StringLength(str, &str_len); | |
| 329 ASSERT_NOT_ERROR(res); | |
| 330 uint32_t* codepoints = | |
| 331 reinterpret_cast<uint32_t*>(malloc(str_len * sizeof(uint32_t))); | |
| 332 ASSERT(codepoints != NULL); | |
| 333 intptr_t actual_len = str_len; | |
| 334 res = Dart_StringGet32(str, codepoints, &actual_len); | |
| 335 ASSERT_NOT_ERROR(res); | |
| 336 ASSERT(str_len == actual_len); | |
| 337 buf->AddChar('\"'); | |
| 338 for (int i = 0; i < str_len; i++) { | |
| 339 buf->AddEscapedChar(codepoints[i]); | |
| 340 } | |
| 341 buf->AddChar('\"'); | |
| 342 free(codepoints); | |
| 343 } | |
| 344 | |
| 345 | |
| 346 static void FormatErrorMsg(dart::TextBuffer* buf, Dart_Handle err) { | |
| 347 // TODO(hausner): Turn message into Dart string and | |
| 348 // properly encode the message. | |
| 349 ASSERT(Dart_IsError(err)); | |
| 350 const char* msg = Dart_GetError(err); | |
| 351 buf->Printf("\"%s\"", msg); | |
| 352 } | |
| 353 | |
| 354 | |
| 355 void DebuggerConnectionHandler::HandleGetScriptURLsCmd(const char* json_msg) { | |
| 356 int msg_id = msgbuf_->MessageId(); | |
| 357 dart::TextBuffer msg(64); | |
| 358 intptr_t lib_id = msgbuf_->GetIntParam("libraryId"); | |
| 359 Dart_Handle lib_url = Dart_GetLibraryURL(lib_id); | |
| 360 ASSERT_NOT_ERROR(lib_url); | |
| 361 Dart_Handle urls = Dart_GetScriptURLs(lib_url); | |
| 362 if (Dart_IsError(urls)) { | |
| 363 SendError(msg_id, Dart_GetError(urls)); | |
| 364 return; | |
| 365 } | |
| 366 ASSERT(Dart_IsList(urls)); | |
| 367 intptr_t num_urls = 0; | |
| 368 Dart_ListLength(urls, &num_urls); | |
| 369 msg.Printf("{ \"id\": %d, ", msg_id); | |
| 370 msg.Printf("\"result\": { \"urls\": ["); | |
| 371 for (int i = 0; i < num_urls; i++) { | |
| 372 Dart_Handle script_url = Dart_ListGetAt(urls, i); | |
| 373 if (i > 0) { | |
| 374 msg.Printf(","); | |
| 375 } | |
| 376 FormatEncodedString(&msg, script_url); | |
| 377 } | |
| 378 msg.Printf("]}}"); | |
| 379 SendMsg(&msg); | |
| 380 } | |
| 381 | |
| 382 | |
| 383 void DebuggerConnectionHandler::HandleGetSourceCmd(const char* json_msg) { | |
| 384 int msg_id = msgbuf_->MessageId(); | |
| 385 dart::TextBuffer msg(64); | |
| 386 intptr_t lib_id = msgbuf_->GetIntParam("libraryId"); | |
| 387 char* url_chars = msgbuf_->GetStringParam("url"); | |
| 388 ASSERT(url_chars != NULL); | |
| 389 Dart_Handle url = Dart_NewString(url_chars); | |
| 390 ASSERT_NOT_ERROR(url); | |
| 391 free(url_chars); | |
| 392 url_chars = NULL; | |
| 393 Dart_Handle source = Dart_ScriptGetSource(lib_id, url); | |
| 394 if (Dart_IsError(source)) { | |
| 395 SendError(msg_id, Dart_GetError(source)); | |
| 396 return; | |
| 397 } | |
| 398 msg.Printf("{ \"id\": %d, ", msg_id); | |
| 399 msg.Printf("\"result\": { \"text\": "); | |
| 400 FormatEncodedString(&msg, source); | |
| 401 msg.Printf("}}"); | |
| 402 SendMsg(&msg); | |
| 403 } | |
| 404 | |
| 405 | |
| 406 void DebuggerConnectionHandler::HandleGetLibrariesCmd(const char* json_msg) { | |
| 407 int msg_id = msgbuf_->MessageId(); | |
| 408 dart::TextBuffer msg(64); | |
| 409 msg.Printf("{ \"id\": %d, \"result\": { \"libraries\": [", msg_id); | |
| 410 Dart_Handle lib_ids = Dart_GetLibraryIds(); | |
| 411 ASSERT_NOT_ERROR(lib_ids); | |
| 412 intptr_t num_libs; | |
| 413 Dart_Handle res = Dart_ListLength(lib_ids, &num_libs); | |
| 414 ASSERT_NOT_ERROR(res); | |
| 415 for (int i = 0; i < num_libs; i++) { | |
| 416 Dart_Handle lib_id_handle = Dart_ListGetAt(lib_ids, i); | |
| 417 ASSERT(Dart_IsInteger(lib_id_handle)); | |
| 418 int lib_id = GetIntValue(lib_id_handle); | |
| 419 Dart_Handle lib_url = Dart_GetLibraryURL(lib_id); | |
| 420 ASSERT_NOT_ERROR(lib_url); | |
| 421 ASSERT(Dart_IsString(lib_url)); | |
| 422 msg.Printf("%s{\"id\":%d,\"url\":", (i == 0) ? "" : ", ", lib_id); | |
| 423 FormatEncodedString(&msg, lib_url); | |
| 424 msg.Printf("}"); | |
| 425 } | |
| 426 msg.Printf("]}}"); | |
| 427 SendMsg(&msg); | |
| 428 } | |
| 429 | |
| 430 | |
| 431 static void FormatTextualValue(dart::TextBuffer* buf, Dart_Handle object) { | |
| 432 Dart_Handle text; | |
| 433 if (Dart_IsNull(object)) { | |
| 434 text = Dart_Null(); | |
| 435 } else { | |
| 436 Dart_ExceptionPauseInfo savedState = Dart_GetExceptionPauseInfo(); | |
| 437 | |
| 438 // TODO(hausner): Check whether recursive/reentrant pauses on exceptions | |
| 439 // should be prevented in Debugger::SignalExceptionThrown() instead. | |
| 440 if (savedState != kNoPauseOnExceptions) { | |
| 441 Dart_Handle res = Dart_SetExceptionPauseInfo(kNoPauseOnExceptions); | |
| 442 ASSERT_NOT_ERROR(res); | |
| 443 } | |
| 444 | |
| 445 text = Dart_ToString(object); | |
| 446 | |
| 447 if (savedState != kNoPauseOnExceptions) { | |
| 448 Dart_Handle res = Dart_SetExceptionPauseInfo(savedState); | |
| 449 ASSERT_NOT_ERROR(res); | |
| 450 } | |
| 451 } | |
| 452 buf->Printf("\"text\":"); | |
| 453 if (Dart_IsNull(text)) { | |
| 454 buf->Printf("null"); | |
| 455 } else if (Dart_IsError(text)) { | |
| 456 FormatErrorMsg(buf, text); | |
| 457 } else { | |
| 458 FormatEncodedString(buf, text); | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 | |
| 463 static void FormatValue(dart::TextBuffer* buf, Dart_Handle object) { | |
| 464 if (Dart_IsInteger(object)) { | |
| 465 buf->Printf("\"kind\":\"integer\","); | |
| 466 } else if (Dart_IsString(object)) { | |
| 467 buf->Printf("\"kind\":\"string\","); | |
| 468 } else if (Dart_IsBoolean(object)) { | |
| 469 buf->Printf("\"kind\":\"boolean\","); | |
| 470 } else if (Dart_IsList(object)) { | |
| 471 intptr_t len = 0; | |
| 472 Dart_Handle res = Dart_ListLength(object, &len); | |
| 473 ASSERT_NOT_ERROR(res); | |
| 474 buf->Printf("\"kind\":\"list\",\"length\":%"Pd",", len); | |
| 475 } else { | |
| 476 buf->Printf("\"kind\":\"object\","); | |
| 477 } | |
| 478 FormatTextualValue(buf, object); | |
| 479 } | |
| 480 | |
| 481 | |
| 482 static void FormatValueObj(dart::TextBuffer* buf, Dart_Handle object) { | |
| 483 buf->Printf("{"); | |
| 484 FormatValue(buf, object); | |
| 485 buf->Printf("}"); | |
| 486 } | |
| 487 | |
| 488 | |
| 489 static void FormatRemoteObj(dart::TextBuffer* buf, Dart_Handle object) { | |
| 490 intptr_t obj_id = Dart_CacheObject(object); | |
| 491 ASSERT(obj_id >= 0); | |
| 492 buf->Printf("{\"objectId\":%"Pd",", obj_id); | |
| 493 FormatValue(buf, object); | |
| 494 buf->Printf("}"); | |
| 495 } | |
| 496 | |
| 497 | |
| 498 static void FormatNamedValue(dart::TextBuffer* buf, | |
| 499 Dart_Handle object_name, | |
| 500 Dart_Handle object) { | |
| 501 ASSERT(Dart_IsString(object_name)); | |
| 502 buf->Printf("{\"name\":\"%s\",", GetStringChars(object_name)); | |
| 503 buf->Printf("\"value\":"); | |
| 504 FormatRemoteObj(buf, object); | |
| 505 buf->Printf("}"); | |
| 506 } | |
| 507 | |
| 508 | |
| 509 static void FormatNamedValueList(dart::TextBuffer* buf, | |
| 510 Dart_Handle obj_list) { | |
| 511 ASSERT(Dart_IsList(obj_list)); | |
| 512 intptr_t list_length = 0; | |
| 513 Dart_Handle res = Dart_ListLength(obj_list, &list_length); | |
| 514 ASSERT_NOT_ERROR(res); | |
| 515 ASSERT(list_length % 2 == 0); | |
| 516 buf->Printf("["); | |
| 517 for (int i = 0; i + 1 < list_length; i += 2) { | |
| 518 Dart_Handle name_handle = Dart_ListGetAt(obj_list, i); | |
| 519 ASSERT_NOT_ERROR(name_handle); | |
| 520 Dart_Handle value_handle = Dart_ListGetAt(obj_list, i + 1); | |
| 521 ASSERT_NOT_ERROR(value_handle); | |
| 522 if (i > 0) { | |
| 523 buf->Printf(","); | |
| 524 } | |
| 525 FormatNamedValue(buf, name_handle, value_handle); | |
| 526 } | |
| 527 buf->Printf("]"); | |
| 528 } | |
| 529 | |
| 530 | |
| 531 static const char* FormatClassProps(dart::TextBuffer* buf, | |
| 532 intptr_t cls_id) { | |
| 533 Dart_Handle name, static_fields; | |
| 534 intptr_t super_id = -1; | |
| 535 intptr_t library_id = -1; | |
| 536 Dart_Handle res = | |
| 537 Dart_GetClassInfo(cls_id, &name, &library_id, &super_id, &static_fields); | |
| 538 RETURN_IF_ERROR(res); | |
| 539 RETURN_IF_ERROR(name); | |
| 540 buf->Printf("{\"name\":\"%s\",", GetStringChars(name)); | |
| 541 if (super_id > 0) { | |
| 542 buf->Printf("\"superclassId\":%"Pd",", super_id); | |
| 543 } | |
| 544 buf->Printf("\"libraryId\":%"Pd",", library_id); | |
| 545 RETURN_IF_ERROR(static_fields); | |
| 546 buf->Printf("\"fields\":"); | |
| 547 FormatNamedValueList(buf, static_fields); | |
| 548 buf->Printf("}"); | |
| 549 return NULL; | |
| 550 } | |
| 551 | |
| 552 | |
| 553 static const char* FormatLibraryProps(dart::TextBuffer* buf, | |
| 554 intptr_t lib_id) { | |
| 555 Dart_Handle url = Dart_GetLibraryURL(lib_id); | |
| 556 RETURN_IF_ERROR(url); | |
| 557 buf->Printf("{\"url\":"); | |
| 558 FormatEncodedString(buf, url); | |
| 559 | |
| 560 // Whether debugging is enabled. | |
| 561 bool is_debuggable = false; | |
| 562 Dart_Handle res = Dart_GetLibraryDebuggable(lib_id, &is_debuggable); | |
| 563 RETURN_IF_ERROR(res); | |
| 564 buf->Printf(",\"debuggingEnabled\":%s", | |
| 565 is_debuggable ? "\"true\"" : "\"false\""); | |
| 566 | |
| 567 // Imports and prefixes. | |
| 568 Dart_Handle import_list = Dart_GetLibraryImports(lib_id); | |
| 569 RETURN_IF_ERROR(import_list); | |
| 570 ASSERT(Dart_IsList(import_list)); | |
| 571 intptr_t list_length = 0; | |
| 572 res = Dart_ListLength(import_list, &list_length); | |
| 573 RETURN_IF_ERROR(res); | |
| 574 buf->Printf(",\"imports\":["); | |
| 575 for (int i = 0; i + 1 < list_length; i += 2) { | |
| 576 Dart_Handle lib_id = Dart_ListGetAt(import_list, i + 1); | |
| 577 ASSERT_NOT_ERROR(lib_id); | |
| 578 buf->Printf("%s{\"libraryId\":%d,", | |
| 579 (i > 0) ? ",": "", | |
| 580 GetIntValue(lib_id)); | |
| 581 | |
| 582 Dart_Handle name = Dart_ListGetAt(import_list, i); | |
| 583 ASSERT_NOT_ERROR(name); | |
| 584 buf->Printf("\"prefix\":\"%s\"}", | |
| 585 Dart_IsNull(name) ? "" : GetStringChars(name)); | |
| 586 } | |
| 587 buf->Printf("],"); | |
| 588 | |
| 589 // Global variables in the library. | |
| 590 Dart_Handle global_vars = Dart_GetLibraryFields(lib_id); | |
| 591 RETURN_IF_ERROR(global_vars); | |
| 592 buf->Printf("\"globals\":"); | |
| 593 FormatNamedValueList(buf, global_vars); | |
| 594 buf->Printf("}"); | |
| 595 return NULL; | |
| 596 } | |
| 597 | |
| 598 | |
| 599 static const char* FormatObjProps(dart::TextBuffer* buf, | |
| 600 Dart_Handle object) { | |
| 601 intptr_t class_id; | |
| 602 if (Dart_IsNull(object)) { | |
| 603 buf->Printf("{\"classId\":-1,\"fields\":[]}"); | |
| 604 return NULL; | |
| 605 } | |
| 606 Dart_Handle res = Dart_GetObjClassId(object, &class_id); | |
| 607 RETURN_IF_ERROR(res); | |
| 608 buf->Printf("{\"classId\": %"Pd",", class_id); | |
| 609 buf->Printf("\"kind\":\"object\",\"fields\":"); | |
| 610 Dart_Handle fields = Dart_GetInstanceFields(object); | |
| 611 RETURN_IF_ERROR(fields); | |
| 612 FormatNamedValueList(buf, fields); | |
| 613 buf->Printf("}"); | |
| 614 return NULL; | |
| 615 } | |
| 616 | |
| 617 | |
| 618 static const char* FormatListSlice(dart::TextBuffer* buf, | |
| 619 Dart_Handle list, | |
| 620 intptr_t list_length, | |
| 621 intptr_t index, | |
| 622 intptr_t slice_length) { | |
| 623 intptr_t end_index = index + slice_length; | |
| 624 ASSERT(end_index <= list_length); | |
| 625 buf->Printf("{\"index\":%"Pd",", index); | |
| 626 buf->Printf("\"length\":%"Pd",", slice_length); | |
| 627 buf->Printf("\"elements\":["); | |
| 628 for (intptr_t i = index; i < end_index; i++) { | |
| 629 Dart_Handle value = Dart_ListGetAt(list, i); | |
| 630 if (i > index) { | |
| 631 buf->Printf(","); | |
| 632 } | |
| 633 FormatValueObj(buf, value); | |
| 634 } | |
| 635 buf->Printf("]}"); | |
| 636 return NULL; | |
| 637 } | |
| 638 | |
| 639 | |
| 640 static void FormatCallFrames(dart::TextBuffer* msg, Dart_StackTrace trace) { | |
| 641 intptr_t trace_len = 0; | |
| 642 Dart_Handle res = Dart_StackTraceLength(trace, &trace_len); | |
| 643 ASSERT_NOT_ERROR(res); | |
| 644 msg->Printf("\"callFrames\" : [ "); | |
| 645 for (int i = 0; i < trace_len; i++) { | |
| 646 Dart_ActivationFrame frame; | |
| 647 res = Dart_GetActivationFrame(trace, i, &frame); | |
| 648 ASSERT_NOT_ERROR(res); | |
| 649 Dart_Handle func_name; | |
| 650 Dart_Handle script_url; | |
| 651 intptr_t line_number = 0; | |
| 652 intptr_t library_id = 0; | |
| 653 res = Dart_ActivationFrameInfo( | |
| 654 frame, &func_name, &script_url, &line_number, &library_id); | |
| 655 ASSERT_NOT_ERROR(res); | |
| 656 ASSERT(Dart_IsString(func_name)); | |
| 657 msg->Printf("%s{\"functionName\":", (i > 0) ? "," : ""); | |
| 658 FormatEncodedString(msg, func_name); | |
| 659 msg->Printf(",\"libraryId\": %"Pd",", library_id); | |
| 660 | |
| 661 ASSERT(Dart_IsString(script_url)); | |
| 662 msg->Printf("\"location\": { \"url\":"); | |
| 663 FormatEncodedString(msg, script_url); | |
| 664 msg->Printf(",\"lineNumber\":%"Pd"},", line_number); | |
| 665 | |
| 666 Dart_Handle locals = Dart_GetLocalVariables(frame); | |
| 667 ASSERT_NOT_ERROR(locals); | |
| 668 msg->Printf("\"locals\":"); | |
| 669 FormatNamedValueList(msg, locals); | |
| 670 msg->Printf("}"); | |
| 671 } | |
| 672 msg->Printf("]"); | |
| 673 } | |
| 674 | |
| 675 | |
| 676 void DebuggerConnectionHandler::HandleGetStackTraceCmd(const char* json_msg) { | |
| 677 int msg_id = msgbuf_->MessageId(); | |
| 678 Dart_StackTrace trace; | |
| 679 Dart_Handle res = Dart_GetStackTrace(&trace); | |
| 680 ASSERT_NOT_ERROR(res); | |
| 681 dart::TextBuffer msg(128); | |
| 682 msg.Printf("{ \"id\": %d, \"result\": {", msg_id); | |
| 683 FormatCallFrames(&msg, trace); | |
| 684 msg.Printf("}}"); | |
| 685 SendMsg(&msg); | |
| 686 } | |
| 687 | |
| 688 | |
| 689 void DebuggerConnectionHandler::HandleSetBpCmd(const char* json_msg) { | |
| 690 int msg_id = msgbuf_->MessageId(); | |
| 691 char* url_chars = msgbuf_->GetStringParam("url"); | |
| 692 ASSERT(url_chars != NULL); | |
| 693 Dart_Handle url = Dart_NewString(url_chars); | |
| 694 ASSERT_NOT_ERROR(url); | |
| 695 free(url_chars); | |
| 696 url_chars = NULL; | |
| 697 intptr_t line_number = msgbuf_->GetIntParam("line"); | |
| 698 Dart_Handle bp_id = Dart_SetBreakpoint(url, line_number); | |
| 699 if (Dart_IsError(bp_id)) { | |
| 700 SendError(msg_id, Dart_GetError(bp_id)); | |
| 701 return; | |
| 702 } | |
| 703 ASSERT(Dart_IsInteger(bp_id)); | |
| 704 uint64_t bp_id_value; | |
| 705 Dart_Handle res = Dart_IntegerToUint64(bp_id, &bp_id_value); | |
| 706 ASSERT_NOT_ERROR(res); | |
| 707 dart::TextBuffer msg(64); | |
| 708 msg.Printf("{ \"id\": %d, \"result\": { \"breakpointId\": %"Pu64" }}", | |
| 709 msg_id, bp_id_value); | |
| 710 SendMsg(&msg); | |
| 711 } | |
| 712 | |
| 713 | |
| 714 void DebuggerConnectionHandler::HandlePauseOnExcCmd(const char* json_msg) { | |
| 715 int msg_id = msgbuf_->MessageId(); | |
| 716 char* exc_chars = msgbuf_->GetStringParam("exceptions"); | |
| 717 Dart_ExceptionPauseInfo info = kNoPauseOnExceptions; | |
| 718 if (strcmp(exc_chars, "none") == 0) { | |
| 719 info = kNoPauseOnExceptions; | |
| 720 } else if (strcmp(exc_chars, "all") == 0) { | |
| 721 info = kPauseOnAllExceptions; | |
| 722 } else if (strcmp(exc_chars, "unhandled") == 0) { | |
| 723 info = kPauseOnUnhandledExceptions; | |
| 724 } else { | |
| 725 SendError(msg_id, "illegal value for parameter 'exceptions'"); | |
| 726 return; | |
| 727 } | |
| 728 Dart_Handle res = Dart_SetExceptionPauseInfo(info); | |
| 729 ASSERT_NOT_ERROR(res); | |
| 730 dart::TextBuffer msg(32); | |
| 731 msg.Printf("{ \"id\": %d }", msg_id); | |
| 732 SendMsg(&msg); | |
| 733 } | |
| 734 | |
| 735 | |
| 736 void DebuggerConnectionHandler::HandleRemBpCmd(const char* json_msg) { | |
| 737 int msg_id = msgbuf_->MessageId(); | |
| 738 int bpt_id = msgbuf_->GetIntParam("breakpointId"); | |
| 739 Dart_Handle res = Dart_RemoveBreakpoint(bpt_id); | |
| 740 if (Dart_IsError(res)) { | |
| 741 SendError(msg_id, Dart_GetError(res)); | |
| 742 return; | |
| 743 } | |
| 744 dart::TextBuffer msg(32); | |
| 745 msg.Printf("{ \"id\": %d }", msg_id); | |
| 746 SendMsg(&msg); | |
| 747 } | |
| 748 | |
| 749 | |
| 750 void DebuggerConnectionHandler::HandleGetObjPropsCmd(const char* json_msg) { | |
| 751 int msg_id = msgbuf_->MessageId(); | |
| 752 intptr_t obj_id = msgbuf_->GetIntParam("objectId"); | |
| 753 Dart_Handle obj = Dart_GetCachedObject(obj_id); | |
| 754 if (Dart_IsError(obj)) { | |
| 755 SendError(msg_id, Dart_GetError(obj)); | |
| 756 return; | |
| 757 } | |
| 758 dart::TextBuffer msg(64); | |
| 759 msg.Printf("{\"id\":%d, \"result\":", msg_id); | |
| 760 const char* err = FormatObjProps(&msg, obj); | |
| 761 if (err != NULL) { | |
| 762 SendError(msg_id, err); | |
| 763 return; | |
| 764 } | |
| 765 msg.Printf("}"); | |
| 766 SendMsg(&msg); | |
| 767 } | |
| 768 | |
| 769 | |
| 770 void DebuggerConnectionHandler::HandleGetListCmd(const char* json_msg) { | |
| 771 const intptr_t kDefaultSliceLength = 100; | |
| 772 int msg_id = msgbuf_->MessageId(); | |
| 773 intptr_t obj_id = msgbuf_->GetIntParam("objectId"); | |
| 774 Dart_Handle list = Dart_GetCachedObject(obj_id); | |
| 775 if (Dart_IsError(list)) { | |
| 776 SendError(msg_id, Dart_GetError(list)); | |
| 777 return; | |
| 778 } | |
| 779 if (!Dart_IsList(list)) { | |
| 780 SendError(msg_id, "object is not a list"); | |
| 781 return; | |
| 782 } | |
| 783 intptr_t list_length = 0; | |
| 784 Dart_Handle res = Dart_ListLength(list, &list_length); | |
| 785 if (Dart_IsError(res)) { | |
| 786 SendError(msg_id, Dart_GetError(res)); | |
| 787 return; | |
| 788 } | |
| 789 | |
| 790 intptr_t index = msgbuf_->GetIntParam("index"); | |
| 791 if (index < 0) { | |
| 792 index = 0; | |
| 793 } else if (index > list_length) { | |
| 794 index = list_length; | |
| 795 } | |
| 796 | |
| 797 // If no slice length is given, get only one element. If slice length | |
| 798 // is given as 0, get entire list. | |
| 799 intptr_t slice_length = msgbuf_->GetOptIntParam("length", 1); | |
| 800 if (slice_length == 0) { | |
| 801 slice_length = list_length - index; | |
| 802 } | |
| 803 if ((index + slice_length) > list_length) { | |
| 804 slice_length = list_length - index; | |
| 805 } | |
| 806 ASSERT(slice_length >= 0); | |
| 807 if (slice_length > kDefaultSliceLength) { | |
| 808 slice_length = kDefaultSliceLength; | |
| 809 } | |
| 810 dart::TextBuffer msg(64); | |
| 811 msg.Printf("{\"id\":%d, \"result\":", msg_id); | |
| 812 if (slice_length == 1) { | |
| 813 Dart_Handle value = Dart_ListGetAt(list, index); | |
| 814 FormatRemoteObj(&msg, value); | |
| 815 } else { | |
| 816 FormatListSlice(&msg, list, list_length, index, slice_length); | |
| 817 } | |
| 818 msg.Printf("}"); | |
| 819 SendMsg(&msg); | |
| 820 } | |
| 821 | |
| 822 | |
| 823 void DebuggerConnectionHandler::HandleGetClassPropsCmd(const char* json_msg) { | |
| 824 int msg_id = msgbuf_->MessageId(); | |
| 825 intptr_t cls_id = msgbuf_->GetIntParam("classId"); | |
| 826 dart::TextBuffer msg(64); | |
| 827 msg.Printf("{\"id\":%d, \"result\":", msg_id); | |
| 828 const char* err = FormatClassProps(&msg, cls_id); | |
| 829 if (err != NULL) { | |
| 830 SendError(msg_id, err); | |
| 831 return; | |
| 832 } | |
| 833 msg.Printf("}"); | |
| 834 SendMsg(&msg); | |
| 835 } | |
| 836 | |
| 837 | |
| 838 void DebuggerConnectionHandler::HandleGetLibPropsCmd(const char* json_msg) { | |
| 839 int msg_id = msgbuf_->MessageId(); | |
| 840 intptr_t lib_id = msgbuf_->GetIntParam("libraryId"); | |
| 841 dart::TextBuffer msg(64); | |
| 842 msg.Printf("{\"id\":%d, \"result\":", msg_id); | |
| 843 const char* err = FormatLibraryProps(&msg, lib_id); | |
| 844 if (err != NULL) { | |
| 845 SendError(msg_id, err); | |
| 846 return; | |
| 847 } | |
| 848 msg.Printf("}"); | |
| 849 SendMsg(&msg); | |
| 850 } | |
| 851 | |
| 852 | |
| 853 void DebuggerConnectionHandler::HandleSetLibPropsCmd(const char* json_msg) { | |
| 854 int msg_id = msgbuf_->MessageId(); | |
| 855 intptr_t lib_id = msgbuf_->GetIntParam("libraryId"); | |
| 856 const char* enable_request = msgbuf_->GetStringParam("debuggingEnabled"); | |
| 857 bool enable; | |
| 858 if (strcmp(enable_request, "true") == 0) { | |
| 859 enable = true; | |
| 860 } else if (strcmp(enable_request, "false") == 0) { | |
| 861 enable = false; | |
| 862 } else { | |
| 863 SendError(msg_id, "illegal argument for 'debuggingEnabled'"); | |
| 864 return; | |
| 865 } | |
| 866 Dart_Handle res = Dart_SetLibraryDebuggable(lib_id, enable); | |
| 867 if (Dart_IsError(res)) { | |
| 868 SendError(msg_id, Dart_GetError(res)); | |
| 869 return; | |
| 870 } | |
| 871 bool enabled = false; | |
| 872 res = Dart_GetLibraryDebuggable(lib_id, &enabled); | |
| 873 if (Dart_IsError(res)) { | |
| 874 SendError(msg_id, Dart_GetError(res)); | |
| 875 return; | |
| 876 } | |
| 877 dart::TextBuffer msg(64); | |
| 878 msg.Printf("{\"id\":%d, \"result\": {\"debuggingEnabled\": \"%s\"}}", | |
| 879 msg_id, | |
| 880 enabled ? "true" : "false"); | |
| 881 SendMsg(&msg); | |
| 882 } | |
| 883 | |
| 884 | |
| 885 void DebuggerConnectionHandler::HandleGetGlobalsCmd(const char* json_msg) { | |
| 886 int msg_id = msgbuf_->MessageId(); | |
| 887 intptr_t lib_id = msgbuf_->GetIntParam("libraryId"); | |
| 888 dart::TextBuffer msg(64); | |
| 889 msg.Printf("{\"id\":%d, \"result\": { \"globals\":", msg_id); | |
| 890 Dart_Handle globals = Dart_GetGlobalVariables(lib_id); | |
| 891 ASSERT_NOT_ERROR(globals); | |
| 892 FormatNamedValueList(&msg, globals); | |
| 893 msg.Printf("}}"); | |
| 894 SendMsg(&msg); | |
| 895 } | |
| 896 | |
| 897 | |
| 898 void DebuggerConnectionHandler::HandleUnknownMsg(const char* json_msg) { | |
| 899 int msg_id = msgbuf_->MessageId(); | |
| 900 ASSERT(msg_id >= 0); | |
| 901 SendError(msg_id, "unknown debugger command"); | |
| 902 } | |
| 903 | 176 |
| 904 | 177 |
| 905 void DebuggerConnectionHandler::HandleMessages() { | 178 void DebuggerConnectionHandler::HandleMessages() { |
| 906 static JSONDebuggerCommand debugger_commands[] = { | 179 static JSONDebuggerCommand generic_debugger_commands[] = { |
| 907 { "resume", HandleResumeCmd }, | 180 { "interrupt", HandleInterruptCmd }, |
| 908 { "getLibraries", HandleGetLibrariesCmd }, | 181 { "isolates", HandleIsolatesListCmd }, |
| 909 { "getClassProperties", HandleGetClassPropsCmd }, | 182 { "quit", HandleQuitCmd }, |
| 910 { "getLibraryProperties", HandleGetLibPropsCmd }, | |
| 911 { "setLibraryProperties", HandleSetLibPropsCmd }, | |
| 912 { "getObjectProperties", HandleGetObjPropsCmd }, | |
| 913 { "getListElements", HandleGetListCmd }, | |
| 914 { "getGlobalVariables", HandleGetGlobalsCmd }, | |
| 915 { "getScriptURLs", HandleGetScriptURLsCmd }, | |
| 916 { "getScriptSource", HandleGetSourceCmd }, | |
| 917 { "getStackTrace", HandleGetStackTraceCmd }, | |
| 918 { "setBreakpoint", HandleSetBpCmd }, | |
| 919 { "setPauseOnException", HandlePauseOnExcCmd }, | |
| 920 { "removeBreakpoint", HandleRemBpCmd }, | |
| 921 { "stepInto", HandleStepIntoCmd }, | |
| 922 { "stepOut", HandleStepOutCmd }, | |
| 923 { "stepOver", HandleStepOverCmd }, | |
| 924 { NULL, NULL } | 183 { NULL, NULL } |
| 925 }; | 184 }; |
| 926 | 185 |
| 927 for (;;) { | 186 for (;;) { |
| 928 SendQueuedMsgs(); | 187 // Read a message. |
| 929 while (!msgbuf_->IsValidMessage() && msgbuf_->Alive()) { | 188 while (!msgbuf_->IsValidMessage() && msgbuf_->Alive()) { |
| 930 msgbuf_->ReadData(); | 189 msgbuf_->ReadData(); |
| 931 } | 190 } |
| 932 if (!msgbuf_->Alive()) { | 191 if (!msgbuf_->Alive()) { |
| 933 return; | 192 return; |
| 934 } | 193 } |
| 194 |
| 195 // Parse out the command portion from the message. |
| 935 dart::JSONReader r(msgbuf_->buf()); | 196 dart::JSONReader r(msgbuf_->buf()); |
| 936 bool found = r.Seek("command"); | 197 bool found = r.Seek("command"); |
| 937 if (r.Error()) { | 198 if (r.Error()) { |
| 938 FATAL("Illegal JSON message received"); | 199 FATAL("Illegal JSON message received"); |
| 939 } | 200 } |
| 940 if (!found) { | 201 if (!found) { |
| 941 printf("'command' not found in JSON message: '%s'\n", msgbuf_->buf()); | 202 printf("'command' not found in JSON message: '%s'\n", msgbuf_->buf()); |
| 942 msgbuf_->PopMessage(); | 203 msgbuf_->PopMessage(); |
| 943 } | 204 } |
| 205 |
| 206 // Check if this is a generic command (not isolate specific). |
| 944 int i = 0; | 207 int i = 0; |
| 945 bool is_handled = false; | 208 bool is_handled = false; |
| 946 request_resume_ = false; | 209 while (generic_debugger_commands[i].cmd_string != NULL) { |
| 947 while (debugger_commands[i].cmd_string != NULL) { | 210 if (r.IsStringLiteral(generic_debugger_commands[i].cmd_string)) { |
| 948 if (r.IsStringLiteral(debugger_commands[i].cmd_string)) { | 211 (*generic_debugger_commands[i].handler_function)(this); |
| 949 is_handled = true; | 212 is_handled = true; |
| 950 (*debugger_commands[i].handler_function)(msgbuf_->buf()); | |
| 951 msgbuf_->PopMessage(); | 213 msgbuf_->PopMessage(); |
| 952 if (request_resume_) { | |
| 953 return; | |
| 954 } | |
| 955 break; | 214 break; |
| 956 } | 215 } |
| 957 i++; | 216 i++; |
| 958 } | 217 } |
| 959 if (!is_handled) { | 218 if (!is_handled) { |
| 219 // Check if this is an isolate specific command. |
| 220 int32_t cmd_idx = DbgMessageQueue::LookupIsolateCommand(r.ValueChars(), |
| 221 r.ValueLen()); |
| 222 if (cmd_idx != DbgMessageQueue::kInvalidCommand) { |
| 223 // Get debug message queue corresponding to isolate. |
| 224 // TODO(asiva): Need to read the isolate id, map it to the appropriate |
| 225 // isolate and pass it down to GetIsolateMessageQueue to get the |
| 226 // appropriate debug message queue. |
| 227 DbgMessageQueue* queue = DbgMessageQueue::GetIsolateMessageQueue(NULL); |
| 228 ASSERT(queue != NULL); |
| 229 queue->AddMessage(cmd_idx, msgbuf_->buf(), r.EndOfObject(), debug_fd_); |
| 230 msgbuf_->PopMessage(); |
| 231 continue; |
| 232 } |
| 233 |
| 234 // This is an unrecognized command, report error and move on to next. |
| 960 printf("unrecognized command received: '%s'\n", msgbuf_->buf()); | 235 printf("unrecognized command received: '%s'\n", msgbuf_->buf()); |
| 961 HandleUnknownMsg(msgbuf_->buf()); | 236 HandleUnknownMsg(); |
| 962 msgbuf_->PopMessage(); | 237 msgbuf_->PopMessage(); |
| 963 } | 238 } |
| 964 } | 239 } |
| 965 } | 240 } |
| 966 | 241 |
| 967 | 242 |
| 968 void DebuggerConnectionHandler::WaitForConnection() { | 243 void DebuggerConnectionHandler::SendError(int debug_fd, |
| 969 MonitorLocker ml(&is_connected_); | 244 int msg_id, |
| 970 while (!IsConnected()) { | 245 const char* err_msg) { |
| 971 dart::Monitor::WaitResult res = ml.Wait(dart::Monitor::kNoTimeout); | 246 dart::TextBuffer msg(64); |
| 972 ASSERT(res == dart::Monitor::kNotified); | 247 msg.Printf("{\"id\": %d, \"error\": \"Error: %s\"}", msg_id, err_msg); |
| 973 } | 248 SendMsg(debug_fd, &msg); |
| 974 } | |
| 975 | |
| 976 | |
| 977 void DebuggerConnectionHandler::SendBreakpointEvent(Dart_StackTrace trace) { | |
| 978 dart::TextBuffer msg(128); | |
| 979 msg.Printf("{ \"event\": \"paused\", \"params\": { "); | |
| 980 msg.Printf("\"reason\": \"breakpoint\", "); | |
| 981 FormatCallFrames(&msg, trace); | |
| 982 msg.Printf("}}"); | |
| 983 SendMsg(&msg); | |
| 984 } | |
| 985 | |
| 986 | |
| 987 void DebuggerConnectionHandler::BreakpointHandler(Dart_Breakpoint bpt, | |
| 988 Dart_StackTrace trace) { | |
| 989 WaitForConnection(); | |
| 990 Dart_EnterScope(); | |
| 991 SendQueuedMsgs(); | |
| 992 SendBreakpointEvent(trace); | |
| 993 HandleMessages(); | |
| 994 if (!msgbuf_->Alive()) { | |
| 995 CloseDbgConnection(); | |
| 996 } | |
| 997 Dart_ExitScope(); | |
| 998 } | |
| 999 | |
| 1000 | |
| 1001 void DebuggerConnectionHandler::IsolateEventHandler(Dart_Isolate isolate, | |
| 1002 Dart_IsolateEvent kind) { | |
| 1003 WaitForConnection(); | |
| 1004 #if 0 | |
| 1005 if (kind == kCreated) { | |
| 1006 printf("Isolate created %p\n", isolate); | |
| 1007 } else if (kind == kInterrupted) { | |
| 1008 printf("Isolate interrupted %p\n", isolate); | |
| 1009 } else if (kind == kShutdown) { | |
| 1010 printf("Isolate shutdown %p\n", isolate); | |
| 1011 } | |
| 1012 #endif | |
| 1013 } | |
| 1014 | |
| 1015 | |
| 1016 void DebuggerConnectionHandler::SendExceptionEvent( | |
| 1017 Dart_Handle exception, | |
| 1018 Dart_StackTrace stack_trace) { | |
| 1019 intptr_t exception_id = Dart_CacheObject(exception); | |
| 1020 ASSERT(exception_id >= 0); | |
| 1021 dart::TextBuffer msg(128); | |
| 1022 msg.Printf("{ \"event\": \"paused\", \"params\": {"); | |
| 1023 msg.Printf("\"reason\": \"exception\", "); | |
| 1024 msg.Printf("\"exception\":"); | |
| 1025 FormatRemoteObj(&msg, exception); | |
| 1026 msg.Printf(", "); | |
| 1027 FormatCallFrames(&msg, stack_trace); | |
| 1028 msg.Printf("}}"); | |
| 1029 SendMsg(&msg); | |
| 1030 } | |
| 1031 | |
| 1032 | |
| 1033 void DebuggerConnectionHandler::ExceptionThrownHandler( | |
| 1034 Dart_Handle exception, | |
| 1035 Dart_StackTrace stack_trace) { | |
| 1036 WaitForConnection(); | |
| 1037 Dart_EnterScope(); | |
| 1038 SendQueuedMsgs(); | |
| 1039 SendExceptionEvent(exception, stack_trace); | |
| 1040 HandleMessages(); | |
| 1041 if (!msgbuf_->Alive()) { | |
| 1042 CloseDbgConnection(); | |
| 1043 } | |
| 1044 Dart_ExitScope(); | |
| 1045 } | |
| 1046 | |
| 1047 | |
| 1048 void DebuggerConnectionHandler::BptResolvedHandler(intptr_t bp_id, | |
| 1049 Dart_Handle url, | |
| 1050 intptr_t line_number) { | |
| 1051 Dart_EnterScope(); | |
| 1052 dart::TextBuffer msg(128); | |
| 1053 msg.Printf("{ \"event\": \"breakpointResolved\", \"params\": {"); | |
| 1054 msg.Printf("\"breakpointId\": %"Pd", \"url\":", bp_id); | |
| 1055 FormatEncodedString(&msg, url); | |
| 1056 msg.Printf(",\"line\": %"Pd" }}", line_number); | |
| 1057 QueueMsg(&msg); | |
| 1058 Dart_ExitScope(); | |
| 1059 } | |
| 1060 | |
| 1061 | |
| 1062 void DebuggerConnectionHandler::AcceptDbgConnection(int debugger_fd) { | |
| 1063 debugger_fd_ = debugger_fd; | |
| 1064 ASSERT(msgbuf_ == NULL); | |
| 1065 msgbuf_ = new MessageBuffer(debugger_fd_); | |
| 1066 { | |
| 1067 MonitorLocker ml(&is_connected_); | |
| 1068 ml.Notify(); | |
| 1069 } | |
| 1070 } | 249 } |
| 1071 | 250 |
| 1072 | 251 |
| 1073 void DebuggerConnectionHandler::CloseDbgConnection() { | 252 void DebuggerConnectionHandler::CloseDbgConnection() { |
| 1074 if (debugger_fd_ >= 0) { | 253 if (debug_fd_ >= 0) { |
| 1075 // TODO(hausner): need a Socket::Close() function. | 254 // TODO(hausner): need a Socket::Close() function. |
| 1076 } | 255 } |
| 1077 if (msgbuf_ != NULL) { | 256 if (msgbuf_ != NULL) { |
| 1078 delete msgbuf_; | 257 delete msgbuf_; |
| 1079 msgbuf_ = NULL; | 258 msgbuf_ = NULL; |
| 1080 } | 259 } |
| 1081 // TODO(hausner): Need to tell the VM debugger object to remove all | 260 // TODO(hausner): Need to tell the VM debugger object to remove all |
| 1082 // breakpoints. | 261 // breakpoints. |
| 1083 } | 262 } |
| 1084 | 263 |
| 1085 | 264 |
| 1086 void DebuggerConnectionHandler::StartHandler(const char* address, | 265 void DebuggerConnectionHandler::StartHandler(const char* address, |
| 1087 int port_number) { | 266 int port_number) { |
| 1088 if (handler_started_) { | 267 MonitorLocker ml(&handler_lock_); |
| 1089 return; | 268 if (listener_fd_ != -1) { |
| 1090 } | 269 return; // The debugger connection handler was already started. |
| 270 } |
| 271 |
| 272 // First setup breakpoint, exception and delayed breakpoint handlers. |
| 273 DbgMessageQueue::Initialize(); |
| 274 |
| 275 // Now setup a listener socket and start a thread which will |
| 276 // listen, accept connections from debuggers, read and handle/dispatch |
| 277 // debugger commands received on these connections. |
| 1091 ASSERT(listener_fd_ == -1); | 278 ASSERT(listener_fd_ == -1); |
| 1092 listener_fd_ = ServerSocket::CreateBindListen(address, port_number, 1); | 279 listener_fd_ = ServerSocket::CreateBindListen(address, port_number, 1); |
| 1093 | |
| 1094 handler_started_ = true; | |
| 1095 DebuggerConnectionImpl::StartHandler(port_number); | 280 DebuggerConnectionImpl::StartHandler(port_number); |
| 1096 Dart_SetIsolateEventHandler(IsolateEventHandler); | 281 } |
| 1097 Dart_SetBreakpointHandler(BreakpointHandler); | 282 |
| 1098 Dart_SetBreakpointResolvedHandler(BptResolvedHandler); | 283 |
| 1099 Dart_SetExceptionThrownHandler(ExceptionThrownHandler); | 284 void DebuggerConnectionHandler::WaitForConnection() { |
| 1100 } | 285 MonitorLocker ml(&handler_lock_); |
| 1101 | 286 while (!IsConnected()) { |
| 1102 | 287 dart::Monitor::WaitResult res = ml.Wait(); |
| 1103 DebuggerConnectionHandler::~DebuggerConnectionHandler() { | 288 ASSERT(res == dart::Monitor::kNotified); |
| 1104 CloseDbgConnection(); | 289 } |
| 1105 } | 290 } |
| 291 |
| 292 |
| 293 void DebuggerConnectionHandler::SendMsg(int debug_fd, dart::TextBuffer* msg) { |
| 294 MonitorLocker ml(&handler_lock_); |
| 295 SendMsgHelper(debug_fd, msg); |
| 296 } |
| 297 |
| 298 |
| 299 void DebuggerConnectionHandler::BroadcastMsg(dart::TextBuffer* msg) { |
| 300 MonitorLocker ml(&handler_lock_); |
| 301 // TODO(asiva): Once we support connection to multiple debuggers |
| 302 // we need to send the message to all of them. |
| 303 ASSERT(singleton_handler != NULL); |
| 304 SendMsgHelper(singleton_handler->debug_fd(), msg); |
| 305 } |
| 306 |
| 307 |
| 308 void DebuggerConnectionHandler::SendMsgHelper(int debug_fd, |
| 309 dart::TextBuffer* msg) { |
| 310 ASSERT(debug_fd >= 0); |
| 311 ASSERT(IsValidJSON(msg->buf())); |
| 312 // Sending messages in short pieces can be used to stress test the |
| 313 // debugger front-end's message handling code. |
| 314 const bool send_in_pieces = false; |
| 315 if (send_in_pieces) { |
| 316 intptr_t remaining = msg->length(); |
| 317 intptr_t sent = 0; |
| 318 const intptr_t max_piece_len = 122; // Pretty arbitrary, not a power of 2. |
| 319 dart::Monitor sleep; |
| 320 while (remaining > 0) { |
| 321 intptr_t piece_len = remaining; |
| 322 if (piece_len > max_piece_len) { |
| 323 piece_len = max_piece_len; |
| 324 } |
| 325 intptr_t written = |
| 326 Socket::Write(debug_fd, msg->buf() + sent, piece_len); |
| 327 ASSERT(written == piece_len); |
| 328 sent += written; |
| 329 remaining -= written; |
| 330 // Wait briefly so the OS does not coalesce message fragments. |
| 331 { |
| 332 MonitorLocker ml(&sleep); |
| 333 ml.Wait(10); |
| 334 } |
| 335 } |
| 336 return; |
| 337 } |
| 338 intptr_t bytes_written = Socket::Write(debug_fd, msg->buf(), msg->length()); |
| 339 ASSERT(msg->length() == bytes_written); |
| 340 // TODO(hausner): Error checking. Probably just shut down the debugger |
| 341 // session if we there is an error while writing. |
| 342 } |
| 343 |
| 344 |
| 345 void DebuggerConnectionHandler::AcceptDbgConnection(int debug_fd) { |
| 346 AddNewDebuggerConnection(debug_fd); |
| 347 { |
| 348 MonitorLocker ml(&handler_lock_); |
| 349 ml.NotifyAll(); |
| 350 } |
| 351 // TODO(asiva): Once we implement support for multiple connections |
| 352 // we should have a different callback for wakeups on fds which |
| 353 // are not the listener_fd_. |
| 354 // In that callback we would lookup the handler object |
| 355 // corresponding to that fd and invoke HandleMessages on it. |
| 356 // For now we run that code here. |
| 357 DebuggerConnectionHandler* handler = GetDebuggerConnectionHandler(debug_fd); |
| 358 if (handler != NULL) { |
| 359 handler->HandleMessages(); |
| 360 delete handler; |
| 361 } |
| 362 } |
| 363 |
| 364 |
| 365 void DebuggerConnectionHandler::HandleInterruptCmd( |
| 366 DebuggerConnectionHandler* handler) { |
| 367 int msg_id = handler->MessageId(); |
| 368 ASSERT(msg_id >= 0); |
| 369 SendError(handler->debug_fd(), msg_id, "interrupt command unimplemented"); |
| 370 } |
| 371 |
| 372 |
| 373 void DebuggerConnectionHandler::HandleIsolatesListCmd( |
| 374 DebuggerConnectionHandler* handler) { |
| 375 int msg_id = handler->MessageId(); |
| 376 ASSERT(msg_id >= 0); |
| 377 SendError(handler->debug_fd(), msg_id, "isolate list command unimplemented"); |
| 378 } |
| 379 |
| 380 |
| 381 void DebuggerConnectionHandler::HandleQuitCmd( |
| 382 DebuggerConnectionHandler* handler) { |
| 383 int msg_id = handler->MessageId(); |
| 384 ASSERT(msg_id >= 0); |
| 385 SendError(handler->debug_fd(), msg_id, "quit command unimplemented"); |
| 386 } |
| 387 |
| 388 |
| 389 void DebuggerConnectionHandler::AddNewDebuggerConnection(int debug_fd) { |
| 390 // TODO(asiva): Support multiple debugger connections, for now we just |
| 391 // create one handler, store it in a static variable and use it. |
| 392 ASSERT(singleton_handler == NULL); |
| 393 singleton_handler = new DebuggerConnectionHandler(debug_fd); |
| 394 } |
| 395 |
| 396 |
| 397 void DebuggerConnectionHandler::RemoveDebuggerConnection(int debug_fd) { |
| 398 // TODO(asiva): Support multiple debugger connections, for now we just |
| 399 // set the static handler back to NULL. |
| 400 ASSERT(singleton_handler != NULL); |
| 401 singleton_handler = NULL; |
| 402 } |
| 403 |
| 404 |
| 405 DebuggerConnectionHandler* |
| 406 DebuggerConnectionHandler::GetDebuggerConnectionHandler(int debug_fd) { |
| 407 // TODO(asiva): Support multiple debugger connections, for now we just |
| 408 // return the one static handler that was created. |
| 409 ASSERT(singleton_handler != NULL); |
| 410 return singleton_handler; |
| 411 } |
| 412 |
| 413 |
| 414 bool DebuggerConnectionHandler::IsConnected() { |
| 415 // TODO(asiva): Support multiple debugger connections. |
| 416 // Return true if a connection has been established. |
| 417 return singleton_handler != NULL; |
| 418 } |
| OLD | NEW |