1// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/service_isolate.h"
6
7#include "vm/compiler/jit/compiler.h"
8#include "vm/dart_api_impl.h"
9#include "vm/dart_api_message.h"
10#include "vm/dart_entry.h"
11#include "vm/isolate.h"
12#include "vm/lockers.h"
13#include "vm/message.h"
14#include "vm/message_handler.h"
15#include "vm/native_arguments.h"
16#include "vm/native_entry.h"
17#include "vm/object.h"
18#include "vm/object_store.h"
19#include "vm/port.h"
20#include "vm/service.h"
21#include "vm/symbols.h"
22#include "vm/thread_pool.h"
23#include "vm/timeline.h"
24
25#if !defined(PRODUCT)
26
27namespace dart {
28
29#define Z (T->zone())
30
31DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests.");
32DEFINE_FLAG(bool,
33 trace_service_pause_events,
34 false,
35 "Trace VM service isolate pause events.");
36DEFINE_FLAG(bool,
37 trace_service_verbose,
38 false,
39 "Provide extra service tracing information.");
40
41// These must be kept in sync with service/constants.dart
42#define VM_SERVICE_ISOLATE_EXIT_MESSAGE_ID 0
43#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
44#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
45
46#define VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID 3
47#define VM_SERVICE_SERVER_INFO_MESSAGE_ID 4
48
49#define VM_SERVICE_METHOD_CALL_FROM_NATIVE 5
50
51static ArrayPtr MakeServiceControlMessage(Dart_Port port_id,
52 intptr_t code,
53 const String& name) {
54 const Array& list = Array::Handle(Array::New(4));
55 ASSERT(!list.IsNull());
56 const Integer& code_int = Integer::Handle(Integer::New(code));
57 const Integer& port_int = Integer::Handle(Integer::New(port_id));
58 const SendPort& send_port = SendPort::Handle(SendPort::New(port_id));
59 list.SetAt(0, code_int);
60 list.SetAt(1, port_int);
61 list.SetAt(2, send_port);
62 list.SetAt(3, name);
63 return list.raw();
64}
65
66static ArrayPtr MakeServerControlMessage(const SendPort& sp,
67 intptr_t code,
68 bool enable = false) {
69 const Array& list = Array::Handle(Array::New(3));
70 ASSERT(!list.IsNull());
71 list.SetAt(0, Integer::Handle(Integer::New(code)));
72 list.SetAt(1, sp);
73 list.SetAt(2, Bool::Get(enable));
74 return list.raw();
75}
76
77const char* ServiceIsolate::kName = DART_VM_SERVICE_ISOLATE_NAME;
78Dart_IsolateGroupCreateCallback ServiceIsolate::create_group_callback_ = NULL;
79Monitor* ServiceIsolate::monitor_ = new Monitor();
80ServiceIsolate::State ServiceIsolate::state_ = ServiceIsolate::kStopped;
81Isolate* ServiceIsolate::isolate_ = NULL;
82Dart_Port ServiceIsolate::port_ = ILLEGAL_PORT;
83Dart_Port ServiceIsolate::origin_ = ILLEGAL_PORT;
84char* ServiceIsolate::server_address_ = NULL;
85char* ServiceIsolate::startup_failure_reason_ = nullptr;
86
87void ServiceIsolate::RequestServerInfo(const SendPort& sp) {
88 const Array& message = Array::Handle(MakeServerControlMessage(
89 sp, VM_SERVICE_SERVER_INFO_MESSAGE_ID, false /* ignored */));
90 ASSERT(!message.IsNull());
91 MessageWriter writer(false);
92 PortMap::PostMessage(
93 writer.WriteMessage(message, port_, Message::kNormalPriority));
94}
95
96void ServiceIsolate::ControlWebServer(const SendPort& sp, bool enable) {
97 const Array& message = Array::Handle(MakeServerControlMessage(
98 sp, VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID, enable));
99 ASSERT(!message.IsNull());
100 MessageWriter writer(false);
101 PortMap::PostMessage(
102 writer.WriteMessage(message, port_, Message::kNormalPriority));
103}
104
105void ServiceIsolate::SetServerAddress(const char* address) {
106 if (server_address_ != NULL) {
107 free(server_address_);
108 server_address_ = NULL;
109 }
110 if (address == NULL) {
111 return;
112 }
113 server_address_ = Utils::StrDup(address);
114}
115
116bool ServiceIsolate::NameEquals(const char* name) {
117 ASSERT(name != NULL);
118 return strcmp(name, kName) == 0;
119}
120
121bool ServiceIsolate::Exists() {
122 MonitorLocker ml(monitor_);
123 return isolate_ != NULL;
124}
125
126bool ServiceIsolate::IsRunning() {
127 MonitorLocker ml(monitor_);
128 return (port_ != ILLEGAL_PORT) && (isolate_ != NULL);
129}
130
131bool ServiceIsolate::IsServiceIsolate(const Isolate* isolate) {
132 MonitorLocker ml(monitor_);
133 return isolate != nullptr && isolate == isolate_;
134}
135
136bool ServiceIsolate::IsServiceIsolateDescendant(Isolate* isolate) {
137 MonitorLocker ml(monitor_);
138 return isolate->origin_id() == origin_;
139}
140
141Dart_Port ServiceIsolate::Port() {
142 MonitorLocker ml(monitor_);
143 return port_;
144}
145
146void ServiceIsolate::WaitForServiceIsolateStartup() {
147 MonitorLocker ml(monitor_);
148 while (state_ == kStarting) {
149 ml.Wait();
150 }
151}
152
153bool ServiceIsolate::SendServiceRpc(uint8_t* request_json,
154 intptr_t request_json_length,
155 Dart_Port reply_port,
156 char** error) {
157 // Keep in sync with "sdk/lib/vmservice/vmservice.dart:_handleNativeRpcCall".
158 Dart_CObject opcode;
159 opcode.type = Dart_CObject_kInt32;
160 opcode.value.as_int32 = VM_SERVICE_METHOD_CALL_FROM_NATIVE;
161
162 Dart_CObject message;
163 message.type = Dart_CObject_kTypedData;
164 message.value.as_typed_data.type = Dart_TypedData_kUint8;
165 message.value.as_typed_data.length = request_json_length;
166 message.value.as_typed_data.values = request_json;
167
168 Dart_CObject send_port;
169 send_port.type = Dart_CObject_kSendPort;
170 send_port.value.as_send_port.id = reply_port;
171 send_port.value.as_send_port.origin_id = ILLEGAL_PORT;
172
173 Dart_CObject* request_array[] = {
174 &opcode,
175 &message,
176 &send_port,
177 };
178
179 Dart_CObject request;
180 request.type = Dart_CObject_kArray;
181 request.value.as_array.values = request_array;
182 request.value.as_array.length = ARRAY_SIZE(request_array);
183 ServiceIsolate::WaitForServiceIsolateStartup();
184 Dart_Port service_port = ServiceIsolate::Port();
185
186 const bool success = Dart_PostCObject(service_port, &request);
187
188 if (!success && error != nullptr) {
189 if (service_port == ILLEGAL_PORT) {
190 if (startup_failure_reason_ != nullptr) {
191 *error = OS::SCreate(/*zone=*/nullptr,
192 "Service isolate failed to start up: %s.",
193 startup_failure_reason_);
194 } else {
195 *error = Utils::StrDup("No service isolate port was found.");
196 }
197 } else {
198 *error = Utils::StrDup("Was unable to post message to service isolate.");
199 }
200 }
201
202 return success;
203}
204
205bool ServiceIsolate::SendIsolateStartupMessage() {
206 if (!IsRunning()) {
207 return false;
208 }
209 Thread* thread = Thread::Current();
210 Isolate* isolate = thread->isolate();
211 if (!FLAG_show_invisible_isolates && Isolate::IsVMInternalIsolate(isolate)) {
212 return false;
213 }
214 ASSERT(isolate != NULL);
215 HANDLESCOPE(thread);
216 const String& name = String::Handle(String::New(isolate->name()));
217 ASSERT(!name.IsNull());
218 const Array& list = Array::Handle(MakeServiceControlMessage(
219 Dart_GetMainPortId(), VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID, name));
220 ASSERT(!list.IsNull());
221 MessageWriter writer(false);
222 if (FLAG_trace_service) {
223 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Isolate %s %" Pd64
224 " registered.\n",
225 name.ToCString(), Dart_GetMainPortId());
226 }
227 return PortMap::PostMessage(
228 writer.WriteMessage(list, port_, Message::kNormalPriority));
229}
230
231bool ServiceIsolate::SendIsolateShutdownMessage() {
232 if (!IsRunning()) {
233 return false;
234 }
235 Thread* thread = Thread::Current();
236 Isolate* isolate = thread->isolate();
237 if (!FLAG_show_invisible_isolates && Isolate::IsVMInternalIsolate(isolate)) {
238 return false;
239 }
240 ASSERT(isolate != NULL);
241 HANDLESCOPE(thread);
242 const String& name = String::Handle(String::New(isolate->name()));
243 ASSERT(!name.IsNull());
244 const Array& list = Array::Handle(MakeServiceControlMessage(
245 Dart_GetMainPortId(), VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID, name));
246 ASSERT(!list.IsNull());
247 MessageWriter writer(false);
248 if (FLAG_trace_service) {
249 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Isolate %s %" Pd64
250 " deregistered.\n",
251 name.ToCString(), Dart_GetMainPortId());
252 }
253 return PortMap::PostMessage(
254 writer.WriteMessage(list, port_, Message::kNormalPriority));
255}
256
257void ServiceIsolate::SendServiceExitMessage() {
258 if (!IsRunning()) {
259 return;
260 }
261 if (FLAG_trace_service) {
262 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
263 ": sending service exit message.\n");
264 }
265
266 Dart_CObject code;
267 code.type = Dart_CObject_kInt32;
268 code.value.as_int32 = VM_SERVICE_ISOLATE_EXIT_MESSAGE_ID;
269 Dart_CObject* values[1] = {&code};
270
271 Dart_CObject message;
272 message.type = Dart_CObject_kArray;
273 message.value.as_array.length = 1;
274 message.value.as_array.values = values;
275
276 ApiMessageWriter writer;
277 PortMap::PostMessage(
278 writer.WriteCMessage(&message, port_, Message::kNormalPriority));
279}
280
281void ServiceIsolate::SetServicePort(Dart_Port port) {
282 MonitorLocker ml(monitor_);
283 port_ = port;
284}
285
286void ServiceIsolate::SetServiceIsolate(Isolate* isolate) {
287 MonitorLocker ml(monitor_);
288 isolate_ = isolate;
289 if (isolate_ != NULL) {
290 isolate_->set_is_service_isolate(true);
291 origin_ = isolate_->origin_id();
292 }
293}
294
295void ServiceIsolate::MaybeMakeServiceIsolate(Isolate* I) {
296 Thread* T = Thread::Current();
297 ASSERT(I == T->isolate());
298 ASSERT(I != NULL);
299 ASSERT(I->name() != NULL);
300 if (!ServiceIsolate::NameEquals(I->name())) {
301 // Not service isolate.
302 return;
303 }
304 if (Exists()) {
305 // Service isolate already exists.
306 return;
307 }
308 SetServiceIsolate(I);
309}
310
311void ServiceIsolate::FinishedExiting() {
312 MonitorLocker ml(monitor_);
313 ASSERT(state_ == kStarted || state_ == kStopping);
314 state_ = kStopped;
315 ml.NotifyAll();
316}
317
318void ServiceIsolate::FinishedInitializing() {
319 MonitorLocker ml(monitor_);
320 ASSERT(state_ == kStarting);
321 state_ = kStarted;
322 ml.NotifyAll();
323}
324
325void ServiceIsolate::InitializingFailed(char* error) {
326 MonitorLocker ml(monitor_);
327 ASSERT(state_ == kStarting);
328 state_ = kStopped;
329 startup_failure_reason_ = error;
330 ml.NotifyAll();
331}
332
333class RunServiceTask : public ThreadPool::Task {
334 public:
335 virtual void Run() {
336 ASSERT(Isolate::Current() == NULL);
337#if defined(SUPPORT_TIMELINE)
338 TimelineBeginEndScope tbes(Timeline::GetVMStream(),
339 "ServiceIsolateStartup");
340#endif // SUPPORT_TIMELINE
341 char* error = NULL;
342 Isolate* isolate = NULL;
343
344 const auto create_group_callback = ServiceIsolate::create_group_callback();
345 ASSERT(create_group_callback != NULL);
346
347 Dart_IsolateFlags api_flags;
348 Isolate::FlagsInitialize(&api_flags);
349
350 isolate = reinterpret_cast<Isolate*>(
351 create_group_callback(ServiceIsolate::kName, ServiceIsolate::kName,
352 NULL, NULL, &api_flags, NULL, &error));
353 if (isolate == NULL) {
354 if (FLAG_trace_service) {
355 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
356 ": Isolate creation error: %s\n",
357 error);
358 }
359
360 char* formatted_error = OS::SCreate(
361 /*zone=*/nullptr, "Invoking the 'create_group' failed with: '%s'",
362 error);
363
364 free(error);
365 error = nullptr;
366 ServiceIsolate::InitializingFailed(formatted_error);
367 return;
368 }
369
370 bool got_unwind;
371 {
372 ASSERT(Isolate::Current() == NULL);
373 StartIsolateScope start_scope(isolate);
374 got_unwind = RunMain(isolate);
375 }
376
377 // FinishedInitializing should be called irrespective of whether
378 // running main caused an error or not. Otherwise, other isolates
379 // waiting for service isolate to come up will deadlock.
380 ServiceIsolate::FinishedInitializing();
381
382 if (got_unwind) {
383 ShutdownIsolate(reinterpret_cast<uword>(isolate));
384 return;
385 }
386
387 isolate->message_handler()->Run(Dart::thread_pool(), NULL, ShutdownIsolate,
388 reinterpret_cast<uword>(isolate));
389 }
390
391 protected:
392 static void ShutdownIsolate(uword parameter) {
393 if (FLAG_trace_service) {
394 OS::PrintErr("vm-service: ShutdownIsolate\n");
395 }
396 Isolate* I = reinterpret_cast<Isolate*>(parameter);
397 ASSERT(ServiceIsolate::IsServiceIsolate(I));
398 {
399 // Print the error if there is one. This may execute dart code to
400 // print the exception object, so we need to use a StartIsolateScope.
401 ASSERT(Isolate::Current() == NULL);
402 StartIsolateScope start_scope(I);
403 Thread* T = Thread::Current();
404 ASSERT(I == T->isolate());
405 I->WaitForOutstandingSpawns();
406 StackZone zone(T);
407 HandleScope handle_scope(T);
408 Error& error = Error::Handle(Z);
409 error = T->sticky_error();
410 if (!error.IsNull() && !error.IsUnwindError()) {
411 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Error: %s\n",
412 error.ToErrorCString());
413 }
414 error = I->sticky_error();
415 if (!error.IsNull() && !error.IsUnwindError()) {
416 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Error: %s\n",
417 error.ToErrorCString());
418 }
419 Dart::RunShutdownCallback();
420 }
421
422 // Shut the isolate down.
423 Dart::ShutdownIsolate(I);
424 if (FLAG_trace_service) {
425 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Shutdown.\n");
426 }
427 ServiceIsolate::FinishedExiting();
428 }
429
430 bool RunMain(Isolate* I) {
431 Thread* T = Thread::Current();
432 ASSERT(I == T->isolate());
433 StackZone zone(T);
434 HANDLESCOPE(T);
435 // Invoke main which will set up the service port.
436 const Library& root_library =
437 Library::Handle(Z, I->object_store()->root_library());
438 if (root_library.IsNull()) {
439 if (FLAG_trace_service) {
440 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
441 ": Embedder did not install a script.");
442 }
443 // Service isolate is not supported by embedder.
444 return false;
445 }
446 ASSERT(!root_library.IsNull());
447 const String& entry_name = String::Handle(Z, String::New("main"));
448 ASSERT(!entry_name.IsNull());
449 const Function& entry = Function::Handle(
450 Z, root_library.LookupFunctionAllowPrivate(entry_name));
451 if (entry.IsNull()) {
452 // Service isolate is not supported by embedder.
453 if (FLAG_trace_service) {
454 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
455 ": Embedder did not provide a main function.");
456 }
457 return false;
458 }
459 ASSERT(!entry.IsNull());
460 const Object& result = Object::Handle(
461 Z, DartEntry::InvokeFunction(entry, Object::empty_array()));
462 if (result.IsError()) {
463 // Service isolate did not initialize properly.
464 if (FLAG_trace_service) {
465 const Error& error = Error::Cast(result);
466 OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
467 ": Calling main resulted in an error: %s",
468 error.ToErrorCString());
469 }
470 if (result.IsUnwindError()) {
471 return true;
472 }
473 return false;
474 }
475 return false;
476 }
477};
478
479void ServiceIsolate::Run() {
480 {
481 MonitorLocker ml(monitor_);
482 ASSERT(state_ == kStopped);
483 state_ = kStarting;
484 ml.NotifyAll();
485 }
486 // Grab the isolate create callback here to avoid race conditions with tests
487 // that change this after Dart_Initialize returns.
488 create_group_callback_ = Isolate::CreateGroupCallback();
489 if (create_group_callback_ == NULL) {
490 ServiceIsolate::InitializingFailed(
491 Utils::StrDup("The 'create_group' callback was not provided"));
492 return;
493 }
494 bool task_started = Dart::thread_pool()->Run<RunServiceTask>();
495 ASSERT(task_started);
496}
497
498void ServiceIsolate::KillServiceIsolate() {
499 {
500 MonitorLocker ml(monitor_);
501 if (state_ == kStopped) {
502 return;
503 }
504 ASSERT(state_ == kStarted);
505 state_ = kStopping;
506 ml.NotifyAll();
507 }
508 Isolate::KillIfExists(isolate_, Isolate::kInternalKillMsg);
509 {
510 MonitorLocker ml(monitor_);
511 while (state_ == kStopping) {
512 ml.Wait();
513 }
514 ASSERT(state_ == kStopped);
515 }
516}
517
518void ServiceIsolate::Shutdown() {
519 {
520 MonitorLocker ml(monitor_);
521 while (state_ == kStarting) {
522 ml.Wait();
523 }
524 }
525
526 if (IsRunning()) {
527 {
528 MonitorLocker ml(monitor_);
529 ASSERT(state_ == kStarted);
530 state_ = kStopping;
531 ml.NotifyAll();
532 }
533 SendServiceExitMessage();
534 {
535 MonitorLocker ml(monitor_);
536 while (state_ == kStopping) {
537 ml.Wait();
538 }
539 ASSERT(state_ == kStopped);
540 }
541 } else {
542 if (isolate_ != NULL) {
543 // TODO(johnmccutchan,turnidge) When it is possible to properly create
544 // the VMService object and set up its shutdown handler in the service
545 // isolate's main() function, this case will no longer be possible and
546 // can be removed.
547 KillServiceIsolate();
548 }
549 }
550 if (server_address_ != NULL) {
551 free(server_address_);
552 server_address_ = NULL;
553 }
554
555 if (startup_failure_reason_ != nullptr) {
556 free(startup_failure_reason_);
557 startup_failure_reason_ = nullptr;
558 }
559}
560
561void ServiceIsolate::BootVmServiceLibrary() {
562 Thread* thread = Thread::Current();
563 const Library& vmservice_library =
564 Library::Handle(Library::LookupLibrary(thread, Symbols::DartVMService()));
565 ASSERT(!vmservice_library.IsNull());
566 const String& boot_function_name = String::Handle(String::New("boot"));
567 const Function& boot_function = Function::Handle(
568 vmservice_library.LookupFunctionAllowPrivate(boot_function_name));
569 ASSERT(!boot_function.IsNull());
570 const Object& result = Object::Handle(
571 DartEntry::InvokeFunction(boot_function, Object::empty_array()));
572 ASSERT(!result.IsNull());
573 if (result.IsUnwindError() || result.IsUnhandledException()) {
574 Exceptions::PropagateError(Error::Cast(result));
575 }
576 Dart_Port port = ILLEGAL_PORT;
577 if (result.IsReceivePort()) {
578 port = ReceivePort::Cast(result).Id();
579 }
580 ASSERT(port != ILLEGAL_PORT);
581 ServiceIsolate::SetServicePort(port);
582}
583
584void ServiceIsolate::VisitObjectPointers(ObjectPointerVisitor* visitor) {}
585
586} // namespace dart
587
588#endif // !defined(PRODUCT)
589