| 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 | |