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 "platform/globals.h"
6#if defined(HOST_OS_WINDOWS)
7
8#include "bin/platform.h"
9
10#include <crtdbg.h>
11
12#include "bin/console.h"
13#include "bin/file.h"
14#include "bin/lockers.h"
15#include "platform/syslog.h"
16#if !defined(PLATFORM_DISABLE_SOCKET)
17#include "bin/socket.h"
18#endif
19#include "bin/thread.h"
20#include "bin/utils.h"
21#include "bin/utils_win.h"
22
23
24namespace dart {
25namespace bin {
26
27const char* Platform::executable_name_ = NULL;
28char* Platform::resolved_executable_name_ = NULL;
29int Platform::script_index_ = 1;
30char** Platform::argv_ = NULL;
31
32class PlatformWin {
33 public:
34 static void InitOnce() {
35 // Set up a no-op handler so that CRT functions return an error instead of
36 // hitting an assertion failure.
37 // See: https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
38 _set_invalid_parameter_handler(InvalidParameterHandler);
39 // Disable the message box for assertions in the CRT in Debug builds.
40 // See: https://msdn.microsoft.com/en-us/library/1y71x448.aspx
41 _CrtSetReportMode(_CRT_ASSERT, 0);
42
43 // Disable dialog boxes for "critical" errors or when OpenFile cannot find
44 // the requested file. However only disable error boxes for general
45 // protection faults if an environment variable is set. Passing
46 // SEM_NOGPFAULTERRORBOX completely disables WindowsErrorReporting (WER)
47 // for the process, which means users loose ability to enable local dump
48 // archiving to collect minidumps for Dart VM crashes.
49 // Our test runner would set DART_SUPPRESS_WER to suppress WER UI during
50 // test suite execution.
51 // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx
52 UINT uMode = SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
53 if (getenv("DART_SUPPRESS_WER") != nullptr) {
54 uMode |= SEM_NOGPFAULTERRORBOX;
55 }
56 SetErrorMode(uMode);
57#ifndef PRODUCT
58 // Set up global exception handler to be able to dump stack trace on crash.
59 SetExceptionHandler();
60#endif
61 }
62
63 // Windows top-level unhandled exception handler function.
64 // See MSDN documentation for UnhandledExceptionFilter.
65 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681401(v=vs.85).aspx
66 static LONG WINAPI
67 DartExceptionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo) {
68 if ((ExceptionInfo->ExceptionRecord->ExceptionCode ==
69 EXCEPTION_ACCESS_VIOLATION) ||
70 (ExceptionInfo->ExceptionRecord->ExceptionCode ==
71 EXCEPTION_ILLEGAL_INSTRUCTION)) {
72 Syslog::PrintErr(
73 "\n===== CRASH =====\n"
74 "ExceptionCode=%d, ExceptionFlags=%d, ExceptionAddress=%p\n",
75 ExceptionInfo->ExceptionRecord->ExceptionCode,
76 ExceptionInfo->ExceptionRecord->ExceptionFlags,
77 ExceptionInfo->ExceptionRecord->ExceptionAddress);
78 Dart_DumpNativeStackTrace(ExceptionInfo->ContextRecord);
79 Console::RestoreConfig();
80 // Note: we want to abort(...) here instead of exiting because exiting
81 // would not cause WER to generate a minidump.
82 Dart_PrepareToAbort();
83 abort();
84 }
85 return EXCEPTION_CONTINUE_SEARCH;
86 }
87
88 static void SetExceptionHandler() {
89 SetUnhandledExceptionFilter(DartExceptionHandler);
90 }
91
92 private:
93 static void InvalidParameterHandler(const wchar_t* expression,
94 const wchar_t* function,
95 const wchar_t* file,
96 unsigned int line,
97 uintptr_t reserved) {
98 // Doing nothing here means that the CRT call that invoked it will
99 // return an error code and/or set errno.
100 }
101
102 DISALLOW_ALLOCATION();
103 DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformWin);
104};
105
106bool Platform::Initialize() {
107 PlatformWin::InitOnce();
108 return true;
109}
110
111int Platform::NumberOfProcessors() {
112 SYSTEM_INFO info;
113 GetSystemInfo(&info);
114 return info.dwNumberOfProcessors;
115}
116
117const char* Platform::OperatingSystem() {
118 return "windows";
119}
120
121// We pull the version number, and other version information out of the
122// registry because GetVersionEx() and friends lie about the OS version after
123// Windows 8.1. See:
124// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
125static const wchar_t* kCurrentVersion =
126 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
127
128static bool GetCurrentVersionDWord(const wchar_t* field, DWORD* value) {
129 DWORD value_size = sizeof(*value);
130 LONG err = RegGetValue(HKEY_LOCAL_MACHINE, kCurrentVersion, field,
131 RRF_RT_REG_DWORD, NULL, value, &value_size);
132 return err == ERROR_SUCCESS;
133}
134
135static bool GetCurrentVersionString(const wchar_t* field, const char** value) {
136 wchar_t wversion[256];
137 DWORD wversion_size = sizeof(wversion);
138 LONG err = RegGetValue(HKEY_LOCAL_MACHINE, kCurrentVersion, field,
139 RRF_RT_REG_SZ, NULL, wversion, &wversion_size);
140 if (err != ERROR_SUCCESS) {
141 return false;
142 }
143 *value = StringUtilsWin::WideToUtf8(wversion);
144 return true;
145}
146
147static const char* VersionNumber() {
148 // Try to get CurrentMajorVersionNumber. If that fails, fall back on
149 // CurrentVersion. If it succeeds also get CurrentMinorVersionNumber.
150 DWORD major;
151 if (!GetCurrentVersionDWord(L"CurrentMajorVersionNumber", &major)) {
152 const char* version;
153 if (!GetCurrentVersionString(L"CurrentVersion", &version)) {
154 return NULL;
155 }
156 return version;
157 }
158
159 DWORD minor;
160 if (!GetCurrentVersionDWord(L"CurrentMinorVersionNumber", &minor)) {
161 return NULL;
162 }
163 const char* kFormat = "%d.%d";
164 int len = snprintf(NULL, 0, kFormat, major, minor);
165 if (len < 0) {
166 return NULL;
167 }
168 char* result = DartUtils::ScopedCString(len + 1);
169 ASSERT(result != NULL);
170 len = snprintf(result, len + 1, kFormat, major, minor);
171 if (len < 0) {
172 return NULL;
173 }
174 return result;
175}
176
177const char* Platform::OperatingSystemVersion() {
178 // Get the product name, e.g. "Windows 10 Home".
179 const char* name;
180 if (!GetCurrentVersionString(L"ProductName", &name)) {
181 return NULL;
182 }
183
184 // Get the version number, e.g. "10.0".
185 const char* version_number = VersionNumber();
186 if (version_number == NULL) {
187 return NULL;
188 }
189
190 // Get the build number.
191 const char* build;
192 if (!GetCurrentVersionString(L"CurrentBuild", &build)) {
193 return NULL;
194 }
195
196 // Put it all together.
197 const char* kFormat = "\"%s\" %s (Build %s)";
198 int len = snprintf(NULL, 0, kFormat, name, version_number, build);
199 char* result = DartUtils::ScopedCString(len + 1);
200 len = snprintf(result, len + 1, kFormat, name, version_number, build);
201 return result;
202}
203
204const char* Platform::LibraryPrefix() {
205 return "";
206}
207
208const char* Platform::LibraryExtension() {
209 return "dll";
210}
211
212const char* Platform::LocaleName() {
213 wchar_t locale_name[LOCALE_NAME_MAX_LENGTH];
214 int result = GetUserDefaultLocaleName(locale_name, LOCALE_NAME_MAX_LENGTH);
215 if (result == 0) {
216 return NULL;
217 }
218 return StringUtilsWin::WideToUtf8(locale_name);
219}
220
221bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) {
222#if defined(PLATFORM_DISABLE_SOCKET)
223 return false;
224#else
225 if (!SocketBase::Initialize()) {
226 return false;
227 }
228 return gethostname(buffer, buffer_length) == 0;
229#endif
230}
231
232char** Platform::Environment(intptr_t* count) {
233 wchar_t* strings = GetEnvironmentStringsW();
234 if (strings == NULL) {
235 return NULL;
236 }
237 wchar_t* tmp = strings;
238 intptr_t i = 0;
239 while (*tmp != '\0') {
240 // Skip environment strings starting with "=".
241 // These are synthetic variables corresponding to dynamic environment
242 // variables like %=C:% and %=ExitCode%, and the Dart environment does
243 // not include these.
244 if (*tmp != '=') {
245 i++;
246 }
247 tmp += (wcslen(tmp) + 1);
248 }
249 *count = i;
250 char** result;
251 result = reinterpret_cast<char**>(Dart_ScopeAllocate(i * sizeof(*result)));
252 tmp = strings;
253 for (intptr_t current = 0; current < i;) {
254 // Skip the strings that were not counted above.
255 if (*tmp != '=') {
256 result[current++] = StringUtilsWin::WideToUtf8(tmp);
257 }
258 tmp += (wcslen(tmp) + 1);
259 }
260 FreeEnvironmentStringsW(strings);
261 return result;
262}
263
264const char* Platform::GetExecutableName() {
265 return executable_name_;
266}
267
268const char* Platform::ResolveExecutablePath() {
269 // GetModuleFileNameW cannot directly provide information on the
270 // required buffer size, so start out with a buffer large enough to
271 // hold any Windows path.
272 const int kTmpBufferSize = 32768;
273 wchar_t* tmp_buffer =
274 reinterpret_cast<wchar_t*>(Dart_ScopeAllocate(kTmpBufferSize));
275 // Ensure no last error before calling GetModuleFileNameW.
276 SetLastError(ERROR_SUCCESS);
277 // Get the required length of the buffer.
278 GetModuleFileNameW(nullptr, tmp_buffer, kTmpBufferSize);
279 if (GetLastError() != ERROR_SUCCESS) {
280 return NULL;
281 }
282 char* path = StringUtilsWin::WideToUtf8(tmp_buffer);
283 // Return the canonical path as the returned path might contain symlinks.
284 const char* canon_path = File::GetCanonicalPath(NULL, path);
285 return canon_path;
286}
287
288intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
289 // Ensure no last error before calling GetModuleFileNameW.
290 SetLastError(ERROR_SUCCESS);
291 const int kTmpBufferSize = 32768;
292 wchar_t tmp_buffer[kTmpBufferSize];
293 // Get the required length of the buffer.
294 GetModuleFileNameW(nullptr, tmp_buffer, kTmpBufferSize);
295 if (GetLastError() != ERROR_SUCCESS) {
296 return -1;
297 }
298 WideToUtf8Scope wide_to_utf8_scope(tmp_buffer);
299 if (wide_to_utf8_scope.length() <= result_size) {
300 strncpy(result, wide_to_utf8_scope.utf8(), result_size);
301 return wide_to_utf8_scope.length();
302 }
303 return -1;
304}
305
306void Platform::Exit(int exit_code) {
307 // Restore the console's output code page
308 Console::RestoreConfig();
309 // On Windows we use ExitProcess so that threads can't clobber the exit_code.
310 // See: https://code.google.com/p/nativeclient/issues/detail?id=2870
311 Dart_PrepareToAbort();
312 ::ExitProcess(exit_code);
313}
314
315void Platform::SetCoreDumpResourceLimit(int value) {
316 // Not supported.
317}
318
319} // namespace bin
320} // namespace dart
321
322#endif // defined(HOST_OS_WINDOWS)
323