| 1 | // Copyright (c) 2012, 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_MACOS) |
| 7 | |
| 8 | #include "vm/os.h" |
| 9 | |
| 10 | #include <errno.h> // NOLINT |
| 11 | #include <limits.h> // NOLINT |
| 12 | #include <mach/clock.h> // NOLINT |
| 13 | #include <mach/mach.h> // NOLINT |
| 14 | #include <mach/mach_time.h> // NOLINT |
| 15 | #include <sys/resource.h> // NOLINT |
| 16 | #include <sys/time.h> // NOLINT |
| 17 | #include <unistd.h> // NOLINT |
| 18 | #if HOST_OS_IOS |
| 19 | #include <syslog.h> // NOLINT |
| 20 | #endif |
| 21 | |
| 22 | #include "platform/utils.h" |
| 23 | #include "vm/isolate.h" |
| 24 | #include "vm/zone.h" |
| 25 | |
| 26 | namespace dart { |
| 27 | |
| 28 | const char* OS::Name() { |
| 29 | #if HOST_OS_IOS |
| 30 | return "ios" ; |
| 31 | #else |
| 32 | return "macos" ; |
| 33 | #endif |
| 34 | } |
| 35 | |
| 36 | intptr_t OS::ProcessId() { |
| 37 | return static_cast<intptr_t>(getpid()); |
| 38 | } |
| 39 | |
| 40 | static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { |
| 41 | time_t seconds = static_cast<time_t>(seconds_since_epoch); |
| 42 | if (seconds != seconds_since_epoch) return false; |
| 43 | struct tm* error_code = localtime_r(&seconds, tm_result); |
| 44 | return error_code != NULL; |
| 45 | } |
| 46 | |
| 47 | const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { |
| 48 | tm decomposed; |
| 49 | bool succeeded = LocalTime(seconds_since_epoch, &decomposed); |
| 50 | // If unsuccessful, return an empty string like V8 does. |
| 51 | return (succeeded && (decomposed.tm_zone != NULL)) ? decomposed.tm_zone : "" ; |
| 52 | } |
| 53 | |
| 54 | int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { |
| 55 | tm decomposed; |
| 56 | bool succeeded = LocalTime(seconds_since_epoch, &decomposed); |
| 57 | // Even if the offset was 24 hours it would still easily fit into 32 bits. |
| 58 | // If unsuccessful, return zero like V8 does. |
| 59 | return succeeded ? static_cast<int>(decomposed.tm_gmtoff) : 0; |
| 60 | } |
| 61 | |
| 62 | int OS::GetLocalTimeZoneAdjustmentInSeconds() { |
| 63 | // TODO(floitsch): avoid excessive calls to tzset? |
| 64 | tzset(); |
| 65 | // Even if the offset was 24 hours it would still easily fit into 32 bits. |
| 66 | // Note that Unix and Dart disagree on the sign. |
| 67 | return static_cast<int>(-timezone); |
| 68 | } |
| 69 | |
| 70 | int64_t OS::GetCurrentTimeMillis() { |
| 71 | return GetCurrentTimeMicros() / 1000; |
| 72 | } |
| 73 | |
| 74 | int64_t OS::GetCurrentTimeMicros() { |
| 75 | // gettimeofday has microsecond resolution. |
| 76 | struct timeval tv; |
| 77 | if (gettimeofday(&tv, NULL) < 0) { |
| 78 | UNREACHABLE(); |
| 79 | return 0; |
| 80 | } |
| 81 | return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec; |
| 82 | } |
| 83 | |
| 84 | static mach_timebase_info_data_t timebase_info; |
| 85 | |
| 86 | int64_t OS::GetCurrentMonotonicTicks() { |
| 87 | if (timebase_info.denom == 0) { |
| 88 | kern_return_t kr = mach_timebase_info(&timebase_info); |
| 89 | ASSERT(KERN_SUCCESS == kr); |
| 90 | } |
| 91 | ASSERT(timebase_info.denom != 0); |
| 92 | // timebase_info converts absolute time tick units into nanoseconds. |
| 93 | int64_t result = mach_absolute_time(); |
| 94 | result *= timebase_info.numer; |
| 95 | result /= timebase_info.denom; |
| 96 | return result; |
| 97 | } |
| 98 | |
| 99 | int64_t OS::GetCurrentMonotonicFrequency() { |
| 100 | return kNanosecondsPerSecond; |
| 101 | } |
| 102 | |
| 103 | int64_t OS::GetCurrentMonotonicMicros() { |
| 104 | ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond); |
| 105 | return GetCurrentMonotonicTicks() / kNanosecondsPerMicrosecond; |
| 106 | } |
| 107 | |
| 108 | int64_t OS::GetCurrentThreadCPUMicros() { |
| 109 | #if HOST_OS_IOS |
| 110 | // Thread CPU time appears unreliable on iOS, sometimes incorrectly reporting |
| 111 | // no time elapsed. |
| 112 | return -1; |
| 113 | #else |
| 114 | mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; |
| 115 | thread_basic_info_data_t info_data; |
| 116 | thread_basic_info_t info = &info_data; |
| 117 | mach_port_t thread_port = pthread_mach_thread_np(pthread_self()); |
| 118 | kern_return_t r = |
| 119 | thread_info(thread_port, THREAD_BASIC_INFO, (thread_info_t)info, &count); |
| 120 | ASSERT(r == KERN_SUCCESS); |
| 121 | int64_t thread_cpu_micros = |
| 122 | (info->system_time.seconds + info->user_time.seconds); |
| 123 | thread_cpu_micros *= kMicrosecondsPerSecond; |
| 124 | thread_cpu_micros += info->user_time.microseconds; |
| 125 | thread_cpu_micros += info->system_time.microseconds; |
| 126 | return thread_cpu_micros; |
| 127 | #endif |
| 128 | } |
| 129 | |
| 130 | int64_t OS::GetCurrentThreadCPUMicrosForTimeline() { |
| 131 | return OS::GetCurrentThreadCPUMicros(); |
| 132 | } |
| 133 | |
| 134 | intptr_t OS::ActivationFrameAlignment() { |
| 135 | #if HOST_OS_IOS |
| 136 | #if TARGET_ARCH_ARM |
| 137 | // Even if we generate code that maintains a stronger alignment, we cannot |
| 138 | // assert the stronger stack alignment because C++ code will not maintain it. |
| 139 | return 8; |
| 140 | #elif TARGET_ARCH_ARM64 |
| 141 | return 16; |
| 142 | #elif TARGET_ARCH_IA32 |
| 143 | return 16; // iOS simulator |
| 144 | #elif TARGET_ARCH_X64 |
| 145 | return 16; // iOS simulator |
| 146 | #else |
| 147 | #error Unimplemented |
| 148 | #endif |
| 149 | #else // HOST_OS_IOS |
| 150 | // OS X activation frames must be 16 byte-aligned; see "Mac OS X ABI |
| 151 | // Function Call Guide". |
| 152 | return 16; |
| 153 | #endif // HOST_OS_IOS |
| 154 | } |
| 155 | |
| 156 | int OS::NumberOfAvailableProcessors() { |
| 157 | return sysconf(_SC_NPROCESSORS_ONLN); |
| 158 | } |
| 159 | |
| 160 | void OS::Sleep(int64_t millis) { |
| 161 | int64_t micros = millis * kMicrosecondsPerMillisecond; |
| 162 | SleepMicros(micros); |
| 163 | } |
| 164 | |
| 165 | void OS::SleepMicros(int64_t micros) { |
| 166 | struct timespec req; // requested. |
| 167 | struct timespec rem; // remainder. |
| 168 | int64_t seconds = micros / kMicrosecondsPerSecond; |
| 169 | if (seconds > kMaxInt32) { |
| 170 | // Avoid truncation of overly large sleep values. |
| 171 | seconds = kMaxInt32; |
| 172 | } |
| 173 | micros = micros - seconds * kMicrosecondsPerSecond; |
| 174 | int64_t nanos = micros * kNanosecondsPerMicrosecond; |
| 175 | req.tv_sec = static_cast<int32_t>(seconds); |
| 176 | req.tv_nsec = static_cast<long>(nanos); // NOLINT (long used in timespec). |
| 177 | while (true) { |
| 178 | int r = nanosleep(&req, &rem); |
| 179 | if (r == 0) { |
| 180 | break; |
| 181 | } |
| 182 | // We should only ever see an interrupt error. |
| 183 | ASSERT(errno == EINTR); |
| 184 | // Copy remainder into requested and repeat. |
| 185 | req = rem; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | void OS::DebugBreak() { |
| 190 | __builtin_trap(); |
| 191 | } |
| 192 | |
| 193 | DART_NOINLINE uintptr_t OS::GetProgramCounter() { |
| 194 | return reinterpret_cast<uintptr_t>( |
| 195 | __builtin_extract_return_addr(__builtin_return_address(0))); |
| 196 | } |
| 197 | |
| 198 | void OS::Print(const char* format, ...) { |
| 199 | #if HOST_OS_IOS |
| 200 | va_list args; |
| 201 | va_start(args, format); |
| 202 | vsyslog(LOG_INFO, format, args); |
| 203 | va_end(args); |
| 204 | #else |
| 205 | va_list args; |
| 206 | va_start(args, format); |
| 207 | VFPrint(stdout, format, args); |
| 208 | va_end(args); |
| 209 | #endif |
| 210 | } |
| 211 | |
| 212 | void OS::VFPrint(FILE* stream, const char* format, va_list args) { |
| 213 | vfprintf(stream, format, args); |
| 214 | fflush(stream); |
| 215 | } |
| 216 | |
| 217 | char* OS::SCreate(Zone* zone, const char* format, ...) { |
| 218 | va_list args; |
| 219 | va_start(args, format); |
| 220 | char* buffer = VSCreate(zone, format, args); |
| 221 | va_end(args); |
| 222 | return buffer; |
| 223 | } |
| 224 | |
| 225 | char* OS::VSCreate(Zone* zone, const char* format, va_list args) { |
| 226 | // Measure. |
| 227 | va_list measure_args; |
| 228 | va_copy(measure_args, args); |
| 229 | intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); |
| 230 | va_end(measure_args); |
| 231 | |
| 232 | char* buffer; |
| 233 | if (zone) { |
| 234 | buffer = zone->Alloc<char>(len + 1); |
| 235 | } else { |
| 236 | buffer = reinterpret_cast<char*>(malloc(len + 1)); |
| 237 | } |
| 238 | ASSERT(buffer != NULL); |
| 239 | |
| 240 | // Print. |
| 241 | va_list print_args; |
| 242 | va_copy(print_args, args); |
| 243 | Utils::VSNPrint(buffer, len + 1, format, print_args); |
| 244 | va_end(print_args); |
| 245 | return buffer; |
| 246 | } |
| 247 | |
| 248 | bool OS::StringToInt64(const char* str, int64_t* value) { |
| 249 | ASSERT(str != NULL && strlen(str) > 0 && value != NULL); |
| 250 | int32_t base = 10; |
| 251 | char* endptr; |
| 252 | int i = 0; |
| 253 | if (str[0] == '-') { |
| 254 | i = 1; |
| 255 | } else if (str[0] == '+') { |
| 256 | i = 1; |
| 257 | } |
| 258 | if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') && |
| 259 | (str[i + 2] != '\0')) { |
| 260 | base = 16; |
| 261 | } |
| 262 | errno = 0; |
| 263 | if (base == 16) { |
| 264 | // Unsigned 64-bit hexadecimal integer literals are allowed but |
| 265 | // immediately interpreted as signed 64-bit integers. |
| 266 | *value = static_cast<int64_t>(strtoull(str, &endptr, base)); |
| 267 | } else { |
| 268 | *value = strtoll(str, &endptr, base); |
| 269 | } |
| 270 | return ((errno == 0) && (endptr != str) && (*endptr == 0)); |
| 271 | } |
| 272 | |
| 273 | void OS::RegisterCodeObservers() {} |
| 274 | |
| 275 | void OS::PrintErr(const char* format, ...) { |
| 276 | #if HOST_OS_IOS |
| 277 | va_list args; |
| 278 | va_start(args, format); |
| 279 | vsyslog(LOG_ERR, format, args); |
| 280 | va_end(args); |
| 281 | #else |
| 282 | va_list args; |
| 283 | va_start(args, format); |
| 284 | VFPrint(stderr, format, args); |
| 285 | va_end(args); |
| 286 | #endif |
| 287 | } |
| 288 | |
| 289 | void OS::Init() { |
| 290 | // See https://github.com/dart-lang/sdk/issues/29539 |
| 291 | // This is a workaround for a macos bug, we eagerly call localtime_r so that |
| 292 | // libnotify is initialized early before any fork happens. |
| 293 | struct timeval tv; |
| 294 | if (gettimeofday(&tv, NULL) < 0) { |
| 295 | FATAL1("gettimeofday returned an error (%s)\n" , strerror(errno)); |
| 296 | return; |
| 297 | } |
| 298 | tm decomposed; |
| 299 | struct tm* error_code = localtime_r(&(tv.tv_sec), &decomposed); |
| 300 | if (error_code == NULL) { |
| 301 | FATAL1("localtime_r returned an error (%s)\n" , strerror(errno)); |
| 302 | return; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | void OS::Cleanup() {} |
| 307 | |
| 308 | void OS::PrepareToAbort() {} |
| 309 | |
| 310 | void OS::Abort() { |
| 311 | PrepareToAbort(); |
| 312 | abort(); |
| 313 | } |
| 314 | |
| 315 | void OS::Exit(int code) { |
| 316 | exit(code); |
| 317 | } |
| 318 | |
| 319 | } // namespace dart |
| 320 | |
| 321 | #endif // defined(HOST_OS_MACOS) |
| 322 | |