1 | // Copyright (c) 2016, 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/globals.h" |
6 | #if defined(HOST_OS_FUCHSIA) |
7 | |
8 | #include "vm/os.h" |
9 | |
10 | #include <errno.h> |
11 | #include <fcntl.h> |
12 | #include <stdint.h> |
13 | |
14 | #include <fuchsia/deprecatedtimezone/cpp/fidl.h> |
15 | #include <lib/async/default.h> |
16 | #include <lib/async-loop/loop.h> |
17 | #include <lib/async-loop/default.h> |
18 | #include <lib/inspect/cpp/inspect.h> |
19 | #include <lib/sys/cpp/component_context.h> |
20 | #include <lib/sys/cpp/service_directory.h> |
21 | #include <lib/sys/inspect/cpp/component.h> |
22 | #include <zircon/process.h> |
23 | #include <zircon/syscalls.h> |
24 | #include <zircon/syscalls/object.h> |
25 | #include <zircon/types.h> |
26 | |
27 | #include "platform/assert.h" |
28 | #include "platform/utils.h" |
29 | #include "vm/zone.h" |
30 | |
31 | namespace { |
32 | |
33 | // The data directory containing ICU timezone data files. |
34 | static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le" ; |
35 | |
36 | // The status codes for tzdata file open and read. |
37 | enum class TZDataStatus { |
38 | OK = 0, |
39 | // The open call for the tzdata file did not succeed. |
40 | COULD_NOT_OPEN = -1, |
41 | // The close call (after tzdata was loaded) did not succeed. |
42 | COULD_NOT_CLOSE = -2, |
43 | }; |
44 | |
45 | // Adds a facility for introspecting timezone data errors. Allows insight into |
46 | // the internal state of the VM even if error reporting facilities fail. |
47 | class InspectMetrics { |
48 | public: |
49 | // Does not take ownership of inspector. |
50 | explicit InspectMetrics(inspect::Inspector* inspector) |
51 | : inspector_(inspector), |
52 | root_(inspector_->GetRoot()), |
53 | metrics_(root_.CreateChild("os" )), |
54 | dst_status_(metrics_.CreateInt("dst_status" , 0)), |
55 | tz_data_status_(metrics_.CreateInt("tz_data_status" , 0)), |
56 | tz_data_close_status_(metrics_.CreateInt("tz_data_close_status" , 0)) {} |
57 | |
58 | // Sets the last status code for DST offset calls. |
59 | void SetDSTOffsetStatus(zx_status_t status) { |
60 | dst_status_.Set(static_cast<int32_t>(status)); |
61 | } |
62 | |
63 | // Sets the return value of call to InitializeTZData, and the status of the |
64 | // reported by close() on tzdata files. |
65 | void SetInitTzData(TZDataStatus value, int32_t status) { |
66 | tz_data_status_.Set(static_cast<int32_t>(value)); |
67 | tz_data_close_status_.Set(status); |
68 | } |
69 | |
70 | private: |
71 | // The inspector that all metrics are being reported into. |
72 | inspect::Inspector* inspector_; |
73 | |
74 | // References inspector_ state. |
75 | inspect::Node& root_; |
76 | |
77 | // The OS metrics node. |
78 | inspect::Node metrics_; |
79 | |
80 | // The status of the last GetTimeZoneOffset call. |
81 | inspect::IntProperty dst_status_; |
82 | |
83 | // The status of the initialization. |
84 | inspect::IntProperty tz_data_status_; |
85 | |
86 | // The return code for the close() call for tzdata files. |
87 | inspect::IntProperty tz_data_close_status_; |
88 | }; |
89 | |
90 | // Initialized on OS:Init(), deinitialized on OS::Cleanup. |
91 | std::unique_ptr<sys::ComponentInspector> component_inspector; |
92 | std::unique_ptr<InspectMetrics> metrics; |
93 | async_loop_t* message_loop = nullptr; |
94 | |
95 | // Initializes the source of timezone data if available. Timezone data file in |
96 | // Fuchsia is at a fixed directory path. Returns true on success. |
97 | bool InitializeTZData() { |
98 | ASSERT(metrics != nullptr); |
99 | // Try opening the path to check if present. No need to verify that it is a |
100 | // directory since ICU loading will return an error if the TZ data path is |
101 | // wrong. |
102 | int fd = openat(AT_FDCWD, kICUTZDataDir, O_RDONLY); |
103 | if (fd < 0) { |
104 | metrics->SetInitTzData(TZDataStatus::COULD_NOT_OPEN, fd); |
105 | return false; |
106 | } |
107 | // 0 == Not overwriting the env var if already set. |
108 | setenv("ICU_TIMEZONE_FILES_DIR" , kICUTZDataDir, 0); |
109 | int32_t close_status = close(fd); |
110 | if (close_status != 0) { |
111 | metrics->SetInitTzData(TZDataStatus::COULD_NOT_CLOSE, close_status); |
112 | return false; |
113 | } |
114 | metrics->SetInitTzData(TZDataStatus::OK, 0); |
115 | return true; |
116 | } |
117 | |
118 | } // namespace |
119 | |
120 | namespace dart { |
121 | |
122 | #ifndef PRODUCT |
123 | |
124 | DEFINE_FLAG(bool, |
125 | generate_perf_events_symbols, |
126 | false, |
127 | "Generate events symbols for profiling with perf" ); |
128 | |
129 | #endif // !PRODUCT |
130 | |
131 | const char* OS::Name() { |
132 | return "fuchsia" ; |
133 | } |
134 | |
135 | intptr_t OS::ProcessId() { |
136 | return static_cast<intptr_t>(getpid()); |
137 | } |
138 | |
139 | // TODO(FL-98): Change this to talk to fuchsia.dart to get timezone service to |
140 | // directly get timezone. |
141 | // |
142 | // Putting this hack right now due to CP-120 as I need to remove |
143 | // component:ConnectToEnvironmentServices and this is the only thing that is |
144 | // blocking it and FL-98 will take time. |
145 | static fuchsia::deprecatedtimezone::TimezoneSyncPtr tz; |
146 | |
147 | static zx_status_t GetLocalAndDstOffsetInSeconds(int64_t seconds_since_epoch, |
148 | int32_t* local_offset, |
149 | int32_t* dst_offset) { |
150 | zx_status_t status = tz->GetTimezoneOffsetMinutes(seconds_since_epoch * 1000, |
151 | local_offset, dst_offset); |
152 | metrics->SetDSTOffsetStatus(status); |
153 | if (status != ZX_OK) { |
154 | return status; |
155 | } |
156 | *local_offset *= 60; |
157 | *dst_offset *= 60; |
158 | return ZX_OK; |
159 | } |
160 | |
161 | const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { |
162 | // TODO(abarth): Handle time zone changes. |
163 | static const auto* tz_name = new std::string([] { |
164 | std::string result; |
165 | tz->GetTimezoneId(&result); |
166 | return result; |
167 | }()); |
168 | return tz_name->c_str(); |
169 | } |
170 | |
171 | int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { |
172 | int32_t local_offset, dst_offset; |
173 | zx_status_t status = GetLocalAndDstOffsetInSeconds( |
174 | seconds_since_epoch, &local_offset, &dst_offset); |
175 | return status == ZX_OK ? local_offset + dst_offset : 0; |
176 | } |
177 | |
178 | int OS::GetLocalTimeZoneAdjustmentInSeconds() { |
179 | int32_t local_offset, dst_offset; |
180 | zx_time_t now = 0; |
181 | zx_clock_get(ZX_CLOCK_UTC, &now); |
182 | zx_status_t status = GetLocalAndDstOffsetInSeconds( |
183 | now / ZX_SEC(1), &local_offset, &dst_offset); |
184 | return status == ZX_OK ? local_offset : 0; |
185 | } |
186 | |
187 | int64_t OS::GetCurrentTimeMillis() { |
188 | return GetCurrentTimeMicros() / 1000; |
189 | } |
190 | |
191 | int64_t OS::GetCurrentTimeMicros() { |
192 | zx_time_t now = 0; |
193 | zx_clock_get(ZX_CLOCK_UTC, &now); |
194 | return now / kNanosecondsPerMicrosecond; |
195 | } |
196 | |
197 | int64_t OS::GetCurrentMonotonicTicks() { |
198 | return zx_clock_get_monotonic(); |
199 | } |
200 | |
201 | int64_t OS::GetCurrentMonotonicFrequency() { |
202 | return kNanosecondsPerSecond; |
203 | } |
204 | |
205 | int64_t OS::GetCurrentMonotonicMicros() { |
206 | int64_t ticks = GetCurrentMonotonicTicks(); |
207 | ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond); |
208 | return ticks / kNanosecondsPerMicrosecond; |
209 | } |
210 | |
211 | int64_t OS::GetCurrentThreadCPUMicros() { |
212 | zx_time_t now = 0; |
213 | zx_clock_get(ZX_CLOCK_THREAD, &now); |
214 | return now / kNanosecondsPerMicrosecond; |
215 | } |
216 | |
217 | // On Fuchsia, thread timestamp values are not used in the tracing/timeline |
218 | // integration. Because of this, we try to avoid querying them, since doing so |
219 | // has both a runtime and trace buffer storage cost. |
220 | int64_t OS::GetCurrentThreadCPUMicrosForTimeline() { |
221 | return -1; |
222 | } |
223 | |
224 | // TODO(5411554): May need to hoist these architecture dependent code |
225 | // into a architecture specific file e.g: os_ia32_fuchsia.cc |
226 | intptr_t OS::ActivationFrameAlignment() { |
227 | #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \ |
228 | defined(TARGET_ARCH_ARM64) |
229 | const int kMinimumAlignment = 16; |
230 | #elif defined(TARGET_ARCH_ARM) |
231 | const int kMinimumAlignment = 8; |
232 | #else |
233 | #error Unsupported architecture. |
234 | #endif |
235 | intptr_t alignment = kMinimumAlignment; |
236 | // TODO(5411554): Allow overriding default stack alignment for |
237 | // testing purposes. |
238 | // Flags::DebugIsInt("stackalign", &alignment); |
239 | ASSERT(Utils::IsPowerOfTwo(alignment)); |
240 | ASSERT(alignment >= kMinimumAlignment); |
241 | return alignment; |
242 | } |
243 | |
244 | int OS::NumberOfAvailableProcessors() { |
245 | return sysconf(_SC_NPROCESSORS_CONF); |
246 | } |
247 | |
248 | void OS::Sleep(int64_t millis) { |
249 | SleepMicros(millis * kMicrosecondsPerMillisecond); |
250 | } |
251 | |
252 | void OS::SleepMicros(int64_t micros) { |
253 | zx_nanosleep(zx_deadline_after(micros * kNanosecondsPerMicrosecond)); |
254 | } |
255 | |
256 | void OS::DebugBreak() { |
257 | UNIMPLEMENTED(); |
258 | } |
259 | |
260 | DART_NOINLINE uintptr_t OS::GetProgramCounter() { |
261 | return reinterpret_cast<uintptr_t>( |
262 | __builtin_extract_return_addr(__builtin_return_address(0))); |
263 | } |
264 | |
265 | void OS::Print(const char* format, ...) { |
266 | va_list args; |
267 | va_start(args, format); |
268 | VFPrint(stdout, format, args); |
269 | va_end(args); |
270 | } |
271 | |
272 | void OS::VFPrint(FILE* stream, const char* format, va_list args) { |
273 | vfprintf(stream, format, args); |
274 | fflush(stream); |
275 | } |
276 | |
277 | char* OS::SCreate(Zone* zone, const char* format, ...) { |
278 | va_list args; |
279 | va_start(args, format); |
280 | char* buffer = VSCreate(zone, format, args); |
281 | va_end(args); |
282 | return buffer; |
283 | } |
284 | |
285 | char* OS::VSCreate(Zone* zone, const char* format, va_list args) { |
286 | // Measure. |
287 | va_list measure_args; |
288 | va_copy(measure_args, args); |
289 | intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); |
290 | va_end(measure_args); |
291 | |
292 | char* buffer; |
293 | if (zone) { |
294 | buffer = zone->Alloc<char>(len + 1); |
295 | } else { |
296 | buffer = reinterpret_cast<char*>(malloc(len + 1)); |
297 | } |
298 | ASSERT(buffer != NULL); |
299 | |
300 | // Print. |
301 | va_list print_args; |
302 | va_copy(print_args, args); |
303 | Utils::VSNPrint(buffer, len + 1, format, print_args); |
304 | va_end(print_args); |
305 | return buffer; |
306 | } |
307 | |
308 | bool OS::StringToInt64(const char* str, int64_t* value) { |
309 | ASSERT(str != NULL && strlen(str) > 0 && value != NULL); |
310 | int32_t base = 10; |
311 | char* endptr; |
312 | int i = 0; |
313 | if (str[0] == '-') { |
314 | i = 1; |
315 | } else if (str[0] == '+') { |
316 | i = 1; |
317 | } |
318 | if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') && |
319 | (str[i + 2] != '\0')) { |
320 | base = 16; |
321 | } |
322 | errno = 0; |
323 | if (base == 16) { |
324 | // Unsigned 64-bit hexadecimal integer literals are allowed but |
325 | // immediately interpreted as signed 64-bit integers. |
326 | *value = static_cast<int64_t>(strtoull(str, &endptr, base)); |
327 | } else { |
328 | *value = strtoll(str, &endptr, base); |
329 | } |
330 | return ((errno == 0) && (endptr != str) && (*endptr == 0)); |
331 | } |
332 | |
333 | void OS::RegisterCodeObservers() { |
334 | #ifndef PRODUCT |
335 | if (FLAG_generate_perf_events_symbols) { |
336 | UNIMPLEMENTED(); |
337 | } |
338 | #endif // !PRODUCT |
339 | } |
340 | |
341 | void OS::PrintErr(const char* format, ...) { |
342 | va_list args; |
343 | va_start(args, format); |
344 | VFPrint(stderr, format, args); |
345 | va_end(args); |
346 | } |
347 | |
348 | void OS::Init() { |
349 | if (async_get_default_dispatcher() == NULL) { |
350 | async_loop_create(&kAsyncLoopConfigAttachToCurrentThread, &message_loop); |
351 | async_set_default_dispatcher(async_loop_get_dispatcher(message_loop)); |
352 | async_loop_start_thread(message_loop, "Fuchsia async loop" , nullptr); |
353 | } |
354 | |
355 | sys::ComponentContext* context = dart::ComponentContext(); |
356 | component_inspector = std::make_unique<sys::ComponentInspector>(context); |
357 | metrics = std::make_unique<InspectMetrics>(component_inspector->inspector()); |
358 | |
359 | InitializeTZData(); |
360 | context->svc()->Connect(tz.NewRequest()); |
361 | } |
362 | |
363 | void OS::Cleanup() { |
364 | metrics = nullptr; |
365 | component_inspector = nullptr; |
366 | if (message_loop != nullptr) { |
367 | async_loop_destroy(message_loop); |
368 | message_loop = nullptr; |
369 | } |
370 | } |
371 | |
372 | void OS::PrepareToAbort() {} |
373 | |
374 | void OS::Abort() { |
375 | PrepareToAbort(); |
376 | abort(); |
377 | } |
378 | |
379 | void OS::Exit(int code) { |
380 | exit(code); |
381 | } |
382 | |
383 | } // namespace dart |
384 | |
385 | #endif // defined(HOST_OS_FUCHSIA) |
386 | |