summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/Process/Windows
diff options
context:
space:
mode:
authorAleksandr Urakov <aleksandr.urakov@jetbrains.com>2019-09-06 05:37:03 +0000
committerAleksandr Urakov <aleksandr.urakov@jetbrains.com>2019-09-06 05:37:03 +0000
commit6179c0eb0d15d73e11af8b6b5538b381c6ed2c53 (patch)
tree44b74e920b98a131fc73e9c54c21cd65c3cbeece /lldb/source/Plugins/Process/Windows
parentdfacf8851e93e28b32fb87bc6430fe7c27cf5836 (diff)
downloadbcm5719-llvm-6179c0eb0d15d73e11af8b6b5538b381c6ed2c53.tar.gz
bcm5719-llvm-6179c0eb0d15d73e11af8b6b5538b381c6ed2c53.zip
[Windows] Add support of watchpoints to `ProcessWindows`
Summary: This patch adds support of watchpoints to the old `ProcessWindows` plugin. The `ProcessWindows` plugin uses the `RegisterContext` to set and reset watchpoints. The `RegisterContext` has some interface to access watchpoints, but it is very limited (e.g. it is impossible to retrieve the last triggered watchpoint with it), that's why I have implemented a slightly different interface in the `RegisterContextWindows`. Moreover, I have made the `ProcessWindows` plugin responsible for search of a vacant watchpoint slot, because watchpoints exist per-process (not per-thread), then we can place the same watchpoint in the same slot in different threads. With this scheme threads don't need to have their own watchpoint lists, and it simplifies identifying of the last triggered watchpoint. Reviewers: asmith, stella.stamenova, amccarth Reviewed By: amccarth Subscribers: labath, zturner, leonid.mashinskiy, abidh, JDevlieghere, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D67168 llvm-svn: 371166
Diffstat (limited to 'lldb/source/Plugins/Process/Windows')
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/ProcessDebugger.h2
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp181
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h16
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp93
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.h26
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp4
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp4
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(
OpenPOWER on IntegriCloud