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 |