Flutter Engine
runner.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 
5 #include "runner.h"
6 
7 #include <fcntl.h>
8 #include <fuchsia/mem/cpp/fidl.h>
9 #include <lib/async/cpp/task.h>
10 #include <lib/inspect/cpp/inspect.h>
11 #include <lib/trace-engine/instrumentation.h>
12 #include <zircon/status.h>
13 #include <zircon/types.h>
14 
15 #include <cstdint>
16 #include <sstream>
17 #include <utility>
18 
19 #include "flutter/fml/make_copyable.h"
20 #include "flutter/lib/ui/text/font_collection.h"
21 #include "flutter/runtime/dart_vm.h"
22 #include "lib/async/default.h"
23 #include "lib/sys/cpp/component_context.h"
26 #include "runtime/dart/utils/vmo.h"
28 #include "third_party/icu/source/common/unicode/udata.h"
29 #include "third_party/skia/include/core/SkGraphics.h"
30 
31 namespace flutter_runner {
32 
33 namespace {
34 
35 static constexpr char kIcuDataPath[] = "/pkg/data/icudtl.dat";
36 
37 // Environment variable containing the path to the directory containing the
38 // timezone files.
39 static constexpr char kICUTZEnv[] = "ICU_TIMEZONE_FILES_DIR";
40 
41 // The data directory containing ICU timezone data files.
42 static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le";
43 
44 // Map the memory into the process and return a pointer to the memory.
45 uintptr_t GetICUData(const fuchsia::mem::Buffer& icu_data) {
46  uint64_t data_size = icu_data.size;
47  if (data_size > std::numeric_limits<size_t>::max())
48  return 0u;
49 
50  uintptr_t data = 0u;
51  zx_status_t status =
52  zx::vmar::root_self()->map(ZX_VM_PERM_READ, 0, icu_data.vmo, 0,
53  static_cast<size_t>(data_size), &data);
54  if (status == ZX_OK) {
55  return data;
56  }
57 
58  return 0u;
59 }
60 
61 // Initializes the timezone data if available. Timezone data file in Fuchsia
62 // is at a fixed directory path. Returns true on success. As a side effect
63 // sets the value of the environment variable "ICU_TIMEZONE_FILES_DIR" to a
64 // fixed value which is fuchsia-specific.
65 bool InitializeTZData() {
66  // We need the ability to change the env variable for testing, so not
67  // overwriting if set.
68  setenv(kICUTZEnv, kICUTZDataDir, 0 /* No overwrite */);
69 
70  const std::string tzdata_dir = getenv(kICUTZEnv);
71  // Try opening the path to check if present. No need to verify that it is a
72  // directory since ICU loading will return an error if the TZ data path is
73  // wrong.
74  int fd = openat(AT_FDCWD, tzdata_dir.c_str(), O_RDONLY);
75  if (fd < 0) {
76  FML_LOG(INFO) << "Could not open: '" << tzdata_dir
77  << "', proceeding without loading the timezone database: "
78  << strerror(errno);
79  return false;
80  }
81  if (close(fd)) {
82  FML_LOG(WARNING) << "Could not close: " << tzdata_dir << ": "
83  << strerror(errno);
84  }
85  return true;
86 }
87 
88 // Return value indicates if initialization was successful.
89 bool InitializeICU() {
90  const char* data_path = kIcuDataPath;
91 
92  fuchsia::mem::Buffer icu_data;
93  if (!dart_utils::VmoFromFilename(data_path, false, &icu_data)) {
94  return false;
95  }
96 
97  uintptr_t data = GetICUData(icu_data);
98  if (!data) {
99  return false;
100  }
101 
102  // If the loading fails, soldier on. The loading is optional as we don't
103  // want to crash the engine in transition.
104  InitializeTZData();
105 
106  // Pass the data to ICU.
107  UErrorCode err = U_ZERO_ERROR;
108  udata_setCommonData(reinterpret_cast<const char*>(data), &err);
109  if (err != U_ZERO_ERROR) {
110  FML_LOG(ERROR) << "error loading ICU data: " << err;
111  return false;
112  }
113  return true;
114 }
115 
116 } // namespace
117 
118 static void SetProcessName() {
119  std::stringstream stream;
120 #if defined(DART_PRODUCT)
121  stream << "io.flutter.product_runner.";
122 #else
123  stream << "io.flutter.runner.";
124 #endif
126  stream << "aot";
127  } else {
128  stream << "jit";
129  }
130  const auto name = stream.str();
131  zx::process::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size());
132 }
133 
134 static void SetThreadName(const std::string& thread_name) {
135  zx::thread::self()->set_property(ZX_PROP_NAME, thread_name.c_str(),
136  thread_name.size());
137 }
138 
139 #if !defined(DART_PRODUCT)
140 // Register native symbol information for the Dart VM's profiler.
141 static void RegisterProfilerSymbols(const char* symbols_path,
142  const char* dso_name) {
143  std::string* symbols = new std::string();
144  if (dart_utils::ReadFileToString(symbols_path, symbols)) {
145  Dart_AddSymbols(dso_name, symbols->data(), symbols->size());
146  } else {
147  FML_LOG(ERROR) << "Failed to load " << symbols_path;
148  }
149 }
150 #endif // !defined(DART_PRODUCT)
151 
153  sys::ComponentContext* context)
154  : task_runner_(task_runner), context_(context) {
155 #if !defined(DART_PRODUCT)
156  // The VM service isolate uses the process-wide namespace. It writes the
157  // vm service protocol port under /tmp. The VMServiceObject exposes that
158  // port number to The Hub.
159  context_->outgoing()->debug_dir()->AddEntry(
161  std::make_unique<dart_utils::VMServiceObject>());
162 
163  inspect::Inspector* inspector = dart_utils::RootInspectNode::GetInspector();
164  inspector->GetRoot().CreateLazyValues(
165  "vmservice_port",
166  [&]() {
167  inspect::Inspector inspector;
168  dart_utils::VMServiceObject::LazyEntryVector out;
170  std::string name = "";
171  if (out.size() > 0) {
172  name = out[0].name;
173  }
174  inspector.GetRoot().CreateString("vm_service_port", name, &inspector);
175  return fpromise::make_ok_promise(inspector);
176  },
177  inspector);
178 
179  SetupTraceObserver();
180 #endif // !defined(DART_PRODUCT)
181 
182  SkGraphics::Init();
183 
184  SetupICU();
185 
186  SetProcessName();
187 
188  SetThreadName("io.flutter.runner.main");
189 
190  context_->outgoing()->AddPublicService<fuchsia::sys::Runner>(
191  std::bind(&Runner::RegisterComponentV1, this, std::placeholders::_1));
192  context_->outgoing()
193  ->AddPublicService<fuchsia::component::runner::ComponentRunner>(
194  std::bind(&Runner::RegisterComponentV2, this, std::placeholders::_1));
195 
196 #if !defined(DART_PRODUCT)
197  if (Dart_IsPrecompiledRuntime()) {
198  RegisterProfilerSymbols("pkg/data/flutter_aot_runner.dartprofilersymbols",
199  "");
200  } else {
201  RegisterProfilerSymbols("pkg/data/flutter_jit_runner.dartprofilersymbols",
202  "");
203  }
204 #endif // !defined(DART_PRODUCT)
205 }
206 
208  context_->outgoing()->RemovePublicService<fuchsia::sys::Runner>();
209  context_->outgoing()
210  ->RemovePublicService<fuchsia::component::runner::ComponentRunner>();
211 
212 #if !defined(DART_PRODUCT)
213  trace_observer_->Stop();
214 #endif // !defined(DART_PRODUCT)
215 }
216 
217 // CF v1 lifecycle methods.
218 
219 void Runner::RegisterComponentV1(
220  fidl::InterfaceRequest<fuchsia::sys::Runner> request) {
221  active_components_v1_bindings_.AddBinding(this, std::move(request));
222 }
223 
224 void Runner::StartComponent(
225  fuchsia::sys::Package package,
226  fuchsia::sys::StartupInfo startup_info,
227  fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
228  // TRACE_DURATION currently requires that the string data does not change
229  // in the traced scope. Since |package| gets moved in the Component::Create
230  // call below, we cannot ensure that |package.resolved_url| does not move or
231  // change, so we make a copy to pass to TRACE_DURATION.
232  // TODO(PT-169): Remove this copy when TRACE_DURATION reads string arguments
233  // eagerly.
234  std::string url_copy = package.resolved_url;
235  TRACE_EVENT1("flutter", "StartComponent", "url", url_copy.c_str());
236  // Notes on component termination: Components typically terminate on the
237  // thread on which they were created. This usually means the thread was
238  // specifically created to host the component. But we want to ensure that
239  // access to the active components collection is made on the same thread. So
240  // we capture the runner in the termination callback. There is no risk of
241  // there being multiple component runner instances in the process at the same
242  // time. So it is safe to use the raw pointer.
243  ComponentV1::TerminationCallback termination_callback =
244  [component_runner = this](const ComponentV1* component) {
245  component_runner->task_runner_->PostTask(
246  [component_runner, component]() {
247  component_runner->OnComponentV1Terminate(component);
248  });
249  };
250 
251  ActiveComponentV1 active_component = ComponentV1::Create(
252  std::move(termination_callback), // termination callback
253  std::move(package), // component package
254  std::move(startup_info), // startup info
255  context_->svc(), // runner incoming services
256  std::move(controller) // controller request
257  );
258 
259  auto key = active_component.component.get();
260  active_components_v1_[key] = std::move(active_component);
261 }
262 
263 void Runner::OnComponentV1Terminate(const ComponentV1* component) {
264  auto app = active_components_v1_.find(component);
265  if (app == active_components_v1_.end()) {
266  FML_LOG(INFO)
267  << "The remote end of the component runner tried to terminate an "
268  "component that has already been terminated, possibly because we "
269  "initiated the termination";
270  return;
271  }
272  ActiveComponentV1& active_component = app->second;
273 
274  // Grab the items out of the entry because we will have to rethread the
275  // destruction.
276  std::unique_ptr<ComponentV1> component_to_destroy =
277  std::move(active_component.component);
278  std::unique_ptr<fml::Thread> component_thread =
279  std::move(active_component.platform_thread);
280 
281  // Delete the entry.
282  active_components_v1_.erase(component);
283 
284  // Post the task to destroy the component and quit its message loop.
285  component_thread->GetTaskRunner()->PostTask(fml::MakeCopyable(
286  [instance = std::move(component_to_destroy),
287  thread = component_thread.get()]() mutable { instance.reset(); }));
288 
289  // Terminate and join the thread's message loop.
290  component_thread->Join();
291 }
292 
293 // CF v2 lifecycle methods.
294 
295 void Runner::RegisterComponentV2(
296  fidl::InterfaceRequest<fuchsia::component::runner::ComponentRunner>
297  request) {
298  active_components_v2_bindings_.AddBinding(this, std::move(request));
299 }
300 
301 void Runner::Start(
302  fuchsia::component::runner::ComponentStartInfo start_info,
303  fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
304  controller) {
305  // TRACE_DURATION currently requires that the string data does not change
306  // in the traced scope. Since |package| gets moved in the ComponentV2::Create
307  // call below, we cannot ensure that |package.resolved_url| does not move or
308  // change, so we make a copy to pass to TRACE_DURATION.
309  // TODO(PT-169): Remove this copy when TRACE_DURATION reads string arguments
310  // eagerly.
311  const std::string url_copy = start_info.resolved_url();
312  TRACE_EVENT1("flutter", "Start", "url", url_copy.c_str());
313 
314  // Notes on component termination: Components typically terminate on the
315  // thread on which they were created. This usually means the thread was
316  // specifically created to host the component. But we want to ensure that
317  // access to the active components collection is made on the same thread. So
318  // we capture the runner in the termination callback. There is no risk of
319  // there being multiple component runner instances in the process at the same
320  // time. So it is safe to use the raw pointer.
321  ComponentV2::TerminationCallback termination_callback =
322  [component_runner = this](const ComponentV2* component) {
323  component_runner->task_runner_->PostTask(
324  [component_runner, component]() {
325  component_runner->OnComponentV2Terminate(component);
326  });
327  };
328 
329  ActiveComponentV2 active_component = ComponentV2::Create(
330  std::move(termination_callback), std::move(start_info),
331  context_->svc() /* runner_incoming_services */, std::move(controller));
332 
333  auto key = active_component.component.get();
334  active_components_v2_[key] = std::move(active_component);
335 }
336 
337 void Runner::OnComponentV2Terminate(const ComponentV2* component) {
338  auto active_component_it = active_components_v2_.find(component);
339  if (active_component_it == active_components_v2_.end()) {
340  FML_LOG(INFO)
341  << "The remote end of the component runner tried to terminate an "
342  "component that has already been terminated, possibly because we "
343  "initiated the termination";
344  return;
345  }
346  ActiveComponentV2& active_component = active_component_it->second;
347 
348  // Grab the items out of the entry because we will have to rethread the
349  // destruction.
350  std::unique_ptr<ComponentV2> component_to_destroy =
351  std::move(active_component.component);
352  std::unique_ptr<fml::Thread> component_thread =
353  std::move(active_component.platform_thread);
354 
355  // Delete the entry.
356  active_components_v2_.erase(component);
357 
358  // Post the task to destroy the component and quit its message loop.
359  component_thread->GetTaskRunner()->PostTask(fml::MakeCopyable(
360  [instance = std::move(component_to_destroy),
361  thread = component_thread.get()]() mutable { instance.reset(); }));
362 
363  // Terminate and join the thread's message loop.
364  component_thread->Join();
365 }
366 
367 void Runner::SetupICU() {
368  // Exposes the TZ data setup for testing. Failing here is not fatal.
369  Runner::SetupTZDataInternal();
370  if (!Runner::SetupICUInternal()) {
371  FML_LOG(ERROR) << "Could not initialize ICU data.";
372  }
373 }
374 
375 // static
376 bool Runner::SetupICUInternal() {
377  return InitializeICU();
378 }
379 
380 // static
381 bool Runner::SetupTZDataInternal() {
382  return InitializeTZData();
383 }
384 
385 #if !defined(DART_PRODUCT)
386 void Runner::SetupTraceObserver() {
388 
389  fml::TaskRunner::RunNowOrPostTask(task_runner_, [&]() {
390  // Running this initialization code on task_runner_ ensures that the call to
391  // `async_get_default_dispatcher()` will capture the correct dispatcher.
392  trace_observer_ = std::make_unique<trace::TraceObserver>();
393  trace_observer_->Start(async_get_default_dispatcher(), [runner = this]() {
394  if (!trace_is_category_enabled("dart:profiler")) {
395  return;
396  }
397  if (trace_state() == TRACE_STARTED) {
398  runner->prolonged_context_ = trace_acquire_prolonged_context();
399  Dart_StartProfiling();
400  } else if (trace_state() == TRACE_STOPPING) {
401  for (auto& it : runner->active_components_v1_) {
404  it.second.platform_thread->GetTaskRunner(), [&]() {
405  it.second.component->WriteProfileToTrace();
406  latch.Signal();
407  });
408  latch.Wait();
409  }
410  // TODO(fxb/50694): Write v2 component profiles to trace once we're
411  // convinced they're stable.
412  Dart_StopProfiling();
413  trace_release_prolonged_context(runner->prolonged_context_);
414  }
415  });
416  latch.Signal();
417  });
418  latch.Wait();
419 }
420 #endif // !defined(DART_PRODUCT)
421 
422 } // namespace flutter_runner
static void SetThreadName(const std::string &thread_name)
Definition: runner.cc:134
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
void InitializeICU(const std::string &icu_data_path)
Definition: icu_util.cc:103
std::unique_ptr< ComponentV2 > component
Definition: component_v2.h:39
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
#define FML_LOG(severity)
Definition: logging.h:65
static inspect::Inspector * GetInspector()
std::unique_ptr< ComponentV1 > component
Definition: component_v1.h:38
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition: switches.h:41
Runner(fml::RefPtr< fml::TaskRunner > task_runner, sys::ComponentContext *context)
Definition: runner.cc:152
bool ReadFileToString(const std::string &path, std::string *result)
Definition: files.cc:56
std::unique_ptr< fml::Thread > platform_thread
Definition: component_v2.h:38
static bool IsRunningPrecompiledCode()
Checks if VM instances in the process can run precompiled code. This call can be made at any time and...
Definition: dart_vm.cc:192
static void RegisterProfilerSymbols(const char *symbols_path, const char *dso_name)
Definition: runner.cc:141
static ActiveComponentV1 Create(TerminationCallback termination_callback, fuchsia::sys::Package package, fuchsia::sys::StartupInfo startup_info, std::shared_ptr< sys::ServiceDirectory > runner_incoming_services, fidl::InterfaceRequest< fuchsia::sys::ComponentController > controller)
Definition: component_v1.cc:95
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
Definition: trace_event.h:94
void GetContents(LazyEntryVector *out_vector) const override
bool VmoFromFilename(const std::string &filename, bool executable, fuchsia::mem::Buffer *buffer)
Definition: vmo.cc:56
fit::function< void(const ComponentV1 *)> TerminationCallback
Definition: component_v1.h:57
static constexpr const char * kPortDirName
static void SetProcessName()
Definition: runner.cc:118
static ActiveComponentV2 Create(TerminationCallback termination_callback, fuchsia::component::runner::ComponentStartInfo start_info, std::shared_ptr< sys::ServiceDirectory > runner_incoming_services, fidl::InterfaceRequest< fuchsia::component::runner::ComponentController > controller)
fit::function< void(const ComponentV2 *)> TerminationCallback
Definition: component_v2.h:62
std::unique_ptr< fml::Thread > platform_thread
Definition: component_v1.h:37