// 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" namespace dart { namespace bin { 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; } } bool MessageParser::HasParam(const char* name) const { const char* params = Params(); ASSERT(params != NULL); dart::JSONReader r(params); return r.Seek(name); } 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.GetDecodedValueChars(param_chars, buflen); 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, bool expand_list); 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, false); if (buf->length() > max_buffer_length) { buf->Printf(", ..."); break; } } buf->Printf("]"); } static void FormatTextualValue(dart::TextBuffer* buf, Dart_Handle object, intptr_t max_chars, bool expand_list) { if (Dart_IsList(object)) { if (expand_list) { FormatTextualListValue(buf, object, max_chars); } else { buf->Printf("[...]"); } } 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) { bool print_text_field = true; if (Dart_IsNumber(object)) { buf->Printf("\"kind\":\"number\""); } 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 if (Dart_IsClosure(object)) { Dart_Handle name, signature; Dart_CodeLocation location; Dart_Handle res = Dart_GetClosureInfo(object, &name, &signature, &location); ASSERT_NOT_ERROR(res); buf->Printf("\"kind\":\"function\",\"name\":\"%s\"", GetStringChars(name)); buf->Printf(",\"signature\":\"%s\"", GetStringChars(signature)); if (!Dart_IsNull(location.script_url)) { ASSERT(Dart_IsString(location.script_url)); buf->Printf(",\"location\": { \"url\":"); FormatEncodedString(buf, location.script_url); buf->Printf(",\"libraryId\":%d,", location.library_id); buf->Printf("\"tokenOffset\":%d}", location.token_pos); } print_text_field = false; } else { buf->Printf("\"kind\":\"object\""); intptr_t class_id = 0; Dart_Handle res = Dart_GetObjClassId(object, &class_id); if (!Dart_IsError(res)) { buf->Printf(",\"classId\":%" Pd "", class_id); } } if (print_text_field) { buf->Printf(",\"text\":\""); const intptr_t max_chars = 250; FormatTextualValue(buf, object, max_chars, true); 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 FormatLocationFromTrace(dart::TextBuffer* msg, Dart_StackTrace trace, const char* prefix) { intptr_t trace_len = 0; Dart_Handle res = Dart_StackTraceLength(trace, &trace_len); ASSERT_NOT_ERROR(res); Dart_ActivationFrame frame; res = Dart_GetActivationFrame(trace, 0, &frame); ASSERT_NOT_ERROR(res); Dart_CodeLocation location; res = Dart_ActivationFrameGetLocation(frame, NULL, NULL, &location); ASSERT_NOT_ERROR(res); if (!Dart_IsNull(location.script_url)) { ASSERT(Dart_IsString(location.script_url)); msg->Printf("%s\"location\": { \"url\":", prefix); FormatEncodedString(msg, location.script_url); msg->Printf(",\"libraryId\":%d,", location.library_id); msg->Printf("\"tokenOffset\":%d}", location.token_pos); } } 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_CodeLocation location; res = Dart_ActivationFrameGetLocation(frame, &func_name, NULL, &location); ASSERT_NOT_ERROR(res); ASSERT(Dart_IsString(func_name)); msg->Printf("%s{\"functionName\":", (i > 0) ? "," : ""); FormatEncodedString(msg, func_name); if (!Dart_IsNull(location.script_url)) { ASSERT(Dart_IsString(location.script_url)); msg->Printf(",\"location\": { \"url\":"); FormatEncodedString(msg, location.script_url); msg->Printf(",\"libraryId\":%d,", location.library_id); msg->Printf("\"tokenOffset\":%d}", location.token_pos); } 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 }, { "evaluateExpr", DbgMessage::HandleEvaluateExprCmd }, { "getObjectProperties", DbgMessage::HandleGetObjPropsCmd }, { "getListElements", DbgMessage::HandleGetListCmd }, { "getGlobalVariables", DbgMessage::HandleGetGlobalsCmd }, { "getScriptURLs", DbgMessage::HandleGetScriptURLsCmd }, { "getScriptSource", DbgMessage::HandleGetSourceCmd }, { "getLineNumberTable", DbgMessage::HandleGetLineNumbersCmd }, { "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::HandleEvaluateExprCmd(DbgMessage* in_msg) { ASSERT(in_msg != NULL); MessageParser msg_parser(in_msg->buffer(), in_msg->buffer_len()); int msg_id = msg_parser.MessageId(); Dart_Handle target; if (msg_parser.HasParam("libraryId")) { intptr_t lib_id = msg_parser.GetIntParam("libraryId"); target = Dart_GetLibraryFromId(lib_id); } else if (msg_parser.HasParam("classId")) { intptr_t cls_id = msg_parser.GetIntParam("classId"); target = Dart_GetClassFromId(cls_id); } else if (msg_parser.HasParam("objectId")) { intptr_t obj_id = msg_parser.GetIntParam("objectId"); target = Dart_GetCachedObject(obj_id); } else { in_msg->SendErrorReply(msg_id, "illegal evaluation target"); return false; } if (Dart_IsError(target)) { in_msg->SendErrorReply(msg_id, Dart_GetError(target)); return false; } char* expr_chars = msg_parser.GetStringParam("expression"); Dart_Handle expr = Dart_NewStringFromCString(expr_chars); if (Dart_IsError(expr)) { in_msg->SendErrorReply(msg_id, Dart_GetError(expr)); return false; } Dart_Handle value = Dart_EvaluateExpr(target, expr); if (Dart_IsError(value)) { in_msg->SendErrorReply(msg_id, Dart_GetError(value)); return false; } dart::TextBuffer msg(64); msg.Printf("{\"id\":%d, \"result\":", msg_id); FormatRemoteObj(&msg, value); msg.Printf("}"); 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::HandleGetLineNumbersCmd(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 info = Dart_ScriptGetTokenInfo(lib_id, url); if (Dart_IsError(info)) { in_msg->SendErrorReply(msg_id, Dart_GetError(info)); return false; } ASSERT(Dart_IsList(info)); intptr_t info_len = 0; Dart_Handle res = Dart_ListLength(info, &info_len); ASSERT_NOT_ERROR(res); msg.Printf("{ \"id\": %d, ", msg_id); msg.Printf("\"result\": { \"lines\": ["); Dart_Handle elem; intptr_t num_elems = 0; for (intptr_t i = 0; i < info_len; i++) { elem = Dart_ListGetAt(info, i); if (Dart_IsNull(elem)) { msg.Printf((i == 0) ? "[" : "], ["); num_elems = 0; } else { ASSERT(Dart_IsInteger(elem)); int value = GetIntValue(elem); if (num_elems == 0) { msg.Printf("%d", value); } else { msg.Printf(",%d", value); } num_elems++; } } 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::Notify() { MonitorLocker ml(&msg_queue_lock_); ml.Notify(); } bool DbgMsgQueue::HandlePendingMessages() { // Handle all available debug messages, up to a resume request. bool resume_requested = false; while (msglist_head_ != NULL && !resume_requested) { ASSERT(msglist_tail_ != NULL); DbgMessage* msg = msglist_head_; msglist_head_ = msglist_head_->next(); if (msglist_head_ == NULL) { msglist_tail_ = NULL; } resume_requested = msg->HandleMessage(); delete msg; } return resume_requested; } void DbgMsgQueue::MessageLoop() { MonitorLocker ml(&msg_queue_lock_); is_running_ = false; // Request notification on isolate messages. This allows us to // respond to vm service messages while at breakpoint. Dart_SetMessageNotifyCallback(DbgMsgQueueList::NotifyIsolate); while (true) { // Handle all available vm service messages, up to a resume // request. if (Dart_HandleServiceMessages()) { break; } // Handle all available debug messages, up to a resume request. if (HandlePendingMessages()) { break; } // Wait for more debug or vm service messages. dart::Monitor::WaitResult res = ml.Wait(); ASSERT(res == dart::Monitor::kNotified); } Dart_SetMessageNotifyCallback(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(intptr_t bp_id, const Dart_CodeLocation& location) { dart::TextBuffer msg(128); msg.Printf("{ \"event\": \"paused\", \"params\": { "); msg.Printf("\"reason\": \"breakpoint\", "); msg.Printf("\"isolateId\": %" Pd64 "", isolate_id_); if (bp_id != ILLEGAL_BREAKPOINT_ID) { msg.Printf(",\"breakpointId\": %" Pd "", bp_id); } if (!Dart_IsNull(location.script_url)) { ASSERT(Dart_IsString(location.script_url)); msg.Printf(",\"location\": { \"url\":"); FormatEncodedString(&msg, location.script_url); msg.Printf(",\"libraryId\":%d,", location.library_id); msg.Printf("\"tokenOffset\":%d}", location.token_pos); } msg.Printf("}}"); DebuggerConnectionHandler::BroadcastMsg(&msg); } // TODO(hausner): Remove stack trace parameter once we remove the stack // trace from the paused event in the wire protocol. 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("\"isolateId\": %" Pd64 ", ", isolate_id_); msg.Printf("\"exception\":"); FormatRemoteObj(&msg, exception); FormatLocationFromTrace(&msg, stack_trace, ", "); msg.Printf("}}"); DebuggerConnectionHandler::BroadcastMsg(&msg); } // TODO(hausner): Remove stack trace parameter once we remove the stack // trace from the interrupted event in the wire protocol. 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("\"isolateId\": %" Pd64 "", isolate_id); FormatLocationFromTrace(&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_ = new dart::Mutex(); void DbgMsgQueueList::Initialize() { // Setup handlers for isolate events, breakpoints, exceptions and // delayed breakpoints. Dart_SetIsolateEventHandler(IsolateEventHandler); Dart_SetPausedEventHandler(PausedEventHandler); 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; } void DbgMsgQueueList::NotifyIsolate(Dart_Isolate isolate) { MutexLocker ml(msg_queue_list_lock_); Dart_IsolateId isolate_id = Dart_GetIsolateId(isolate); DbgMsgQueue* queue = DbgMsgQueueList::GetIsolateMsgQueueLocked(isolate_id); if (queue != NULL) { queue->Notify(); } } 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::ListIsolateIds(dart::TextBuffer* msg) { MutexLocker ml(msg_queue_list_lock_); if (list_ == NULL) { return; // No items in the list. } DbgMsgQueue* queue = list_; msg->Printf("%" Pd64 "", queue->isolate_id()); queue = queue->next(); while (queue != NULL) { msg->Printf(",%" Pd64 "", queue->isolate_id()); queue = queue->next(); } } void DbgMsgQueueList::BptResolvedHandler(Dart_IsolateId isolate_id, intptr_t bp_id, const Dart_CodeLocation& location) { Dart_EnterScope(); dart::TextBuffer msg(128); msg.Printf("{ \"event\": \"breakpointResolved\", \"params\": {"); msg.Printf("\"breakpointId\": %" Pd "", bp_id); msg.Printf(", \"isolateId\":%" Pd64 "", isolate_id); ASSERT(!Dart_IsNull(location.script_url)); ASSERT(Dart_IsString(location.script_url)); msg.Printf(", \"location\":{\"url\":"); FormatEncodedString(&msg, location.script_url); msg.Printf(",\"libraryId\":%d", location.library_id); msg.Printf(",\"tokenOffset\":%d}}}", location.token_pos); DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id); ASSERT(msg_queue != NULL); msg_queue->QueueOutputMsg(&msg); Dart_ExitScope(); } void DbgMsgQueueList::PausedEventHandler(Dart_IsolateId isolate_id, intptr_t bp_id, const Dart_CodeLocation& loc) { DebuggerConnectionHandler::WaitForConnection(); Dart_EnterScope(); DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id); ASSERT(msg_queue != NULL); msg_queue->SendQueuedMsgs(); msg_queue->SendBreakpointEvent(bp_id, loc); msg_queue->MessageLoop(); 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->MessageLoop(); 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->MessageLoop(); } else { ASSERT(kind == kShutdown); RemoveIsolateMsgQueue(isolate_id); } } Dart_ExitScope(); } } // namespace bin } // namespace dart