// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/service_isolate.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_api_impl.h" #include "vm/dart_api_message.h" #include "vm/dart_entry.h" #include "vm/isolate.h" #include "vm/lockers.h" #include "vm/message.h" #include "vm/message_handler.h" #include "vm/native_arguments.h" #include "vm/native_entry.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/port.h" #include "vm/service.h" #include "vm/symbols.h" #include "vm/thread_pool.h" #include "vm/timeline.h" namespace dart { #define Z (T->zone()) DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests."); DEFINE_FLAG(bool, trace_service_pause_events, false, "Trace VM service isolate pause events."); DEFINE_FLAG(bool, trace_service_verbose, false, "Provide extra service tracing information."); // These must be kept in sync with service/constants.dart #define VM_SERVICE_ISOLATE_EXIT_MESSAGE_ID 0 #define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1 #define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2 #define VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID 3 #define VM_SERVICE_SERVER_INFO_MESSAGE_ID 4 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 RawArray* MakeServerControlMessage(const SendPort& sp, intptr_t code, bool enable = false) { const Array& list = Array::Handle(Array::New(3)); ASSERT(!list.IsNull()); list.SetAt(0, Integer::Handle(Integer::New(code))); list.SetAt(1, sp); list.SetAt(2, Bool::Get(enable)); return list.raw(); } const char* ServiceIsolate::kName = "vm-service"; Isolate* ServiceIsolate::isolate_ = NULL; Dart_Port ServiceIsolate::port_ = ILLEGAL_PORT; Dart_Port ServiceIsolate::load_port_ = ILLEGAL_PORT; Dart_Port ServiceIsolate::origin_ = ILLEGAL_PORT; Dart_IsolateCreateCallback ServiceIsolate::create_callback_ = NULL; Monitor* ServiceIsolate::monitor_ = new Monitor(); bool ServiceIsolate::initializing_ = true; bool ServiceIsolate::shutting_down_ = false; char* ServiceIsolate::server_address_ = NULL; void ServiceIsolate::RequestServerInfo(const SendPort& sp) { const Array& message = Array::Handle(MakeServerControlMessage( sp, VM_SERVICE_SERVER_INFO_MESSAGE_ID, false /* ignored */)); ASSERT(!message.IsNull()); MessageWriter writer(false); PortMap::PostMessage( writer.WriteMessage(message, port_, Message::kNormalPriority)); } void ServiceIsolate::ControlWebServer(const SendPort& sp, bool enable) { const Array& message = Array::Handle(MakeServerControlMessage( sp, VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID, enable)); ASSERT(!message.IsNull()); MessageWriter writer(false); PortMap::PostMessage( writer.WriteMessage(message, port_, Message::kNormalPriority)); } void ServiceIsolate::SetServerAddress(const char* address) { if (server_address_ != NULL) { free(server_address_); server_address_ = NULL; } if (address == NULL) { return; } server_address_ = strdup(address); } bool ServiceIsolate::NameEquals(const char* name) { ASSERT(name != NULL); return strcmp(name, kName) == 0; } bool ServiceIsolate::Exists() { MonitorLocker ml(monitor_); return isolate_ != NULL; } bool ServiceIsolate::IsRunning() { MonitorLocker ml(monitor_); return (port_ != ILLEGAL_PORT) && (isolate_ != NULL); } bool ServiceIsolate::IsServiceIsolate(const Isolate* isolate) { MonitorLocker ml(monitor_); return isolate == isolate_; } bool ServiceIsolate::IsServiceIsolateDescendant(const Isolate* isolate) { MonitorLocker ml(monitor_); return isolate->origin_id() == origin_; } Dart_Port ServiceIsolate::Port() { MonitorLocker ml(monitor_); return port_; } Dart_Port ServiceIsolate::WaitForLoadPort() { MonitorLocker ml(monitor_); while (initializing_ && (load_port_ == ILLEGAL_PORT)) { ml.Wait(); } return load_port_; } Dart_Port ServiceIsolate::LoadPort() { MonitorLocker ml(monitor_); return load_port_; } bool ServiceIsolate::SendIsolateStartupMessage() { if (!IsRunning()) { return false; } Thread* thread = Thread::Current(); Isolate* isolate = thread->isolate(); if (IsServiceIsolateDescendant(isolate)) { return false; } ASSERT(isolate != NULL); HANDLESCOPE(thread); 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()); MessageWriter writer(false); if (FLAG_trace_service) { OS::PrintErr("vm-service: Isolate %s %" Pd64 " registered.\n", name.ToCString(), Dart_GetMainPortId()); } return PortMap::PostMessage( writer.WriteMessage(list, port_, Message::kNormalPriority)); } bool ServiceIsolate::SendIsolateShutdownMessage() { if (!IsRunning()) { return false; } Thread* thread = Thread::Current(); Isolate* isolate = thread->isolate(); if (IsServiceIsolateDescendant(isolate)) { return false; } ASSERT(isolate != NULL); HANDLESCOPE(thread); 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()); MessageWriter writer(false); if (FLAG_trace_service) { OS::PrintErr("vm-service: Isolate %s %" Pd64 " deregistered.\n", name.ToCString(), Dart_GetMainPortId()); } return PortMap::PostMessage( writer.WriteMessage(list, port_, Message::kNormalPriority)); } void ServiceIsolate::SendServiceExitMessage() { if (!IsRunning()) { return; } if (FLAG_trace_service) { OS::PrintErr("vm-service: sending service exit message.\n"); } Dart_CObject code; code.type = Dart_CObject_kInt32; code.value.as_int32 = VM_SERVICE_ISOLATE_EXIT_MESSAGE_ID; Dart_CObject* values[1] = {&code}; Dart_CObject message; message.type = Dart_CObject_kArray; message.value.as_array.length = 1; message.value.as_array.values = values; ApiMessageWriter writer; PortMap::PostMessage( writer.WriteCMessage(&message, port_, Message::kNormalPriority)); } void ServiceIsolate::SetServicePort(Dart_Port port) { MonitorLocker ml(monitor_); port_ = port; } void ServiceIsolate::SetServiceIsolate(Isolate* isolate) { MonitorLocker ml(monitor_); isolate_ = isolate; if (isolate_ != NULL) { isolate_->set_is_service_isolate(true); origin_ = isolate_->origin_id(); } } void ServiceIsolate::SetLoadPort(Dart_Port port) { MonitorLocker ml(monitor_); load_port_ = port; } void ServiceIsolate::MaybeMakeServiceIsolate(Isolate* I) { Thread* T = Thread::Current(); ASSERT(I == T->isolate()); ASSERT(I != NULL); ASSERT(I->name() != NULL); if (!ServiceIsolate::NameEquals(I->name())) { // Not service isolate. return; } if (Exists()) { // Service isolate already exists. return; } SetServiceIsolate(I); } void ServiceIsolate::FinishedExiting() { MonitorLocker ml(monitor_); shutting_down_ = false; ml.NotifyAll(); } void ServiceIsolate::FinishedInitializing() { MonitorLocker ml(monitor_); initializing_ = false; ml.NotifyAll(); } class RunServiceTask : public ThreadPool::Task { public: virtual void Run() { ASSERT(Isolate::Current() == NULL); #ifndef PRODUCT TimelineDurationScope tds(Timeline::GetVMStream(), "ServiceIsolateStartup"); #endif // !PRODUCT char* error = NULL; Isolate* isolate = NULL; Dart_IsolateCreateCallback create_callback = ServiceIsolate::create_callback(); ASSERT(create_callback != NULL); Dart_IsolateFlags api_flags; Isolate::FlagsInitialize(&api_flags); isolate = reinterpret_cast(create_callback( ServiceIsolate::kName, NULL, NULL, NULL, &api_flags, NULL, &error)); if (isolate == NULL) { if (FLAG_trace_service) { OS::PrintErr("vm-service: Isolate creation error: %s\n", error); } ServiceIsolate::SetServiceIsolate(NULL); ServiceIsolate::FinishedInitializing(); ServiceIsolate::FinishedExiting(); return; } bool got_unwind; { ASSERT(Isolate::Current() == NULL); StartIsolateScope start_scope(isolate); got_unwind = RunMain(isolate); } // FinishedInitializing should be called irrespective of whether // running main caused an error or not. Otherwise, other isolates // waiting for service isolate to come up will deadlock. ServiceIsolate::FinishedInitializing(); if (got_unwind) { ShutdownIsolate(reinterpret_cast(isolate)); return; } isolate->message_handler()->Run(Dart::thread_pool(), NULL, ShutdownIsolate, reinterpret_cast(isolate)); } protected: static void ShutdownIsolate(uword parameter) { if (FLAG_trace_service) { OS::PrintErr("vm-service: ShutdownIsolate\n"); } Isolate* I = reinterpret_cast(parameter); ASSERT(ServiceIsolate::IsServiceIsolate(I)); I->WaitForOutstandingSpawns(); { // Print the error if there is one. This may execute dart code to // print the exception object, so we need to use a StartIsolateScope. ASSERT(Isolate::Current() == NULL); StartIsolateScope start_scope(I); Thread* T = Thread::Current(); ASSERT(I == T->isolate()); StackZone zone(T); HandleScope handle_scope(T); Error& error = Error::Handle(Z); error = T->sticky_error(); if (!error.IsNull() && !error.IsUnwindError()) { OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString()); } error = I->sticky_error(); if (!error.IsNull() && !error.IsUnwindError()) { OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString()); } TransitionVMToNative transition(T); Dart::RunShutdownCallback(); } ASSERT(ServiceIsolate::IsServiceIsolate(I)); ServiceIsolate::SetServiceIsolate(NULL); ServiceIsolate::SetServicePort(ILLEGAL_PORT); // Shut the isolate down. Dart::ShutdownIsolate(I); if (FLAG_trace_service) { OS::PrintErr("vm-service: Shutdown.\n"); } ServiceIsolate::FinishedExiting(); } bool RunMain(Isolate* I) { Thread* T = Thread::Current(); ASSERT(I == T->isolate()); StackZone zone(T); HANDLESCOPE(T); // Invoke main which will return the loadScriptPort. const Library& root_library = Library::Handle(Z, I->object_store()->root_library()); if (root_library.IsNull()) { if (FLAG_trace_service) { OS::PrintErr("vm-service: Embedder did not install a script."); } // Service isolate is not supported by embedder. return false; } ASSERT(!root_library.IsNull()); const String& entry_name = String::Handle(Z, String::New("main")); ASSERT(!entry_name.IsNull()); const Function& entry = Function::Handle( Z, root_library.LookupFunctionAllowPrivate(entry_name)); if (entry.IsNull()) { // Service isolate is not supported by embedder. if (FLAG_trace_service) { OS::PrintErr("vm-service: Embedder did not provide a main function."); } return false; } ASSERT(!entry.IsNull()); const Object& result = Object::Handle( Z, 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::PrintErr("vm-service: Calling main resulted in an error: %s", error.ToErrorCString()); } if (result.IsUnwindError()) { return true; } return false; } ASSERT(result.IsReceivePort()); const ReceivePort& rp = ReceivePort::Cast(result); ServiceIsolate::SetLoadPort(rp.Id()); return false; } }; void ServiceIsolate::Run() { // Grab the isolate create callback here to avoid race conditions with tests // that change this after Dart_Initialize returns. create_callback_ = Isolate::CreateCallback(); if (create_callback_ == NULL) { ServiceIsolate::FinishedInitializing(); return; } Dart::thread_pool()->Run(new RunServiceTask()); } void ServiceIsolate::KillServiceIsolate() { { MonitorLocker ml(monitor_); shutting_down_ = true; } Isolate::KillIfExists(isolate_, Isolate::kInternalKillMsg); { MonitorLocker ml(monitor_); while (shutting_down_) { ml.Wait(); } } } void ServiceIsolate::Shutdown() { if (IsRunning()) { { MonitorLocker ml(monitor_); shutting_down_ = true; } SendServiceExitMessage(); { MonitorLocker ml(monitor_); while (shutting_down_ && (port_ != ILLEGAL_PORT)) { ml.Wait(); } } } else { if (isolate_ != NULL) { // TODO(johnmccutchan,turnidge) When it is possible to properly create // the VMService object and set up its shutdown handler in the service // isolate's main() function, this case will no longer be possible and // can be removed. KillServiceIsolate(); } } if (server_address_ != NULL) { free(server_address_); server_address_ = NULL; } } void ServiceIsolate::BootVmServiceLibrary() { Thread* thread = Thread::Current(); const Library& vmservice_library = Library::Handle(Library::LookupLibrary(thread, Symbols::DartVMService())); ASSERT(!vmservice_library.IsNull()); const String& boot_function_name = String::Handle(String::New("boot")); const Function& boot_function = Function::Handle( vmservice_library.LookupFunctionAllowPrivate(boot_function_name)); ASSERT(!boot_function.IsNull()); const Object& result = Object::Handle( DartEntry::InvokeFunction(boot_function, Object::empty_array())); ASSERT(!result.IsNull()); if (result.IsUnwindError() || result.IsUnhandledException()) { Exceptions::PropagateError(Error::Cast(result)); } Dart_Port port = ILLEGAL_PORT; if (result.IsReceivePort()) { port = ReceivePort::Cast(result).Id(); } ASSERT(port != ILLEGAL_PORT); ServiceIsolate::SetServicePort(port); } void ServiceIsolate::VisitObjectPointers(ObjectPointerVisitor* visitor) {} } // namespace dart