Flutter Engine
dart_component_controller.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <fcntl.h>
8 #include <lib/async-loop/loop.h>
9 #include <lib/async/cpp/task.h>
10 #include <lib/async/default.h>
11 #include <lib/fdio/directory.h>
12 #include <lib/fdio/fd.h>
13 #include <lib/fdio/namespace.h>
14 #include <lib/fidl/cpp/string.h>
15 #include <lib/sys/cpp/service_directory.h>
16 #include <lib/syslog/global.h>
17 #include <lib/zx/clock.h>
18 #include <lib/zx/thread.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <zircon/status.h>
23 
24 #include <regex>
25 #include <utility>
26 
31 #include "third_party/dart/runtime/include/dart_tools_api.h"
37 
38 #include "builtin_libraries.h"
39 #include "logging.h"
40 
41 using tonic::ToDart;
42 
43 namespace dart_runner {
44 
45 constexpr char kDataKey[] = "data";
46 
47 namespace {
48 
49 void AfterTask(async_loop_t*, void*) {
52  // Verify that the queue exists, as this method could have been called back as
53  // part of the exit routine, after the destruction of the microtask queue.
54  if (queue) {
55  queue->RunMicrotasks();
56  }
57 }
58 
59 constexpr async_loop_config_t kLoopConfig = {
60  .default_accessors =
61  {
62  .getter = async_get_default_dispatcher,
63  .setter = async_set_default_dispatcher,
64  },
65  .make_default_for_current_thread = true,
66  .epilogue = &AfterTask,
67 };
68 
69 // Find the last path component.
70 // fuchsia-pkg://fuchsia.com/hello_dart#meta/hello_dart.cmx -> hello_dart.cmx
71 std::string GetLabelFromURL(const std::string& url) {
72  for (size_t i = url.length() - 1; i > 0; i--) {
73  if (url[i] == '/') {
74  return url.substr(i + 1, url.length() - 1);
75  }
76  }
77  return url;
78 }
79 
80 } // namespace
81 
83  fuchsia::sys::Package package,
84  fuchsia::sys::StartupInfo startup_info,
85  std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
86  fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller)
87  : loop_(new async::Loop(&kLoopConfig)),
88  label_(GetLabelFromURL(package.resolved_url)),
89  url_(std::move(package.resolved_url)),
90  package_(std::move(package)),
91  startup_info_(std::move(startup_info)),
92  runner_incoming_services_(runner_incoming_services),
93  binding_(this) {
94  for (size_t i = 0; i < startup_info_.program_metadata->size(); ++i) {
95  auto pg = startup_info_.program_metadata->at(i);
96  if (pg.key.compare(kDataKey) == 0) {
97  data_path_ = "pkg/" + pg.value;
98  }
99  }
100  if (data_path_.empty()) {
101  FX_LOGF(ERROR, LOG_TAG, "Could not find a /pkg/data directory for %s",
102  url_.c_str());
103  return;
104  }
105  if (controller.is_valid()) {
106  binding_.Bind(std::move(controller));
107  binding_.set_error_handler([this](zx_status_t status) { Kill(); });
108  }
109 
110  zx_status_t status =
111  zx::timer::create(ZX_TIMER_SLACK_LATE, ZX_CLOCK_MONOTONIC, &idle_timer_);
112  if (status != ZX_OK) {
113  FX_LOGF(INFO, LOG_TAG, "Idle timer creation failed: %s",
114  zx_status_get_string(status));
115  } else {
116  idle_wait_.set_object(idle_timer_.get());
117  idle_wait_.set_trigger(ZX_TIMER_SIGNALED);
118  idle_wait_.Begin(async_get_default_dispatcher());
119  }
120 }
121 
123  if (namespace_) {
124  fdio_ns_destroy(namespace_);
125  namespace_ = nullptr;
126  }
127  close(stdoutfd_);
128  close(stderrfd_);
129 }
130 
132  // Name the thread after the url of the component being launched.
133  zx::thread::self()->set_property(ZX_PROP_NAME, label_.c_str(), label_.size());
134  Dart_SetThreadName(label_.c_str());
135 
136  if (!SetupNamespace()) {
137  return false;
138  }
139 
140  if (SetupFromAppSnapshot()) {
141  FX_LOGF(INFO, LOG_TAG, "%s is running from an app snapshot", url_.c_str());
142  } else if (SetupFromKernel()) {
143  FX_LOGF(INFO, LOG_TAG, "%s is running from kernel", url_.c_str());
144  } else {
145  FX_LOGF(ERROR, LOG_TAG,
146  "Could not find a program in %s. Was data specified"
147  " correctly in the component manifest?",
148  url_.c_str());
149  return false;
150  }
151 
152  return true;
153 }
154 
155 constexpr char kTmpPath[] = "/tmp";
156 constexpr char kServiceRootPath[] = "/svc";
157 
158 bool DartComponentController::SetupNamespace() {
159  fuchsia::sys::FlatNamespace* flat = &startup_info_.flat_namespace;
160  zx_status_t status = fdio_ns_create(&namespace_);
161  if (status != ZX_OK) {
162  FX_LOG(ERROR, LOG_TAG, "Failed to create namespace");
163  return false;
164  }
165 
167 
168  for (size_t i = 0; i < flat->paths.size(); ++i) {
169  if (flat->paths.at(i) == kTmpPath) {
170  // /tmp is covered by the local memfs.
171  continue;
172  }
173 
174  zx::channel dir;
175  if (flat->paths.at(i) == kServiceRootPath) {
176  // clone /svc so component_context can still use it below
177  dir = zx::channel(fdio_service_clone(flat->directories.at(i).get()));
178  } else {
179  dir = std::move(flat->directories.at(i));
180  }
181 
182  zx_handle_t dir_handle = dir.release();
183  const char* path = flat->paths.at(i).data();
184  status = fdio_ns_bind(namespace_, path, dir_handle);
185  if (status != ZX_OK) {
186  FX_LOGF(ERROR, LOG_TAG, "Failed to bind %s to namespace: %s",
187  flat->paths.at(i).c_str(), zx_status_get_string(status));
188  zx_handle_close(dir_handle);
189  return false;
190  }
191  }
192 
193  return true;
194 }
195 
196 bool DartComponentController::SetupFromKernel() {
199  namespace_, data_path_ + "/app.dilplist", manifest)) {
200  return false;
201  }
202 
204  nullptr, "/pkg/data/isolate_core_snapshot_data.bin",
205  isolate_snapshot_data_)) {
206  return false;
207  }
209  nullptr, "/pkg/data/isolate_core_snapshot_instructions.bin",
210  isolate_snapshot_instructions_, true /* executable */)) {
211  return false;
212  }
213 
214  if (!CreateIsolate(isolate_snapshot_data_.address(),
215  isolate_snapshot_instructions_.address())) {
216  return false;
217  }
218 
219  Dart_EnterScope();
220 
221  std::string str(reinterpret_cast<const char*>(manifest.address()),
222  manifest.size());
223  Dart_Handle library = Dart_Null();
224  for (size_t start = 0; start < manifest.size();) {
225  size_t end = str.find("\n", start);
226  if (end == std::string::npos) {
227  FX_LOG(ERROR, LOG_TAG, "Malformed manifest");
228  Dart_ExitScope();
229  return false;
230  }
231 
232  std::string path = data_path_ + "/" + str.substr(start, end - start);
233  start = end + 1;
234 
236  if (!dart_utils::MappedResource::LoadFromNamespace(namespace_, path,
237  kernel)) {
238  FX_LOGF(ERROR, LOG_TAG, "Failed to find kernel: %s", path.c_str());
239  Dart_ExitScope();
240  return false;
241  }
242  library = Dart_LoadLibraryFromKernel(kernel.address(), kernel.size());
243  if (Dart_IsError(library)) {
244  FX_LOGF(ERROR, LOG_TAG, "Failed to load kernel: %s",
245  Dart_GetError(library));
246  Dart_ExitScope();
247  return false;
248  }
249 
250  kernel_peices_.emplace_back(std::move(kernel));
251  }
252  Dart_SetRootLibrary(library);
253 
254  Dart_Handle result = Dart_FinalizeLoading(false);
255  if (Dart_IsError(result)) {
256  FX_LOGF(ERROR, LOG_TAG, "Failed to FinalizeLoading: %s",
257  Dart_GetError(result));
258  Dart_ExitScope();
259  return false;
260  }
261 
262  return true;
263 }
264 
265 bool DartComponentController::SetupFromAppSnapshot() {
266 #if !defined(AOT_RUNTIME)
267  return false;
268 #else
269  // Load the ELF snapshot as available, and fall back to a blobs snapshot
270  // otherwise.
271  const uint8_t *isolate_data, *isolate_instructions;
272  if (elf_snapshot_.Load(namespace_, data_path_ + "/app_aot_snapshot.so")) {
273  isolate_data = elf_snapshot_.IsolateData();
274  isolate_instructions = elf_snapshot_.IsolateInstrs();
275  if (isolate_data == nullptr || isolate_instructions == nullptr) {
276  return false;
277  }
278  } else {
280  namespace_, data_path_ + "/isolate_snapshot_data.bin",
281  isolate_snapshot_data_)) {
282  return false;
283  }
285  namespace_, data_path_ + "/isolate_snapshot_instructions.bin",
286  isolate_snapshot_instructions_, true /* executable */)) {
287  return false;
288  }
289  }
290  return CreateIsolate(isolate_data, isolate_instructions);
291 #endif // defined(AOT_RUNTIME)
292 }
293 
294 int DartComponentController::SetupFileDescriptor(
295  fuchsia::sys::FileDescriptorPtr fd) {
296  if (!fd) {
297  return -1;
298  }
299  // fd->handle1 and fd->handle2 are no longer used.
300  int outfd = -1;
301  zx_status_t status = fdio_fd_create(fd->handle0.release(), &outfd);
302  if (status != ZX_OK) {
303  FX_LOGF(ERROR, LOG_TAG, "Failed to extract output fd: %s",
304  zx_status_get_string(status));
305  return -1;
306  }
307  return outfd;
308 }
309 
310 bool DartComponentController::CreateIsolate(
311  const uint8_t* isolate_snapshot_data,
312  const uint8_t* isolate_snapshot_instructions) {
313  // Create the isolate from the snapshot.
314  char* error = nullptr;
315 
316  // TODO(dart_runner): Pass if we start using tonic's loader.
317  intptr_t namespace_fd = -1;
318  // Freed in IsolateShutdownCallback.
319  auto state = new std::shared_ptr<tonic::DartState>(new tonic::DartState(
320  namespace_fd, [this](Dart_Handle result) { MessageEpilogue(result); }));
321 
322  isolate_ = Dart_CreateIsolateGroup(
323  url_.c_str(), label_.c_str(), isolate_snapshot_data,
324  isolate_snapshot_instructions, nullptr /* flags */, state, state, &error);
325  if (!isolate_) {
326  FX_LOGF(ERROR, LOG_TAG, "Dart_CreateIsolateGroup failed: %s", error);
327  return false;
328  }
329 
330  state->get()->SetIsolate(isolate_);
331 
333  [loop = loop_.get()](auto callback) {
334  async::PostTask(loop->dispatcher(), std::move(callback));
335  };
336  state->get()->message_handler().Initialize(dispatcher);
337 
338  state->get()->SetReturnCodeCallback(
339  [this](uint32_t return_code) { return_code_ = return_code; });
340 
341  return true;
342 }
343 
345  async::PostTask(loop_->dispatcher(), [loop = loop_.get(), app = this] {
346  if (!app->Main()) {
347  loop->Quit();
348  }
349  });
350  loop_->Run();
351  SendReturnCode();
352 }
353 
355  Dart_EnterScope();
356 
358 
359  std::vector<std::string> arguments =
360  startup_info_.launch_info.arguments.value_or(std::vector<std::string>());
361 
362  stdoutfd_ = SetupFileDescriptor(std::move(startup_info_.launch_info.out));
363  stderrfd_ = SetupFileDescriptor(std::move(startup_info_.launch_info.err));
364  auto directory_request =
365  std::move(startup_info_.launch_info.directory_request);
366 
367  auto* flat = &startup_info_.flat_namespace;
368  std::unique_ptr<sys::ServiceDirectory> svc;
369  for (size_t i = 0; i < flat->paths.size(); ++i) {
370  zx::channel dir;
371  if (flat->paths.at(i) == kServiceRootPath) {
372  svc = std::make_unique<sys::ServiceDirectory>(
373  std::move(flat->directories.at(i)));
374  break;
375  }
376  }
377  if (!svc) {
378  FX_LOG(ERROR, LOG_TAG, "Unable to get /svc for dart component");
379  return false;
380  }
381 
382  fidl::InterfaceHandle<fuchsia::sys::Environment> environment;
383  svc->Connect(environment.NewRequest());
384 
386  url_, namespace_, stdoutfd_, stderrfd_, std::move(environment),
387  std::move(directory_request), false /* service_isolate */);
388  namespace_ = nullptr;
389 
390  Dart_ExitScope();
391  Dart_ExitIsolate();
392  char* error = Dart_IsolateMakeRunnable(isolate_);
393  if (error != nullptr) {
394  Dart_EnterIsolate(isolate_);
395  Dart_ShutdownIsolate();
396  FX_LOGF(ERROR, LOG_TAG, "Unable to make isolate runnable: %s", error);
397  free(error);
398  return false;
399  }
400  Dart_EnterIsolate(isolate_);
401  Dart_EnterScope();
402 
403  Dart_Handle dart_arguments =
404  Dart_NewListOf(Dart_CoreType_String, arguments.size());
405  if (Dart_IsError(dart_arguments)) {
406  FX_LOGF(ERROR, LOG_TAG, "Failed to allocate Dart arguments list: %s",
407  Dart_GetError(dart_arguments));
408  Dart_ExitScope();
409  return false;
410  }
411  for (size_t i = 0; i < arguments.size(); i++) {
413  Dart_ListSetAt(dart_arguments, i, ToDart(arguments.at(i))));
414  }
415 
416  Dart_Handle argv[] = {
417  dart_arguments,
418  };
419 
420  Dart_Handle main_result = Dart_Invoke(Dart_RootLibrary(), ToDart("main"),
421  dart_utils::ArraySize(argv), argv);
422  if (Dart_IsError(main_result)) {
423  auto dart_state = tonic::DartState::Current();
424  if (!dart_state->has_set_return_code()) {
425  // The program hasn't set a return code meaning this exit is unexpected.
426  FX_LOG(ERROR, LOG_TAG, Dart_GetError(main_result));
427  return_code_ = tonic::GetErrorExitCode(main_result);
428 
429  dart_utils::HandleIfException(runner_incoming_services_, url_,
430  main_result);
431  }
432  Dart_ExitScope();
433  return false;
434  }
435 
436  Dart_ExitScope();
437  return true;
438 }
439 
440 void DartComponentController::Kill() {
441  if (Dart_CurrentIsolate()) {
444  if (queue) {
445  queue->Destroy();
446  }
447 
448  loop_->Quit();
449 
450  // TODO(rosswang): The docs warn of threading issues if doing this again,
451  // but without this, attempting to shut down the isolate finalizes app
452  // contexts that can't tell a shutdown is in progress and so fatal.
453  Dart_SetMessageNotifyCallback(nullptr);
454 
455  Dart_ShutdownIsolate();
456  }
457 }
458 
459 void DartComponentController::Detach() {
460  binding_.set_error_handler([](zx_status_t status) {});
461 }
462 
464  binding_.events().OnTerminated(return_code_,
465  fuchsia::sys::TerminationReason::EXITED);
466 }
467 
468 const zx::duration kIdleWaitDuration = zx::sec(2);
469 const zx::duration kIdleNotifyDuration = zx::msec(500);
470 const zx::duration kIdleSlack = zx::sec(1);
471 
472 void DartComponentController::MessageEpilogue(Dart_Handle result) {
473  auto dart_state = tonic::DartState::Current();
474  // If the Dart program has set a return code, then it is intending to shut
475  // down by way of a fatal error, and so there is no need to override
476  // return_code_.
477  if (dart_state->has_set_return_code()) {
478  Dart_ShutdownIsolate();
479  return;
480  }
481 
482  dart_utils::HandleIfException(runner_incoming_services_, url_, result);
483 
484  // Otherwise, see if there was any other error.
485  return_code_ = tonic::GetErrorExitCode(result);
486  if (return_code_ != 0) {
487  Dart_ShutdownIsolate();
488  return;
489  }
490 
491  idle_start_ = zx::clock::get_monotonic();
492  zx_status_t status =
493  idle_timer_.set(idle_start_ + kIdleWaitDuration, kIdleSlack);
494  if (status != ZX_OK) {
495  FX_LOGF(INFO, LOG_TAG, "Idle timer set failed: %s",
496  zx_status_get_string(status));
497  }
498 }
499 
500 void DartComponentController::OnIdleTimer(async_dispatcher_t* dispatcher,
501  async::WaitBase* wait,
502  zx_status_t status,
503  const zx_packet_signal* signal) {
504  if ((status != ZX_OK) || !(signal->observed & ZX_TIMER_SIGNALED) ||
505  !Dart_CurrentIsolate()) {
506  // Timer closed or isolate shutdown.
507  return;
508  }
509 
510  zx::time deadline = idle_start_ + kIdleWaitDuration;
511  zx::time now = zx::clock::get_monotonic();
512  if (now >= deadline) {
513  // No Dart message has been processed for kIdleWaitDuration: assume we'll
514  // stay idle for kIdleNotifyDuration.
515  Dart_NotifyIdle((now + kIdleNotifyDuration).get());
516  idle_start_ = zx::time(0);
517  idle_timer_.cancel(); // De-assert signal.
518  } else {
519  // Early wakeup or message pushed idle time forward: reschedule.
520  zx_status_t status = idle_timer_.set(deadline, kIdleSlack);
521  if (status != ZX_OK) {
522  FX_LOGF(INFO, LOG_TAG, "Idle timer set failed: %s",
523  zx_status_get_string(status));
524  }
525  }
526  wait->Begin(dispatcher); // ignore errors
527 }
528 
529 } // namespace dart_runner
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
static DartMicrotaskQueue * GetForCurrentThread()
size_t ArraySize(T(&array)[SIZE])
Definition: inlines.h:26
const uint8_t uint32_t uint32_t GError ** error
const uint8_t * IsolateData() const
Definition: ref_ptr.h:252
static DartState * Current()
Definition: dart_state.cc:56
int GetErrorExitCode(Dart_Handle handle)
Definition: dart_error.cc:46
GAsyncResult * result
constexpr char kTmpPath[]
FlKeyEvent FlKeyResponderAsyncCallback callback
static bool LoadFromNamespace(fdio_ns_t *namespc, const std::string &path, MappedResource &resource, bool executable=false)
std::function< void(std::function< void(void)>)> TaskDispatcher
bool Load(fdio_ns_t *namespc, const std::string &path)
void HandleIfException(std::shared_ptr<::sys::ServiceDirectory > services, const std::string &component_url, Dart_Handle result)
void InitBuiltinLibrariesForIsolate(const std::string &script_uri, fdio_ns_t *namespc, int stdoutfd, int stderrfd, fidl::InterfaceHandle< fuchsia::sys::Environment > environment, zx::channel directory_request, bool service_isolate)
const uint8_t * address() const
const zx::duration kIdleNotifyDuration
#define LOG_TAG
Definition: logging.h:11
DartComponentController(fuchsia::sys::Package package, fuchsia::sys::StartupInfo startup_info, std::shared_ptr< sys::ServiceDirectory > runner_incoming_services, fidl::InterfaceRequest< fuchsia::sys::ComponentController > controller)
const zx::duration kIdleSlack
Dart_Handle ToDart(const T &object)
bool LogIfError(Dart_Handle handle)
Definition: dart_error.cc:15
const zx::duration kIdleWaitDuration
const uint8_t * IsolateInstrs() const
AtkStateType state
constexpr char kDataKey[]
constexpr char kServiceRootPath[]
static void SetupComponent(fdio_ns_t *ns)
Definition: tempfs.cc:63