From 3c8d3b6870dc099a667a2836d4237e36abc59dff Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 12 Mar 2018 20:31:09 +0000 Subject: [PATCH] [VM] Refactored console configuration out of platform_*.cc into console_config.h and reapplied c9700040c344133f496e6451dffe20de3121acb6 for POSIX platforms (Windows needs more work). We may also want to move code in bin/stdio.h. Change-Id: Ic7cbc690f490e0127387d9a23866ea7eddbc84cf Reviewed-on: https://dart-review.googlesource.com/45746 Commit-Queue: Ben Konyi Reviewed-by: Zach Anderson --- runtime/bin/console.h | 26 ++++++ runtime/bin/console_posix.cc | 92 ++++++++++++++++++++ runtime/bin/console_win.cc | 143 ++++++++++++++++++++++++++++++++ runtime/bin/gen_snapshot.cc | 2 + runtime/bin/io_impl_sources.gni | 3 + runtime/bin/main.cc | 4 + runtime/bin/platform_android.cc | 5 +- runtime/bin/platform_fuchsia.cc | 2 + runtime/bin/platform_linux.cc | 5 +- runtime/bin/platform_macos.cc | 5 +- runtime/bin/platform_win.cc | 117 +------------------------- runtime/bin/run_vm_tests.cc | 4 + 12 files changed, 287 insertions(+), 121 deletions(-) create mode 100644 runtime/bin/console.h create mode 100644 runtime/bin/console_posix.cc create mode 100644 runtime/bin/console_win.cc diff --git a/runtime/bin/console.h b/runtime/bin/console.h new file mode 100644 index 00000000000..519d21c9e11 --- /dev/null +++ b/runtime/bin/console.h @@ -0,0 +1,26 @@ +// Copyright (c) 2018, 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. + +#ifndef RUNTIME_BIN_CONSOLE_H_ +#define RUNTIME_BIN_CONSOLE_H_ + +#include "platform/globals.h" + +namespace dart { +namespace bin { + +class Console { + public: + static void SaveConfig(); + static void RestoreConfig(); + + private: + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Console); +}; + +} // namespace bin +} // namespace dart + +#endif // RUNTIME_BIN_CONSOLE_H_ diff --git a/runtime/bin/console_posix.cc b/runtime/bin/console_posix.cc new file mode 100644 index 00000000000..1abbe05c520 --- /dev/null +++ b/runtime/bin/console_posix.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2018, 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_LINUX) || defined(HOST_OS_MACOS) || \ + defined(HOST_OS_ANDROID) || defined(HOST_OS_FUCHSIA) + +#include "bin/console.h" + +#include +#include +#include + +#include "bin/fdutils.h" +#include "platform/signal_blocker.h" + +namespace dart { +namespace bin { + +class PosixConsole { + public: + static const tcflag_t kInvalidFlag = -1; + + static void Initialize() { + SaveMode(STDOUT_FILENO, &stdout_initial_c_lflag_); + SaveMode(STDERR_FILENO, &stderr_initial_c_lflag_); + SaveMode(STDIN_FILENO, &stdin_initial_c_lflag_); + } + + static void Cleanup() { + RestoreMode(STDOUT_FILENO, stdout_initial_c_lflag_); + RestoreMode(STDERR_FILENO, stderr_initial_c_lflag_); + RestoreMode(STDIN_FILENO, stdin_initial_c_lflag_); + ClearLFlags(); + } + + private: + static tcflag_t stdout_initial_c_lflag_; + static tcflag_t stderr_initial_c_lflag_; + static tcflag_t stdin_initial_c_lflag_; + + static void ClearLFlags() { + stdout_initial_c_lflag_ = kInvalidFlag; + stderr_initial_c_lflag_ = kInvalidFlag; + stdin_initial_c_lflag_ = kInvalidFlag; + } + + static void SaveMode(intptr_t fd, tcflag_t* flag) { + ASSERT(flag != NULL); + struct termios term; + int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); + if (status != 0) { + return; + } + *flag = term.c_lflag; + } + + static void RestoreMode(intptr_t fd, tcflag_t flag) { + if (flag == kInvalidFlag) { + return; + } + struct termios term; + int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); + if (status != 0) { + return; + } + term.c_lflag = flag; + NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); + } + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(PosixConsole); +}; + +tcflag_t PosixConsole::stdout_initial_c_lflag_ = PosixConsole::kInvalidFlag; +tcflag_t PosixConsole::stderr_initial_c_lflag_ = PosixConsole::kInvalidFlag; +tcflag_t PosixConsole::stdin_initial_c_lflag_ = PosixConsole::kInvalidFlag; + +void Console::SaveConfig() { + PosixConsole::Initialize(); +} + +void Console::RestoreConfig() { + PosixConsole::Cleanup(); +} + +} // namespace bin +} // namespace dart + +#endif // defined(HOST_OS_LINUX) || defined(HOST_OS_MACOS) || \ + // defined(HOST_OS_ANDROID) || defined(HOST_OS_FUCHSIA) diff --git a/runtime/bin/console_win.cc b/runtime/bin/console_win.cc new file mode 100644 index 00000000000..6442dc2277d --- /dev/null +++ b/runtime/bin/console_win.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2018, 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/console.h" + +#include "bin/file.h" +#include "bin/lockers.h" +#include "bin/platform.h" +#include "bin/utils.h" +#include "bin/utils_win.h" + +// These are not always defined in the header files. See: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx +#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT +#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 +#endif + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +namespace dart { +namespace bin { + +class ConsoleWin { + public: + static const int kInvalidFlag = -1; + + static void Initialize() { + saved_output_cp_ = kInvalidFlag; + saved_input_cp_ = kInvalidFlag; + // Set up a signal handler that restores the console state on a + // CTRL_C_EVENT signal. This will only run when there is no signal handler + // registered for the CTRL_C_EVENT from Dart code. + SetConsoleCtrlHandler(SignalHandler, TRUE); + + // Set both the input and output code pages to UTF8. + const int output_cp = GetConsoleOutputCP(); + const int input_cp = GetConsoleCP(); + if (output_cp != CP_UTF8) { + SetConsoleOutputCP(CP_UTF8); + saved_output_cp_ = output_cp; + } + if (input_cp != CP_UTF8) { + SetConsoleCP(CP_UTF8); + saved_input_cp_ = input_cp; + } + + // Try to set the bits for ANSI support, but swallow any failures. + saved_stdout_mode_ = + ModifyMode(STD_OUTPUT_HANDLE, ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + // TODO(28984): Due to issue #29104, we cannot set + // ENABLE_VIRTUAL_TERMINAL_INPUT here, as it causes ENABLE_PROCESSED_INPUT + // to be ignored. + } + + static void Cleanup() { + // STD_OUTPUT_HANDLE, may have been closed or redirected. Therefore, we + // explicitly open the CONOUT$, CONERR$ and CONIN$ devices, so that we can + // be sure that we are really restoring the console to its original state. + if (saved_stdout_mode_ != kInvalidFlag) { + CleanupDevices("CONOUT$", STD_OUTPUT_HANDLE, saved_stdout_mode_); + saved_stdout_mode_ = kInvalidFlag; + } + if (saved_output_cp_ != kInvalidFlag) { + SetConsoleOutputCP(saved_output_cp_); + saved_output_cp_ = kInvalidFlag; + } + if (saved_input_cp_ != kInvalidFlag) { + SetConsoleCP(saved_input_cp_); + saved_input_cp_ = kInvalidFlag; + } + } + + private: + static int saved_output_cp_; + static int saved_input_cp_; + static DWORD saved_stdout_mode_; + + static BOOL WINAPI SignalHandler(DWORD signal) { + if (signal == CTRL_C_EVENT) { + Cleanup(); + } + return FALSE; + } + + static DWORD ModifyMode(DWORD handle, DWORD flags) { + HANDLE h = GetStdHandle(handle); + DWORD mode; + DWORD old_mode = 0; + + if ((h != INVALID_HANDLE_VALUE) && GetConsoleMode(h, &mode)) { + old_mode = mode; + if (flags != 0) { + const DWORD request = mode | flags; + SetConsoleMode(h, request); + } + } + return old_mode; + } + + static void CleanupDevices(const char* device, + DWORD handle, + DWORD orig_flags) { + const intptr_t kWideBufLen = 64; + wchar_t widebuf[kWideBufLen]; + int result = + MultiByteToWideChar(CP_UTF8, 0, device, -1, widebuf, kWideBufLen); + ASSERT(result != 0); + HANDLE h = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (h != INVALID_HANDLE_VALUE) { + SetStdHandle(STD_OUTPUT_HANDLE, h); + if (orig_flags != kInvalidFlag) { + SetConsoleMode(h, orig_flags); + } + } + } + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(ConsoleWin); +}; + +int ConsoleWin::saved_output_cp_ = ConsoleWin::kInvalidFlag; +int ConsoleWin::saved_input_cp_ = ConsoleWin::kInvalidFlag; +DWORD ConsoleWin::saved_stdout_mode_ = ConsoleWin::kInvalidFlag; + +void Console::SaveConfig() { + ConsoleWin::Initialize(); +} + +void Console::RestoreConfig() { + ConsoleWin::Cleanup(); +} + +} // namespace bin +} // namespace dart + +#endif // defined(HOST_OS_WINDOWS) diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc index 7179a535264..145e48b57d6 100644 --- a/runtime/bin/gen_snapshot.cc +++ b/runtime/bin/gen_snapshot.cc @@ -12,6 +12,7 @@ #include #include "bin/builtin.h" +#include "bin/console.h" #include "bin/dartutils.h" #include "bin/dfe.h" #include "bin/eventhandler.h" @@ -1489,6 +1490,7 @@ int main(int argc, char** argv) { Log::PrintErr("Initialization failed\n"); return kErrorExitCode; } + Console::SaveConfig(); Thread::InitOnce(); Loader::InitOnce(); DartUtils::SetOriginalWorkingDirectory(); diff --git a/runtime/bin/io_impl_sources.gni b/runtime/bin/io_impl_sources.gni index 408652f69b6..1e46f4d70eb 100644 --- a/runtime/bin/io_impl_sources.gni +++ b/runtime/bin/io_impl_sources.gni @@ -5,6 +5,9 @@ # This file contains some C++ sources for the dart:io library. The other # implementation files are in builtin_impl_sources.gypi. io_impl_sources = [ + "console.h", + "console_posix.cc", + "console_win.cc", "eventhandler.cc", "eventhandler.h", "eventhandler_android.cc", diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index a9e51c35d34..f7868e1e946 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -10,6 +10,7 @@ #include "include/dart_tools_api.h" #include "bin/builtin.h" +#include "bin/console.h" #include "bin/dartutils.h" #include "bin/dfe.h" #include "bin/directory.h" @@ -1008,6 +1009,9 @@ void main(int argc, char** argv) { Platform::Exit(kErrorExitCode); } + // Save the console state so we can restore it at shutdown. + Console::SaveConfig(); + // On Windows, the argv strings are code page encoded and not // utf8. We need to convert them to utf8. bool argv_converted = ShellUtils::GetUtf8Argv(argc, argv); diff --git a/runtime/bin/platform_android.cc b/runtime/bin/platform_android.cc index 8031f9e9680..6471168d499 100644 --- a/runtime/bin/platform_android.cc +++ b/runtime/bin/platform_android.cc @@ -7,14 +7,14 @@ #include "bin/platform.h" +#include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT -#include "bin/fdutils.h" +#include "bin/console.h" #include "bin/file.h" -#include "bin/log.h" namespace dart { namespace bin { @@ -152,6 +152,7 @@ const char* Platform::ResolveExecutablePath() { } void Platform::Exit(int exit_code) { + Console::RestoreConfig(); exit(exit_code); } diff --git a/runtime/bin/platform_fuchsia.cc b/runtime/bin/platform_fuchsia.cc index cda405eab84..58aecb122db 100644 --- a/runtime/bin/platform_fuchsia.cc +++ b/runtime/bin/platform_fuchsia.cc @@ -14,6 +14,7 @@ #include #include +#include "bin/console.h" #include "bin/dartutils.h" #include "bin/fdutils.h" #include "bin/file.h" @@ -147,6 +148,7 @@ const char* Platform::ResolveExecutablePath() { } void Platform::Exit(int exit_code) { + Console::RestoreConfig(); exit(exit_code); } diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc index 0d5d885ba6d..afd8cac4425 100644 --- a/runtime/bin/platform_linux.cc +++ b/runtime/bin/platform_linux.cc @@ -7,14 +7,14 @@ #include "bin/platform.h" +#include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT -#include "bin/fdutils.h" +#include "bin/console.h" #include "bin/file.h" -#include "bin/log.h" namespace dart { namespace bin { @@ -152,6 +152,7 @@ const char* Platform::ResolveExecutablePath() { } void Platform::Exit(int exit_code) { + Console::RestoreConfig(); exit(exit_code); } diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc index d6544be10df..5c148b18e76 100644 --- a/runtime/bin/platform_macos.cc +++ b/runtime/bin/platform_macos.cc @@ -12,6 +12,7 @@ #if !HOST_OS_IOS #include // NOLINT #endif // !HOST_OS_IOS +#include // NOLINT #include #include // NOLINT #include // NOLINT @@ -20,9 +21,8 @@ #include // NOLINT #include // NOLINT -#include "bin/fdutils.h" +#include "bin/console.h" #include "bin/file.h" -#include "bin/log.h" namespace dart { namespace bin { @@ -240,6 +240,7 @@ const char* Platform::ResolveExecutablePath() { } void Platform::Exit(int exit_code) { + Console::RestoreConfig(); exit(exit_code); } diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc index b7a211570b1..796f2508b6a 100644 --- a/runtime/bin/platform_win.cc +++ b/runtime/bin/platform_win.cc @@ -9,6 +9,7 @@ #include +#include "bin/console.h" #include "bin/file.h" #include "bin/lockers.h" #include "bin/log.h" @@ -19,15 +20,6 @@ #include "bin/utils.h" #include "bin/utils_win.h" -// These are not always defined in the header files. See: -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx -#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT -#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 -#endif - -#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING -#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 -#endif namespace dart { @@ -44,9 +36,6 @@ char** Platform::argv_ = NULL; class PlatformWin { public: static void InitOnce() { - platform_win_mutex_ = new Mutex(); - saved_output_cp_ = -1; - saved_input_cp_ = -1; // 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 @@ -59,58 +48,12 @@ class PlatformWin { // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | SEM_NOGPFAULTERRORBOX); - // Set up a signal handler that restores the console state on a - // CTRL_C_EVENT signal. This will only run when there is no signal handler - // registered for the CTRL_C_EVENT from Dart code. - SetConsoleCtrlHandler(SignalHandler, TRUE); #ifndef PRODUCT // Set up global exception handler to be able to dump stack trace on crash. SetExceptionHandler(); #endif } - static BOOL WINAPI SignalHandler(DWORD signal) { - if (signal == CTRL_C_EVENT) { - // We call this without taking the lock because this is a signal - // handler, and because the process is about to go down. - RestoreConsoleLocked(); - } - return FALSE; - } - - static void SaveAndConfigureConsole() { - MutexLocker ml(platform_win_mutex_); - // Set both the input and output code pages to UTF8. - ASSERT(saved_output_cp_ == -1); - ASSERT(saved_input_cp_ == -1); - const int output_cp = GetConsoleOutputCP(); - const int input_cp = GetConsoleCP(); - if (output_cp != CP_UTF8) { - SetConsoleOutputCP(CP_UTF8); - saved_output_cp_ = output_cp; - } - if (input_cp != CP_UTF8) { - SetConsoleCP(CP_UTF8); - saved_input_cp_ = input_cp; - } - - // Try to set the bits for ANSI support, but swallow any failures. - HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD out_mode; - if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) { - const DWORD request = out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(out, request); - } - // TODO(28984): Due to issue #29104, we cannot set - // ENABLE_VIRTUAL_TERMINAL_INPUT here, as it causes ENABLE_PROCESSED_INPUT - // to be ignored. - } - - static void RestoreConsole() { - MutexLocker ml(platform_win_mutex_); - RestoreConsoleLocked(); - } - // 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 @@ -139,57 +82,6 @@ class PlatformWin { } private: - static Mutex* platform_win_mutex_; - static int saved_output_cp_; - static int saved_input_cp_; - - static void RestoreConsoleLocked() { - // STD_OUTPUT_HANDLE and STD_INPUT_HANDLE may have been closed or - // redirected. Therefore, we explicitly open the CONOUT$ and CONIN$ - // devices, so that we can be sure that we are really unsetting - // ENABLE_VIRTUAL_TERMINAL_PROCESSING and ENABLE_VIRTUAL_TERMINAL_INPUT - // respectively. - const intptr_t kWideBufLen = 64; - const char* conout = "CONOUT$"; - wchar_t widebuf[kWideBufLen]; - int result = - MultiByteToWideChar(CP_UTF8, 0, conout, -1, widebuf, kWideBufLen); - ASSERT(result != 0); - HANDLE out = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if (out != INVALID_HANDLE_VALUE) { - SetStdHandle(STD_OUTPUT_HANDLE, out); - } - DWORD out_mode; - if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) { - DWORD request = out_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT; - SetConsoleMode(out, request); - } - - const char* conin = "CONIN$"; - result = MultiByteToWideChar(CP_UTF8, 0, conin, -1, widebuf, kWideBufLen); - ASSERT(result != 0); - HANDLE in = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if (in != INVALID_HANDLE_VALUE) { - SetStdHandle(STD_INPUT_HANDLE, in); - } - DWORD in_mode; - if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) { - DWORD request = in_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT; - SetConsoleMode(in, request); - } - - if (saved_output_cp_ != -1) { - SetConsoleOutputCP(saved_output_cp_); - saved_output_cp_ = -1; - } - if (saved_input_cp_ != -1) { - SetConsoleCP(saved_input_cp_); - saved_input_cp_ = -1; - } - } - static void InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, @@ -203,13 +95,8 @@ class PlatformWin { DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformWin); }; -int PlatformWin::saved_output_cp_ = -1; -int PlatformWin::saved_input_cp_ = -1; -Mutex* PlatformWin::platform_win_mutex_ = NULL; - bool Platform::Initialize() { PlatformWin::InitOnce(); - PlatformWin::SaveAndConfigureConsole(); return true; } @@ -394,7 +281,7 @@ 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 - PlatformWin::RestoreConsole(); + 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); diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc index 6246c4a6425..42e4f632fa0 100644 --- a/runtime/bin/run_vm_tests.cc +++ b/runtime/bin/run_vm_tests.cc @@ -4,6 +4,7 @@ #include +#include "bin/console.h" #include "bin/dartutils.h" #include "bin/dfe.h" #include "bin/eventhandler.h" @@ -212,6 +213,9 @@ static int Main(int argc, const char** argv) { return 1; } + // Save the console state so we can restore it later. + dart::bin::Console::SaveConfig(); + if (argc < 2) { // Bad parameter count. PrintUsage();