// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef BIN_DBG_MESSAGE_H_ #define BIN_DBG_MESSAGE_H_ #include "bin/builtin.h" #include "bin/utils.h" #include "include/dart_debugger_api.h" #include "platform/globals.h" #include "platform/json.h" #include "platform/thread.h" // TODO(hausner): Need better error handling. #define ASSERT_NOT_ERROR(handle) \ ASSERT(!Dart_IsError(handle)) #define RETURN_IF_ERROR(handle) \ if (Dart_IsError(handle)) { \ return Dart_GetError(handle); \ } // Class to parse a JSON debug command message. class MessageParser { public: MessageParser(const char* buf, int buf_length) : buf_(buf), buf_length_(buf_length) { } ~MessageParser() { } // Accessors. const char* buf() const { return buf_; } bool IsValidMessage() const; int MessageId() const; const char* Params() const; intptr_t GetIntParam(const char* name) const; intptr_t GetOptIntParam(const char* name, intptr_t default_val) const; // GetStringParam mallocs the buffer that it returns. Caller must free. char* GetStringParam(const char* name) const; private: const char* buf_; int buf_length_; DISALLOW_COPY_AND_ASSIGN(MessageParser); }; // Class which represents a debug command message and handles it. class DbgMessage { public: DbgMessage(int32_t cmd_idx, const char* start, const char* end, int debug_fd) : next_(NULL), cmd_idx_(cmd_idx), buffer_(NULL), buffer_len_(end - start), debug_fd_(debug_fd) { buffer_ = reinterpret_cast(malloc(buffer_len_)); ASSERT(buffer_ != NULL); memmove(buffer_, start, buffer_len_); } ~DbgMessage() { next_ = NULL; free(buffer_); } // Accessors. DbgMessage* next() const { return next_; } void set_next(DbgMessage* next) { next_ = next; } char* buffer() const { return buffer_; } int32_t buffer_len() const { return buffer_len_; } int debug_fd() const { return debug_fd_; } // Handle debugger command message. // Returns true if the execution needs to resume after this message is // handled, false otherwise. bool HandleMessage(); // Send reply after successful handling of command. void SendReply(dart::TextBuffer* msg); // Reply error returned by the command handler. void SendErrorReply(int msg_id, const char* err_msg); // Handlers for specific commands, they are static because these // functions are populated as function pointers into a dispatch table. static bool HandleResumeCmd(DbgMessage* msg); static bool HandleStepIntoCmd(DbgMessage* msg); static bool HandleStepOverCmd(DbgMessage* msg); static bool HandleStepOutCmd(DbgMessage* msg); static bool HandleGetLibrariesCmd(DbgMessage* msg); static bool HandleGetClassPropsCmd(DbgMessage* msg); static bool HandleGetLibPropsCmd(DbgMessage* msg); static bool HandleSetLibPropsCmd(DbgMessage* msg); static bool HandleGetGlobalsCmd(DbgMessage* msg); static bool HandleGetObjPropsCmd(DbgMessage* msg); static bool HandleGetListCmd(DbgMessage* msg); static bool HandleGetScriptURLsCmd(DbgMessage* msg); static bool HandleGetSourceCmd(DbgMessage* msg); static bool HandleGetStackTraceCmd(DbgMessage* msg); static bool HandlePauseOnExcCmd(DbgMessage* msg); static bool HandleSetBpCmd(DbgMessage* msg); static bool HandleRemBpCmd(DbgMessage* msg); private: DbgMessage* next_; // Next message in the queue. int32_t cmd_idx_; // Isolate specific debugger command index. char* buffer_; // Debugger command message. int32_t buffer_len_; // Length of the debugger command message. int debug_fd_; // Debugger connection on which replies are to be sent. DISALLOW_IMPLICIT_CONSTRUCTORS(DbgMessage); }; // Class which represents a queue of debug command messages sent to an isolate. class DbgMsgQueue { public: DbgMsgQueue(Dart_IsolateId isolate_id, DbgMsgQueue* next) : is_running_(true), is_interrupted_(false), msglist_head_(NULL), msglist_tail_(NULL), queued_output_messages_(64), isolate_id_(isolate_id), next_(next) { } ~DbgMsgQueue() { DbgMessage* msg = msglist_head_; while (msglist_head_ != NULL) { msglist_head_ = msglist_head_->next(); delete msg; msg = msglist_head_; } msglist_tail_ = NULL; isolate_id_ = ILLEGAL_ISOLATE_ID; next_ = NULL; } // Accessors. Dart_IsolateId isolate_id() const { return isolate_id_; } DbgMsgQueue* next() const { return next_; } void set_next(DbgMsgQueue* next) { next_ = next; } // Add specified debug command message to the queue. void AddMessage(int32_t cmd_idx, const char* start, const char* end, int debug_fd); // Handle all debug command messages in the queue. void HandleMessages(); // Interrupt Isolate. void InterruptIsolate(); // Queue output messages, these are sent out when we hit an event. void QueueOutputMsg(dart::TextBuffer* msg); // Send all queued messages over to the debugger. void SendQueuedMsgs(); // Send breakpoint event message over to the debugger. void SendBreakpointEvent(Dart_StackTrace trace); // Send Exception event message over to the debugger. void SendExceptionEvent(Dart_Handle exception, Dart_StackTrace trace); // Send Isolate event message over to the debugger. void SendIsolateEvent(Dart_IsolateId isolate_id, Dart_IsolateEvent kind); private: bool is_running_; // True if isolate is running and not in the debugger loop. bool is_interrupted_; // True if interrupt command is pending. DbgMessage* msglist_head_; // Start of debugger messages list. DbgMessage* msglist_tail_; // End of debugger messages list. // Text buffer that accumulates messages to be output. dart::TextBuffer queued_output_messages_; Dart_IsolateId isolate_id_; // Id of the isolate tied to this queue. DbgMsgQueue* next_; // Used for creating a list of message queues. // The isolate waits on this condition variable when it needs to process // debug messages. dart::Monitor msg_queue_lock_; DISALLOW_COPY_AND_ASSIGN(DbgMsgQueue); }; // Maintains a list of message queues. class DbgMsgQueueList { public: static const int32_t kInvalidCommand = -1; // Initialize the message queue processing by setting up the appropriate // handlers. static void Initialize(); // Parses the input message and returns the command index if the message // contains a valid isolate specific command. // It returns kInvalidCommand otherwise. static int32_t LookupIsolateCommand(const char* buf, int32_t buflen); // Queue debugger message targetted for the isolate. static bool AddIsolateMessage(Dart_IsolateId isolate_id, int32_t cmd_idx, const char* start, const char* end, int debug_fd); // Interrupt isolate. static bool InterruptIsolate(Dart_IsolateId isolate_id); // Add Debugger Message Queue corresponding to the Isolate. static DbgMsgQueue* AddIsolateMsgQueue(Dart_IsolateId isolate_id); // Get Debugger Message Queue corresponding to the Isolate. static DbgMsgQueue* GetIsolateMsgQueue(Dart_IsolateId isolate_id); // Remove Debugger Message Queue corresponding to the Isolate. static void RemoveIsolateMsgQueue(Dart_IsolateId isolate_id); // Generic handlers for breakpoints, exceptions and delayed breakpoint // resolution. static void BptResolvedHandler(Dart_IsolateId isolate_id, intptr_t bp_id, Dart_Handle url, intptr_t line_number); static void BreakpointHandler(Dart_IsolateId isolate_id, Dart_Breakpoint bpt, Dart_StackTrace trace); static void ExceptionThrownHandler(Dart_IsolateId isolate_id, Dart_Handle exception, Dart_StackTrace stack_trace); static void IsolateEventHandler(Dart_IsolateId isolate_id, Dart_IsolateEvent kind); private: static DbgMsgQueue* GetIsolateMsgQueueLocked(Dart_IsolateId isolate_id); static DbgMsgQueue* list_; static dart::Mutex msg_queue_list_lock_; DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(DbgMsgQueueList); }; #endif // BIN_DBG_MESSAGE_H_