| // Copyright (c) 2013, 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 "vm/debugger.h" |
| #include "vm/heap_histogram.h" |
| #include "vm/isolate.h" |
| #include "vm/message.h" |
| #include "vm/object.h" |
| #include "vm/object_id_ring.h" |
| #include "vm/object_store.h" |
| #include "vm/port.h" |
| #include "vm/service.h" |
| |
| namespace dart { |
| |
| typedef void (*ServiceMessageHandler)(Isolate* isolate, JSONStream* stream); |
| |
| struct ServiceMessageHandlerEntry { |
| const char* command; |
| ServiceMessageHandler handler; |
| }; |
| |
| static ServiceMessageHandler FindServiceMessageHandler(const char* command); |
| |
| |
| static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { |
| void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); |
| return reinterpret_cast<uint8_t*>(new_ptr); |
| } |
| |
| |
| static void PostReply(const String& reply, Dart_Port reply_port) { |
| uint8_t* data = NULL; |
| MessageWriter writer(&data, &allocator); |
| writer.WriteMessage(reply); |
| PortMap::PostMessage(new Message(reply_port, Message::kIllegalPort, data, |
| writer.BytesWritten(), |
| Message::kNormalPriority)); |
| } |
| |
| |
| void Service::HandleServiceMessage(Isolate* isolate, Dart_Port reply_port, |
| const Instance& msg) { |
| ASSERT(isolate != NULL); |
| ASSERT(reply_port != ILLEGAL_PORT); |
| ASSERT(!msg.IsNull()); |
| ASSERT(msg.IsGrowableObjectArray()); |
| |
| { |
| StackZone zone(isolate); |
| HANDLESCOPE(isolate); |
| const GrowableObjectArray& message = GrowableObjectArray::Cast(msg); |
| // Message is a list with three entries. |
| ASSERT(message.Length() == 3); |
| |
| GrowableObjectArray& path = GrowableObjectArray::Handle(); |
| GrowableObjectArray& option_keys = GrowableObjectArray::Handle(); |
| GrowableObjectArray& option_values = GrowableObjectArray::Handle(); |
| path ^= message.At(0); |
| option_keys ^= message.At(1); |
| option_values ^= message.At(2); |
| |
| ASSERT(!path.IsNull()); |
| ASSERT(!option_keys.IsNull()); |
| ASSERT(!option_values.IsNull()); |
| // Path always has at least one entry in it. |
| ASSERT(path.Length() > 0); |
| // Same number of option keys as values. |
| ASSERT(option_keys.Length() == option_values.Length()); |
| |
| String& pathSegment = String::Handle(); |
| pathSegment ^= path.At(0); |
| ASSERT(!pathSegment.IsNull()); |
| |
| ServiceMessageHandler handler = |
| FindServiceMessageHandler(pathSegment.ToCString()); |
| ASSERT(handler != NULL); |
| { |
| JSONStream js; |
| |
| // Setup JSONStream arguments and options. The arguments and options |
| // are zone allocated and will be freed immediately after handling the |
| // message. |
| Zone* zoneAllocator = zone.GetZone(); |
| const char** arguments = zoneAllocator->Alloc<const char*>(path.Length()); |
| String& string_iterator = String::Handle(); |
| for (intptr_t i = 0; i < path.Length(); i++) { |
| string_iterator ^= path.At(i); |
| arguments[i] = |
| zoneAllocator->MakeCopyOfString(string_iterator.ToCString()); |
| } |
| js.SetArguments(arguments, path.Length()); |
| if (option_keys.Length() > 0) { |
| const char** option_keys_native = |
| zoneAllocator->Alloc<const char*>(option_keys.Length()); |
| const char** option_values_native = |
| zoneAllocator->Alloc<const char*>(option_keys.Length()); |
| for (intptr_t i = 0; i < option_keys.Length(); i++) { |
| string_iterator ^= option_keys.At(i); |
| option_keys_native[i] = |
| zoneAllocator->MakeCopyOfString(string_iterator.ToCString()); |
| string_iterator ^= option_values.At(i); |
| option_values_native[i] = |
| zoneAllocator->MakeCopyOfString(string_iterator.ToCString()); |
| } |
| js.SetOptions(option_keys_native, option_values_native, |
| option_keys.Length()); |
| } |
| |
| handler(isolate, &js); |
| const String& reply = String::Handle(String::New(js.ToCString())); |
| ASSERT(!reply.IsNull()); |
| PostReply(reply, reply_port); |
| } |
| } |
| } |
| |
| |
| static void PrintArgumentsAndOptions(const JSONObject& obj, JSONStream* js) { |
| JSONObject jsobj(&obj, "message"); |
| { |
| JSONArray jsarr(&jsobj, "arguments"); |
| for (intptr_t i = 0; i < js->num_arguments(); i++) { |
| jsarr.AddValue(js->GetArgument(i)); |
| } |
| } |
| { |
| JSONArray jsarr(&jsobj, "option_keys"); |
| for (intptr_t i = 0; i < js->num_options(); i++) { |
| jsarr.AddValue(js->GetOptionKey(i)); |
| } |
| } |
| { |
| JSONArray jsarr(&jsobj, "option_values"); |
| for (intptr_t i = 0; i < js->num_options(); i++) { |
| jsarr.AddValue(js->GetOptionValue(i)); |
| } |
| } |
| } |
| |
| |
| static void PrintCollectionErrorResponse(const char* collection_name, |
| JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "error"); |
| jsobj.AddPropertyF("text", "Must specify collection object id: /%s/id", |
| collection_name); |
| } |
| |
| |
| static void PrintGenericError(JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "error"); |
| jsobj.AddProperty("text", "Invalid request."); |
| PrintArgumentsAndOptions(jsobj, js); |
| } |
| |
| |
| static void HandleName(Isolate* isolate, JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "IsolateName"); |
| jsobj.AddProperty("id", static_cast<intptr_t>(isolate->main_port())); |
| jsobj.AddProperty("name", isolate->name()); |
| } |
| |
| |
| static void HandleStackTrace(Isolate* isolate, JSONStream* js) { |
| DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "StackTrace"); |
| JSONArray jsarr(&jsobj, "members"); |
| intptr_t n_frames = stack->Length(); |
| String& url = String::Handle(); |
| String& function = String::Handle(); |
| for (int i = 0; i < n_frames; i++) { |
| ActivationFrame* frame = stack->FrameAt(i); |
| url ^= frame->SourceUrl(); |
| function ^= frame->function().UserVisibleName(); |
| JSONObject jsobj(&jsarr); |
| jsobj.AddProperty("name", function.ToCString()); |
| jsobj.AddProperty("url", url.ToCString()); |
| jsobj.AddProperty("line", frame->LineNumber()); |
| jsobj.AddProperty("function", frame->function()); |
| jsobj.AddProperty("code", frame->code()); |
| } |
| } |
| |
| |
| static void HandleObjectHistogram(Isolate* isolate, JSONStream* js) { |
| ObjectHistogram* histogram = Isolate::Current()->object_histogram(); |
| if (histogram == NULL) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "error"); |
| jsobj.AddProperty("text", "Run with --print_object_histogram"); |
| return; |
| } |
| histogram->PrintToJSONStream(js); |
| } |
| |
| |
| static void HandleEcho(Isolate* isolate, JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "message"); |
| PrintArgumentsAndOptions(jsobj, js); |
| } |
| |
| |
| // Print an error message if there is no ID argument. |
| #define REQUIRE_COLLECTION_ID(collection) \ |
| if (js->num_arguments() == 1) { \ |
| PrintCollectionErrorResponse(collection, js); \ |
| return; \ |
| } |
| |
| |
| static void HandleClasses(Isolate* isolate, JSONStream* js) { |
| if (js->num_arguments() == 1) { |
| ClassTable* table = isolate->class_table(); |
| table->PrintToJSONStream(js); |
| return; |
| } |
| ASSERT(js->num_arguments() >= 2); |
| intptr_t id = atoi(js->GetArgument(1)); |
| ClassTable* table = isolate->class_table(); |
| if (!table->IsValidIndex(id)) { |
| Object::null_object().PrintToJSONStream(js, false); |
| } else { |
| Class& cls = Class::Handle(table->At(id)); |
| cls.PrintToJSONStream(js, false); |
| } |
| } |
| |
| |
| static void HandleLibrary(Isolate* isolate, JSONStream* js) { |
| if (js->num_arguments() == 1) { |
| const Library& lib = |
| Library::Handle(isolate->object_store()->root_library()); |
| lib.PrintToJSONStream(js, false); |
| return; |
| } |
| PrintGenericError(js); |
| } |
| |
| |
| 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)); |
| Object& obj = Object::Handle(ring->GetObjectForId(id)); |
| obj.PrintToJSONStream(js, false); |
| } |
| |
| |
| static ServiceMessageHandlerEntry __message_handlers[] = { |
| { "name", HandleName }, |
| { "stacktrace", HandleStackTrace }, |
| { "objecthistogram", HandleObjectHistogram}, |
| { "library", HandleLibrary }, |
| { "classes", HandleClasses }, |
| { "objects", HandleObjects }, |
| { "_echo", HandleEcho }, |
| }; |
| |
| |
| static void HandleFallthrough(Isolate* isolate, JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "error"); |
| jsobj.AddProperty("text", "request not understood."); |
| PrintArgumentsAndOptions(jsobj, js); |
| } |
| |
| |
| static ServiceMessageHandler FindServiceMessageHandler(const char* command) { |
| intptr_t num_message_handlers = sizeof(__message_handlers) / |
| sizeof(__message_handlers[0]); |
| for (intptr_t i = 0; i < num_message_handlers; i++) { |
| const ServiceMessageHandlerEntry& entry = __message_handlers[i]; |
| if (!strcmp(command, entry.command)) { |
| return entry.handler; |
| } |
| } |
| return HandleFallthrough; |
| } |
| |
| } // namespace dart |