summaryrefslogtreecommitdiff
path: root/source/Host/windows
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host/windows')
-rw-r--r--source/Host/windows/Condition.cpp98
-rw-r--r--source/Host/windows/ConnectionGenericFileWindows.cpp354
-rw-r--r--source/Host/windows/EditLineWin.cpp435
-rw-r--r--source/Host/windows/FileSystem.cpp221
-rw-r--r--source/Host/windows/Host.cpp326
-rw-r--r--source/Host/windows/HostInfoWindows.cpp118
-rw-r--r--source/Host/windows/HostProcessWindows.cpp137
-rw-r--r--source/Host/windows/HostThreadWindows.cpp98
-rw-r--r--source/Host/windows/LockFileWindows.cpp93
-rw-r--r--source/Host/windows/Mutex.cpp109
-rw-r--r--source/Host/windows/PipeWindows.cpp336
-rw-r--r--source/Host/windows/ProcessLauncherWindows.cpp105
-rw-r--r--source/Host/windows/ProcessRunLock.cpp106
-rw-r--r--source/Host/windows/ThisThread.cpp66
-rw-r--r--source/Host/windows/Windows.cpp231
15 files changed, 2833 insertions, 0 deletions
diff --git a/source/Host/windows/Condition.cpp b/source/Host/windows/Condition.cpp
new file mode 100644
index 0000000000000..2f16ad77d7a3b
--- /dev/null
+++ b/source/Host/windows/Condition.cpp
@@ -0,0 +1,98 @@
+//===-- Condition.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+
+#include "lldb/Host/Condition.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Host/windows/windows.h"
+
+
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Default constructor
+//
+// The default constructor will initialize a new pthread condition
+// and maintain the condition in the object state.
+//----------------------------------------------------------------------
+Condition::Condition () :
+ m_condition()
+{
+ m_condition = static_cast<PCONDITION_VARIABLE>(malloc(sizeof(CONDITION_VARIABLE)));
+ InitializeConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition));
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//
+// Destroys the pthread condition that the object owns.
+//----------------------------------------------------------------------
+Condition::~Condition ()
+{
+ free(m_condition);
+}
+
+//----------------------------------------------------------------------
+// Unblock all threads waiting for a condition variable
+//----------------------------------------------------------------------
+int
+Condition::Broadcast ()
+{
+ WakeAllConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition));
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Unblocks one thread waiting for the condition variable
+//----------------------------------------------------------------------
+int
+Condition::Signal ()
+{
+ WakeConditionVariable(static_cast<PCONDITION_VARIABLE>(m_condition));
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// The Wait() function atomically blocks the current thread
+// waiting on the owned condition variable, and unblocks the mutex
+// specified by "mutex". The waiting thread unblocks only after
+// another thread calls Signal(), or Broadcast() with the same
+// condition variable, or if "abstime" is valid (non-NULL) this
+// function will return when the system time reaches the time
+// specified in "abstime". If "abstime" is NULL this function will
+// wait for an infinite amount of time for the condition variable
+// to be signaled or broadcasted.
+//
+// The current thread re-acquires the lock on "mutex".
+//----------------------------------------------------------------------
+int
+Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out)
+{
+ DWORD wait = INFINITE;
+ if (abstime != NULL) {
+ int wval = (*abstime - TimeValue::Now()) / 1000000;
+ if (wval < 0) wval = 0;
+
+ wait = wval;
+ }
+
+ int err = SleepConditionVariableCS(static_cast<PCONDITION_VARIABLE>(m_condition), static_cast<PCRITICAL_SECTION>(mutex.m_mutex), wait);
+
+ if (timed_out != NULL)
+ {
+ if ((err == 0) && GetLastError() == ERROR_TIMEOUT)
+ *timed_out = true;
+ else
+ *timed_out = false;
+ }
+
+ return err == 0;
+}
+
diff --git a/source/Host/windows/ConnectionGenericFileWindows.cpp b/source/Host/windows/ConnectionGenericFileWindows.cpp
new file mode 100644
index 0000000000000..eebf3d4f633c8
--- /dev/null
+++ b/source/Host/windows/ConnectionGenericFileWindows.cpp
@@ -0,0 +1,354 @@
+//===-- ConnectionGenericFileWindows.cpp ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+// This is a simple helper class to package up the information needed to return from a Read/Write
+// operation function. Since there is a lot of code to be run before exit regardless of whether the
+// operation succeeded or failed, combined with many possible return paths, this is the cleanest
+// way to represent it.
+class ReturnInfo
+{
+ public:
+ void
+ Set(size_t bytes, ConnectionStatus status, DWORD error_code)
+ {
+ m_error.SetError(error_code, eErrorTypeWin32);
+ m_bytes = bytes;
+ m_status = status;
+ }
+
+ void
+ Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg)
+ {
+ m_error.SetErrorString(error_msg.data());
+ m_bytes = bytes;
+ m_status = status;
+ }
+
+ size_t
+ GetBytes() const
+ {
+ return m_bytes;
+ }
+ ConnectionStatus
+ GetStatus() const
+ {
+ return m_status;
+ }
+ const Error &
+ GetError() const
+ {
+ return m_error;
+ }
+
+ private:
+ Error m_error;
+ size_t m_bytes;
+ ConnectionStatus m_status;
+};
+}
+
+ConnectionGenericFile::ConnectionGenericFile()
+ : m_file(INVALID_HANDLE_VALUE)
+ , m_owns_file(false)
+{
+ ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
+ ::ZeroMemory(&m_file_position, sizeof(m_file_position));
+ InitializeEventHandles();
+}
+
+ConnectionGenericFile::ConnectionGenericFile(lldb::file_t file, bool owns_file)
+ : m_file(file)
+ , m_owns_file(owns_file)
+{
+ ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
+ ::ZeroMemory(&m_file_position, sizeof(m_file_position));
+ InitializeEventHandles();
+}
+
+ConnectionGenericFile::~ConnectionGenericFile()
+{
+ if (m_owns_file && IsConnected())
+ ::CloseHandle(m_file);
+
+ ::CloseHandle(m_event_handles[kBytesAvailableEvent]);
+ ::CloseHandle(m_event_handles[kInterruptEvent]);
+}
+
+void
+ConnectionGenericFile::InitializeEventHandles()
+{
+ m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ // Note, we should use a manual reset event for the hEvent argument of the OVERLAPPED. This
+ // is because both WaitForMultipleObjects and GetOverlappedResult (if you set the bWait
+ // argument to TRUE) will wait for the event to be signalled. If we use an auto-reset event,
+ // WaitForMultipleObjects will reset the event, return successfully, and then
+ // GetOverlappedResult will block since the event is no longer signalled.
+ m_event_handles[kBytesAvailableEvent] = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+bool
+ConnectionGenericFile::IsConnected() const
+{
+ return m_file && (m_file != INVALID_HANDLE_VALUE);
+}
+
+lldb::ConnectionStatus
+ConnectionGenericFile::Connect(const char *s, Error *error_ptr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionGenericFile::Connect (url = '%s')", static_cast<void *>(this), s);
+
+ if (strstr(s, "file://") != s)
+ {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s);
+ return eConnectionStatusError;
+ }
+
+ if (IsConnected())
+ {
+ ConnectionStatus status = Disconnect(error_ptr);
+ if (status != eConnectionStatusSuccess)
+ return status;
+ }
+
+ // file://PATH
+ const char *path = s + strlen("file://");
+ // Open the file for overlapped access. If it does not exist, create it. We open it overlapped
+ // so that we can issue asynchronous reads and then use WaitForMultipleObjects to allow the read
+ // to be interrupted by an event object.
+ m_file = ::CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
+ if (m_file == INVALID_HANDLE_VALUE)
+ {
+ if (error_ptr)
+ error_ptr->SetError(::GetLastError(), eErrorTypeWin32);
+ return eConnectionStatusError;
+ }
+
+ m_owns_file = true;
+ m_uri.assign(s);
+ return eConnectionStatusSuccess;
+}
+
+lldb::ConnectionStatus
+ConnectionGenericFile::Disconnect(Error *error_ptr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionGenericFile::Disconnect ()", static_cast<void *>(this));
+
+ if (!IsConnected())
+ return eConnectionStatusSuccess;
+
+ // Reset the handle so that after we unblock any pending reads, subsequent calls to Read() will
+ // see a disconnected state.
+ HANDLE old_file = m_file;
+ m_file = INVALID_HANDLE_VALUE;
+
+ // Set the disconnect event so that any blocking reads unblock, then cancel any pending IO operations.
+ ::CancelIoEx(old_file, &m_overlapped);
+
+ // Close the file handle if we owned it, but don't close the event handles. We could always
+ // reconnect with the same Connection instance.
+ if (m_owns_file)
+ ::CloseHandle(old_file);
+
+ ::ZeroMemory(&m_file_position, sizeof(m_file_position));
+ m_owns_file = false;
+ m_uri.clear();
+ return eConnectionStatusSuccess;
+}
+
+size_t
+ConnectionGenericFile::Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr)
+{
+ ReturnInfo return_info;
+ BOOL result = 0;
+ DWORD bytes_read = 0;
+
+ if (error_ptr)
+ error_ptr->Clear();
+
+ if (!IsConnected())
+ {
+ return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
+ goto finish;
+ }
+
+ m_overlapped.hEvent = m_event_handles[kBytesAvailableEvent];
+
+ result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped);
+ if (result || ::GetLastError() == ERROR_IO_PENDING)
+ {
+ if (!result)
+ {
+ // The expected return path. The operation is pending. Wait for the operation to complete
+ // or be interrupted.
+ TimeValue time_value;
+ time_value.OffsetWithMicroSeconds(timeout_usec);
+ DWORD milliseconds = time_value.milliseconds();
+ DWORD wait_result = ::WaitForMultipleObjects(llvm::array_lengthof(m_event_handles), m_event_handles, FALSE, milliseconds);
+ // All of the events are manual reset events, so make sure we reset them to non-signalled.
+ switch (wait_result)
+ {
+ case WAIT_OBJECT_0 + kBytesAvailableEvent:
+ break;
+ case WAIT_OBJECT_0 + kInterruptEvent:
+ return_info.Set(0, eConnectionStatusInterrupted, 0);
+ goto finish;
+ case WAIT_TIMEOUT:
+ return_info.Set(0, eConnectionStatusTimedOut, 0);
+ goto finish;
+ case WAIT_FAILED:
+ return_info.Set(0, eConnectionStatusError, ::GetLastError());
+ goto finish;
+ }
+ }
+ // The data is ready. Figure out how much was read and return;
+ if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE))
+ {
+ DWORD result_error = ::GetLastError();
+ // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during a blocking read.
+ // This triggers a call to CancelIoEx, which causes the operation to complete and the
+ // result to be ERROR_OPERATION_ABORTED.
+ if (result_error == ERROR_HANDLE_EOF || result_error == ERROR_OPERATION_ABORTED || result_error == ERROR_BROKEN_PIPE)
+ return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
+ else
+ return_info.Set(bytes_read, eConnectionStatusError, result_error);
+ }
+ else if (bytes_read == 0)
+ return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
+ else
+ return_info.Set(bytes_read, eConnectionStatusSuccess, 0);
+
+ goto finish;
+ }
+ else if (::GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ // The write end of a pipe was closed. This is equivalent to EOF.
+ return_info.Set(0, eConnectionStatusEndOfFile, 0);
+ }
+ else
+ {
+ // An unknown error occurred. Fail out.
+ return_info.Set(0, eConnectionStatusError, ::GetLastError());
+ }
+ goto finish;
+
+finish:
+ status = return_info.GetStatus();
+ if (error_ptr)
+ *error_ptr = return_info.GetError();
+
+ // kBytesAvailableEvent is a manual reset event. Make sure it gets reset here so that any
+ // subsequent operations don't immediately see bytes available.
+ ResetEvent(m_event_handles[kBytesAvailableEvent]);
+
+ IncrementFilePointer(return_info.GetBytes());
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ {
+ log->Printf("%" PRIxPTR " ConnectionGenericFile::Read() handle = %" PRIxPTR ", dst = %" PRIxPTR ", dst_len = %" PRIu64
+ ") => %" PRIu64 ", error = %s",
+ this, m_file, dst, static_cast<uint64_t>(dst_len), static_cast<uint64_t>(return_info.GetBytes()),
+ return_info.GetError().AsCString());
+ }
+
+ return return_info.GetBytes();
+}
+
+size_t
+ConnectionGenericFile::Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr)
+{
+ ReturnInfo return_info;
+ DWORD bytes_written = 0;
+ BOOL result = 0;
+
+ if (error_ptr)
+ error_ptr->Clear();
+
+ if (!IsConnected())
+ {
+ return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
+ goto finish;
+ }
+
+ m_overlapped.hEvent = NULL;
+
+ // Writes are not interruptible like reads are, so just block until it's done.
+ result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped);
+ if (!result && ::GetLastError() != ERROR_IO_PENDING)
+ {
+ return_info.Set(0, eConnectionStatusError, ::GetLastError());
+ goto finish;
+ }
+
+ if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE))
+ {
+ return_info.Set(bytes_written, eConnectionStatusError, ::GetLastError());
+ goto finish;
+ }
+
+ return_info.Set(bytes_written, eConnectionStatusSuccess, 0);
+ goto finish;
+
+finish:
+ status = return_info.GetStatus();
+ if (error_ptr)
+ *error_ptr = return_info.GetError();
+
+ IncrementFilePointer(return_info.GetBytes());
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ {
+ log->Printf("%" PRIxPTR " ConnectionGenericFile::Write() handle = %" PRIxPTR ", src = %" PRIxPTR ", src_len = %" PRIu64
+ ") => %" PRIu64 ", error = %s",
+ this, m_file, src, static_cast<uint64_t>(src_len), static_cast<uint64_t>(return_info.GetBytes()),
+ return_info.GetError().AsCString());
+ }
+ return return_info.GetBytes();
+}
+
+std::string
+ConnectionGenericFile::GetURI()
+{
+ return m_uri;
+}
+
+bool
+ConnectionGenericFile::InterruptRead()
+{
+ return ::SetEvent(m_event_handles[kInterruptEvent]);
+}
+
+void
+ConnectionGenericFile::IncrementFilePointer(DWORD amount)
+{
+ LARGE_INTEGER old_pos;
+ old_pos.HighPart = m_overlapped.OffsetHigh;
+ old_pos.LowPart = m_overlapped.Offset;
+ old_pos.QuadPart += amount;
+ m_overlapped.Offset = old_pos.LowPart;
+ m_overlapped.OffsetHigh = old_pos.HighPart;
+}
diff --git a/source/Host/windows/EditLineWin.cpp b/source/Host/windows/EditLineWin.cpp
new file mode 100644
index 0000000000000..55fe52dc8ccd1
--- /dev/null
+++ b/source/Host/windows/EditLineWin.cpp
@@ -0,0 +1,435 @@
+//===-- EditLineWin.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// this file is only relevant for Visual C++
+#if defined( _WIN32 )
+
+#include "lldb/Host/windows/windows.h"
+
+#include "lldb/Host/windows/editlinewin.h"
+#include <vector>
+#include <assert.h>
+
+// edit line EL_ADDFN function pointer type
+typedef unsigned char(*el_addfn_func)(EditLine *e, int ch);
+typedef const char* (*el_prompt_func)(EditLine *);
+
+// edit line wrapper binding container
+struct el_binding
+{
+ //
+ const char *name;
+ const char *help;
+ // function pointer to callback routine
+ el_addfn_func func;
+ // ascii key this function is bound to
+ const char *key;
+};
+
+// stored key bindings
+static std::vector<el_binding*> _bindings;
+
+//TODO: this should in fact be related to the exact edit line context we create
+static void *clientData = NULL;
+
+// store the current prompt string
+// default to what we expect to receive anyway
+static const char *_prompt = "(lldb) ";
+
+#if !defined( _WIP_INPUT_METHOD )
+
+static char *
+el_get_s (char *buffer, int chars)
+{
+ return gets_s(buffer, chars);
+}
+#else
+
+static void
+con_output (char _in)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get the cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // output this char
+ WriteConsoleOutputCharacterA( hout, &_in, 1, info.dwCursorPosition, &written );
+ // advance cursor position
+ info.dwCursorPosition.X++;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+}
+
+static void
+con_backspace (void)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // nudge cursor backwards
+ info.dwCursorPosition.X--;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+ // blank out the last character
+ WriteConsoleOutputCharacterA( hout, " ", 1, info.dwCursorPosition, &written );
+}
+
+static void
+con_return (void)
+{
+ HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ DWORD written = 0;
+ // get cursor position
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo( hout, &info );
+ // move onto the new line
+ info.dwCursorPosition.X = 0;
+ info.dwCursorPosition.Y++;
+ SetConsoleCursorPosition( hout, info.dwCursorPosition );
+}
+
+static bool
+runBind (char _key)
+{
+ for ( int i=0; i<_bindings.size(); i++ )
+ {
+ el_binding *bind = _bindings[i];
+ if ( bind->key[0] == _key )
+ {
+ bind->func( (EditLine*) -1, _key );
+ return true;
+ }
+ }
+ return false;
+}
+
+// replacement get_s which is EL_BIND aware
+static char *
+el_get_s (char *buffer, int chars)
+{
+ //
+ char *head = buffer;
+ //
+ for ( ;; Sleep( 10 ) )
+ {
+ //
+ INPUT_RECORD _record;
+ //
+ DWORD _read = 0;
+ if ( ReadConsoleInputA( GetStdHandle( STD_INPUT_HANDLE ), &_record, 1, &_read ) == FALSE )
+ break;
+ // if we didn't read a key
+ if ( _read == 0 )
+ continue;
+ // only interested in key events
+ if ( _record.EventType != KEY_EVENT )
+ continue;
+ // is the key down
+ if (! _record.Event.KeyEvent.bKeyDown )
+ continue;
+ // read the ascii key character
+ char _key = _record.Event.KeyEvent.uChar.AsciiChar;
+ // non ascii conformant key press
+ if ( _key == 0 )
+ {
+ // check the scan code
+ // if VK_UP scroll back through history
+ // if VK_DOWN scroll forward through history
+ continue;
+ }
+ // try to execute any bind this key may have
+ if ( runBind( _key ) )
+ continue;
+ // if we read a return key
+ if ( _key == '\n' || _key == '\r' )
+ {
+ con_return( );
+ break;
+ }
+ // key is backspace
+ if ( _key == 0x8 )
+ {
+ // avoid deleting past beginning
+ if ( head > buffer )
+ {
+ con_backspace( );
+ head--;
+ }
+ continue;
+ }
+
+ // add this key to the input buffer
+ if ( (head-buffer) < (chars-1) )
+ {
+ con_output( _key );
+ *(head++) = _key;
+ }
+ }
+ // insert end of line character
+ *head = '\0';
+
+ return buffer;
+}
+#endif
+
+// edit line initialize
+EditLine *
+el_init (const char *, FILE *, FILE *, FILE *)
+{
+ //
+ SetConsoleTitleA( "lldb" );
+ // return dummy handle
+ return (EditLine*) -1;
+}
+
+const char *
+el_gets (EditLine *el, int *length)
+{
+ // print the prompt if we have one
+ if ( _prompt != NULL )
+ printf("%s", _prompt);
+ // create a buffer for the user input
+ char *buffer = new char[ MAX_PATH ];
+ // try to get user input string
+ if ( el_get_s( buffer, MAX_PATH ) )
+ {
+ // get the string length in 'length'
+ while ( buffer[ *length ] != '\0' )
+ (*length)++;
+ // return the input buffer
+ // remember that this memory has the be free'd somewhere
+ return buffer;
+ }
+ else
+ {
+ // on error
+ delete [] buffer;
+ return NULL;
+ }
+}
+
+int
+el_set (EditLine *el, int code, ...)
+{
+ va_list vl;
+ va_start(vl, code);
+ //
+ switch ( code )
+ {
+ // edit line set prompt message
+ case ( EL_PROMPT ):
+ {
+ // EL_PROMPT, char *(*f)( EditLine *)
+ // define a prompt printing function as 'f', which is to return a string that
+ // contains the prompt.
+
+ // get the function pointer from the arg list
+ void *func_vp = (void*)va_arg(vl, el_prompt_func);
+ // cast to suitable prototype
+ el_prompt_func func_fp = (el_prompt_func)func_vp;
+ // call to get the prompt as a string
+ _prompt = func_fp( el );
+ }
+ break;
+
+ case (EL_PROMPT_ESC) :
+ {
+ // EL_PROMPT, char *(*f)( EditLine *)
+ // define a prompt printing function as 'f', which is to return a string that
+ // contains the prompt.
+
+ // get the function pointer from the arg list
+ void *func_vp = (void*)va_arg(vl, el_prompt_func);
+ va_arg(vl, int);
+ // call to get the prompt as a string
+ el_prompt_func func_fp = (el_prompt_func)func_vp;
+ _prompt = func_fp(el);
+ }
+ break;
+
+ case ( EL_EDITOR ):
+ {
+ // EL_EDITOR, const char *mode
+ // set editing mode to "emacs" or "vi"
+ }
+ break;
+ case ( EL_HIST ):
+ {
+ // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr
+ // defines which history function to use, which is usually history(). Ptr should be the
+ // value returned by history_init().
+ }
+ break;
+ case ( EL_ADDFN ):
+ {
+ // EL_ADDFN, const char *name, const char *help, unsigned char (*func)(EditLine *e, int ch)
+ // add a user defined function, func), referred to as 'name' which is invoked when a key which is bound to 'name' is
+ // entered. 'help' is a description of 'name'. at invocation time, 'ch' is the key which caused the invocation. the
+ // return value of 'func()' should be one of:
+ // CC_NORM add a normal character
+ // CC_NEWLINE end of line was entered
+ // CC_EOF EOF was entered
+ // CC_ARGHACK expecting further command input as arguments, do nothing visually.
+ // CC_REFRESH refresh display.
+ // CC_REFRESH_BEEP refresh display and beep.
+ // CC_CURSOR cursor moved so update and perform CC_REFRESH
+ // CC_REDISPLAY redisplay entire input line. this is useful if a key binding outputs extra information.
+ // CC_ERROR an error occurred. beep and flush tty.
+ // CC_FATAL fatal error, reset tty to known state.
+
+ el_binding *binding = new el_binding;
+ binding->name = va_arg( vl, const char *);
+ binding->help = va_arg( vl, const char *);
+ binding->func = va_arg( vl, el_addfn_func );
+ binding->key = 0;
+ // add this to the bindings list
+ _bindings.push_back( binding );
+ }
+ break;
+ case ( EL_BIND ):
+ {
+ // EL_BIND, const char *, ..., NULL
+ // perform the BIND built-in command. Refer to editrc(5) for more information.
+
+ const char *name = va_arg( vl, const char* );
+
+ for ( int i=0; i<_bindings.size(); i++ )
+ {
+ el_binding *bind = _bindings[i];
+ if ( strcmp( bind->name, name ) == 0 )
+ {
+ bind->key = va_arg( vl, const char * );
+ break;
+ }
+ }
+
+ }
+ break;
+ case ( EL_CLIENTDATA ):
+ {
+ clientData = va_arg(vl, void*);
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+el_end (EditLine *el)
+{
+ //assert( !"Not implemented!" );
+}
+
+void
+el_reset (EditLine *)
+{
+ assert( !"Not implemented!" );
+}
+
+int
+el_getc (EditLine *, char *)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+void
+el_push (EditLine *, const char *)
+{
+}
+
+void
+el_beep (EditLine *)
+{
+ Beep( 1000, 500 );
+}
+
+int
+el_parse (EditLine *, int, const char **)
+{
+ assert( !"Not implemented!" );
+ return 0;
+}
+
+int
+el_get (EditLine *el, int code, ...)
+{
+ va_list vl;
+ va_start( vl, code );
+
+ switch ( code )
+ {
+ case ( EL_CLIENTDATA ):
+ {
+ void **dout = va_arg( vl, void** );
+ *dout = clientData;
+ }
+ break;
+ default:
+ assert( !"Not implemented!" );
+ }
+ return 0;
+}
+
+int
+el_source (EditLine *el, const char *file)
+{
+ // init edit line by reading the contents of 'file'
+ // nothing to do here on windows...
+ return 0;
+}
+
+void
+el_resize (EditLine *)
+{
+ assert( !"Not implemented!" );
+}
+
+const LineInfo *
+el_line (EditLine *el)
+{
+ return 0;
+}
+
+int
+el_insertstr (EditLine *, const char *)
+{
+// assert( !"Not implemented!" );
+ return 0;
+}
+
+void
+el_deletestr (EditLine *, int)
+{
+ assert( !"Not implemented!" );
+}
+
+History *
+history_init (void)
+{
+ // return dummy handle
+ return (History*) -1;
+}
+
+void
+history_end (History *)
+{
+// assert( !"Not implemented!" );
+}
+
+int
+history (History *, HistEvent *, int op, ...)
+{
+ // perform operation 'op' on the history list with
+ // optional arguments as needed by the operation.
+ return 0;
+}
+
+#endif
diff --git a/source/Host/windows/FileSystem.cpp b/source/Host/windows/FileSystem.cpp
new file mode 100644
index 0000000000000..2cca12b92711b
--- /dev/null
+++ b/source/Host/windows/FileSystem.cpp
@@ -0,0 +1,221 @@
+//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/windows/windows.h"
+
+#include <shellapi.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/windows/AutoHandle.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb_private;
+
+const char *
+FileSystem::DEV_NULL = "nul";
+
+FileSpec::PathSyntax
+FileSystem::GetNativePathSyntax()
+{
+ return FileSpec::ePathSyntaxWindows;
+}
+
+Error
+FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
+{
+ // On Win32, the mode parameter is ignored, as Windows files and directories support a
+ // different permission model than POSIX.
+ Error error;
+ const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
+ if (err_code)
+ {
+ error.SetErrorString(err_code.message().c_str());
+ }
+
+ return error;
+}
+
+Error
+FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
+{
+ Error error;
+ if (!recurse)
+ {
+ BOOL result = ::RemoveDirectory(file_spec.GetCString());
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ }
+ else
+ {
+ // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
+ // indicate the end of the list.
+ std::string path_buffer{file_spec.GetPath()};
+ path_buffer.push_back(0);
+
+ SHFILEOPSTRUCT shfos = {0};
+ shfos.wFunc = FO_DELETE;
+ shfos.pFrom = path_buffer.c_str();
+ shfos.fFlags = FOF_NO_UI;
+
+ int result = ::SHFileOperation(&shfos);
+ // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
+ if (result != 0)
+ error.SetErrorStringWithFormat("SHFileOperation failed");
+ }
+ return error;
+}
+
+Error
+FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
+{
+ Error error;
+ // Beware that Windows's permission model is different from Unix's, and it's
+ // not clear if this API is supposed to check ACLs. To match the caller's
+ // expectations as closely as possible, we'll use Microsoft's _stat, which
+ // attempts to emulate POSIX stat. This should be good enough for basic
+ // checks like FileSpec::Readable.
+ struct _stat file_stats;
+ if (::_stat(file_spec.GetCString(), &file_stats) == 0)
+ {
+ // The owner permission bits in "st_mode" currently match the definitions
+ // for the owner file mode bits.
+ file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
+ }
+ else
+ {
+ error.SetErrorToErrno();
+ }
+
+ return error;
+}
+
+Error
+FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
+{
+ Error error;
+ error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
+ return error;
+}
+
+lldb::user_id_t
+FileSystem::GetFileSize(const FileSpec &file_spec)
+{
+ return file_spec.GetByteSize();
+}
+
+bool
+FileSystem::GetFileExists(const FileSpec &file_spec)
+{
+ return file_spec.Exists();
+}
+
+Error
+FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
+{
+ Error error;
+ if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+int
+FileSystem::GetHardlinkCount(const FileSpec &file_spec)
+{
+ HANDLE file_handle = ::CreateFile(file_spec.GetCString(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+
+ if (file_handle == INVALID_HANDLE_VALUE)
+ return -1;
+
+ AutoHandle auto_file_handle(file_handle);
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (::GetFileInformationByHandle(file_handle, &file_info))
+ return file_info.nNumberOfLinks;
+
+ return -1;
+}
+
+Error
+FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
+{
+ Error error;
+ DWORD attrib = ::GetFileAttributes(dst.GetCString());
+ if (attrib == INVALID_FILE_ATTRIBUTES)
+ {
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+ }
+ bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
+ DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
+ BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+Error
+FileSystem::Unlink(const FileSpec &file_spec)
+{
+ Error error;
+ BOOL result = ::DeleteFile(file_spec.GetCString());
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+Error
+FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
+{
+ Error error;
+ HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+ }
+
+ char buf[PATH_MAX];
+ // Subtract 1 from the path length since this function does not add a null terminator.
+ DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
+ FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+ if (result == 0)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ else
+ dst.SetFile(buf, false);
+
+ ::CloseHandle(h);
+ return error;
+}
+
+Error
+FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst)
+{
+ return Error("ResolveSymbolicLink() isn't implemented on Windows");
+}
+
+bool
+FileSystem::IsLocal(const FileSpec &spec)
+{
+ if (spec)
+ {
+ // TODO: return true if the file is on a locally mounted file system
+ return true;
+ }
+
+ return false;
+}
diff --git a/source/Host/windows/Host.cpp b/source/Host/windows/Host.cpp
new file mode 100644
index 0000000000000..2c9a139df256c
--- /dev/null
+++ b/source/Host/windows/Host.cpp
@@ -0,0 +1,326 @@
+//===-- source/Host/windows/Host.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <stdio.h>
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/AutoHandle.h"
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Target/Process.h"
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredData.h"
+
+// Windows includes
+#include <TlHelp32.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+ bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple)
+ {
+ // Open the PE File as a binary file, and parse just enough information to determine the
+ // machine type.
+ File imageBinary(
+ executable.GetPath().c_str(),
+ File::eOpenOptionRead,
+ lldb::eFilePermissionsUserRead);
+ imageBinary.SeekFromStart(0x3c);
+ int32_t peOffset = 0;
+ uint32_t peHead = 0;
+ uint16_t machineType = 0;
+ size_t readSize = sizeof(peOffset);
+ imageBinary.Read(&peOffset, readSize);
+ imageBinary.SeekFromStart(peOffset);
+ imageBinary.Read(&peHead, readSize);
+ if (peHead != 0x00004550) // "PE\0\0", little-endian
+ return false; // Error: Can't find PE header
+ readSize = 2;
+ imageBinary.Read(&machineType, readSize);
+ triple.setVendor(llvm::Triple::PC);
+ triple.setOS(llvm::Triple::Win32);
+ triple.setArch(llvm::Triple::UnknownArch);
+ if (machineType == 0x8664)
+ triple.setArch(llvm::Triple::x86_64);
+ else if (machineType == 0x14c)
+ triple.setArch(llvm::Triple::x86);
+
+ return true;
+ }
+
+ bool GetExecutableForProcess(const AutoHandle &handle, std::string &path)
+ {
+ // Get the process image path. MAX_PATH isn't long enough, paths can actually be up to 32KB.
+ std::vector<char> buffer(32768);
+ DWORD dwSize = buffer.size();
+ if (!::QueryFullProcessImageNameA(handle.get(), 0, &buffer[0], &dwSize))
+ return false;
+ path.assign(&buffer[0]);
+ return true;
+ }
+
+ void GetProcessExecutableAndTriple(const AutoHandle &handle, ProcessInstanceInfo &process)
+ {
+ // We may not have permissions to read the path from the process. So start off by
+ // setting the executable file to whatever Toolhelp32 gives us, and then try to
+ // enhance this with more detailed information, but fail gracefully.
+ std::string executable;
+ llvm::Triple triple;
+ triple.setVendor(llvm::Triple::PC);
+ triple.setOS(llvm::Triple::Win32);
+ triple.setArch(llvm::Triple::UnknownArch);
+ if (GetExecutableForProcess(handle, executable))
+ {
+ FileSpec executableFile(executable.c_str(), false);
+ process.SetExecutableFile(executableFile, true);
+ GetTripleForProcess(executableFile, triple);
+ }
+ process.SetArchitecture(ArchSpec(triple));
+
+ // TODO(zturner): Add the ability to get the process user name.
+ }
+}
+
+lldb::DataBufferSP
+Host::GetAuxvData(lldb_private::Process *process)
+{
+ return 0;
+}
+
+lldb::tid_t
+Host::GetCurrentThreadID()
+{
+ return lldb::tid_t(::GetCurrentThreadId());
+}
+
+lldb::thread_t
+Host::GetCurrentThread ()
+{
+ return lldb::thread_t(::GetCurrentThread());
+}
+
+lldb::thread_key_t
+Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback)
+{
+ return TlsAlloc();
+}
+
+void*
+Host::ThreadLocalStorageGet(lldb::thread_key_t key)
+{
+ return ::TlsGetValue (key);
+}
+
+void
+Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value)
+{
+ ::TlsSetValue (key, value);
+}
+
+void
+Host::Kill(lldb::pid_t pid, int signo)
+{
+ TerminateProcess((HANDLE) pid, 1);
+}
+
+
+const char *
+Host::GetSignalAsCString(int signo)
+{
+ return NULL;
+}
+
+FileSpec
+Host::GetModuleFileSpecForHostAddress (const void *host_addr)
+{
+ FileSpec module_filespec;
+
+ HMODULE hmodule = NULL;
+ if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)host_addr, &hmodule))
+ return module_filespec;
+
+ std::vector<char> buffer(MAX_PATH);
+ DWORD chars_copied = 0;
+ do {
+ chars_copied = ::GetModuleFileName(hmodule, &buffer[0], buffer.size());
+ if (chars_copied == buffer.size() && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ buffer.resize(buffer.size() * 2);
+ } while (chars_copied >= buffer.size());
+
+ module_filespec.SetFile(&buffer[0], false);
+ return module_filespec;
+}
+
+uint32_t
+Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos)
+{
+ process_infos.Clear();
+
+ AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+ if (!snapshot.IsValid())
+ return 0;
+
+ PROCESSENTRY32 pe = {0};
+ pe.dwSize = sizeof(PROCESSENTRY32);
+ if (Process32First(snapshot.get(), &pe))
+ {
+ do
+ {
+ AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID), nullptr);
+
+ ProcessInstanceInfo process;
+ process.SetExecutableFile(FileSpec(pe.szExeFile, false), true);
+ process.SetProcessID(pe.th32ProcessID);
+ process.SetParentProcessID(pe.th32ParentProcessID);
+ GetProcessExecutableAndTriple(handle, process);
+
+ if (match_info.MatchAllProcesses() || match_info.Matches(process))
+ process_infos.Append(process);
+ } while (Process32Next(snapshot.get(), &pe));
+ }
+ return process_infos.GetSize();
+}
+
+bool
+Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
+{
+ process_info.Clear();
+
+ AutoHandle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid),
+ nullptr);
+ if (!handle.IsValid())
+ return false;
+
+ process_info.SetProcessID(pid);
+ GetProcessExecutableAndTriple(handle, process_info);
+
+ // Need to read the PEB to get parent process and command line arguments.
+ return true;
+}
+
+HostThread
+Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals)
+{
+ return HostThread();
+}
+
+Error
+Host::ShellExpandArguments (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments))
+ {
+ FileSpec expand_tool_spec;
+ if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec))
+ {
+ error.SetErrorString("could not find support executable directory for the lldb-argdumper tool");
+ return error;
+ }
+ expand_tool_spec.AppendPathComponent("lldb-argdumper.exe");
+ if (!expand_tool_spec.Exists())
+ {
+ error.SetErrorString("could not find the lldb-argdumper tool");
+ return error;
+ }
+
+ std::string quoted_cmd_string;
+ launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string);
+ std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/');
+ StreamString expand_command;
+
+ expand_command.Printf("\"%s\" %s",
+ expand_tool_spec.GetPath().c_str(),
+ quoted_cmd_string.c_str());
+
+ int status;
+ std::string output;
+ RunShellCommand(expand_command.GetData(), launch_info.GetWorkingDirectory(), &status, nullptr, &output, 10);
+
+ if (status != 0)
+ {
+ error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status);
+ return error;
+ }
+
+ auto data_sp = StructuredData::ParseJSON(output);
+ if (!data_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ auto dict_sp = data_sp->GetAsDictionary();
+ if (!data_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
+ if (!args_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ auto args_array_sp = args_sp->GetAsArray();
+ if (!args_array_sp)
+ {
+ error.SetErrorString("invalid JSON");
+ return error;
+ }
+
+ launch_info.GetArguments().Clear();
+
+ for (size_t i = 0;
+ i < args_array_sp->GetSize();
+ i++)
+ {
+ auto item_sp = args_array_sp->GetItemAtIndex(i);
+ if (!item_sp)
+ continue;
+ auto str_sp = item_sp->GetAsString();
+ if (!str_sp)
+ continue;
+
+ launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str());
+ }
+ }
+
+ return error;
+}
+
+size_t
+Host::GetEnvironment(StringList &env)
+{
+ // The environment block on Windows is a contiguous buffer of NULL terminated strings,
+ // where the end of the environment block is indicated by two consecutive NULLs.
+ LPCH environment_block = ::GetEnvironmentStrings();
+ env.Clear();
+ while (*environment_block != '\0')
+ {
+ llvm::StringRef current_var(environment_block);
+ if (current_var[0] != '=')
+ env.AppendString(current_var);
+
+ environment_block += current_var.size()+1;
+ }
+ return env.GetSize();
+}
diff --git a/source/Host/windows/HostInfoWindows.cpp b/source/Host/windows/HostInfoWindows.cpp
new file mode 100644
index 0000000000000..6dce71d9172ad
--- /dev/null
+++ b/source/Host/windows/HostInfoWindows.cpp
@@ -0,0 +1,118 @@
+//===-- HostInfoWindows.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/windows/windows.h"
+
+#include <mutex> // std::once
+
+#include "lldb/Host/windows/HostInfoWindows.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb_private;
+
+FileSpec HostInfoWindows::m_program_filespec;
+
+size_t
+HostInfoWindows::GetPageSize()
+{
+ SYSTEM_INFO systemInfo;
+ GetNativeSystemInfo(&systemInfo);
+ return systemInfo.dwPageSize;
+}
+
+bool
+HostInfoWindows::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update)
+{
+ OSVERSIONINFOEX info;
+
+ ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+#pragma warning(push)
+#pragma warning(disable : 4996)
+ // Starting with Microsoft SDK for Windows 8.1, this function is deprecated in favor of the
+ // new Windows Version Helper APIs. Since we don't specify a minimum SDK version, it's easier
+ // to simply disable the warning rather than try to support both APIs.
+ if (GetVersionEx((LPOSVERSIONINFO)&info) == 0)
+ {
+ return false;
+ }
+#pragma warning(pop)
+
+ major = info.dwMajorVersion;
+ minor = info.dwMinorVersion;
+ update = info.wServicePackMajor;
+
+ return true;
+}
+
+bool
+HostInfoWindows::GetOSBuildString(std::string &s)
+{
+ s.clear();
+ uint32_t major, minor, update;
+ if (!GetOSVersion(major, minor, update))
+ return false;
+
+ llvm::raw_string_ostream stream(s);
+ stream << "Windows NT " << major << "." << minor << "." << update;
+ return true;
+}
+
+bool
+HostInfoWindows::GetOSKernelDescription(std::string &s)
+{
+ return GetOSBuildString(s);
+}
+
+bool
+HostInfoWindows::GetHostname(std::string &s)
+{
+ char buffer[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+ if (!::GetComputerName(buffer, &dwSize))
+ return false;
+
+ s.assign(buffer, buffer + dwSize);
+ return true;
+}
+
+FileSpec
+HostInfoWindows::GetProgramFileSpec()
+{
+ static std::once_flag g_once_flag;
+ std::call_once(g_once_flag, []() {
+ char buffer[PATH_MAX];
+ ::GetModuleFileName(NULL, buffer, sizeof(buffer));
+ m_program_filespec.SetFile(buffer, false);
+ });
+ return m_program_filespec;
+}
+
+FileSpec
+HostInfoWindows::GetDefaultShell()
+{
+ return FileSpec(::getenv("ComSpec"), false);
+}
+
+bool
+HostInfoWindows::ComputePythonDirectory(FileSpec &file_spec)
+{
+ FileSpec lldb_file_spec;
+ if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec))
+ return false;
+ llvm::SmallString<64> path(lldb_file_spec.GetDirectory().AsCString());
+ llvm::sys::path::remove_filename(path);
+ llvm::sys::path::append(path, "lib", "site-packages");
+ std::replace(path.begin(), path.end(), '\\', '/');
+ file_spec.GetDirectory().SetString(path.c_str());
+ return true;
+}
diff --git a/source/Host/windows/HostProcessWindows.cpp b/source/Host/windows/HostProcessWindows.cpp
new file mode 100644
index 0000000000000..0f81c18d34afb
--- /dev/null
+++ b/source/Host/windows/HostProcessWindows.cpp
@@ -0,0 +1,137 @@
+//===-- HostProcessWindows.cpp ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/HostProcessWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <Psapi.h>
+
+using namespace lldb_private;
+
+namespace
+{
+struct MonitorInfo
+{
+ HostProcess::MonitorCallback callback;
+ void *baton;
+ HANDLE process_handle;
+};
+}
+
+HostProcessWindows::HostProcessWindows()
+ : HostNativeProcessBase()
+ , m_owns_handle(true)
+{
+}
+
+HostProcessWindows::HostProcessWindows(lldb::process_t process)
+ : HostNativeProcessBase(process)
+ , m_owns_handle(true)
+{
+}
+
+HostProcessWindows::~HostProcessWindows()
+{
+ Close();
+}
+
+void
+HostProcessWindows::SetOwnsHandle(bool owns)
+{
+ m_owns_handle = owns;
+}
+
+Error HostProcessWindows::Terminate()
+{
+ Error error;
+ if (m_process == nullptr)
+ error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32);
+
+ if (!::TerminateProcess(m_process, 0))
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+
+ return error;
+}
+
+Error HostProcessWindows::GetMainModule(FileSpec &file_spec) const
+{
+ Error error;
+ if (m_process == nullptr)
+ error.SetError(ERROR_INVALID_HANDLE, lldb::eErrorTypeWin32);
+
+ char path[MAX_PATH] = { 0 };
+ if (::GetProcessImageFileName(m_process, path, llvm::array_lengthof(path)))
+ file_spec.SetFile(path, false);
+ else
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+
+ return error;
+}
+
+lldb::pid_t HostProcessWindows::GetProcessId() const
+{
+ return (m_process == LLDB_INVALID_PROCESS) ? -1 : ::GetProcessId(m_process);
+}
+
+bool HostProcessWindows::IsRunning() const
+{
+ if (m_process == nullptr)
+ return false;
+
+ DWORD code = 0;
+ if (!::GetExitCodeProcess(m_process, &code))
+ return false;
+
+ return (code == STILL_ACTIVE);
+}
+
+HostThread
+HostProcessWindows::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals)
+{
+ HostThread monitor_thread;
+ MonitorInfo *info = new MonitorInfo;
+ info->callback = callback;
+ info->baton = callback_baton;
+
+ // Since the life of this HostProcessWindows instance and the life of the process may be different, duplicate the handle so that
+ // the monitor thread can have ownership over its own copy of the handle.
+ HostThread result;
+ if (::DuplicateHandle(GetCurrentProcess(), m_process, GetCurrentProcess(), &info->process_handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ result = ThreadLauncher::LaunchThread("ChildProcessMonitor", HostProcessWindows::MonitorThread, info, nullptr);
+ return result;
+}
+
+lldb::thread_result_t
+HostProcessWindows::MonitorThread(void *thread_arg)
+{
+ DWORD exit_code;
+
+ MonitorInfo *info = static_cast<MonitorInfo *>(thread_arg);
+ if (info)
+ {
+ ::WaitForSingleObject(info->process_handle, INFINITE);
+ ::GetExitCodeProcess(info->process_handle, &exit_code);
+ info->callback(info->baton, ::GetProcessId(info->process_handle), true, 0, exit_code);
+ ::CloseHandle(info->process_handle);
+ delete (info);
+ }
+ return 0;
+}
+
+void HostProcessWindows::Close()
+{
+ if (m_owns_handle && m_process != LLDB_INVALID_PROCESS)
+ ::CloseHandle(m_process);
+ m_process = nullptr;
+}
diff --git a/source/Host/windows/HostThreadWindows.cpp b/source/Host/windows/HostThreadWindows.cpp
new file mode 100644
index 0000000000000..d59064fb1c156
--- /dev/null
+++ b/source/Host/windows/HostThreadWindows.cpp
@@ -0,0 +1,98 @@
+//===-- HostThreadWindows.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Error.h"
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/HostThreadWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+void __stdcall ExitThreadProxy(ULONG_PTR dwExitCode)
+{
+ ::ExitThread(dwExitCode);
+}
+}
+
+HostThreadWindows::HostThreadWindows()
+ : HostNativeThreadBase()
+ , m_owns_handle(true)
+{
+}
+
+HostThreadWindows::HostThreadWindows(lldb::thread_t thread)
+ : HostNativeThreadBase(thread)
+ , m_owns_handle(true)
+{
+}
+
+HostThreadWindows::~HostThreadWindows()
+{
+ Reset();
+}
+
+void
+HostThreadWindows::SetOwnsHandle(bool owns)
+{
+ m_owns_handle = owns;
+}
+
+Error
+HostThreadWindows::Join(lldb::thread_result_t *result)
+{
+ Error error;
+ if (IsJoinable())
+ {
+ DWORD wait_result = ::WaitForSingleObject(m_thread, INFINITE);
+ if (WAIT_OBJECT_0 == wait_result && result)
+ {
+ DWORD exit_code = 0;
+ if (!::GetExitCodeThread(m_thread, &exit_code))
+ *result = 0;
+ *result = exit_code;
+ }
+ else if (WAIT_OBJECT_0 != wait_result)
+ error.SetError(::GetLastError(), eErrorTypeWin32);
+ }
+ else
+ error.SetError(ERROR_INVALID_HANDLE, eErrorTypeWin32);
+
+ Reset ();
+ return error;
+}
+
+Error
+HostThreadWindows::Cancel()
+{
+ Error error;
+
+ DWORD result = ::QueueUserAPC(::ExitThreadProxy, m_thread, 0);
+ error.SetError(result, eErrorTypeWin32);
+ return error;
+}
+
+lldb::tid_t
+HostThreadWindows::GetThreadId() const
+{
+ return ::GetThreadId(m_thread);
+}
+
+void
+HostThreadWindows::Reset()
+{
+ if (m_owns_handle && m_thread != LLDB_INVALID_HOST_THREAD)
+ ::CloseHandle(m_thread);
+
+ HostNativeThreadBase::Reset();
+}
diff --git a/source/Host/windows/LockFileWindows.cpp b/source/Host/windows/LockFileWindows.cpp
new file mode 100644
index 0000000000000..161d3dbd82604
--- /dev/null
+++ b/source/Host/windows/LockFileWindows.cpp
@@ -0,0 +1,93 @@
+//===-- LockFileWindows.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/windows/LockFileWindows.h"
+
+#include <io.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+
+Error fileLock (HANDLE file_handle, DWORD flags, const uint64_t start, const uint64_t len)
+{
+ if (start != 0)
+ return Error ("Non-zero start lock regions are not supported");
+
+ OVERLAPPED overlapped = {0};
+
+ if (!::LockFileEx (file_handle, flags, 0, len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING)
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ DWORD bytes;
+ if (!::GetOverlappedResult (file_handle, &overlapped, &bytes, TRUE))
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ return Error ();
+}
+
+} // namespace
+
+LockFileWindows::LockFileWindows (int fd)
+ : LockFileBase (fd),
+ m_file (reinterpret_cast<HANDLE> (_get_osfhandle (fd)))
+{
+}
+
+LockFileWindows::~LockFileWindows ()
+{
+ Unlock ();
+}
+
+bool
+LockFileWindows::IsValidFile () const
+{
+ return LockFileBase::IsValidFile() && m_file != INVALID_HANDLE_VALUE;
+}
+
+Error
+LockFileWindows::DoWriteLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK, start, len);
+}
+
+Error
+LockFileWindows::DoTryWriteLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, start, len);
+}
+
+Error
+LockFileWindows::DoReadLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, 0, start, len);
+}
+
+Error
+LockFileWindows::DoTryReadLock (const uint64_t start, const uint64_t len)
+{
+ return fileLock (m_file, LOCKFILE_FAIL_IMMEDIATELY, start, len);
+}
+
+Error
+LockFileWindows::DoUnlock ()
+{
+ OVERLAPPED overlapped = {0};
+
+ if (!::UnlockFileEx (m_file, 0, m_len, 0, &overlapped) && ::GetLastError () != ERROR_IO_PENDING)
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ DWORD bytes;
+ if (!::GetOverlappedResult (m_file, &overlapped, &bytes, TRUE))
+ return Error (::GetLastError (), eErrorTypeWin32);
+
+ return Error ();
+}
diff --git a/source/Host/windows/Mutex.cpp b/source/Host/windows/Mutex.cpp
new file mode 100644
index 0000000000000..ed90f46eb54bd
--- /dev/null
+++ b/source/Host/windows/Mutex.cpp
@@ -0,0 +1,109 @@
+//===-- Mutex.cpp -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Mutex.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/windows/windows.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#if 0
+// This logging is way too verbose to enable even for a log channel.
+// This logging can be enabled by changing the "#if 0", but should be
+// reverted prior to checking in.
+#include <cstdio>
+#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_LOG(fmt, ...)
+#endif
+
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Default constructor.
+//
+// Creates a pthread mutex with no attributes.
+//----------------------------------------------------------------------
+Mutex::Mutex () :
+ m_mutex()
+{
+ m_mutex = static_cast<PCRITICAL_SECTION>(malloc(sizeof(CRITICAL_SECTION)));
+ InitializeCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+}
+
+//----------------------------------------------------------------------
+// Default constructor.
+//
+// Creates a pthread mutex with "type" as the mutex type.
+//----------------------------------------------------------------------
+Mutex::Mutex (Mutex::Type type) :
+ m_mutex()
+{
+ m_mutex = static_cast<PCRITICAL_SECTION>(malloc(sizeof(CRITICAL_SECTION)));
+ InitializeCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+}
+
+//----------------------------------------------------------------------
+// Destructor.
+//
+// Destroys the mutex owned by this object.
+//----------------------------------------------------------------------
+Mutex::~Mutex()
+{
+ DeleteCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+ free(m_mutex);
+}
+
+//----------------------------------------------------------------------
+// Locks the mutex owned by this object, if the mutex is already
+// locked, the calling thread will block until the mutex becomes
+// available.
+//
+// RETURNS
+// The error code from the pthread_mutex_lock() function call.
+//----------------------------------------------------------------------
+int
+Mutex::Lock()
+{
+ DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), m_mutex);
+
+ EnterCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Attempts to lock the mutex owned by this object without blocking.
+// If the mutex is already locked, TryLock() will not block waiting
+// for the mutex, but will return an error condition.
+//
+// RETURNS
+// The error code from the pthread_mutex_trylock() function call.
+//----------------------------------------------------------------------
+int
+Mutex::TryLock(const char *failure_message)
+{
+ return TryEnterCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex)) == 0;
+}
+
+//----------------------------------------------------------------------
+// If the current thread holds the lock on the owned mutex, then
+// Unlock() will unlock the mutex. Calling Unlock() on this object
+// that the calling thread does not hold will result in undefined
+// behavior.
+//
+// RETURNS
+// The error code from the pthread_mutex_unlock() function call.
+//----------------------------------------------------------------------
+int
+Mutex::Unlock()
+{
+ LeaveCriticalSection(static_cast<PCRITICAL_SECTION>(m_mutex));
+ return 0;
+}
diff --git a/source/Host/windows/PipeWindows.cpp b/source/Host/windows/PipeWindows.cpp
new file mode 100644
index 0000000000000..d4afd6e749436
--- /dev/null
+++ b/source/Host/windows/PipeWindows.cpp
@@ -0,0 +1,336 @@
+//===-- PipeWindows.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/windows/PipeWindows.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <rpc.h>
+
+#include <atomic>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+std::atomic<uint32_t> g_pipe_serial(0);
+}
+
+PipeWindows::PipeWindows()
+{
+ m_read = INVALID_HANDLE_VALUE;
+ m_write = INVALID_HANDLE_VALUE;
+
+ m_read_fd = -1;
+ m_write_fd = -1;
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+}
+
+PipeWindows::~PipeWindows()
+{
+ Close();
+}
+
+Error
+PipeWindows::CreateNew(bool child_process_inherit)
+{
+ // Even for anonymous pipes, we open a named pipe. This is because you cannot get
+ // overlapped i/o on Windows without using a named pipe. So we synthesize a unique
+ // name.
+ uint32_t serial = g_pipe_serial.fetch_add(1);
+ std::string pipe_name;
+ llvm::raw_string_ostream pipe_name_stream(pipe_name);
+ pipe_name_stream << "lldb.pipe." << ::GetCurrentProcessId() << "." << serial;
+ pipe_name_stream.flush();
+
+ return CreateNew(pipe_name.c_str(), child_process_inherit);
+}
+
+Error
+PipeWindows::CreateNew(llvm::StringRef name, bool child_process_inherit)
+{
+ if (name.empty())
+ return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32);
+
+ if (CanRead() || CanWrite())
+ return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
+
+ std::string pipe_path = "\\\\.\\Pipe\\";
+ pipe_path.append(name);
+
+ // Always open for overlapped i/o. We implement blocking manually in Read and Write.
+ DWORD read_mode = FILE_FLAG_OVERLAPPED;
+ m_read =
+ ::CreateNamedPipe(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL);
+ if (INVALID_HANDLE_VALUE == m_read)
+ return Error(::GetLastError(), eErrorTypeWin32);
+ m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ // Open the write end of the pipe.
+ Error result = OpenNamedPipe(name, child_process_inherit, false);
+ if (!result.Success())
+ {
+ CloseReadFileDescriptor();
+ return result;
+ }
+
+ return result;
+}
+
+Error
+PipeWindows::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name)
+{
+ llvm::SmallString<128> pipe_name;
+ Error error;
+ ::UUID unique_id;
+ RPC_CSTR unique_string;
+ RPC_STATUS status = ::UuidCreate(&unique_id);
+ if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY)
+ status = ::UuidToStringA(&unique_id, &unique_string);
+ if (status == RPC_S_OK)
+ {
+ pipe_name = prefix;
+ pipe_name += "-";
+ pipe_name += reinterpret_cast<char *>(unique_string);
+ ::RpcStringFreeA(&unique_string);
+ error = CreateNew(pipe_name, child_process_inherit);
+ }
+ else
+ {
+ error.SetError(status, eErrorTypeWin32);
+ }
+ if (error.Success())
+ name = pipe_name;
+ return error;
+}
+
+Error
+PipeWindows::OpenAsReader(llvm::StringRef name, bool child_process_inherit)
+{
+ if (CanRead() || CanWrite())
+ return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
+
+ return OpenNamedPipe(name, child_process_inherit, true);
+}
+
+Error
+PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout)
+{
+ if (CanRead() || CanWrite())
+ return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
+
+ return OpenNamedPipe(name, child_process_inherit, false);
+}
+
+Error
+PipeWindows::OpenNamedPipe(llvm::StringRef name, bool child_process_inherit, bool is_read)
+{
+ if (name.empty())
+ return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32);
+
+ assert(is_read ? !CanRead() : !CanWrite());
+
+ SECURITY_ATTRIBUTES attributes = {0};
+ attributes.bInheritHandle = child_process_inherit;
+
+ std::string pipe_path = "\\\\.\\Pipe\\";
+ pipe_path.append(name);
+
+ if (is_read)
+ {
+ m_read = ::CreateFile(pipe_path.c_str(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (INVALID_HANDLE_VALUE == m_read)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
+
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ }
+ else
+ {
+ m_write = ::CreateFile(pipe_path.c_str(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (INVALID_HANDLE_VALUE == m_write)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY);
+
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+ }
+
+ return Error();
+}
+
+int
+PipeWindows::GetReadFileDescriptor() const
+{
+ return m_read_fd;
+}
+
+int
+PipeWindows::GetWriteFileDescriptor() const
+{
+ return m_write_fd;
+}
+
+int
+PipeWindows::ReleaseReadFileDescriptor()
+{
+ if (!CanRead())
+ return -1;
+ int result = m_read_fd;
+ m_read_fd = -1;
+ if (m_read_overlapped.hEvent)
+ ::CloseHandle(m_read_overlapped.hEvent);
+ m_read = INVALID_HANDLE_VALUE;
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+ return result;
+}
+
+int
+PipeWindows::ReleaseWriteFileDescriptor()
+{
+ if (!CanWrite())
+ return -1;
+ int result = m_write_fd;
+ m_write_fd = -1;
+ m_write = INVALID_HANDLE_VALUE;
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+ return result;
+}
+
+void
+PipeWindows::CloseReadFileDescriptor()
+{
+ if (!CanRead())
+ return;
+
+ if (m_read_overlapped.hEvent)
+ ::CloseHandle(m_read_overlapped.hEvent);
+ _close(m_read_fd);
+ m_read = INVALID_HANDLE_VALUE;
+ m_read_fd = -1;
+ ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
+}
+
+void
+PipeWindows::CloseWriteFileDescriptor()
+{
+ if (!CanWrite())
+ return;
+
+ _close(m_write_fd);
+ m_write = INVALID_HANDLE_VALUE;
+ m_write_fd = -1;
+ ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
+}
+
+void
+PipeWindows::Close()
+{
+ CloseReadFileDescriptor();
+ CloseWriteFileDescriptor();
+}
+
+Error
+PipeWindows::Delete(llvm::StringRef name)
+{
+ return Error();
+}
+
+bool
+PipeWindows::CanRead() const
+{
+ return (m_read != INVALID_HANDLE_VALUE);
+}
+
+bool
+PipeWindows::CanWrite() const
+{
+ return (m_write != INVALID_HANDLE_VALUE);
+}
+
+HANDLE
+PipeWindows::GetReadNativeHandle()
+{
+ return m_read;
+}
+
+HANDLE
+PipeWindows::GetWriteNativeHandle()
+{
+ return m_write;
+}
+
+Error
+PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &duration, size_t &bytes_read)
+{
+ if (!CanRead())
+ return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32);
+
+ bytes_read = 0;
+ DWORD sys_bytes_read = size;
+ BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped);
+ if (!result && GetLastError() != ERROR_IO_PENDING)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ DWORD timeout = (duration == std::chrono::microseconds::zero()) ? INFINITE : duration.count() * 1000;
+ DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout);
+ if (wait_result != WAIT_OBJECT_0)
+ {
+ // The operation probably failed. However, if it timed out, we need to cancel the I/O.
+ // Between the time we returned from WaitForSingleObject and the time we call CancelIoEx,
+ // the operation may complete. If that hapens, CancelIoEx will fail and return ERROR_NOT_FOUND.
+ // If that happens, the original operation should be considered to have been successful.
+ bool failed = true;
+ DWORD failure_error = ::GetLastError();
+ if (wait_result == WAIT_TIMEOUT)
+ {
+ BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped);
+ if (!cancel_result && GetLastError() == ERROR_NOT_FOUND)
+ failed = false;
+ }
+ if (failed)
+ return Error(failure_error, eErrorTypeWin32);
+ }
+
+ // Now we call GetOverlappedResult setting bWait to false, since we've already waited
+ // as long as we're willing to.
+ if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE))
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ bytes_read = sys_bytes_read;
+ return Error();
+}
+
+Error
+PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written)
+{
+ if (!CanWrite())
+ return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32);
+
+ DWORD sys_bytes_written = 0;
+ BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, &m_write_overlapped);
+ if (!write_result && GetLastError() != ERROR_IO_PENDING)
+ return Error(::GetLastError(), eErrorTypeWin32);
+
+ BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE);
+ if (!result)
+ return Error(::GetLastError(), eErrorTypeWin32);
+ return Error();
+}
diff --git a/source/Host/windows/ProcessLauncherWindows.cpp b/source/Host/windows/ProcessLauncherWindows.cpp
new file mode 100644
index 0000000000000..355dd40544f9a
--- /dev/null
+++ b/source/Host/windows/ProcessLauncherWindows.cpp
@@ -0,0 +1,105 @@
+//===-- ProcessLauncherWindows.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/windows/ProcessLauncherWindows.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+
+#include <string>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostProcess
+ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error)
+{
+ error.Clear();
+
+ std::string executable;
+ std::string commandLine;
+ std::vector<char> environment;
+ STARTUPINFO startupinfo = {0};
+ PROCESS_INFORMATION pi = {0};
+
+ HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
+ HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
+ HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
+
+ startupinfo.cb = sizeof(startupinfo);
+ startupinfo.dwFlags |= STARTF_USESTDHANDLES;
+ startupinfo.hStdError = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
+ startupinfo.hStdInput = stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
+ startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
+
+ const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
+ if (hide_console_var && llvm::StringRef(hide_console_var).equals_lower("true"))
+ {
+ startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
+ startupinfo.wShowWindow = SW_HIDE;
+ }
+
+ DWORD flags = CREATE_NEW_CONSOLE;
+ if (launch_info.GetFlags().Test(eLaunchFlagDebug))
+ flags |= DEBUG_ONLY_THIS_PROCESS;
+
+ executable = launch_info.GetExecutableFile().GetPath();
+ launch_info.GetArguments().GetQuotedCommandString(commandLine);
+ BOOL result = ::CreateProcessA(executable.c_str(), const_cast<char *>(commandLine.c_str()), NULL, NULL, TRUE, flags, NULL,
+ launch_info.GetWorkingDirectory().GetCString(), &startupinfo, &pi);
+ if (result)
+ {
+ // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess.
+ ::CloseHandle(pi.hThread);
+ }
+
+ if (stdin_handle)
+ ::CloseHandle(stdin_handle);
+ if (stdout_handle)
+ ::CloseHandle(stdout_handle);
+ if (stderr_handle)
+ ::CloseHandle(stderr_handle);
+
+ if (!result)
+ error.SetError(::GetLastError(), eErrorTypeWin32);
+ return HostProcess(pi.hProcess);
+}
+
+HANDLE
+ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd)
+{
+ const FileAction *action = launch_info.GetFileActionForFD(fd);
+ if (action == nullptr)
+ return NULL;
+ SECURITY_ATTRIBUTES secattr = {0};
+ secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secattr.bInheritHandle = TRUE;
+
+ const char *path = action->GetPath();
+ DWORD access = 0;
+ DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ DWORD create = 0;
+ DWORD flags = 0;
+ if (fd == STDIN_FILENO)
+ {
+ access = GENERIC_READ;
+ create = OPEN_EXISTING;
+ flags = FILE_ATTRIBUTE_READONLY;
+ }
+ if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ {
+ access = GENERIC_WRITE;
+ create = CREATE_ALWAYS;
+ if (fd == STDERR_FILENO)
+ flags = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ HANDLE result = ::CreateFile(path, access, share, &secattr, create, flags, NULL);
+ return (result == INVALID_HANDLE_VALUE) ? NULL : result;
+}
diff --git a/source/Host/windows/ProcessRunLock.cpp b/source/Host/windows/ProcessRunLock.cpp
new file mode 100644
index 0000000000000..1f21552a30623
--- /dev/null
+++ b/source/Host/windows/ProcessRunLock.cpp
@@ -0,0 +1,106 @@
+#include "lldb/Host/ProcessRunLock.h"
+#include "lldb/Host/windows/windows.h"
+
+namespace
+{
+#if defined(__MINGW32__)
+// Taken from WinNT.h
+typedef struct _RTL_SRWLOCK {
+ PVOID Ptr;
+} RTL_SRWLOCK, *PRTL_SRWLOCK;
+
+// Taken from WinBase.h
+typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
+#endif
+}
+
+
+static PSRWLOCK GetLock(lldb::rwlock_t lock)
+{
+ return static_cast<PSRWLOCK>(lock);
+}
+
+static bool ReadLock(lldb::rwlock_t rwlock)
+{
+ ::AcquireSRWLockShared(GetLock(rwlock));
+ return true;
+}
+
+static bool ReadUnlock(lldb::rwlock_t rwlock)
+{
+ ::ReleaseSRWLockShared(GetLock(rwlock));
+ return true;
+}
+
+static bool WriteLock(lldb::rwlock_t rwlock)
+{
+ ::AcquireSRWLockExclusive(GetLock(rwlock));
+ return true;
+}
+
+static bool WriteTryLock(lldb::rwlock_t rwlock)
+{
+ return !!::TryAcquireSRWLockExclusive(GetLock(rwlock));
+}
+
+static bool WriteUnlock(lldb::rwlock_t rwlock)
+{
+ ::ReleaseSRWLockExclusive(GetLock(rwlock));
+ return true;
+}
+
+using namespace lldb_private;
+
+ProcessRunLock::ProcessRunLock()
+ : m_running(false)
+{
+ m_rwlock = new SRWLOCK;
+ InitializeSRWLock(GetLock(m_rwlock));
+}
+
+ProcessRunLock::~ProcessRunLock()
+{
+ delete m_rwlock;
+}
+
+bool ProcessRunLock::ReadTryLock()
+{
+ ::ReadLock(m_rwlock);
+ if (m_running == false)
+ return true;
+ ::ReadUnlock(m_rwlock);
+ return false;
+}
+
+bool ProcessRunLock::ReadUnlock()
+{
+ return ::ReadUnlock(m_rwlock);
+}
+
+bool ProcessRunLock::SetRunning ()
+{
+ WriteLock(m_rwlock);
+ m_running = true;
+ WriteUnlock(m_rwlock);
+ return true;
+}
+
+bool ProcessRunLock::TrySetRunning ()
+{
+ if (WriteTryLock(m_rwlock))
+ {
+ bool was_running = m_running;
+ m_running = true;
+ WriteUnlock(m_rwlock);
+ return !was_running;
+ }
+ return false;
+}
+
+bool ProcessRunLock::SetStopped ()
+{
+ WriteLock(m_rwlock);
+ m_running = false;
+ WriteUnlock(m_rwlock);
+ return true;
+}
diff --git a/source/Host/windows/ThisThread.cpp b/source/Host/windows/ThisThread.cpp
new file mode 100644
index 0000000000000..bcd5b8d1c1d09
--- /dev/null
+++ b/source/Host/windows/ThisThread.cpp
@@ -0,0 +1,66 @@
+//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Error.h"
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/ThisThread.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+namespace
+{
+static const DWORD MS_VC_EXCEPTION = 0x406D1388;
+
+#pragma pack(push, 8)
+struct THREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to thread name
+ DWORD dwThreadId; // Thread ID (-1 == current thread)
+ DWORD dwFlags; // Reserved. Do not use.
+};
+#pragma pack(pop)
+}
+
+#endif
+
+void
+ThisThread::SetName(llvm::StringRef name)
+{
+// Other compilers don't yet support SEH, so we can only set the thread if compiling with MSVC.
+// TODO(zturner): Once clang-cl supports SEH, relax this conditional.
+#if defined(_MSC_VER) && !defined(__clang__)
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name.data();
+ info.dwThreadId = ::GetCurrentThreadId();
+ info.dwFlags = 0;
+
+ __try {
+ ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {}
+#endif
+}
+
+void
+ThisThread::GetName(llvm::SmallVectorImpl<char> &name)
+{
+ // Getting the thread name is not supported on Windows.
+ // TODO(zturner): In SetName(), make a TLS entry that contains the thread's name, and in this function
+ // try to extract that TLS entry.
+ name.clear();
+}
diff --git a/source/Host/windows/Windows.cpp b/source/Host/windows/Windows.cpp
new file mode 100644
index 0000000000000..71bff7a9d2e2f
--- /dev/null
+++ b/source/Host/windows/Windows.cpp
@@ -0,0 +1,231 @@
+//===-- Windows.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This file provides Windows support functions
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Host/windows/win32.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <io.h>
+#include <cerrno>
+#include <ctype.h>
+
+// These prototypes are defined in <direct.h>, but it also defines chdir() and getcwd(), giving multiply defined errors
+extern "C"
+{
+ char *_getcwd(char *buffer, int maxlen);
+ int _chdir(const char *path);
+}
+
+int vasprintf(char **ret, const char *fmt, va_list ap)
+{
+ char *buf;
+ int len;
+ size_t buflen;
+ va_list ap2;
+
+#if defined(_MSC_VER) || defined(__MINGW64)
+ ap2 = ap;
+ len = _vscprintf(fmt, ap2);
+#else
+ va_copy(ap2, ap);
+ len = vsnprintf(NULL, 0, fmt, ap2);
+#endif
+
+ if (len >= 0 && (buf = (char*) malloc ((buflen = (size_t) (len + 1)))) != NULL) {
+ len = vsnprintf(buf, buflen, fmt, ap);
+ *ret = buf;
+ } else {
+ *ret = NULL;
+ len = -1;
+ }
+
+ va_end(ap2);
+ return len;
+}
+
+char* strcasestr(const char *s, const char* find)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != 0) {
+ c = tolower((unsigned char) c);
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return 0;
+ } while ((char) tolower((unsigned char) sc) != c);
+ } while (strncasecmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *) s);
+}
+
+char* realpath(const char * name, char * resolved)
+{
+ char *retname = NULL; /* we will return this, if we fail */
+
+ /* SUSv3 says we must set `errno = EINVAL', and return NULL,
+ * if `name' is passed as a NULL pointer.
+ */
+
+ if (name == NULL)
+ errno = EINVAL;
+
+ /* Otherwise, `name' must refer to a readable filesystem object,
+ * if we are going to resolve its absolute path name.
+ */
+
+ else if (access(name, 4) == 0)
+ {
+ /* If `name' didn't point to an existing entity,
+ * then we don't get to here; we simply fall past this block,
+ * returning NULL, with `errno' appropriately set by `access'.
+ *
+ * When we _do_ get to here, then we can use `_fullpath' to
+ * resolve the full path for `name' into `resolved', but first,
+ * check that we have a suitable buffer, in which to return it.
+ */
+
+ if ((retname = resolved) == NULL)
+ {
+ /* Caller didn't give us a buffer, so we'll exercise the
+ * option granted by SUSv3, and allocate one.
+ *
+ * `_fullpath' would do this for us, but it uses `malloc', and
+ * Microsoft's implementation doesn't set `errno' on failure.
+ * If we don't do this explicitly ourselves, then we will not
+ * know if `_fullpath' fails on `malloc' failure, or for some
+ * other reason, and we want to set `errno = ENOMEM' for the
+ * `malloc' failure case.
+ */
+
+ retname = (char*) malloc(_MAX_PATH);
+ }
+
+ /* By now, we should have a valid buffer.
+ * If we don't, then we know that `malloc' failed,
+ * so we can set `errno = ENOMEM' appropriately.
+ */
+
+ if (retname == NULL)
+ errno = ENOMEM;
+
+ /* Otherwise, when we do have a valid buffer,
+ * `_fullpath' should only fail if the path name is too long.
+ */
+
+ else if ((retname = _fullpath(retname, name, _MAX_PATH)) == NULL)
+ errno = ENAMETOOLONG;
+ }
+
+ /* By the time we get to here,
+ * `retname' either points to the required resolved path name,
+ * or it is NULL, with `errno' set appropriately, either of which
+ * is our required return condition.
+ */
+
+ if (retname != NULL)
+ {
+ // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS
+ int initialLength = strlen(retname);
+ TCHAR buffer[MAX_PATH];
+ GetShortPathName(retname, buffer, MAX_PATH);
+ GetLongPathName(buffer, retname, initialLength + 1);
+
+ // Force drive to be upper case
+ if (retname[1] == ':')
+ retname[0] = toupper(retname[0]);
+ }
+
+ return retname;
+}
+
+#ifdef _MSC_VER
+
+char* basename(char *path)
+{
+ char* l1 = strrchr(path, '\\');
+ char* l2 = strrchr(path, '/');
+ if (l2 > l1) l1 = l2;
+ if (!l1) return path; // no base name
+ return &l1[1];
+}
+
+// use _getcwd() instead of GetCurrentDirectory() because it updates errno
+char* getcwd(char* path, int max)
+{
+ return _getcwd(path, max);
+}
+
+// use _chdir() instead of SetCurrentDirectory() because it updates errno
+int chdir(const char* path)
+{
+ return _chdir(path);
+}
+
+char *dirname(char *path)
+{
+ char* l1 = strrchr(path, '\\');
+ char* l2 = strrchr(path, '/');
+ if (l2 > l1) l1 = l2;
+ if (!l1) return NULL; // no dir name
+ *l1 = 0;
+ return path;
+}
+
+int strcasecmp(const char* s1, const char* s2)
+{
+ return stricmp(s1, s2);
+}
+
+int strncasecmp(const char* s1, const char* s2, size_t n)
+{
+ return strnicmp(s1, s2, n);
+}
+
+int usleep(uint32_t useconds)
+{
+ Sleep(useconds / 1000);
+ return 0;
+}
+
+#if _MSC_VER < 1900
+namespace lldb_private {
+int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
+{
+ int old_errno = errno;
+ int r = ::vsnprintf(buffer, count, format, argptr);
+ int new_errno = errno;
+ buffer[count-1] = '\0';
+ if (r == -1 || r == count)
+ {
+ FILE *nul = fopen("nul", "w");
+ int bytes_written = ::vfprintf(nul, format, argptr);
+ fclose(nul);
+ if (bytes_written < count)
+ errno = new_errno;
+ else
+ {
+ errno = old_errno;
+ r = bytes_written;
+ }
+ }
+ return r;
+}
+} // namespace lldb_private
+#endif
+
+#endif // _MSC_VER