// 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. #include "bin/dbg_connection.h" #include "bin/dbg_message.h" #include "bin/dartutils.h" #include "bin/thread.h" #include "bin/utils.h" #include "platform/globals.h" #include "platform/json.h" #include "platform/thread.h" #include "platform/utils.h" #include "include/dart_api.h" bool MessageParser::IsValidMessage() const { if (buf_length_ == 0) { return false; } dart::JSONReader msg_reader(buf_); return msg_reader.EndOfObject() != NULL; } int MessageParser::MessageId() const { dart::JSONReader r(buf_); r.Seek("id"); if (r.Type() == dart::JSONReader::kInteger) { return atoi(r.ValueChars()); } else { return -1; } } const char* MessageParser::Params() const { dart::JSONReader r(buf_); r.Seek("params"); if (r.Type() == dart::JSONReader::kObject) { return r.ValueChars(); } else { return NULL; } } intptr_t MessageParser::GetIntParam(const char* name) const { const char* params = Params(); ASSERT(params != NULL); dart::JSONReader r(params); r.Seek(name); ASSERT(r.Type() == dart::JSONReader::kInteger); return strtol(r.ValueChars(), NULL, 10); } intptr_t MessageParser::GetOptIntParam(const char* name, intptr_t default_val) const { const char* params = Params(); ASSERT(params != NULL); dart::JSONReader r(params); r.Seek(name); if (r.Type() == dart::JSONReader::kInteger) { return strtol(r.ValueChars(), NULL, 10); } else { return default_val; } } static const char* GetStringChars(Dart_Handle str) { ASSERT(Dart_IsString(str)); const char* chars; Dart_Handle res = Dart_StringToCString(str, &chars); ASSERT(!Dart_IsError(res)); return chars; } static int GetIntValue(Dart_Handle int_handle) { int64_t int64_val = -1; ASSERT(Dart_IsInteger(int_handle)); Dart_Handle res = Dart_IntegerToInt64(int_handle, &int64_val); ASSERT_NOT_ERROR(res); // TODO(hausner): Range check. return int64_val; } char* MessageParser::GetStringParam(const char* name) const { const char* params = Params(); ASSERT(params != NULL); dart::JSONReader pr(params); pr.Seek(name); if (pr.Type() != dart::JSONReader::kString) { return NULL; } intptr_t buflen = pr.ValueLen() + 1; char* param_chars = reinterpret_cast(malloc(buflen)); pr.GetValueChars(param_chars, buflen); // TODO(hausner): Decode escape sequences. return param_chars; } static void FormatEncodedCharsTrunc(dart::TextBuffer* buf, Dart_Handle str, intptr_t max_chars) { intptr_t str_len = 0; Dart_Handle res = Dart_StringLength(str, &str_len); ASSERT_NOT_ERROR(res); intptr_t num_chars = (str_len > max_chars) ? max_chars : str_len; uint16_t* codepoints = reinterpret_cast(malloc(num_chars * sizeof(uint16_t))); ASSERT(codepoints != NULL); intptr_t actual_len = num_chars; res = Dart_StringToUTF16(str, codepoints, &actual_len); ASSERT_NOT_ERROR(res); ASSERT(num_chars == actual_len); for (int i = 0; i < num_chars; i++) { buf->AddEscapedChar(codepoints[i]); } if (str_len > max_chars) { buf->Printf("..."); } free(codepoints); } static void FormatEncodedChars(dart::TextBuffer* buf, Dart_Handle str) { intptr_t str_len = 0; Dart_Handle res = Dart_StringLength(str, &str_len); ASSERT_NOT_ERROR(res); uint16_t* codepoints = reinterpret_cast(malloc(str_len * sizeof(uint16_t))); ASSERT(codepoints != NULL); intptr_t actual_len = str_len; res = Dart_StringToUTF16(str, codepoints, &actual_len); ASSERT_NOT_ERROR(res); ASSERT(str_len == actual_len); for (int i = 0; i < str_len; i++) { buf->AddEscapedChar(codepoints[i]); } free(codepoints); } static void FormatEncodedString(dart::TextBuffer* buf, Dart_Handle str) { buf->AddChar('\"'); FormatEncodedChars(buf, str); buf->AddChar('\"'); } static void FormatTextualValue(dart::TextBuffer* buf, Dart_Handle object, intptr_t max_chars); static void FormatTextualListValue(dart::TextBuffer* buf, Dart_Handle list, intptr_t max_chars) { intptr_t len = 0; Dart_Handle res = Dart_ListLength(list, &len); ASSERT_NOT_ERROR(res); const intptr_t initial_buffer_length = buf->length(); // Maximum number of characters we print for array elements. const intptr_t max_buffer_length = initial_buffer_length + max_chars; buf->Printf("["); for (int i = 0; i < len; i++) { if (i > 0) { buf->Printf(", "); } Dart_Handle elem = Dart_ListGetAt(list, i); const intptr_t max_element_chars = 50; FormatTextualValue(buf, elem, max_element_chars); if (buf->length() > max_buffer_length) { buf->Printf(", ..."); break; } } buf->Printf("]"); } static void FormatTextualValue(dart::TextBuffer* buf, Dart_Handle object, intptr_t max_chars) { if (Dart_IsList(object)) { FormatTextualListValue(buf, object, max_chars); } else if (Dart_IsNull(object)) { buf->Printf("null"); } else if (Dart_IsString(object)) { buf->Printf("\\\""); FormatEncodedCharsTrunc(buf, object, max_chars); buf->Printf("\\\""); } else { Dart_Handle text = Dart_ToString(object); if (Dart_IsNull(text)) { buf->Printf("null"); } else if (Dart_IsError(text)) { buf->Printf("#ERROR"); } else { FormatEncodedCharsTrunc(buf, text, max_chars); } } } static void FormatValue(dart::TextBuffer* buf, Dart_Handle object) { if (Dart_IsInteger(object)) { buf->Printf("\"kind\":\"integer\","); } else if (Dart_IsString(object)) { buf->Printf("\"kind\":\"string\","); } else if (Dart_IsBoolean(object)) { buf->Printf("\"kind\":\"boolean\","); } else if (Dart_IsList(object)) { intptr_t len = 0; Dart_Handle res = Dart_ListLength(object, &len); ASSERT_NOT_ERROR(res); buf->Printf("\"kind\":\"list\",\"length\":%"Pd",", len); } else { buf->Printf("\"kind\":\"object\","); } buf->Printf("\"text\":\""); const intptr_t max_chars = 250; FormatTextualValue(buf, object, max_chars); buf->Printf("\""); } static void FormatValueObj(dart::TextBuffer* buf, Dart_Handle object) { buf->Printf("{"); FormatValue(buf, object); buf->Printf("}"); } static void FormatRemoteObj(dart::TextBuffer* buf, Dart_Handle object) { intptr_t obj_id = Dart_CacheObject(object); ASSERT(obj_id >= 0); buf->Printf("{\"objectId\":%"Pd",", obj_id); FormatValue(buf, object); buf->Printf("}"); } static void FormatNamedValue(dart::TextBuffer* buf, Dart_Handle object_name, Dart_Handle object) { ASSERT(Dart_IsString(object_name)); buf->Printf("{\"name\":\"%s\",", GetStringChars(object_name)); buf->Printf("\"value\":"); FormatRemoteObj(buf, object); buf->Printf("}"); } static void FormatNamedValueList(dart::TextBuffer* buf, Dart_Handle obj_list) { ASSERT(Dart_IsList(obj_list)); intptr_t list_length = 0; Dart_Handle res = Dart_ListLength(obj_list, &list_length); ASSERT_NOT_ERROR(res); ASSERT(list_length % 2 == 0); buf->Printf("["); for (int i = 0; i + 1 < list_length; i += 2) { Dart_Handle name_handle = Dart_ListGetAt(obj_list, i); ASSERT_NOT_ERROR(name_handle); Dart_Handle value_handle = Dart_ListGetAt(obj_list, i + 1); ASSERT_NOT_ERROR(value_handle); if (i > 0) { buf->Printf(","); } FormatNamedValue(buf, name_handle, value_handle); } buf->Printf("]"); } static const char* FormatClassProps(dart::TextBuffer* buf, intptr_t cls_id) { Dart_Handle name, static_fields; intptr_t super_id = -1; intptr_t library_id = -1; Dart_Handle res = Dart_GetClassInfo(cls_id, &name, &library_id, &super_id, &static_fields); RETURN_IF_ERROR(res); RETURN_IF_ERROR(name); buf->Printf("{\"name\":\"%s\",", GetStringChars(name)); if (super_id > 0) { buf->Printf("\"superclassId\":%"Pd",", super_id); } buf->Printf("\"libraryId\":%"Pd",", library_id); RETURN_IF_ERROR(static_fields); buf->Printf("\"fields\":"); FormatNamedValueList(buf, static_fields); buf->Printf("}"); return NULL; } static const char* FormatLibraryProps(dart::TextBuffer* buf, intptr_t lib_id) { Dart_Handle url = Dart_GetLibraryURL(lib_id); RETURN_IF_ERROR(url); buf->Printf("{\"url\":"); FormatEncodedString(buf, url); // Whether debugging is enabled. bool is_debuggable = false; Dart_Handle res = Dart_GetLibraryDebuggable(lib_id, &is_debuggable); RETURN_IF_ERROR(res); buf->Printf(",\"debuggingEnabled\":%s", is_debuggable ? "\"true\"" : "\"false\""); // Imports and prefixes. Dart_Handle import_list = Dart_GetLibraryImports(lib_id); RETURN_IF_ERROR(import_list); ASSERT(Dart_IsList(import_list)); intptr_t list_length = 0; res = Dart_ListLength(import_list, &list_length); RETURN_IF_ERROR(res); buf->Printf(",\"imports\":["); for (int i = 0; i + 1 < list_length; i += 2) { Dart_Handle lib_id = Dart_ListGetAt(import_list, i + 1); ASSERT_NOT_ERROR(lib_id); buf->Printf("%s{\"libraryId\":%d,", (i > 0) ? ",": "", GetIntValue(lib_id)); Dart_Handle name = Dart_ListGetAt(import_list, i); ASSERT_NOT_ERROR(name); buf->Printf("\"prefix\":\"%s\"}", Dart_IsNull(name) ? "" : GetStringChars(name)); } buf->Printf("],"); // Global variables in the library. Dart_Handle global_vars = Dart_GetLibraryFields(lib_id); RETURN_IF_ERROR(global_vars); buf->Printf("\"globals\":"); FormatNamedValueList(buf, global_vars); buf->Printf("}"); return NULL; } static const char* FormatObjProps(dart::TextBuffer* buf, Dart_Handle object) { intptr_t class_id; if (Dart_IsNull(object)) { buf->Printf("{\"classId\":-1,\"fields\":[]}"); return NULL; } Dart_Handle res = Dart_GetObjClassId(object, &class_id); RETURN_IF_ERROR(res); buf->Printf("{\"classId\": %"Pd",", class_id); buf->Printf("\"kind\":\"object\",\"fields\":"); Dart_Handle fields = Dart_GetInstanceFields(object); RETURN_IF_ERROR(fields); FormatNamedValueList(buf, fields); buf->Printf("}"); return NULL; } static const char* FormatListSlice(dart::TextBuffer* buf, Dart_Handle list, intptr_t list_length, intptr_t index, intptr_t slice_length) { intptr_t end_index = index + slice_length; ASSERT(end_index <= list_length); buf->Printf("{\"index\":%"Pd",", index); buf->Printf("\"length\":%"Pd",", slice_length); buf->Printf("\"elements\":["); for (intptr_t i = index; i < end_index; i++) { Dart_Handle value = Dart_ListGetAt(list, i); if (i > index) { buf->Printf(","); } FormatValueObj(buf, value); } buf->Printf("]}"); return NULL; } static void FormatCallFrames(dart::TextBuffer* msg, Dart_StackTrace trace) { intptr_t trace_len = 0; Dart_Handle res = Dart_StackTraceLength(trace, &trace_len); ASSERT_NOT_ERROR(res); msg->Printf("\"callFrames\" : [ "); for (int i = 0; i < trace_len; i++) { Dart_ActivationFrame frame; res = Dart_GetActivationFrame(trace, i, &frame); ASSERT_NOT_ERROR(res); Dart_Handle func_name; Dart_Handle script_url; intptr_t line_number = 0; intptr_t library_id = 0; res = Dart_ActivationFrameInfo( frame, &func_name, &script_url, &line_number, &library_id); ASSERT_NOT_ERROR(res); ASSERT(Dart_IsString(func_name)); msg->Printf("%s{\"functionName\":", (i > 0) ? "," : ""); FormatEncodedString(msg, func_name); msg->Printf(",\"libraryId\": %"Pd",", library_id); ASSERT(Dart_IsString(script_url)); msg->Printf("\"location\": { \"url\":"); FormatEncodedString(msg, script_url); msg->Printf(",\"lineNumber\":%"Pd"},", line_number); Dart_Handle locals = Dart_GetLocalVariables(frame); ASSERT_NOT_ERROR(locals); msg->Printf("\"locals\":"); FormatNamedValueList(msg, locals); msg->Printf("}"); } msg->Printf("]"); } typedef bool (*CommandHandler)(DbgMessage* msg); struct JSONDebuggerCommand { const char* cmd_string; CommandHandler handler_function; }; static JSONDebuggerCommand debugger_commands[] = { { "resume", DbgMessage::HandleResumeCmd }, { "stepInto", DbgMessage::HandleStepIntoCmd }, { "stepOut", DbgMessage::HandleStepOutCmd }, { "stepOver", DbgMessage::HandleStepOverCmd }, { "getLibraries", DbgMessage::HandleGetLibrariesCmd }, { "getClassProperties", DbgMessage::HandleGetClassPropsCmd }, { "getLibraryProperties", DbgMessage::HandleGetLibPropsCmd }, { "setLibraryProperties", DbgMessage::HandleSetLibPropsCmd }, { "getObjectProperties", DbgMessage::HandleGetObjPropsCmd }, { "getListElements", DbgMessage::HandleGetListCmd }, { "getGlobalVariables", DbgMessage::HandleGetGlobalsCmd }, { "getScriptURLs", DbgMessage::HandleGetScriptURLsCmd }, { "getScriptSource", DbgMessage::HandleGetSourceCmd }, { "getStackTrace", DbgMessage::HandleGetStackTraceCmd }, { "setBreakpoint", DbgMessage::HandleSetBpCmd }, { "setPauseOnException", DbgMessage::HandlePauseOnExcCmd }, { "removeBreakpoint", DbgMessage::HandleRemBpCmd }, { NULL, NULL } }; bool DbgMessage::HandleMessage() { // Dispatch to the appropriate handler for the command. int max_index = (sizeof(debugger_commands) / sizeof(JSONDebuggerCommand)); ASSERT(cmd_idx_ < max_index); return (*debugger_commands[cmd_idx_].handler_function)(this); } void DbgMessage::SendReply(dart::TextBuffer* reply) { DebuggerConnectionHandler::SendMsg(debug_fd(), reply); } void DbgMessage::SendErrorReply(int msg_id, const char* err_msg) { DebuggerConnectionHandler::SendError(debug_fd(), msg_id, err_msg); } bool DbgMessage::HandleResumeCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); dart::TextBuffer msg(64); msg.Printf("{ \"id\": %d }", msg_id); in_msg->SendReply(&msg); return true; } bool DbgMessage::HandleStepIntoCmd(DbgMessage* in_msg) { Dart_Handle res = Dart_SetStepInto(); ASSERT_NOT_ERROR(res); return HandleResumeCmd(in_msg); } bool DbgMessage::HandleStepOutCmd(DbgMessage* in_msg) { Dart_Handle res = Dart_SetStepOut(); ASSERT_NOT_ERROR(res); return HandleResumeCmd(in_msg); } bool DbgMessage::HandleStepOverCmd(DbgMessage* in_msg) { Dart_Handle res = Dart_SetStepOver(); ASSERT_NOT_ERROR(res); return HandleResumeCmd(in_msg); } bool DbgMessage::HandleGetLibrariesCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); dart::TextBuffer msg(64); msg.Printf("{ \"id\": %d, \"result\": { \"libraries\": [", msg_id); Dart_Handle lib_ids = Dart_GetLibraryIds(); ASSERT_NOT_ERROR(lib_ids); intptr_t num_libs; Dart_Handle res = Dart_ListLength(lib_ids, &num_libs); ASSERT_NOT_ERROR(res); for (int i = 0; i < num_libs; i++) { Dart_Handle lib_id_handle = Dart_ListGetAt(lib_ids, i); ASSERT(Dart_IsInteger(lib_id_handle)); int lib_id = GetIntValue(lib_id_handle); Dart_Handle lib_url = Dart_GetLibraryURL(lib_id); ASSERT_NOT_ERROR(lib_url); ASSERT(Dart_IsString(lib_url)); msg.Printf("%s{\"id\":%d,\"url\":", (i == 0) ? "" : ", ", lib_id); FormatEncodedString(&msg, lib_url); msg.Printf("}"); } msg.Printf("]}}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetClassPropsCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); intptr_t cls_id = msg_parser.GetIntParam("classId"); dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\":", msg_id); const char* err = FormatClassProps(&msg, cls_id); if (err != NULL) { in_msg->SendErrorReply(msg_id, err); return false; } msg.Printf("}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetLibPropsCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); intptr_t lib_id = msg_parser.GetIntParam("libraryId"); dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\":", msg_id); const char* err = FormatLibraryProps(&msg, lib_id); if (err != NULL) { in_msg->SendErrorReply(msg_id, err); return false; } msg.Printf("}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleSetLibPropsCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); intptr_t lib_id = msg_parser.GetIntParam("libraryId"); const char* enable_request = msg_parser.GetStringParam("debuggingEnabled"); bool enable; if (strcmp(enable_request, "true") == 0) { enable = true; } else if (strcmp(enable_request, "false") == 0) { enable = false; } else { in_msg->SendErrorReply(msg_id, "illegal argument for 'debuggingEnabled'"); return false; } Dart_Handle res = Dart_SetLibraryDebuggable(lib_id, enable); if (Dart_IsError(res)) { in_msg->SendErrorReply(msg_id, Dart_GetError(res)); return false; } bool enabled = false; res = Dart_GetLibraryDebuggable(lib_id, &enabled); if (Dart_IsError(res)) { in_msg->SendErrorReply(msg_id, Dart_GetError(res)); return false; } dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\": {\"debuggingEnabled\": \"%s\"}}", msg_id, enabled ? "true" : "false"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetObjPropsCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); intptr_t obj_id = msg_parser.GetIntParam("objectId"); Dart_Handle obj = Dart_GetCachedObject(obj_id); if (Dart_IsError(obj)) { in_msg->SendErrorReply(msg_id, Dart_GetError(obj)); return false; } dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\":", msg_id); const char* err = FormatObjProps(&msg, obj); if (err != NULL) { in_msg->SendErrorReply(msg_id, err); return false; } msg.Printf("}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetListCmd(DbgMessage* in_msg) { const intptr_t kDefaultSliceLength = 100; ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); intptr_t obj_id = msg_parser.GetIntParam("objectId"); Dart_Handle list = Dart_GetCachedObject(obj_id); if (Dart_IsError(list)) { in_msg->SendErrorReply(msg_id, Dart_GetError(list)); return false; } if (!Dart_IsList(list)) { in_msg->SendErrorReply(msg_id, "object is not a list"); return false; } intptr_t list_length = 0; Dart_Handle res = Dart_ListLength(list, &list_length); if (Dart_IsError(res)) { in_msg->SendErrorReply(msg_id, Dart_GetError(res)); return false; } intptr_t index = msg_parser.GetIntParam("index"); if (index < 0) { index = 0; } else if (index > list_length) { index = list_length; } // If no slice length is given, get only one element. If slice length // is given as 0, get entire list. intptr_t slice_length = msg_parser.GetOptIntParam("length", 1); if (slice_length == 0) { slice_length = list_length - index; } if ((index + slice_length) > list_length) { slice_length = list_length - index; } ASSERT(slice_length >= 0); if (slice_length > kDefaultSliceLength) { slice_length = kDefaultSliceLength; } dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\":", msg_id); if (slice_length == 1) { Dart_Handle value = Dart_ListGetAt(list, index); FormatRemoteObj(&msg, value); } else { FormatListSlice(&msg, list, list_length, index, slice_length); } msg.Printf("}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetGlobalsCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); intptr_t lib_id = msg_parser.GetIntParam("libraryId"); dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\": { \"globals\":", msg_id); Dart_Handle globals = Dart_GetGlobalVariables(lib_id); ASSERT_NOT_ERROR(globals); FormatNamedValueList(&msg, globals); msg.Printf("}}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetScriptURLsCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); dart::TextBuffer msg(64); intptr_t lib_id = msg_parser.GetIntParam("libraryId"); Dart_Handle lib_url = Dart_GetLibraryURL(lib_id); ASSERT_NOT_ERROR(lib_url); Dart_Handle urls = Dart_GetScriptURLs(lib_url); if (Dart_IsError(urls)) { in_msg->SendErrorReply(msg_id, Dart_GetError(urls)); return false; } ASSERT(Dart_IsList(urls)); intptr_t num_urls = 0; Dart_ListLength(urls, &num_urls); msg.Printf("{ \"id\": %d, ", msg_id); msg.Printf("\"result\": { \"urls\": ["); for (int i = 0; i < num_urls; i++) { Dart_Handle script_url = Dart_ListGetAt(urls, i); if (i > 0) { msg.Printf(","); } FormatEncodedString(&msg, script_url); } msg.Printf("]}}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetSourceCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); dart::TextBuffer msg(64); intptr_t lib_id = msg_parser.GetIntParam("libraryId"); char* url_chars = msg_parser.GetStringParam("url"); ASSERT(url_chars != NULL); Dart_Handle url = DartUtils::NewString(url_chars); ASSERT_NOT_ERROR(url); free(url_chars); url_chars = NULL; Dart_Handle source = Dart_ScriptGetSource(lib_id, url); if (Dart_IsError(source)) { in_msg->SendErrorReply(msg_id, Dart_GetError(source)); return false; } msg.Printf("{ \"id\": %d, ", msg_id); msg.Printf("\"result\": { \"text\": "); FormatEncodedString(&msg, source); msg.Printf("}}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleGetStackTraceCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); Dart_StackTrace trace; Dart_Handle res = Dart_GetStackTrace(&trace); ASSERT_NOT_ERROR(res); dart::TextBuffer msg(128); msg.Printf("{ \"id\": %d, \"result\": {", msg_id); FormatCallFrames(&msg, trace); msg.Printf("}}"); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleSetBpCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); char* url_chars = msg_parser.GetStringParam("url"); ASSERT(url_chars != NULL); Dart_Handle url = DartUtils::NewString(url_chars); ASSERT_NOT_ERROR(url); free(url_chars); url_chars = NULL; intptr_t line_number = msg_parser.GetIntParam("line"); Dart_Handle bp_id = Dart_SetBreakpoint(url, line_number); if (Dart_IsError(bp_id)) { in_msg->SendErrorReply(msg_id, Dart_GetError(bp_id)); return false; } ASSERT(Dart_IsInteger(bp_id)); uint64_t bp_id_value; Dart_Handle res = Dart_IntegerToUint64(bp_id, &bp_id_value); ASSERT_NOT_ERROR(res); dart::TextBuffer msg(64); msg.Printf("{ \"id\": %d, \"result\": { \"breakpointId\": %"Pu64" }}", msg_id, bp_id_value); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandlePauseOnExcCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); char* exc_chars = msg_parser.GetStringParam("exceptions"); Dart_ExceptionPauseInfo info = kNoPauseOnExceptions; if (strcmp(exc_chars, "none") == 0) { info = kNoPauseOnExceptions; } else if (strcmp(exc_chars, "all") == 0) { info = kPauseOnAllExceptions; } else if (strcmp(exc_chars, "unhandled") == 0) { info = kPauseOnUnhandledExceptions; } else { in_msg->SendErrorReply(msg_id, "illegal value for parameter 'exceptions'"); return false; } Dart_Handle res = Dart_SetExceptionPauseInfo(info); ASSERT_NOT_ERROR(res); dart::TextBuffer msg(32); msg.Printf("{ \"id\": %d }", msg_id); in_msg->SendReply(&msg); return false; } bool DbgMessage::HandleRemBpCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); int bpt_id = msg_parser.GetIntParam("breakpointId"); Dart_Handle res = Dart_RemoveBreakpoint(bpt_id); if (Dart_IsError(res)) { in_msg->SendErrorReply(msg_id, Dart_GetError(res)); return false; } dart::TextBuffer msg(32); msg.Printf("{ \"id\": %d }", msg_id); in_msg->SendReply(&msg); return false; } void DbgMsgQueue::AddMessage(int32_t cmd_idx, const char* start, const char* end, int debug_fd) { if ((end > start) && ((end - start) < kMaxInt32)) { MonitorLocker ml(&msg_queue_lock_); DbgMessage* msg = new DbgMessage(cmd_idx, start, end, debug_fd); if (msglist_head_ == NULL) { ASSERT(msglist_tail_ == NULL); msglist_head_ = msg; msglist_tail_ = msg; ml.Notify(); } else { ASSERT(msglist_tail_ != NULL); msglist_tail_->set_next(msg); msglist_tail_ = msg; } } } void DbgMsgQueue::HandleMessages() { bool resume_requested = false; MonitorLocker ml(&msg_queue_lock_); is_running_ = false; while (!resume_requested) { while (msglist_head_ == NULL) { ASSERT(msglist_tail_ == NULL); dart::Monitor::WaitResult res = ml.Wait(); // Wait for debugger commands. ASSERT(res == dart::Monitor::kNotified); } while (msglist_head_ != NULL && !resume_requested) { ASSERT(msglist_tail_ != NULL); DbgMessage* msg = msglist_head_; msglist_head_ = msglist_head_->next(); resume_requested = msg->HandleMessage(); delete msg; } if (msglist_head_ == NULL) { msglist_tail_ = NULL; } } is_interrupted_ = false; is_running_ = true; } void DbgMsgQueue::InterruptIsolate() { Dart_Isolate isolate = Dart_GetIsolate(isolate_id_); MonitorLocker ml(&msg_queue_lock_); if (is_running_ && !is_interrupted_) { is_interrupted_ = true; Dart_InterruptIsolate(isolate); } } void DbgMsgQueue::QueueOutputMsg(dart::TextBuffer* msg) { queued_output_messages_.Printf("%s", msg->buf()); } void DbgMsgQueue::SendQueuedMsgs() { if (queued_output_messages_.length() > 0) { DebuggerConnectionHandler::BroadcastMsg(&queued_output_messages_); queued_output_messages_.Clear(); } } void DbgMsgQueue::SendBreakpointEvent(Dart_StackTrace trace) { dart::TextBuffer msg(128); msg.Printf("{ \"event\": \"paused\", \"params\": { "); msg.Printf("\"reason\": \"breakpoint\", "); msg.Printf("\"id\": %"Pd64", ", isolate_id_); FormatCallFrames(&msg, trace); msg.Printf("}}"); DebuggerConnectionHandler::BroadcastMsg(&msg); } void DbgMsgQueue::SendExceptionEvent(Dart_Handle exception, Dart_StackTrace stack_trace) { intptr_t exception_id = Dart_CacheObject(exception); ASSERT(exception_id >= 0); dart::TextBuffer msg(128); msg.Printf("{ \"event\": \"paused\", \"params\": {"); msg.Printf("\"reason\": \"exception\", "); msg.Printf("\"id\": %"Pd64", ", isolate_id_); msg.Printf("\"exception\":"); FormatRemoteObj(&msg, exception); msg.Printf(", "); FormatCallFrames(&msg, stack_trace); msg.Printf("}}"); DebuggerConnectionHandler::BroadcastMsg(&msg); } void DbgMsgQueue::SendIsolateEvent(Dart_IsolateId isolate_id, Dart_IsolateEvent kind) { dart::TextBuffer msg(128); if (kind == kInterrupted) { Dart_StackTrace trace; Dart_Handle res = Dart_GetStackTrace(&trace); ASSERT_NOT_ERROR(res); msg.Printf("{ \"event\": \"paused\", \"params\": { "); msg.Printf("\"reason\": \"interrupted\", "); msg.Printf("\"id\": %"Pd64", ", isolate_id); FormatCallFrames(&msg, trace); msg.Printf("}}"); } else { msg.Printf("{ \"event\": \"isolate\", \"params\": { "); if (kind == kCreated) { msg.Printf("\"reason\": \"created\", "); } else { ASSERT(kind == kShutdown); msg.Printf("\"reason\": \"shutdown\", "); } msg.Printf("\"id\": %"Pd64" ", isolate_id); msg.Printf("}}"); } DebuggerConnectionHandler::BroadcastMsg(&msg); } DbgMsgQueue* DbgMsgQueueList::list_ = NULL; dart::Mutex DbgMsgQueueList::msg_queue_list_lock_; void DbgMsgQueueList::Initialize() { // Setup handlers for isolate events, breakpoints, exceptions and // delayed breakpoints. Dart_SetIsolateEventHandler(IsolateEventHandler); Dart_SetBreakpointHandler(BreakpointHandler); Dart_SetBreakpointResolvedHandler(BptResolvedHandler); Dart_SetExceptionThrownHandler(ExceptionThrownHandler); } int32_t DbgMsgQueueList::LookupIsolateCommand(const char* buf, int32_t buflen) { // Check if we have a isolate specific debugger command. int32_t i = 0; while (debugger_commands[i].cmd_string != NULL) { if (strncmp(buf, debugger_commands[i].cmd_string, buflen) == 0) { return i; } i++; } return kInvalidCommand; } bool DbgMsgQueueList::AddIsolateMessage(Dart_IsolateId isolate_id, int32_t cmd_idx, const char* start, const char* end, int debug_fd) { MutexLocker ml(&msg_queue_list_lock_); DbgMsgQueue* queue = DbgMsgQueueList::GetIsolateMsgQueueLocked(isolate_id); if (queue != NULL) { queue->AddMessage(cmd_idx, start, end, debug_fd); return true; } return false; } bool DbgMsgQueueList::InterruptIsolate(Dart_IsolateId isolate_id) { MutexLocker ml(&msg_queue_list_lock_); DbgMsgQueue* queue = DbgMsgQueueList::GetIsolateMsgQueueLocked(isolate_id); if (queue != NULL) { queue->InterruptIsolate(); return true; } return false; } DbgMsgQueue* DbgMsgQueueList::AddIsolateMsgQueue(Dart_IsolateId isolate_id) { MutexLocker ml(&msg_queue_list_lock_); DbgMsgQueue* queue = new DbgMsgQueue(isolate_id, list_); ASSERT(queue != NULL); list_ = queue; return queue; } DbgMsgQueue* DbgMsgQueueList::GetIsolateMsgQueue(Dart_IsolateId isolate_id) { MutexLocker ml(&msg_queue_list_lock_); ASSERT(Dart_GetIsolate(isolate_id) == Dart_CurrentIsolate()); return GetIsolateMsgQueueLocked(isolate_id); } DbgMsgQueue* DbgMsgQueueList::GetIsolateMsgQueueLocked(Dart_IsolateId id) { if (list_ == NULL) { return NULL; // No items in the list. } // Find message queue corresponding to isolate id. DbgMsgQueue* iterator = list_; while (iterator != NULL && iterator->isolate_id() != id) { iterator = iterator->next(); } return iterator; } void DbgMsgQueueList::RemoveIsolateMsgQueue(Dart_IsolateId isolate_id) { MutexLocker ml(&msg_queue_list_lock_); if (list_ == NULL) { return; // No items in the list. } DbgMsgQueue* queue = list_; if (queue->isolate_id() == isolate_id) { list_ = queue->next(); // Remove from list. delete queue; // Delete the message queue. return; } else { DbgMsgQueue* iterator = queue; queue = queue->next(); while (queue != NULL) { if (queue->isolate_id() == isolate_id) { iterator->set_next(queue->next()); // Remove from list. delete queue; // Delete the message queue. return; } iterator = queue; queue = queue->next(); } } UNREACHABLE(); } void DbgMsgQueueList::BptResolvedHandler(Dart_IsolateId isolate_id, intptr_t bp_id, Dart_Handle url, intptr_t line_number) { Dart_EnterScope(); dart::TextBuffer msg(128); msg.Printf("{ \"event\": \"breakpointResolved\", \"params\": {"); msg.Printf("\"breakpointId\": %"Pd", \"url\":", bp_id); FormatEncodedString(&msg, url); msg.Printf(",\"line\": %"Pd" }}", line_number); DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id); ASSERT(msg_queue != NULL); msg_queue->QueueOutputMsg(&msg); Dart_ExitScope(); } void DbgMsgQueueList::BreakpointHandler(Dart_IsolateId isolate_id, Dart_Breakpoint bpt, Dart_StackTrace trace) { DebuggerConnectionHandler::WaitForConnection(); Dart_EnterScope(); DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id); ASSERT(msg_queue != NULL); msg_queue->SendQueuedMsgs(); msg_queue->SendBreakpointEvent(trace); msg_queue->HandleMessages(); Dart_ExitScope(); } void DbgMsgQueueList::ExceptionThrownHandler(Dart_IsolateId isolate_id, Dart_Handle exception, Dart_StackTrace stack_trace) { DebuggerConnectionHandler::WaitForConnection(); Dart_EnterScope(); DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id); ASSERT(msg_queue != NULL); msg_queue->SendQueuedMsgs(); msg_queue->SendExceptionEvent(exception, stack_trace); msg_queue->HandleMessages(); Dart_ExitScope(); } void DbgMsgQueueList::IsolateEventHandler(Dart_IsolateId isolate_id, Dart_IsolateEvent kind) { DebuggerConnectionHandler::WaitForConnection(); Dart_EnterScope(); if (kind == kCreated) { DbgMsgQueue* msg_queue = AddIsolateMsgQueue(isolate_id); msg_queue->SendIsolateEvent(isolate_id, kind); } else { DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id); ASSERT(msg_queue != NULL); msg_queue->SendQueuedMsgs(); msg_queue->SendIsolateEvent(isolate_id, kind); if (kind == kInterrupted) { msg_queue->HandleMessages(); } else { ASSERT(kind == kShutdown); RemoveIsolateMsgQueue(isolate_id); } } Dart_ExitScope(); }