From 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 6 Jan 2016 20:12:03 +0000 Subject: Vendor import of lldb trunk r256945: https://llvm.org/svn/llvm-project/lldb/trunk@256945 --- .../Plugins/Process/Windows/Common/CMakeLists.txt | 23 + .../Process/Windows/Common/ExceptionRecord.h | 99 +++ .../Process/Windows/Common/ProcessWindows.cpp | 100 +++ .../Process/Windows/Common/ProcessWindows.h | 52 ++ .../Process/Windows/Common/ProcessWindowsLog.cpp | 194 ++++ .../Process/Windows/Common/ProcessWindowsLog.h | 96 ++ .../Windows/Common/RegisterContextWindows.cpp | 155 ++++ .../Windows/Common/RegisterContextWindows.h | 67 ++ .../Process/Windows/Common/TargetThreadWindows.cpp | 98 ++ .../Process/Windows/Common/TargetThreadWindows.h | 50 ++ .../Common/x64/RegisterContextWindows_x64.cpp | 323 +++++++ .../Common/x64/RegisterContextWindows_x64.h | 48 + .../Common/x86/RegisterContextWindows_x86.cpp | 178 ++++ .../Common/x86/RegisterContextWindows_x86.h | 48 + source/Plugins/Process/Windows/Live/CMakeLists.txt | 24 + .../Process/Windows/Live/DebuggerThread.cpp | 546 ++++++++++++ .../Plugins/Process/Windows/Live/DebuggerThread.h | 100 +++ source/Plugins/Process/Windows/Live/ForwardDecl.h | 41 + .../Plugins/Process/Windows/Live/IDebugDelegate.h | 46 + .../Process/Windows/Live/LocalDebugDelegate.cpp | 91 ++ .../Process/Windows/Live/LocalDebugDelegate.h | 68 ++ .../Process/Windows/Live/ProcessWindowsLive.cpp | 989 +++++++++++++++++++++ .../Process/Windows/Live/ProcessWindowsLive.h | 125 +++ .../Windows/Live/TargetThreadWindowsLive.cpp | 147 +++ .../Process/Windows/Live/TargetThreadWindowsLive.h | 55 ++ .../Live/x64/RegisterContextWindowsLive_x64.cpp | 171 ++++ .../Live/x64/RegisterContextWindowsLive_x64.h | 40 + .../Live/x86/RegisterContextWindowsLive_x86.cpp | 100 +++ .../Live/x86/RegisterContextWindowsLive_x86.h | 36 + .../Process/Windows/MiniDump/CMakeLists.txt | 21 + .../Windows/MiniDump/ProcessWinMiniDump.cpp | 550 ++++++++++++ .../Process/Windows/MiniDump/ProcessWinMiniDump.h | 140 +++ .../Process/Windows/MiniDump/ThreadWinMiniDump.cpp | 104 +++ .../Process/Windows/MiniDump/ThreadWinMiniDump.h | 49 + .../x64/RegisterContextWindowsMiniDump_x64.cpp | 47 + .../x64/RegisterContextWindowsMiniDump_x64.h | 36 + .../x86/RegisterContextWindowsMiniDump_x86.cpp | 47 + .../x86/RegisterContextWindowsMiniDump_x86.h | 36 + 38 files changed, 5140 insertions(+) create mode 100644 source/Plugins/Process/Windows/Common/CMakeLists.txt create mode 100644 source/Plugins/Process/Windows/Common/ExceptionRecord.h create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindows.cpp create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindows.h create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp create mode 100644 source/Plugins/Process/Windows/Common/ProcessWindowsLog.h create mode 100644 source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp create mode 100644 source/Plugins/Process/Windows/Common/RegisterContextWindows.h create mode 100644 source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp create mode 100644 source/Plugins/Process/Windows/Common/TargetThreadWindows.h create mode 100644 source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp create mode 100644 source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h create mode 100644 source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp create mode 100644 source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h create mode 100644 source/Plugins/Process/Windows/Live/CMakeLists.txt create mode 100644 source/Plugins/Process/Windows/Live/DebuggerThread.cpp create mode 100644 source/Plugins/Process/Windows/Live/DebuggerThread.h create mode 100644 source/Plugins/Process/Windows/Live/ForwardDecl.h create mode 100644 source/Plugins/Process/Windows/Live/IDebugDelegate.h create mode 100644 source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp create mode 100644 source/Plugins/Process/Windows/Live/LocalDebugDelegate.h create mode 100644 source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp create mode 100644 source/Plugins/Process/Windows/Live/ProcessWindowsLive.h create mode 100644 source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp create mode 100644 source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h create mode 100644 source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp create mode 100644 source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h create mode 100644 source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp create mode 100644 source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h create mode 100644 source/Plugins/Process/Windows/MiniDump/CMakeLists.txt create mode 100644 source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h create mode 100644 source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h create mode 100644 source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h create mode 100644 source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp create mode 100644 source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h (limited to 'source/Plugins/Process/Windows') diff --git a/source/Plugins/Process/Windows/Common/CMakeLists.txt b/source/Plugins/Process/Windows/Common/CMakeLists.txt new file mode 100644 index 0000000000000..5a53c3c3300c2 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories(.) +include_directories(../../Utility) + +set(PROC_WINDOWS_COMMON_SOURCES + RegisterContextWindows.cpp + ProcessWindows.cpp + ProcessWindowsLog.cpp + TargetThreadWindows.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_COMMON_SOURCES ${PROC_WINDOWS_COMMON_SOURCES} + x86/RegisterContextWindows_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_COMMON_SOURCES ${PROC_WINDOWS_COMMON_SOURCES} + x64/RegisterContextWindows_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindowsCommon + ${PROC_WINDOWS_COMMON_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/Common/ExceptionRecord.h b/source/Plugins/Process/Windows/Common/ExceptionRecord.h new file mode 100644 index 0000000000000..79dae3fca4e83 --- /dev/null +++ b/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 diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp new file mode 100644 index 0000000000000..0e6900d8fb7f5 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -0,0 +1,100 @@ +//===-- ProcessWindows.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessWindows.h" + +// 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/windows/windows.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +namespace lldb_private +{ + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, Listener &listener) + : lldb_private::Process(target_sp, listener) +{ +} + +ProcessWindows::~ProcessWindows() +{ +} + +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; +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + + +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; +} + +// The Windows page protection bits are NOT independent masks that can be bitwise-ORed +// together. For example, PAGE_EXECUTE_READ is not (PAGE_EXECUTE | PAGE_READ). +// To test for an access type, it's necessary to test for any of the bits that provide +// that access type. +bool +ProcessWindows::IsPageReadable(uint32_t protect) +{ + return (protect & PAGE_NOACCESS) == 0; +} + +bool +ProcessWindows::IsPageWritable(uint32_t protect) +{ + return (protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY)) != 0; +} + +bool +ProcessWindows::IsPageExecutable(uint32_t protect) +{ + return (protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0; +} + +} diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.h b/source/Plugins/Process/Windows/Common/ProcessWindows.h new file mode 100644 index 0000000000000..2a437c0ca9092 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.h @@ -0,0 +1,52 @@ +//===-- 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_Common_ProcessWindows_H_ +#define liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ + +// Other libraries and framework includes +#include "lldb/lldb-forward.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +namespace lldb_private +{ + +class ProcessWindows : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindows(lldb::TargetSP target_sp, + lldb_private::Listener &listener); + + ~ProcessWindows(); + + 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::addr_t GetImageInfoAddress() override; + +protected: + // These decode the page protection bits. + static bool + IsPageReadable(uint32_t protect); + + static bool + IsPageWritable(uint32_t protect); + + static bool + IsPageExecutable(uint32_t protect); +}; + +} + +#endif // liblldb_Plugins_Process_Windows_Common_ProcessWindows_H_ diff --git a/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp new file mode 100644 index 0000000000000..47722c5146b24 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.cpp @@ -0,0 +1,194 @@ +//===-- 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" + +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; + + if (args[0] != nullptr) + { + 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; + log->SetStream(lldb::StreamSP()); + } + } + + 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 = ""; diff --git a/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h b/source/Plugins/Process/Windows/Common/ProcessWindowsLog.h new file mode 100644 index 0000000000000..d798d131faebd --- /dev/null +++ b/source/Plugins/Process/Windows/Common/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_ diff --git a/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp b/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp new file mode 100644 index 0000000000000..d61675f09b1b2 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/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; +} diff --git a/source/Plugins/Process/Windows/Common/RegisterContextWindows.h b/source/Plugins/Process/Windows/Common/RegisterContextWindows.h new file mode 100644 index 0000000000000..66b7a9004eade --- /dev/null +++ b/source/Plugins/Process/Windows/Common/RegisterContextWindows.h @@ -0,0 +1,67 @@ +//===-- 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: + virtual bool CacheAllRegisterValues(); + + CONTEXT m_context; + bool m_context_stale; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindows_H_ diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp new file mode 100644 index 0000000000000..dcb6f0c72435f --- /dev/null +++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp @@ -0,0 +1,98 @@ +//===-- 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" + +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() +{ +} + +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; +} diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.h b/source/Plugins/Process/Windows/Common/TargetThreadWindows.h new file mode 100644 index 0000000000000..701b56b6d26a7 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.h @@ -0,0 +1,50 @@ +//===-- 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" + +namespace lldb_private +{ +class ProcessWindows; +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; + bool CalculateStopInfo() override; + Unwind *GetUnwinder() override; + + bool DoResume(); + + HostThread + GetHostThread() const + { + return m_host_thread; + } + + private: + HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp new file mode 100644 index 0000000000000..103cff4a2a563 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/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 eh_frame DWARF GENERIC + // GDB LLDB VALUE REGS INVALIDATE REGS + // ================================ ========================= ====================== ========================= + // =================== ================= ========== =============== + {DEFINE_GPR(rax, nullptr), + {dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rax_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbx, nullptr), + {dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rbx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rcx, nullptr), + {dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rcx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdx, nullptr), + {dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rdx_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rdi, nullptr), + {dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rdi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsi, nullptr), + {dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_rsi_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r8, nullptr), + {dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r8_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r9, nullptr), + {dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r9_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r10, nullptr), + {dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r10_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r11, nullptr), + {dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r11_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r12, nullptr), + {dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r12_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r13, nullptr), + {dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r13_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r14, nullptr), + {dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r14_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(r15, nullptr), + {dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_r15_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rbp, "fp"), + {dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_rbp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rsp, "sp"), + {dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_rsp_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR(rip, "pc"), + {dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_rip_x86_64}, + nullptr, + nullptr}, + {DEFINE_GPR_BIN(eflags, "flags"), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, 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); +} diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h new file mode 100644 index 0000000000000..e69179d99c6c7 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/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_RegisterContextWindows_x64_H_ diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp new file mode 100644 index 0000000000000..e57e1effec9c9 --- /dev/null +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp @@ -0,0 +1,178 @@ +//===-- RegisterContextWindows_x86.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContext_x86.h" +#include "RegisterContextWindows_x86.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 eh_frame DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// ============================== ======================= =================== ========================= =================== ================= ========== =============== + { DEFINE_GPR(eax, nullptr), { ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_eax_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebx, nullptr), { ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ebx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ecx, nullptr), { ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_ecx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edx, nullptr), { ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edx_i386 }, nullptr, nullptr}, + { DEFINE_GPR(edi, nullptr), { ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_edi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esi, nullptr), { ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_esi_i386 }, nullptr, nullptr}, + { DEFINE_GPR(ebp, "fp"), { ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, lldb_ebp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(esp, "sp"), { ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, lldb_esp_i386 }, nullptr, nullptr}, + { DEFINE_GPR(eip, "pc"), { ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_eip_i386 }, nullptr, nullptr}, + { DEFINE_GPR_BIN(eflags, "flags"), { ehframe_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; +} diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h new file mode 100644 index 0000000000000..7d854ef64a5ca --- /dev/null +++ b/source/Plugins/Process/Windows/Common/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; + +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindows_x86_H_ diff --git a/source/Plugins/Process/Windows/Live/CMakeLists.txt b/source/Plugins/Process/Windows/Live/CMakeLists.txt new file mode 100644 index 0000000000000..bdb680ad0bee0 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/CMakeLists.txt @@ -0,0 +1,24 @@ +include_directories(.) +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_SOURCES + DebuggerThread.cpp + LocalDebugDelegate.cpp + ProcessWindowsLive.cpp + TargetThreadWindowsLive.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x86/RegisterContextWindowsLive_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_SOURCES ${PROC_WINDOWS_SOURCES} + x64/RegisterContextWindowsLive_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWindows + ${PROC_WINDOWS_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.cpp b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp new file mode 100644 index 0000000000000..d058a412c8968 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -0,0 +1,546 @@ +//===-- DebuggerThread.DebuggerThread --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "IDebugDelegate.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/windows/HostProcessWindows.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Target/Process.h" + +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +namespace +{ +struct DebugLaunchContext +{ + DebugLaunchContext(DebuggerThread *thread, const ProcessLaunchInfo &launch_info) + : m_thread(thread) + , m_launch_info(launch_info) + { + } + DebuggerThread *m_thread; + ProcessLaunchInfo m_launch_info; +}; + +struct DebugAttachContext +{ + DebugAttachContext(DebuggerThread *thread, lldb::pid_t pid, const ProcessAttachInfo &attach_info) + : m_thread(thread) + , m_pid(pid) + , m_attach_info(attach_info) + { + } + DebuggerThread *m_thread; + lldb::pid_t m_pid; + ProcessAttachInfo m_attach_info; +}; +} + +DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate) + : m_debug_delegate(debug_delegate) + , m_image_file(nullptr) + , m_debugging_ended_event(nullptr) + , m_is_shutting_down(false) + , m_pid_to_detach(0) + , m_detached(false) +{ + m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); +} + +DebuggerThread::~DebuggerThread() +{ + ::CloseHandle(m_debugging_ended_event); +} + +Error +DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str()); + + Error error; + DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadLaunchRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DebugLaunch couldn't launch debugger thread. %s", error.AsCString()); + } + + return error; +} + +Error +DebuggerThread::DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread::DebugAttach attaching to '%u'", (DWORD)pid); + + Error error; + DebugAttachContext *context = new DebugAttachContext(this, pid, attach_info); + HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", + DebuggerThreadAttachRoutine, context, &error)); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DebugAttach couldn't attach to process '%u'. %s", (DWORD)pid, + error.AsCString()); + } + + return error; +} + +lldb::thread_result_t +DebuggerThread::DebuggerThreadLaunchRoutine(void *data) +{ + DebugLaunchContext *context = static_cast(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); + + // Set m_is_shutting_down to true if it was false. Return if it was already true. + bool expected = false; + if (!m_is_shutting_down.compare_exchange_strong(expected, true)) + return error; + + // Make a copy of the process, since the termination sequence will reset + // DebuggerThread's internal copy and it needs to remain open for the Wait operation. + HostProcess process_copy = m_process; + lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle(); + + if (terminate) + { + // Initiate the termination before continuing the exception, so that the next debug + // event we get is the exit process event, and not some other event. + BOOL terminate_suceeded = TerminateProcess(handle, 0); + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'", + handle, pid, (terminate_suceeded ? "true" : "false")); + } + + // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint + // messing around in the debugger), continue it now. But only AFTER calling TerminateProcess + // to make sure that the very next call to WaitForDebugEvent is an exit process event. + if (m_active_exception.get()) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "StopDebugging masking active exception"); + + ContinueAsyncException(ExceptionResult::MaskException); + } + + if (!terminate) + { + // Indicate that we want to detach. + m_pid_to_detach = GetProcess().GetProcessId(); + + // Force a fresh break so that the detach can happen from the debugger thread. + if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle())) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + } + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid); + + DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000); + if (wait_result != WAIT_OBJECT_0) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u", + m_debugging_ended_event, wait_result); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid); + } + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "StopDebugging encountered an error while trying to stop process %u. %s", + pid, error.AsCString()); + } + return error; +} + +void +DebuggerThread::ContinueAsyncException(ExceptionResult result) +{ + if (!m_active_exception.get()) + return; + + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION, + "ContinueAsyncException called for inferior process %I64u, broadcasting.", + m_process.GetProcessId()); + + m_active_exception.reset(); + m_exception_pred.SetValue(result, eBroadcastAlways); +} + +void +DebuggerThread::FreeProcessHandles() +{ + m_process = HostProcess(); + m_main_thread = HostThread(); + if (m_image_file) + { + ::CloseHandle(m_image_file); + m_image_file = nullptr; + } +} + +void +DebuggerThread::DebugLoop() +{ + DEBUG_EVENT dbe = {0}; + bool should_debug = true; + WINLOG_IFALL(WINDOWS_LOG_EVENT, "Entering WaitForDebugEvent loop"); + while (should_debug) + { + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "Calling WaitForDebugEvent"); + BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE); + if (wait_result) + { + DWORD continue_status = DBG_CONTINUE; + switch (dbe.dwDebugEventCode) + { + case EXCEPTION_DEBUG_EVENT: + { + ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); + + if (status == ExceptionResult::MaskException) + continue_status = DBG_CONTINUE; + else if (status == ExceptionResult::SendToApplication) + continue_status = DBG_EXCEPTION_NOT_HANDLED; + + break; + } + case CREATE_THREAD_DEBUG_EVENT: + continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); + break; + case CREATE_PROCESS_DEBUG_EVENT: + continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); + break; + case EXIT_THREAD_DEBUG_EVENT: + continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); + break; + case EXIT_PROCESS_DEBUG_EVENT: + continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); + should_debug = false; + break; + case LOAD_DLL_DEBUG_EVENT: + continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); + break; + case UNLOAD_DLL_DEBUG_EVENT: + continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); + break; + case OUTPUT_DEBUG_STRING_EVENT: + continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); + break; + case RIP_EVENT: + continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); + if (dbe.u.RipInfo.dwType == SLE_ERROR) + should_debug = false; + break; + } + + WINLOGD_IFALL(WINDOWS_LOG_EVENT, "DebugLoop calling ContinueDebugEvent(%u, %u, %u) on thread %u.", + dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId()); + + ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); + + if (m_detached) + { + should_debug = false; + } + } + else + { + WINERR_IFALL(WINDOWS_LOG_EVENT, + "DebugLoop returned FALSE from WaitForDebugEvent. Error = %u", + ::GetCurrentThreadId(), ::GetLastError()); + + should_debug = false; + } + } + FreeProcessHandles(); + + WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting."); + SetEvent(m_debugging_ended_event); +} + +ExceptionResult +DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id) +{ + if (m_is_shutting_down) + { + // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a magic exception that + // we use simply to wake up the DebuggerThread so that we can close out the debug loop. + if (m_pid_to_detach != 0 && info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS, + "Breakpoint exception is cue to detach from process 0x%x", + m_pid_to_detach); + ::DebugActiveProcessStop(m_pid_to_detach); + m_detached = true; + } + + // Don't perform any blocking operations while we're shutting down. That will + // cause TerminateProcess -> WaitForSingleObject to time out. + return ExceptionResult::SendToApplication; + } + + bool first_chance = (info.dwFirstChance != 0); + + m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord, thread_id)); + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION, + "HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x", + first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id); + + ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, + *m_active_exception); + m_exception_pred.SetValue(result, eBroadcastNever); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent waiting for ExceptionPred != BreakInDebugger"); + + m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result); + + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION, + "DebuggerThread::HandleExceptionEvent got ExceptionPred = %u", + m_exception_pred.GetValue()); + + return result; +} + +DWORD +DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id) +{ + WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD, + "HandleCreateThreadEvent Thread 0x%x spawned in process %I64u", + thread_id, m_process.GetProcessId()); + HostThread thread(info.hThread); + thread.GetNativeThread().SetOwnsHandle(false); + m_debug_delegate->OnCreateThread(thread); + return DBG_CONTINUE; +} + +DWORD +DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id) +{ + uint32_t process_id = ::GetProcessId(info.hProcess); + + WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_PROCESS, "HandleCreateProcessEvent process %u spawned", process_id); + + std::string thread_name; + llvm::raw_string_ostream name_stream(thread_name); + name_stream << "lldb.plugin.process-windows.slave[" << process_id << "]"; + name_stream.flush(); + ThisThread::SetName(thread_name.c_str()); + + // info.hProcess and info.hThread are closed automatically by Windows when + // EXIT_PROCESS_DEBUG_EVENT is received. + m_process = HostProcess(info.hProcess); + ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); + m_main_thread = HostThread(info.hThread); + m_main_thread.GetNativeThread().SetOwnsHandle(false); + m_image_file = info.hFile; + + lldb::addr_t load_addr = reinterpret_cast(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; +} diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.h b/source/Plugins/Process/Windows/Live/DebuggerThread.h new file mode 100644 index 0000000000000..6a26194139508 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.h @@ -0,0 +1,100 @@ +//===-- DebuggerThread.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_DebuggerThread_H_ +#define liblldb_Plugins_Process_Windows_DebuggerThread_H_ + +#include +#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). + std::atomic m_is_shutting_down; // Signals the debug loop to stop processing certain types of + // events that block shutdown. + bool m_detached; // Indicates we've detached from the inferior process and the debug loop can exit. + + static lldb::thread_result_t DebuggerThreadLaunchRoutine(void *data); + lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info); + static lldb::thread_result_t DebuggerThreadAttachRoutine(void *data); + lldb::thread_result_t DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &launch_info); +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ForwardDecl.h b/source/Plugins/Process/Windows/Live/ForwardDecl.h new file mode 100644 index 0000000000000..a782597555e12 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ForwardDecl.h @@ -0,0 +1,41 @@ +//===-- ForwardDecl.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_ForwardDecl_H_ +#define liblldb_Plugins_Process_Windows_ForwardDecl_H_ + +#include + +// ExceptionResult is returned by the debug delegate to specify how it processed +// the exception. +enum class ExceptionResult +{ + BreakInDebugger, // Break in the debugger and give the user a chance to interact with + // the program before continuing. + MaskException, // Eat the exception and don't let the application know it occurred. + SendToApplication // Send the exception to the application to be handled as if there were + // no debugger attached. +}; + +namespace lldb_private +{ + +class ProcessWindows; + +class IDebugDelegate; +class DebuggerThread; +class ExceptionRecord; + +typedef std::shared_ptr DebugDelegateSP; +typedef std::shared_ptr DebuggerThreadSP; +typedef std::shared_ptr ExceptionRecordSP; +typedef std::unique_ptr ExceptionRecordUP; +} + +#endif \ No newline at end of file diff --git a/source/Plugins/Process/Windows/Live/IDebugDelegate.h b/source/Plugins/Process/Windows/Live/IDebugDelegate.h new file mode 100644 index 0000000000000..0e7849bb8ffeb --- /dev/null +++ b/source/Plugins/Process/Windows/Live/IDebugDelegate.h @@ -0,0 +1,46 @@ +//===-- IDebugDelegate.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_IDebugDelegate_H_ +#define liblldb_Plugins_Process_Windows_IDebugDelegate_H_ + +#include "ForwardDecl.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include + +namespace lldb_private +{ +class Error; +class HostThread; + +//---------------------------------------------------------------------- +// IDebugDelegate +// +// IDebugDelegate defines an interface which allows implementors to receive +// notification of events that happen in a debugged process. +//---------------------------------------------------------------------- +class IDebugDelegate +{ + public: + virtual ~IDebugDelegate() {} + + virtual void OnExitProcess(uint32_t exit_code) = 0; + virtual void OnDebuggerConnected(lldb::addr_t image_base) = 0; + virtual ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) = 0; + virtual void OnCreateThread(const HostThread &thread) = 0; + virtual void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) = 0; + virtual void OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) = 0; + virtual void OnUnloadDll(lldb::addr_t module_addr) = 0; + virtual void OnDebugString(const std::string &string) = 0; + virtual void OnDebuggerError(const Error &error, uint32_t type) = 0; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp new file mode 100644 index 0000000000000..a0ac9725c7560 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.cpp @@ -0,0 +1,91 @@ +//===-- LocalDebugDelegate.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" + +using namespace lldb; +using namespace lldb_private; + +LocalDebugDelegate::LocalDebugDelegate(ProcessWP process) + : m_process(process) +{ +} + +void +LocalDebugDelegate::OnExitProcess(uint32_t exit_code) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnExitProcess(exit_code); +} + +void +LocalDebugDelegate::OnDebuggerConnected(lldb::addr_t image_base) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebuggerConnected(image_base); +} + +ExceptionResult +LocalDebugDelegate::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + return process->OnDebugException(first_chance, record); + else + return ExceptionResult::MaskException; +} + +void +LocalDebugDelegate::OnCreateThread(const HostThread &thread) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnCreateThread(thread); +} + +void +LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnExitThread(thread_id, exit_code); +} + +void +LocalDebugDelegate::OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnLoadDll(module_spec, module_addr); +} + +void +LocalDebugDelegate::OnUnloadDll(lldb::addr_t module_addr) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnUnloadDll(module_addr); +} + +void +LocalDebugDelegate::OnDebugString(const std::string &string) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebugString(string); +} + +void +LocalDebugDelegate::OnDebuggerError(const Error &error, uint32_t type) +{ + if (ProcessWindowsLiveSP process = GetProcessPointer()) + process->OnDebuggerError(error, type); +} + +ProcessWindowsLiveSP +LocalDebugDelegate::GetProcessPointer() +{ + ProcessSP process = m_process.lock(); + return std::static_pointer_cast(process); +} diff --git a/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h new file mode 100644 index 0000000000000..ec2f9418142f9 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/LocalDebugDelegate.h @@ -0,0 +1,68 @@ +//===-- LocalDebugDelegate.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ +#define liblldb_Plugins_Process_Windows_LocalDebugDelegate_H_ + +#include + +#include "IDebugDelegate.h" + +#include "lldb/lldb-forward.h" + +namespace lldb_private +{ + +class ProcessWindowsLive; +typedef std::shared_ptr ProcessWindowsLiveSP; + +//---------------------------------------------------------------------- +// LocalDebugDelegate +// +// LocalDebugDelegate creates a connection between a ProcessWindowsLive and the +// debug driver. This serves to decouple ProcessWindowsLive from the debug driver. +// It would be possible to get a similar decoupling by just having +// ProcessWindowsLive implement this interface directly. There are two reasons why +// we don't do this: +// +// 1) In the future when we add support for local debugging through LLGS, and we +// go through the Native*Protocol interface, it is likely we will need the +// additional flexibility provided by this sort of adapter pattern. +// 2) LLDB holds a shared_ptr to the ProcessWindows, and our driver thread +// needs access to it as well. To avoid a race condition, we want to make +// sure that we're also holding onto a shared_ptr. +// lldb_private::Process supports enable_shared_from_this, but that gives us +// a ProcessSP (which is exactly what we are trying to decouple from the +// driver), so this adapter serves as a way to transparently hold the +// ProcessSP while still keeping it decoupled from the driver. +//---------------------------------------------------------------------- +class LocalDebugDelegate : public IDebugDelegate +{ + public: + explicit LocalDebugDelegate(lldb::ProcessWP process); + + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) override; + void OnCreateThread(const HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + void OnDebugString(const std::string &message) override; + void OnDebuggerError(const Error &error, uint32_t type) override; + + private: + ProcessWindowsLiveSP + GetProcessPointer(); + + lldb::ProcessWP m_process; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp new file mode 100644 index 0000000000000..55102cc12ebba --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp @@ -0,0 +1,989 @@ +//===-- ProcessWindowsLive.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Windows includes +#include "lldb/Host/windows/windows.h" +#include + +// 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/Common/ProcessWindowsLog.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "LocalDebugDelegate.h" +#include "ProcessWindowsLive.h" +#include "TargetThreadWindowsLive.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb; +using namespace lldb_private; + +#define BOOL_STR(b) ((b) ? "true" : "false") + +namespace +{ + +std::string +GetProcessExecutableName(HANDLE process_handle) +{ + std::vector 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 +ProcessWindowsLive::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *) +{ + return ProcessSP(new ProcessWindowsLive(target_sp, listener)); +} + +void +ProcessWindowsLive::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + }); +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindowsLive::ProcessWindowsLive(lldb::TargetSP target_sp, Listener &listener) + : lldb_private::ProcessWindows(target_sp, listener) +{ +} + +ProcessWindowsLive::~ProcessWindowsLive() +{ +} + +void +ProcessWindowsLive::Terminate() +{ +} + +lldb_private::ConstString +ProcessWindowsLive::GetPluginNameStatic() +{ + static ConstString g_name("windows"); + return g_name; +} + +const char * +ProcessWindowsLive::GetPluginDescriptionStatic() +{ + return "Process plugin for Windows"; +} + +Error +ProcessWindowsLive::EnableBreakpointSite(BreakpointSite *bp_site) +{ + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite called with bp_site 0x%p " + "(id=%d, addr=0x%x)", + bp_site->GetID(), bp_site->GetLoadAddress()); + + Error error = EnableSoftwareBreakpoint(bp_site); + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite failed. %s", error.AsCString()); + } + return error; +} + +Error +ProcessWindowsLive::DisableBreakpointSite(BreakpointSite *bp_site) +{ + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite called with bp_site 0x%p " + "(id=%d, addr=0x%x)", + bp_site->GetID(), bp_site->GetLoadAddress()); + + Error error = DisableSoftwareBreakpoint(bp_site); + + if (!error.Success()) + { + WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite failed. %s", error.AsCString()); + } + return error; +} + +bool +ProcessWindowsLive::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // Add all the threads that were previously running and for which we did not detect a thread + // exited event. + int new_size = 0; + int continued_threads = 0; + int exited_threads = 0; + int new_threads = 0; + + for (ThreadSP old_thread : old_thread_list.Threads()) + { + lldb::tid_t old_thread_id = old_thread->GetID(); + auto exited_thread_iter = m_session_data->m_exited_threads.find(old_thread_id); + if (exited_thread_iter == m_session_data->m_exited_threads.end()) + { + new_thread_list.AddThread(old_thread); + ++new_size; + ++continued_threads; + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and is still running.", + old_thread_id); + } + else + { + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and has exited.", + old_thread_id); + ++exited_threads; + } + } + + // Also add all the threads that are new since the last time we broke into the debugger. + for (const auto &thread_info : m_session_data->m_new_threads) + { + ThreadSP thread(new TargetThreadWindowsLive(*this, thread_info.second)); + thread->SetID(thread_info.first); + new_thread_list.AddThread(thread); + ++new_size; + ++new_threads; + WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u is new since last update.", thread_info.first); + } + + WINLOG_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - %d new threads, %d old threads, %d exited threads.", + new_threads, continued_threads, exited_threads); + + m_session_data->m_new_threads.clear(); + m_session_data->m_exited_threads.clear(); + + return new_size > 0; +} + +Error +ProcessWindowsLive::DoLaunch(Module *exe_module, + ProcessLaunchInfo &launch_info) +{ + // Even though m_session_data is accessed here, it is before a debugger thread has been + // kicked off. So there's no race conditions, and it shouldn't be necessary to acquire + // the mutex. + + Error result; + if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) + { + StreamString stream; + stream.Printf("ProcessWindows unable to launch '%s'. ProcessWindows can only be used for debug launches.", + launch_info.GetExecutableFile().GetPath().c_str()); + std::string message = stream.GetString(); + result.SetErrorString(message.c_str()); + + WINERR_IFALL(WINDOWS_LOG_PROCESS, message.c_str()); + return result; + } + + bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); + m_session_data.reset(new ProcessWindowsData(stop_at_entry)); + + SetPrivateState(eStateLaunching); + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + m_session_data->m_debugger.reset(new DebuggerThread(delegate)); + DebuggerThreadSP debugger = m_session_data->m_debugger; + + // Kick off the DebugLaunch asynchronously and wait for it to complete. + result = debugger->DebugLaunch(launch_info); + if (result.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); + return result; + } + + HostProcess process; + Error error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", + launch_info.GetExecutableFile().GetPath().c_str(), error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", + launch_info.GetExecutableFile().GetPath().c_str()); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + launch_info.SetProcessID(process.GetProcessId()); + SetID(process.GetProcessId()); + + return result; +} + +Error +ProcessWindowsLive::DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + m_session_data.reset(new ProcessWindowsData(!attach_info.GetContinueOnceAttached())); + + DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); + DebuggerThreadSP debugger(new DebuggerThread(delegate)); + + m_session_data->m_debugger = debugger; + + DWORD process_id = static_cast(pid); + Error error = debugger->DebugAttach(process_id, attach_info); + if (error.Fail()) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error occurred initiating the asynchronous attach. %s", + error.AsCString()); + return error; + } + + HostProcess process; + error = WaitForDebuggerConnection(debugger, process); + if (error.Fail()) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, + "DoAttachToProcessWithID encountered an error waiting for the debugger to connect. %s", + error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoAttachToProcessWithID successfully attached to process with pid=%u", + process_id); + + // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state + // should already be set to eStateStopped as a result of hitting the initial breakpoint. If + // it was not set, the breakpoint should have already been resumed from and the private state + // should already be eStateRunning. + SetID(process.GetProcessId()); + return error; +} + +Error +ProcessWindowsLive::WaitForDebuggerConnection(DebuggerThreadSP debugger, HostProcess &process) +{ + Error result; + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection Waiting for loader breakpoint."); + + // Block this function until we receive the initial stop from the process. + if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0) + { + WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_BREAKPOINTS, "WaitForDebuggerConnection hit loader breakpoint, returning."); + + process = debugger->GetProcess(); + return m_session_data->m_launch_error; + } + else + return Error(::GetLastError(), eErrorTypeWin32); +} + +Error +ProcessWindowsLive::DoResume() +{ + llvm::sys::ScopedLock lock(m_mutex); + Error error; + + StateType private_state = GetPrivateState(); + if (private_state == eStateStopped || private_state == eStateCrashed) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u while state is %u. Resuming...", + m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + + ExceptionRecordSP active_exception = + m_session_data->m_debugger->GetActiveException().lock(); + if (active_exception) + { + // Resume the process and continue processing debug events. Mask + // the exception so that from the process's view, there is no + // indication that anything happened. + m_session_data->m_debugger->ContinueAsyncException( + ExceptionResult::MaskException); + } + + WINLOG_IFANY(WINDOWS_LOG_PROCESS | WINDOWS_LOG_THREAD, "DoResume resuming %u threads.", + m_thread_list.GetSize()); + + for (int i = 0; i < m_thread_list.GetSize(); ++i) + { + auto thread = std::static_pointer_cast( + m_thread_list.GetThreadAtIndex(i)); + thread->DoResume(); + } + + SetPrivateState(eStateRunning); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u but state is %u. Returning...", + m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState()); + } + return error; +} + + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +lldb_private::ConstString +ProcessWindowsLive::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWindowsLive::GetPluginVersion() +{ + return 1; +} + +Error +ProcessWindowsLive::DoDetach(bool keep_stopped) +{ + DebuggerThreadSP debugger_thread; + StateType private_state; + { + // Acquire the lock only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which + // will also acquire the lock. Thus we have to release the lock before + // calling StopDebugging(). + llvm::sys::ScopedLock lock(m_mutex); + + private_state = GetPrivateState(); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called while state = %u, but there is no active session.", + private_state); + return Error(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Error error; + if (private_state != eStateExited && private_state != eStateDetached) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called for process %I64u while state = %u. Detaching...", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + error = debugger_thread->StopDebugging(false); + if (error.Success()) + { + SetPrivateState(eStateDetached); + } + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DoDetach called for process %I64u while state = %u, but cannot destroy in this state.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + } + + return error; +} + +Error +ProcessWindowsLive::DoDestroy() +{ + DebuggerThreadSP debugger_thread; + StateType private_state; + { + // Acquire this lock inside an inner scope, only long enough to get the DebuggerThread. + // StopDebugging() will trigger a call back into ProcessWindows which will acquire the lock + // again, so we need to not deadlock. + llvm::sys::ScopedLock lock(m_mutex); + + private_state = GetPrivateState(); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called while state = %u, but there is no active session.", + private_state); + return Error(); + } + + debugger_thread = m_session_data->m_debugger; + } + + Error error; + if (private_state != eStateExited && private_state != eStateDetached) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called for process %I64u while state = %u. Shutting down...", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + error = debugger_thread->StopDebugging(true); + + // By the time StopDebugging returns, there is no more debugger thread, so + // we can be assured that no other thread will race for the session data. + m_session_data.reset(); + } + else + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, + "DoDestroy called for process %I64u while state = %u, but cannot destroy in this state.", + debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state); + } + + return error; +} + +void +ProcessWindowsLive::RefreshStateAfterStop() +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called with no active session. Returning..."); + return; + } + + m_thread_list.RefreshStateAfterStop(); + + std::weak_ptr exception_record = m_session_data->m_debugger->GetActiveException(); + ExceptionRecordSP active_exception = exception_record.lock(); + if (!active_exception) + { + WINERR_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called for process %I64u but there is no " + "active exception. Why is the process stopped?", + m_session_data->m_debugger->GetProcess().GetProcessId()); + return; + } + + StopInfoSP stop_info; + m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + ThreadSP stop_thread = m_thread_list.GetSelectedThread(); + if (!stop_thread) + return; + + RegisterContextSP register_context = stop_thread->GetRegisterContext(); + + // The current EIP is AFTER the BP opcode, which is one byte. + uint64_t pc = register_context->GetPC() - 1; + if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT) + { + BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc)); + + if (site) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "RefreshStateAfterStop detected breakpoint in process %I64u at " + "address 0x%I64x with breakpoint site %d", + m_session_data->m_debugger->GetProcess().GetProcessId(), pc, site->GetID()); + + if (site->ValidForThisThread(stop_thread.get())) + { + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "Breakpoint site %d is valid for this thread (0x%I64x), creating stop info.", + site->GetID(), stop_thread->GetID()); + + stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID( + *stop_thread, site->GetID()); + register_context->SetPC(pc); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION, + "Breakpoint site %d is not valid for this thread, creating empty stop info.", + site->GetID()); + } + } + stop_thread->SetStopInfo(stop_info); + } + else if (active_exception->GetExceptionCode() == EXCEPTION_SINGLE_STEP) + { + stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); + stop_thread->SetStopInfo(stop_info); + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u", + stop_thread->GetID()); + } + else + { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8) + << " encountered at address " << llvm::format_hex(pc, 8); + stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); + stop_thread->SetStopInfo(stop_info); + WINLOG_IFALL(WINDOWS_LOG_EXCEPTION, desc_stream.str().c_str()); + } +} + +bool +ProcessWindowsLive::IsAlive() +{ + StateType state = GetPrivateState(); + switch (state) + { + case eStateCrashed: + case eStateDetached: + case eStateUnloaded: + case eStateExited: + case eStateInvalid: + return false; + default: + return true; + } +} + +Error +ProcessWindowsLive::DoHalt(bool &caused_stop) +{ + Error error; + StateType state = GetPrivateState(); + if (state == eStateStopped) + caused_stop = false; + else + { + llvm::sys::ScopedLock lock(m_mutex); + caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle()); + if (!caused_stop) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoHalt called DebugBreakProcess, but it failed with error %u", + error.GetError()); + } + } + return error; +} + +void +ProcessWindowsLive::DidLaunch() +{ + ArchSpec arch_spec; + DidAttach(arch_spec); +} + +void +ProcessWindowsLive::DidAttach(ArchSpec &arch_spec) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // The initial stop won't broadcast the state change event, so account for that here. + if (m_session_data && GetPrivateState() == eStateStopped && m_session_data->m_stop_at_entry) + RefreshStateAfterStop(); +} + +size_t +ProcessWindowsLive::DoReadMemory(lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error) +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + return 0; + + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory attempting to read %u bytes from address 0x%I64x", size, vm_addr); + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast(vm_addr); + SIZE_T bytes_read = 0; + if (!ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, buf, size, &bytes_read)) + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory failed with error code %u", error.GetError()); + } + return bytes_read; +} + +size_t +ProcessWindowsLive::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) +{ + llvm::sys::ScopedLock lock(m_mutex); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr); + + if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_MEMORY, "DoWriteMemory cannot write, there is no active debugger connection."); + return 0; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + void *addr = reinterpret_cast(vm_addr); + SIZE_T bytes_written = 0; + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (WriteProcessMemory(handle, addr, buf, size, &bytes_written)) + FlushInstructionCache(handle, addr, bytes_written); + else + { + error.SetError(GetLastError(), eErrorTypeWin32); + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory failed with error code %u", error.GetError()); + } + return bytes_written; +} + +Error +ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info) +{ + Error error; + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + error.SetErrorString("GetMemoryRegionInfo called with no debugging session."); + WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); + return error; + } + + HostProcess process = m_session_data->m_debugger->GetProcess(); + lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); + if (handle == nullptr || handle == LLDB_INVALID_PROCESS) + { + error.SetErrorString("GetMemoryRegionInfo called with an invalid target process."); + WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); + return error; + } + + WINLOG_IFALL(WINDOWS_LOG_MEMORY, "GetMemoryRegionInfo getting info for address 0x%I64x", vm_addr); + + void *addr = reinterpret_cast(vm_addr); + MEMORY_BASIC_INFORMATION mem_info = {0}; + SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); + if (result == 0) + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, + "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", + error.GetError(), vm_addr); + return error; + } + const bool readable = IsPageReadable(mem_info.Protect); + const bool executable = IsPageExecutable(mem_info.Protect); + const bool writable = IsPageWritable(mem_info.Protect); + info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + + error.SetError(::GetLastError(), eErrorTypeWin32); + WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", + BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); + return error; +} + +bool +ProcessWindowsLive::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target_sp->GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + // However, if there is no executable module, we return true since we might be preparing to attach. + return true; +} + +void +ProcessWindowsLive::OnExitProcess(uint32_t exit_code) +{ + // No need to acquire the lock since m_session_data isn't accessed. + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Process %u exited with code %u", GetID(), exit_code); + + TargetSP target = m_target_sp.lock(); + if (target) + { + ModuleSP executable_module = target->GetExecutableModule(); + ModuleList unloaded_modules; + unloaded_modules.Append(executable_module); + target->ModulesDidUnload(unloaded_modules, true); + } + + SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); + SetPrivateState(eStateExited); +} + +void +ProcessWindowsLive::OnDebuggerConnected(lldb::addr_t image_base) +{ + DebuggerThreadSP debugger = m_session_data->m_debugger; + + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger connected to process %I64u. Image base = 0x%I64x", + debugger->GetProcess().GetProcessId(), image_base); + + ModuleSP module = GetTarget().GetExecutableModule(); + if (!module) + { + // During attach, we won't have the executable module, so find it now. + const DWORD pid = debugger->GetProcess().GetProcessId(); + const std::string file_name = GetProcessExecutableName(pid); + if (file_name.empty()) + { + return; + } + + FileSpec executable_file(file_name, true); + ModuleSpec module_spec(executable_file); + Error error; + module = GetTarget().GetSharedModule(module_spec, &error); + if (!module) + { + return; + } + + GetTarget().SetExecutableModule(module, false); + } + + bool load_addr_changed; + module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed); + + ModuleList loaded_modules; + loaded_modules.Append(module); + GetTarget().ModulesDidLoad(loaded_modules); + + // Add the main executable module to the list of pending module loads. We can't call + // GetTarget().ModulesDidLoad() here because we still haven't returned from DoLaunch() / DoAttach() yet + // so the target may not have set the process instance to `this` yet. + llvm::sys::ScopedLock lock(m_mutex); + const HostThreadWindows &wmain_thread = debugger->GetMainThread().GetNativeThread(); + m_session_data->m_new_threads[wmain_thread.GetThreadId()] = debugger->GetMainThread(); +} + +ExceptionResult +ProcessWindowsLive::OnDebugException(bool first_chance, const ExceptionRecord &record) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // FIXME: Without this check, occasionally when running the test suite there is + // an issue where m_session_data can be null. It's not clear how this could happen + // but it only surfaces while running the test suite. In order to properly diagnose + // this, we probably need to first figure allow the test suite to print out full + // lldb logs, and then add logging to the process plugin. + if (!m_session_data) + { + WINERR_IFANY(WINDOWS_LOG_EXCEPTION, + "Debugger thread reported exception 0x%x at address 0x%I64x, but there is no session.", + record.GetExceptionCode(), record.GetExceptionAddress()); + return ExceptionResult::SendToApplication; + } + + if (!first_chance) + { + // Any second chance exception is an application crash by definition. + SetPrivateState(eStateCrashed); + } + + ExceptionResult result = ExceptionResult::SendToApplication; + switch (record.GetExceptionCode()) + { + case EXCEPTION_BREAKPOINT: + // Handle breakpoints at the first chance. + result = ExceptionResult::BreakInDebugger; + + if (!m_session_data->m_initial_stop_received) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit loader breakpoint at address 0x%I64x, setting initial stop event.", + record.GetExceptionAddress()); + m_session_data->m_initial_stop_received = true; + ::SetEvent(m_session_data->m_initial_stop_event); + } + else + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS, + "Hit non-loader breakpoint at address 0x%I64x.", + record.GetExceptionAddress()); + } + SetPrivateState(eStateStopped); + break; + case EXCEPTION_SINGLE_STEP: + result = ExceptionResult::BreakInDebugger; + SetPrivateState(eStateStopped); + break; + default: + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION, + "Debugger thread reported exception 0x%x at address 0x%I64x (first_chance=%s)", + record.GetExceptionCode(), record.GetExceptionAddress(), BOOL_STR(first_chance)); + // For non-breakpoints, give the application a chance to handle the exception first. + if (first_chance) + result = ExceptionResult::SendToApplication; + else + result = ExceptionResult::BreakInDebugger; + } + + return result; +} + +void +ProcessWindowsLive::OnCreateThread(const HostThread &new_thread) +{ + llvm::sys::ScopedLock lock(m_mutex); + const HostThreadWindows &wnew_thread = new_thread.GetNativeThread(); + m_session_data->m_new_threads[wnew_thread.GetThreadId()] = new_thread; +} + +void +ProcessWindowsLive::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) +{ + llvm::sys::ScopedLock lock(m_mutex); + + // On a forced termination, we may get exit thread events after the session + // data has been cleaned up. + if (!m_session_data) + return; + + // A thread may have started and exited before the debugger stopped allowing a refresh. + // Just remove it from the new threads list in that case. + auto iter = m_session_data->m_new_threads.find(thread_id); + if (iter != m_session_data->m_new_threads.end()) + m_session_data->m_new_threads.erase(iter); + else + m_session_data->m_exited_threads.insert(thread_id); +} + +void +ProcessWindowsLive::OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) +{ + // Confusingly, there is no Target::AddSharedModule. Instead, calling GetSharedModule() with + // a new module will add it to the module list and return a corresponding ModuleSP. + Error error; + ModuleSP module = GetTarget().GetSharedModule(module_spec, &error); + bool load_addr_changed = false; + module->SetLoadAddress(GetTarget(), module_addr, false, load_addr_changed); + + ModuleList loaded_modules; + loaded_modules.Append(module); + GetTarget().ModulesDidLoad(loaded_modules); +} + +void +ProcessWindowsLive::OnUnloadDll(lldb::addr_t module_addr) +{ + Address resolved_addr; + if (GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + { + ModuleSP module = resolved_addr.GetModule(); + if (module) + { + ModuleList unloaded_modules; + unloaded_modules.Append(module); + GetTarget().ModulesDidUnload(unloaded_modules, false); + } + } +} + +void +ProcessWindowsLive::OnDebugString(const std::string &string) +{ +} + +void +ProcessWindowsLive::OnDebuggerError(const Error &error, uint32_t type) +{ + llvm::sys::ScopedLock lock(m_mutex); + + if (m_session_data->m_initial_stop_received) + { + // This happened while debugging. Do we shutdown the debugging session, try to continue, + // or do something else? + WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred during debugging. Unexpected behavior may result. %s", + error.GetError(), error.AsCString()); + } + else + { + // If we haven't actually launched the process yet, this was an error launching the + // process. Set the internal error and signal the initial stop event so that the DoLaunch + // method wakes up and returns a failure. + m_session_data->m_launch_error = error; + ::SetEvent(m_session_data->m_initial_stop_event); + WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred launching the process before the initial stop. %s", + error.GetError(), error.AsCString()); + return; + } +} diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h new file mode 100644 index 0000000000000..2429f873c8231 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h @@ -0,0 +1,125 @@ +//===-- ProcessWindowsLive.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ + +// C Includes + +// C++ Includes +#include +#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" + +#include "plugins/Process/Windows/Common/ProcessWindows.h" + +class ProcessMonitor; + +namespace lldb_private +{ +class HostProcess; +class ProcessWindowsData; + +class ProcessWindowsLive : public lldb_private::ProcessWindows, public lldb_private::IDebugDelegate +{ +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec *); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindowsLive(lldb::TargetSP target_sp, + lldb_private::Listener &listener); + + ~ProcessWindowsLive(); + + // lldb_private::Process overrides + lldb_private::ConstString GetPluginName() override; + uint32_t GetPluginVersion() override; + + lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; + + lldb_private::Error DoDetach(bool keep_stopped) override; + lldb_private::Error DoLaunch(lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; + lldb_private::Error DoAttachToProcessWithID(lldb::pid_t pid, + const lldb_private::ProcessAttachInfo &attach_info) override; + lldb_private::Error DoResume() override; + lldb_private::Error DoDestroy() override; + lldb_private::Error DoHalt(bool &caused_stop) override; + + void DidLaunch() override; + void DidAttach(lldb_private::ArchSpec &arch_spec) override; + + void RefreshStateAfterStop() override; + + bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; + bool + DestroyRequiresHalt() override + { + return false; + } + bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; + bool IsAlive() override; + + size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error) override; + size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) override; + lldb_private::Error GetMemoryRegionInfo(lldb::addr_t vm_addr, lldb_private::MemoryRegionInfo &info) override; + + // IDebugDelegate overrides. + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, const lldb_private::ExceptionRecord &record) override; + void OnCreateThread(const lldb_private::HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + void OnDebugString(const std::string &string) override; + void OnDebuggerError(const lldb_private::Error &error, uint32_t type) override; + + private: + lldb_private::Error WaitForDebuggerConnection(lldb_private::DebuggerThreadSP debugger, + lldb_private::HostProcess &process); + + llvm::sys::Mutex m_mutex; + + // Data for the active debugging session. + std::unique_ptr m_session_data; +}; + +} + +#endif // liblldb_Plugins_Process_Windows_Live_ProcessWindowsLive_H_ diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp new file mode 100644 index 0000000000000..52b32716e674a --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.cpp @@ -0,0 +1,147 @@ +//===-- TargetThreadWindowsLive.cpp------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/Logging.h" +#include "lldb/Core/State.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/RegisterContext.h" + +#include "TargetThreadWindowsLive.h" +#include "ProcessWindows.h" +#include "ProcessWindowsLog.h" +#include "UnwindLLDB.h" + +#if defined(_WIN64) +#include "x64/RegisterContextWindowsLive_x64.h" +#else +#include "x86/RegisterContextWindowsLive_x86.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +TargetThreadWindowsLive::TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread) + : TargetThreadWindows(process, thread) + , m_host_thread(thread) +{ +} + +TargetThreadWindowsLive::~TargetThreadWindowsLive() +{ + DestroyThread(); +} + +void +TargetThreadWindowsLive::RefreshStateAfterStop() +{ + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + SetState(eStateStopped); + GetRegisterContext()->InvalidateIfNeeded(false); +} + +void +TargetThreadWindowsLive::WillResume(lldb::StateType resume_state) +{ +} + +void +TargetThreadWindowsLive::DidStop() +{ +} + +RegisterContextSP +TargetThreadWindowsLive::GetRegisterContext() +{ + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrameIndex(0); + + return m_reg_context_sp; +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrame(StackFrame *frame) +{ + return CreateRegisterContextForFrameIndex(frame->GetConcreteFrameIndex()); +} + +RegisterContextSP +TargetThreadWindowsLive::CreateRegisterContextForFrameIndex(uint32_t idx) +{ + if (!m_reg_context_sp) + { + ArchSpec arch = HostInfo::GetArchitecture(); + switch (arch.GetMachine()) + { + case llvm::Triple::x86: +#if defined(_WIN64) + // FIXME: This is a Wow64 process, create a RegisterContextWindows_Wow64 +#else + m_reg_context_sp.reset(new RegisterContextWindowsLive_x86(*this, idx)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + m_reg_context_sp.reset(new RegisterContextWindowsLive_x64(*this, idx)); +#else + // LLDB is 32-bit, but the target process is 64-bit. We probably can't debug this. +#endif + default: + break; + } + } + return m_reg_context_sp; +} + +bool +TargetThreadWindowsLive::CalculateStopInfo() +{ + SetStopInfo(m_stop_info_sp); + return true; +} + +Unwind * +TargetThreadWindowsLive::GetUnwinder() +{ + // FIXME: Implement an unwinder based on the Windows unwinder exposed through DIA SDK. + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + return m_unwinder_ap.get(); +} + +bool +TargetThreadWindowsLive::DoResume() +{ + StateType resume_state = GetTemporaryResumeState(); + StateType current_state = GetState(); + if (resume_state == current_state) + return true; + + if (resume_state == eStateStepping) + { + uint32_t flags_index = GetRegisterContext()->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = GetRegisterContext()->ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext()->WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) + { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do + { + previous_suspend_count = ::ResumeThread(thread_handle); + } while (previous_suspend_count > 0); + } + return true; +} diff --git a/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h new file mode 100644 index 0000000000000..15262b9e94239 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/TargetThreadWindowsLive.h @@ -0,0 +1,55 @@ +//===-- TargetThreadWindowsLive.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ +#define liblldb_Plugins_Process_Windows_TargetThreadWindowsLive_H_ + +#include "lldb/lldb-forward.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Thread.h" + +#include "Plugins/Process/Windows/Common/TargetThreadWindows.h" + +namespace lldb_private +{ +class ProcessWindows; +class HostThread; +class StackFrame; + +class TargetThreadWindowsLive : public lldb_private::TargetThreadWindows +{ + public: + TargetThreadWindowsLive(ProcessWindows &process, const HostThread &thread); + virtual ~TargetThreadWindowsLive(); + + // lldb_private::Thread overrides + void RefreshStateAfterStop() override; + void WillResume(lldb::StateType resume_state) override; + void DidStop() override; + lldb::RegisterContextSP GetRegisterContext() override; + lldb::RegisterContextSP CreateRegisterContextForFrame(StackFrame *frame) override; + bool CalculateStopInfo() override; + Unwind *GetUnwinder() override; + + bool DoResume(); + + HostThread + GetHostThread() const + { + return m_host_thread; + } + + private: + lldb::RegisterContextSP CreateRegisterContextForFrameIndex(uint32_t idx); + + HostThread m_host_thread; +}; +} + +#endif diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp new file mode 100644 index 0000000000000..e74647ab68fb5 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.cpp @@ -0,0 +1,171 @@ +//===-- RegisterContextWindowsLive_x64.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "RegisterContextWindowsLive_x64.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextWindowsLive_x64::RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x64::~RegisterContextWindowsLive_x64() +{ +} + + +bool +RegisterContextWindowsLive_x64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + reg_value.SetUInt64(m_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(m_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(m_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(m_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(m_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(m_context.Rsi); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(m_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(m_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(m_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(m_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(m_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(m_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(m_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(m_context.R15); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(m_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(m_context.Rsp); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(m_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(m_context.EFlags); + break; + } + return true; +} + +bool +RegisterContextWindowsLive_x64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + switch (reg_info->kinds[eRegisterKindLLDB]) + { + case lldb_rax_x86_64: + m_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + m_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + m_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + m_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + m_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + m_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + m_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + m_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + m_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + m_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + m_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + m_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + m_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + m_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + m_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + m_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + m_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + m_context.EFlags = reg_value.GetAsUInt64(); + break; + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h new file mode 100644 index 0000000000000..bd250a994d3a8 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x64/RegisterContextWindowsLive_x64.h @@ -0,0 +1,40 @@ +//===-- RegisterContextWindowsLive_x64.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x64_H_ +#define liblldb_RegisterContextWindowsLive_x64_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x64 : public RegisterContextWindows_x64 +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindowsLive_x64(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsLive_x64(); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x64_H_ diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp new file mode 100644 index 0000000000000..f2decc72d16df --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.cpp @@ -0,0 +1,100 @@ +//===-- RegisterContextWindowsLive_x86.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb-x86-register-enums.h" +#include "ProcessWindowsLog.h" +#include "RegisterContextWindowsLive_x86.h" +#include "TargetThreadWindows.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsLive_x86::RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ +} + +RegisterContextWindowsLive_x86::~RegisterContextWindowsLive_x86() +{ +} + + +bool +RegisterContextWindowsLive_x86::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + // Since we cannot only write a single register value to the inferior, we need to make sure + // our cached copy of the register values are fresh. Otherwise when writing EAX, for example, + // we may also overwrite some other register with a stale value. + if (!CacheAllRegisterValues()) + return false; + + uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + switch (reg) + { + case lldb_eax_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EAX", reg_value.GetAsUInt32()); + m_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBX", reg_value.GetAsUInt32()); + m_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ECX", reg_value.GetAsUInt32()); + m_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDX", reg_value.GetAsUInt32()); + m_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDI", reg_value.GetAsUInt32()); + m_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESI", reg_value.GetAsUInt32()); + m_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBP", reg_value.GetAsUInt32()); + m_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESP", reg_value.GetAsUInt32()); + m_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EIP", reg_value.GetAsUInt32()); + m_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EFLAGS", reg_value.GetAsUInt32()); + m_context.EFlags = reg_value.GetAsUInt32(); + break; + default: + WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to unknown register %u", reg_value.GetAsUInt32(), + reg); + } + + // Physically update the registers in the target process. + TargetThreadWindows &wthread = static_cast(m_thread); + return ::SetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h new file mode 100644 index 0000000000000..9554f017408a4 --- /dev/null +++ b/source/Plugins/Process/Windows/Live/x86/RegisterContextWindowsLive_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsLive_x86.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindowsLive_x86_H_ +#define liblldb_RegisterContextWindowsLive_x86_H_ + +#include "lldb/lldb-forward.h" +#include "../../Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsLive_x86 : public RegisterContextWindows_x86 +{ + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextWindowsLive_x86(Thread &thread, uint32_t concrete_frame_idx); + + virtual ~RegisterContextWindowsLive_x86(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsLive_x86_H_ diff --git a/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt b/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt new file mode 100644 index 0000000000000..b43246b00351c --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories(../../Utility) +include_directories(../Common) + +set(PROC_WINDOWS_MINIDUMP_SOURCES + ProcessWinMiniDump.cpp + ThreadWinMiniDump.cpp + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(PROC_WINDOWS_MINIDUMP_SOURCES ${PROC_WINDOWS_MINIDUMP_SOURCES} + x86/RegisterContextWindowsMiniDump_x86.cpp + ) +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(PROC_WINDOWS_MINIDUMP_SOURCES ${PROC_WINDOWS_MINIDUMP_SOURCES} + x64/RegisterContextWindowsMiniDump_x64.cpp + ) +endif() + +add_lldb_library(lldbPluginProcessWinMiniDump + ${PROC_WINDOWS_MINIDUMP_SOURCES} + ) diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp new file mode 100644 index 0000000000000..fbc96f085ed4c --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -0,0 +1,550 @@ +//===-- 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 + +#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#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/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#include "ExceptionRecord.h" +#include "ThreadWinMiniDump.h" + +using namespace lldb_private; + +namespace +{ + +// Getting a string out of a mini dump is a chore. You're usually given a +// relative virtual address (RVA), which points to a counted string that's in +// Windows Unicode (UTF-16). This wrapper handles all the redirection and +// returns a UTF-8 copy of the string. +std::string +GetMiniDumpString(const void *base_addr, const RVA rva) +{ + std::string result; + if (!base_addr) + { + return result; + } + auto md_string = reinterpret_cast(static_cast(base_addr) + rva); + auto source_start = reinterpret_cast(md_string->Buffer); + const auto source_length = ::wcslen(md_string->Buffer); + const auto source_end = source_start + source_length; + result.resize(4*source_length); // worst case length + auto result_start = reinterpret_cast(&result[0]); + const auto result_end = result_start + result.size(); + ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, strictConversion); + const auto result_size = std::distance(reinterpret_cast(&result[0]), result_start); + result.resize(result_size); // shrink to actual length + return result; +} + +} // anonymous namespace + +// 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(lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + process_sp.reset(new ProcessWinMiniDump(target_sp, listener, *crash_file)); + } + return process_sp; +} + +bool +ProcessWinMiniDump::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + // TODO(amccarth): Eventually, this needs some actual logic. + return true; +} + +ProcessWinMiniDump::ProcessWinMiniDump(lldb::TargetSP target_sp, Listener &listener, + const FileSpec &core_file) : + ProcessWindows(target_sp, 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; + } + + GetTarget().SetArchitecture(DetermineArchitecture()); + ReadMiscInfo(); // notably for process ID + ReadModuleList(); + 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) { + const auto &mini_dump_thread = thread_list_ptr->Threads[i]; + auto thread_sp = std::make_shared(*this, mini_dump_thread.ThreadId); + if (mini_dump_thread.ThreadContext.DataSize >= sizeof(CONTEXT)) + { + const CONTEXT *context = reinterpret_cast(static_cast(m_data_up->m_base_addr) + mini_dump_thread.ThreadContext.Rva); + thread_sp->SetContext(context); + } + 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; +} + +bool +ProcessWinMiniDump::WarnBeforeDetach () const +{ + // Since this is post-mortem debugging, there's no need to warn the user + // that quitting the debugger will terminate the process. + return false; +} + +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 we have it all cached our our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t +ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // I don't have a sense of how frequently this is called or how many memory + // ranges a mini dump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + Range range = {0}; + if (!FindMemoryRange(addr, &range)) + { + return 0; + } + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is, then copy it to the destination buffer. + lldbassert(range.start <= addr); + const size_t offset = addr - range.start; + lldbassert(offset < range.size); + const size_t overlap = std::min(size, range.size - offset); + std::memcpy(buf, range.ptr + offset, overlap); + return overlap; +} + +Error +ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info) +{ + Error error; + size_t size; + const auto list = reinterpret_cast(FindDumpStream(MemoryInfoListStream, &size)); + if (list == nullptr || size < sizeof(MINIDUMP_MEMORY_INFO_LIST)) + { + error.SetErrorString("the mini dump contains no memory range information"); + return error; + } + + if (list->SizeOfEntry < sizeof(MINIDUMP_MEMORY_INFO)) + { + error.SetErrorString("the entries in the mini dump memory info list are smaller than expected"); + return error; + } + + if (size < list->SizeOfHeader + list->SizeOfEntry * list->NumberOfEntries) + { + error.SetErrorString("the mini dump memory info list is incomplete"); + return error; + } + + for (int i = 0; i < list->NumberOfEntries; ++i) + { + const auto entry = reinterpret_cast(reinterpret_cast(list) + + list->SizeOfHeader + i * list->SizeOfEntry); + const auto head = entry->BaseAddress; + const auto tail = head + entry->RegionSize; + if (head <= load_addr && load_addr < tail) + { + info.SetReadable(IsPageReadable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(IsPageWritable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(IsPageExecutable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + return error; + } + } + // Note that the memory info list doesn't seem to contain ranges in kernel space, + // so if you're walking a stack that has kernel frames, the stack may appear + // truncated. + error.SetErrorString("address is not in a known range"); + return error; +} + +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); + }); +} + +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; + } +} + +bool +ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const +{ + size_t stream_size = 0; + auto mem_list_stream = static_cast(FindDumpStream(MemoryListStream, &stream_size)); + if (mem_list_stream) + { + for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR &mem_desc = mem_list_stream->MemoryRanges[i]; + const MINIDUMP_LOCATION_DESCRIPTOR &loc_desc = mem_desc.Memory; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = loc_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast(m_data_up->m_base_addr) + loc_desc.Rva; + return true; + } + } + } + + // Some mini dumps have a Memory64ListStream that captures all the heap + // memory. We can't exactly use the same loop as above, because the mini + // dump uses slightly different data structures to describe those. + auto mem_list64_stream = static_cast(FindDumpStream(Memory64ListStream, &stream_size)); + if (mem_list64_stream) + { + size_t base_rva = mem_list64_stream->BaseRva; + for (ULONG32 i = 0; i < mem_list64_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR64 &mem_desc = mem_list64_stream->MemoryRanges[i]; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = mem_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast(m_data_up->m_base_addr) + base_rva; + return true; + } + base_rva += range_size; + } + } + + return false; +} + + +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::ReadMiscInfo() +{ + size_t size = 0; + const auto misc_info_ptr = static_cast(FindDumpStream(MiscInfoStream, &size)); + if (!misc_info_ptr || size < sizeof(MINIDUMP_MISC_INFO)) { + return; + } + + if ((misc_info_ptr->Flags1 & MINIDUMP_MISC1_PROCESS_ID) != 0) { + // This misc info record has the process ID. + SetID(misc_info_ptr->ProcessId); + } +} + +void +ProcessWinMiniDump::ReadModuleList() +{ + size_t size = 0; + auto module_list_ptr = static_cast(FindDumpStream(ModuleListStream, &size)); + if (!module_list_ptr || module_list_ptr->NumberOfModules == 0) + { + return; + } + + for (ULONG32 i = 0; i < module_list_ptr->NumberOfModules; ++i) + { + const auto &module = module_list_ptr->Modules[i]; + const auto file_name = GetMiniDumpString(m_data_up->m_base_addr, module.ModuleNameRva); + ModuleSpec module_spec = FileSpec(file_name, true); + + lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec); + if (!module_sp) + { + continue; + } + bool load_addr_changed = false; + module_sp->SetLoadAddress(GetTarget(), module.BaseOfImage, false, load_addr_changed); + } +} + +void * +ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) const +{ + 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; +} diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h new file mode 100644 index 0000000000000..12864be371275 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h @@ -0,0 +1,140 @@ +//===-- 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" + +#include "Plugins/Process/Windows/Common/ProcessWindows.h" + +struct ThreadData; + +class ProcessWinMiniDump : public lldb_private::ProcessWindows +{ + public: + static lldb::ProcessSP + CreateInstance (lldb::TargetSP target_sp, + 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::TargetSP target_sp, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + virtual + ~ProcessWinMiniDump(); + + bool + CanDebug(lldb::TargetSP target_sp, 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; + + bool + WarnBeforeDetach () const 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_private::ArchSpec + GetArchitecture(); + + lldb_private::Error + GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &range_info) override; + + protected: + void + Clear(); + + bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + private: + // Describes a range of memory captured in the mini dump. + struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + size_t size; // size of the range in bytes + const uint8_t *ptr; // absolute pointer to the first byte of the range + }; + + // If the mini dump has a memory range that contains the desired address, it + // returns true with the details of the range in *range_out. Otherwise, it + // returns false. + bool + FindMemoryRange(lldb::addr_t addr, Range *range_out) const; + + lldb_private::Error + MapMiniDumpIntoMemory(const char *file); + + lldb_private::ArchSpec + DetermineArchitecture(); + + void + ReadExceptionRecord(); + + void + ReadMiscInfo(); + + void + ReadModuleList(); + + // 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) const; + + // 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_ diff --git a/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp new file mode 100644 index 0000000000000..ddcd15b1ae120 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.cpp @@ -0,0 +1,104 @@ +//===-- 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" + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/windows/windows.h" +#include + +#include "ProcessWinMiniDump.h" +#if defined(_WIN64) +#include "x64/RegisterContextWindowsMiniDump_x64.h" +#else +#include "x86/RegisterContextWindowsMiniDump_x86.h" +#endif + +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. + +class ThreadWinMiniDump::Data { + public: + Data() : m_context(nullptr) {} + const CONTEXT *m_context; +}; + +ThreadWinMiniDump::ThreadWinMiniDump(lldb_private::Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_data(new Data) +{ +} + +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; + 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 + reg_ctx_sp.reset(new RegisterContextWindowsMiniDump_x86(*this, concrete_frame_idx, m_data->m_context)); +#endif + break; + case llvm::Triple::x86_64: +#if defined(_WIN64) + reg_ctx_sp.reset(new RegisterContextWindowsMiniDump_x64(*this, concrete_frame_idx, m_data->m_context)); +#else + // LLDB is 32-bit, but the target process is 64-bit. We probably can't debug this. +#endif + default: + break; + } + return reg_ctx_sp; +} + +void +ThreadWinMiniDump::ClearStackFrames() +{ +} + +void +ThreadWinMiniDump::SetContext(const void *context) +{ + if (m_data) + { + m_data->m_context = static_cast(context); + } +} + +bool +ThreadWinMiniDump::CalculateStopInfo() +{ + return false; +} diff --git a/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h new file mode 100644 index 0000000000000..c789254221023 --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/ThreadWinMiniDump.h @@ -0,0 +1,49 @@ +//===-- 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; + + void + SetContext(const void *context); + +protected: + lldb::RegisterContextSP m_reg_context_sp; + class Data; + std::unique_ptr m_data; // for WinAPI-specific data + + bool CalculateStopInfo() override; +}; + +#endif diff --git a/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp new file mode 100644 index 0000000000000..41d9195f1eedb --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.cpp @@ -0,0 +1,47 @@ +//===-- RegisterContextWindowsMiniDump_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/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump_x64.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsMiniDump_x64::RegisterContextWindowsMiniDump_x64(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context) + : RegisterContextWindows_x64(thread, concrete_frame_idx) +{ + if (context) + { + m_context = *context; + m_context_stale = false; + } +} + +RegisterContextWindowsMiniDump_x64::~RegisterContextWindowsMiniDump_x64() +{ +} + +bool +RegisterContextWindowsMiniDump_x64::WriteRegister(const RegisterInfo * /* reg_info */, const RegisterValue & /* reg_value */) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump_x64::CacheAllRegisterValues() +{ + // Since this is post-mortem debugging, we either have the context or we don't. + return !m_context_stale; +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h new file mode 100644 index 0000000000000..86d58046113fc --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x64/RegisterContextWindowsMiniDump_x64.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsMiniDump_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_RegisterContextWindowsMiniDump_x64_H_ +#define liblldb_RegisterContextWindowsMiniDump_x64_H_ + +#include "lldb/lldb-forward.h" +#include "Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump_x64 : public RegisterContextWindows_x64 +{ + public: + RegisterContextWindowsMiniDump_x64(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context); + + virtual ~RegisterContextWindowsMiniDump_x64(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + protected: + bool CacheAllRegisterValues() override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_x64_H_ diff --git a/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp new file mode 100644 index 0000000000000..2c8a069c5376e --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.cpp @@ -0,0 +1,47 @@ +//===-- RegisterContextWindowsMiniDump_x86.cpp ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-types.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextWindowsMiniDump_x86.h" + +using namespace lldb; + +namespace lldb_private +{ + +RegisterContextWindowsMiniDump_x86::RegisterContextWindowsMiniDump_x86(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context) + : RegisterContextWindows_x86(thread, concrete_frame_idx) +{ + if (context) + { + m_context = *context; + m_context_stale = false; + } +} + +RegisterContextWindowsMiniDump_x86::~RegisterContextWindowsMiniDump_x86() +{ +} + +bool +RegisterContextWindowsMiniDump_x86::WriteRegister(const RegisterInfo * /* reg_info */, const RegisterValue & /* reg_value */) +{ + return false; +} + +bool +RegisterContextWindowsMiniDump_x86::CacheAllRegisterValues() +{ + // Since this is post-mortem debugging, we either have the context or we don't. + return !m_context_stale; +} + +} // namespace lldb_private diff --git a/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h new file mode 100644 index 0000000000000..d36e0cfd9e7bc --- /dev/null +++ b/source/Plugins/Process/Windows/MiniDump/x86/RegisterContextWindowsMiniDump_x86.h @@ -0,0 +1,36 @@ +//===-- RegisterContextWindowsMiniDump_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_RegisterContextWindowsMiniDump_x86_H_ +#define liblldb_RegisterContextWindowsMiniDump_x86_H_ + +#include "lldb/lldb-forward.h" +#include "Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h" + +namespace lldb_private +{ + +class Thread; + +class RegisterContextWindowsMiniDump_x86 : public RegisterContextWindows_x86 +{ + public: + RegisterContextWindowsMiniDump_x86(Thread &thread, uint32_t concrete_frame_idx, const CONTEXT *context); + + virtual ~RegisterContextWindowsMiniDump_x86(); + + bool WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + protected: + bool CacheAllRegisterValues() override; +}; + +} + +#endif // #ifndef liblldb_RegisterContextWindowsMiniDump_x86_H_ -- cgit v1.2.3