Index: source/API/SystemInitializerFull.cpp =================================================================== --- source/API/SystemInitializerFull.cpp +++ source/API/SystemInitializerFull.cpp @@ -70,8 +70,8 @@ #if defined(_MSC_VER) #include "lldb/Host/windows/windows.h" -#include "Plugins/Process/Windows/ProcessWindows.h" -#include "Plugins/Process/win-minidump/ProcessWinMiniDump.h" +#include "Plugins/Process/Windows/Live/ProcessWindows.h" +#include "Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h" #endif #include "llvm/Support/TargetSelect.h" Index: source/Initialization/SystemInitializerCommon.cpp =================================================================== --- source/Initialization/SystemInitializerCommon.cpp +++ source/Initialization/SystemInitializerCommon.cpp @@ -47,7 +47,7 @@ #if defined(_MSC_VER) #include "lldb/Host/windows/windows.h" -#include "Plugins/Process/Windows/ProcessWindowsLog.h" +#include "Plugins/Process/Windows/Live/ProcessWindowsLog.h" #endif #include "llvm/Support/TargetSelect.h" Index: source/Plugins/Process/CMakeLists.txt =================================================================== --- source/Plugins/Process/CMakeLists.txt +++ source/Plugins/Process/CMakeLists.txt @@ -5,8 +5,8 @@ add_subdirectory(FreeBSD) add_subdirectory(POSIX) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") - add_subdirectory(Windows) - add_subdirectory(win-minidump) + add_subdirectory(Windows/Live) + add_subdirectory(Windows/MiniDump) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_subdirectory(MacOSX-Kernel) endif() Index: source/Plugins/Process/Windows/CMakeLists.txt =================================================================== --- source/Plugins/Process/Windows/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -set(LLVM_NO_RTTI 1) - -include_directories(.) -include_directories(../Utility) - -set(PROC_WINDOWS_SOURCES - DebuggerThread.cpp - LocalDebugDelegate.cpp - ProcessWindows.cpp - ProcessWindowsLog.cpp - RegisterContextWindows.cpp - TargetThreadWindows.cpp - ) - -if (CMAKE_SIZEOF_VOID_P EQUAL 4) - set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} - x86/RegisterContextWindows_x86.cpp - ) -elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} - x64/RegisterContextWindows_x64.cpp - ) -endif() - -add_lldb_library(lldbPluginProcessWindows - ${PROC_WINDOWS_SOURCES} - ) Index: source/Plugins/Process/Windows/Common/ExceptionRecord.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Common/ExceptionRecord.h @@ -0,0 +1,99 @@ +//===-- ExceptionRecord.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_ExceptionRecord_H_ +#define liblldb_Plugins_Process_Windows_ExceptionRecord_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/windows/windows.h" +#include + +#include +#include + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +// ExceptionRecord +// +// ExceptionRecord defines an interface which allows implementors to receive +// notification of events that happen in a debugged process. +//---------------------------------------------------------------------- +class ExceptionRecord +{ + public: + ExceptionRecord(const EXCEPTION_RECORD &record, lldb::tid_t thread_id) + { + m_code = record.ExceptionCode; + m_continuable = (record.ExceptionFlags == 0); + if (record.ExceptionRecord) + m_next_exception.reset(new ExceptionRecord(*record.ExceptionRecord, thread_id)); + m_exception_addr = reinterpret_cast(record.ExceptionAddress); + m_thread_id = thread_id; + m_arguments.assign(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters); + } + + // MINIDUMP_EXCEPTIONs are almost identical to EXCEPTION_RECORDs. + ExceptionRecord(const MINIDUMP_EXCEPTION &record, lldb::tid_t thread_id) : + m_code(record.ExceptionCode), + m_continuable(record.ExceptionFlags == 0), + m_next_exception(nullptr), + m_exception_addr(static_cast(record.ExceptionAddress)), + m_thread_id(thread_id), + m_arguments(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters) + { + // Set up link to nested exception. + if (record.ExceptionRecord) + { + m_next_exception.reset(new ExceptionRecord(*reinterpret_cast(record.ExceptionRecord), + thread_id)); + } + } + + virtual ~ExceptionRecord() {} + + DWORD + GetExceptionCode() const + { + return m_code; + } + bool + IsContinuable() const + { + return m_continuable; + } + const ExceptionRecord * + GetNextException() const + { + return m_next_exception.get(); + } + lldb::addr_t + GetExceptionAddress() const + { + return m_exception_addr; + } + + lldb::tid_t + GetThreadID() const + { + return m_thread_id; + } + + private: + DWORD m_code; + bool m_continuable; + std::shared_ptr m_next_exception; + lldb::addr_t m_exception_addr; + lldb::tid_t m_thread_id; + std::vector m_arguments; +}; +} + +#endif Index: source/Plugins/Process/Windows/DebuggerThread.h =================================================================== --- source/Plugins/Process/Windows/DebuggerThread.h +++ /dev/null @@ -1,98 +0,0 @@ -//===-- 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 -#include - -#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 -{ - 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 - 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 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 m_pid_to_detach; // Signals the loop to detach from the process (specified by pid). - 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 Index: source/Plugins/Process/Windows/DebuggerThread.cpp =================================================================== --- source/Plugins/Process/Windows/DebuggerThread.cpp +++ /dev/null @@ -1,533 +0,0 @@ -//===-- 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/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_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(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(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 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 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); - - // 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) -{ - 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); - - if (m_pid_to_detach != 0 && m_active_exception->GetExceptionCode() == 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); - if (::DebugActiveProcessStop(m_pid_to_detach)) { - m_detached = true; - return ExceptionResult::MaskException; - } else { - WINLOG_IFANY(WINDOWS_LOG_PROCESS, "Failed to detach, treating as a regular breakpoint"); - } - } - - 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(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 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(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(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; -} Index: source/Plugins/Process/Windows/ExceptionRecord.h =================================================================== --- source/Plugins/Process/Windows/ExceptionRecord.h +++ /dev/null @@ -1,101 +0,0 @@ -//===-- ExceptionRecord.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_ExceptionRecord_H_ -#define liblldb_Plugins_Process_Windows_ExceptionRecord_H_ - -#include "ForwardDecl.h" - -#include "lldb/lldb-forward.h" -#include "lldb/Host/windows/windows.h" -#include - -#include -#include - -namespace lldb_private -{ - -//---------------------------------------------------------------------- -// ExceptionRecord -// -// ExceptionRecord defines an interface which allows implementors to receive -// notification of events that happen in a debugged process. -//---------------------------------------------------------------------- -class ExceptionRecord -{ - public: - ExceptionRecord(const EXCEPTION_RECORD &record, lldb::tid_t thread_id) - { - m_code = record.ExceptionCode; - m_continuable = (record.ExceptionFlags == 0); - if (record.ExceptionRecord) - m_next_exception.reset(new ExceptionRecord(*record.ExceptionRecord, thread_id)); - m_exception_addr = reinterpret_cast(record.ExceptionAddress); - m_thread_id = thread_id; - m_arguments.assign(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters); - } - - // MINIDUMP_EXCEPTIONs are almost identical to EXCEPTION_RECORDs. - ExceptionRecord(const MINIDUMP_EXCEPTION &record, lldb::tid_t thread_id) : - m_code(record.ExceptionCode), - m_continuable(record.ExceptionFlags == 0), - m_next_exception(nullptr), - m_exception_addr(static_cast(record.ExceptionAddress)), - m_thread_id(thread_id), - m_arguments(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters) - { - // Set up link to nested exception. - if (record.ExceptionRecord) - { - m_next_exception.reset(new ExceptionRecord(*reinterpret_cast(record.ExceptionRecord), - thread_id)); - } - } - - virtual ~ExceptionRecord() {} - - DWORD - GetExceptionCode() const - { - return m_code; - } - bool - IsContinuable() const - { - return m_continuable; - } - const ExceptionRecord * - GetNextException() const - { - return m_next_exception.get(); - } - lldb::addr_t - GetExceptionAddress() const - { - return m_exception_addr; - } - - lldb::tid_t - GetThreadID() const - { - return m_thread_id; - } - - private: - DWORD m_code; - bool m_continuable; - std::shared_ptr m_next_exception; - lldb::addr_t m_exception_addr; - lldb::tid_t m_thread_id; - std::vector m_arguments; -}; -} - -#endif Index: source/Plugins/Process/Windows/ForwardDecl.h =================================================================== --- source/Plugins/Process/Windows/ForwardDecl.h +++ /dev/null @@ -1,41 +0,0 @@ -//===-- 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_ - -class ProcessWindows; - -#include - -// 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 IDebugDelegate; -class DebuggerThread; -class ExceptionRecord; - -typedef std::shared_ptr DebugDelegateSP; -typedef std::shared_ptr DebuggerThreadSP; -typedef std::shared_ptr ExceptionRecordSP; -typedef std::unique_ptr ExceptionRecordUP; -} - -#endif \ No newline at end of file Index: source/Plugins/Process/Windows/IDebugDelegate.h =================================================================== --- source/Plugins/Process/Windows/IDebugDelegate.h +++ /dev/null @@ -1,46 +0,0 @@ -//===-- 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 - -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 Index: source/Plugins/Process/Windows/Live/CMakeLists.txt =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/CMakeLists.txt @@ -0,0 +1,28 @@ +set(LLVM_NO_RTTI 1) + +include_directories(.) +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_SOURCES + DebuggerThread.cpp + LocalDebugDelegate.cpp + ProcessWindows.cpp + ProcessWindowsLog.cpp + RegisterContextWindows.cpp + TargetThreadWindows.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x86/RegisterContextWindows_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x64/RegisterContextWindows_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindows + ${PROC_WINDOWS_SOURCES} + ) Index: source/Plugins/Process/Windows/Live/DebuggerThread.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/DebuggerThread.h @@ -0,0 +1,98 @@ +//===-- 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 +#include + +#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 +{ + 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 + 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 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 m_pid_to_detach; // Signals the loop to detach from the process (specified by pid). + 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 Index: source/Plugins/Process/Windows/Live/DebuggerThread.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -0,0 +1,533 @@ +//===-- 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/Live/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_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(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(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 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 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); + + // 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) +{ + 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); + + if (m_pid_to_detach != 0 && m_active_exception->GetExceptionCode() == 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); + if (::DebugActiveProcessStop(m_pid_to_detach)) { + m_detached = true; + return ExceptionResult::MaskException; + } else { + WINLOG_IFANY(WINDOWS_LOG_PROCESS, "Failed to detach, treating as a regular breakpoint"); + } + } + + 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(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 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(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(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; +} Index: source/Plugins/Process/Windows/Live/ForwardDecl.h =================================================================== --- /dev/null +++ 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_ + +class ProcessWindows; + +#include + +// 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 IDebugDelegate; +class DebuggerThread; +class ExceptionRecord; + +typedef std::shared_ptr DebugDelegateSP; +typedef std::shared_ptr DebuggerThreadSP; +typedef std::shared_ptr ExceptionRecordSP; +typedef std::unique_ptr ExceptionRecordUP; +} + +#endif \ No newline at end of file Index: source/Plugins/Process/Windows/Live/IDebugDelegate.h =================================================================== --- /dev/null +++ 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 + +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 Index: source/Plugins/Process/Windows/Live/LocalDebugDelegate.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/LocalDebugDelegate.h @@ -0,0 +1,60 @@ +//===-- 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 "IDebugDelegate.h" +#include "lldb/lldb-forward.h" + +class ProcessWindows; + +namespace lldb_private +{ +//---------------------------------------------------------------------- +// LocalDebugDelegate +// +// LocalDebugDelegate creates a connection between a ProcessWindows and the +// debug driver. This serves to decouple ProcessWindows from the debug driver. +// It would be possible to get a similar decoupling by just having +// ProcessWindows 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 also +// also 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::ProcessSP 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: + lldb::ProcessSP m_process; +}; +} + +#endif Index: source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp @@ -0,0 +1,73 @@ +//===-- 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 "ProcessWindows.h" + +using namespace lldb; +using namespace lldb_private; + +LocalDebugDelegate::LocalDebugDelegate(ProcessSP process) + : m_process(process) +{ +} + +void +LocalDebugDelegate::OnExitProcess(uint32_t exit_code) +{ + ((ProcessWindows &)*m_process).OnExitProcess(exit_code); +} + +void +LocalDebugDelegate::OnDebuggerConnected(lldb::addr_t image_base) +{ + ((ProcessWindows &)*m_process).OnDebuggerConnected(image_base); +} + +ExceptionResult +LocalDebugDelegate::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + return ((ProcessWindows &)*m_process).OnDebugException(first_chance, record); +} + +void +LocalDebugDelegate::OnCreateThread(const HostThread &thread) +{ + ((ProcessWindows &)*m_process).OnCreateThread(thread); +} + +void +LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + ((ProcessWindows &)*m_process).OnExitThread(thread_id, exit_code); +} + +void +LocalDebugDelegate::OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + ((ProcessWindows &)*m_process).OnLoadDll(module_spec, module_addr); +} + +void +LocalDebugDelegate::OnUnloadDll(lldb::addr_t module_addr) +{ + ((ProcessWindows &)*m_process).OnUnloadDll(module_addr); +} + +void +LocalDebugDelegate::OnDebugString(const std::string &string) +{ + ((ProcessWindows &)*m_process).OnDebugString(string); +} + +void +LocalDebugDelegate::OnDebuggerError(const Error &error, uint32_t type) +{ + ((ProcessWindows &)*m_process).OnDebuggerError(error, type); +} Index: source/Plugins/Process/Windows/Live/ProcessWindows.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/ProcessWindows.h @@ -0,0 +1,127 @@ +//===-- ProcessWindows.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_ProcessWindows_H_ +#define liblldb_Plugins_Process_Windows_ProcessWindows_H_ + +// C Includes + +// C++ Includes +#include +#include + +// 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" + +class ProcessMonitor; + +namespace lldb_private +{ +class HostProcess; +class ProcessWindowsData; +} + +class ProcessWindows : public lldb_private::Process, public lldb_private::IDebugDelegate +{ +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance(lldb_private::Target& target, + 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 + //------------------------------------------------------------------ + ProcessWindows(lldb_private::Target& target, + lldb_private::Listener &listener); + + ~ProcessWindows(); + + // lldb_private::Process overrides + lldb_private::ConstString GetPluginName() override; + uint32_t GetPluginVersion() override; + + size_t GetSTDOUT(char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t GetSTDERR(char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t PutSTDIN(const char *buf, size_t buf_size, lldb_private::Error &error) 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; + lldb::addr_t GetImageInfoAddress() override; + + bool CanDebug(lldb_private::Target &target, 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 m_session_data; +}; + +#endif // liblldb_Plugins_Process_Windows_ProcessWindows_H_ Index: source/Plugins/Process/Windows/Live/ProcessWindows.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/ProcessWindows.cpp @@ -0,0 +1,1016 @@ +//===-- ProcessWindows.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 + +// C++ Includes +#include +#include +#include +#include + +// 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/live/ProcessWindowsLog.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "LocalDebugDelegate.h" +#include "ProcessWindows.h" +#include "TargetThreadWindows.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 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 m_new_threads; + std::set m_exited_threads; +}; +} +//------------------------------------------------------------------------------ +// Static functions. + +ProcessSP +ProcessWindows::CreateInstance(Target &target, Listener &listener, const FileSpec *) +{ + return ProcessSP(new ProcessWindows(target, listener)); +} + +void +ProcessWindows::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindows::ProcessWindows(Target &target, Listener &listener) + : lldb_private::Process(target, listener) +{ +} + +ProcessWindows::~ProcessWindows() +{ +} + +void +ProcessWindows::Terminate() +{ +} + +lldb_private::ConstString +ProcessWindows::GetPluginNameStatic() +{ + static ConstString g_name("windows"); + return g_name; +} + +const char * +ProcessWindows::GetPluginDescriptionStatic() +{ + return "Process plugin for Windows"; +} + +size_t +ProcessWindows::GetSTDOUT(char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("GetSTDOUT unsupported on Windows"); + return 0; +} + +size_t +ProcessWindows::GetSTDERR(char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("GetSTDERR unsupported on Windows"); + return 0; +} + +size_t +ProcessWindows::PutSTDIN(const char *buf, size_t buf_size, Error &error) +{ + error.SetErrorString("PutSTDIN unsupported on Windows"); + return 0; +} + +Error +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::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 TargetThreadWindows(*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 +ProcessWindows::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 +ProcessWindows::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(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 +ProcessWindows::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 +ProcessWindows::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( + 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 +ProcessWindows::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWindows::GetPluginVersion() +{ + return 1; +} + +Error +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::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 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 +ProcessWindows::IsAlive() +{ + StateType state = GetPrivateState(); + switch (state) + { + case eStateCrashed: + case eStateDetached: + case eStateUnloaded: + case eStateExited: + case eStateInvalid: + return false; + default: + return true; + } +} + +Error +ProcessWindows::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 ProcessWindows::DidLaunch() +{ + ArchSpec arch_spec; + DidAttach(arch_spec); +} + +void +ProcessWindows::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 +ProcessWindows::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(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 +ProcessWindows::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(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 +ProcessWindows::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(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; + } + bool readable = !(mem_info.Protect & PAGE_NOACCESS); + bool executable = mem_info.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); + bool writable = mem_info.Protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY); + 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; +} + +lldb::addr_t +ProcessWindows::GetImageInfoAddress() +{ + Target &target = GetTarget(); + ObjectFile *obj_file = target.GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(&target); + if (addr.IsValid()) + return addr.GetLoadAddress(&target); + else + return LLDB_INVALID_ADDRESS; +} + +bool +ProcessWindows::CanDebug(Target &target, 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.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 +ProcessWindows::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); + + ModuleSP executable_module = GetTarget().GetExecutableModule(); + ModuleList unloaded_modules; + unloaded_modules.Append(executable_module); + GetTarget().ModulesDidUnload(unloaded_modules, true); + + SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); + SetPrivateState(eStateExited); +} + +void +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::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 +ProcessWindows::OnDebugString(const std::string &string) +{ +} + +void +ProcessWindows::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; + } +} \ No newline at end of file Index: source/Plugins/Process/Windows/Live/ProcessWindowsLog.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/ProcessWindowsLog.h @@ -0,0 +1,96 @@ +//===-- ProcessWindowsLog.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_ProcessWindowsLog_h_ +#define liblldb_ProcessWindowsLog_h_ + +#include "lldb/Core/Log.h" + +#define WINDOWS_LOG_VERBOSE (1u << 0) +#define WINDOWS_LOG_PROCESS (1u << 1) // Log process operations +#define WINDOWS_LOG_EXCEPTION (1u << 1) // Log exceptions +#define WINDOWS_LOG_THREAD (1u << 2) // Log thread operations +#define WINDOWS_LOG_MEMORY (1u << 3) // Log memory reads/writes calls +#define WINDOWS_LOG_BREAKPOINTS (1u << 4) // Log breakpoint operations +#define WINDOWS_LOG_STEP (1u << 5) // Log step operations +#define WINDOWS_LOG_REGISTERS (1u << 6) // Log register operations +#define WINDOWS_LOG_EVENT (1u << 7) // Low level debug events +#define WINDOWS_LOG_ALL (UINT32_MAX) + +enum class LogMaskReq +{ + All, + Any +}; + +class ProcessWindowsLog +{ + static const char *m_pluginname; + +public: + // --------------------------------------------------------------------- + // Public Static Methods + // --------------------------------------------------------------------- + static void + Initialize(); + + static void + Terminate(); + + static void + RegisterPluginName(const char *pluginName) + { + m_pluginname = pluginName; + } + + static void + RegisterPluginName(lldb_private::ConstString pluginName) + { + m_pluginname = pluginName.GetCString(); + } + + static bool + TestLogFlags(uint32_t mask, LogMaskReq req); + + static lldb_private::Log * + GetLog(); + + static void + DisableLog(const char **args, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog(lldb::StreamSP &log_stream_sp, uint32_t log_options, + const char **args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories(lldb_private::Stream *strm); +}; + +#define WINLOGF_IF(Flags, Req, Method, ...) \ + { \ + if (ProcessWindowsLog::TestLogFlags(Flags, Req)) \ + { \ + Log *log = ProcessWindowsLog::GetLog(); \ + if (log) \ + log->Method(__VA_ARGS__); \ + } \ + } + +#define WINLOG_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Printf, __VA_ARGS__) +#define WINLOG_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Printf, __VA_ARGS__) +#define WINLOGV_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Verbose, __VA_ARGS__) +#define WINLOGV_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Verbose, __VA_ARGS__) +#define WINLOGD_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Debug, __VA_ARGS__) +#define WINLOGD_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Debug, __VA_ARGS__) +#define WINERR_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Error, __VA_ARGS__) +#define WINERR_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Error, __VA_ARGS__) +#define WINWARN_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Warning, __VA_ARGS__) +#define WINWARN_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Warning, __VA_ARGS__) + +#endif // liblldb_ProcessWindowsLog_h_ Index: source/Plugins/Process/Windows/Live/ProcessWindowsLog.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/ProcessWindowsLog.cpp @@ -0,0 +1,190 @@ +//===-- ProcessWindowsLog.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWindowsLog.h" + +#include + +#include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/Args.h" +#include "llvm/Support/ManagedStatic.h" + +#include "ProcessWindows.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = nullptr; + +static llvm::ManagedStatic g_once_flag; + +void +ProcessWindowsLog::Initialize() +{ + static ConstString g_name("windows"); + + std::call_once(*g_once_flag, [](){ + Log::Callbacks log_callbacks = { + DisableLog, + EnableLog, + ListLogCategories + }; + + Log::RegisterLogChannel(g_name, log_callbacks); + RegisterPluginName(g_name); + }); +} + +void +ProcessWindowsLog::Terminate() +{ +} + +Log * +ProcessWindowsLog::GetLog() +{ + return (g_log_enabled) ? g_log : nullptr; +} + +bool +ProcessWindowsLog::TestLogFlags(uint32_t mask, LogMaskReq req) +{ + Log *log = GetLog(); + if (!log) + return false; + + uint32_t log_mask = log->GetMask().Get(); + if (req == LogMaskReq::All) + return ((log_mask & mask) == mask); + else + return (log_mask & mask); +} + +static uint32_t +GetFlagBits(const char *arg) +{ + if (::strcasecmp(arg, "all") == 0 ) return WINDOWS_LOG_ALL; + else if (::strcasecmp(arg, "break") == 0 ) return WINDOWS_LOG_BREAKPOINTS; + else if (::strcasecmp(arg, "event") == 0 ) return WINDOWS_LOG_EVENT; + else if (::strcasecmp(arg, "exception") == 0 ) return WINDOWS_LOG_EXCEPTION; + else if (::strcasecmp(arg, "memory") == 0 ) return WINDOWS_LOG_MEMORY; + else if (::strcasecmp(arg, "process") == 0 ) return WINDOWS_LOG_PROCESS; + else if (::strcasecmp(arg, "registers") == 0 ) return WINDOWS_LOG_REGISTERS; + else if (::strcasecmp(arg, "step") == 0 ) return WINDOWS_LOG_STEP; + else if (::strcasecmp(arg, "thread") == 0 ) return WINDOWS_LOG_THREAD; + else if (::strcasecmp(arg, "verbose") == 0 ) return WINDOWS_LOG_VERBOSE; + return 0; +} + +void +ProcessWindowsLog::DisableLog(const char **args, Stream *feedback_strm) +{ + Log *log (GetLog()); + if (log) + { + uint32_t flag_bits = 0; + + flag_bits = log->GetMask().Get(); + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits &= ~bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories(feedback_strm); + } + } + + log->GetMask().Reset(flag_bits); + if (flag_bits == 0) + g_log_enabled = false; + } + + return; +} + +Log * +ProcessWindowsLog::EnableLog(StreamSP &log_stream_sp, uint32_t log_options, const char **args, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits |= bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = WINDOWS_LOG_ALL; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + g_log_enabled = true; + } + return g_log; +} + +void +ProcessWindowsLog::ListLogCategories(Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " break - log breakpoints\n" + " event - log low level debugger events\n" + " exception - log exception information\n" + " memory - log memory reads and writes\n" + " process - log process events and activities\n" + " registers - log register read/writes\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n", + ProcessWindowsLog::m_pluginname); +} + +const char *ProcessWindowsLog::m_pluginname = ""; Index: source/Plugins/Process/Windows/Live/RegisterContextWindows.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/RegisterContextWindows.h @@ -0,0 +1,69 @@ +//===-- RegisterContextWindows.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_RegisterContextWindows_H_ +#define liblldb_RegisterContextWindows_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Target/RegisterContext.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows : public lldb_private::RegisterContext +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + void InvalidateAllRegisters() override; + + bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + + //------------------------------------------------------------------ + // Subclasses can override these functions if desired + //------------------------------------------------------------------ + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + bool HardwareSingleStep(bool enable) override; + + protected: + bool CacheAllRegisterValues(); + + CONTEXT m_context; + + private: + bool m_context_stale; +}; +} + +#endif // #ifndef liblldb_RegisterContextPOSIX_x86_H_ Index: source/Plugins/Process/Windows/Live/RegisterContextWindows.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/RegisterContextWindows.cpp @@ -0,0 +1,155 @@ +//===-- RegisterContextWindows.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/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "ProcessWindowsLog.h" +#include "RegisterContextWindows.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +const DWORD kWinContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows::RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx) + , m_context() + , m_context_stale(true) +{ +} + +RegisterContextWindows::~RegisterContextWindows() +{ +} + +void +RegisterContextWindows::InvalidateAllRegisters() +{ + m_context_stale = true; +} + +bool +RegisterContextWindows::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + if (!CacheAllRegisterValues()) + return false; + if (data_sp->GetByteSize() < sizeof(m_context)) + { + data_sp.reset(new DataBufferHeap(sizeof(CONTEXT), 0)); + } + memcpy(data_sp->GetBytes(), &m_context, sizeof(m_context)); + return true; +} + +bool +RegisterContextWindows::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + assert(data_sp->GetByteSize() >= sizeof(m_context)); + memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context)); + + TargetThreadWindows &wthread = static_cast(m_thread); + if (!::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) + return false; + + return true; +} + +uint32_t +RegisterContextWindows::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert(kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + +//------------------------------------------------------------------ +// Subclasses can these functions if desired +//------------------------------------------------------------------ +uint32_t +RegisterContextWindows::NumSupportedHardwareBreakpoints() +{ + // Support for hardware breakpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindows::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) +{ + return 0; +} + +bool +RegisterContextWindows::ClearHardwareBreakpoint(uint32_t hw_idx) +{ + return false; +} + +uint32_t +RegisterContextWindows::NumSupportedHardwareWatchpoints() +{ + // Support for hardware watchpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindows::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) +{ + return 0; +} + +bool +RegisterContextWindows::ClearHardwareWatchpoint(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextWindows::HardwareSingleStep(bool enable) +{ + return false; +} + +bool +RegisterContextWindows::CacheAllRegisterValues() +{ + if (!m_context_stale) + return true; + + TargetThreadWindows &wthread = static_cast(m_thread); + memset(&m_context, 0, sizeof(m_context)); + m_context.ContextFlags = kWinContextFlags; + if (!::GetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) + { + WINERR_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext failed with error %u while caching register values.", + ::GetLastError()); + return false; + } + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext successfully updated the register values.", ::GetLastError()); + m_context_stale = false; + return true; +} Index: source/Plugins/Process/Windows/Live/TargetThreadWindows.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/TargetThreadWindows.h @@ -0,0 +1,57 @@ +//===-- TargetThreadWindows.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_TargetThreadWindows_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindows_H_ + +#include "ForwardDecl.h" +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" +class ProcessWindows; + +namespace lldb_private +{ + +class HostThread; +class StackFrame; + +class TargetThreadWindows : public lldb_private::Thread +{ + public: + TargetThreadWindows(ProcessWindows &process, const HostThread &thread); + virtual ~TargetThreadWindows(); + + // 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); + + lldb::StackFrameUP m_stack_frame; + + HostThread m_host_thread; +}; +} + +#endif \ No newline at end of file Index: source/Plugins/Process/Windows/Live/TargetThreadWindows.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/TargetThreadWindows.cpp @@ -0,0 +1,147 @@ +//===-- TargetThreadWindows.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 "TargetThreadWindows.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +#if defined(_WIN64) +#include "x64/RegisterContextWindows_x64.h" +#else +#include "x86/RegisterContextWindows_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindows::TargetThreadWindows(ProcessWindows &process, const HostThread &thread) + : Thread(process, thread.GetNativeThread().GetThreadId()) + , m_host_thread(thread) +{ +} + +TargetThreadWindows::~TargetThreadWindows() +{ + DestroyThread(); +} + +void +TargetThreadWindows::RefreshStateAfterStop() +{ + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + SetState(eStateStopped); + GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindows::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindows::DidStop() +{ +} + +RegisterContextSP +TargetThreadWindows::GetRegisterContext() +{ + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrameIndex(0); + + return m_reg_context_sp; +} + +RegisterContextSP +TargetThreadWindows::CreateRegisterContextForFrame(StackFrame *frame) +{ + return CreateRegisterContextForFrameIndex(frame->GetConcreteFrameIndex()); +} + +RegisterContextSP +TargetThreadWindows::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 RegisterContextWindows_x86(*this, idx)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + m_reg_context_sp.reset(new RegisterContextWindows_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 +TargetThreadWindows::CalculateStopInfo() +{ + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind * +TargetThreadWindows::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 +TargetThreadWindows::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; +} Index: source/Plugins/Process/Windows/Live/x64/RegisterContextWindows_x64.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/x64/RegisterContextWindows_x64.h @@ -0,0 +1,48 @@ +//===-- RegisterContextWindows_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_RegisterContextWindows_x64_H_ +#define liblldb_RegisterContextWindows_x64_H_ + +#include "lldb/lldb-forward.h" +#include "RegisterContextWindows.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows_x64 : public RegisterContextWindows +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows_x64(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextPOSIX_x64_H_ Index: source/Plugins/Process/Windows/Live/x64/RegisterContextWindows_x64.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/x64/RegisterContextWindows_x64.cpp @@ -0,0 +1,323 @@ +//===-- RegisterContextWindows_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 "RegisterContext_x86.h" +#include "RegisterContextWindows_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFINE_GPR(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatHexUppercase +#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatBinary + +namespace +{ + +// This enum defines the layout of the global RegisterInfo array. This is necessary because +// lldb register sets are defined in terms of indices into the register array. As such, the +// order of RegisterInfos defined in global registers array must match the order defined here. +// When defining the register set layouts, these values can appear in an arbitrary order, and that +// determines the order that register values are displayed in a dump. +enum RegisterIndex +{ + eRegisterIndexRax, + eRegisterIndexRbx, + eRegisterIndexRcx, + eRegisterIndexRdx, + eRegisterIndexRdi, + eRegisterIndexRsi, + eRegisterIndexR8, + eRegisterIndexR9, + eRegisterIndexR10, + eRegisterIndexR11, + eRegisterIndexR12, + eRegisterIndexR13, + eRegisterIndexR14, + eRegisterIndexR15, + eRegisterIndexRbp, + eRegisterIndexRsp, + eRegisterIndexRip, + eRegisterIndexRflags +}; + +// Array of all register information supported by Windows x86 +RegisterInfo g_register_infos[] = { + // Macro auto defines most stuff GCC DWARF GENERIC + // GDB LLDB VALUE REGS INVALIDATE REGS + // ================================ ========================= ====================== ========================= + // =================== ================= ========== =============== + {DEFINE_GPR(rax, nullptr), + {gcc_dwarf_rax_x86_64, gcc_dwarf_rax_x86_64, LLDB_INVALID_REGNUM, gdb_rax_x86_64, lldb_rax_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbx, nullptr), + {gcc_dwarf_rbx_x86_64, gcc_dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, gdb_rbx_x86_64, lldb_rbx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rcx, nullptr), + {gcc_dwarf_rcx_x86_64, gcc_dwarf_rcx_x86_64, LLDB_INVALID_REGNUM, gdb_rcx_x86_64, lldb_rcx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdx, nullptr), + {gcc_dwarf_rdx_x86_64, gcc_dwarf_rdx_x86_64, LLDB_INVALID_REGNUM, gdb_rdx_x86_64, lldb_rdx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdi, nullptr), + {gcc_dwarf_rdi_x86_64, gcc_dwarf_rdi_x86_64, LLDB_INVALID_REGNUM, gdb_rdi_x86_64, lldb_rdi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsi, nullptr), + {gcc_dwarf_rsi_x86_64, gcc_dwarf_rsi_x86_64, LLDB_INVALID_REGNUM, gdb_rsi_x86_64, lldb_rsi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r8, nullptr), + {gcc_dwarf_r8_x86_64, gcc_dwarf_r8_x86_64, LLDB_INVALID_REGNUM, gdb_r8_x86_64, lldb_r8_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r9, nullptr), + {gcc_dwarf_r9_x86_64, gcc_dwarf_r9_x86_64, LLDB_INVALID_REGNUM, gdb_r9_x86_64, lldb_r9_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r10, nullptr), + {gcc_dwarf_r10_x86_64, gcc_dwarf_r10_x86_64, LLDB_INVALID_REGNUM, gdb_r10_x86_64, lldb_r10_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r11, nullptr), + {gcc_dwarf_r11_x86_64, gcc_dwarf_r11_x86_64, LLDB_INVALID_REGNUM, gdb_r11_x86_64, lldb_r11_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r12, nullptr), + {gcc_dwarf_r12_x86_64, gcc_dwarf_r12_x86_64, LLDB_INVALID_REGNUM, gdb_r12_x86_64, lldb_r12_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r13, nullptr), + {gcc_dwarf_r13_x86_64, gcc_dwarf_r13_x86_64, LLDB_INVALID_REGNUM, gdb_r13_x86_64, lldb_r13_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r14, nullptr), + {gcc_dwarf_r14_x86_64, gcc_dwarf_r14_x86_64, LLDB_INVALID_REGNUM, gdb_r14_x86_64, lldb_r14_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r15, nullptr), + {gcc_dwarf_r15_x86_64, gcc_dwarf_r15_x86_64, LLDB_INVALID_REGNUM, gdb_r15_x86_64, lldb_r15_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbp, "fp"), + {gcc_dwarf_rbp_x86_64, gcc_dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, gdb_rbp_x86_64, lldb_rbp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsp, "sp"), + {gcc_dwarf_rsp_x86_64, gcc_dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, gdb_rsp_x86_64, lldb_rsp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rip, "pc"), + {gcc_dwarf_rip_x86_64, gcc_dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, gdb_rip_x86_64, lldb_rip_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR_BIN(eflags, "flags"), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_rflags_x86_64, lldb_rflags_x86_64}, + nullptr, + nullptr}, +}; + +// Array of lldb register numbers used to define the set of all General Purpose Registers +uint32_t g_gpr_reg_indices[] = {eRegisterIndexRax, eRegisterIndexRbx, eRegisterIndexRcx, eRegisterIndexRdx, + eRegisterIndexRdi, eRegisterIndexRsi, eRegisterIndexR8, eRegisterIndexR9, + eRegisterIndexR10, eRegisterIndexR11, eRegisterIndexR12, eRegisterIndexR13, + eRegisterIndexR14, eRegisterIndexR15, eRegisterIndexRbp, eRegisterIndexRsp, + eRegisterIndexRip, eRegisterIndexRflags}; + +RegisterSet g_register_sets[] = { + {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, +}; +} + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows_x64::RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows(thread, concrete_frame_idx) +{ +} + +RegisterContextWindows_x64::~RegisterContextWindows_x64() +{ +} + +size_t +RegisterContextWindows_x64::GetRegisterCount() +{ + return llvm::array_lengthof(g_register_infos); +} + +const RegisterInfo * +RegisterContextWindows_x64::GetRegisterInfoAtIndex(size_t reg) +{ + return &g_register_infos[reg]; +} + +size_t +RegisterContextWindows_x64::GetRegisterSetCount() +{ + return llvm::array_lengthof(g_register_sets); +} + +const RegisterSet * +RegisterContextWindows_x64::GetRegisterSet(size_t reg_set) +{ + return &g_register_sets[reg_set]; +} + +bool +RegisterContextWindows_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 +RegisterContextWindows_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(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} Index: source/Plugins/Process/Windows/Live/x86/RegisterContextWindows_x86.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/x86/RegisterContextWindows_x86.h @@ -0,0 +1,48 @@ +//===-- RegisterContextWindows_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_RegisterContextWindows_x86_H_ +#define liblldb_RegisterContextWindows_x86_H_ + +#include "lldb/lldb-forward.h" +#include "RegisterContextWindows.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindows_x86 : public RegisterContextWindows +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindows_x86(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextPOSIX_x86_H_ Index: source/Plugins/Process/Windows/Live/x86/RegisterContextWindows_x86.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/Live/x86/RegisterContextWindows_x86.cpp @@ -0,0 +1,241 @@ +//===-- RegisterContextWindows_x86.h ----------------------------*- 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 "RegisterContext_x86.h" +#include "RegisterContextWindows_x86.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFINE_GPR(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatHexUppercase +#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatBinary + +namespace +{ + +// This enum defines the layout of the global RegisterInfo array. This is necessary because +// lldb register sets are defined in terms of indices into the register array. As such, the +// order of RegisterInfos defined in global registers array must match the order defined here. +// When defining the register set layouts, these values can appear in an arbitrary order, and that +// determines the order that register values are displayed in a dump. +enum RegisterIndex +{ + eRegisterIndexEax, + eRegisterIndexEbx, + eRegisterIndexEcx, + eRegisterIndexEdx, + eRegisterIndexEdi, + eRegisterIndexEsi, + eRegisterIndexEbp, + eRegisterIndexEsp, + eRegisterIndexEip, + eRegisterIndexEflags +}; + +// Array of all register information supported by Windows x86 +RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// ============================== ======================= =================== ========================= =================== ================= ========== =============== + { DEFINE_GPR(eax, nullptr), { gcc_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_eax_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebx, nullptr), { gcc_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ebx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ecx, nullptr), { gcc_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ecx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edx, nullptr), { gcc_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edi, nullptr), { gcc_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esi, nullptr), { gcc_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_esi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebp, "fp"), { gcc_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_ebp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esp, "sp"), { gcc_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_esp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(eip, "pc"), { gcc_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_eip_i386 }, nullptr, nullptr}, + { DEFINE_GPR_BIN(eflags, "flags"), { gcc_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_eflags_i386}, nullptr, nullptr}, +}; + +// Array of lldb register numbers used to define the set of all General Purpose Registers +uint32_t g_gpr_reg_indices[] = +{ + eRegisterIndexEax, + eRegisterIndexEbx, + eRegisterIndexEcx, + eRegisterIndexEdx, + eRegisterIndexEdi, + eRegisterIndexEsi, + eRegisterIndexEbp, + eRegisterIndexEsp, + eRegisterIndexEip, + eRegisterIndexEflags +}; + +RegisterSet g_register_sets[] = { + {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, +}; +} + +//------------------------------------------------------------------ +// Constructors and Destructors +//------------------------------------------------------------------ +RegisterContextWindows_x86::RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows(thread, concrete_frame_idx) +{ +} + +RegisterContextWindows_x86::~RegisterContextWindows_x86() +{ +} + +size_t +RegisterContextWindows_x86::GetRegisterCount() +{ + return llvm::array_lengthof(g_register_infos); +} + +const RegisterInfo * +RegisterContextWindows_x86::GetRegisterInfoAtIndex(size_t reg) +{ + return &g_register_infos[reg]; +} + +size_t +RegisterContextWindows_x86::GetRegisterSetCount() +{ + return llvm::array_lengthof(g_register_sets); +} + +const RegisterSet * +RegisterContextWindows_x86::GetRegisterSet(size_t reg_set) +{ + return &g_register_sets[reg_set]; +} + +bool +RegisterContextWindows_x86::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + switch (reg) + { + case lldb_eax_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EAX", m_context.Eax); + reg_value.SetUInt32(m_context.Eax); + break; + case lldb_ebx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBX", m_context.Ebx); + reg_value.SetUInt32(m_context.Ebx); + break; + case lldb_ecx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ECX", m_context.Ecx); + reg_value.SetUInt32(m_context.Ecx); + break; + case lldb_edx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDX", m_context.Edx); + reg_value.SetUInt32(m_context.Edx); + break; + case lldb_edi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDI", m_context.Edi); + reg_value.SetUInt32(m_context.Edi); + break; + case lldb_esi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESI", m_context.Esi); + reg_value.SetUInt32(m_context.Esi); + break; + case lldb_ebp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBP", m_context.Ebp); + reg_value.SetUInt32(m_context.Ebp); + break; + case lldb_esp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESP", m_context.Esp); + reg_value.SetUInt32(m_context.Esp); + break; + case lldb_eip_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EIP", m_context.Eip); + reg_value.SetUInt32(m_context.Eip); + break; + case lldb_eflags_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EFLAGS", m_context.EFlags); + reg_value.SetUInt32(m_context.EFlags); + break; + default: + WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Requested unknown register %u", reg); + break; + } + return true; +} + +bool +RegisterContextWindows_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(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} Index: source/Plugins/Process/Windows/LocalDebugDelegate.h =================================================================== --- source/Plugins/Process/Windows/LocalDebugDelegate.h +++ /dev/null @@ -1,60 +0,0 @@ -//===-- 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 "IDebugDelegate.h" -#include "lldb/lldb-forward.h" - -class ProcessWindows; - -namespace lldb_private -{ -//---------------------------------------------------------------------- -// LocalDebugDelegate -// -// LocalDebugDelegate creates a connection between a ProcessWindows and the -// debug driver. This serves to decouple ProcessWindows from the debug driver. -// It would be possible to get a similar decoupling by just having -// ProcessWindows 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 also -// also 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::ProcessSP 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: - lldb::ProcessSP m_process; -}; -} - -#endif Index: source/Plugins/Process/Windows/LocalDebugDelegate.cpp =================================================================== --- source/Plugins/Process/Windows/LocalDebugDelegate.cpp +++ /dev/null @@ -1,73 +0,0 @@ -//===-- 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 "ProcessWindows.h" - -using namespace lldb; -using namespace lldb_private; - -LocalDebugDelegate::LocalDebugDelegate(ProcessSP process) - : m_process(process) -{ -} - -void -LocalDebugDelegate::OnExitProcess(uint32_t exit_code) -{ - ((ProcessWindows &)*m_process).OnExitProcess(exit_code); -} - -void -LocalDebugDelegate::OnDebuggerConnected(lldb::addr_t image_base) -{ - ((ProcessWindows &)*m_process).OnDebuggerConnected(image_base); -} - -ExceptionResult -LocalDebugDelegate::OnDebugException(bool first_chance, const ExceptionRecord &record) -{ - return ((ProcessWindows &)*m_process).OnDebugException(first_chance, record); -} - -void -LocalDebugDelegate::OnCreateThread(const HostThread &thread) -{ - ((ProcessWindows &)*m_process).OnCreateThread(thread); -} - -void -LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) -{ - ((ProcessWindows &)*m_process).OnExitThread(thread_id, exit_code); -} - -void -LocalDebugDelegate::OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) -{ - ((ProcessWindows &)*m_process).OnLoadDll(module_spec, module_addr); -} - -void -LocalDebugDelegate::OnUnloadDll(lldb::addr_t module_addr) -{ - ((ProcessWindows &)*m_process).OnUnloadDll(module_addr); -} - -void -LocalDebugDelegate::OnDebugString(const std::string &string) -{ - ((ProcessWindows &)*m_process).OnDebugString(string); -} - -void -LocalDebugDelegate::OnDebuggerError(const Error &error, uint32_t type) -{ - ((ProcessWindows &)*m_process).OnDebuggerError(error, type); -} Index: source/Plugins/Process/Windows/MiniDump/CMakeLists.txt =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(../../Utility) +include_directories(../Common) + +add_lldb_library(lldbPluginProcessWinMiniDump + ProcessWinMiniDump.cpp + RegisterContextWindowsMiniDump.cpp + ThreadWinMiniDump.cpp + ) Index: source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h @@ -0,0 +1,116 @@ +//===-- ProcessWinMiniDump.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_ProcessWinMiniDump_h_ +#define liblldb_ProcessWinMiniDump_h_ + +#include +#include + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +struct ThreadData; + +class ProcessWinMiniDump : public lldb_private::Process +{ +public: + static lldb::ProcessSP + CreateInstance (lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + ProcessWinMiniDump(lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + virtual + ~ProcessWinMiniDump(); + + bool + CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override; + + lldb_private::Error + DoLoadCore() override; + + lldb_private::DynamicLoader * + GetDynamicLoader() override; + + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + + lldb_private::Error + DoDestroy() override; + + void + RefreshStateAfterStop() override; + + bool + IsAlive() override; + + size_t + ReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + size_t + DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + + lldb::addr_t + GetImageInfoAddress() override; + + lldb_private::ArchSpec + GetArchitecture(); + +protected: + void + Clear(); + + bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + +private: + lldb_private::Error + MapMiniDumpIntoMemory(const char *file); + + lldb_private::ArchSpec + DetermineArchitecture(); + + void + ReadExceptionRecord(); + + // A thin wrapper around WinAPI's MiniDumpReadDumpStream to avoid redundant + // checks. If there's a failure (e.g., if the requested stream doesn't exist), + // the function returns nullptr and sets *size_out to 0. + void * + FindDumpStream(unsigned stream_number, size_t *size_out); + + // Isolate the data to keep Windows-specific types out of this header. Can't + // use the typical pimpl idiom because the implementation of this class also + // needs access to public and protected members of the base class. + class Data; + std::unique_ptr m_data_up; +}; + +#endif // liblldb_ProcessWinMiniDump_h_ Index: source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -0,0 +1,362 @@ +//===-- ProcessWinMiniDump.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWinMiniDump.h" + +#include "lldb/Host/windows/windows.h" +#include + +#include +#include + +#include + +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/UnixSignals.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" + +#include "ExceptionRecord.h" +#include "ThreadWinMiniDump.h" + +using namespace lldb_private; + +// Encapsulates the private data for ProcessWinMiniDump. +// TODO(amccarth): Determine if we need a mutex for access. +class ProcessWinMiniDump::Data +{ +public: + Data(); + ~Data(); + + FileSpec m_core_file; + HANDLE m_dump_file; // handle to the open minidump file + HANDLE m_mapping; // handle to the file mapping for the minidump file + void * m_base_addr; // base memory address of the minidump + std::shared_ptr m_exception_sp; +}; + +ConstString +ProcessWinMiniDump::GetPluginNameStatic() +{ + static ConstString g_name("win-minidump"); + return g_name; +} + +const char * +ProcessWinMiniDump::GetPluginDescriptionStatic() +{ + return "Windows minidump plug-in."; +} + +void +ProcessWinMiniDump::Terminate() +{ + PluginManager::UnregisterPlugin(ProcessWinMiniDump::CreateInstance); +} + + +lldb::ProcessSP +ProcessWinMiniDump::CreateInstance(Target &target, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + process_sp.reset(new ProcessWinMiniDump(target, listener, *crash_file)); + } + return process_sp; +} + +bool +ProcessWinMiniDump::CanDebug(Target &target, bool plugin_specified_by_name) +{ + // TODO(amccarth): Eventually, this needs some actual logic. + return true; +} + +ProcessWinMiniDump::ProcessWinMiniDump(Target& target, Listener &listener, + const FileSpec &core_file) : + Process(target, listener), + m_data_up(new Data) +{ + m_data_up->m_core_file = core_file; +} + +ProcessWinMiniDump::~ProcessWinMiniDump() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +ConstString +ProcessWinMiniDump::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWinMiniDump::GetPluginVersion() +{ + return 1; +} + + +Error +ProcessWinMiniDump::DoLoadCore() +{ + Error error; + + error = MapMiniDumpIntoMemory(m_data_up->m_core_file.GetCString()); + if (error.Fail()) + { + return error; + } + + m_target.SetArchitecture(DetermineArchitecture()); + // TODO(amccarth): Build the module list. + + ReadExceptionRecord(); + + return error; + +} + +DynamicLoader * +ProcessWinMiniDump::GetDynamicLoader() +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderWindowsDYLD::GetPluginNameStatic().GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + size_t size = 0; + auto thread_list_ptr = static_cast(FindDumpStream(ThreadListStream, &size)); + if (thread_list_ptr) + { + const ULONG32 thread_count = thread_list_ptr->NumberOfThreads; + for (ULONG32 i = 0; i < thread_count; ++i) { + std::shared_ptr thread_sp(new ThreadWinMiniDump(*this, thread_list_ptr->Threads[i].ThreadId)); + new_thread_list.AddThread(thread_sp); + } + } + + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessWinMiniDump::RefreshStateAfterStop() +{ + if (!m_data_up) return; + if (!m_data_up->m_exception_sp) return; + + auto active_exception = m_data_up->m_exception_sp; + 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(active_exception->GetExceptionAddress(), 8); + m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + auto stop_thread = m_thread_list.GetSelectedThread(); + auto stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); + stop_thread->SetStopInfo(stop_info); +} + +Error +ProcessWinMiniDump::DoDestroy() +{ + return Error(); +} + +bool +ProcessWinMiniDump::IsAlive() +{ + return true; +} + +size_t +ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t +ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // TODO + return 0; +} + +void +ProcessWinMiniDump::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessWinMiniDump::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +lldb::addr_t +ProcessWinMiniDump::GetImageInfoAddress() +{ + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(target); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + return LLDB_INVALID_ADDRESS; +} + +ArchSpec +ProcessWinMiniDump::GetArchitecture() +{ + // TODO + return ArchSpec(); +} + + +ProcessWinMiniDump::Data::Data() : + m_dump_file(INVALID_HANDLE_VALUE), + m_mapping(NULL), + m_base_addr(nullptr) +{ +} + +ProcessWinMiniDump::Data::~Data() +{ + if (m_base_addr) + { + ::UnmapViewOfFile(m_base_addr); + m_base_addr = nullptr; + } + if (m_mapping) + { + ::CloseHandle(m_mapping); + m_mapping = NULL; + } + if (m_dump_file != INVALID_HANDLE_VALUE) + { + ::CloseHandle(m_dump_file); + m_dump_file = INVALID_HANDLE_VALUE; + } +} + +Error +ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) +{ + Error error; + + m_data_up->m_dump_file = ::CreateFile(file, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (m_data_up->m_dump_file == INVALID_HANDLE_VALUE) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + m_data_up->m_mapping = ::CreateFileMapping(m_data_up->m_dump_file, NULL, + PAGE_READONLY, 0, 0, NULL); + if (m_data_up->m_mapping == NULL) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + m_data_up->m_base_addr = ::MapViewOfFile(m_data_up->m_mapping, FILE_MAP_READ, 0, 0, 0); + if (m_data_up->m_base_addr == NULL) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return error; + } + + return error; +} + + +ArchSpec +ProcessWinMiniDump::DetermineArchitecture() +{ + size_t size = 0; + auto system_info_ptr = static_cast(FindDumpStream(SystemInfoStream, &size)); + if (system_info_ptr) + { + switch (system_info_ptr->ProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_INTEL: + return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE); + case PROCESSOR_ARCHITECTURE_AMD64: + return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE); + default: + break; + } + } + + return ArchSpec(); // invalid or unknown +} + +void +ProcessWinMiniDump::ReadExceptionRecord() { + size_t size = 0; + auto exception_stream_ptr = static_cast(FindDumpStream(ExceptionStream, &size)); + if (exception_stream_ptr) + { + m_data_up->m_exception_sp.reset(new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId)); + } +} + +void * +ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) { + void *stream = nullptr; + *size_out = 0; + + assert(m_data_up != nullptr); + assert(m_data_up->m_base_addr != 0); + + MINIDUMP_DIRECTORY *dir = nullptr; + if (::MiniDumpReadDumpStream(m_data_up->m_base_addr, stream_number, &dir, nullptr, nullptr) && + dir != nullptr && dir->Location.DataSize > 0) + { + assert(dir->StreamType == stream_number); + *size_out = dir->Location.DataSize; + stream = static_cast(static_cast(m_data_up->m_base_addr) + dir->Location.Rva); + } + + return stream; +} Index: source/Plugins/Process/Windows/MiniDump/RegisterContextWindowsMiniDump.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/RegisterContextWindowsMiniDump.h @@ -0,0 +1,73 @@ +//===-- RegisterContextWindowsMiniDump.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_RegisterContextWindowsMiniDump_H_ +#define liblldb_RegisterContextWindowsMiniDump_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Target/RegisterContext.h" + + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump : public lldb_private::RegisterContext +{ + public: + RegisterContextWindowsMiniDump(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsMiniDump(); + + void + InvalidateAllRegisters() override; + + size_t + GetRegisterCount() override; + + const RegisterInfo * + GetRegisterInfoAtIndex(size_t reg) override; + + size_t + GetRegisterSetCount() override; + + const RegisterSet * + GetRegisterSet(size_t reg_set) override; + + bool + ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool + WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + bool HardwareSingleStep(bool enable) override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_H_ Index: source/Plugins/Process/Windows/MiniDump/RegisterContextWindowsMiniDump.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/RegisterContextWindowsMiniDump.cpp @@ -0,0 +1,146 @@ +//===-- RegisterContextWindowsMiniDump.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/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +// This is a do-nothing stub implementation for now. + +RegisterContextWindowsMiniDump::RegisterContextWindowsMiniDump(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsMiniDump::~RegisterContextWindowsMiniDump() +{ +} + +void +RegisterContextWindowsMiniDump::InvalidateAllRegisters() +{ +} + +size_t +RegisterContextWindowsMiniDump::GetRegisterCount() +{ + return 0; +} + +const RegisterInfo * +RegisterContextWindowsMiniDump::GetRegisterInfoAtIndex(size_t reg) +{ + return nullptr; +} + +size_t +RegisterContextWindowsMiniDump::GetRegisterSetCount() +{ + return 0; +} + +const RegisterSet * +RegisterContextWindowsMiniDump::GetRegisterSet(size_t reg_set) +{ + return nullptr; +} + +bool +RegisterContextWindowsMiniDump::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +uint32_t +RegisterContextWindowsMiniDump::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert(kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + +uint32_t +RegisterContextWindowsMiniDump::NumSupportedHardwareBreakpoints() +{ + // Support for hardware breakpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindowsMiniDump::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) +{ + return 0; +} + +bool +RegisterContextWindowsMiniDump::ClearHardwareBreakpoint(uint32_t hw_idx) +{ + return false; +} + +uint32_t +RegisterContextWindowsMiniDump::NumSupportedHardwareWatchpoints() +{ + // Support for hardware watchpoints not yet implemented. + return 0; +} + +uint32_t +RegisterContextWindowsMiniDump::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) +{ + return 0; +} + +bool +RegisterContextWindowsMiniDump::ClearHardwareWatchpoint(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump::HardwareSingleStep(bool enable) +{ + return false; +} Index: source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h @@ -0,0 +1,51 @@ +//===-- ThreadWinMiniDump.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_ThreadWinMiniDump_h_ +#define liblldb_ThreadWinMiniDump_h_ + +#include + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" + +class ThreadWinMiniDump : public lldb_private::Thread +{ +public: + ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid); + + virtual + ~ThreadWinMiniDump(); + + void + RefreshStateAfterStop() override; + + lldb::RegisterContextSP + GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + void + ClearStackFrames() override; + + const char * + GetName() override; + + void + SetName(const char *name); + +protected: + std::string m_thread_name; + lldb::RegisterContextSP m_reg_context_sp; + + bool CalculateStopInfo() override; +}; + +#endif Index: source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp @@ -0,0 +1,80 @@ +//===-- ThreadWinMiniDump.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ThreadWinMiniDump.h" + +// Windows includes +#include "lldb/Host/windows/windows.h" +#include + +#include "ProcessWinMiniDump.h" +#include "RegisterContextWindowsMiniDump.h" + +using namespace lldb; +using namespace lldb_private; + +// This is a minimal implementation in order to get something running. It will +// be fleshed out as more mini-dump functionality is added. + +ThreadWinMiniDump::ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name() +{ +} + +ThreadWinMiniDump::~ThreadWinMiniDump() +{ +} + +void +ThreadWinMiniDump::RefreshStateAfterStop() +{ +} + +lldb::RegisterContextSP +ThreadWinMiniDump::GetRegisterContext() +{ + if (m_reg_context_sp.get() == NULL) { + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadWinMiniDump::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) +{ + const uint32_t concrete_frame_idx = (frame) ? frame->GetConcreteFrameIndex() : 0; + RegisterContextSP reg_ctx_sp(new RegisterContextWindowsMiniDump(*this, concrete_frame_idx)); + return reg_ctx_sp; +} + +void +ThreadWinMiniDump::ClearStackFrames() +{ +} + +const char * +ThreadWinMiniDump::GetName() +{ + return m_thread_name.empty() ? nullptr : m_thread_name.c_str(); +} + +void +ThreadWinMiniDump::SetName(const char *name) +{ + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); +} + +bool ThreadWinMiniDump::CalculateStopInfo() +{ + return false; +} Index: source/Plugins/Process/Windows/ProcessWindows.h =================================================================== --- source/Plugins/Process/Windows/ProcessWindows.h +++ /dev/null @@ -1,127 +0,0 @@ -//===-- ProcessWindows.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_ProcessWindows_H_ -#define liblldb_Plugins_Process_Windows_ProcessWindows_H_ - -// C Includes - -// C++ Includes -#include -#include - -// 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" - -class ProcessMonitor; - -namespace lldb_private -{ -class HostProcess; -class ProcessWindowsData; -} - -class ProcessWindows : public lldb_private::Process, public lldb_private::IDebugDelegate -{ -public: - //------------------------------------------------------------------ - // Static functions. - //------------------------------------------------------------------ - static lldb::ProcessSP - CreateInstance(lldb_private::Target& target, - 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 - //------------------------------------------------------------------ - ProcessWindows(lldb_private::Target& target, - lldb_private::Listener &listener); - - ~ProcessWindows(); - - // lldb_private::Process overrides - lldb_private::ConstString GetPluginName() override; - uint32_t GetPluginVersion() override; - - size_t GetSTDOUT(char *buf, size_t buf_size, lldb_private::Error &error) override; - size_t GetSTDERR(char *buf, size_t buf_size, lldb_private::Error &error) override; - size_t PutSTDIN(const char *buf, size_t buf_size, lldb_private::Error &error) 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; - lldb::addr_t GetImageInfoAddress() override; - - bool CanDebug(lldb_private::Target &target, 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 m_session_data; -}; - -#endif // liblldb_Plugins_Process_Windows_ProcessWindows_H_ Index: source/Plugins/Process/Windows/ProcessWindows.cpp =================================================================== --- source/Plugins/Process/Windows/ProcessWindows.cpp +++ /dev/null @@ -1,1016 +0,0 @@ -//===-- ProcessWindows.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 - -// C++ Includes -#include -#include -#include -#include - -// 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/ProcessWindowsLog.h" - -#include "DebuggerThread.h" -#include "ExceptionRecord.h" -#include "LocalDebugDelegate.h" -#include "ProcessWindows.h" -#include "TargetThreadWindows.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 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 m_new_threads; - std::set m_exited_threads; -}; -} -//------------------------------------------------------------------------------ -// Static functions. - -ProcessSP -ProcessWindows::CreateInstance(Target &target, Listener &listener, const FileSpec *) -{ - return ProcessSP(new ProcessWindows(target, listener)); -} - -void -ProcessWindows::Initialize() -{ - static std::once_flag g_once_flag; - - std::call_once(g_once_flag, []() - { - PluginManager::RegisterPlugin(GetPluginNameStatic(), - GetPluginDescriptionStatic(), - CreateInstance); - }); -} - -//------------------------------------------------------------------------------ -// Constructors and destructors. - -ProcessWindows::ProcessWindows(Target &target, Listener &listener) - : lldb_private::Process(target, listener) -{ -} - -ProcessWindows::~ProcessWindows() -{ -} - -void -ProcessWindows::Terminate() -{ -} - -lldb_private::ConstString -ProcessWindows::GetPluginNameStatic() -{ - static ConstString g_name("windows"); - return g_name; -} - -const char * -ProcessWindows::GetPluginDescriptionStatic() -{ - return "Process plugin for Windows"; -} - -size_t -ProcessWindows::GetSTDOUT(char *buf, size_t buf_size, Error &error) -{ - error.SetErrorString("GetSTDOUT unsupported on Windows"); - return 0; -} - -size_t -ProcessWindows::GetSTDERR(char *buf, size_t buf_size, Error &error) -{ - error.SetErrorString("GetSTDERR unsupported on Windows"); - return 0; -} - -size_t -ProcessWindows::PutSTDIN(const char *buf, size_t buf_size, Error &error) -{ - error.SetErrorString("PutSTDIN unsupported on Windows"); - return 0; -} - -Error -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::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 TargetThreadWindows(*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 -ProcessWindows::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 -ProcessWindows::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(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 -ProcessWindows::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 -ProcessWindows::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( - 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 -ProcessWindows::GetPluginName() -{ - return GetPluginNameStatic(); -} - -uint32_t -ProcessWindows::GetPluginVersion() -{ - return 1; -} - -Error -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::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 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 -ProcessWindows::IsAlive() -{ - StateType state = GetPrivateState(); - switch (state) - { - case eStateCrashed: - case eStateDetached: - case eStateUnloaded: - case eStateExited: - case eStateInvalid: - return false; - default: - return true; - } -} - -Error -ProcessWindows::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 ProcessWindows::DidLaunch() -{ - ArchSpec arch_spec; - DidAttach(arch_spec); -} - -void -ProcessWindows::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 -ProcessWindows::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(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 -ProcessWindows::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(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 -ProcessWindows::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(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; - } - bool readable = !(mem_info.Protect & PAGE_NOACCESS); - bool executable = mem_info.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); - bool writable = mem_info.Protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY); - 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; -} - -lldb::addr_t -ProcessWindows::GetImageInfoAddress() -{ - Target &target = GetTarget(); - ObjectFile *obj_file = target.GetExecutableModule()->GetObjectFile(); - Address addr = obj_file->GetImageInfoAddress(&target); - if (addr.IsValid()) - return addr.GetLoadAddress(&target); - else - return LLDB_INVALID_ADDRESS; -} - -bool -ProcessWindows::CanDebug(Target &target, 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.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 -ProcessWindows::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); - - ModuleSP executable_module = GetTarget().GetExecutableModule(); - ModuleList unloaded_modules; - unloaded_modules.Append(executable_module); - GetTarget().ModulesDidUnload(unloaded_modules, true); - - SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); - SetPrivateState(eStateExited); -} - -void -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::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 -ProcessWindows::OnDebugString(const std::string &string) -{ -} - -void -ProcessWindows::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; - } -} \ No newline at end of file Index: source/Plugins/Process/Windows/ProcessWindowsLog.h =================================================================== --- source/Plugins/Process/Windows/ProcessWindowsLog.h +++ /dev/null @@ -1,96 +0,0 @@ -//===-- ProcessWindowsLog.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_ProcessWindowsLog_h_ -#define liblldb_ProcessWindowsLog_h_ - -#include "lldb/Core/Log.h" - -#define WINDOWS_LOG_VERBOSE (1u << 0) -#define WINDOWS_LOG_PROCESS (1u << 1) // Log process operations -#define WINDOWS_LOG_EXCEPTION (1u << 1) // Log exceptions -#define WINDOWS_LOG_THREAD (1u << 2) // Log thread operations -#define WINDOWS_LOG_MEMORY (1u << 3) // Log memory reads/writes calls -#define WINDOWS_LOG_BREAKPOINTS (1u << 4) // Log breakpoint operations -#define WINDOWS_LOG_STEP (1u << 5) // Log step operations -#define WINDOWS_LOG_REGISTERS (1u << 6) // Log register operations -#define WINDOWS_LOG_EVENT (1u << 7) // Low level debug events -#define WINDOWS_LOG_ALL (UINT32_MAX) - -enum class LogMaskReq -{ - All, - Any -}; - -class ProcessWindowsLog -{ - static const char *m_pluginname; - -public: - // --------------------------------------------------------------------- - // Public Static Methods - // --------------------------------------------------------------------- - static void - Initialize(); - - static void - Terminate(); - - static void - RegisterPluginName(const char *pluginName) - { - m_pluginname = pluginName; - } - - static void - RegisterPluginName(lldb_private::ConstString pluginName) - { - m_pluginname = pluginName.GetCString(); - } - - static bool - TestLogFlags(uint32_t mask, LogMaskReq req); - - static lldb_private::Log * - GetLog(); - - static void - DisableLog(const char **args, lldb_private::Stream *feedback_strm); - - static lldb_private::Log * - EnableLog(lldb::StreamSP &log_stream_sp, uint32_t log_options, - const char **args, lldb_private::Stream *feedback_strm); - - static void - ListLogCategories(lldb_private::Stream *strm); -}; - -#define WINLOGF_IF(Flags, Req, Method, ...) \ - { \ - if (ProcessWindowsLog::TestLogFlags(Flags, Req)) \ - { \ - Log *log = ProcessWindowsLog::GetLog(); \ - if (log) \ - log->Method(__VA_ARGS__); \ - } \ - } - -#define WINLOG_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Printf, __VA_ARGS__) -#define WINLOG_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Printf, __VA_ARGS__) -#define WINLOGV_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Verbose, __VA_ARGS__) -#define WINLOGV_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Verbose, __VA_ARGS__) -#define WINLOGD_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Debug, __VA_ARGS__) -#define WINLOGD_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Debug, __VA_ARGS__) -#define WINERR_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Error, __VA_ARGS__) -#define WINERR_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Error, __VA_ARGS__) -#define WINWARN_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Warning, __VA_ARGS__) -#define WINWARN_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Warning, __VA_ARGS__) - -#endif // liblldb_ProcessWindowsLog_h_ Index: source/Plugins/Process/Windows/ProcessWindowsLog.cpp =================================================================== --- source/Plugins/Process/Windows/ProcessWindowsLog.cpp +++ /dev/null @@ -1,190 +0,0 @@ -//===-- ProcessWindowsLog.cpp -----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ProcessWindowsLog.h" - -#include - -#include "lldb/Core/StreamFile.h" -#include "lldb/Interpreter/Args.h" -#include "llvm/Support/ManagedStatic.h" - -#include "ProcessWindows.h" - -using namespace lldb; -using namespace lldb_private; - - -// We want to avoid global constructors where code needs to be run so here we -// control access to our static g_log_sp by hiding it in a singleton function -// that will construct the static g_log_sp the first time this function is -// called. -static bool g_log_enabled = false; -static Log * g_log = nullptr; - -static llvm::ManagedStatic g_once_flag; - -void -ProcessWindowsLog::Initialize() -{ - static ConstString g_name("windows"); - - std::call_once(*g_once_flag, [](){ - Log::Callbacks log_callbacks = { - DisableLog, - EnableLog, - ListLogCategories - }; - - Log::RegisterLogChannel(g_name, log_callbacks); - RegisterPluginName(g_name); - }); -} - -void -ProcessWindowsLog::Terminate() -{ -} - -Log * -ProcessWindowsLog::GetLog() -{ - return (g_log_enabled) ? g_log : nullptr; -} - -bool -ProcessWindowsLog::TestLogFlags(uint32_t mask, LogMaskReq req) -{ - Log *log = GetLog(); - if (!log) - return false; - - uint32_t log_mask = log->GetMask().Get(); - if (req == LogMaskReq::All) - return ((log_mask & mask) == mask); - else - return (log_mask & mask); -} - -static uint32_t -GetFlagBits(const char *arg) -{ - if (::strcasecmp(arg, "all") == 0 ) return WINDOWS_LOG_ALL; - else if (::strcasecmp(arg, "break") == 0 ) return WINDOWS_LOG_BREAKPOINTS; - else if (::strcasecmp(arg, "event") == 0 ) return WINDOWS_LOG_EVENT; - else if (::strcasecmp(arg, "exception") == 0 ) return WINDOWS_LOG_EXCEPTION; - else if (::strcasecmp(arg, "memory") == 0 ) return WINDOWS_LOG_MEMORY; - else if (::strcasecmp(arg, "process") == 0 ) return WINDOWS_LOG_PROCESS; - else if (::strcasecmp(arg, "registers") == 0 ) return WINDOWS_LOG_REGISTERS; - else if (::strcasecmp(arg, "step") == 0 ) return WINDOWS_LOG_STEP; - else if (::strcasecmp(arg, "thread") == 0 ) return WINDOWS_LOG_THREAD; - else if (::strcasecmp(arg, "verbose") == 0 ) return WINDOWS_LOG_VERBOSE; - return 0; -} - -void -ProcessWindowsLog::DisableLog(const char **args, Stream *feedback_strm) -{ - Log *log (GetLog()); - if (log) - { - uint32_t flag_bits = 0; - - flag_bits = log->GetMask().Get(); - for (; args[0]; args++) - { - const char *arg = args[0]; - uint32_t bits = GetFlagBits(arg); - - if (bits) - { - flag_bits &= ~bits; - } - else - { - feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); - ListLogCategories(feedback_strm); - } - } - - log->GetMask().Reset(flag_bits); - if (flag_bits == 0) - g_log_enabled = false; - } - - return; -} - -Log * -ProcessWindowsLog::EnableLog(StreamSP &log_stream_sp, uint32_t log_options, const char **args, Stream *feedback_strm) -{ - // Try see if there already is a log - that way we can reuse its settings. - // We could reuse the log in toto, but we don't know that the stream is the same. - uint32_t flag_bits = 0; - if (g_log) - flag_bits = g_log->GetMask().Get(); - - // Now make a new log with this stream if one was provided - if (log_stream_sp) - { - if (g_log) - g_log->SetStream(log_stream_sp); - else - g_log = new Log(log_stream_sp); - } - - if (g_log) - { - bool got_unknown_category = false; - for (; args[0]; args++) - { - const char *arg = args[0]; - uint32_t bits = GetFlagBits(arg); - - if (bits) - { - flag_bits |= bits; - } - else - { - feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); - if (got_unknown_category == false) - { - got_unknown_category = true; - ListLogCategories (feedback_strm); - } - } - } - if (flag_bits == 0) - flag_bits = WINDOWS_LOG_ALL; - g_log->GetMask().Reset(flag_bits); - g_log->GetOptions().Reset(log_options); - g_log_enabled = true; - } - return g_log; -} - -void -ProcessWindowsLog::ListLogCategories(Stream *strm) -{ - strm->Printf("Logging categories for '%s':\n" - " all - turn on all available logging categories\n" - " break - log breakpoints\n" - " event - log low level debugger events\n" - " exception - log exception information\n" - " memory - log memory reads and writes\n" - " process - log process events and activities\n" - " registers - log register read/writes\n" - " thread - log thread events and activities\n" - " step - log step related activities\n" - " verbose - enable verbose logging\n", - ProcessWindowsLog::m_pluginname); -} - -const char *ProcessWindowsLog::m_pluginname = ""; Index: source/Plugins/Process/Windows/RegisterContextWindows.h =================================================================== --- source/Plugins/Process/Windows/RegisterContextWindows.h +++ /dev/null @@ -1,69 +0,0 @@ -//===-- RegisterContextWindows.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_RegisterContextWindows_H_ -#define liblldb_RegisterContextWindows_H_ - -#include "lldb/lldb-forward.h" -#include "lldb/Target/RegisterContext.h" - -namespace lldb_private -{ - -class Thread; - -class RegisterContextWindows : public lldb_private::RegisterContext -{ - public: - //------------------------------------------------------------------ - // Constructors and Destructors - //------------------------------------------------------------------ - RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx); - - virtual ~RegisterContextWindows(); - - //------------------------------------------------------------------ - // Subclasses must override these functions - //------------------------------------------------------------------ - void InvalidateAllRegisters() override; - - bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; - - bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; - - uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; - - //------------------------------------------------------------------ - // Subclasses can override these functions if desired - //------------------------------------------------------------------ - uint32_t NumSupportedHardwareBreakpoints() override; - - uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; - - bool ClearHardwareBreakpoint(uint32_t hw_idx) override; - - uint32_t NumSupportedHardwareWatchpoints() override; - - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) override; - - bool ClearHardwareWatchpoint(uint32_t hw_index) override; - - bool HardwareSingleStep(bool enable) override; - - protected: - bool CacheAllRegisterValues(); - - CONTEXT m_context; - - private: - bool m_context_stale; -}; -} - -#endif // #ifndef liblldb_RegisterContextPOSIX_x86_H_ Index: source/Plugins/Process/Windows/RegisterContextWindows.cpp =================================================================== --- source/Plugins/Process/Windows/RegisterContextWindows.cpp +++ /dev/null @@ -1,155 +0,0 @@ -//===-- RegisterContextWindows.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/DataBufferHeap.h" -#include "lldb/Core/Error.h" -#include "lldb/Host/windows/HostThreadWindows.h" -#include "lldb/Host/windows/windows.h" - -#include "ProcessWindowsLog.h" -#include "RegisterContextWindows.h" -#include "TargetThreadWindows.h" - -#include "llvm/ADT/STLExtras.h" - -using namespace lldb; -using namespace lldb_private; - -const DWORD kWinContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - -//------------------------------------------------------------------ -// Constructors and Destructors -//------------------------------------------------------------------ -RegisterContextWindows::RegisterContextWindows(Thread &thread, uint32_t concrete_frame_idx) - : RegisterContext(thread, concrete_frame_idx) - , m_context() - , m_context_stale(true) -{ -} - -RegisterContextWindows::~RegisterContextWindows() -{ -} - -void -RegisterContextWindows::InvalidateAllRegisters() -{ - m_context_stale = true; -} - -bool -RegisterContextWindows::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) -{ - if (!CacheAllRegisterValues()) - return false; - if (data_sp->GetByteSize() < sizeof(m_context)) - { - data_sp.reset(new DataBufferHeap(sizeof(CONTEXT), 0)); - } - memcpy(data_sp->GetBytes(), &m_context, sizeof(m_context)); - return true; -} - -bool -RegisterContextWindows::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) -{ - assert(data_sp->GetByteSize() >= sizeof(m_context)); - memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context)); - - TargetThreadWindows &wthread = static_cast(m_thread); - if (!::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) - return false; - - return true; -} - -uint32_t -RegisterContextWindows::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) -{ - const uint32_t num_regs = GetRegisterCount(); - - assert(kind < kNumRegisterKinds); - for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) - { - const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); - - if (reg_info->kinds[kind] == num) - return reg_idx; - } - - return LLDB_INVALID_REGNUM; -} - -//------------------------------------------------------------------ -// Subclasses can these functions if desired -//------------------------------------------------------------------ -uint32_t -RegisterContextWindows::NumSupportedHardwareBreakpoints() -{ - // Support for hardware breakpoints not yet implemented. - return 0; -} - -uint32_t -RegisterContextWindows::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) -{ - return 0; -} - -bool -RegisterContextWindows::ClearHardwareBreakpoint(uint32_t hw_idx) -{ - return false; -} - -uint32_t -RegisterContextWindows::NumSupportedHardwareWatchpoints() -{ - // Support for hardware watchpoints not yet implemented. - return 0; -} - -uint32_t -RegisterContextWindows::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) -{ - return 0; -} - -bool -RegisterContextWindows::ClearHardwareWatchpoint(uint32_t hw_index) -{ - return false; -} - -bool -RegisterContextWindows::HardwareSingleStep(bool enable) -{ - return false; -} - -bool -RegisterContextWindows::CacheAllRegisterValues() -{ - if (!m_context_stale) - return true; - - TargetThreadWindows &wthread = static_cast(m_thread); - memset(&m_context, 0, sizeof(m_context)); - m_context.ContextFlags = kWinContextFlags; - if (!::GetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context)) - { - WINERR_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext failed with error %u while caching register values.", - ::GetLastError()); - return false; - } - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext successfully updated the register values.", ::GetLastError()); - m_context_stale = false; - return true; -} Index: source/Plugins/Process/Windows/TargetThreadWindows.h =================================================================== --- source/Plugins/Process/Windows/TargetThreadWindows.h +++ /dev/null @@ -1,57 +0,0 @@ -//===-- TargetThreadWindows.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_TargetThreadWindows_H_ -#define liblldb_Plugins_Process_Windows_TargetThreadWindows_H_ - -#include "ForwardDecl.h" -#include "lldb/lldb-forward.h" -#include "lldb/Host/HostThread.h" -#include "lldb/Target/Thread.h" -class ProcessWindows; - -namespace lldb_private -{ - -class HostThread; -class StackFrame; - -class TargetThreadWindows : public lldb_private::Thread -{ - public: - TargetThreadWindows(ProcessWindows &process, const HostThread &thread); - virtual ~TargetThreadWindows(); - - // 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); - - lldb::StackFrameUP m_stack_frame; - - HostThread m_host_thread; -}; -} - -#endif \ No newline at end of file Index: source/Plugins/Process/Windows/TargetThreadWindows.cpp =================================================================== --- source/Plugins/Process/Windows/TargetThreadWindows.cpp +++ /dev/null @@ -1,147 +0,0 @@ -//===-- TargetThreadWindows.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 "TargetThreadWindows.h" -#include "ProcessWindows.h" -#include "ProcessWindowsLog.h" -#include "UnwindLLDB.h" - -#if defined(_WIN64) -#include "x64/RegisterContextWindows_x64.h" -#else -#include "x86/RegisterContextWindows_x86.h" -#endif - -using namespace lldb; -using namespace lldb_private; - -TargetThreadWindows::TargetThreadWindows(ProcessWindows &process, const HostThread &thread) - : Thread(process, thread.GetNativeThread().GetThreadId()) - , m_host_thread(thread) -{ -} - -TargetThreadWindows::~TargetThreadWindows() -{ - DestroyThread(); -} - -void -TargetThreadWindows::RefreshStateAfterStop() -{ - ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); - SetState(eStateStopped); - GetRegisterContext()->InvalidateIfNeeded(false); -} - -void -TargetThreadWindows::WillResume(lldb::StateType resume_state) -{ -} - -void -TargetThreadWindows::DidStop() -{ -} - -RegisterContextSP -TargetThreadWindows::GetRegisterContext() -{ - if (!m_reg_context_sp) - m_reg_context_sp = CreateRegisterContextForFrameIndex(0); - - return m_reg_context_sp; -} - -RegisterContextSP -TargetThreadWindows::CreateRegisterContextForFrame(StackFrame *frame) -{ - return CreateRegisterContextForFrameIndex(frame->GetConcreteFrameIndex()); -} - -RegisterContextSP -TargetThreadWindows::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 RegisterContextWindows_x86(*this, idx)); -#endif - break; - case llvm::Triple::x86_64: -#if defined(_WIN64) - m_reg_context_sp.reset(new RegisterContextWindows_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 -TargetThreadWindows::CalculateStopInfo() -{ - SetStopInfo(m_stop_info_sp); - return true; -} - -Unwind * -TargetThreadWindows::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 -TargetThreadWindows::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; -} Index: source/Plugins/Process/Windows/x64/RegisterContextWindows_x64.h =================================================================== --- source/Plugins/Process/Windows/x64/RegisterContextWindows_x64.h +++ /dev/null @@ -1,48 +0,0 @@ -//===-- RegisterContextWindows_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_RegisterContextWindows_x64_H_ -#define liblldb_RegisterContextWindows_x64_H_ - -#include "lldb/lldb-forward.h" -#include "RegisterContextWindows.h" - -namespace lldb_private -{ - -class Thread; - -class RegisterContextWindows_x64 : public RegisterContextWindows -{ - public: - //------------------------------------------------------------------ - // Constructors and Destructors - //------------------------------------------------------------------ - RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx); - - virtual ~RegisterContextWindows_x64(); - - //------------------------------------------------------------------ - // Subclasses must override these functions - //------------------------------------------------------------------ - size_t GetRegisterCount() override; - - const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; - - size_t GetRegisterSetCount() override; - - const RegisterSet *GetRegisterSet(size_t reg_set) override; - - bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; - - bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; -}; -} - -#endif // #ifndef liblldb_RegisterContextPOSIX_x64_H_ Index: source/Plugins/Process/Windows/x64/RegisterContextWindows_x64.cpp =================================================================== --- source/Plugins/Process/Windows/x64/RegisterContextWindows_x64.cpp +++ /dev/null @@ -1,323 +0,0 @@ -//===-- RegisterContextWindows_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 "RegisterContext_x86.h" -#include "RegisterContextWindows_x64.h" -#include "TargetThreadWindows.h" - -#include "llvm/ADT/STLExtras.h" - -using namespace lldb; -using namespace lldb_private; - -#define DEFINE_GPR(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatHexUppercase -#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 8, 0, eEncodingUint, eFormatBinary - -namespace -{ - -// This enum defines the layout of the global RegisterInfo array. This is necessary because -// lldb register sets are defined in terms of indices into the register array. As such, the -// order of RegisterInfos defined in global registers array must match the order defined here. -// When defining the register set layouts, these values can appear in an arbitrary order, and that -// determines the order that register values are displayed in a dump. -enum RegisterIndex -{ - eRegisterIndexRax, - eRegisterIndexRbx, - eRegisterIndexRcx, - eRegisterIndexRdx, - eRegisterIndexRdi, - eRegisterIndexRsi, - eRegisterIndexR8, - eRegisterIndexR9, - eRegisterIndexR10, - eRegisterIndexR11, - eRegisterIndexR12, - eRegisterIndexR13, - eRegisterIndexR14, - eRegisterIndexR15, - eRegisterIndexRbp, - eRegisterIndexRsp, - eRegisterIndexRip, - eRegisterIndexRflags -}; - -// Array of all register information supported by Windows x86 -RegisterInfo g_register_infos[] = { - // Macro auto defines most stuff GCC DWARF GENERIC - // GDB LLDB VALUE REGS INVALIDATE REGS - // ================================ ========================= ====================== ========================= - // =================== ================= ========== =============== - {DEFINE_GPR(rax, nullptr), - {gcc_dwarf_rax_x86_64, gcc_dwarf_rax_x86_64, LLDB_INVALID_REGNUM, gdb_rax_x86_64, lldb_rax_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rbx, nullptr), - {gcc_dwarf_rbx_x86_64, gcc_dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, gdb_rbx_x86_64, lldb_rbx_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rcx, nullptr), - {gcc_dwarf_rcx_x86_64, gcc_dwarf_rcx_x86_64, LLDB_INVALID_REGNUM, gdb_rcx_x86_64, lldb_rcx_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rdx, nullptr), - {gcc_dwarf_rdx_x86_64, gcc_dwarf_rdx_x86_64, LLDB_INVALID_REGNUM, gdb_rdx_x86_64, lldb_rdx_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rdi, nullptr), - {gcc_dwarf_rdi_x86_64, gcc_dwarf_rdi_x86_64, LLDB_INVALID_REGNUM, gdb_rdi_x86_64, lldb_rdi_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rsi, nullptr), - {gcc_dwarf_rsi_x86_64, gcc_dwarf_rsi_x86_64, LLDB_INVALID_REGNUM, gdb_rsi_x86_64, lldb_rsi_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r8, nullptr), - {gcc_dwarf_r8_x86_64, gcc_dwarf_r8_x86_64, LLDB_INVALID_REGNUM, gdb_r8_x86_64, lldb_r8_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r9, nullptr), - {gcc_dwarf_r9_x86_64, gcc_dwarf_r9_x86_64, LLDB_INVALID_REGNUM, gdb_r9_x86_64, lldb_r9_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r10, nullptr), - {gcc_dwarf_r10_x86_64, gcc_dwarf_r10_x86_64, LLDB_INVALID_REGNUM, gdb_r10_x86_64, lldb_r10_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r11, nullptr), - {gcc_dwarf_r11_x86_64, gcc_dwarf_r11_x86_64, LLDB_INVALID_REGNUM, gdb_r11_x86_64, lldb_r11_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r12, nullptr), - {gcc_dwarf_r12_x86_64, gcc_dwarf_r12_x86_64, LLDB_INVALID_REGNUM, gdb_r12_x86_64, lldb_r12_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r13, nullptr), - {gcc_dwarf_r13_x86_64, gcc_dwarf_r13_x86_64, LLDB_INVALID_REGNUM, gdb_r13_x86_64, lldb_r13_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r14, nullptr), - {gcc_dwarf_r14_x86_64, gcc_dwarf_r14_x86_64, LLDB_INVALID_REGNUM, gdb_r14_x86_64, lldb_r14_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(r15, nullptr), - {gcc_dwarf_r15_x86_64, gcc_dwarf_r15_x86_64, LLDB_INVALID_REGNUM, gdb_r15_x86_64, lldb_r15_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rbp, "fp"), - {gcc_dwarf_rbp_x86_64, gcc_dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, gdb_rbp_x86_64, lldb_rbp_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rsp, "sp"), - {gcc_dwarf_rsp_x86_64, gcc_dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, gdb_rsp_x86_64, lldb_rsp_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR(rip, "pc"), - {gcc_dwarf_rip_x86_64, gcc_dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, gdb_rip_x86_64, lldb_rip_x86_64}, - nullptr, - nullptr}, - {DEFINE_GPR_BIN(eflags, "flags"), - {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_rflags_x86_64, lldb_rflags_x86_64}, - nullptr, - nullptr}, -}; - -// Array of lldb register numbers used to define the set of all General Purpose Registers -uint32_t g_gpr_reg_indices[] = {eRegisterIndexRax, eRegisterIndexRbx, eRegisterIndexRcx, eRegisterIndexRdx, - eRegisterIndexRdi, eRegisterIndexRsi, eRegisterIndexR8, eRegisterIndexR9, - eRegisterIndexR10, eRegisterIndexR11, eRegisterIndexR12, eRegisterIndexR13, - eRegisterIndexR14, eRegisterIndexR15, eRegisterIndexRbp, eRegisterIndexRsp, - eRegisterIndexRip, eRegisterIndexRflags}; - -RegisterSet g_register_sets[] = { - {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, -}; -} - -//------------------------------------------------------------------ -// Constructors and Destructors -//------------------------------------------------------------------ -RegisterContextWindows_x64::RegisterContextWindows_x64(Thread &thread, uint32_t concrete_frame_idx) - : RegisterContextWindows(thread, concrete_frame_idx) -{ -} - -RegisterContextWindows_x64::~RegisterContextWindows_x64() -{ -} - -size_t -RegisterContextWindows_x64::GetRegisterCount() -{ - return llvm::array_lengthof(g_register_infos); -} - -const RegisterInfo * -RegisterContextWindows_x64::GetRegisterInfoAtIndex(size_t reg) -{ - return &g_register_infos[reg]; -} - -size_t -RegisterContextWindows_x64::GetRegisterSetCount() -{ - return llvm::array_lengthof(g_register_sets); -} - -const RegisterSet * -RegisterContextWindows_x64::GetRegisterSet(size_t reg_set) -{ - return &g_register_sets[reg_set]; -} - -bool -RegisterContextWindows_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 -RegisterContextWindows_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(m_thread); - return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); -} Index: source/Plugins/Process/Windows/x86/RegisterContextWindows_x86.h =================================================================== --- source/Plugins/Process/Windows/x86/RegisterContextWindows_x86.h +++ /dev/null @@ -1,48 +0,0 @@ -//===-- RegisterContextWindows_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_RegisterContextWindows_x86_H_ -#define liblldb_RegisterContextWindows_x86_H_ - -#include "lldb/lldb-forward.h" -#include "RegisterContextWindows.h" - -namespace lldb_private -{ - -class Thread; - -class RegisterContextWindows_x86 : public RegisterContextWindows -{ - public: - //------------------------------------------------------------------ - // Constructors and Destructors - //------------------------------------------------------------------ - RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx); - - virtual ~RegisterContextWindows_x86(); - - //------------------------------------------------------------------ - // Subclasses must override these functions - //------------------------------------------------------------------ - size_t GetRegisterCount() override; - - const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; - - size_t GetRegisterSetCount() override; - - const RegisterSet *GetRegisterSet(size_t reg_set) override; - - bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; - - bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; -}; -} - -#endif // #ifndef liblldb_RegisterContextPOSIX_x86_H_ Index: source/Plugins/Process/Windows/x86/RegisterContextWindows_x86.cpp =================================================================== --- source/Plugins/Process/Windows/x86/RegisterContextWindows_x86.cpp +++ /dev/null @@ -1,241 +0,0 @@ -//===-- RegisterContextWindows_x86.h ----------------------------*- 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 "RegisterContext_x86.h" -#include "RegisterContextWindows_x86.h" -#include "TargetThreadWindows.h" - -#include "llvm/ADT/STLExtras.h" - -using namespace lldb; -using namespace lldb_private; - -#define DEFINE_GPR(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatHexUppercase -#define DEFINE_GPR_BIN(reg, alt) #reg, alt, 4, 0, eEncodingUint, eFormatBinary - -namespace -{ - -// This enum defines the layout of the global RegisterInfo array. This is necessary because -// lldb register sets are defined in terms of indices into the register array. As such, the -// order of RegisterInfos defined in global registers array must match the order defined here. -// When defining the register set layouts, these values can appear in an arbitrary order, and that -// determines the order that register values are displayed in a dump. -enum RegisterIndex -{ - eRegisterIndexEax, - eRegisterIndexEbx, - eRegisterIndexEcx, - eRegisterIndexEdx, - eRegisterIndexEdi, - eRegisterIndexEsi, - eRegisterIndexEbp, - eRegisterIndexEsp, - eRegisterIndexEip, - eRegisterIndexEflags -}; - -// Array of all register information supported by Windows x86 -RegisterInfo g_register_infos[] = -{ -// Macro auto defines most stuff GCC DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS -// ============================== ======================= =================== ========================= =================== ================= ========== =============== - { DEFINE_GPR(eax, nullptr), { gcc_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_eax_i386 }, nullptr, nullptr}, - { DEFINE_GPR(ebx, nullptr), { gcc_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ebx_i386 }, nullptr, nullptr}, - { DEFINE_GPR(ecx, nullptr), { gcc_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ecx_i386 }, nullptr, nullptr}, - { DEFINE_GPR(edx, nullptr), { gcc_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edx_i386 }, nullptr, nullptr}, - { DEFINE_GPR(edi, nullptr), { gcc_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edi_i386 }, nullptr, nullptr}, - { DEFINE_GPR(esi, nullptr), { gcc_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_esi_i386 }, nullptr, nullptr}, - { DEFINE_GPR(ebp, "fp"), { gcc_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_ebp_i386 }, nullptr, nullptr}, - { DEFINE_GPR(esp, "sp"), { gcc_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_esp_i386 }, nullptr, nullptr}, - { DEFINE_GPR(eip, "pc"), { gcc_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_eip_i386 }, nullptr, nullptr}, - { DEFINE_GPR_BIN(eflags, "flags"), { gcc_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_eflags_i386}, nullptr, nullptr}, -}; - -// Array of lldb register numbers used to define the set of all General Purpose Registers -uint32_t g_gpr_reg_indices[] = -{ - eRegisterIndexEax, - eRegisterIndexEbx, - eRegisterIndexEcx, - eRegisterIndexEdx, - eRegisterIndexEdi, - eRegisterIndexEsi, - eRegisterIndexEbp, - eRegisterIndexEsp, - eRegisterIndexEip, - eRegisterIndexEflags -}; - -RegisterSet g_register_sets[] = { - {"General Purpose Registers", "gpr", llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices}, -}; -} - -//------------------------------------------------------------------ -// Constructors and Destructors -//------------------------------------------------------------------ -RegisterContextWindows_x86::RegisterContextWindows_x86(Thread &thread, uint32_t concrete_frame_idx) - : RegisterContextWindows(thread, concrete_frame_idx) -{ -} - -RegisterContextWindows_x86::~RegisterContextWindows_x86() -{ -} - -size_t -RegisterContextWindows_x86::GetRegisterCount() -{ - return llvm::array_lengthof(g_register_infos); -} - -const RegisterInfo * -RegisterContextWindows_x86::GetRegisterInfoAtIndex(size_t reg) -{ - return &g_register_infos[reg]; -} - -size_t -RegisterContextWindows_x86::GetRegisterSetCount() -{ - return llvm::array_lengthof(g_register_sets); -} - -const RegisterSet * -RegisterContextWindows_x86::GetRegisterSet(size_t reg_set) -{ - return &g_register_sets[reg_set]; -} - -bool -RegisterContextWindows_x86::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) -{ - if (!CacheAllRegisterValues()) - return false; - - uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; - switch (reg) - { - case lldb_eax_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EAX", m_context.Eax); - reg_value.SetUInt32(m_context.Eax); - break; - case lldb_ebx_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBX", m_context.Ebx); - reg_value.SetUInt32(m_context.Ebx); - break; - case lldb_ecx_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ECX", m_context.Ecx); - reg_value.SetUInt32(m_context.Ecx); - break; - case lldb_edx_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDX", m_context.Edx); - reg_value.SetUInt32(m_context.Edx); - break; - case lldb_edi_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDI", m_context.Edi); - reg_value.SetUInt32(m_context.Edi); - break; - case lldb_esi_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESI", m_context.Esi); - reg_value.SetUInt32(m_context.Esi); - break; - case lldb_ebp_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBP", m_context.Ebp); - reg_value.SetUInt32(m_context.Ebp); - break; - case lldb_esp_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESP", m_context.Esp); - reg_value.SetUInt32(m_context.Esp); - break; - case lldb_eip_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EIP", m_context.Eip); - reg_value.SetUInt32(m_context.Eip); - break; - case lldb_eflags_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EFLAGS", m_context.EFlags); - reg_value.SetUInt32(m_context.EFlags); - break; - default: - WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Requested unknown register %u", reg); - break; - } - return true; -} - -bool -RegisterContextWindows_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(m_thread); - return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); -} Index: source/Plugins/Process/win-minidump/CMakeLists.txt =================================================================== --- source/Plugins/Process/win-minidump/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -include_directories(../Utility) - -add_lldb_library(lldbPluginProcessWinMiniDump - ProcessWinMiniDump.cpp - RegisterContextWindowsMiniDump.cpp - ThreadWinMiniDump.cpp - ) Index: source/Plugins/Process/win-minidump/ProcessWinMiniDump.h =================================================================== --- source/Plugins/Process/win-minidump/ProcessWinMiniDump.h +++ /dev/null @@ -1,116 +0,0 @@ -//===-- ProcessWinMiniDump.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_ProcessWinMiniDump_h_ -#define liblldb_ProcessWinMiniDump_h_ - -#include -#include - -#include "lldb/Core/ConstString.h" -#include "lldb/Core/Error.h" -#include "lldb/Target/Process.h" - -struct ThreadData; - -class ProcessWinMiniDump : public lldb_private::Process -{ -public: - static lldb::ProcessSP - CreateInstance (lldb_private::Target& target, - lldb_private::Listener &listener, - const lldb_private::FileSpec *crash_file_path); - - static void - Initialize(); - - static void - Terminate(); - - static lldb_private::ConstString - GetPluginNameStatic(); - - static const char * - GetPluginDescriptionStatic(); - - ProcessWinMiniDump(lldb_private::Target& target, - lldb_private::Listener &listener, - const lldb_private::FileSpec &core_file); - - virtual - ~ProcessWinMiniDump(); - - bool - CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override; - - lldb_private::Error - DoLoadCore() override; - - lldb_private::DynamicLoader * - GetDynamicLoader() override; - - lldb_private::ConstString - GetPluginName() override; - - uint32_t - GetPluginVersion() override; - - lldb_private::Error - DoDestroy() override; - - void - RefreshStateAfterStop() override; - - bool - IsAlive() override; - - size_t - ReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; - - size_t - DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; - - lldb::addr_t - GetImageInfoAddress() override; - - lldb_private::ArchSpec - GetArchitecture(); - -protected: - void - Clear(); - - bool - UpdateThreadList(lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list) override; - -private: - lldb_private::Error - MapMiniDumpIntoMemory(const char *file); - - lldb_private::ArchSpec - DetermineArchitecture(); - - void - ReadExceptionRecord(); - - // A thin wrapper around WinAPI's MiniDumpReadDumpStream to avoid redundant - // checks. If there's a failure (e.g., if the requested stream doesn't exist), - // the function returns nullptr and sets *size_out to 0. - void * - FindDumpStream(unsigned stream_number, size_t *size_out); - - // Isolate the data to keep Windows-specific types out of this header. Can't - // use the typical pimpl idiom because the implementation of this class also - // needs access to public and protected members of the base class. - class Data; - std::unique_ptr m_data_up; -}; - -#endif // liblldb_ProcessWinMiniDump_h_ Index: source/Plugins/Process/win-minidump/ProcessWinMiniDump.cpp =================================================================== --- source/Plugins/Process/win-minidump/ProcessWinMiniDump.cpp +++ /dev/null @@ -1,362 +0,0 @@ -//===-- ProcessWinMiniDump.cpp ----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ProcessWinMiniDump.h" - -#include "lldb/Host/windows/windows.h" -#include - -#include -#include - -#include - -#include "lldb/Core/PluginManager.h" -#include "lldb/Core/Module.h" -#include "lldb/Core/ModuleSpec.h" -#include "lldb/Core/Section.h" -#include "lldb/Core/State.h" -#include "lldb/Core/DataBufferHeap.h" -#include "lldb/Core/Log.h" -#include "lldb/Target/StopInfo.h" -#include "lldb/Target/Target.h" -#include "lldb/Target/DynamicLoader.h" -#include "lldb/Target/UnixSignals.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/raw_ostream.h" -#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" - -#include "../windows/ExceptionRecord.h" // TODO(amccarth): move this file to a common location -#include "ThreadWinMiniDump.h" - -using namespace lldb_private; - -// Encapsulates the private data for ProcessWinMiniDump. -// TODO(amccarth): Determine if we need a mutex for access. -class ProcessWinMiniDump::Data -{ -public: - Data(); - ~Data(); - - FileSpec m_core_file; - HANDLE m_dump_file; // handle to the open minidump file - HANDLE m_mapping; // handle to the file mapping for the minidump file - void * m_base_addr; // base memory address of the minidump - std::shared_ptr m_exception_sp; -}; - -ConstString -ProcessWinMiniDump::GetPluginNameStatic() -{ - static ConstString g_name("win-minidump"); - return g_name; -} - -const char * -ProcessWinMiniDump::GetPluginDescriptionStatic() -{ - return "Windows minidump plug-in."; -} - -void -ProcessWinMiniDump::Terminate() -{ - PluginManager::UnregisterPlugin(ProcessWinMiniDump::CreateInstance); -} - - -lldb::ProcessSP -ProcessWinMiniDump::CreateInstance(Target &target, Listener &listener, const FileSpec *crash_file) -{ - lldb::ProcessSP process_sp; - if (crash_file) - { - process_sp.reset(new ProcessWinMiniDump(target, listener, *crash_file)); - } - return process_sp; -} - -bool -ProcessWinMiniDump::CanDebug(Target &target, bool plugin_specified_by_name) -{ - // TODO(amccarth): Eventually, this needs some actual logic. - return true; -} - -ProcessWinMiniDump::ProcessWinMiniDump(Target& target, Listener &listener, - const FileSpec &core_file) : - Process(target, listener), - m_data_up(new Data) -{ - m_data_up->m_core_file = core_file; -} - -ProcessWinMiniDump::~ProcessWinMiniDump() -{ - Clear(); - // We need to call finalize on the process before destroying ourselves - // to make sure all of the broadcaster cleanup goes as planned. If we - // destruct this class, then Process::~Process() might have problems - // trying to fully destroy the broadcaster. - Finalize(); -} - -ConstString -ProcessWinMiniDump::GetPluginName() -{ - return GetPluginNameStatic(); -} - -uint32_t -ProcessWinMiniDump::GetPluginVersion() -{ - return 1; -} - - -Error -ProcessWinMiniDump::DoLoadCore() -{ - Error error; - - error = MapMiniDumpIntoMemory(m_data_up->m_core_file.GetCString()); - if (error.Fail()) - { - return error; - } - - m_target.SetArchitecture(DetermineArchitecture()); - // TODO(amccarth): Build the module list. - - ReadExceptionRecord(); - - return error; - -} - -DynamicLoader * -ProcessWinMiniDump::GetDynamicLoader() -{ - if (m_dyld_ap.get() == NULL) - m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderWindowsDYLD::GetPluginNameStatic().GetCString())); - return m_dyld_ap.get(); -} - -bool -ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) -{ - size_t size = 0; - auto thread_list_ptr = static_cast(FindDumpStream(ThreadListStream, &size)); - if (thread_list_ptr) - { - const ULONG32 thread_count = thread_list_ptr->NumberOfThreads; - for (ULONG32 i = 0; i < thread_count; ++i) { - std::shared_ptr thread_sp(new ThreadWinMiniDump(*this, thread_list_ptr->Threads[i].ThreadId)); - new_thread_list.AddThread(thread_sp); - } - } - - return new_thread_list.GetSize(false) > 0; -} - -void -ProcessWinMiniDump::RefreshStateAfterStop() -{ - if (!m_data_up) return; - if (!m_data_up->m_exception_sp) return; - - auto active_exception = m_data_up->m_exception_sp; - 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(active_exception->GetExceptionAddress(), 8); - m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); - auto stop_thread = m_thread_list.GetSelectedThread(); - auto stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); - stop_thread->SetStopInfo(stop_info); -} - -Error -ProcessWinMiniDump::DoDestroy() -{ - return Error(); -} - -bool -ProcessWinMiniDump::IsAlive() -{ - return true; -} - -size_t -ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) -{ - // Don't allow the caching that lldb_private::Process::ReadMemory does - // since in core files we have it all cached our our core file anyway. - return DoReadMemory(addr, buf, size, error); -} - -size_t -ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) -{ - // TODO - return 0; -} - -void -ProcessWinMiniDump::Clear() -{ - m_thread_list.Clear(); -} - -void -ProcessWinMiniDump::Initialize() -{ - static std::once_flag g_once_flag; - - std::call_once(g_once_flag, []() - { - PluginManager::RegisterPlugin(GetPluginNameStatic(), - GetPluginDescriptionStatic(), - CreateInstance); - }); -} - -lldb::addr_t -ProcessWinMiniDump::GetImageInfoAddress() -{ - Target *target = &GetTarget(); - ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); - Address addr = obj_file->GetImageInfoAddress(target); - - if (addr.IsValid()) - return addr.GetLoadAddress(target); - return LLDB_INVALID_ADDRESS; -} - -ArchSpec -ProcessWinMiniDump::GetArchitecture() -{ - // TODO - return ArchSpec(); -} - - -ProcessWinMiniDump::Data::Data() : - m_dump_file(INVALID_HANDLE_VALUE), - m_mapping(NULL), - m_base_addr(nullptr) -{ -} - -ProcessWinMiniDump::Data::~Data() -{ - if (m_base_addr) - { - ::UnmapViewOfFile(m_base_addr); - m_base_addr = nullptr; - } - if (m_mapping) - { - ::CloseHandle(m_mapping); - m_mapping = NULL; - } - if (m_dump_file != INVALID_HANDLE_VALUE) - { - ::CloseHandle(m_dump_file); - m_dump_file = INVALID_HANDLE_VALUE; - } -} - -Error -ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) -{ - Error error; - - m_data_up->m_dump_file = ::CreateFile(file, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (m_data_up->m_dump_file == INVALID_HANDLE_VALUE) - { - error.SetError(::GetLastError(), lldb::eErrorTypeWin32); - return error; - } - - m_data_up->m_mapping = ::CreateFileMapping(m_data_up->m_dump_file, NULL, - PAGE_READONLY, 0, 0, NULL); - if (m_data_up->m_mapping == NULL) - { - error.SetError(::GetLastError(), lldb::eErrorTypeWin32); - return error; - } - - m_data_up->m_base_addr = ::MapViewOfFile(m_data_up->m_mapping, FILE_MAP_READ, 0, 0, 0); - if (m_data_up->m_base_addr == NULL) - { - error.SetError(::GetLastError(), lldb::eErrorTypeWin32); - return error; - } - - return error; -} - - -ArchSpec -ProcessWinMiniDump::DetermineArchitecture() -{ - size_t size = 0; - auto system_info_ptr = static_cast(FindDumpStream(SystemInfoStream, &size)); - if (system_info_ptr) - { - switch (system_info_ptr->ProcessorArchitecture) - { - case PROCESSOR_ARCHITECTURE_INTEL: - return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE); - case PROCESSOR_ARCHITECTURE_AMD64: - return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE); - default: - break; - } - } - - return ArchSpec(); // invalid or unknown -} - -void -ProcessWinMiniDump::ReadExceptionRecord() { - size_t size = 0; - auto exception_stream_ptr = static_cast(FindDumpStream(ExceptionStream, &size)); - if (exception_stream_ptr) - { - m_data_up->m_exception_sp.reset(new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId)); - } -} - -void * -ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) { - void *stream = nullptr; - *size_out = 0; - - assert(m_data_up != nullptr); - assert(m_data_up->m_base_addr != 0); - - MINIDUMP_DIRECTORY *dir = nullptr; - if (::MiniDumpReadDumpStream(m_data_up->m_base_addr, stream_number, &dir, nullptr, nullptr) && - dir != nullptr && dir->Location.DataSize > 0) - { - assert(dir->StreamType == stream_number); - *size_out = dir->Location.DataSize; - stream = static_cast(static_cast(m_data_up->m_base_addr) + dir->Location.Rva); - } - - return stream; -} Index: source/Plugins/Process/win-minidump/RegisterContextWindowsMiniDump.h =================================================================== --- source/Plugins/Process/win-minidump/RegisterContextWindowsMiniDump.h +++ /dev/null @@ -1,73 +0,0 @@ -//===-- RegisterContextWindowsMiniDump.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_RegisterContextWindowsMiniDump_H_ -#define liblldb_RegisterContextWindowsMiniDump_H_ - -#include "lldb/lldb-forward.h" -#include "lldb/Target/RegisterContext.h" - - -namespace lldb_private -{ - -class Thread; - -class RegisterContextWindowsMiniDump : public lldb_private::RegisterContext -{ - public: - RegisterContextWindowsMiniDump(Thread &thread, uint32_t concrete_frame_idx); - - virtual ~RegisterContextWindowsMiniDump(); - - void - InvalidateAllRegisters() override; - - size_t - GetRegisterCount() override; - - const RegisterInfo * - GetRegisterInfoAtIndex(size_t reg) override; - - size_t - GetRegisterSetCount() override; - - const RegisterSet * - GetRegisterSet(size_t reg_set) override; - - bool - ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; - - bool - WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; - - bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; - - bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; - - uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; - - uint32_t NumSupportedHardwareBreakpoints() override; - - uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; - - bool ClearHardwareBreakpoint(uint32_t hw_idx) override; - - uint32_t NumSupportedHardwareWatchpoints() override; - - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) override; - - bool ClearHardwareWatchpoint(uint32_t hw_index) override; - - bool HardwareSingleStep(bool enable) override; -}; - -} - -#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_H_ Index: source/Plugins/Process/win-minidump/RegisterContextWindowsMiniDump.cpp =================================================================== --- source/Plugins/Process/win-minidump/RegisterContextWindowsMiniDump.cpp +++ /dev/null @@ -1,146 +0,0 @@ -//===-- RegisterContextWindowsMiniDump.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/DataBufferHeap.h" -#include "lldb/Core/Error.h" -#include "lldb/Host/windows/HostThreadWindows.h" -#include "lldb/Host/windows/windows.h" - -#include "RegisterContextWindowsMiniDump.h" - -#include "llvm/ADT/STLExtras.h" - -using namespace lldb; -using namespace lldb_private; - -// This is a do-nothing stub implementation for now. - -RegisterContextWindowsMiniDump::RegisterContextWindowsMiniDump(Thread &thread, uint32_t concrete_frame_idx) - : RegisterContext(thread, concrete_frame_idx) -{ -} - -RegisterContextWindowsMiniDump::~RegisterContextWindowsMiniDump() -{ -} - -void -RegisterContextWindowsMiniDump::InvalidateAllRegisters() -{ -} - -size_t -RegisterContextWindowsMiniDump::GetRegisterCount() -{ - return 0; -} - -const RegisterInfo * -RegisterContextWindowsMiniDump::GetRegisterInfoAtIndex(size_t reg) -{ - return nullptr; -} - -size_t -RegisterContextWindowsMiniDump::GetRegisterSetCount() -{ - return 0; -} - -const RegisterSet * -RegisterContextWindowsMiniDump::GetRegisterSet(size_t reg_set) -{ - return nullptr; -} - -bool -RegisterContextWindowsMiniDump::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) -{ - return false; -} - -bool -RegisterContextWindowsMiniDump::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) -{ - return false; -} - -bool -RegisterContextWindowsMiniDump::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) -{ - return false; -} - -bool -RegisterContextWindowsMiniDump::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) -{ - return false; -} - -uint32_t -RegisterContextWindowsMiniDump::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) -{ - const uint32_t num_regs = GetRegisterCount(); - - assert(kind < kNumRegisterKinds); - for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) - { - const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); - - if (reg_info->kinds[kind] == num) - return reg_idx; - } - - return LLDB_INVALID_REGNUM; -} - -uint32_t -RegisterContextWindowsMiniDump::NumSupportedHardwareBreakpoints() -{ - // Support for hardware breakpoints not yet implemented. - return 0; -} - -uint32_t -RegisterContextWindowsMiniDump::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) -{ - return 0; -} - -bool -RegisterContextWindowsMiniDump::ClearHardwareBreakpoint(uint32_t hw_idx) -{ - return false; -} - -uint32_t -RegisterContextWindowsMiniDump::NumSupportedHardwareWatchpoints() -{ - // Support for hardware watchpoints not yet implemented. - return 0; -} - -uint32_t -RegisterContextWindowsMiniDump::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write) -{ - return 0; -} - -bool -RegisterContextWindowsMiniDump::ClearHardwareWatchpoint(uint32_t hw_index) -{ - return false; -} - -bool -RegisterContextWindowsMiniDump::HardwareSingleStep(bool enable) -{ - return false; -} Index: source/Plugins/Process/win-minidump/ThreadWinMiniDump.h =================================================================== --- source/Plugins/Process/win-minidump/ThreadWinMiniDump.h +++ /dev/null @@ -1,51 +0,0 @@ -//===-- ThreadWinMiniDump.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_ThreadWinMiniDump_h_ -#define liblldb_ThreadWinMiniDump_h_ - -#include - -#include "lldb/Core/DataExtractor.h" -#include "lldb/Target/Thread.h" - -class ThreadWinMiniDump : public lldb_private::Thread -{ -public: - ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid); - - virtual - ~ThreadWinMiniDump(); - - void - RefreshStateAfterStop() override; - - lldb::RegisterContextSP - GetRegisterContext() override; - - lldb::RegisterContextSP - CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; - - void - ClearStackFrames() override; - - const char * - GetName() override; - - void - SetName(const char *name); - -protected: - std::string m_thread_name; - lldb::RegisterContextSP m_reg_context_sp; - - bool CalculateStopInfo() override; -}; - -#endif Index: source/Plugins/Process/win-minidump/ThreadWinMiniDump.cpp =================================================================== --- source/Plugins/Process/win-minidump/ThreadWinMiniDump.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//===-- ThreadWinMiniDump.cpp -----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ThreadWinMiniDump.h" - -// Windows includes -#include "lldb/Host/windows/windows.h" -#include - -#include "ProcessWinMiniDump.h" -#include "RegisterContextWindowsMiniDump.h" - -using namespace lldb; -using namespace lldb_private; - -// This is a minimal implementation in order to get something running. It will -// be fleshed out as more mini-dump functionality is added. - -ThreadWinMiniDump::ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid) : - Thread(process, tid), - m_thread_name() -{ -} - -ThreadWinMiniDump::~ThreadWinMiniDump() -{ -} - -void -ThreadWinMiniDump::RefreshStateAfterStop() -{ -} - -lldb::RegisterContextSP -ThreadWinMiniDump::GetRegisterContext() -{ - if (m_reg_context_sp.get() == NULL) { - m_reg_context_sp = CreateRegisterContextForFrame (NULL); - } - return m_reg_context_sp; -} - -lldb::RegisterContextSP -ThreadWinMiniDump::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) -{ - const uint32_t concrete_frame_idx = (frame) ? frame->GetConcreteFrameIndex() : 0; - RegisterContextSP reg_ctx_sp(new RegisterContextWindowsMiniDump(*this, concrete_frame_idx)); - return reg_ctx_sp; -} - -void -ThreadWinMiniDump::ClearStackFrames() -{ -} - -const char * -ThreadWinMiniDump::GetName() -{ - return m_thread_name.empty() ? nullptr : m_thread_name.c_str(); -} - -void -ThreadWinMiniDump::SetName(const char *name) -{ - if (name && name[0]) - m_thread_name.assign(name); - else - m_thread_name.clear(); -} - -bool ThreadWinMiniDump::CalculateStopInfo() -{ - return false; -}