Files
sdk/runtime/bin/platform_win.cc
T
Vyacheslav Egorov 6bf4d2ab1a [vm] Do not suppress WER when running VM outside of our test suite.
We prevent our crashing tests from hitting timeouts on the bots
by disabling Windows Error Reporting UI via SetErrorMode.

However this also disables builtin crash dump generation functionality
that WER has.

This change moves WER suppression for GP faults under a flag to
make sure that we can collect crash dumps when VM crashes on
user machines.

We also make sure that our exception handler call abort()
instead of calling exit() - because exit would not cause
WER to generate a dump.

Bug: https://github.com/flutter/flutter/issues/22558
Change-Id: I42f3e31cfaaa578f6a040b8f10621e5663cddc09
Reviewed-on: https://dart-review.googlesource.com/c/87061
Auto-Submit: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
2018-12-12 18:34:13 +00:00

312 lines
9.8 KiB
C++

// 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 "platform/globals.h"
#if defined(HOST_OS_WINDOWS)
#include "bin/platform.h"
#include <crtdbg.h>
#include "bin/console.h"
#include "bin/file.h"
#include "bin/lockers.h"
#include "bin/log.h"
#if !defined(PLATFORM_DISABLE_SOCKET)
#include "bin/socket.h"
#endif
#include "bin/thread.h"
#include "bin/utils.h"
#include "bin/utils_win.h"
namespace dart {
// Defined in vm/os_thread_win.cc
extern bool private_flag_windows_run_tls_destructors;
namespace bin {
const char* Platform::executable_name_ = NULL;
char* Platform::resolved_executable_name_ = NULL;
int Platform::script_index_ = 1;
char** Platform::argv_ = NULL;
class PlatformWin {
public:
static void InitOnce() {
// Set up a no-op handler so that CRT functions return an error instead of
// hitting an assertion failure.
// See: https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
_set_invalid_parameter_handler(InvalidParameterHandler);
// Disable the message box for assertions in the CRT in Debug builds.
// See: https://msdn.microsoft.com/en-us/library/1y71x448.aspx
_CrtSetReportMode(_CRT_ASSERT, 0);
// Disable dialog boxes for "critical" errors or when OpenFile cannot find
// the requested file. However only disable error boxes for general
// protection faults if an environment variable is set. Passing
// SEM_NOGPFAULTERRORBOX completely disables WindowsErrorReporting (WER)
// for the process, which means users loose ability to enable local dump
// archiving to collect minidumps for Dart VM crashes.
// Our test runner would set DART_SUPPRESS_WER to suppress WER UI during
// test suite execution.
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx
UINT uMode = SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
if (getenv("DART_SUPPRESS_WER") != nullptr) {
uMode |= SEM_NOGPFAULTERRORBOX;
}
SetErrorMode(uMode);
#ifndef PRODUCT
// Set up global exception handler to be able to dump stack trace on crash.
SetExceptionHandler();
#endif
}
// Windows top-level unhandled exception handler function.
// See MSDN documentation for UnhandledExceptionFilter.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681401(v=vs.85).aspx
static LONG WINAPI
DartExceptionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo) {
if ((ExceptionInfo->ExceptionRecord->ExceptionCode ==
EXCEPTION_ACCESS_VIOLATION) ||
(ExceptionInfo->ExceptionRecord->ExceptionCode ==
EXCEPTION_ILLEGAL_INSTRUCTION)) {
Log::PrintErr(
"\n===== CRASH =====\n"
"version=%s\n"
"ExceptionCode=%d, ExceptionFlags=%d, ExceptionAddress=%p\n",
Dart_VersionString(), ExceptionInfo->ExceptionRecord->ExceptionCode,
ExceptionInfo->ExceptionRecord->ExceptionFlags,
ExceptionInfo->ExceptionRecord->ExceptionAddress);
Dart_DumpNativeStackTrace(ExceptionInfo->ContextRecord);
Console::RestoreConfig();
// TODO(zra): Remove once VM shuts down cleanly.
::dart::private_flag_windows_run_tls_destructors = false;
// Note: we want to abort(...) here instead of exiting because exiting
// would not cause WER to generate a minidump.
abort();
}
return EXCEPTION_CONTINUE_SEARCH;
}
static void SetExceptionHandler() {
SetUnhandledExceptionFilter(DartExceptionHandler);
}
private:
static void InvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t reserved) {
// Doing nothing here means that the CRT call that invoked it will
// return an error code and/or set errno.
}
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformWin);
};
bool Platform::Initialize() {
PlatformWin::InitOnce();
return true;
}
int Platform::NumberOfProcessors() {
SYSTEM_INFO info;
GetSystemInfo(&info);
return info.dwNumberOfProcessors;
}
const char* Platform::OperatingSystem() {
return "windows";
}
// We pull the version number, and other version information out of the
// registry because GetVersionEx() and friends lie about the OS version after
// Windows 8.1. See:
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
static const wchar_t* kCurrentVersion =
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
static bool GetCurrentVersionDWord(const wchar_t* field, DWORD* value) {
DWORD value_size = sizeof(*value);
LONG err = RegGetValue(HKEY_LOCAL_MACHINE, kCurrentVersion, field,
RRF_RT_REG_DWORD, NULL, value, &value_size);
return err == ERROR_SUCCESS;
}
static bool GetCurrentVersionString(const wchar_t* field, const char** value) {
wchar_t wversion[256];
DWORD wversion_size = sizeof(wversion);
LONG err = RegGetValue(HKEY_LOCAL_MACHINE, kCurrentVersion, field,
RRF_RT_REG_SZ, NULL, wversion, &wversion_size);
if (err != ERROR_SUCCESS) {
return false;
}
*value = StringUtilsWin::WideToUtf8(wversion);
return true;
}
static const char* VersionNumber() {
// Try to get CurrentMajorVersionNumber. If that fails, fall back on
// CurrentVersion. If it succeeds also get CurrentMinorVersionNumber.
DWORD major;
if (!GetCurrentVersionDWord(L"CurrentMajorVersionNumber", &major)) {
const char* version;
if (!GetCurrentVersionString(L"CurrentVersion", &version)) {
return NULL;
}
return version;
}
DWORD minor;
if (!GetCurrentVersionDWord(L"CurrentMinorVersionNumber", &minor)) {
return NULL;
}
const char* kFormat = "%d.%d";
int len = snprintf(NULL, 0, kFormat, major, minor);
if (len < 0) {
return NULL;
}
char* result = DartUtils::ScopedCString(len + 1);
ASSERT(result != NULL);
len = snprintf(result, len + 1, kFormat, major, minor);
if (len < 0) {
return NULL;
}
return result;
}
const char* Platform::OperatingSystemVersion() {
// Get the product name, e.g. "Windows 10 Home".
const char* name;
if (!GetCurrentVersionString(L"ProductName", &name)) {
return NULL;
}
// Get the version number, e.g. "10.0".
const char* version_number = VersionNumber();
if (version_number == NULL) {
return NULL;
}
// Get the build number.
const char* build;
if (!GetCurrentVersionString(L"CurrentBuild", &build)) {
return NULL;
}
// Put it all together.
const char* kFormat = "\"%s\" %s (Build %s)";
int len = snprintf(NULL, 0, kFormat, name, version_number, build);
char* result = DartUtils::ScopedCString(len + 1);
len = snprintf(result, len + 1, kFormat, name, version_number, build);
return result;
}
const char* Platform::LibraryPrefix() {
return "";
}
const char* Platform::LibraryExtension() {
return "dll";
}
const char* Platform::LocaleName() {
wchar_t locale_name[LOCALE_NAME_MAX_LENGTH];
int result = GetUserDefaultLocaleName(locale_name, LOCALE_NAME_MAX_LENGTH);
if (result == 0) {
return NULL;
}
return StringUtilsWin::WideToUtf8(locale_name);
}
bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) {
#if defined(PLATFORM_DISABLE_SOCKET)
return false;
#else
if (!SocketBase::Initialize()) {
return false;
}
return gethostname(buffer, buffer_length) == 0;
#endif
}
char** Platform::Environment(intptr_t* count) {
wchar_t* strings = GetEnvironmentStringsW();
if (strings == NULL) {
return NULL;
}
wchar_t* tmp = strings;
intptr_t i = 0;
while (*tmp != '\0') {
// Skip environment strings starting with "=".
// These are synthetic variables corresponding to dynamic environment
// variables like %=C:% and %=ExitCode%, and the Dart environment does
// not include these.
if (*tmp != '=') {
i++;
}
tmp += (wcslen(tmp) + 1);
}
*count = i;
char** result;
result = reinterpret_cast<char**>(Dart_ScopeAllocate(i * sizeof(*result)));
tmp = strings;
for (intptr_t current = 0; current < i;) {
// Skip the strings that were not counted above.
if (*tmp != '=') {
result[current++] = StringUtilsWin::WideToUtf8(tmp);
}
tmp += (wcslen(tmp) + 1);
}
FreeEnvironmentStringsW(strings);
return result;
}
const char* Platform::GetExecutableName() {
return executable_name_;
}
const char* Platform::ResolveExecutablePath() {
// GetModuleFileNameW cannot directly provide information on the
// required buffer size, so start out with a buffer large enough to
// hold any Windows path.
const int kTmpBufferSize = 32768;
wchar_t* tmp_buffer =
reinterpret_cast<wchar_t*>(Dart_ScopeAllocate(kTmpBufferSize));
// Ensure no last error before calling GetModuleFileNameW.
SetLastError(ERROR_SUCCESS);
// Get the required length of the buffer.
int path_length = GetModuleFileNameW(NULL, tmp_buffer, kTmpBufferSize);
if (GetLastError() != ERROR_SUCCESS) {
return NULL;
}
char* path = StringUtilsWin::WideToUtf8(tmp_buffer);
// Return the canonical path as the returned path might contain symlinks.
const char* canon_path = File::GetCanonicalPath(NULL, path);
return canon_path;
}
void Platform::Exit(int exit_code) {
// TODO(zra): Remove once VM shuts down cleanly.
::dart::private_flag_windows_run_tls_destructors = false;
// Restore the console's output code page
Console::RestoreConfig();
// 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(exit_code);
}
void Platform::SetCoreDumpResourceLimit(int value) {
// Not supported.
}
} // namespace bin
} // namespace dart
#endif // defined(HOST_OS_WINDOWS)