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