Index: runtime/vm/service.cc |
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc |
index c05ba8c277015cf11625e02ac3e5b3863fb19227..f60cd9655fd3dfc6ce6ee7736c0810900f7a05aa 100644 |
--- a/runtime/vm/service.cc |
+++ b/runtime/vm/service.cc |
@@ -27,6 +27,7 @@ |
#include "vm/port.h" |
#include "vm/profiler_service.h" |
#include "vm/reusable_handles.h" |
+#include "vm/service_isolate.h" |
#include "vm/stack_frame.h" |
#include "vm/symbols.h" |
#include "vm/unicode.h" |
@@ -34,962 +35,686 @@ |
namespace dart { |
-DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests."); |
-DEFINE_FLAG(bool, trace_service_pause_events, false, |
- "Trace VM service isolate pause events."); |
+DECLARE_FLAG(bool, trace_service); |
+DECLARE_FLAG(bool, trace_service_pause_events); |
DECLARE_FLAG(bool, enable_type_checks); |
DECLARE_FLAG(bool, enable_asserts); |
-struct ResourcesEntry { |
- const char* path_; |
- const char* resource_; |
- int length_; |
-}; |
- |
-extern ResourcesEntry __service_resources_[]; |
- |
-class Resources { |
- public: |
- static const int kNoSuchInstance = -1; |
- static int ResourceLookup(const char* path, const char** resource) { |
- ResourcesEntry* table = ResourceTable(); |
- for (int i = 0; table[i].path_ != NULL; i++) { |
- const ResourcesEntry& entry = table[i]; |
- if (strcmp(path, entry.path_) == 0) { |
- *resource = entry.resource_; |
- ASSERT(entry.length_ > 0); |
- return entry.length_; |
- } |
- } |
- return kNoSuchInstance; |
- } |
- |
- static const char* Path(int idx) { |
- ASSERT(idx >= 0); |
- ResourcesEntry* entry = At(idx); |
- if (entry == NULL) { |
- return NULL; |
- } |
- ASSERT(entry->path_ != NULL); |
- return entry->path_; |
- } |
+// TODO(johnmccutchan): Unify embedder service handler lists and their APIs. |
+EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; |
+EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; |
+uint32_t Service::event_mask_ = 0; |
+struct ServiceMethodDescriptor; |
+ServiceMethodDescriptor* FindMethod(const char* method_name); |
- static int Length(int idx) { |
- ASSERT(idx >= 0); |
- ResourcesEntry* entry = At(idx); |
- if (entry == NULL) { |
- return kNoSuchInstance; |
- } |
- ASSERT(entry->path_ != NULL); |
- return entry->length_; |
- } |
+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 const uint8_t* Resource(int idx) { |
- ASSERT(idx >= 0); |
- ResourcesEntry* entry = At(idx); |
- if (entry == NULL) { |
- return NULL; |
+static void PrintRequest(const JSONObject& obj, JSONStream* js) { |
+ JSONObject jsobj(&obj, "request"); |
+ jsobj.AddProperty("method", js->method()); |
+ { |
+ JSONArray jsarr(&jsobj, "param_keys"); |
+ for (intptr_t i = 0; i < js->num_params(); i++) { |
+ jsarr.AddValue(js->GetParamKey(i)); |
} |
- return reinterpret_cast<const uint8_t*>(entry->resource_); |
} |
- |
- private: |
- static ResourcesEntry* At(int idx) { |
- ASSERT(idx >= 0); |
- ResourcesEntry* table = ResourceTable(); |
- for (int i = 0; table[i].path_ != NULL; i++) { |
- if (idx == i) { |
- return &table[i]; |
- } |
+ { |
+ JSONArray jsarr(&jsobj, "param_values"); |
+ for (intptr_t i = 0; i < js->num_params(); i++) { |
+ jsarr.AddValue(js->GetParamValue(i)); |
} |
- return NULL; |
- } |
- |
- static ResourcesEntry* ResourceTable() { |
- return &__service_resources_[0]; |
- } |
- |
- DISALLOW_ALLOCATION(); |
- DISALLOW_IMPLICIT_CONSTRUCTORS(Resources); |
-}; |
- |
- |
-class EmbedderServiceHandler { |
- public: |
- explicit EmbedderServiceHandler(const char* name) : name_(NULL), |
- callback_(NULL), |
- user_data_(NULL), |
- next_(NULL) { |
- ASSERT(name != NULL); |
- name_ = strdup(name); |
- } |
- |
- ~EmbedderServiceHandler() { |
- free(name_); |
- } |
- |
- const char* name() const { return name_; } |
- |
- Dart_ServiceRequestCallback callback() const { return callback_; } |
- void set_callback(Dart_ServiceRequestCallback callback) { |
- callback_ = callback; |
- } |
- |
- void* user_data() const { return user_data_; } |
- void set_user_data(void* user_data) { |
- user_data_ = user_data; |
- } |
- |
- EmbedderServiceHandler* next() const { return next_; } |
- void set_next(EmbedderServiceHandler* next) { |
- next_ = next; |
} |
+} |
- private: |
- char* name_; |
- Dart_ServiceRequestCallback callback_; |
- void* user_data_; |
- EmbedderServiceHandler* next_; |
-}; |
+static void PrintError(JSONStream* js, |
+ const char* format, ...) { |
+ Isolate* isolate = Isolate::Current(); |
-class LibraryCoverageFilter : public CoverageFilter { |
- public: |
- explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} |
- bool ShouldOutputCoverageFor(const Library& lib, |
- const Script& script, |
- const Class& cls, |
- const Function& func) const { |
- return lib.raw() == lib_.raw(); |
- } |
- private: |
- const Library& lib_; |
-}; |
+ va_list args; |
+ va_start(args, format); |
+ intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
+ va_end(args); |
+ char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
+ va_list args2; |
+ va_start(args2, format); |
+ OS::VSNPrint(buffer, (len + 1), format, args2); |
+ va_end(args2); |
-class ScriptCoverageFilter : public CoverageFilter { |
- public: |
- explicit ScriptCoverageFilter(const Script& script) |
- : script_(script) {} |
- bool ShouldOutputCoverageFor(const Library& lib, |
- const Script& script, |
- const Class& cls, |
- const Function& func) const { |
- return script.raw() == script_.raw(); |
- } |
- private: |
- const Script& script_; |
-}; |
+ JSONObject jsobj(js); |
+ jsobj.AddProperty("type", "Error"); |
+ jsobj.AddProperty("message", buffer); |
+ PrintRequest(jsobj, js); |
+} |
-class ClassCoverageFilter : public CoverageFilter { |
- public: |
- explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} |
- bool ShouldOutputCoverageFor(const Library& lib, |
- const Script& script, |
- const Class& cls, |
- const Function& func) const { |
- return cls.raw() == cls_.raw(); |
- } |
- private: |
- const Class& cls_; |
-}; |
+static void PrintMissingParamError(JSONStream* js, |
+ const char* param) { |
+ PrintError(js, "%s expects the '%s' parameter", |
+ js->method(), param); |
+} |
-class FunctionCoverageFilter : public CoverageFilter { |
- public: |
- explicit FunctionCoverageFilter(const Function& func) : func_(func) {} |
- bool ShouldOutputCoverageFor(const Library& lib, |
- const Script& script, |
- const Class& cls, |
- const Function& func) const { |
- return func.raw() == func_.raw(); |
- } |
- private: |
- const Function& func_; |
-}; |
+static void PrintInvalidParamError(JSONStream* js, |
+ const char* param) { |
+ PrintError(js, "%s: invalid '%s' parameter: %s", |
+ js->method(), param, js->LookupParam(param)); |
+} |
-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 PrintUnrecognizedMethodError(JSONStream* js) { |
+ PrintError(js, "unrecognized method: %s", js->method()); |
} |
-static void SendIsolateServiceMessage(Dart_NativeArguments args) { |
- NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
- Isolate* isolate = arguments->isolate(); |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- GET_NON_NULL_NATIVE_ARGUMENT(SendPort, sp, arguments->NativeArgAt(0)); |
- GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(1)); |
+static void PrintErrorWithKind(JSONStream* js, |
+ const char* kind, |
+ const char* format, ...) { |
+ Isolate* isolate = Isolate::Current(); |
- // Set the type of the OOB message. |
- message.SetAt(0, Smi::Handle(isolate, Smi::New(Message::kServiceOOBMsg))); |
+ va_list args; |
+ va_start(args, format); |
+ intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
+ va_end(args); |
- // Serialize message. |
- uint8_t* data = NULL; |
- MessageWriter writer(&data, &allocator, false); |
- writer.WriteMessage(message); |
+ char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
+ va_list args2; |
+ va_start(args2, format); |
+ OS::VSNPrint(buffer, (len + 1), format, args2); |
+ va_end(args2); |
- // TODO(turnidge): Throw an exception when the return value is false? |
- PortMap::PostMessage(new Message(sp.Id(), data, writer.BytesWritten(), |
- Message::kOOBPriority)); |
+ JSONObject jsobj(js); |
+ jsobj.AddProperty("type", "Error"); |
+ jsobj.AddProperty("id", ""); |
+ jsobj.AddProperty("kind", kind); |
+ jsobj.AddProperty("message", buffer); |
+ PrintRequest(jsobj, js); |
} |
-static void SendRootServiceMessage(Dart_NativeArguments args) { |
- NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
- Isolate* isolate = arguments->isolate(); |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(0)); |
- Service::HandleRootMessage(message); |
+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; |
} |
-class ScopeStopwatch : public ValueObject { |
- public: |
- explicit ScopeStopwatch(const char* name) : name_(name) { |
- start_ = OS::GetCurrentTimeMicros(); |
+static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) { |
+ if ((s == NULL) || (*s == '\0')) { |
+ // Empty string. |
+ return false; |
} |
- |
- int64_t GetElapsed() const { |
- int64_t end = OS::GetCurrentTimeMicros(); |
- ASSERT(end >= start_); |
- return end - start_; |
+ if (id == NULL) { |
+ // No id pointer. |
+ return false; |
} |
- |
- ~ScopeStopwatch() { |
- int64_t elapsed = GetElapsed(); |
- OS::Print("[%" Pd "] %s took %" Pd64 " micros.\n", |
- OS::ProcessId(), name_, elapsed); |
+ 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; |
} |
- |
- private: |
- const char* name_; |
- int64_t start_; |
-}; |
- |
- |
-bool Service::IsRunning() { |
- MonitorLocker ml(monitor_); |
- return (service_port_ != ILLEGAL_PORT) && (service_isolate_ != NULL); |
+ *id = r; |
+ return true; |
} |
-void Service::SetServicePort(Dart_Port port) { |
- MonitorLocker ml(monitor_); |
- service_port_ = port; |
+static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { |
+ if ((s == NULL) || (*s == '\0')) { |
+ // Empty string. |
+ return false; |
+ } |
+ if (id == NULL) { |
+ // No id pointer. |
+ return false; |
+ } |
+ int64_t r = 0; |
+ char* end_ptr = NULL; |
+ r = strtoll(s, &end_ptr, base); |
+ if (end_ptr == s) { |
+ // String was not advanced at all, cannot be valid. |
+ return false; |
+ } |
+ *id = r; |
+ return true; |
} |
-void Service::SetServiceIsolate(Isolate* isolate) { |
- MonitorLocker ml(monitor_); |
- service_isolate_ = isolate; |
- if (service_isolate_ != NULL) { |
- service_isolate_->is_service_isolate_ = true; |
+// Scans the string until the '-' character. Returns pointer to string |
+// at '-' character. Returns NULL if not found. |
+static const char* ScanUntilDash(const char* s) { |
+ if ((s == NULL) || (*s == '\0')) { |
+ // Empty string. |
+ return NULL; |
+ } |
+ while (*s != '\0') { |
+ if (*s == '-') { |
+ return s; |
+ } |
+ s++; |
} |
+ return NULL; |
} |
-bool Service::HasServiceIsolate() { |
- MonitorLocker ml(monitor_); |
- return service_isolate_ != NULL; |
+static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { |
+ if ((s == NULL) || (*s == '\0')) { |
+ // Empty string. |
+ return false; |
+ } |
+ if ((timestamp == NULL) || (address == NULL)) { |
+ // Bad arguments. |
+ return false; |
+ } |
+ // Extract the timestamp. |
+ if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { |
+ return false; |
+ } |
+ s = ScanUntilDash(s); |
+ if (s == NULL) { |
+ return false; |
+ } |
+ // Skip the dash. |
+ s++; |
+ // Extract the PC. |
+ if (!GetUnsignedIntegerId(s, address, 16)) { |
+ return false; |
+ } |
+ return true; |
} |
-bool Service::IsServiceIsolate(Isolate* isolate) { |
- MonitorLocker ml(monitor_); |
- return isolate == service_isolate_; |
-} |
+// TODO(johnmccutchan): Split into separate file and write unit tests. |
+class MethodParameter { |
+ public: |
+ MethodParameter(const char* name, bool required) |
+ : name_(name), required_(required) { |
+ } |
-Dart_Port Service::WaitForLoadPort() { |
- MonitorLocker ml(monitor_); |
+ virtual ~MethodParameter() { } |
- while (initializing_ && (load_port_ == ILLEGAL_PORT)) { |
- ml.Wait(); |
+ virtual bool Validate(const char* value) const { |
+ return true; |
} |
- return load_port_; |
-} |
- |
+ const char* name() const { |
+ return name_; |
+ } |
-Dart_Port Service::LoadPort() { |
- MonitorLocker ml(monitor_); |
- return load_port_; |
-} |
+ bool required() const { |
+ return required_; |
+ } |
+ private: |
+ const char* name_; |
+ bool required_; |
+}; |
-void Service::SetLoadPort(Dart_Port port) { |
- MonitorLocker ml(monitor_); |
- load_port_ = port; |
-} |
+class NoSuchParameter : public MethodParameter { |
+ public: |
+ explicit NoSuchParameter(const char* name) |
+ : MethodParameter(name, false) { |
+ } |
-void Service::SetEventMask(uint32_t mask) { |
- event_mask_ = mask; |
-} |
+ virtual bool Validate(const char* value) const { |
+ return (value == NULL); |
+ } |
+}; |
-static void SetEventMask(Dart_NativeArguments args) { |
- NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
- Isolate* isolate = arguments->isolate(); |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- GET_NON_NULL_NATIVE_ARGUMENT(Integer, mask, arguments->NativeArgAt(0)); |
- Service::SetEventMask(mask.AsTruncatedUint32Value()); |
-} |
+#define NO_ISOLATE_PARAMETER new NoSuchParameter("isolateId") |
-// These must be kept in sync with service/constants.dart |
-#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1 |
-#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2 |
+class BoolParameter : public MethodParameter { |
+ public: |
+ BoolParameter(const char* name, bool required) |
+ : MethodParameter(name, required) { |
+ } |
+ virtual bool Validate(const char* value) const { |
+ if (value == NULL) { |
+ return false; |
+ } |
+ return (strcmp("true", value) == 0) || (strcmp("false", value) == 0); |
+ } |
-static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code, |
- const String& name) { |
- const Array& list = Array::Handle(Array::New(4)); |
- ASSERT(!list.IsNull()); |
- const Integer& code_int = Integer::Handle(Integer::New(code)); |
- const Integer& port_int = Integer::Handle(Integer::New(port_id)); |
- const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); |
- list.SetAt(0, code_int); |
- list.SetAt(1, port_int); |
- list.SetAt(2, send_port); |
- list.SetAt(3, name); |
- return list.raw(); |
-} |
+ static bool Interpret(const char* value) { |
+ return strcmp("true", value) == 0; |
+ } |
+}; |
-class RegisterRunningIsolatesVisitor : public IsolateVisitor { |
+class IdParameter : public MethodParameter { |
public: |
- explicit RegisterRunningIsolatesVisitor(Isolate* service_isolate) |
- : IsolateVisitor(), |
- register_function_(Function::Handle(service_isolate)), |
- service_isolate_(service_isolate) { |
- ASSERT(Service::IsServiceIsolate(Isolate::Current())); |
- // Get library. |
- const String& library_url = Symbols::DartVMService(); |
- ASSERT(!library_url.IsNull()); |
- const Library& library = |
- Library::Handle(Library::LookupLibrary(library_url)); |
- ASSERT(!library.IsNull()); |
- // Get function. |
- const String& function_name = |
- String::Handle(String::New("_registerIsolate")); |
- ASSERT(!function_name.IsNull()); |
- register_function_ = library.LookupFunctionAllowPrivate(function_name); |
- ASSERT(!register_function_.IsNull()); |
- } |
- |
- virtual void VisitIsolate(Isolate* isolate) { |
- ASSERT(Service::IsServiceIsolate(Isolate::Current())); |
- if (Service::IsServiceIsolate(isolate) || |
- (isolate == Dart::vm_isolate())) { |
- // We do not register the service or vm isolate. |
- return; |
- } |
- // Setup arguments for call. |
- Dart_Port port_id = isolate->main_port(); |
- const Integer& port_int = Integer::Handle(Integer::New(port_id)); |
- ASSERT(!port_int.IsNull()); |
- const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); |
- const String& name = String::Handle(String::New(isolate->name())); |
- ASSERT(!name.IsNull()); |
- const Array& args = Array::Handle(Array::New(3)); |
- ASSERT(!args.IsNull()); |
- args.SetAt(0, port_int); |
- args.SetAt(1, send_port); |
- args.SetAt(2, name); |
- Object& r = Object::Handle(service_isolate_); |
- r = DartEntry::InvokeFunction(register_function_, args); |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Isolate %s %" Pd64 " registered.\n", |
- name.ToCString(), |
- port_id); |
- } |
- ASSERT(!r.IsError()); |
+ IdParameter(const char* name, bool required) |
+ : MethodParameter(name, required) { |
} |
- private: |
- Function& register_function_; |
- Isolate* service_isolate_; |
+ virtual bool Validate(const char* value) const { |
+ return (value != NULL); |
+ } |
}; |
-static Dart_Port ExtractPort(Isolate* isolate, Dart_Handle receivePort) { |
- const ReceivePort& rp = Api::UnwrapReceivePortHandle(isolate, receivePort); |
- if (rp.IsNull()) { |
- return ILLEGAL_PORT; |
- } |
- return rp.Id(); |
-} |
+#define ISOLATE_PARAMETER new IdParameter("isolateId", true) |
-static void OnStart(Dart_NativeArguments args) { |
- NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
- Isolate* isolate = arguments->isolate(); |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- { |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Booting dart:vmservice library.\n"); |
- } |
- // Boot the dart:vmservice library. |
- Dart_EnterScope(); |
- Dart_Handle url_str = |
- Dart_NewStringFromCString(Symbols::Name(Symbols::kDartVMServiceId)); |
- Dart_Handle library = Dart_LookupLibrary(url_str); |
- ASSERT(Dart_IsLibrary(library)); |
- Dart_Handle result = |
- Dart_Invoke(library, Dart_NewStringFromCString("boot"), 0, NULL); |
- ASSERT(!Dart_IsError(result)); |
- Dart_Port port = ExtractPort(isolate, result); |
- ASSERT(port != ILLEGAL_PORT); |
- Service::SetServicePort(port); |
- Dart_ExitScope(); |
+class EnumParameter : public MethodParameter { |
+ public: |
+ EnumParameter(const char* name, bool required, const char** enums) |
+ : MethodParameter(name, required), |
+ enums_(enums) { |
} |
- { |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Registering running isolates.\n"); |
+ virtual bool Validate(const char* value) const { |
+ if (value == NULL) { |
+ return true; |
} |
- // Register running isolates with service. |
- RegisterRunningIsolatesVisitor register_isolates(isolate); |
- Isolate::VisitIsolates(®ister_isolates); |
+ for (intptr_t i = 0; enums_[i] != NULL; i++) { |
+ if (strcmp(value, enums_[i]) == 0) { |
+ return true; |
+ } |
+ } |
+ return false; |
} |
-} |
- |
- |
-struct VmServiceNativeEntry { |
- const char* name; |
- int num_arguments; |
- Dart_NativeFunction function; |
-}; |
- |
-static VmServiceNativeEntry _VmServiceNativeEntries[] = { |
- {"VMService_SendIsolateServiceMessage", 2, SendIsolateServiceMessage}, |
- {"VMService_SendRootServiceMessage", 1, SendRootServiceMessage}, |
- {"VMService_SetEventMask", 1, SetEventMask}, |
- {"VMService_OnStart", 0, OnStart }, |
+ private: |
+ const char** enums_; |
}; |
-static Dart_NativeFunction VmServiceNativeResolver(Dart_Handle name, |
- int num_arguments, |
- bool* auto_setup_scope) { |
- const Object& obj = Object::Handle(Api::UnwrapHandle(name)); |
- if (!obj.IsString()) { |
- return NULL; |
- } |
- const char* function_name = obj.ToCString(); |
- ASSERT(function_name != NULL); |
- ASSERT(auto_setup_scope != NULL); |
- *auto_setup_scope = true; |
- intptr_t n = |
- sizeof(_VmServiceNativeEntries) / sizeof(_VmServiceNativeEntries[0]); |
- for (intptr_t i = 0; i < n; i++) { |
- VmServiceNativeEntry entry = _VmServiceNativeEntries[i]; |
- if ((strcmp(function_name, entry.name) == 0) && |
- (num_arguments == entry.num_arguments)) { |
- return entry.function; |
+// If the key is not found, this function returns the last element in the |
+// values array. This can be used to encode the default value. |
+template<typename T> |
+T EnumMapper(const char* value, const char** enums, T* values) { |
+ ASSERT(value != NULL); |
+ intptr_t i = 0; |
+ for (i = 0; enums[i] != NULL; i++) { |
+ if (strcmp(value, enums[i]) == 0) { |
+ return values[i]; |
} |
} |
- return NULL; |
+ // Default value. |
+ return values[i]; |
} |
-const char* Service::kIsolateName = "vm-service"; |
-EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; |
-EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; |
-Isolate* Service::service_isolate_ = NULL; |
-Dart_Port Service::service_port_ = ILLEGAL_PORT; |
-Dart_Port Service::load_port_ = ILLEGAL_PORT; |
-Dart_IsolateCreateCallback Service::create_callback_ = NULL; |
-Monitor* Service::monitor_ = NULL; |
-bool Service::initializing_ = true; |
-uint32_t Service::event_mask_ = 0; |
- |
-bool Service::IsServiceIsolateName(const char* name) { |
- ASSERT(name != NULL); |
- return strcmp(name, kIsolateName) == 0; |
-} |
+typedef bool (*ServiceMethodEntry)(Isolate* isolate, JSONStream* js); |
-bool Service::SendIsolateStartupMessage() { |
- if (!IsRunning()) { |
- return false; |
- } |
- Isolate* isolate = Isolate::Current(); |
- if (IsServiceIsolate(isolate)) { |
- return false; |
- } |
- ASSERT(isolate != NULL); |
- HANDLESCOPE(isolate); |
- const String& name = String::Handle(String::New(isolate->name())); |
- ASSERT(!name.IsNull()); |
- const Array& list = Array::Handle( |
- MakeServiceControlMessage(Dart_GetMainPortId(), |
- VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID, |
- name)); |
- ASSERT(!list.IsNull()); |
- uint8_t* data = NULL; |
- MessageWriter writer(&data, &allocator, false); |
- writer.WriteMessage(list); |
- intptr_t len = writer.BytesWritten(); |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Isolate %s %" Pd64 " registered.\n", |
- name.ToCString(), |
- Dart_GetMainPortId()); |
- } |
- return PortMap::PostMessage( |
- new Message(service_port_, data, len, Message::kNormalPriority)); |
-} |
+struct ServiceMethodDescriptor { |
+ const char* name; |
+ const ServiceMethodEntry entry; |
+ const MethodParameter* const * parameters; |
+}; |
-bool Service::SendIsolateShutdownMessage() { |
- if (!IsRunning()) { |
- return false; |
- } |
- Isolate* isolate = Isolate::Current(); |
- if (IsServiceIsolate(isolate)) { |
- return false; |
- } |
- ASSERT(isolate != NULL); |
- HANDLESCOPE(isolate); |
- const String& name = String::Handle(String::New(isolate->name())); |
- ASSERT(!name.IsNull()); |
- const Array& list = Array::Handle( |
- MakeServiceControlMessage(Dart_GetMainPortId(), |
- VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID, |
- name)); |
- ASSERT(!list.IsNull()); |
- uint8_t* data = NULL; |
- MessageWriter writer(&data, &allocator, false); |
- writer.WriteMessage(list); |
- intptr_t len = writer.BytesWritten(); |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Isolate %s %" Pd64 " deregistered.\n", |
- name.ToCString(), |
- Dart_GetMainPortId()); |
+// TODO(johnmccutchan): Do we reject unexpected parameters? |
+static bool ValidateParameters(const MethodParameter* const* parameters, |
+ JSONStream* js) { |
+ if (parameters == NULL) { |
+ return true; |
} |
- return PortMap::PostMessage( |
- new Message(service_port_, data, len, Message::kNormalPriority)); |
-} |
- |
- |
-Dart_Handle Service::GetSource(const char* name) { |
- ASSERT(name != NULL); |
- int i = 0; |
- while (true) { |
- const char* path = Resources::Path(i); |
- if (path == NULL) { |
- break; |
+ for (intptr_t i = 0; parameters[i] != NULL; i++) { |
+ const MethodParameter* parameter = parameters[i]; |
+ const char* name = parameter->name(); |
+ const bool required = parameter->required(); |
+ const char* value = js->LookupParam(name); |
+ const bool has_parameter = (value != NULL); |
+ if (required && !has_parameter) { |
+ PrintMissingParamError(js, name); |
+ return false; |
} |
- ASSERT(*path != '\0'); |
- // Skip the '/'. |
- path++; |
- if (strcmp(name, path) == 0) { |
- const uint8_t* str = Resources::Resource(i); |
- intptr_t length = Resources::Length(i); |
- return Dart_NewStringFromUTF8(str, length); |
+ if (!parameter->Validate(value)) { |
+ PrintInvalidParamError(js, name); |
+ return false; |
} |
- i++; |
- } |
- return Dart_Null(); |
-} |
- |
- |
-Dart_Handle Service::LibraryTagHandler(Dart_LibraryTag tag, |
- Dart_Handle library, |
- Dart_Handle url) { |
- if (tag == Dart_kCanonicalizeUrl) { |
- // url is already canonicalized. |
- return url; |
- } |
- if (tag != Dart_kSourceTag) { |
- FATAL("Service::LibraryTagHandler encountered an unexpected tag."); |
} |
- ASSERT(tag == Dart_kSourceTag); |
- const char* url_string = NULL; |
- Dart_Handle result = Dart_StringToCString(url, &url_string); |
- if (Dart_IsError(result)) { |
- return result; |
- } |
- Dart_Handle source = GetSource(url_string); |
- if (Dart_IsError(source)) { |
- return source; |
- } |
- return Dart_LoadSource(library, url, source, 0, 0); |
+ return true; |
} |
-void Service::MaybeInjectVMServiceLibrary(Isolate* isolate) { |
+void Service::InvokeMethod(Isolate* isolate, const Array& msg) { |
ASSERT(isolate != NULL); |
- ASSERT(isolate->name() != NULL); |
- if (!Service::IsServiceIsolateName(isolate->name())) { |
- // Not service isolate. |
- return; |
- } |
- if (HasServiceIsolate()) { |
- // Service isolate already exists. |
- return; |
- } |
- SetServiceIsolate(isolate); |
- |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- |
- // Register dart:vmservice library. |
- const String& url_str = String::Handle(Symbols::DartVMService().raw()); |
- const Library& library = Library::Handle(Library::New(url_str)); |
- library.Register(); |
- library.set_native_entry_resolver(VmServiceNativeResolver); |
- |
- // Temporarily install our library tag handler. |
- isolate->set_library_tag_handler(LibraryTagHandler); |
- |
- // Get script source. |
- const char* resource = NULL; |
- const char* path = "/vmservice.dart"; |
- intptr_t r = Resources::ResourceLookup(path, &resource); |
- ASSERT(r != Resources::kNoSuchInstance); |
- ASSERT(resource != NULL); |
- const String& source_str = String::Handle( |
- String::FromUTF8(reinterpret_cast<const uint8_t*>(resource), r)); |
- ASSERT(!source_str.IsNull()); |
- const Script& script = Script::Handle( |
- isolate, Script::New(url_str, source_str, RawScript::kLibraryTag)); |
- |
- // Compile script. |
- Dart_EnterScope(); // Need to enter scope for tag handler. |
- library.SetLoadInProgress(); |
- const Error& error = Error::Handle(isolate, |
- Compiler::Compile(library, script)); |
- ASSERT(error.IsNull()); |
- Dart_Handle result = Dart_FinalizeLoading(false); |
- ASSERT(!Dart_IsError(result)); |
- Dart_ExitScope(); |
- |
- // Uninstall our library tag handler. |
- isolate->set_library_tag_handler(NULL); |
-} |
- |
- |
-void Service::FinishedInitializing() { |
- MonitorLocker ml(monitor_); |
- initializing_ = false; |
- ml.NotifyAll(); |
-} |
- |
+ ASSERT(!msg.IsNull()); |
+ ASSERT(msg.Length() == 5); |
-static void ShutdownIsolate(uword parameter) { |
- Isolate* isolate = reinterpret_cast<Isolate*>(parameter); |
- ASSERT(Service::IsServiceIsolate(isolate)); |
{ |
- // Print the error if there is one. This may execute dart code to |
- // print the exception object, so we need to use a StartIsolateScope. |
- StartIsolateScope start_scope(isolate); |
StackZone zone(isolate); |
- HandleScope handle_scope(isolate); |
- Error& error = Error::Handle(); |
- error = isolate->object_store()->sticky_error(); |
- if (!error.IsNull()) { |
- OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString()); |
- } |
- Dart::RunShutdownCallback(); |
- } |
- { |
- // Shut the isolate down. |
- SwitchIsolateScope switch_scope(isolate); |
- Dart::ShutdownIsolate(); |
- } |
- Service::SetServiceIsolate(NULL); |
- Service::SetServicePort(ILLEGAL_PORT); |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Shutdown.\n"); |
- } |
-} |
+ HANDLESCOPE(isolate); |
+ Instance& reply_port = Instance::Handle(isolate); |
+ String& method_name = String::Handle(isolate); |
+ Array& param_keys = Array::Handle(isolate); |
+ Array& param_values = Array::Handle(isolate); |
+ reply_port ^= msg.At(1); |
+ method_name ^= msg.At(2); |
+ param_keys ^= msg.At(3); |
+ param_values ^= msg.At(4); |
-class RunServiceTask : public ThreadPool::Task { |
- public: |
- virtual void Run() { |
- ASSERT(Isolate::Current() == NULL); |
- char* error = NULL; |
- Isolate* isolate = NULL; |
- |
- Dart_IsolateCreateCallback create_callback = Service::create_callback(); |
- // TODO(johnmccutchan): Support starting up service isolate without embedder |
- // provided isolate creation callback. |
- if (create_callback == NULL) { |
- Service::FinishedInitializing(); |
- return; |
- } |
+ ASSERT(!method_name.IsNull()); |
+ ASSERT(!param_keys.IsNull()); |
+ ASSERT(!param_values.IsNull()); |
+ ASSERT(param_keys.Length() == param_values.Length()); |
- isolate = |
- reinterpret_cast<Isolate*>(create_callback(Service::kIsolateName, |
- NULL, |
- NULL, |
- NULL, |
- &error)); |
- if (isolate == NULL) { |
- OS::PrintErr("vm-service: Isolate creation error: %s\n", error); |
- Service::FinishedInitializing(); |
- return; |
+ if (!reply_port.IsSendPort()) { |
+ FATAL("SendPort expected."); |
} |
- Isolate::SetCurrent(NULL); |
- |
- RunMain(isolate); |
- |
- Service::FinishedInitializing(); |
+ JSONStream js; |
+ js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
+ method_name, param_keys, param_values); |
- isolate->message_handler()->Run(Dart::thread_pool(), |
- NULL, |
- ShutdownIsolate, |
- reinterpret_cast<uword>(isolate)); |
- } |
+ const char* c_method_name = method_name.ToCString(); |
- protected: |
- void RunMain(Isolate* isolate) { |
- StartIsolateScope iso_scope(isolate); |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- // Invoke main which will return the loadScriptPort. |
- const Library& root_library = |
- Library::Handle(isolate, isolate->object_store()->root_library()); |
- if (root_library.IsNull()) { |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Embedder did not install a script."); |
+ ServiceMethodDescriptor* method = FindMethod(c_method_name); |
+ if (method != NULL) { |
+ if (!ValidateParameters(method->parameters, &js)) { |
+ js.PostReply(); |
+ return; |
} |
- // Service isolate is not supported by embedder. |
- return; |
- } |
- ASSERT(!root_library.IsNull()); |
- const String& entry_name = String::Handle(isolate, String::New("main")); |
- ASSERT(!entry_name.IsNull()); |
- const Function& entry = |
- Function::Handle(isolate, |
- root_library.LookupFunctionAllowPrivate(entry_name)); |
- if (entry.IsNull()) { |
- // Service isolate is not supported by embedder. |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Embedder did not provide a main function."); |
+ if (method->entry(isolate, &js)) { |
+ js.PostReply(); |
} |
return; |
} |
- ASSERT(!entry.IsNull()); |
- const Object& result = |
- Object::Handle(isolate, |
- DartEntry::InvokeFunction(entry, |
- Object::empty_array())); |
- ASSERT(!result.IsNull()); |
- if (result.IsError()) { |
- // Service isolate did not initialize properly. |
- if (FLAG_trace_service) { |
- const Error& error = Error::Cast(result); |
- OS::Print("vm-service: Calling main resulted in an error: %s", |
- error.ToErrorCString()); |
- } |
+ |
+ EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(c_method_name); |
+ if (handler == NULL) { |
+ handler = FindRootEmbedderHandler(c_method_name); |
+ } |
+ |
+ if (handler != NULL) { |
+ EmbedderHandleMessage(handler, &js); |
+ js.PostReply(); |
return; |
} |
- ASSERT(result.IsReceivePort()); |
- const ReceivePort& rp = ReceivePort::Cast(result); |
- Service::SetLoadPort(rp.Id()); |
+ |
+ PrintUnrecognizedMethodError(&js); |
+ js.PostReply(); |
+ return; |
} |
-}; |
+} |
-void Service::RunService() { |
- ASSERT(monitor_ == NULL); |
- monitor_ = new Monitor(); |
- ASSERT(monitor_ != NULL); |
- // Grab the isolate create callback here to avoid race conditions with tests |
- // that change this after Dart_Initialize returns. |
- create_callback_ = Isolate::CreateCallback(); |
- Dart::thread_pool()->Run(new RunServiceTask()); |
+void Service::HandleRootMessage(const Array& msg_instance) { |
+ Isolate* isolate = Isolate::Current(); |
+ InvokeMethod(isolate, msg_instance); |
} |
-// A handler for a per-isolate request. |
-// |
-// If a handler returns true, the reply is complete and ready to be |
-// posted. If a handler returns false, then it is responsible for |
-// posting the reply (this can be used for asynchronous delegation of |
-// the response handling). |
-typedef bool (*IsolateMessageHandler)(Isolate* isolate, JSONStream* stream); |
-struct IsolateMessageHandlerEntry { |
- const char* method; |
- IsolateMessageHandler handler; |
-}; |
+void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
+ ASSERT(isolate != NULL); |
+ InvokeMethod(isolate, msg); |
+} |
-static IsolateMessageHandler FindIsolateMessageHandler(const char* method); |
+bool Service::EventMaskHas(uint32_t mask) { |
+ return (event_mask_ & mask) != 0; |
+} |
-// A handler for a root (vm-global) request. |
-// |
-// If a handler returns true, the reply is complete and ready to be |
-// posted. If a handler returns false, then it is responsible for |
-// posting the reply (this can be used for asynchronous delegation of |
-// the response handling). |
-typedef bool (*RootMessageHandler)(JSONStream* stream); |
-struct RootMessageHandlerEntry { |
- const char* method; |
- RootMessageHandler handler; |
-}; |
+bool Service::NeedsDebuggerEvents() { |
+ return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyDebugMask); |
+} |
-static RootMessageHandler FindRootMessageHandler(const char* method); |
+bool Service::NeedsGCEvents() { |
+ return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyGCMask); |
+} |
-static void PrintRequest(const JSONObject& obj, JSONStream* js) { |
- JSONObject jsobj(&obj, "request"); |
- jsobj.AddProperty("method", js->method()); |
- { |
- JSONArray jsarr(&jsobj, "param_keys"); |
- for (intptr_t i = 0; i < js->num_params(); i++) { |
- jsarr.AddValue(js->GetParamKey(i)); |
- } |
- } |
- { |
- JSONArray jsarr(&jsobj, "param_values"); |
- for (intptr_t i = 0; i < js->num_params(); i++) { |
- jsarr.AddValue(js->GetParamValue(i)); |
- } |
- } |
+ |
+void Service::SetEventMask(uint32_t mask) { |
+ event_mask_ = mask; |
} |
-static void PrintError(JSONStream* js, |
- const char* format, ...) { |
+void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { |
+ if (!ServiceIsolate::IsRunning()) { |
+ return; |
+ } |
Isolate* isolate = Isolate::Current(); |
+ ASSERT(isolate != NULL); |
+ HANDLESCOPE(isolate); |
- va_list args; |
- va_start(args, format); |
- intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
- va_end(args); |
+ // Construct a list of the form [eventId, eventMessage]. |
+ const Array& list = Array::Handle(Array::New(2)); |
+ ASSERT(!list.IsNull()); |
+ list.SetAt(0, Integer::Handle(Integer::New(eventId))); |
+ list.SetAt(1, eventMessage); |
- char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
- va_list args2; |
- va_start(args2, format); |
- OS::VSNPrint(buffer, (len + 1), format, args2); |
- va_end(args2); |
+ // Push the event to port_. |
+ uint8_t* data = NULL; |
+ MessageWriter writer(&data, &allocator, false); |
+ writer.WriteMessage(list); |
+ intptr_t len = writer.BytesWritten(); |
+ if (FLAG_trace_service) { |
+ OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", |
+ eventId, len); |
+ } |
+ // TODO(turnidge): For now we ignore failure to send an event. Revisit? |
+ PortMap::PostMessage( |
+ new Message(ServiceIsolate::Port(), data, len, Message::kNormalPriority)); |
+} |
- JSONObject jsobj(js); |
- jsobj.AddProperty("type", "Error"); |
- jsobj.AddProperty("message", buffer); |
- PrintRequest(jsobj, js); |
+ |
+void Service::SendEvent(intptr_t eventId, |
+ const String& meta, |
+ const uint8_t* data, |
+ intptr_t size) { |
+ // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] |
+ const intptr_t meta_bytes = Utf8::Length(meta); |
+ const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; |
+ const TypedData& message = TypedData::Handle( |
+ TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); |
+ intptr_t offset = 0; |
+ // TODO(koda): Rename these methods SetHostUint64, etc. |
+ message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); |
+ offset += sizeof(uint64_t); |
+ { |
+ NoGCScope no_gc; |
+ meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); |
+ offset += meta_bytes; |
+ } |
+ // TODO(koda): It would be nice to avoid this copy (requires changes to |
+ // MessageWriter code). |
+ { |
+ NoGCScope no_gc; |
+ memmove(message.DataAddr(offset), data, size); |
+ offset += size; |
+ } |
+ ASSERT(offset == total_bytes); |
+ SendEvent(eventId, message); |
} |
-static void PrintMissingParamError(JSONStream* js, |
- const char* param) { |
- PrintError(js, "%s expects the '%s' parameter", |
- js->method(), param); |
+void Service::HandleGCEvent(GCEvent* event) { |
+ JSONStream js; |
+ event->PrintJSON(&js); |
+ const String& message = String::Handle(String::New(js.ToCString())); |
+ SendEvent(kEventFamilyGC, message); |
} |
-static void PrintInvalidParamError(JSONStream* js, |
- const char* param) { |
- PrintError(js, "%s: invalid '%s' parameter: %s", |
- js->method(), param, js->LookupParam(param)); |
+void Service::HandleDebuggerEvent(DebuggerEvent* event) { |
+ JSONStream js; |
+ event->PrintJSON(&js); |
+ const String& message = String::Handle(String::New(js.ToCString())); |
+ SendEvent(kEventFamilyDebug, message); |
} |
-static void PrintErrorWithKind(JSONStream* js, |
- const char* kind, |
- const char* format, ...) { |
- Isolate* isolate = Isolate::Current(); |
+class EmbedderServiceHandler { |
+ public: |
+ explicit EmbedderServiceHandler(const char* name) : name_(NULL), |
+ callback_(NULL), |
+ user_data_(NULL), |
+ next_(NULL) { |
+ ASSERT(name != NULL); |
+ name_ = strdup(name); |
+ } |
- va_list args; |
- va_start(args, format); |
- intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
- va_end(args); |
+ ~EmbedderServiceHandler() { |
+ free(name_); |
+ } |
- char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
- va_list args2; |
- va_start(args2, format); |
- OS::VSNPrint(buffer, (len + 1), format, args2); |
- va_end(args2); |
+ const char* name() const { return name_; } |
- JSONObject jsobj(js); |
- jsobj.AddProperty("type", "Error"); |
- jsobj.AddProperty("id", ""); |
- jsobj.AddProperty("kind", kind); |
- jsobj.AddProperty("message", buffer); |
- PrintRequest(jsobj, js); |
+ Dart_ServiceRequestCallback callback() const { return callback_; } |
+ void set_callback(Dart_ServiceRequestCallback callback) { |
+ callback_ = callback; |
+ } |
+ |
+ void* user_data() const { return user_data_; } |
+ void set_user_data(void* user_data) { |
+ user_data_ = user_data; |
+ } |
+ |
+ EmbedderServiceHandler* next() const { return next_; } |
+ void set_next(EmbedderServiceHandler* next) { |
+ next_ = next; |
+ } |
+ |
+ private: |
+ char* name_; |
+ Dart_ServiceRequestCallback callback_; |
+ void* user_data_; |
+ EmbedderServiceHandler* next_; |
+}; |
+ |
+ |
+void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, |
+ JSONStream* js) { |
+ ASSERT(handler != NULL); |
+ Dart_ServiceRequestCallback callback = handler->callback(); |
+ ASSERT(callback != NULL); |
+ const char* r = NULL; |
+ const char* name = js->method(); |
+ const char** keys = js->param_keys(); |
+ const char** values = js->param_values(); |
+ r = callback(name, keys, values, js->num_params(), handler->user_data()); |
+ ASSERT(r != NULL); |
+ // TODO(johnmccutchan): Allow for NULL returns? |
+ TextBuffer* buffer = js->buffer(); |
+ buffer->AddString(r); |
+ free(const_cast<char*>(r)); |
} |
-void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
- ASSERT(isolate != NULL); |
- ASSERT(!msg.IsNull()); |
- ASSERT(msg.Length() == 5); |
+void Service::RegisterIsolateEmbedderCallback( |
+ const char* name, |
+ Dart_ServiceRequestCallback callback, |
+ void* user_data) { |
+ if (name == NULL) { |
+ return; |
+ } |
+ EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); |
+ if (handler != NULL) { |
+ // Update existing handler entry. |
+ handler->set_callback(callback); |
+ handler->set_user_data(user_data); |
+ return; |
+ } |
+ // Create a new handler. |
+ handler = new EmbedderServiceHandler(name); |
+ handler->set_callback(callback); |
+ handler->set_user_data(user_data); |
- { |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
+ // Insert into isolate_service_handler_head_ list. |
+ handler->set_next(isolate_service_handler_head_); |
+ isolate_service_handler_head_ = handler; |
+} |
- Instance& reply_port = Instance::Handle(isolate); |
- String& method = String::Handle(isolate); |
- Array& param_keys = Array::Handle(isolate); |
- Array& param_values = Array::Handle(isolate); |
- reply_port ^= msg.At(1); |
- method ^= msg.At(2); |
- param_keys ^= msg.At(3); |
- param_values ^= msg.At(4); |
- ASSERT(!method.IsNull()); |
- ASSERT(!param_keys.IsNull()); |
- ASSERT(!param_values.IsNull()); |
- ASSERT(param_keys.Length() == param_values.Length()); |
+EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( |
+ const char* name) { |
+ EmbedderServiceHandler* current = isolate_service_handler_head_; |
+ while (current != NULL) { |
+ if (strcmp(name, current->name()) == 0) { |
+ return current; |
+ } |
+ current = current->next(); |
+ } |
+ return NULL; |
+} |
+ |
+ |
+void Service::RegisterRootEmbedderCallback( |
+ const char* name, |
+ Dart_ServiceRequestCallback callback, |
+ void* user_data) { |
+ if (name == NULL) { |
+ return; |
+ } |
+ EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); |
+ if (handler != NULL) { |
+ // Update existing handler entry. |
+ handler->set_callback(callback); |
+ handler->set_user_data(user_data); |
+ return; |
+ } |
+ // Create a new handler. |
+ handler = new EmbedderServiceHandler(name); |
+ handler->set_callback(callback); |
+ handler->set_user_data(user_data); |
+ |
+ // Insert into root_service_handler_head_ list. |
+ handler->set_next(root_service_handler_head_); |
+ root_service_handler_head_ = handler; |
+} |
- if (!reply_port.IsSendPort()) { |
- FATAL("SendPort expected."); |
- } |
- IsolateMessageHandler handler = |
- FindIsolateMessageHandler(method.ToCString()); |
- { |
- JSONStream js; |
- js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
- method, param_keys, param_values); |
- if (handler == NULL) { |
- // Check for an embedder handler. |
- EmbedderServiceHandler* e_handler = |
- FindIsolateEmbedderHandler(method.ToCString()); |
- if (e_handler != NULL) { |
- EmbedderHandleMessage(e_handler, &js); |
- } else { |
- if (FindRootMessageHandler(method.ToCString()) != NULL) { |
- PrintError(&js, "%s does not expect the 'isolateId' parameter", |
- method.ToCString()); |
- } else { |
- PrintError(&js, "Unrecognized method: %s", method.ToCString()); |
- } |
- } |
- js.PostReply(); |
- } else { |
- if (handler(isolate, &js)) { |
- // Handler returns true if the reply is ready to be posted. |
- // TODO(johnmccutchan): Support asynchronous replies. |
- js.PostReply(); |
- } |
- } |
+EmbedderServiceHandler* Service::FindRootEmbedderHandler( |
+ const char* name) { |
+ EmbedderServiceHandler* current = root_service_handler_head_; |
+ while (current != NULL) { |
+ if (strcmp(name, current->name()) == 0) { |
+ return current; |
} |
+ current = current->next(); |
} |
+ return NULL; |
} |
+static const MethodParameter* get_isolate_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolate(Isolate* isolate, JSONStream* js) { |
isolate->PrintJSON(js, false); |
return true; |
} |
+static const MethodParameter* get_stack_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetStack(Isolate* isolate, JSONStream* js) { |
DebuggerStackTrace* stack = isolate->debugger()->StackTrace(); |
JSONObject jsobj(js); |
@@ -1048,112 +773,6 @@ static bool HandleIsolateEcho(Isolate* isolate, JSONStream* js) { |
} |
-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 bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { |
- if ((s == NULL) || (*s == '\0')) { |
- // Empty string. |
- return false; |
- } |
- if (id == NULL) { |
- // No id pointer. |
- return false; |
- } |
- int64_t r = 0; |
- char* end_ptr = NULL; |
- r = strtoll(s, &end_ptr, base); |
- if (end_ptr == s) { |
- // String was not advanced at all, cannot be valid. |
- return false; |
- } |
- *id = r; |
- return true; |
-} |
- |
-// Scans the string until the '-' character. Returns pointer to string |
-// at '-' character. Returns NULL if not found. |
-static const char* ScanUntilDash(const char* s) { |
- if ((s == NULL) || (*s == '\0')) { |
- // Empty string. |
- return NULL; |
- } |
- while (*s != '\0') { |
- if (*s == '-') { |
- return s; |
- } |
- s++; |
- } |
- return NULL; |
-} |
- |
- |
-static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { |
- if ((s == NULL) || (*s == '\0')) { |
- // Empty string. |
- return false; |
- } |
- if ((timestamp == NULL) || (address == NULL)) { |
- // Bad arguments. |
- return false; |
- } |
- // Extract the timestamp. |
- if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { |
- return false; |
- } |
- s = ScanUntilDash(s); |
- if (s == NULL) { |
- return false; |
- } |
- // Skip the dash. |
- s++; |
- // Extract the PC. |
- if (!GetUnsignedIntegerId(s, address, 16)) { |
- return false; |
- } |
- return true; |
-} |
- |
- |
static bool ContainsNonInstance(const Object& obj) { |
if (obj.IsArray()) { |
const Array& array = Array::Cast(obj); |
@@ -1596,6 +1215,12 @@ static bool PrintInboundReferences(Isolate* isolate, |
} |
+static const MethodParameter* get_inbound_references_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetInboundReferences(Isolate* isolate, |
JSONStream* js) { |
const char* target_id = js->LookupParam("targetId"); |
@@ -1694,6 +1319,13 @@ static bool PrintRetainingPath(Isolate* isolate, |
return true; |
} |
+ |
+static const MethodParameter* get_retaining_path_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetRetainingPath(Isolate* isolate, |
JSONStream* js) { |
const char* target_id = js->LookupParam("targetId"); |
@@ -1737,6 +1369,12 @@ static bool HandleIsolateGetRetainingPath(Isolate* isolate, |
} |
+static const MethodParameter* get_retained_size_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetRetainedSize(Isolate* isolate, JSONStream* js) { |
const char* target_id = js->LookupParam("targetId"); |
if (target_id == NULL) { |
@@ -1784,6 +1422,12 @@ static bool HandleIsolateGetRetainedSize(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* eval_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateEval(Isolate* isolate, JSONStream* js) { |
const char* target_id = js->LookupParam("targetId"); |
if (target_id == NULL) { |
@@ -1876,6 +1520,12 @@ class GetInstancesVisitor : public ObjectGraph::Visitor { |
}; |
+static const MethodParameter* get_instances_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetInstances(Isolate* isolate, JSONStream* js) { |
const char* target_id = js->LookupParam("classId"); |
if (target_id == NULL) { |
@@ -1922,6 +1572,69 @@ static bool HandleIsolateGetInstances(Isolate* isolate, JSONStream* js) { |
} |
+class LibraryCoverageFilter : public CoverageFilter { |
+ public: |
+ explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} |
+ bool ShouldOutputCoverageFor(const Library& lib, |
+ const Script& script, |
+ const Class& cls, |
+ const Function& func) const { |
+ return lib.raw() == lib_.raw(); |
+ } |
+ private: |
+ const Library& lib_; |
+}; |
+ |
+ |
+class ScriptCoverageFilter : public CoverageFilter { |
+ public: |
+ explicit ScriptCoverageFilter(const Script& script) |
+ : script_(script) {} |
+ bool ShouldOutputCoverageFor(const Library& lib, |
+ const Script& script, |
+ const Class& cls, |
+ const Function& func) const { |
+ return script.raw() == script_.raw(); |
+ } |
+ private: |
+ const Script& script_; |
+}; |
+ |
+ |
+class ClassCoverageFilter : public CoverageFilter { |
+ public: |
+ explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} |
+ bool ShouldOutputCoverageFor(const Library& lib, |
+ const Script& script, |
+ const Class& cls, |
+ const Function& func) const { |
+ return cls.raw() == cls_.raw(); |
+ } |
+ private: |
+ const Class& cls_; |
+}; |
+ |
+ |
+class FunctionCoverageFilter : public CoverageFilter { |
+ public: |
+ explicit FunctionCoverageFilter(const Function& func) : func_(func) {} |
+ bool ShouldOutputCoverageFor(const Library& lib, |
+ const Script& script, |
+ const Class& cls, |
+ const Function& func) const { |
+ return func.raw() == func_.raw(); |
+ } |
+ private: |
+ const Function& func_; |
+}; |
+ |
+ |
+static const MethodParameter* get_coverage_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { |
if (!js->HasParam("targetId")) { |
CodeCoverage::PrintJSON(isolate, js, NULL); |
@@ -1960,6 +1673,12 @@ static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* add_breakpoint_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateAddBreakpoint(Isolate* isolate, JSONStream* js) { |
if (!js->HasParam("line")) { |
PrintMissingParamError(js, "line"); |
@@ -1990,6 +1709,12 @@ static bool HandleIsolateAddBreakpoint(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* remove_breakpoint_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateRemoveBreakpoint(Isolate* isolate, JSONStream* js) { |
if (!js->HasParam("breakpointId")) { |
PrintMissingParamError(js, "breakpointId"); |
@@ -2026,6 +1751,7 @@ static RawClass* GetMetricsClass(Isolate* isolate) { |
} |
+ |
static bool HandleNativeMetricsList(Isolate* isolate, JSONStream* js) { |
JSONObject obj(js); |
obj.AddProperty("type", "MetricList"); |
@@ -2106,6 +1832,12 @@ static bool HandleDartMetric(Isolate* isolate, JSONStream* js, const char* id) { |
} |
+static const MethodParameter* get_metric_list_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetMetricList(Isolate* isolate, JSONStream* js) { |
bool native_metrics = false; |
if (js->HasParam("type")) { |
@@ -2128,6 +1860,12 @@ static bool HandleIsolateGetMetricList(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* get_metric_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetMetric(Isolate* isolate, JSONStream* js) { |
const char* metric_id = js->LookupParam("metricId"); |
if (metric_id == NULL) { |
@@ -2154,12 +1892,24 @@ static bool HandleIsolateGetMetric(Isolate* isolate, JSONStream* js) { |
} |
-static bool HandleVMGetMetricList(JSONStream* js) { |
+static const MethodParameter* get_vm_metric_list_params[] = { |
+ NO_ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
+static bool HandleVMGetMetricList(Isolate* isolate, JSONStream* js) { |
return false; |
} |
-static bool HandleVMGetMetric(JSONStream* js) { |
+static const MethodParameter* get_vm_metric_params[] = { |
+ NO_ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
+static bool HandleVMGetMetric(Isolate* isolate, JSONStream* js) { |
const char* metric_id = js->LookupParam("metricId"); |
if (metric_id == NULL) { |
PrintMissingParamError(js, "metricId"); |
@@ -2168,6 +1918,12 @@ static bool HandleVMGetMetric(JSONStream* js) { |
} |
+static const MethodParameter* resume_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateResume(Isolate* isolate, JSONStream* js) { |
const char* step_param = js->LookupParam("step"); |
if (isolate->message_handler()->paused_on_start()) { |
@@ -2214,6 +1970,12 @@ static bool HandleIsolateResume(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* get_breakpoints_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetBreakpoints(Isolate* isolate, JSONStream* js) { |
JSONObject jsobj(js); |
jsobj.AddProperty("type", "BreakpointList"); |
@@ -2223,6 +1985,12 @@ static bool HandleIsolateGetBreakpoints(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* pause_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolatePause(Isolate* isolate, JSONStream* js) { |
// TODO(turnidge): Don't double-interrupt the isolate here. |
isolate->ScheduleInterrupts(Isolate::kApiInterrupt); |
@@ -2233,6 +2001,12 @@ static bool HandleIsolatePause(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* get_tag_profile_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { |
JSONObject miniProfile(js); |
miniProfile.AddProperty("type", "TagProfile"); |
@@ -2241,29 +2015,48 @@ static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { |
return true; |
} |
+ |
+static const char* tags_enum_names[] = { |
+ "None", |
+ "UserVM", |
+ "UserOnly", |
+ "VMUser", |
+ "VMOnly", |
+ NULL, |
+}; |
+ |
+ |
+static ProfilerService::TagOrder tags_enum_values[] = { |
+ ProfilerService::kNoTags, |
+ ProfilerService::kUserVM, |
+ ProfilerService::kUser, |
+ ProfilerService::kVMUser, |
+ ProfilerService::kVM, |
+ ProfilerService::kNoTags, // Default value. |
+}; |
+ |
+ |
+static const MethodParameter* get_cpu_profile_params[] = { |
+ ISOLATE_PARAMETER, |
+ new EnumParameter("tags", true, tags_enum_names), |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) { |
- ProfilerService::TagOrder tag_order = ProfilerService::kUserVM; |
- if (js->HasParam("tags")) { |
- if (js->ParamIs("tags", "None")) { |
- tag_order = ProfilerService::kNoTags; |
- } else if (js->ParamIs("tags", "UserVM")) { |
- tag_order = ProfilerService::kUserVM; |
- } else if (js->ParamIs("tags", "UserOnly")) { |
- tag_order = ProfilerService::kUser; |
- } else if (js->ParamIs("tags", "VMUser")) { |
- tag_order = ProfilerService::kVMUser; |
- } else if (js->ParamIs("tags", "VMOnly")) { |
- tag_order = ProfilerService::kVM; |
- } else { |
- PrintInvalidParamError(js, "tags"); |
- return true; |
- } |
- } |
+ ProfilerService::TagOrder tag_order = |
+ EnumMapper(js->LookupParam("tags"), tags_enum_names, tags_enum_values); |
ProfilerService::PrintJSON(js, tag_order); |
return true; |
} |
+static const MethodParameter* get_allocation_profile_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetAllocationProfile(Isolate* isolate, |
JSONStream* js) { |
bool should_reset_accumulator = false; |
@@ -2297,12 +2090,24 @@ static bool HandleIsolateGetAllocationProfile(Isolate* isolate, |
} |
+static const MethodParameter* get_heap_map_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetHeapMap(Isolate* isolate, JSONStream* js) { |
isolate->heap()->PrintHeapMapToJSONStream(isolate, js); |
return true; |
} |
+static const MethodParameter* request_heap_snapshot_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateRequestHeapSnapshot(Isolate* isolate, JSONStream* js) { |
Service::SendGraphEvent(isolate); |
// TODO(koda): Provide some id that ties this request to async response(s). |
@@ -2353,6 +2158,12 @@ class ContainsAddressVisitor : public FindObjectVisitor { |
}; |
+static const MethodParameter* get_object_by_address_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetObjectByAddress(Isolate* isolate, JSONStream* js) { |
const char* addr_str = js->LookupParam("address"); |
if (addr_str == NULL) { |
@@ -2400,6 +2211,12 @@ static bool HandleIsolateRespondWithMalformedObject(Isolate* isolate, |
} |
+static const MethodParameter* get_object_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetObject(Isolate* isolate, JSONStream* js) { |
const char* id = js->LookupParam("objectId"); |
if (id == NULL) { |
@@ -2435,6 +2252,12 @@ static bool HandleIsolateGetObject(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* get_class_list_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetClassList(Isolate* isolate, JSONStream* js) { |
ClassTable* table = isolate->class_table(); |
JSONObject jsobj(js); |
@@ -2443,6 +2266,12 @@ static bool HandleIsolateGetClassList(Isolate* isolate, JSONStream* js) { |
} |
+static const MethodParameter* get_type_arguments_list_params[] = { |
+ ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
static bool HandleIsolateGetTypeArgumentsList(Isolate* isolate, |
JSONStream* js) { |
bool only_with_instantiations = false; |
@@ -2472,122 +2301,6 @@ static bool HandleIsolateGetTypeArgumentsList(Isolate* isolate, |
} |
-static IsolateMessageHandlerEntry isolate_handlers_new[] = { |
- { "getIsolate", HandleIsolate }, |
- { "getObject", HandleIsolateGetObject }, |
- { "getObjectByAddress", HandleIsolateGetObjectByAddress }, |
- { "getBreakpoints", HandleIsolateGetBreakpoints }, |
- { "pause", HandleIsolatePause }, |
- { "resume", HandleIsolateResume }, |
- { "getStack", HandleIsolateGetStack }, |
- { "getCpuProfile", HandleIsolateGetCpuProfile }, |
- { "getTagProfile", HandleIsolateGetTagProfile }, |
- { "getAllocationProfile", HandleIsolateGetAllocationProfile }, |
- { "getHeapMap", HandleIsolateGetHeapMap }, |
- { "addBreakpoint", HandleIsolateAddBreakpoint }, |
- { "removeBreakpoint", HandleIsolateRemoveBreakpoint }, |
- { "getCoverage", HandleIsolateGetCoverage }, |
- { "eval", HandleIsolateEval }, |
- { "getRetainedSize", HandleIsolateGetRetainedSize }, |
- { "getRetainingPath", HandleIsolateGetRetainingPath }, |
- { "getInboundReferences", HandleIsolateGetInboundReferences }, |
- { "getInstances", HandleIsolateGetInstances }, |
- { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot }, |
- { "getClassList", HandleIsolateGetClassList }, |
- { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList }, |
- { "getIsolateMetricList", HandleIsolateGetMetricList }, |
- { "getIsolateMetric", HandleIsolateGetMetric }, |
- { "_echo", HandleIsolateEcho }, |
- { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent }, |
- { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson }, |
- { "_respondWithMalformedObject", HandleIsolateRespondWithMalformedObject }, |
-}; |
- |
- |
-static IsolateMessageHandler FindIsolateMessageHandler(const char* method) { |
- intptr_t num_message_handlers = sizeof(isolate_handlers_new) / |
- sizeof(isolate_handlers_new[0]); |
- for (intptr_t i = 0; i < num_message_handlers; i++) { |
- const IsolateMessageHandlerEntry& entry = isolate_handlers_new[i]; |
- if (strcmp(method, entry.method) == 0) { |
- return entry.handler; |
- } |
- } |
- if (FLAG_trace_service) { |
- OS::Print("Service has no isolate message handler for <%s>\n", method); |
- } |
- return NULL; |
-} |
- |
- |
-void Service::HandleRootMessage(const Instance& msg_instance) { |
- Isolate* isolate = Isolate::Current(); |
- ASSERT(!msg_instance.IsNull()); |
- ASSERT(msg_instance.IsArray()); |
- |
- { |
- StackZone zone(isolate); |
- HANDLESCOPE(isolate); |
- |
- const Array& msg = Array::Cast(msg_instance); |
- ASSERT(msg.Length() == 5); |
- |
- Instance& reply_port = Instance::Handle(isolate); |
- String& method = String::Handle(isolate); |
- Array& param_keys = Array::Handle(isolate); |
- Array& param_values = Array::Handle(isolate); |
- reply_port ^= msg.At(1); |
- method ^= msg.At(2); |
- param_keys ^= msg.At(3); |
- param_values ^= msg.At(4); |
- |
- ASSERT(!method.IsNull()); |
- ASSERT(!param_keys.IsNull()); |
- ASSERT(!param_values.IsNull()); |
- ASSERT(param_keys.Length() == param_values.Length()); |
- |
- if (!reply_port.IsSendPort()) { |
- FATAL("SendPort expected."); |
- } |
- |
- RootMessageHandler handler = |
- FindRootMessageHandler(method.ToCString()); |
- { |
- JSONStream js; |
- js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
- method, param_keys, param_values); |
- if (handler == NULL) { |
- // Check for an embedder handler. |
- EmbedderServiceHandler* e_handler = |
- FindRootEmbedderHandler(method.ToCString()); |
- if (e_handler != NULL) { |
- EmbedderHandleMessage(e_handler, &js); |
- } else { |
- if (FindIsolateMessageHandler(method.ToCString()) != NULL) { |
- PrintMissingParamError(&js, "isolateId"); |
- } else { |
- PrintError(&js, "Unrecognized method: %s", method.ToCString()); |
- } |
- } |
- js.PostReply(); |
- } else { |
- if (handler(&js)) { |
- // Handler returns true if the reply is ready to be posted. |
- // TODO(johnmccutchan): Support asynchronous replies. |
- js.PostReply(); |
- } |
- } |
- } |
- } |
-} |
- |
- |
-static bool HandleRootEcho(JSONStream* js) { |
- JSONObject jsobj(js); |
- return HandleCommonEcho(&jsobj, js); |
-} |
- |
- |
class ServiceIsolateVisitor : public IsolateVisitor { |
public: |
explicit ServiceIsolateVisitor(JSONArray* jsarr) |
@@ -2597,7 +2310,8 @@ class ServiceIsolateVisitor : public IsolateVisitor { |
virtual ~ServiceIsolateVisitor() {} |
void VisitIsolate(Isolate* isolate) { |
- if (isolate != Dart::vm_isolate() && !Service::IsServiceIsolate(isolate)) { |
+ if ((isolate != Dart::vm_isolate()) && |
+ !ServiceIsolate::IsServiceIsolate(isolate)) { |
jsarr_->AddValue(isolate); |
} |
} |
@@ -2607,8 +2321,13 @@ class ServiceIsolateVisitor : public IsolateVisitor { |
}; |
-static bool HandleVM(JSONStream* js) { |
- Isolate* isolate = Isolate::Current(); |
+static const MethodParameter* get_vm_params[] = { |
+ NO_ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
+static bool HandleVM(Isolate* isolate, JSONStream* js) { |
JSONObject jsobj(js); |
jsobj.AddProperty("type", "VM"); |
jsobj.AddProperty("id", "vm"); |
@@ -2639,13 +2358,25 @@ static bool HandleVM(JSONStream* js) { |
} |
-static bool HandleVMFlagList(JSONStream* js) { |
+static const MethodParameter* get_flag_list_params[] = { |
+ NO_ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
+static bool HandleVMFlagList(Isolate* isolate, JSONStream* js) { |
Flags::PrintJSON(js); |
return true; |
} |
-static bool HandleVMSetFlag(JSONStream* js) { |
+static const MethodParameter* set_flags_params[] = { |
+ NO_ISOLATE_PARAMETER, |
+ NULL, |
+}; |
+ |
+ |
+static bool HandleVMSetFlag(Isolate* isolate, JSONStream* js) { |
const char* flag_name = js->LookupParam("name"); |
if (flag_name == NULL) { |
PrintMissingParamError(js, "name"); |
@@ -2671,198 +2402,87 @@ static bool HandleVMSetFlag(JSONStream* js) { |
} |
-static RootMessageHandlerEntry root_handlers_new[] = { |
- { "getVM", HandleVM }, |
- { "getFlagList", HandleVMFlagList }, |
- { "setFlag", HandleVMSetFlag }, |
- { "getVMMetricList", HandleVMGetMetricList }, |
- { "getVMMetric", HandleVMGetMetric }, |
- { "_echo", HandleRootEcho }, |
+static ServiceMethodDescriptor service_methods_[] = { |
+ { "_echo", HandleIsolateEcho, |
+ NULL }, |
+ { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson, |
+ NULL }, |
+ { "_respondWithMalformedObject", HandleIsolateRespondWithMalformedObject, |
+ NULL }, |
+ { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent, |
+ NULL }, |
+ { "addBreakpoint", HandleIsolateAddBreakpoint, |
+ add_breakpoint_params }, |
+ { "eval", HandleIsolateEval, |
+ eval_params }, |
+ { "getAllocationProfile", HandleIsolateGetAllocationProfile, |
+ get_allocation_profile_params }, |
+ { "getBreakpoints", HandleIsolateGetBreakpoints, |
+ get_breakpoints_params }, |
+ { "getClassList", HandleIsolateGetClassList, |
+ get_class_list_params }, |
+ { "getCoverage", HandleIsolateGetCoverage, |
+ get_coverage_params }, |
+ { "getCpuProfile", HandleIsolateGetCpuProfile, |
+ get_cpu_profile_params }, |
+ { "getFlagList", HandleVMFlagList , |
+ get_flag_list_params }, |
+ { "getHeapMap", HandleIsolateGetHeapMap, |
+ get_heap_map_params }, |
+ { "getInboundReferences", HandleIsolateGetInboundReferences, |
+ get_inbound_references_params }, |
+ { "getInstances", HandleIsolateGetInstances, |
+ get_instances_params }, |
+ { "getIsolate", HandleIsolate, |
+ get_isolate_params }, |
+ { "getIsolateMetric", HandleIsolateGetMetric, |
+ get_metric_params }, |
+ { "getIsolateMetricList", HandleIsolateGetMetricList, |
+ get_metric_list_params }, |
+ { "getObject", HandleIsolateGetObject, |
+ get_object_params }, |
+ { "getObjectByAddress", HandleIsolateGetObjectByAddress, |
+ get_object_by_address_params }, |
+ { "getRetainedSize", HandleIsolateGetRetainedSize, |
+ get_retained_size_params }, |
+ { "getRetainingPath", HandleIsolateGetRetainingPath, |
+ get_retaining_path_params }, |
+ { "getStack", HandleIsolateGetStack, |
+ get_stack_params }, |
+ { "getTagProfile", HandleIsolateGetTagProfile, |
+ get_tag_profile_params }, |
+ { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList, |
+ get_type_arguments_list_params }, |
+ { "getVM", HandleVM , |
+ get_vm_params }, |
+ { "getVMMetric", HandleVMGetMetric, |
+ get_vm_metric_params }, |
+ { "getVMMetricList", HandleVMGetMetricList, |
+ get_vm_metric_list_params }, |
+ { "pause", HandleIsolatePause, |
+ pause_params }, |
+ { "removeBreakpoint", HandleIsolateRemoveBreakpoint, |
+ remove_breakpoint_params }, |
+ { "resume", HandleIsolateResume, |
+ resume_params }, |
+ { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot, |
+ request_heap_snapshot_params }, |
+ { "setFlag", HandleVMSetFlag , |
+ set_flags_params }, |
}; |
-static RootMessageHandler FindRootMessageHandler(const char* method) { |
- intptr_t num_message_handlers = sizeof(root_handlers_new) / |
- sizeof(root_handlers_new[0]); |
- for (intptr_t i = 0; i < num_message_handlers; i++) { |
- const RootMessageHandlerEntry& entry = root_handlers_new[i]; |
- if (strcmp(method, entry.method) == 0) { |
- return entry.handler; |
- } |
- } |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: No root message handler for <%s>.\n", method); |
- } |
- return NULL; |
-} |
- |
- |
-void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { |
- if (!IsRunning()) { |
- return; |
- } |
- Isolate* isolate = Isolate::Current(); |
- ASSERT(isolate != NULL); |
- HANDLESCOPE(isolate); |
- |
- // Construct a list of the form [eventId, eventMessage]. |
- const Array& list = Array::Handle(Array::New(2)); |
- ASSERT(!list.IsNull()); |
- list.SetAt(0, Integer::Handle(Integer::New(eventId))); |
- list.SetAt(1, eventMessage); |
- |
- // Push the event to port_. |
- uint8_t* data = NULL; |
- MessageWriter writer(&data, &allocator, false); |
- writer.WriteMessage(list); |
- intptr_t len = writer.BytesWritten(); |
- if (FLAG_trace_service) { |
- OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", |
- eventId, len); |
- } |
- // TODO(turnidge): For now we ignore failure to send an event. Revisit? |
- PortMap::PostMessage( |
- new Message(service_port_, data, len, Message::kNormalPriority)); |
-} |
- |
- |
-void Service::SendEvent(intptr_t eventId, |
- const String& meta, |
- const uint8_t* data, |
- intptr_t size) { |
- // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] |
- const intptr_t meta_bytes = Utf8::Length(meta); |
- const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; |
- const TypedData& message = TypedData::Handle( |
- TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); |
- intptr_t offset = 0; |
- // TODO(koda): Rename these methods SetHostUint64, etc. |
- message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); |
- offset += sizeof(uint64_t); |
- { |
- NoGCScope no_gc; |
- meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); |
- offset += meta_bytes; |
- } |
- // TODO(koda): It would be nice to avoid this copy (requires changes to |
- // MessageWriter code). |
- { |
- NoGCScope no_gc; |
- memmove(message.DataAddr(offset), data, size); |
- offset += size; |
- } |
- ASSERT(offset == total_bytes); |
- SendEvent(eventId, message); |
-} |
- |
- |
-void Service::HandleGCEvent(GCEvent* event) { |
- JSONStream js; |
- event->PrintJSON(&js); |
- const String& message = String::Handle(String::New(js.ToCString())); |
- SendEvent(kEventFamilyGC, message); |
-} |
- |
- |
-void Service::HandleDebuggerEvent(DebuggerEvent* event) { |
- JSONStream js; |
- event->PrintJSON(&js); |
- const String& message = String::Handle(String::New(js.ToCString())); |
- SendEvent(kEventFamilyDebug, message); |
-} |
- |
- |
-void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, |
- JSONStream* js) { |
- ASSERT(handler != NULL); |
- Dart_ServiceRequestCallback callback = handler->callback(); |
- ASSERT(callback != NULL); |
- const char* r = NULL; |
- const char* name = js->method(); |
- const char** keys = js->param_keys(); |
- const char** values = js->param_values(); |
- r = callback(name, keys, values, js->num_params(), handler->user_data()); |
- ASSERT(r != NULL); |
- // TODO(johnmccutchan): Allow for NULL returns? |
- TextBuffer* buffer = js->buffer(); |
- buffer->AddString(r); |
- free(const_cast<char*>(r)); |
-} |
- |
- |
-void Service::RegisterIsolateEmbedderCallback( |
- const char* name, |
- Dart_ServiceRequestCallback callback, |
- void* user_data) { |
- if (name == NULL) { |
- return; |
- } |
- EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); |
- if (handler != NULL) { |
- // Update existing handler entry. |
- handler->set_callback(callback); |
- handler->set_user_data(user_data); |
- return; |
- } |
- // Create a new handler. |
- handler = new EmbedderServiceHandler(name); |
- handler->set_callback(callback); |
- handler->set_user_data(user_data); |
- |
- // Insert into isolate_service_handler_head_ list. |
- handler->set_next(isolate_service_handler_head_); |
- isolate_service_handler_head_ = handler; |
-} |
- |
- |
-EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( |
- const char* name) { |
- EmbedderServiceHandler* current = isolate_service_handler_head_; |
- while (current != NULL) { |
- if (strcmp(name, current->name()) == 0) { |
- return current; |
+ServiceMethodDescriptor* FindMethod(const char* method_name) { |
+ intptr_t num_methods = sizeof(service_methods_) / |
+ sizeof(service_methods_[0]); |
+ for (intptr_t i = 0; i < num_methods; i++) { |
+ ServiceMethodDescriptor& method = service_methods_[i]; |
+ if (strcmp(method_name, method.name) == 0) { |
+ return &method; |
} |
- current = current->next(); |
} |
return NULL; |
} |
-void Service::RegisterRootEmbedderCallback( |
- const char* name, |
- Dart_ServiceRequestCallback callback, |
- void* user_data) { |
- if (name == NULL) { |
- return; |
- } |
- EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); |
- if (handler != NULL) { |
- // Update existing handler entry. |
- handler->set_callback(callback); |
- handler->set_user_data(user_data); |
- return; |
- } |
- // Create a new handler. |
- handler = new EmbedderServiceHandler(name); |
- handler->set_callback(callback); |
- handler->set_user_data(user_data); |
- |
- // Insert into root_service_handler_head_ list. |
- handler->set_next(root_service_handler_head_); |
- root_service_handler_head_ = handler; |
-} |
- |
- |
-EmbedderServiceHandler* Service::FindRootEmbedderHandler( |
- const char* name) { |
- EmbedderServiceHandler* current = root_service_handler_head_; |
- while (current != NULL) { |
- if (strcmp(name, current->name()) == 0) { |
- return current; |
- } |
- current = current->next(); |
- } |
- return NULL; |
-} |
- |
} // namespace dart |