diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 | 
| commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
| tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /source/Host/windows | |
| parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) | |
Notes
Diffstat (limited to 'source/Host/windows')
| -rw-r--r-- | source/Host/windows/Condition.cpp | 98 | ||||
| -rw-r--r-- | source/Host/windows/ConnectionGenericFileWindows.cpp | 354 | ||||
| -rw-r--r-- | source/Host/windows/EditLineWin.cpp | 435 | ||||
| -rw-r--r-- | source/Host/windows/FileSystem.cpp | 221 | ||||
| -rw-r--r-- | source/Host/windows/Host.cpp | 326 | ||||
| -rw-r--r-- | source/Host/windows/HostInfoWindows.cpp | 118 | ||||
| -rw-r--r-- | source/Host/windows/HostProcessWindows.cpp | 137 | ||||
| -rw-r--r-- | source/Host/windows/HostThreadWindows.cpp | 98 | ||||
| -rw-r--r-- | source/Host/windows/LockFileWindows.cpp | 93 | ||||
| -rw-r--r-- | source/Host/windows/Mutex.cpp | 109 | ||||
| -rw-r--r-- | source/Host/windows/PipeWindows.cpp | 336 | ||||
| -rw-r--r-- | source/Host/windows/ProcessLauncherWindows.cpp | 105 | ||||
| -rw-r--r-- | source/Host/windows/ProcessRunLock.cpp | 106 | ||||
| -rw-r--r-- | source/Host/windows/ThisThread.cpp | 66 | ||||
| -rw-r--r-- | source/Host/windows/Windows.cpp | 231 | 
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 | 
