diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Windows/Common')
7 files changed, 259 insertions, 67 deletions
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h index fd0b0e5bc0a..b4d053f8834 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h @@ -41,7 +41,7 @@ public: HANDLE m_initial_stop_event = nullptr; bool m_initial_stop_received = false; bool m_stop_at_entry; - std::map<lldb::tid_t, HostThread> m_new_threads; + std::map<lldb::tid_t, lldb::ThreadSP> m_new_threads; std::set<lldb::tid_t> m_exited_threads; }; diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 7303e177b46..461dbebb3d8 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -12,6 +12,7 @@ #include "lldb/Host/windows/windows.h" #include <psapi.h> +#include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" @@ -104,7 +105,10 @@ const char *ProcessWindows::GetPluginDescriptionStatic() { ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) - : lldb_private::Process(target_sp, listener_sp) {} + : lldb_private::Process(target_sp, listener_sp), + m_watchpoint_ids( + RegisterContextWindows::GetNumHardwareBreakpointSlots(), + LLDB_INVALID_BREAK_ID) {} ProcessWindows::~ProcessWindows() {} @@ -382,11 +386,35 @@ void ProcessWindows::RefreshStateAfterStop() { stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, site->GetID()); stop_thread->SetStopInfo(stop_info); - } else { - LLDB_LOG(log, "single stepping thread {0}", stop_thread->GetID()); - stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); + + return; + } + + auto *reg_ctx = static_cast<RegisterContextWindows *>( + stop_thread->GetRegisterContext().get()); + uint32_t slot_id = reg_ctx->GetTriggeredHardwareBreakpointSlotId(); + if (slot_id != LLDB_INVALID_INDEX32) { + int id = m_watchpoint_ids[slot_id]; + LLDB_LOG(log, + "Single-stepped onto a watchpoint in process {0} at address " + "{1:x} with watchpoint {2}", + m_session_data->m_debugger->GetProcess().GetProcessId(), pc, id); + + if (lldb::WatchpointSP wp_sp = + GetTarget().GetWatchpointList().FindByID(id)) + wp_sp->SetHardwareIndex(slot_id); + + stop_info = StopInfo::CreateStopReasonWithWatchpointID( + *stop_thread, id, m_watchpoints[id].address); stop_thread->SetStopInfo(stop_info); + + return; } + + LLDB_LOG(log, "single stepping thread {0}", stop_thread->GetID()); + stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); + stop_thread->SetStopInfo(stop_info); + return; } @@ -494,9 +522,7 @@ bool ProcessWindows::UpdateThreadList(ThreadList &old_thread_list, // Also add all the threads that are new since the last time we broke into // the debugger. for (const auto &thread_info : m_session_data->m_new_threads) { - ThreadSP thread(new TargetThreadWindows(*this, thread_info.second)); - thread->SetID(thread_info.first); - new_thread_list.AddThread(thread); + new_thread_list.AddThread(thread_info.second); ++new_size; ++new_threads; LLDB_LOGV(log, "Thread {0} is new since last update.", thread_info.first); @@ -588,9 +614,9 @@ void ProcessWindows::OnExitProcess(uint32_t exit_code) { SetProcessExitStatus(GetID(), true, 0, exit_code); SetPrivateState(eStateExited); - // If the process exits before any initial stop then notify the debugger + // If the process exits before any initial stop then notify the debugger // of the error otherwise WaitForDebuggerConnection() will be blocked. - // An example of this issue is when a process fails to load a dependent DLL. + // An example of this issue is when a process fails to load a dependent DLL. if (m_session_data && !m_session_data->m_initial_stop_received) { Status error(exit_code, eErrorTypeWin32); OnDebuggerError(error, 0); @@ -619,8 +645,8 @@ void ProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { FileSystem::Instance().Resolve(executable_file); ModuleSpec module_spec(executable_file); Status error; - module = GetTarget().GetOrCreateModule(module_spec, - true /* notify */, &error); + module = + GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error); if (!module) { return; } @@ -636,10 +662,15 @@ void ProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { // 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(); + + const HostThread &host_main_thread = debugger->GetMainThread(); + ThreadSP main_thread = + std::make_shared<TargetThreadWindows>(*this, host_main_thread); + + tid_t id = host_main_thread.GetNativeThread().GetThreadId(); + main_thread->SetID(id); + + m_session_data->m_new_threads[id] = main_thread; } ExceptionResult @@ -711,8 +742,22 @@ ProcessWindows::OnDebugException(bool first_chance, void ProcessWindows::OnCreateThread(const HostThread &new_thread) { llvm::sys::ScopedLock lock(m_mutex); - const HostThreadWindows &wnew_thread = new_thread.GetNativeThread(); - m_session_data->m_new_threads[wnew_thread.GetThreadId()] = new_thread; + + ThreadSP thread = std::make_shared<TargetThreadWindows>(*this, new_thread); + + const HostNativeThread &native_new_thread = new_thread.GetNativeThread(); + tid_t id = native_new_thread.GetThreadId(); + thread->SetID(id); + + m_session_data->m_new_threads[id] = thread; + + for (const std::map<int, WatchpointInfo>::value_type &p : m_watchpoints) { + auto *reg_ctx = static_cast<RegisterContextWindows *>( + thread->GetRegisterContext().get()); + reg_ctx->AddHardwareBreakpoint(p.second.slot_id, p.second.address, + p.second.size, p.second.read, + p.second.write); + } } void ProcessWindows::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) { @@ -770,4 +815,106 @@ void ProcessWindows::OnDebuggerError(const Status &error, uint32_t type) { return; } } + +Status ProcessWindows::GetWatchpointSupportInfo(uint32_t &num) { + num = RegisterContextWindows::GetNumHardwareBreakpointSlots(); + return {}; +} + +Status ProcessWindows::GetWatchpointSupportInfo(uint32_t &num, bool &after) { + num = RegisterContextWindows::GetNumHardwareBreakpointSlots(); + after = RegisterContextWindows::DoHardwareBreakpointsTriggerAfter(); + return {}; +} + +Status ProcessWindows::EnableWatchpoint(Watchpoint *wp, bool notify) { + Status error; + + if (wp->IsEnabled()) { + wp->SetEnabled(true, notify); + return error; + } + + WatchpointInfo info; + for (info.slot_id = 0; + info.slot_id < RegisterContextWindows::GetNumHardwareBreakpointSlots(); + info.slot_id++) + if (m_watchpoint_ids[info.slot_id] == LLDB_INVALID_BREAK_ID) + break; + if (info.slot_id == RegisterContextWindows::GetNumHardwareBreakpointSlots()) { + error.SetErrorStringWithFormat("Can't find free slot for watchpoint %i", + wp->GetID()); + return error; + } + info.address = wp->GetLoadAddress(); + info.size = wp->GetByteSize(); + info.read = wp->WatchpointRead(); + info.write = wp->WatchpointWrite(); + + for (unsigned i = 0U; i < m_thread_list.GetSize(); i++) { + Thread *thread = m_thread_list.GetThreadAtIndex(i).get(); + auto *reg_ctx = static_cast<RegisterContextWindows *>( + thread->GetRegisterContext().get()); + if (!reg_ctx->AddHardwareBreakpoint(info.slot_id, info.address, info.size, + info.read, info.write)) { + error.SetErrorStringWithFormat( + "Can't enable watchpoint %i on thread 0x%llx", wp->GetID(), + thread->GetID()); + break; + } + } + if (error.Fail()) { + for (unsigned i = 0U; i < m_thread_list.GetSize(); i++) { + Thread *thread = m_thread_list.GetThreadAtIndex(i).get(); + auto *reg_ctx = static_cast<RegisterContextWindows *>( + thread->GetRegisterContext().get()); + reg_ctx->RemoveHardwareBreakpoint(info.slot_id); + } + return error; + } + + m_watchpoints[wp->GetID()] = info; + m_watchpoint_ids[info.slot_id] = wp->GetID(); + + wp->SetEnabled(true, notify); + + return error; +} + +Status ProcessWindows::DisableWatchpoint(Watchpoint *wp, bool notify) { + Status error; + + if (!wp->IsEnabled()) { + wp->SetEnabled(false, notify); + return error; + } + + auto it = m_watchpoints.find(wp->GetID()); + if (it == m_watchpoints.end()) { + error.SetErrorStringWithFormat("Info about watchpoint %i is not found", + wp->GetID()); + return error; + } + + for (unsigned i = 0U; i < m_thread_list.GetSize(); i++) { + Thread *thread = m_thread_list.GetThreadAtIndex(i).get(); + auto *reg_ctx = static_cast<RegisterContextWindows *>( + thread->GetRegisterContext().get()); + if (!reg_ctx->RemoveHardwareBreakpoint(it->second.slot_id)) { + error.SetErrorStringWithFormat( + "Can't disable watchpoint %i on thread 0x%llx", wp->GetID(), + thread->GetID()); + break; + } + } + if (error.Fail()) + return error; + + m_watchpoint_ids[it->second.slot_id] = LLDB_INVALID_BREAK_ID; + m_watchpoints.erase(it); + + wp->SetEnabled(false, notify); + + return error; +} } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h index 8d5134a49a4..a1085df022c 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h @@ -98,6 +98,22 @@ public: void OnUnloadDll(lldb::addr_t module_addr) override; void OnDebugString(const std::string &string) override; void OnDebuggerError(const Status &error, uint32_t type) override; + + Status GetWatchpointSupportInfo(uint32_t &num) override; + Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override; + Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override; + Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override; + +private: + struct WatchpointInfo { + uint32_t slot_id; + lldb::addr_t address; + uint32_t size; + bool read; + bool write; + }; + std::map<lldb::break_id_t, WatchpointInfo> m_watchpoints; + std::vector<lldb::break_id_t> m_watchpoint_ids; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp index b4a442c77ed..597673b8522 100644 --- a/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp @@ -17,6 +17,7 @@ #include "TargetThreadWindows.h" #include "llvm/ADT/STLExtras.h" +#include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; @@ -52,13 +53,7 @@ bool RegisterContextWindows::WriteAllRegisterValues( assert(data_sp->GetByteSize() >= sizeof(m_context)); memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context)); - TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); - if (!::SetThreadContext( - wthread.GetHostThread().GetNativeThread().GetSystemHandle(), - &m_context)) - return false; - - return true; + return ApplyAllRegisterValues(); } uint32_t RegisterContextWindows::ConvertRegisterKindToRegisterNumber( @@ -76,37 +71,69 @@ uint32_t RegisterContextWindows::ConvertRegisterKindToRegisterNumber( return LLDB_INVALID_REGNUM; } -// Subclasses can these functions if desired -uint32_t RegisterContextWindows::NumSupportedHardwareBreakpoints() { - // Support for hardware breakpoints not yet implemented. - return 0; -} +bool RegisterContextWindows::HardwareSingleStep(bool enable) { return false; } -uint32_t RegisterContextWindows::SetHardwareBreakpoint(lldb::addr_t addr, - size_t size) { - return 0; -} +bool RegisterContextWindows::AddHardwareBreakpoint(uint32_t slot, + lldb::addr_t address, + uint32_t size, bool read, + bool write) { + if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS) + return false; -bool RegisterContextWindows::ClearHardwareBreakpoint(uint32_t hw_idx) { - return false; -} + switch (size) { + case 1: + case 2: + case 4: +#if defined(_M_AMD64) + case 8: +#endif + break; + default: + return false; + } -uint32_t RegisterContextWindows::NumSupportedHardwareWatchpoints() { - // Support for hardware watchpoints not yet implemented. - return 0; -} + if (!CacheAllRegisterValues()) + return false; + + unsigned shift = 2 * slot; + m_context.Dr7 |= 1ULL << shift; + + (&m_context.Dr0)[slot] = address; -uint32_t RegisterContextWindows::SetHardwareWatchpoint(lldb::addr_t addr, - size_t size, bool read, - bool write) { - return 0; + shift = 18 + 4 * slot; + m_context.Dr7 &= ~(3ULL << shift); + m_context.Dr7 |= (size == 8 ? 2ULL : size - 1) << shift; + + shift = 16 + 4 * slot; + m_context.Dr7 &= ~(3ULL << shift); + m_context.Dr7 |= (read ? 3ULL : (write ? 1ULL : 0)) << shift; + + return ApplyAllRegisterValues(); } -bool RegisterContextWindows::ClearHardwareWatchpoint(uint32_t hw_index) { - return false; +bool RegisterContextWindows::RemoveHardwareBreakpoint(uint32_t slot) { + if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS) + return false; + + if (!CacheAllRegisterValues()) + return false; + + unsigned shift = 2 * slot; + m_context.Dr7 &= ~(1ULL << shift); + + return ApplyAllRegisterValues(); } -bool RegisterContextWindows::HardwareSingleStep(bool enable) { return false; } +uint32_t RegisterContextWindows::GetTriggeredHardwareBreakpointSlotId() { + if (!CacheAllRegisterValues()) + return LLDB_INVALID_INDEX32; + + for (unsigned i = 0UL; i < NUM_HARDWARE_BREAKPOINT_SLOTS; i++) + if (m_context.Dr6 & (1ULL << i)) + return i; + + return LLDB_INVALID_INDEX32; +} bool RegisterContextWindows::CacheAllRegisterValues() { Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); @@ -146,3 +173,9 @@ bool RegisterContextWindows::CacheAllRegisterValues() { m_context_stale = false; return true; } + +bool RegisterContextWindows::ApplyAllRegisterValues() { + TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); + return ::SetThreadContext( + wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); +} diff --git a/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.h b/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.h index 916f9f3a156..9ae04022825 100644 --- a/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.h +++ b/lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.h @@ -33,28 +33,28 @@ public: 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; + bool HardwareSingleStep(bool enable) override; - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, - bool write) override; + static constexpr uint32_t GetNumHardwareBreakpointSlots() { + return NUM_HARDWARE_BREAKPOINT_SLOTS; + } + static constexpr bool DoHardwareBreakpointsTriggerAfter() { return true; } - bool ClearHardwareWatchpoint(uint32_t hw_index) override; + bool AddHardwareBreakpoint(uint32_t slot, lldb::addr_t address, uint32_t size, + bool read, bool write); + bool RemoveHardwareBreakpoint(uint32_t slot); - bool HardwareSingleStep(bool enable) override; + uint32_t GetTriggeredHardwareBreakpointSlotId(); protected: + static constexpr unsigned NUM_HARDWARE_BREAKPOINT_SLOTS = 4; + virtual bool CacheAllRegisterValues(); + virtual bool ApplyAllRegisterValues(); CONTEXT m_context; bool m_context_stale; }; -} +} // namespace lldb_private #endif // #ifndef liblldb_RegisterContextWindows_H_ diff --git a/lldb/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp b/lldb/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp index 014b15c925b..fc59d099d2d 100644 --- a/lldb/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp @@ -532,9 +532,7 @@ bool RegisterContextWindows_x64::WriteRegister(const RegisterInfo *reg_info, } // Physically update the registers in the target process. - TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); - return ::SetThreadContext( - wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); + return ApplyAllRegisterValues(); } #endif // defined(__x86_64__) || defined(_M_X64) diff --git a/lldb/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp b/lldb/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp index b8f7bb46181..2a271ac44e4 100644 --- a/lldb/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp @@ -267,9 +267,7 @@ bool RegisterContextWindows_x86::WriteRegister(const RegisterInfo *reg_info, } // Physically update the registers in the target process. - TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread); - return ::SetThreadContext( - wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); + return ApplyAllRegisterValues(); } bool RegisterContextWindows_x86::ReadRegisterHelper( |