diff options
Diffstat (limited to 'source/Plugins/Process/Windows/Live')
15 files changed, 2579 insertions, 0 deletions
| diff --git a/source/Plugins/Process/Windows/Live/CMakeLists.txt b/source/Plugins/Process/Windows/Live/CMakeLists.txt new file mode 100644 index 0000000000000..bdb680ad0bee0 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/CMakeLists.txt @@ -0,0 +1,24 @@ +include_directories(.) +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_SOURCES +  DebuggerThread.cpp +  LocalDebugDelegate.cpp +  ProcessWindowsLive.cpp +  TargetThreadWindowsLive.cpp +  ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) +  set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} +    x86/RegisterContextWindowsLive_x86.cpp +    ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) +  set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} +    x64/RegisterContextWindowsLive_x64.cpp +    ) +endif() + +add_lldb_library(lldbPluginProcessWindows +  ${PROC_WINDOWS_SOURCES} +  ) diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.cpp b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp new file mode 100644 index 0000000000000..d058a412c8968 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -0,0 +1,546 @@ +//===-- DebuggerThread.DebuggerThread --------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "IDebugDelegate.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostProcessWindows.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +struct DebugLaunchContext +{ +    DebugLaunchContext(DebuggerThread *thread, const ProcessLaunchInfo &launch_info) +        : m_thread(thread) +        , m_launch_info(launch_info) +    { +    } +    DebuggerThread *m_thread; +    ProcessLaunchInfo m_launch_info; +}; + +struct DebugAttachContext +{ +    DebugAttachContext(DebuggerThread *thread, lldb::pid_t pid, const ProcessAttachInfo &attach_info) +        : m_thread(thread) +        , m_pid(pid) +        , m_attach_info(attach_info) +    { +    } +    DebuggerThread *m_thread; +    lldb::pid_t m_pid; +    ProcessAttachInfo m_attach_info; +}; +} + +DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate) +    : m_debug_delegate(debug_delegate) +    , m_image_file(nullptr) +    , m_debugging_ended_event(nullptr) +    , m_is_shutting_down(false) +    , m_pid_to_detach(0) +    , m_detached(false) +{ +    m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); +} + +DebuggerThread::~DebuggerThread() +{ +    ::CloseHandle(m_debugging_ended_event); +} + +Error +DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) +{ +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, +        "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str()); + +    Error error; +    DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); +    HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", +                                                         DebuggerThreadLaunchRoutine, context, &error)); + +    if (!error.Success()) +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, +            "DebugLaunch couldn't launch debugger thread.  %s", error.AsCString()); +    } + +    return error; +} + +Error +DebuggerThread::DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread::DebugAttach attaching to '%u'", (DWORD)pid); + +    Error error; +    DebugAttachContext *context = new DebugAttachContext(this, pid, attach_info); +    HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", +                                                         DebuggerThreadAttachRoutine, context, &error)); + +    if (!error.Success()) +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "DebugAttach couldn't attach to process '%u'.  %s", (DWORD)pid, +                     error.AsCString()); +    } + +    return error; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(void *data) +{ +    DebugLaunchContext *context = static_cast<DebugLaunchContext *>(data); +    lldb::thread_result_t result = context->m_thread->DebuggerThreadLaunchRoutine(context->m_launch_info); +    delete context; +    return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(void *data) +{ +    DebugAttachContext *context = static_cast<DebugAttachContext *>(data); +    lldb::thread_result_t result = +        context->m_thread->DebuggerThreadAttachRoutine(context->m_pid, context->m_attach_info); +    delete context; +    return result; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info) +{ +    // Grab a shared_ptr reference to this so that we know it won't get deleted until after the +    // thread routine has exited. +    std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to launch '%s' on background thread.", +                 launch_info.GetExecutableFile().GetPath().c_str()); + +    Error error; +    ProcessLauncherWindows launcher; +    HostProcess process(launcher.LaunchProcess(launch_info, error)); +    // If we couldn't create the process, notify waiters immediately.  Otherwise enter the debug +    // loop and wait until we get the create process debug notification.  Note that if the process +    // was created successfully, we can throw away the process handle we got from CreateProcess +    // because Windows will give us another (potentially more useful?) handle when it sends us the +    // CREATE_PROCESS_DEBUG_EVENT. +    if (error.Success()) +        DebugLoop(); +    else +        m_debug_delegate->OnDebuggerError(error, 0); + +    return 0; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ +    // Grab a shared_ptr reference to this so that we know it won't get deleted until after the +    // thread routine has exited. +    std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to attach to process '%u' on background thread.", +                 (DWORD)pid); + +    if (!DebugActiveProcess((DWORD)pid)) +    { +        Error error(::GetLastError(), eErrorTypeWin32); +        m_debug_delegate->OnDebuggerError(error, 0); +        return 0; +    } + +    // The attach was successful, enter the debug loop.  From here on out, this is no different than +    // a create process operation, so all the same comments in DebugLaunch should apply from this +    // point out. +    DebugLoop(); + +    return 0; +} + +Error +DebuggerThread::StopDebugging(bool terminate) +{ +    Error error; + +    lldb::pid_t pid = m_process.GetProcessId(); + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, +        "StopDebugging('%s') called (inferior=%I64u).", +        (terminate ? "true" : "false"), pid); + +    // Set m_is_shutting_down to true if it was false.  Return if it was already true. +    bool expected = false; +    if (!m_is_shutting_down.compare_exchange_strong(expected, true)) +        return error; + +    // Make a copy of the process, since the termination sequence will reset +    // DebuggerThread's internal copy and it needs to remain open for the Wait operation. +    HostProcess process_copy = m_process; +    lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle(); + +    if (terminate) +    { +        // Initiate the termination before continuing the exception, so that the next debug +        // event we get is the exit process event, and not some other event. +        BOOL terminate_suceeded = TerminateProcess(handle, 0); +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, +            "StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'", +            handle, pid, (terminate_suceeded ? "true" : "false")); +    } + +    // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint +    // messing around in the debugger), continue it now.  But only AFTER calling TerminateProcess +    // to make sure that the very next call to WaitForDebugEvent is an exit process event. +    if (m_active_exception.get()) +    { +        WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, +            "StopDebugging masking active exception"); + +        ContinueAsyncException(ExceptionResult::MaskException); +    } + +    if (!terminate) +    { +        // Indicate that we want to detach. +        m_pid_to_detach = GetProcess().GetProcessId(); + +        // Force a fresh break so that the detach can happen from the debugger thread. +        if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle())) +        { +            error.SetError(::GetLastError(), eErrorTypeWin32); +        } +    } + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid); + +    DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000); +    if (wait_result != WAIT_OBJECT_0) +    { +        error.SetError(GetLastError(), eErrorTypeWin32); +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u", +                        m_debugging_ended_event, wait_result); +    } +    else +    { +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid); +    } + +    if (!error.Success()) +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, +            "StopDebugging encountered an error while trying to stop process %u.  %s", +            pid, error.AsCString()); +    } +    return error; +} + +void +DebuggerThread::ContinueAsyncException(ExceptionResult result) +{ +    if (!m_active_exception.get()) +        return; + +    WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, +        "ContinueAsyncException called for inferior process %I64u, broadcasting.", +        m_process.GetProcessId()); + +    m_active_exception.reset(); +    m_exception_pred.SetValue(result, eBroadcastAlways); +} + +void +DebuggerThread::FreeProcessHandles() +{ +    m_process = HostProcess(); +    m_main_thread = HostThread(); +    if (m_image_file) +    { +        ::CloseHandle(m_image_file); +        m_image_file = nullptr; +    } +} + +void +DebuggerThread::DebugLoop() +{ +    DEBUG_EVENT dbe = {0}; +    bool should_debug = true; +    WINLOG_IFALL(WINDOWS_LOG_EVENT, "Entering WaitForDebugEvent loop"); +    while (should_debug) +    { +        WINLOGD_IFALL(WINDOWS_LOG_EVENT, "Calling WaitForDebugEvent"); +        BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE); +        if (wait_result) +        { +            DWORD continue_status = DBG_CONTINUE; +            switch (dbe.dwDebugEventCode) +            { +                case EXCEPTION_DEBUG_EVENT: +                { +                    ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); + +                    if (status == ExceptionResult::MaskException) +                        continue_status = DBG_CONTINUE; +                    else if (status == ExceptionResult::SendToApplication) +                        continue_status = DBG_EXCEPTION_NOT_HANDLED; + +                    break; +                } +                case CREATE_THREAD_DEBUG_EVENT: +                    continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); +                    break; +                case CREATE_PROCESS_DEBUG_EVENT: +                    continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); +                    break; +                case EXIT_THREAD_DEBUG_EVENT: +                    continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); +                    break; +                case EXIT_PROCESS_DEBUG_EVENT: +                    continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); +                    should_debug = false; +                    break; +                case LOAD_DLL_DEBUG_EVENT: +                    continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); +                    break; +                case UNLOAD_DLL_DEBUG_EVENT: +                    continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); +                    break; +                case OUTPUT_DEBUG_STRING_EVENT: +                    continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); +                    break; +                case RIP_EVENT: +                    continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); +                    if (dbe.u.RipInfo.dwType == SLE_ERROR) +                        should_debug = false; +                    break; +            } + +            WINLOGD_IFALL(WINDOWS_LOG_EVENT, "DebugLoop calling ContinueDebugEvent(%u, %u, %u) on thread %u.", +                          dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId()); + +            ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); + +            if (m_detached) +            { +                should_debug = false; +            } +        } +        else +        { +            WINERR_IFALL(WINDOWS_LOG_EVENT, +                "DebugLoop returned FALSE from WaitForDebugEvent.  Error = %u", +                ::GetCurrentThreadId(), ::GetLastError()); + +            should_debug = false; +        } +    } +    FreeProcessHandles(); + +    WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting."); +    SetEvent(m_debugging_ended_event); +} + +ExceptionResult +DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id) +{ +    if (m_is_shutting_down) +    { +        // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a magic exception that +        // we use simply to wake up the DebuggerThread so that we can close out the debug loop. +        if (m_pid_to_detach != 0 && info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) +        { +            WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS, +                            "Breakpoint exception is cue to detach from process 0x%x", +                            m_pid_to_detach); +            ::DebugActiveProcessStop(m_pid_to_detach); +            m_detached = true; +        } + +        // Don't perform any blocking operations while we're shutting down.  That will +        // cause TerminateProcess -> WaitForSingleObject to time out. +        return ExceptionResult::SendToApplication; +    } + +    bool first_chance = (info.dwFirstChance != 0); + +    m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord, thread_id)); +    WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION, +                 "HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x", +                 first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id); + +    ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, +                                                                *m_active_exception); +    m_exception_pred.SetValue(result, eBroadcastNever); + +    WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, +        "DebuggerThread::HandleExceptionEvent waiting for ExceptionPred != BreakInDebugger"); + +    m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result); + +    WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, +        "DebuggerThread::HandleExceptionEvent got ExceptionPred = %u", +         m_exception_pred.GetValue()); + +    return result; +} + +DWORD +DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ +    WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, +        "HandleCreateThreadEvent Thread 0x%x spawned in process %I64u", +        thread_id, m_process.GetProcessId()); +    HostThread thread(info.hThread); +    thread.GetNativeThread().SetOwnsHandle(false); +    m_debug_delegate->OnCreateThread(thread); +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ +    uint32_t process_id = ::GetProcessId(info.hProcess); + +    WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_PROCESS, "HandleCreateProcessEvent process %u spawned", process_id); + +    std::string thread_name; +    llvm::raw_string_ostream name_stream(thread_name); +    name_stream << "lldb.plugin.process-windows.slave[" << process_id << "]"; +    name_stream.flush(); +    ThisThread::SetName(thread_name.c_str()); + +    // info.hProcess and info.hThread are closed automatically by Windows when +    // EXIT_PROCESS_DEBUG_EVENT is received. +    m_process = HostProcess(info.hProcess); +    ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); +    m_main_thread = HostThread(info.hThread); +    m_main_thread.GetNativeThread().SetOwnsHandle(false); +    m_image_file = info.hFile; + +    lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfImage); +    m_debug_delegate->OnDebuggerConnected(load_addr); + +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ +    WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, +        "HandleExitThreadEvent Thread %u exited with code %u in process %I64u", +        thread_id, info.dwExitCode, m_process.GetProcessId()); +    m_debug_delegate->OnExitThread(thread_id, info.dwExitCode); +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ +    WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, +        "HandleExitProcessEvent process %I64u exited with code %u", +        m_process.GetProcessId(), info.dwExitCode); + +    m_debug_delegate->OnExitProcess(info.dwExitCode); + +    FreeProcessHandles(); +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ +    if (info.hFile == nullptr) +    { +        // Not sure what this is, so just ignore it. +        WINWARN_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent has a NULL file handle, returning...", +                      m_process.GetProcessId()); +        return DBG_CONTINUE; +    } + +    std::vector<char> buffer(1); +    DWORD required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS); +    if (required_size > 0) +    { +        buffer.resize(required_size + 1); +        required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], required_size + 1, VOLUME_NAME_DOS); +        llvm::StringRef path_str(&buffer[0]); +        const char *path = path_str.data(); +        if (path_str.startswith("\\\\?\\")) +            path += 4; + +        FileSpec file_spec(path, false); +        ModuleSpec module_spec(file_spec); +        lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll); + +        WINLOG_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent DLL '%s' loaded at address 0x%p...", +                     m_process.GetProcessId(), path, info.lpBaseOfDll); + +        m_debug_delegate->OnLoadDll(module_spec, load_addr); +    } +    else +    { +        WINERR_IFALL(WINDOWS_LOG_EVENT, +                     "Inferior %I64u - HandleLoadDllEvent Error %u occurred calling GetFinalPathNameByHandle", +                     m_process.GetProcessId(), ::GetLastError()); +    } +    // Windows does not automatically close info.hFile, so we need to do it. +    ::CloseHandle(info.hFile); +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id) +{ +    WINLOG_IFALL(WINDOWS_LOG_EVENT, +        "HandleUnloadDllEvent process %I64u unloading DLL at addr 0x%p.", +        m_process.GetProcessId(), info.lpBaseOfDll); + +    m_debug_delegate->OnUnloadDll(reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll)); +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id) +{ +    return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) +{ +    WINERR_IFALL(WINDOWS_LOG_EVENT, +        "HandleRipEvent encountered error %u (type=%u) in process %I64u thread %u", +        info.dwError, info.dwType, m_process.GetProcessId(), thread_id); + +    Error error(info.dwError, eErrorTypeWin32); +    m_debug_delegate->OnDebuggerError(error, info.dwType); + +    return DBG_CONTINUE; +} diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.h b/source/Plugins/Process/Windows/Live/DebuggerThread.h new file mode 100644 index 0000000000000..6a26194139508 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.h @@ -0,0 +1,100 @@ +//===-- DebuggerThread.h ----------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_DebuggerThread_H_ +#define liblldb_Plugins_Process_Windows_DebuggerThread_H_ + +#include <atomic> +#include <memory> + +#include "ForwardDecl.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/windows/windows.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +// DebuggerThread +// +// Debugs a single process, notifying listeners as appropriate when interesting +// things occur. +//---------------------------------------------------------------------- +class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> +{ +  public: +    DebuggerThread(DebugDelegateSP debug_delegate); +    virtual ~DebuggerThread(); + +    Error DebugLaunch(const ProcessLaunchInfo &launch_info); +    Error DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info); + +    HostProcess +    GetProcess() const +    { +        return m_process; +    } +    HostThread +    GetMainThread() const +    { +        return m_main_thread; +    } +    std::weak_ptr<ExceptionRecord> +    GetActiveException() +    { +        return m_active_exception; +    } + +    Error StopDebugging(bool terminate); + +    void ContinueAsyncException(ExceptionResult result); + +  private: +    void FreeProcessHandles(); +    void DebugLoop(); +    ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id); +    DWORD HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id); +    DWORD HandleRipEvent(const RIP_INFO &info, DWORD thread_id); + +    DebugDelegateSP m_debug_delegate; + +    HostProcess m_process;    // The process being debugged. +    HostThread m_main_thread; // The main thread of the inferior. +    HANDLE m_image_file;      // The image file of the process being debugged. + +    ExceptionRecordSP m_active_exception; // The current exception waiting to be handled + +    Predicate<ExceptionResult> m_exception_pred; // A predicate which gets signalled when an exception +                                                 // is finished processing and the debug loop can be +                                                 // continued. + +    HANDLE m_debugging_ended_event; // An event which gets signalled by the debugger thread when it +                                    // exits the debugger loop and is detached from the inferior. + +    std::atomic<DWORD> m_pid_to_detach;  // Signals the loop to detach from the process (specified by pid). +    std::atomic<bool> m_is_shutting_down;   // Signals the debug loop to stop processing certain types of +                                            // events that block shutdown. +    bool m_detached;  // Indicates we've detached from the inferior process and the debug loop can exit. + +    static lldb::thread_result_t DebuggerThreadLaunchRoutine(void *data); +    lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info); +    static lldb::thread_result_t DebuggerThreadAttachRoutine(void *data); +    lldb::thread_result_t DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &launch_info); +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ForwardDecl.h b/source/Plugins/Process/Windows/Live/ForwardDecl.h new file mode 100644 index 0000000000000..a782597555e12 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ForwardDecl.h @@ -0,0 +1,41 @@ +//===-- ForwardDecl.h -------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_ForwardDecl_H_ +#define liblldb_Plugins_Process_Windows_ForwardDecl_H_ + +#include <memory> + +// ExceptionResult is returned by the debug delegate to specify how it processed +// the exception. +enum class ExceptionResult +{ +    BreakInDebugger,  // Break in the debugger and give the user a chance to interact with +                      // the program before continuing. +    MaskException,    // Eat the exception and don't let the application know it occurred. +    SendToApplication // Send the exception to the application to be handled as if there were +                      // no debugger attached. +}; + +namespace lldb_private +{ + +class ProcessWindows; + +class IDebugDelegate; +class DebuggerThread; +class ExceptionRecord; + +typedef std::shared_ptr<IDebugDelegate> DebugDelegateSP; +typedef std::shared_ptr<DebuggerThread> DebuggerThreadSP; +typedef std::shared_ptr<ExceptionRecord> ExceptionRecordSP; +typedef std::unique_ptr<ExceptionRecord> ExceptionRecordUP; +} + +#endif
\ No newline at end of file diff --git a/source/Plugins/Process/Windows/Live/IDebugDelegate.h b/source/Plugins/Process/Windows/Live/IDebugDelegate.h new file mode 100644 index 0000000000000..0e7849bb8ffeb --- /dev/null +++ b/source/Plugins/Process/Windows/Live/IDebugDelegate.h @@ -0,0 +1,46 @@ +//===-- IDebugDelegate.h ----------------------------------------*- C++ -*-===//
 +//
 +//                     The LLVM Compiler Infrastructure
 +//
 +// This file is distributed under the University of Illinois Open Source
 +// License. See LICENSE.TXT for details.
 +//
 +//===----------------------------------------------------------------------===//
 +
 +#ifndef liblldb_Plugins_Process_Windows_IDebugDelegate_H_
 +#define liblldb_Plugins_Process_Windows_IDebugDelegate_H_
 +
 +#include "ForwardDecl.h"
 +#include "lldb/lldb-forward.h"
 +#include "lldb/lldb-types.h"
 +#include <string>
 +
 +namespace lldb_private
 +{
 +class Error;
 +class HostThread;
 +
 +//----------------------------------------------------------------------
 +// IDebugDelegate
 +//
 +// IDebugDelegate defines an interface which allows implementors to receive
 +// notification of events that happen in a debugged process.
 +//----------------------------------------------------------------------
 +class IDebugDelegate
 +{
 +  public:
 +    virtual ~IDebugDelegate() {}
 +
 +    virtual void OnExitProcess(uint32_t exit_code) = 0;
 +    virtual void OnDebuggerConnected(lldb::addr_t image_base) = 0;
 +    virtual ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) = 0;
 +    virtual void OnCreateThread(const HostThread &thread) = 0;
 +    virtual void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) = 0;
 +    virtual void OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) = 0;
 +    virtual void OnUnloadDll(lldb::addr_t module_addr) = 0;
 +    virtual void OnDebugString(const std::string &string) = 0;
 +    virtual void OnDebuggerError(const Error &error, uint32_t type) = 0;
 +};
 +}
 +
 +#endif
 diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp new file mode 100644 index 0000000000000..a0ac9725c7560 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp @@ -0,0 +1,91 @@ +//===-- LocalDebugDelegate.cpp ----------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" + +using namespace lldb; +using namespace lldb_private; + +LocalDebugDelegate::LocalDebugDelegate(ProcessWP process) +    : m_process(process) +{ +} + +void +LocalDebugDelegate::OnExitProcess(uint32_t exit_code) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnExitProcess(exit_code); +} + +void +LocalDebugDelegate::OnDebuggerConnected(lldb::addr_t image_base) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnDebuggerConnected(image_base); +} + +ExceptionResult +LocalDebugDelegate::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        return process->OnDebugException(first_chance, record); +    else +        return ExceptionResult::MaskException; +} + +void +LocalDebugDelegate::OnCreateThread(const HostThread &thread) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnCreateThread(thread); +} + +void +LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnExitThread(thread_id, exit_code); +} + +void +LocalDebugDelegate::OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnLoadDll(module_spec, module_addr); +} + +void +LocalDebugDelegate::OnUnloadDll(lldb::addr_t module_addr) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnUnloadDll(module_addr); +} + +void +LocalDebugDelegate::OnDebugString(const std::string &string) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnDebugString(string); +} + +void +LocalDebugDelegate::OnDebuggerError(const Error &error, uint32_t type) +{ +    if (ProcessWindowsLiveSP process = GetProcessPointer()) +        process->OnDebuggerError(error, type); +} + +ProcessWindowsLiveSP +LocalDebugDelegate::GetProcessPointer() +{ +    ProcessSP process = m_process.lock(); +    return std::static_pointer_cast<ProcessWindowsLive>(process); +} diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h new file mode 100644 index 0000000000000..ec2f9418142f9 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h @@ -0,0 +1,68 @@ +//===-- LocalDebugDelegate.h ------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ +#define liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ + +#include <memory> + +#include "IDebugDelegate.h" + +#include "lldb/lldb-forward.h" + +namespace lldb_private +{ + +class ProcessWindowsLive; +typedef std::shared_ptr<ProcessWindowsLive> ProcessWindowsLiveSP; + +//---------------------------------------------------------------------- +// LocalDebugDelegate +// +// LocalDebugDelegate creates a connection between a ProcessWindowsLive and the +// debug driver.  This serves to decouple ProcessWindowsLive from the debug driver. +// It would be possible to get a similar decoupling by just having +// ProcessWindowsLive implement this interface directly.  There are two reasons why +// we don't do this: +// +// 1) In the future when we add support for local debugging through LLGS, and we +//    go through the Native*Protocol interface, it is likely we will need the +//    additional flexibility provided by this sort of adapter pattern. +// 2) LLDB holds a shared_ptr to the ProcessWindows, and our driver thread +//    needs access to it as well.  To avoid a race condition, we want to make +//    sure that we're also holding onto a shared_ptr. +//    lldb_private::Process supports enable_shared_from_this, but that gives us +//    a ProcessSP (which is exactly what we are trying to decouple from the +//    driver), so this adapter serves as a way to transparently hold the +//    ProcessSP while still keeping it decoupled from the driver. +//---------------------------------------------------------------------- +class LocalDebugDelegate : public IDebugDelegate +{ +  public: +    explicit LocalDebugDelegate(lldb::ProcessWP process); + +    void OnExitProcess(uint32_t exit_code) override; +    void OnDebuggerConnected(lldb::addr_t image_base) override; +    ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) override; +    void OnCreateThread(const HostThread &thread) override; +    void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; +    void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; +    void OnUnloadDll(lldb::addr_t module_addr) override; +    void OnDebugString(const std::string &message) override; +    void OnDebuggerError(const Error &error, uint32_t type) override; + +  private: +    ProcessWindowsLiveSP +    GetProcessPointer(); + +    lldb::ProcessWP m_process; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp new file mode 100644 index 0000000000000..55102cc12ebba --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp @@ -0,0 +1,989 @@ +//===-- ProcessWindowsLive.cpp ----------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Windows includes +#include "lldb/Host/windows/windows.h" +#include <psapi.h> + +// C++ Includes +#include <list> +#include <mutex> +#include <set> +#include <vector> + +// Other libraries and framework includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" +#include "TargetThreadWindowsLive.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +#define BOOL_STR(b) ((b) ? "true" : "false") + +namespace +{ + +std::string +GetProcessExecutableName(HANDLE process_handle) +{ +    std::vector<char> file_name; +    DWORD file_name_size = MAX_PATH;  // first guess, not an absolute limit +    DWORD copied = 0; +    do +    { +        file_name_size *= 2; +        file_name.resize(file_name_size); +        copied = ::GetModuleFileNameEx(process_handle, NULL, file_name.data(), file_name_size); +    } while (copied >= file_name_size); +    file_name.resize(copied); +    return std::string(file_name.begin(), file_name.end()); +} + +std::string +GetProcessExecutableName(DWORD pid) +{ +    std::string file_name; +    HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); +    if (process_handle != NULL) +    { +        file_name = GetProcessExecutableName(process_handle); +        ::CloseHandle(process_handle); +    } +    return file_name; +} + +}  // anonymous namespace + +namespace lldb_private +{ + +// We store a pointer to this class in the ProcessWindows, so that we don't expose Windows +// OS specific types and implementation details from a public header file. +class ProcessWindowsData +{ +  public: +    ProcessWindowsData(bool stop_at_entry) +        : m_stop_at_entry(stop_at_entry) +        , m_initial_stop_event(nullptr) +        , m_initial_stop_received(false) +    { +        m_initial_stop_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); +    } + +    ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); } + +    lldb_private::Error m_launch_error; +    lldb_private::DebuggerThreadSP m_debugger; +    StopInfoSP m_pending_stop_info; +    HANDLE m_initial_stop_event; +    bool m_stop_at_entry; +    bool m_initial_stop_received; +    std::map<lldb::tid_t, HostThread> m_new_threads; +    std::set<lldb::tid_t> m_exited_threads; +}; +} +//------------------------------------------------------------------------------ +// Static functions. + +ProcessSP +ProcessWindowsLive::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *) +{ +    return ProcessSP(new ProcessWindowsLive(target_sp, listener)); +} + +void +ProcessWindowsLive::Initialize() +{ +    static std::once_flag g_once_flag; + +    std::call_once(g_once_flag, []() +    { +        PluginManager::RegisterPlugin(GetPluginNameStatic(), +                                      GetPluginDescriptionStatic(), +                                      CreateInstance); +    }); +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindowsLive::ProcessWindowsLive(lldb::TargetSP target_sp, Listener &listener) +    : lldb_private::ProcessWindows(target_sp, listener) +{ +} + +ProcessWindowsLive::~ProcessWindowsLive() +{ +} + +void +ProcessWindowsLive::Terminate() +{ +} + +lldb_private::ConstString +ProcessWindowsLive::GetPluginNameStatic() +{ +    static ConstString g_name("windows"); +    return g_name; +} + +const char * +ProcessWindowsLive::GetPluginDescriptionStatic() +{ +    return "Process plugin for Windows"; +} + +Error +ProcessWindowsLive::EnableBreakpointSite(BreakpointSite *bp_site) +{ +    WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite called with bp_site 0x%p " +                                          "(id=%d, addr=0x%x)", +                 bp_site->GetID(), bp_site->GetLoadAddress()); + +    Error error = EnableSoftwareBreakpoint(bp_site); +    if (!error.Success()) +    { +        WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite failed.  %s", error.AsCString()); +    } +    return error; +} + +Error +ProcessWindowsLive::DisableBreakpointSite(BreakpointSite *bp_site) +{ +    WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite called with bp_site 0x%p " +                                          "(id=%d, addr=0x%x)", +                 bp_site->GetID(), bp_site->GetLoadAddress()); + +    Error error = DisableSoftwareBreakpoint(bp_site); + +    if (!error.Success()) +    { +        WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite failed.  %s", error.AsCString()); +    } +    return error; +} + +bool +ProcessWindowsLive::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ +    // Add all the threads that were previously running and for which we did not detect a thread +    // exited event. +    int new_size = 0; +    int continued_threads = 0; +    int exited_threads = 0; +    int new_threads = 0; + +    for (ThreadSP old_thread : old_thread_list.Threads()) +    { +        lldb::tid_t old_thread_id = old_thread->GetID(); +        auto exited_thread_iter = m_session_data->m_exited_threads.find(old_thread_id); +        if (exited_thread_iter == m_session_data->m_exited_threads.end()) +        { +            new_thread_list.AddThread(old_thread); +            ++new_size; +            ++continued_threads; +            WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and is still running.", +                          old_thread_id); +        } +        else +        { +            WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and has exited.", +                          old_thread_id); +            ++exited_threads; +        } +    } + +    // Also add all the threads that are new since the last time we broke into the debugger. +    for (const auto &thread_info : m_session_data->m_new_threads) +    { +        ThreadSP thread(new TargetThreadWindowsLive(*this, thread_info.second)); +        thread->SetID(thread_info.first); +        new_thread_list.AddThread(thread); +        ++new_size; +        ++new_threads; +        WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u is new since last update.", thread_info.first); +    } + +    WINLOG_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - %d new threads, %d old threads, %d exited threads.", +                 new_threads, continued_threads, exited_threads); + +    m_session_data->m_new_threads.clear(); +    m_session_data->m_exited_threads.clear(); + +    return new_size > 0; +} + +Error +ProcessWindowsLive::DoLaunch(Module *exe_module, +                             ProcessLaunchInfo &launch_info) +{ +    // Even though m_session_data is accessed here, it is before a debugger thread has been +    // kicked off.  So there's no race conditions, and it shouldn't be necessary to acquire +    // the mutex. + +    Error result; +    if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) +    { +        StreamString stream; +        stream.Printf("ProcessWindows unable to launch '%s'.  ProcessWindows can only be used for debug launches.", +                      launch_info.GetExecutableFile().GetPath().c_str()); +        std::string message = stream.GetString(); +        result.SetErrorString(message.c_str()); + +        WINERR_IFALL(WINDOWS_LOG_PROCESS, message.c_str()); +        return result; +    } + +    bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); +    m_session_data.reset(new ProcessWindowsData(stop_at_entry)); + +    SetPrivateState(eStateLaunching); +    DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); +    m_session_data->m_debugger.reset(new DebuggerThread(delegate)); +    DebuggerThreadSP debugger = m_session_data->m_debugger; + +    // Kick off the DebugLaunch asynchronously and wait for it to complete. +    result = debugger->DebugLaunch(launch_info); +    if (result.Fail()) +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'.  %s", +                     launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); +        return result; +    } + +    HostProcess process; +    Error error = WaitForDebuggerConnection(debugger, process); +    if (error.Fail()) +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'.  %s", +                     launch_info.GetExecutableFile().GetPath().c_str(), error.AsCString()); +        return error; +    } + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", +                 launch_info.GetExecutableFile().GetPath().c_str()); + +    // We've hit the initial stop.  If eLaunchFlagsStopAtEntry was specified, the private state +    // should already be set to eStateStopped as a result of hitting the initial breakpoint.  If +    // it was not set, the breakpoint should have already been resumed from and the private state +    // should already be eStateRunning. +    launch_info.SetProcessID(process.GetProcessId()); +    SetID(process.GetProcessId()); + +    return result; +} + +Error +ProcessWindowsLive::DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ +    m_session_data.reset(new ProcessWindowsData(!attach_info.GetContinueOnceAttached())); + +    DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); +    DebuggerThreadSP debugger(new DebuggerThread(delegate)); + +    m_session_data->m_debugger = debugger; + +    DWORD process_id = static_cast<DWORD>(pid); +    Error error = debugger->DebugAttach(process_id, attach_info); +    if (error.Fail()) +    { +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, +                     "DoAttachToProcessWithID encountered an error occurred initiating the asynchronous attach.  %s", +                     error.AsCString()); +        return error; +    } + +    HostProcess process; +    error = WaitForDebuggerConnection(debugger, process); +    if (error.Fail()) +    { +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, +                     "DoAttachToProcessWithID encountered an error waiting for the debugger to connect.  %s", +                     error.AsCString()); +        return error; +    } + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoAttachToProcessWithID successfully attached to process with pid=%u", +                 process_id); + +    // We've hit the initial stop.  If eLaunchFlagsStopAtEntry was specified, the private state +    // should already be set to eStateStopped as a result of hitting the initial breakpoint.  If +    // it was not set, the breakpoint should have already been resumed from and the private state +    // should already be eStateRunning. +    SetID(process.GetProcessId()); +    return error; +} + +Error +ProcessWindowsLive::WaitForDebuggerConnection(DebuggerThreadSP debugger, HostProcess &process) +{ +    Error result; +    WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection Waiting for loader breakpoint."); + +    // Block this function until we receive the initial stop from the process. +    if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) +    { +        WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection hit loader breakpoint, returning."); + +        process = debugger->GetProcess(); +        return m_session_data->m_launch_error; +    } +    else +        return Error(::GetLastError(), eErrorTypeWin32); +} + +Error +ProcessWindowsLive::DoResume() +{ +    llvm::sys::ScopedLock lock(m_mutex); +    Error error; + +    StateType private_state = GetPrivateState(); +    if (private_state == eStateStopped || private_state == eStateCrashed) +    { +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u while state is %u.  Resuming...", +                     m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + +        ExceptionRecordSP active_exception = +            m_session_data->m_debugger->GetActiveException().lock(); +        if (active_exception) +        { +            // Resume the process and continue processing debug events.  Mask +            // the exception so that from the process's view, there is no +            // indication that anything happened. +            m_session_data->m_debugger->ContinueAsyncException( +                ExceptionResult::MaskException); +        } + +        WINLOG_IFANY(WINDOWS_LOG_PROCESS | WINDOWS_LOG_THREAD, "DoResume resuming %u threads.", +                     m_thread_list.GetSize()); + +        for (int i = 0; i < m_thread_list.GetSize(); ++i) +        { +            auto thread = std::static_pointer_cast<TargetThreadWindowsLive>( +                m_thread_list.GetThreadAtIndex(i)); +            thread->DoResume(); +        } + +        SetPrivateState(eStateRunning); +    } +    else +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u but state is %u.  Returning...", +                     m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); +    } +    return error; +} + + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +lldb_private::ConstString +ProcessWindowsLive::GetPluginName() +{ +    return GetPluginNameStatic(); +} + +uint32_t +ProcessWindowsLive::GetPluginVersion() +{ +    return 1; +} + +Error +ProcessWindowsLive::DoDetach(bool keep_stopped) +{ +    DebuggerThreadSP debugger_thread; +    StateType private_state; +    { +        // Acquire the lock only long enough to get the DebuggerThread. +        // StopDebugging() will trigger a call back into ProcessWindows which +        // will also acquire the lock.  Thus we have to release the lock before +        // calling StopDebugging(). +        llvm::sys::ScopedLock lock(m_mutex); + +        private_state = GetPrivateState(); + +        if (!m_session_data) +        { +            WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called while state = %u, but there is no active session.", +                          private_state); +            return Error(); +        } + +        debugger_thread = m_session_data->m_debugger; +    } + +    Error error; +    if (private_state != eStateExited && private_state != eStateDetached) +    { +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called for process %I64u while state = %u.  Detaching...", +                     debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); +        error = debugger_thread->StopDebugging(false); +        if (error.Success()) +        { +            SetPrivateState(eStateDetached); +        } + +        // By the time StopDebugging returns, there is no more debugger thread, so +        // we can be assured that no other thread will race for the session data. +        m_session_data.reset(); +    } +    else +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, +                     "DoDetach called for process %I64u while state = %u, but cannot destroy in this state.", +                     debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); +    } + +    return error; +} + +Error +ProcessWindowsLive::DoDestroy() +{ +    DebuggerThreadSP debugger_thread; +    StateType private_state; +    { +        // Acquire this lock inside an inner scope, only long enough to get the DebuggerThread. +        // StopDebugging() will trigger a call back into ProcessWindows which will acquire the lock +        // again, so we need to not deadlock. +        llvm::sys::ScopedLock lock(m_mutex); + +        private_state = GetPrivateState(); + +        if (!m_session_data) +        { +            WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called while state = %u, but there is no active session.", +                          private_state); +            return Error(); +        } + +        debugger_thread = m_session_data->m_debugger; +    } + +    Error error; +    if (private_state != eStateExited && private_state != eStateDetached) +    { +        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called for process %I64u while state = %u.  Shutting down...", +                     debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); +        error = debugger_thread->StopDebugging(true); + +        // By the time StopDebugging returns, there is no more debugger thread, so +        // we can be assured that no other thread will race for the session data. +        m_session_data.reset(); +    } +    else +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, +                     "DoDestroy called for process %I64u while state = %u, but cannot destroy in this state.", +                     debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); +    } + +    return error; +} + +void +ProcessWindowsLive::RefreshStateAfterStop() +{ +    llvm::sys::ScopedLock lock(m_mutex); + +    if (!m_session_data) +    { +        WINWARN_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called with no active session.  Returning..."); +        return; +    } + +    m_thread_list.RefreshStateAfterStop(); + +    std::weak_ptr<ExceptionRecord> exception_record = m_session_data->m_debugger->GetActiveException(); +    ExceptionRecordSP active_exception = exception_record.lock(); +    if (!active_exception) +    { +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called for process %I64u but there is no " +                                          "active exception.  Why is the process stopped?", +                     m_session_data->m_debugger->GetProcess().GetProcessId()); +        return; +    } + +    StopInfoSP stop_info; +    m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); +    ThreadSP stop_thread = m_thread_list.GetSelectedThread(); +    if (!stop_thread) +        return; + +    RegisterContextSP register_context = stop_thread->GetRegisterContext(); + +    // The current EIP is AFTER the BP opcode, which is one byte. +    uint64_t pc = register_context->GetPC() - 1; +    if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT) +    { +        BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc)); + +        if (site) +        { +            WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, +                         "RefreshStateAfterStop detected breakpoint in process %I64u at " +                         "address 0x%I64x with breakpoint site %d", +                         m_session_data->m_debugger->GetProcess().GetProcessId(), pc, site->GetID()); + +            if (site->ValidForThisThread(stop_thread.get())) +            { +                WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, +                             "Breakpoint site %d is valid for this thread (0x%I64x), creating stop info.", +                             site->GetID(), stop_thread->GetID()); + +                stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID( +                    *stop_thread, site->GetID()); +                register_context->SetPC(pc); +            } +            else +            { +                WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, +                             "Breakpoint site %d is not valid for this thread, creating empty stop info.", +                             site->GetID()); +            } +        } +        stop_thread->SetStopInfo(stop_info); +    } +    else if (active_exception->GetExceptionCode() == EXCEPTION_SINGLE_STEP) +    { +        stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); +        stop_thread->SetStopInfo(stop_info); +        WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u", +                     stop_thread->GetID()); +    } +    else +    { +        std::string desc; +        llvm::raw_string_ostream desc_stream(desc); +        desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8) +                    << " encountered at address " << llvm::format_hex(pc, 8); +        stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); +        stop_thread->SetStopInfo(stop_info); +        WINLOG_IFALL(WINDOWS_LOG_EXCEPTION, desc_stream.str().c_str()); +    } +} + +bool +ProcessWindowsLive::IsAlive() +{ +    StateType state = GetPrivateState(); +    switch (state) +    { +        case eStateCrashed: +        case eStateDetached: +        case eStateUnloaded: +        case eStateExited: +        case eStateInvalid: +            return false; +        default: +            return true; +    } +} + +Error +ProcessWindowsLive::DoHalt(bool &caused_stop) +{ +    Error error; +    StateType state = GetPrivateState(); +    if (state == eStateStopped) +        caused_stop = false; +    else +    { +        llvm::sys::ScopedLock lock(m_mutex); +        caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle()); +        if (!caused_stop) +        { +            error.SetError(::GetLastError(), eErrorTypeWin32); +            WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoHalt called DebugBreakProcess, but it failed with error %u", +                         error.GetError()); +        } +    } +    return error; +} + +void +ProcessWindowsLive::DidLaunch() +{ +    ArchSpec arch_spec; +    DidAttach(arch_spec); +} + +void +ProcessWindowsLive::DidAttach(ArchSpec &arch_spec) +{ +    llvm::sys::ScopedLock lock(m_mutex); + +    // The initial stop won't broadcast the state change event, so account for that here. +    if (m_session_data && GetPrivateState() == eStateStopped && m_session_data->m_stop_at_entry) +        RefreshStateAfterStop(); +} + +size_t +ProcessWindowsLive::DoReadMemory(lldb::addr_t vm_addr, +                                 void *buf, +                                 size_t size, +                                 Error &error) +{ +    llvm::sys::ScopedLock lock(m_mutex); + +    if (!m_session_data) +        return 0; + +    WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory attempting to read %u bytes from address 0x%I64x", size, vm_addr); + +    HostProcess process = m_session_data->m_debugger->GetProcess(); +    void *addr = reinterpret_cast<void *>(vm_addr); +    SIZE_T bytes_read = 0; +    if (!ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, buf, size, &bytes_read)) +    { +        error.SetError(GetLastError(), eErrorTypeWin32); +        WINERR_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory failed with error code %u", error.GetError()); +    } +    return bytes_read; +} + +size_t +ProcessWindowsLive::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) +{ +    llvm::sys::ScopedLock lock(m_mutex); +    WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); + +    if (!m_session_data) +    { +        WINERR_IFANY(WINDOWS_LOG_MEMORY, "DoWriteMemory cannot write, there is no active debugger connection."); +        return 0; +    } + +    HostProcess process = m_session_data->m_debugger->GetProcess(); +    void *addr = reinterpret_cast<void *>(vm_addr); +    SIZE_T bytes_written = 0; +    lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); +    if (WriteProcessMemory(handle, addr, buf, size, &bytes_written)) +        FlushInstructionCache(handle, addr, bytes_written); +    else +    { +        error.SetError(GetLastError(), eErrorTypeWin32); +        WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory failed with error code %u", error.GetError()); +    } +    return bytes_written; +} + +Error +ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info) +{ +    Error error; +    llvm::sys::ScopedLock lock(m_mutex); + +    if (!m_session_data) +    { +        error.SetErrorString("GetMemoryRegionInfo called with no debugging session."); +        WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); +        return error; +    } + +    HostProcess process = m_session_data->m_debugger->GetProcess(); +    lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); +    if (handle == nullptr || handle == LLDB_INVALID_PROCESS) +    { +        error.SetErrorString("GetMemoryRegionInfo called with an invalid target process."); +        WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); +        return error; +    } + +    WINLOG_IFALL(WINDOWS_LOG_MEMORY, "GetMemoryRegionInfo getting info for address 0x%I64x", vm_addr); + +    void *addr = reinterpret_cast<void *>(vm_addr); +    MEMORY_BASIC_INFORMATION mem_info = {0}; +    SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); +    if (result == 0) +    { +        error.SetError(::GetLastError(), eErrorTypeWin32); +        WINERR_IFALL(WINDOWS_LOG_MEMORY, +                     "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", +                     error.GetError(), vm_addr); +        return error; +    } +    const bool readable = IsPageReadable(mem_info.Protect); +    const bool executable = IsPageExecutable(mem_info.Protect); +    const bool writable = IsPageWritable(mem_info.Protect); +    info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); +    info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); +    info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + +    error.SetError(::GetLastError(), eErrorTypeWin32); +    WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", +                  BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); +    return error; +} + +bool +ProcessWindowsLive::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ +    if (plugin_specified_by_name) +        return true; + +    // For now we are just making sure the file exists for a given module +    ModuleSP exe_module_sp(target_sp->GetExecutableModule()); +    if (exe_module_sp.get()) +        return exe_module_sp->GetFileSpec().Exists(); +    // However, if there is no executable module, we return true since we might be preparing to attach. +    return true; +} + +void +ProcessWindowsLive::OnExitProcess(uint32_t exit_code) +{ +    // No need to acquire the lock since m_session_data isn't accessed. +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Process %u exited with code %u", GetID(), exit_code); + +    TargetSP target = m_target_sp.lock(); +    if (target) +    { +        ModuleSP executable_module = target->GetExecutableModule(); +        ModuleList unloaded_modules; +        unloaded_modules.Append(executable_module); +        target->ModulesDidUnload(unloaded_modules, true); +    } + +    SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); +    SetPrivateState(eStateExited); +} + +void +ProcessWindowsLive::OnDebuggerConnected(lldb::addr_t image_base) +{ +    DebuggerThreadSP debugger = m_session_data->m_debugger; + +    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger connected to process %I64u.  Image base = 0x%I64x", +                 debugger->GetProcess().GetProcessId(), image_base); + +    ModuleSP module = GetTarget().GetExecutableModule(); +    if (!module) +    { +        // During attach, we won't have the executable module, so find it now. +        const DWORD pid = debugger->GetProcess().GetProcessId(); +        const std::string file_name = GetProcessExecutableName(pid); +        if (file_name.empty()) +        { +            return; +        } + +        FileSpec executable_file(file_name, true); +        ModuleSpec module_spec(executable_file); +        Error error; +        module = GetTarget().GetSharedModule(module_spec, &error); +        if (!module) +        { +            return; +        } + +        GetTarget().SetExecutableModule(module, false); +    } + +    bool load_addr_changed; +    module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed); + +    ModuleList loaded_modules; +    loaded_modules.Append(module); +    GetTarget().ModulesDidLoad(loaded_modules); + +    // Add the main executable module to the list of pending module loads.  We can't call +    // GetTarget().ModulesDidLoad() here because we still haven't returned from DoLaunch() / DoAttach() yet +    // so the target may not have set the process instance to `this` yet. +    llvm::sys::ScopedLock lock(m_mutex); +    const HostThreadWindows &wmain_thread = debugger->GetMainThread().GetNativeThread(); +    m_session_data->m_new_threads[wmain_thread.GetThreadId()] = debugger->GetMainThread(); +} + +ExceptionResult +ProcessWindowsLive::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ +    llvm::sys::ScopedLock lock(m_mutex); + +    // FIXME: Without this check, occasionally when running the test suite there is +    // an issue where m_session_data can be null.  It's not clear how this could happen +    // but it only surfaces while running the test suite.  In order to properly diagnose +    // this, we probably need to first figure allow the test suite to print out full +    // lldb logs, and then add logging to the process plugin. +    if (!m_session_data) +    { +        WINERR_IFANY(WINDOWS_LOG_EXCEPTION, +                     "Debugger thread reported exception 0x%x at address 0x%I64x, but there is no session.", +                     record.GetExceptionCode(), record.GetExceptionAddress()); +        return ExceptionResult::SendToApplication; +    } + +    if (!first_chance) +    { +        // Any second chance exception is an application crash by definition. +        SetPrivateState(eStateCrashed); +    } + +    ExceptionResult result = ExceptionResult::SendToApplication; +    switch (record.GetExceptionCode()) +    { +        case EXCEPTION_BREAKPOINT: +            // Handle breakpoints at the first chance. +            result = ExceptionResult::BreakInDebugger; + +            if (!m_session_data->m_initial_stop_received) +            { +                WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, +                             "Hit loader breakpoint at address 0x%I64x, setting initial stop event.", +                             record.GetExceptionAddress()); +                m_session_data->m_initial_stop_received = true; +                ::SetEvent(m_session_data->m_initial_stop_event); +            } +            else +            { +                WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, +                             "Hit non-loader breakpoint at address 0x%I64x.", +                             record.GetExceptionAddress()); +            } +            SetPrivateState(eStateStopped); +            break; +        case EXCEPTION_SINGLE_STEP: +            result = ExceptionResult::BreakInDebugger; +            SetPrivateState(eStateStopped); +            break; +        default: +            WINLOG_IFANY(WINDOWS_LOG_EXCEPTION, +                         "Debugger thread reported exception 0x%x at address 0x%I64x (first_chance=%s)", +                         record.GetExceptionCode(), record.GetExceptionAddress(), BOOL_STR(first_chance)); +            // For non-breakpoints, give the application a chance to handle the exception first. +            if (first_chance) +                result = ExceptionResult::SendToApplication; +            else +                result = ExceptionResult::BreakInDebugger; +    } + +    return result; +} + +void +ProcessWindowsLive::OnCreateThread(const HostThread &new_thread) +{ +    llvm::sys::ScopedLock lock(m_mutex); +    const HostThreadWindows &wnew_thread = new_thread.GetNativeThread(); +    m_session_data->m_new_threads[wnew_thread.GetThreadId()] = new_thread; +} + +void +ProcessWindowsLive::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ +    llvm::sys::ScopedLock lock(m_mutex); + +    // On a forced termination, we may get exit thread events after the session +    // data has been cleaned up. +    if (!m_session_data) +        return; + +    // A thread may have started and exited before the debugger stopped allowing a refresh. +    // Just remove it from the new threads list in that case. +    auto iter = m_session_data->m_new_threads.find(thread_id); +    if (iter != m_session_data->m_new_threads.end()) +        m_session_data->m_new_threads.erase(iter); +    else +        m_session_data->m_exited_threads.insert(thread_id); +} + +void +ProcessWindowsLive::OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) +{ +    // Confusingly, there is no Target::AddSharedModule.  Instead, calling GetSharedModule() with +    // a new module will add it to the module list and return a corresponding ModuleSP. +    Error error; +    ModuleSP module = GetTarget().GetSharedModule(module_spec, &error); +    bool load_addr_changed = false; +    module->SetLoadAddress(GetTarget(), module_addr, false, load_addr_changed); + +    ModuleList loaded_modules; +    loaded_modules.Append(module); +    GetTarget().ModulesDidLoad(loaded_modules); +} + +void +ProcessWindowsLive::OnUnloadDll(lldb::addr_t module_addr) +{ +    Address resolved_addr; +    if (GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) +    { +        ModuleSP module = resolved_addr.GetModule(); +        if (module) +        { +            ModuleList unloaded_modules; +            unloaded_modules.Append(module); +            GetTarget().ModulesDidUnload(unloaded_modules, false); +        } +    } +} + +void +ProcessWindowsLive::OnDebugString(const std::string &string) +{ +} + +void +ProcessWindowsLive::OnDebuggerError(const Error &error, uint32_t type) +{ +    llvm::sys::ScopedLock lock(m_mutex); + +    if (m_session_data->m_initial_stop_received) +    { +        // This happened while debugging.  Do we shutdown the debugging session, try to continue, +        // or do something else? +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred during debugging.  Unexpected behavior may result.  %s", +                     error.GetError(), error.AsCString()); +    } +    else +    { +        // If we haven't actually launched the process yet, this was an error launching the +        // process.  Set the internal error and signal the initial stop event so that the DoLaunch +        // method wakes up and returns a failure. +        m_session_data->m_launch_error = error; +        ::SetEvent(m_session_data->m_initial_stop_event); +        WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred launching the process before the initial stop.  %s", +                     error.GetError(), error.AsCString()); +        return; +    } +} diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h new file mode 100644 index 0000000000000..2429f873c8231 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h @@ -0,0 +1,125 @@ +//===-- ProcessWindowsLive.h ------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ + +// C Includes + +// C++ Includes +#include <memory> +#include <queue> + +// Other libraries and framework includes +#include "ForwardDecl.h" +#include "IDebugDelegate.h" +#include "lldb/lldb-forward.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" + +#include "llvm/Support/Mutex.h" + +#include "plugins/Process/Windows/Common/ProcessWindows.h" + +class ProcessMonitor; + +namespace lldb_private +{ +class HostProcess; +class ProcessWindowsData; + +class ProcessWindowsLive : public lldb_private::ProcessWindows, public lldb_private::IDebugDelegate +{ +public: +    //------------------------------------------------------------------ +    // Static functions. +    //------------------------------------------------------------------ +    static lldb::ProcessSP +    CreateInstance(lldb::TargetSP target_sp, +                   lldb_private::Listener &listener, +                   const lldb_private::FileSpec *); + +    static void +    Initialize(); + +    static void +    Terminate(); + +    static lldb_private::ConstString +    GetPluginNameStatic(); + +    static const char * +    GetPluginDescriptionStatic(); + +    //------------------------------------------------------------------ +    // Constructors and destructors +    //------------------------------------------------------------------ +    ProcessWindowsLive(lldb::TargetSP target_sp, +                       lldb_private::Listener &listener); + +    ~ProcessWindowsLive(); + +    // lldb_private::Process overrides +    lldb_private::ConstString GetPluginName() override; +    uint32_t GetPluginVersion() override; + +    lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; +    lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + +    lldb_private::Error DoDetach(bool keep_stopped) override; +    lldb_private::Error DoLaunch(lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; +    lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid, +                                                const lldb_private::ProcessAttachInfo &attach_info) override; +    lldb_private::Error DoResume() override; +    lldb_private::Error DoDestroy() override; +    lldb_private::Error DoHalt(bool &caused_stop) override; + +    void DidLaunch() override; +    void DidAttach(lldb_private::ArchSpec &arch_spec) override; + +    void RefreshStateAfterStop() override; + +    bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; +    bool +    DestroyRequiresHalt() override +    { +        return false; +    } +    bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; +    bool IsAlive() override; + +    size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error) override; +    size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) override; +    lldb_private::Error GetMemoryRegionInfo(lldb::addr_t vm_addr, lldb_private::MemoryRegionInfo &info) override; + +    // IDebugDelegate overrides. +    void OnExitProcess(uint32_t exit_code) override; +    void OnDebuggerConnected(lldb::addr_t image_base) override; +    ExceptionResult OnDebugException(bool first_chance, const lldb_private::ExceptionRecord &record) override; +    void OnCreateThread(const lldb_private::HostThread &thread) override; +    void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; +    void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; +    void OnUnloadDll(lldb::addr_t module_addr) override; +    void OnDebugString(const std::string &string) override; +    void OnDebuggerError(const lldb_private::Error &error, uint32_t type) override; + +  private: +    lldb_private::Error WaitForDebuggerConnection(lldb_private::DebuggerThreadSP debugger, +                                                  lldb_private::HostProcess &process); + +    llvm::sys::Mutex m_mutex; + +    // Data for the active debugging session. +    std::unique_ptr<lldb_private::ProcessWindowsData> m_session_data; +}; + +} + +#endif  // liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp new file mode 100644 index 0000000000000..52b32716e674a --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp @@ -0,0 +1,147 @@ +//===-- TargetThreadWindowsLive.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/Log.h" +#include "lldb/Core/Logging.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/RegisterContext.h" + +#include "TargetThreadWindowsLive.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +#if defined(_WIN64) +#include "x64/RegisterContextWindowsLive_x64.h" +#else +#include "x86/RegisterContextWindowsLive_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindowsLive::TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread) +    : TargetThreadWindows(process, thread) +    , m_host_thread(thread) +{ +} + +TargetThreadWindowsLive::~TargetThreadWindowsLive() +{ +    DestroyThread(); +} + +void +TargetThreadWindowsLive::RefreshStateAfterStop() +{ +    ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); +    SetState(eStateStopped); +    GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindowsLive::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindowsLive::DidStop() +{ +} + +RegisterContextSP +TargetThreadWindowsLive::GetRegisterContext() +{ +    if (!m_reg_context_sp) +        m_reg_context_sp = CreateRegisterContextForFrameIndex(0); + +    return m_reg_context_sp; +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrame(StackFrame *frame) +{ +    return CreateRegisterContextForFrameIndex(frame->GetConcreteFrameIndex()); +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrameIndex(uint32_t idx) +{ +    if (!m_reg_context_sp) +    { +        ArchSpec arch = HostInfo::GetArchitecture(); +        switch (arch.GetMachine()) +        { +            case llvm::Triple::x86: +#if defined(_WIN64) +                // FIXME: This is a Wow64 process, create a RegisterContextWindows_Wow64 +#else +                m_reg_context_sp.reset(new RegisterContextWindowsLive_x86(*this, idx)); +#endif +                break; +            case llvm::Triple::x86_64: +#if defined(_WIN64) +                m_reg_context_sp.reset(new RegisterContextWindowsLive_x64(*this, idx)); +#else +                // LLDB is 32-bit, but the target process is 64-bit.  We probably can't debug this. +#endif +            default: +                break; +        } +    } +    return m_reg_context_sp; +} + +bool +TargetThreadWindowsLive::CalculateStopInfo() +{ +    SetStopInfo(m_stop_info_sp); +    return true; +} + +Unwind * +TargetThreadWindowsLive::GetUnwinder() +{ +    // FIXME: Implement an unwinder based on the Windows unwinder exposed through DIA SDK. +    if (m_unwinder_ap.get() == NULL) +        m_unwinder_ap.reset(new UnwindLLDB(*this)); +    return m_unwinder_ap.get(); +} + +bool +TargetThreadWindowsLive::DoResume() +{ +    StateType resume_state = GetTemporaryResumeState(); +    StateType current_state = GetState(); +    if (resume_state == current_state) +        return true; + +    if (resume_state == eStateStepping) +    { +        uint32_t flags_index = GetRegisterContext()->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); +        uint64_t flags_value = GetRegisterContext()->ReadRegisterAsUnsigned(flags_index, 0); +        flags_value |= 0x100; // Set the trap flag on the CPU +        GetRegisterContext()->WriteRegisterFromUnsigned(flags_index, flags_value); +    } + +    if (resume_state == eStateStepping || resume_state == eStateRunning) +    { +        DWORD previous_suspend_count = 0; +        HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); +        do +        { +            previous_suspend_count = ::ResumeThread(thread_handle); +        } while (previous_suspend_count > 0); +    } +    return true; +} diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h new file mode 100644 index 0000000000000..15262b9e94239 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h @@ -0,0 +1,55 @@ +//===-- TargetThreadWindowsLive.h -------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" + +#include "Plugins/Process/Windows/Common/TargetThreadWindows.h" + +namespace lldb_private +{ +class ProcessWindows; +class HostThread; +class StackFrame; + +class TargetThreadWindowsLive : public lldb_private::TargetThreadWindows +{ +  public: +    TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread); +    virtual ~TargetThreadWindowsLive(); + +    // lldb_private::Thread overrides +    void RefreshStateAfterStop() override; +    void WillResume(lldb::StateType resume_state) override; +    void DidStop() override; +    lldb::RegisterContextSP GetRegisterContext() override; +    lldb::RegisterContextSP CreateRegisterContextForFrame(StackFrame *frame) override; +    bool CalculateStopInfo() override; +    Unwind *GetUnwinder() override; + +    bool DoResume(); + +    HostThread +    GetHostThread() const +    { +        return m_host_thread; +    } + +  private: +    lldb::RegisterContextSP CreateRegisterContextForFrameIndex(uint32_t idx); + +    HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp new file mode 100644 index 0000000000000..e74647ab68fb5 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp @@ -0,0 +1,171 @@ +//===-- RegisterContextWindowsLive_x64.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/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "RegisterContextWindowsLive_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextWindowsLive_x64::RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx) +    : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x64::~RegisterContextWindowsLive_x64() +{ +} + + +bool +RegisterContextWindowsLive_x64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ +    if (!CacheAllRegisterValues()) +        return false; + +    switch (reg_info->kinds[eRegisterKindLLDB]) +    { +        case lldb_rax_x86_64: +            reg_value.SetUInt64(m_context.Rax); +            break; +        case lldb_rbx_x86_64: +            reg_value.SetUInt64(m_context.Rbx); +            break; +        case lldb_rcx_x86_64: +            reg_value.SetUInt64(m_context.Rcx); +            break; +        case lldb_rdx_x86_64: +            reg_value.SetUInt64(m_context.Rdx); +            break; +        case lldb_rdi_x86_64: +            reg_value.SetUInt64(m_context.Rdi); +            break; +        case lldb_rsi_x86_64: +            reg_value.SetUInt64(m_context.Rsi); +            break; +        case lldb_r8_x86_64: +            reg_value.SetUInt64(m_context.R8); +            break; +        case lldb_r9_x86_64: +            reg_value.SetUInt64(m_context.R9); +            break; +        case lldb_r10_x86_64: +            reg_value.SetUInt64(m_context.R10); +            break; +        case lldb_r11_x86_64: +            reg_value.SetUInt64(m_context.R11); +            break; +        case lldb_r12_x86_64: +            reg_value.SetUInt64(m_context.R12); +            break; +        case lldb_r13_x86_64: +            reg_value.SetUInt64(m_context.R13); +            break; +        case lldb_r14_x86_64: +            reg_value.SetUInt64(m_context.R14); +            break; +        case lldb_r15_x86_64: +            reg_value.SetUInt64(m_context.R15); +            break; +        case lldb_rbp_x86_64: +            reg_value.SetUInt64(m_context.Rbp); +            break; +        case lldb_rsp_x86_64: +            reg_value.SetUInt64(m_context.Rsp); +            break; +        case lldb_rip_x86_64: +            reg_value.SetUInt64(m_context.Rip); +            break; +        case lldb_rflags_x86_64: +            reg_value.SetUInt64(m_context.EFlags); +            break; +    } +    return true; +} + +bool +RegisterContextWindowsLive_x64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ +    // Since we cannot only write a single register value to the inferior, we need to make sure +    // our cached copy of the register values are fresh.  Otherwise when writing EAX, for example, +    // we may also overwrite some other register with a stale value. +    if (!CacheAllRegisterValues()) +        return false; + +    switch (reg_info->kinds[eRegisterKindLLDB]) +    { +        case lldb_rax_x86_64: +            m_context.Rax = reg_value.GetAsUInt64(); +            break; +        case lldb_rbx_x86_64: +            m_context.Rbx = reg_value.GetAsUInt64(); +            break; +        case lldb_rcx_x86_64: +            m_context.Rcx = reg_value.GetAsUInt64(); +            break; +        case lldb_rdx_x86_64: +            m_context.Rdx = reg_value.GetAsUInt64(); +            break; +        case lldb_rdi_x86_64: +            m_context.Rdi = reg_value.GetAsUInt64(); +            break; +        case lldb_rsi_x86_64: +            m_context.Rsi = reg_value.GetAsUInt64(); +            break; +        case lldb_r8_x86_64: +            m_context.R8 = reg_value.GetAsUInt64(); +            break; +        case lldb_r9_x86_64: +            m_context.R9 = reg_value.GetAsUInt64(); +            break; +        case lldb_r10_x86_64: +            m_context.R10 = reg_value.GetAsUInt64(); +            break; +        case lldb_r11_x86_64: +            m_context.R11 = reg_value.GetAsUInt64(); +            break; +        case lldb_r12_x86_64: +            m_context.R12 = reg_value.GetAsUInt64(); +            break; +        case lldb_r13_x86_64: +            m_context.R13 = reg_value.GetAsUInt64(); +            break; +        case lldb_r14_x86_64: +            m_context.R14 = reg_value.GetAsUInt64(); +            break; +        case lldb_r15_x86_64: +            m_context.R15 = reg_value.GetAsUInt64(); +            break; +        case lldb_rbp_x86_64: +            m_context.Rbp = reg_value.GetAsUInt64(); +            break; +        case lldb_rsp_x86_64: +            m_context.Rsp = reg_value.GetAsUInt64(); +            break; +        case lldb_rip_x86_64: +            m_context.Rip = reg_value.GetAsUInt64(); +            break; +        case lldb_rflags_x86_64: +            m_context.EFlags = reg_value.GetAsUInt64(); +            break; +    } + +    // Physically update the registers in the target process. +    TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); +    return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h new file mode 100644 index 0000000000000..bd250a994d3a8 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h @@ -0,0 +1,40 @@ +//===-- RegisterContextWindowsLive_x64.h ------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x64_H_ +#define liblldb_RegisterContextWindowsLive_x64_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x64 : public RegisterContextWindows_x64 +{ +  public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx); + +    virtual ~RegisterContextWindowsLive_x64(); + +    //------------------------------------------------------------------ +    // Subclasses must override these functions +    //------------------------------------------------------------------ +    bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + +    bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x64_H_ diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp new file mode 100644 index 0000000000000..f2decc72d16df --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp @@ -0,0 +1,100 @@ +//===-- RegisterContextWindowsLive_x86.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/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContextWindowsLive_x86.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsLive_x86::RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx) +    : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x86::~RegisterContextWindowsLive_x86() +{ +} + + +bool +RegisterContextWindowsLive_x86::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ +    // Since we cannot only write a single register value to the inferior, we need to make sure +    // our cached copy of the register values are fresh.  Otherwise when writing EAX, for example, +    // we may also overwrite some other register with a stale value. +    if (!CacheAllRegisterValues()) +        return false; + +    uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; +    switch (reg) +    { +        case lldb_eax_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EAX", reg_value.GetAsUInt32()); +            m_context.Eax = reg_value.GetAsUInt32(); +            break; +        case lldb_ebx_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBX", reg_value.GetAsUInt32()); +            m_context.Ebx = reg_value.GetAsUInt32(); +            break; +        case lldb_ecx_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ECX", reg_value.GetAsUInt32()); +            m_context.Ecx = reg_value.GetAsUInt32(); +            break; +        case lldb_edx_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDX", reg_value.GetAsUInt32()); +            m_context.Edx = reg_value.GetAsUInt32(); +            break; +        case lldb_edi_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDI", reg_value.GetAsUInt32()); +            m_context.Edi = reg_value.GetAsUInt32(); +            break; +        case lldb_esi_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESI", reg_value.GetAsUInt32()); +            m_context.Esi = reg_value.GetAsUInt32(); +            break; +        case lldb_ebp_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBP", reg_value.GetAsUInt32()); +            m_context.Ebp = reg_value.GetAsUInt32(); +            break; +        case lldb_esp_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESP", reg_value.GetAsUInt32()); +            m_context.Esp = reg_value.GetAsUInt32(); +            break; +        case lldb_eip_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EIP", reg_value.GetAsUInt32()); +            m_context.Eip = reg_value.GetAsUInt32(); +            break; +        case lldb_eflags_i386: +            WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EFLAGS", reg_value.GetAsUInt32()); +            m_context.EFlags = reg_value.GetAsUInt32(); +            break; +        default: +            WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to unknown register %u", reg_value.GetAsUInt32(), +                          reg); +    } + +    // Physically update the registers in the target process. +    TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); +    return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} + +}  // namespace lldb_private diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h new file mode 100644 index 0000000000000..9554f017408a4 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsLive_x86.h ------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x86_H_ +#define liblldb_RegisterContextWindowsLive_x86_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x86 : public RegisterContextWindows_x86 +{ +  public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx); + +    virtual ~RegisterContextWindowsLive_x86(); + +    bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x86_H_ | 
