Flutter Engine
The 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/async/default.h>
11#include <lib/inspect/cpp/inspect.h>
12#include <lib/sys/cpp/component_context.h>
13#include <lib/trace-engine/instrumentation.h>
14#include <lib/vfs/cpp/pseudo_dir.h>
15#include <zircon/status.h>
16#include <zircon/types.h>
17
18#include <cstdint>
19#include <sstream>
20#include <utility>
21
22#include "flutter/fml/make_copyable.h"
23#include "flutter/lib/ui/text/font_collection.h"
24#include "flutter/runtime/dart_vm.h"
29#include "third_party/icu/source/common/unicode/udata.h"
31
32namespace flutter_runner {
33
34namespace {
35
36static constexpr char kIcuDataPath[] = "/pkg/data/icudtl.dat";
37
38// Environment variable containing the path to the directory containing the
39// timezone files.
40static constexpr char kICUTZEnv[] = "ICU_TIMEZONE_FILES_DIR";
41
42// The data directory containing ICU timezone data files.
43static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le";
44
45// Map the memory into the process and return a pointer to the memory.
46uintptr_t GetICUData(const fuchsia::mem::Buffer& icu_data) {
47 uint64_t data_size = icu_data.size;
48 if (data_size > std::numeric_limits<size_t>::max())
49 return 0u;
50
51 uintptr_t data = 0u;
52 zx_status_t status =
53 zx::vmar::root_self()->map(ZX_VM_PERM_READ, 0, icu_data.vmo, 0,
54 static_cast<size_t>(data_size), &data);
55 if (status == ZX_OK) {
56 return data;
57 }
58
59 return 0u;
60}
61
62// Initializes the timezone data if available. Timezone data file in Fuchsia
63// is at a fixed directory path. Returns true on success. As a side effect
64// sets the value of the environment variable "ICU_TIMEZONE_FILES_DIR" to a
65// fixed value which is fuchsia-specific.
66bool InitializeTZData() {
67 // We need the ability to change the env variable for testing, so not
68 // overwriting if set.
69 setenv(kICUTZEnv, kICUTZDataDir, 0 /* No overwrite */);
70
71 const std::string tzdata_dir = getenv(kICUTZEnv);
72 // Try opening the path to check if present. No need to verify that it is a
73 // directory since ICU loading will return an error if the TZ data path is
74 // wrong.
75 int fd = openat(AT_FDCWD, tzdata_dir.c_str(), O_RDONLY);
76 if (fd < 0) {
77 FML_LOG(INFO) << "Could not open: '" << tzdata_dir
78 << "', proceeding without loading the timezone database: "
79 << strerror(errno);
80 return false;
81 }
82 if (close(fd)) {
83 FML_LOG(WARNING) << "Could not close: " << tzdata_dir << ": "
84 << strerror(errno);
85 }
86 return true;
87}
88
89// Return value indicates if initialization was successful.
90bool InitializeICU() {
91 const char* data_path = kIcuDataPath;
92
93 fuchsia::mem::Buffer icu_data;
94 if (!dart_utils::VmoFromFilename(data_path, false, &icu_data)) {
95 return false;
96 }
97
98 uintptr_t data = GetICUData(icu_data);
99 if (!data) {
100 return false;
101 }
102
103 // If the loading fails, soldier on. The loading is optional as we don't
104 // want to crash the engine in transition.
105 InitializeTZData();
106
107 // Pass the data to ICU.
108 UErrorCode err = U_ZERO_ERROR;
109 udata_setCommonData(reinterpret_cast<const char*>(data), &err);
110 if (err != U_ZERO_ERROR) {
111 FML_LOG(ERROR) << "error loading ICU data: " << err;
112 return false;
113 }
114 return true;
115}
116
117} // namespace
118
119static void SetProcessName() {
120 std::stringstream stream;
121#if defined(DART_PRODUCT)
122 stream << "io.flutter.product_runner.";
123#else
124 stream << "io.flutter.runner.";
125#endif
127 stream << "aot";
128 } else {
129 stream << "jit";
130 }
131 const auto name = stream.str();
132 zx::process::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size());
133}
134
135static void SetThreadName(const std::string& thread_name) {
136 zx::thread::self()->set_property(ZX_PROP_NAME, thread_name.c_str(),
137 thread_name.size());
138}
139
140#if !defined(DART_PRODUCT)
141// Register native symbol information for the Dart VM's profiler.
142static void RegisterProfilerSymbols(const char* symbols_path,
143 const char* dso_name) {
144 std::string* symbols = new std::string();
145 if (dart_utils::ReadFileToString(symbols_path, symbols)) {
146 Dart_AddSymbols(dso_name, symbols->data(), symbols->size());
147 } else {
148 FML_LOG(ERROR) << "Failed to load " << symbols_path;
149 }
150}
151#endif // !defined(DART_PRODUCT)
152
154 sys::ComponentContext* context)
155 : task_runner_(task_runner), context_(context) {
156#if !defined(DART_PRODUCT)
157 // The VM service isolate uses the process-wide namespace. It writes the
158 // vm service protocol port under /tmp. The VMServiceObject exposes that
159 // port number to The Hub.
160 context_->outgoing()->debug_dir()->AddEntry(
162 std::make_unique<dart_utils::VMServiceObject>());
163
164 inspect::Inspector* inspector = dart_utils::RootInspectNode::GetInspector();
165 inspector->GetRoot().CreateLazyValues(
166 "vmservice_port",
167 [&]() {
168 inspect::Inspector inspector;
169 dart_utils::VMServiceObject::LazyEntryVector out;
171 std::string name = "";
172 if (!out.empty()) {
173 name = out[0].name;
174 }
175 inspector.GetRoot().CreateString("vm_service_port", name, &inspector);
176 return fpromise::make_ok_promise(inspector);
177 },
178 inspector);
179
180 SetupTraceObserver();
181#endif // !defined(DART_PRODUCT)
182
184
185 SetupICU();
186
188
189 SetThreadName("io.flutter.runner.main");
190
191 context_->outgoing()
192 ->AddPublicService<fuchsia::component::runner::ComponentRunner>(
193 std::bind(&Runner::RegisterComponentV2, this, std::placeholders::_1));
194
195#if !defined(DART_PRODUCT)
197 RegisterProfilerSymbols("pkg/data/flutter_aot_runner.dartprofilersymbols",
198 "");
199 } else {
200 RegisterProfilerSymbols("pkg/data/flutter_jit_runner.dartprofilersymbols",
201 "");
202 }
203#endif // !defined(DART_PRODUCT)
204}
205
207 context_->outgoing()
208 ->RemovePublicService<fuchsia::component::runner::ComponentRunner>();
209
210#if !defined(DART_PRODUCT)
211 trace_observer_->Stop();
212#endif // !defined(DART_PRODUCT)
213}
214
215// CF v2 lifecycle methods.
216
217void Runner::RegisterComponentV2(
218 fidl::InterfaceRequest<fuchsia::component::runner::ComponentRunner>
219 request) {
220 active_components_v2_bindings_.AddBinding(this, std::move(request));
221}
222
223void Runner::Start(
224 fuchsia::component::runner::ComponentStartInfo start_info,
225 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
226 controller) {
227 // TRACE_DURATION currently requires that the string data does not change
228 // in the traced scope. Since |package| gets moved in the ComponentV2::Create
229 // call below, we cannot ensure that |package.resolved_url| does not move or
230 // change, so we make a copy to pass to TRACE_DURATION.
231 // TODO(PT-169): Remove this copy when TRACE_DURATION reads string arguments
232 // eagerly.
233 const std::string url_copy = start_info.resolved_url();
234 TRACE_EVENT1("flutter", "Start", "url", url_copy.c_str());
235
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 ComponentV2::TerminationCallback termination_callback =
244 [component_runner = this](const ComponentV2* component) {
245 component_runner->task_runner_->PostTask(
246 [component_runner, component]() {
247 component_runner->OnComponentV2Terminate(component);
248 });
249 };
250
251 ActiveComponentV2 active_component = ComponentV2::Create(
252 std::move(termination_callback), std::move(start_info),
253 context_->svc() /* runner_incoming_services */, std::move(controller));
254
255 auto key = active_component.component.get();
256 active_components_v2_[key] = std::move(active_component);
257}
258
259void Runner::OnComponentV2Terminate(const ComponentV2* component) {
260 auto active_component_it = active_components_v2_.find(component);
261 if (active_component_it == active_components_v2_.end()) {
262 FML_LOG(INFO)
263 << "The remote end of the component runner tried to terminate an "
264 "component that has already been terminated, possibly because we "
265 "initiated the termination";
266 return;
267 }
268 ActiveComponentV2& active_component = active_component_it->second;
269
270 // Grab the items out of the entry because we will have to rethread the
271 // destruction.
272 std::unique_ptr<ComponentV2> component_to_destroy =
273 std::move(active_component.component);
274 std::unique_ptr<fml::Thread> component_thread =
275 std::move(active_component.platform_thread);
276
277 // Delete the entry.
278 active_components_v2_.erase(component);
279
280 // Post the task to destroy the component and quit its message loop.
281 component_thread->GetTaskRunner()->PostTask(fml::MakeCopyable(
282 [instance = std::move(component_to_destroy),
283 thread = component_thread.get()]() mutable { instance.reset(); }));
284
285 // Terminate and join the thread's message loop.
286 component_thread->Join();
287}
288
289void Runner::SetupICU() {
290 // Exposes the TZ data setup for testing. Failing here is not fatal.
291 Runner::SetupTZDataInternal();
292 if (!Runner::SetupICUInternal()) {
293 FML_LOG(ERROR) << "Could not initialize ICU data.";
294 }
295}
296
297// static
298bool Runner::SetupICUInternal() {
299 return InitializeICU();
300}
301
302// static
303bool Runner::SetupTZDataInternal() {
304 return InitializeTZData();
305}
306
307#if !defined(DART_PRODUCT)
308void Runner::SetupTraceObserver() {
310
311 fml::TaskRunner::RunNowOrPostTask(task_runner_, [&]() {
312 // Running this initialization code on task_runner_ ensures that the call to
313 // `async_get_default_dispatcher()` will capture the correct dispatcher.
314 trace_observer_ = std::make_unique<trace::TraceObserver>();
315 trace_observer_->Start(async_get_default_dispatcher(), [runner = this]() {
316 if (!trace_is_category_enabled("dart:profiler")) {
317 return;
318 }
319 if (trace_state() == TRACE_STARTED) {
320 runner->prolonged_context_ = trace_acquire_prolonged_context();
322 } else if (trace_state() == TRACE_STOPPING) {
323 auto write_profile_trace_for_components = [](auto& components) {
324 for (auto& it : components) {
327 it.second.platform_thread->GetTaskRunner(), [&]() {
328 it.second.component->WriteProfileToTrace();
329 latch.Signal();
330 });
331 latch.Wait();
332 }
333 };
334 write_profile_trace_for_components(runner->active_components_v2_);
335
337 trace_release_prolonged_context(runner->prolonged_context_);
338 }
339 });
340 latch.Signal();
341 });
342 latch.Wait();
343}
344#endif // !defined(DART_PRODUCT)
345
346} // namespace flutter_runner
static void Init()
Definition: SkGraphics.cpp:22
static inspect::Inspector * GetInspector()
void GetContents(LazyEntryVector *out_vector) const override
static constexpr const char * kPortDirName
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:205
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:61
Runner(fml::RefPtr< fml::TaskRunner > task_runner, sys::ComponentContext *context)
Definition: runner.cc:153
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
Definition: task_runner.cc:55
DART_EXPORT void Dart_StartProfiling(void)
DART_EXPORT void Dart_StopProfiling(void)
DART_EXPORT bool Dart_IsPrecompiledRuntime(void)
DART_EXPORT void Dart_AddSymbols(const char *dso_name, void *buffer, intptr_t buffer_size)
VkInstance instance
Definition: main.cc:48
#define FML_LOG(severity)
Definition: logging.h:82
static float max(float r, float g, float b)
Definition: hsl.cpp:49
bool ReadFileToString(const std::string &path, std::string *result)
Definition: files.cc:56
bool VmoFromFilename(const std::string &filename, bool executable, fuchsia::mem::Buffer *buffer)
Definition: vmo.cc:57
sys::ComponentContext * ComponentContext()
static void SetThreadName(const std::string &thread_name)
Definition: runner.cc:135
static void RegisterProfilerSymbols(const char *symbols_path, const char *dso_name)
Definition: runner.cc:142
static void SetProcessName()
Definition: runner.cc:119
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
void InitializeICU(const std::string &icu_data_path)
Definition: icu_util.cc:102
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define ERROR(message)
Definition: elf_loader.cc:260
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
Definition: trace_event.h:141