| Index: dart/runtime/vm/service.cc
|
| ===================================================================
|
| --- dart/runtime/vm/service.cc (revision 31530)
|
| +++ dart/runtime/vm/service.cc (working copy)
|
| @@ -14,6 +14,7 @@
|
| #include "vm/object_id_ring.h"
|
| #include "vm/object_store.h"
|
| #include "vm/port.h"
|
| +#include "vm/profiler.h"
|
|
|
| namespace dart {
|
|
|
| @@ -196,15 +197,15 @@
|
| jsobj.AddProperty("type", "StackTrace");
|
| JSONArray jsarr(&jsobj, "members");
|
| intptr_t n_frames = stack->Length();
|
| - String& url = String::Handle();
|
| String& function = String::Handle();
|
| + Script& script = Script::Handle();
|
| for (int i = 0; i < n_frames; i++) {
|
| ActivationFrame* frame = stack->FrameAt(i);
|
| - url ^= frame->SourceUrl();
|
| + script ^= frame->SourceScript();
|
| function ^= frame->function().UserVisibleName();
|
| JSONObject jsobj(&jsarr);
|
| jsobj.AddProperty("name", function.ToCString());
|
| - jsobj.AddProperty("url", url.ToCString());
|
| + jsobj.AddProperty("script", script);
|
| jsobj.AddProperty("line", frame->LineNumber());
|
| jsobj.AddProperty("function", frame->function());
|
| jsobj.AddProperty("code", frame->code());
|
| @@ -239,6 +240,164 @@
|
| }
|
|
|
|
|
| +#define CHECK_COLLECTION_ID_BOUNDS(collection, length, arg, id, js) \
|
| + if (!GetIntegerId(arg, &id)) { \
|
| + PrintError(js, "Must specify collection object id: %s/id", collection); \
|
| + return; \
|
| + } \
|
| + if ((id < 0) || (id >= length)) { \
|
| + PrintError(js, "%s id (%" Pd ") must be in [0, %" Pd ").", collection, id, \
|
| + length); \
|
| + return; \
|
| + }
|
| +
|
| +
|
| +static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) {
|
| + if ((s == NULL) || (*s == '\0')) {
|
| + // Empty string.
|
| + return false;
|
| + }
|
| + if (id == NULL) {
|
| + // No id pointer.
|
| + return false;
|
| + }
|
| + intptr_t r = 0;
|
| + char* end_ptr = NULL;
|
| + r = strtol(s, &end_ptr, base);
|
| + if (end_ptr == s) {
|
| + // String was not advanced at all, cannot be valid.
|
| + return false;
|
| + }
|
| + *id = r;
|
| + return true;
|
| +}
|
| +
|
| +
|
| +static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) {
|
| + if ((s == NULL) || (*s == '\0')) {
|
| + // Empty string.
|
| + return false;
|
| + }
|
| + if (id == NULL) {
|
| + // No id pointer.
|
| + return false;
|
| + }
|
| + uintptr_t r = 0;
|
| + char* end_ptr = NULL;
|
| + r = strtoul(s, &end_ptr, base);
|
| + if (end_ptr == s) {
|
| + // String was not advanced at all, cannot be valid.
|
| + return false;
|
| + }
|
| + *id = r;
|
| + return true;
|
| +}
|
| +
|
| +
|
| +static void HandleClassesClosures(Isolate* isolate, const Class& cls,
|
| + JSONStream* js) {
|
| + intptr_t id;
|
| + if (js->num_arguments() > 4) {
|
| + PrintError(js, "Command too long");
|
| + return;
|
| + }
|
| + if (!GetIntegerId(js->GetArgument(3), &id)) {
|
| + PrintError(js, "Must specify collection object id: closures/id");
|
| + return;
|
| + }
|
| + Function& func = Function::Handle();
|
| + func ^= cls.ClosureFunctionFromIndex(id);
|
| + if (func.IsNull()) {
|
| + PrintError(js, "Closure function %" Pd " not found", id);
|
| + return;
|
| + }
|
| + func.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| +static void HandleClassesDispatchers(Isolate* isolate, const Class& cls,
|
| + JSONStream* js) {
|
| + intptr_t id;
|
| + if (js->num_arguments() > 4) {
|
| + PrintError(js, "Command too long");
|
| + return;
|
| + }
|
| + if (!GetIntegerId(js->GetArgument(3), &id)) {
|
| + PrintError(js, "Must specify collection object id: dispatchers/id");
|
| + return;
|
| + }
|
| + Function& func = Function::Handle();
|
| + func ^= cls.InvocationDispatcherFunctionFromIndex(id);
|
| + if (func.IsNull()) {
|
| + PrintError(js, "Dispatcher %" Pd " not found", id);
|
| + return;
|
| + }
|
| + func.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| +static void HandleClassesFunctions(Isolate* isolate, const Class& cls,
|
| + JSONStream* js) {
|
| + intptr_t id;
|
| + if (js->num_arguments() > 4) {
|
| + PrintError(js, "Command too long");
|
| + return;
|
| + }
|
| + if (!GetIntegerId(js->GetArgument(3), &id)) {
|
| + PrintError(js, "Must specify collection object id: functions/id");
|
| + return;
|
| + }
|
| + Function& func = Function::Handle();
|
| + func ^= cls.FunctionFromIndex(id);
|
| + if (func.IsNull()) {
|
| + PrintError(js, "Function %" Pd " not found", id);
|
| + return;
|
| + }
|
| + func.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| +static void HandleClassesImplicitClosures(Isolate* isolate, const Class& cls,
|
| + JSONStream* js) {
|
| + intptr_t id;
|
| + if (js->num_arguments() > 4) {
|
| + PrintError(js, "Command too long");
|
| + return;
|
| + }
|
| + if (!GetIntegerId(js->GetArgument(3), &id)) {
|
| + PrintError(js, "Must specify collection object id: implicit_closures/id");
|
| + return;
|
| + }
|
| + Function& func = Function::Handle();
|
| + func ^= cls.ImplicitClosureFunctionFromIndex(id);
|
| + if (func.IsNull()) {
|
| + PrintError(js, "Implicit closure function %" Pd " not found", id);
|
| + return;
|
| + }
|
| + func.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| +static void HandleClassesFields(Isolate* isolate, const Class& cls,
|
| + JSONStream* js) {
|
| + intptr_t id;
|
| + if (js->num_arguments() > 4) {
|
| + PrintError(js, "Command too long");
|
| + return;
|
| + }
|
| + if (!GetIntegerId(js->GetArgument(3), &id)) {
|
| + PrintError(js, "Must specify collection object id: fields/id");
|
| + return;
|
| + }
|
| + Field& field = Field::Handle(cls.FieldFromIndex(id));
|
| + if (field.IsNull()) {
|
| + PrintError(js, "Field %" Pd " not found", id);
|
| + return;
|
| + }
|
| + field.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| static void HandleClasses(Isolate* isolate, JSONStream* js) {
|
| if (js->num_arguments() == 1) {
|
| ClassTable* table = isolate->class_table();
|
| @@ -246,14 +405,38 @@
|
| return;
|
| }
|
| ASSERT(js->num_arguments() >= 2);
|
| - intptr_t id = atoi(js->GetArgument(1));
|
| + intptr_t id;
|
| + if (!GetIntegerId(js->GetArgument(1), &id)) {
|
| + PrintError(js, "Must specify collection object id: /classes/id");
|
| + return;
|
| + }
|
| ClassTable* table = isolate->class_table();
|
| if (!table->IsValidIndex(id)) {
|
| - Object::null_object().PrintToJSONStream(js, false);
|
| - } else {
|
| - Class& cls = Class::Handle(table->At(id));
|
| + PrintError(js, "%" Pd " is not a valid class id.", id);;
|
| + return;
|
| + }
|
| + Class& cls = Class::Handle(table->At(id));
|
| + if (js->num_arguments() == 2) {
|
| cls.PrintToJSONStream(js, false);
|
| + return;
|
| + } else if (js->num_arguments() >= 3) {
|
| + const char* second = js->GetArgument(2);
|
| + if (!strcmp(second, "closures")) {
|
| + HandleClassesClosures(isolate, cls, js);
|
| + } else if (!strcmp(second, "fields")) {
|
| + HandleClassesFields(isolate, cls, js);
|
| + } else if (!strcmp(second, "functions")) {
|
| + HandleClassesFunctions(isolate, cls, js);
|
| + } else if (!strcmp(second, "implicit_closures")) {
|
| + HandleClassesImplicitClosures(isolate, cls, js);
|
| + } else if (!strcmp(second, "dispatchers")) {
|
| + HandleClassesDispatchers(isolate, cls, js);
|
| + } else {
|
| + PrintError(js, "Invalid sub collection %s", second);
|
| + }
|
| + return;
|
| }
|
| + UNREACHABLE();
|
| }
|
|
|
|
|
| @@ -268,17 +451,109 @@
|
| }
|
|
|
|
|
| +static void HandleLibraries(Isolate* isolate, JSONStream* js) {
|
| + // TODO(johnmccutchan): Support fields and functions on libraries.
|
| + REQUIRE_COLLECTION_ID("libraries");
|
| + const GrowableObjectArray& libs =
|
| + GrowableObjectArray::Handle(isolate->object_store()->libraries());
|
| + ASSERT(!libs.IsNull());
|
| + intptr_t id = 0;
|
| + CHECK_COLLECTION_ID_BOUNDS("libraries", libs.Length(), js->GetArgument(1),
|
| + id, js);
|
| + Library& lib = Library::Handle();
|
| + lib ^= libs.At(id);
|
| + ASSERT(!lib.IsNull());
|
| + lib.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| static void HandleObjects(Isolate* isolate, JSONStream* js) {
|
| REQUIRE_COLLECTION_ID("objects");
|
| ASSERT(js->num_arguments() >= 2);
|
| ObjectIdRing* ring = isolate->object_id_ring();
|
| ASSERT(ring != NULL);
|
| - intptr_t id = atoi(js->GetArgument(1));
|
| + intptr_t id = -1;
|
| + if (!GetIntegerId(js->GetArgument(1), &id)) {
|
| + Object::null_object().PrintToJSONStream(js, false);
|
| + return;
|
| + }
|
| Object& obj = Object::Handle(ring->GetObjectForId(id));
|
| obj.PrintToJSONStream(js, false);
|
| }
|
|
|
|
|
| +
|
| +static void HandleScriptsEnumerate(Isolate* isolate, JSONStream* js) {
|
| + JSONObject jsobj(js);
|
| + jsobj.AddProperty("type", "ScriptList");
|
| + {
|
| + JSONArray members(&jsobj, "members");
|
| + const GrowableObjectArray& libs =
|
| + GrowableObjectArray::Handle(isolate->object_store()->libraries());
|
| + int num_libs = libs.Length();
|
| + Library &lib = Library::Handle();
|
| + Script& script = Script::Handle();
|
| + for (intptr_t i = 0; i < num_libs; i++) {
|
| + lib ^= libs.At(i);
|
| + ASSERT(!lib.IsNull());
|
| + ASSERT(Smi::IsValid(lib.index()));
|
| + const Array& loaded_scripts = Array::Handle(lib.LoadedScripts());
|
| + ASSERT(!loaded_scripts.IsNull());
|
| + intptr_t num_scripts = loaded_scripts.Length();
|
| + for (intptr_t i = 0; i < num_scripts; i++) {
|
| + script ^= loaded_scripts.At(i);
|
| + members.AddValue(script);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +static void HandleScriptsFetch(Isolate* isolate, JSONStream* js) {
|
| + const GrowableObjectArray& libs =
|
| + GrowableObjectArray::Handle(isolate->object_store()->libraries());
|
| + int num_libs = libs.Length();
|
| + Library &lib = Library::Handle();
|
| + Script& script = Script::Handle();
|
| + String& url = String::Handle();
|
| + const String& id = String::Handle(String::New(js->GetArgument(1)));
|
| + ASSERT(!id.IsNull());
|
| + // The id is the url of the script % encoded, decode it.
|
| + String& requested_url = String::Handle(String::DecodeURI(id));
|
| + for (intptr_t i = 0; i < num_libs; i++) {
|
| + lib ^= libs.At(i);
|
| + ASSERT(!lib.IsNull());
|
| + ASSERT(Smi::IsValid(lib.index()));
|
| + const Array& loaded_scripts = Array::Handle(lib.LoadedScripts());
|
| + ASSERT(!loaded_scripts.IsNull());
|
| + intptr_t num_scripts = loaded_scripts.Length();
|
| + for (intptr_t i = 0; i < num_scripts; i++) {
|
| + script ^= loaded_scripts.At(i);
|
| + ASSERT(!script.IsNull());
|
| + url ^= script.url();
|
| + if (url.Equals(requested_url)) {
|
| + script.PrintToJSONStream(js, false);
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + PrintError(js, "Cannot find script %s\n", requested_url.ToCString());
|
| +}
|
| +
|
| +
|
| +static void HandleScripts(Isolate* isolate, JSONStream* js) {
|
| + if (js->num_arguments() == 1) {
|
| + // Enumerate all scripts.
|
| + HandleScriptsEnumerate(isolate, js);
|
| + } else if (js->num_arguments() == 2) {
|
| + // Fetch specific script.
|
| + HandleScriptsFetch(isolate, js);
|
| + } else {
|
| + PrintError(js, "Command too long");
|
| + }
|
| +}
|
| +
|
| +
|
| static void HandleDebug(Isolate* isolate, JSONStream* js) {
|
| if (js->num_arguments() == 1) {
|
| PrintError(js, "Must specify a subcommand");
|
| @@ -295,8 +570,11 @@
|
|
|
| } else if (js->num_arguments() == 3) {
|
| // Print individual breakpoint.
|
| - intptr_t id = atoi(js->GetArgument(2));
|
| - SourceBreakpoint* bpt = isolate->debugger()->GetBreakpointById(id);
|
| + intptr_t id = 0;
|
| + SourceBreakpoint* bpt = NULL;
|
| + if (GetIntegerId(js->GetArgument(2), &id)) {
|
| + bpt = isolate->debugger()->GetBreakpointById(id);
|
| + }
|
| if (bpt != NULL) {
|
| bpt->PrintToJSONStream(js);
|
| } else {
|
| @@ -319,15 +597,40 @@
|
| }
|
|
|
|
|
| +static void HandleCode(Isolate* isolate, JSONStream* js) {
|
| + REQUIRE_COLLECTION_ID("code");
|
| + uintptr_t pc;
|
| + if (!GetUnsignedIntegerId(js->GetArgument(1), &pc, 16)) {
|
| + PrintError(js, "Must specify code address: code/c0deadd0.");
|
| + return;
|
| + }
|
| + Code& code = Code::Handle(Code::LookupCode(pc));
|
| + if (code.IsNull()) {
|
| + PrintError(js, "Could not find code at %" Px "", pc);
|
| + return;
|
| + }
|
| + code.PrintToJSONStream(js, false);
|
| +}
|
| +
|
| +
|
| +static void HandleProfile(Isolate* isolate, JSONStream* js) {
|
| + Profiler::PrintToJSONStream(isolate, js, true);
|
| +}
|
| +
|
| +
|
| static ServiceMessageHandlerEntry __message_handlers[] = {
|
| { "_echo", HandleEcho },
|
| { "classes", HandleClasses },
|
| + { "code", HandleCode },
|
| { "cpu", HandleCpu },
|
| { "debug", HandleDebug },
|
| + { "libraries", HandleLibraries },
|
| { "library", HandleLibrary },
|
| { "name", HandleName },
|
| { "objecthistogram", HandleObjectHistogram},
|
| { "objects", HandleObjects },
|
| + { "profile", HandleProfile },
|
| + { "scripts", HandleScripts },
|
| { "stacktrace", HandleStackTrace },
|
| };
|
|
|
|
|