| 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
|
|
|