blob: 3eb9be090ae02902ac7cda87a9b01686c92a5750 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/globals.h"
#if defined(DART_HOST_OS_WINDOWS)
#include "vm/os.h"
#include <malloc.h> // NOLINT
#include <process.h> // NOLINT
#include <psapi.h> // NOLINT
#include <time.h> // NOLINT
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/os_thread.h"
#include "vm/zone.h"
namespace dart {
// Defined in vm/os_thread_win.cc
extern bool private_flag_windows_run_tls_destructors;
const char* OS::Name() {
return "windows";
}
intptr_t OS::ProcessId() {
return static_cast<intptr_t>(GetCurrentProcessId());
}
// As a side-effect sets the globals _timezone, _daylight and _tzname.
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(seconds_since_epoch);
if (seconds != seconds_since_epoch) {
return false;
}
// localtime_s implicitly sets _timezone, _daylight and _tzname.
errno_t error_code = localtime_s(tm_result, &seconds);
return error_code == 0;
}
static int GetDaylightSavingBiasInSeconds() {
TIME_ZONE_INFORMATION zone_information;
memset(&zone_information, 0, sizeof(zone_information));
if (GetTimeZoneInformation(&zone_information) == TIME_ZONE_ID_INVALID) {
// By default the daylight saving offset is an hour.
return -60 * 60;
} else {
return static_cast<int>(zone_information.DaylightBias * 60);
}
}
const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) {
TIME_ZONE_INFORMATION zone_information;
memset(&zone_information, 0, sizeof(zone_information));
// Initialize and grab the time zone data.
_tzset();
DWORD status = GetTimeZoneInformation(&zone_information);
if (GetTimeZoneInformation(&zone_information) == TIME_ZONE_ID_INVALID) {
// If we can't get the time zone data, the Windows docs indicate that we
// are probably out of memory. Return an empty string.
return "";
}
// Figure out whether we're in standard or daylight.
bool daylight_savings = (status == TIME_ZONE_ID_DAYLIGHT);
if (status == TIME_ZONE_ID_UNKNOWN) {
tm local_time;
if (LocalTime(seconds_since_epoch, &local_time)) {
daylight_savings = (local_time.tm_isdst == 1);
}
}
// Convert the wchar string to a null-terminated utf8 string.
wchar_t* wchar_name = daylight_savings ? zone_information.DaylightName
: zone_information.StandardName;
intptr_t utf8_len =
WideCharToMultiByte(CP_UTF8, 0, wchar_name, -1, NULL, 0, NULL, NULL);
char* name = ThreadState::Current()->zone()->Alloc<char>(utf8_len + 1);
WideCharToMultiByte(CP_UTF8, 0, wchar_name, -1, name, utf8_len, NULL, NULL);
name[utf8_len] = '\0';
return name;
}
int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) {
tm decomposed;
// LocalTime will set _timezone.
bool succeeded = LocalTime(seconds_since_epoch, &decomposed);
if (succeeded) {
int inDaylightSavingsTime = decomposed.tm_isdst;
ASSERT(inDaylightSavingsTime == 0 || inDaylightSavingsTime == 1);
// Dart and Windows disagree on the sign of the bias.
int offset = static_cast<int>(-_timezone);
if (inDaylightSavingsTime == 1) {
static int daylight_bias = GetDaylightSavingBiasInSeconds();
// Subtract because windows and Dart disagree on the sign.
offset = offset - daylight_bias;
}
return offset;
} else {
// Return zero like V8 does.
return 0;
}
}
int OS::GetLocalTimeZoneAdjustmentInSeconds() {
// TODO(floitsch): avoid excessive calls to _tzset?
_tzset();
// Dart and Windows disagree on the sign of the bias.
return static_cast<int>(-_timezone);
}
int64_t OS::GetCurrentTimeMillis() {
return GetCurrentTimeMicros() / 1000;
}
int64_t OS::GetCurrentTimeMicros() {
static const int64_t kTimeEpoc = 116444736000000000LL;
static const int64_t kTimeScaler = 10; // 100 ns to us.
// Although win32 uses 64-bit integers for representing timestamps,
// these are packed into a FILETIME structure. The FILETIME
// structure is just a struct representing a 64-bit integer. The
// TimeStamp union allows access to both a FILETIME and an integer
// representation of the timestamp. The Windows timestamp is in
// 100-nanosecond intervals since January 1, 1601.
union TimeStamp {
FILETIME ft_;
int64_t t_;
};
TimeStamp time;
GetSystemTimeAsFileTime(&time.ft_);
return (time.t_ - kTimeEpoc) / kTimeScaler;
}
static int64_t qpc_ticks_per_second = 0;
int64_t OS::GetCurrentMonotonicTicks() {
if (qpc_ticks_per_second == 0) {
// QueryPerformanceCounter not supported, fallback.
return GetCurrentTimeMicros();
}
// Grab performance counter value.
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
return static_cast<int64_t>(now.QuadPart);
}
int64_t OS::GetCurrentMonotonicFrequency() {
if (qpc_ticks_per_second == 0) {
// QueryPerformanceCounter not supported, fallback.
return kMicrosecondsPerSecond;
}
return qpc_ticks_per_second;
}
int64_t OS::GetCurrentMonotonicMicros() {
int64_t ticks = GetCurrentMonotonicTicks();
int64_t frequency = GetCurrentMonotonicFrequency();
// Convert to microseconds.
int64_t seconds = ticks / frequency;
int64_t leftover_ticks = ticks - (seconds * frequency);
int64_t result = seconds * kMicrosecondsPerSecond;
result += ((leftover_ticks * kMicrosecondsPerSecond) / frequency);
return result;
}
int64_t OS::GetCurrentThreadCPUMicros() {
// TODO(johnmccutchan): Implement. See base/time_win.cc for details.
return -1;
}
int64_t OS::GetCurrentThreadCPUMicrosForTimeline() {
return OS::GetCurrentThreadCPUMicros();
}
intptr_t OS::ActivationFrameAlignment() {
#if defined(TARGET_ARCH_ARM64)
return 16;
#elif defined(TARGET_ARCH_ARM)
return 8;
#elif defined(_WIN64)
// Windows 64-bit ABI requires the stack to be 16-byte aligned.
return 16;
#else
// No requirements on Win32.
return 1;
#endif
}
int OS::NumberOfAvailableProcessors() {
SYSTEM_INFO info;
GetSystemInfo(&info);
return info.dwNumberOfProcessors;
}
void OS::Sleep(int64_t millis) {
::Sleep(millis);
}
void OS::SleepMicros(int64_t micros) {
// Windows only supports millisecond sleeps.
if (micros < kMicrosecondsPerMillisecond) {
// Calling ::Sleep with 0 has no determined behaviour, round up.
micros = kMicrosecondsPerMillisecond;
}
OS::Sleep(micros / kMicrosecondsPerMillisecond);
}
void OS::DebugBreak() {
#if defined(_MSC_VER)
// Microsoft Visual C/C++ or drop-in replacement.
__debugbreak();
#elif defined(__GCC__)
__builtin_trap();
#else
// Microsoft style assembly.
__asm {
int 3
}
#endif
}
DART_NOINLINE uintptr_t OS::GetProgramCounter() {
return reinterpret_cast<uintptr_t>(_ReturnAddress());
}
void OS::Print(const char* format, ...) {
va_list args;
va_start(args, format);
VFPrint(stdout, format, args);
va_end(args);
}
void OS::VFPrint(FILE* stream, const char* format, va_list args) {
vfprintf(stream, format, args);
fflush(stream);
}
char* OS::SCreate(Zone* zone, const char* format, ...) {
va_list args;
va_start(args, format);
char* buffer = VSCreate(zone, format, args);
va_end(args);
return buffer;
}
char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
// Measure.
va_list measure_args;
va_copy(measure_args, args);
intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args);
va_end(measure_args);
char* buffer;
if (zone) {
buffer = zone->Alloc<char>(len + 1);
} else {
buffer = reinterpret_cast<char*>(malloc(len + 1));
}
ASSERT(buffer != NULL);
// Print.
va_list print_args;
va_copy(print_args, args);
Utils::VSNPrint(buffer, len + 1, format, print_args);
va_end(print_args);
return buffer;
}
bool OS::StringToInt64(const char* str, int64_t* value) {
ASSERT(str != NULL && strlen(str) > 0 && value != NULL);
int32_t base = 10;
char* endptr;
int i = 0;
if (str[0] == '-') {
i = 1;
} else if (str[0] == '+') {
i = 1;
}
if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') &&
(str[i + 2] != '\0')) {
base = 16;
}
errno = 0;
if (base == 16) {
// Unsigned 64-bit hexadecimal integer literals are allowed but
// immediately interpreted as signed 64-bit integers.
*value = static_cast<int64_t>(_strtoui64(str, &endptr, base));
} else {
*value = _strtoi64(str, &endptr, base);
}
return ((errno == 0) && (endptr != str) && (*endptr == 0));
}
void OS::RegisterCodeObservers() {}
void OS::PrintErr(const char* format, ...) {
va_list args;
va_start(args, format);
VFPrint(stderr, format, args);
va_end(args);
}
void OS::Init() {
static bool init_once_called = false;
if (init_once_called) {
return;
}
init_once_called = true;
// Do not pop up a message box when abort is called.
_set_abort_behavior(0, _WRITE_ABORT_MSG);
ThreadLocalData::Init();
LARGE_INTEGER ticks_per_sec;
if (!QueryPerformanceFrequency(&ticks_per_sec)) {
qpc_ticks_per_second = 0;
} else {
qpc_ticks_per_second = static_cast<int64_t>(ticks_per_sec.QuadPart);
}
}
void OS::Cleanup() {
// TODO(zra): Enable once VM can shutdown cleanly.
// ThreadLocalData::Cleanup();
}
void OS::PrepareToAbort() {
// TODO(zra): Remove once VM shuts down cleanly.
private_flag_windows_run_tls_destructors = false;
}
void OS::Abort() {
PrepareToAbort();
abort();
}
void OS::Exit(int code) {
// TODO(zra): Remove once VM shuts down cleanly.
private_flag_windows_run_tls_destructors = false;
// On Windows we use ExitProcess so that threads can't clobber the exit_code.
// See: https://code.google.com/p/nativeclient/issues/detail?id=2870
::ExitProcess(code);
}
} // namespace dart
#endif // defined(DART_HOST_OS_WINDOWS)