| Index: runtime/vm/service.cc
|
| diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
|
| index df8f3dc996ea5709aed180c9c8ce47d1e21ec21f..e68727a3c41829a1889e847403d243797df540be 100644
|
| --- a/runtime/vm/service.cc
|
| +++ b/runtime/vm/service.cc
|
| @@ -5,6 +5,7 @@
|
| #include "vm/service.h"
|
|
|
| #include "include/dart_api.h"
|
| +#include "include/dart_native_api.h"
|
| #include "platform/globals.h"
|
|
|
| #include "vm/compiler.h"
|
| @@ -74,32 +75,46 @@ EmbedderServiceHandler* Service::root_service_handler_head_ = NULL;
|
| struct ServiceMethodDescriptor;
|
| ServiceMethodDescriptor* FindMethod(const char* method_name);
|
|
|
| -// TODO(turnidge): Build a general framework later. For now, we have
|
| -// a small set of well-known streams.
|
| -bool Service::needs_isolate_events_ = false;
|
| -bool Service::needs_debug_events_ = false;
|
| -bool Service::needs_gc_events_ = false;
|
| -bool Service::needs_echo_events_ = false;
|
| -bool Service::needs_graph_events_ = false;
|
|
|
| -void Service::ListenStream(const char* stream_id) {
|
| +// Support for streams defined in embedders.
|
| +Dart_ServiceStreamListenCallback Service::stream_listen_callback_ = NULL;
|
| +Dart_ServiceStreamCancelCallback Service::stream_cancel_callback_ = NULL;
|
| +
|
| +
|
| +// These are the set of streams known to the core VM.
|
| +StreamInfo Service::isolate_stream("Isolate");
|
| +StreamInfo Service::debug_stream("Debug");
|
| +StreamInfo Service::gc_stream("GC");
|
| +StreamInfo Service::echo_stream("_Echo");
|
| +StreamInfo Service::graph_stream("_Graph");
|
| +
|
| +
|
| +static StreamInfo* streams_[] = {
|
| + &Service::isolate_stream,
|
| + &Service::debug_stream,
|
| + &Service::gc_stream,
|
| + &Service::echo_stream,
|
| + &Service::graph_stream,
|
| +};
|
| +
|
| +
|
| +bool Service::ListenStream(const char* stream_id) {
|
| if (FLAG_trace_service) {
|
| OS::Print("vm-service: starting stream '%s'\n",
|
| stream_id);
|
| }
|
| - if (strcmp(stream_id, "Isolate") == 0) {
|
| - needs_isolate_events_ = true;
|
| - } else if (strcmp(stream_id, "Debug") == 0) {
|
| - needs_debug_events_ = true;
|
| - } else if (strcmp(stream_id, "GC") == 0) {
|
| - needs_gc_events_ = true;
|
| - } else if (strcmp(stream_id, "_Echo") == 0) {
|
| - needs_echo_events_ = true;
|
| - } else if (strcmp(stream_id, "_Graph") == 0) {
|
| - needs_graph_events_ = true;
|
| - } else {
|
| - UNREACHABLE();
|
| + intptr_t num_streams = sizeof(streams_) /
|
| + sizeof(streams_[0]);
|
| + for (intptr_t i = 0; i < num_streams; i++) {
|
| + if (strcmp(stream_id, streams_[i]->id()) == 0) {
|
| + streams_[i]->set_enabled(true);
|
| + return true;
|
| + }
|
| + }
|
| + if (stream_listen_callback_) {
|
| + return (*stream_listen_callback_)(stream_id);
|
| }
|
| + return false;
|
| }
|
|
|
| void Service::CancelStream(const char* stream_id) {
|
| @@ -107,18 +122,16 @@ void Service::CancelStream(const char* stream_id) {
|
| OS::Print("vm-service: stopping stream '%s'\n",
|
| stream_id);
|
| }
|
| - if (strcmp(stream_id, "Isolate") == 0) {
|
| - needs_isolate_events_ = false;
|
| - } else if (strcmp(stream_id, "Debug") == 0) {
|
| - needs_debug_events_ = false;
|
| - } else if (strcmp(stream_id, "GC") == 0) {
|
| - needs_gc_events_ = false;
|
| - } else if (strcmp(stream_id, "_Echo") == 0) {
|
| - needs_echo_events_ = false;
|
| - } else if (strcmp(stream_id, "_Graph") == 0) {
|
| - needs_graph_events_ = false;
|
| - } else {
|
| - UNREACHABLE();
|
| + intptr_t num_streams = sizeof(streams_) /
|
| + sizeof(streams_[0]);
|
| + for (intptr_t i = 0; i < num_streams; i++) {
|
| + if (strcmp(stream_id, streams_[i]->id()) == 0) {
|
| + streams_[i]->set_enabled(false);
|
| + return;
|
| + }
|
| + }
|
| + if (stream_cancel_callback_) {
|
| + return (*stream_cancel_callback_)(stream_id);
|
| }
|
| }
|
|
|
| @@ -623,6 +636,7 @@ void Service::SendEvent(const char* stream_id,
|
| }
|
|
|
|
|
| +// TODO(turnidge): Rewrite this method to use Post_CObject instead.
|
| void Service::SendEventWithData(const char* stream_id,
|
| const char* event_type,
|
| const String& meta,
|
| @@ -658,6 +672,9 @@ void Service::HandleEvent(ServiceEvent* event) {
|
| if (ServiceIsolate::IsServiceIsolateDescendant(event->isolate())) {
|
| return;
|
| }
|
| + if (!ServiceIsolate::IsRunning()) {
|
| + return;
|
| + }
|
| JSONStream js;
|
| const char* stream_id = event->stream_id();
|
| ASSERT(stream_id != NULL);
|
| @@ -666,9 +683,35 @@ void Service::HandleEvent(ServiceEvent* event) {
|
| jsobj.AddProperty("event", event);
|
| jsobj.AddProperty("streamId", stream_id);
|
| }
|
| - const String& message = String::Handle(String::New(js.ToCString()));
|
| - SendEvent(stream_id, ServiceEvent::EventTypeToCString(event->type()),
|
| - message);
|
| +
|
| + // Message is of the format [<stream id>, <json string>].
|
| + //
|
| + // Build the event message in the C heap to avoid dart heap
|
| + // allocation. This method can be called while we have acquired a
|
| + // direct pointer to typed data, so we can't allocate here.
|
| + Dart_CObject list_cobj;
|
| + Dart_CObject* list_values[2];
|
| + list_cobj.type = Dart_CObject_kArray;
|
| + list_cobj.value.as_array.length = 2;
|
| + list_cobj.value.as_array.values = list_values;
|
| +
|
| + Dart_CObject stream_id_cobj;
|
| + stream_id_cobj.type = Dart_CObject_kString;
|
| + stream_id_cobj.value.as_string = const_cast<char*>(stream_id);
|
| + list_values[0] = &stream_id_cobj;
|
| +
|
| + Dart_CObject json_cobj;
|
| + json_cobj.type = Dart_CObject_kString;
|
| + json_cobj.value.as_string = const_cast<char*>(js.ToCString());
|
| + list_values[1] = &json_cobj;
|
| +
|
| + if (FLAG_trace_service) {
|
| + OS::Print(
|
| + "vm-service: Pushing event of type %s to stream %s\n",
|
| + event->KindAsCString(), stream_id);
|
| + }
|
| +
|
| + Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
|
| }
|
|
|
|
|
| @@ -792,6 +835,14 @@ void Service::RegisterRootEmbedderCallback(
|
| }
|
|
|
|
|
| +void Service::SetEmbedderStreamCallbacks(
|
| + Dart_ServiceStreamListenCallback listen_callback,
|
| + Dart_ServiceStreamCancelCallback cancel_callback) {
|
| + stream_listen_callback_ = listen_callback;
|
| + stream_cancel_callback_ = cancel_callback;
|
| +}
|
| +
|
| +
|
| EmbedderServiceHandler* Service::FindRootEmbedderHandler(
|
| const char* name) {
|
| EmbedderServiceHandler* current = root_service_handler_head_;
|
| @@ -875,16 +926,16 @@ void Service::SendEchoEvent(Isolate* isolate, const char* text) {
|
| event.AddProperty("text", text);
|
| }
|
| }
|
| - jsobj.AddProperty("streamId", "_Echo");
|
| + jsobj.AddProperty("streamId", echo_stream.id());
|
| }
|
| const String& message = String::Handle(String::New(js.ToCString()));
|
| uint8_t data[] = {0, 128, 255};
|
| - SendEventWithData("_Echo", "_Echo", message, data, sizeof(data));
|
| + SendEventWithData(echo_stream.id(), "_Echo", message, data, sizeof(data));
|
| }
|
|
|
|
|
| static bool TriggerEchoEvent(Isolate* isolate, JSONStream* js) {
|
| - if (Service::NeedsEchoEvents()) {
|
| + if (Service::echo_stream.enabled()) {
|
| Service::SendEchoEvent(isolate, js->LookupParam("text"));
|
| }
|
| JSONObject jsobj(js);
|
| @@ -2190,7 +2241,7 @@ static bool Resume(Isolate* isolate, JSONStream* js) {
|
| const char* step_param = js->LookupParam("step");
|
| if (isolate->message_handler()->paused_on_start()) {
|
| isolate->message_handler()->set_pause_on_start(false);
|
| - if (Service::NeedsDebugEvents()) {
|
| + if (Service::debug_stream.enabled()) {
|
| ServiceEvent event(isolate, ServiceEvent::kResume);
|
| Service::HandleEvent(&event);
|
| }
|
| @@ -2384,7 +2435,7 @@ static const MethodParameter* request_heap_snapshot_params[] = {
|
|
|
|
|
| static bool RequestHeapSnapshot(Isolate* isolate, JSONStream* js) {
|
| - if (Service::NeedsGraphEvents()) {
|
| + if (Service::graph_stream.enabled()) {
|
| Service::SendGraphEvent(isolate);
|
| }
|
| // TODO(koda): Provide some id that ties this request to async response(s).
|
| @@ -2419,7 +2470,7 @@ void Service::SendGraphEvent(Isolate* isolate) {
|
| event.AddProperty("chunkCount", num_chunks);
|
| event.AddProperty("nodeCount", node_count);
|
| }
|
| - jsobj.AddProperty("streamId", "_Graph");
|
| + jsobj.AddProperty("streamId", graph_stream.id());
|
| }
|
|
|
| const String& message = String::Handle(String::New(js.ToCString()));
|
| @@ -2429,13 +2480,14 @@ void Service::SendGraphEvent(Isolate* isolate) {
|
| ? stream.bytes_written() - (i * kChunkSize)
|
| : kChunkSize;
|
|
|
| - SendEventWithData("_Graph", "_Graph", message, chunk_start, chunk_size);
|
| + SendEventWithData(graph_stream.id(), "_Graph", message,
|
| + chunk_start, chunk_size);
|
| }
|
| }
|
|
|
|
|
| void Service::SendInspectEvent(Isolate* isolate, const Object& inspectee) {
|
| - if (!Service::NeedsDebugEvents()) {
|
| + if (!Service::debug_stream.enabled()) {
|
| return;
|
| }
|
| ServiceEvent event(isolate, ServiceEvent::kInspect);
|
| @@ -2444,6 +2496,22 @@ void Service::SendInspectEvent(Isolate* isolate, const Object& inspectee) {
|
| }
|
|
|
|
|
| +void Service::SendEmbedderEvent(Isolate* isolate,
|
| + const char* stream_id,
|
| + const char* event_kind,
|
| + const uint8_t* bytes,
|
| + intptr_t bytes_len) {
|
| + if (!Service::debug_stream.enabled()) {
|
| + return;
|
| + }
|
| + ServiceEvent event(isolate, ServiceEvent::kEmbedder);
|
| + event.set_embedder_kind(event_kind);
|
| + event.set_embedder_stream_id(stream_id);
|
| + event.set_bytes(bytes, bytes_len);
|
| + Service::HandleEvent(&event);
|
| +}
|
| +
|
| +
|
| class ContainsAddressVisitor : public FindObjectVisitor {
|
| public:
|
| ContainsAddressVisitor(Isolate* isolate, uword addr)
|
| @@ -2726,7 +2794,7 @@ static bool SetExceptionPauseInfo(Isolate* isolate, JSONStream* js) {
|
| }
|
|
|
| isolate->debugger()->SetExceptionPauseInfo(info);
|
| - if (Service::NeedsDebugEvents()) {
|
| + if (Service::debug_stream.enabled()) {
|
| ServiceEvent event(isolate, ServiceEvent::kDebuggerSettingsUpdate);
|
| Service::HandleEvent(&event);
|
| }
|
| @@ -2812,7 +2880,7 @@ static const MethodParameter* set_name_params[] = {
|
|
|
| static bool SetName(Isolate* isolate, JSONStream* js) {
|
| isolate->set_debugger_name(js->LookupParam("name"));
|
| - if (Service::NeedsIsolateEvents()) {
|
| + if (Service::isolate_stream.enabled()) {
|
| ServiceEvent event(isolate, ServiceEvent::kIsolateUpdate);
|
| Service::HandleEvent(&event);
|
| }
|
|
|