blob: 36cfeba4614adc03330417e9edf3a43c3642c4d3 [file] [log] [blame]
// 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<char*>(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<uint16_t*>(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<uint16_t*>(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_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 {
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);
}
}
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 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, &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, &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")) {
in_msg->SendErrorReply(msg_id,
"libararyId evaluation target not supported");
return false;
} else if (msg_parser.HasParam("classId")) {
in_msg->SendErrorReply(msg_id,
"classId evaluation target not supported");
return false;
} 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::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(const Dart_CodeLocation& location) {
dart::TextBuffer msg(128);
msg.Printf("{ \"event\": \"paused\", \"params\": { ");
msg.Printf("\"reason\": \"breakpoint\", ");
msg.Printf("\"isolateId\": %" Pd64 "", isolate_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;
}
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,
const Dart_CodeLocation& loc) {
DebuggerConnectionHandler::WaitForConnection();
Dart_EnterScope();
DbgMsgQueue* msg_queue = GetIsolateMsgQueue(isolate_id);
ASSERT(msg_queue != NULL);
msg_queue->SendQueuedMsgs();
msg_queue->SendBreakpointEvent(loc);
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();
}
} // namespace bin
} // namespace dart