Add watchpoint support for Linux on 64-bit host.
llvm-svn: 181341
diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
index d893b89..4ebde16 100644
--- a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
+++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
@@ -47,6 +47,11 @@
#define PTRACE_SETREGSET 0x4205
#endif
+// Support hardware breakpoints in case it has not been defined
+#ifndef TRAP_HWBKPT
+ #define TRAP_HWBKPT 4
+#endif
+
using namespace lldb_private;
// FIXME: this code is host-dependent with respect to types and
@@ -1415,6 +1420,10 @@
case TRAP_BRKPT:
message = ProcessMessage::Break(pid);
break;
+
+ case TRAP_HWBKPT:
+ message = ProcessMessage::Watch(pid, (lldb::addr_t)info->si_addr);
+ break;
}
return message;
diff --git a/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp b/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp
index 8928d37..8e8f0c0 100644
--- a/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp
+++ b/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp
@@ -15,6 +15,7 @@
// C++ Includes
// Other libraries and framework includes
// Project includes
+#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Host/Host.h"
#include "lldb/Target/Process.h"
@@ -229,6 +230,10 @@
BreakNotify(message);
break;
+ case ProcessMessage::eWatchpointMessage:
+ WatchNotify(message);
+ break;
+
case ProcessMessage::eCrashMessage:
CrashNotify(message);
break;
@@ -239,6 +244,58 @@
}
}
+bool
+POSIXThread::EnableHardwareWatchpoint(Watchpoint *wp)
+{
+ bool result = false;
+ if (wp)
+ {
+ addr_t wp_addr = wp->GetLoadAddress();
+ size_t wp_size = wp->GetByteSize();
+ bool wp_read = wp->WatchpointRead();
+ bool wp_write = wp->WatchpointWrite();
+ uint32_t wp_hw_index;
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ {
+ wp_hw_index = reg_ctx_sp->SetHardwareWatchpoint(wp_addr, wp_size,
+ wp_read, wp_write);
+ if (wp_hw_index != LLDB_INVALID_INDEX32)
+ {
+ wp->SetHardwareIndex(wp_hw_index);
+ result = true;
+ }
+ }
+ }
+ return result;
+}
+
+bool
+POSIXThread::DisableHardwareWatchpoint(Watchpoint *wp)
+{
+ bool result = false;
+ if (wp)
+ {
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ {
+ result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex());
+ if (result == true)
+ wp->SetHardwareIndex(LLDB_INVALID_INDEX32);
+ }
+ }
+ return result;
+}
+
+uint32_t
+POSIXThread::NumSupportedHardwareWatchpoints()
+{
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ return reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
void
POSIXThread::BreakNotify(const ProcessMessage &message)
{
@@ -260,12 +317,50 @@
lldb::break_id_t bp_id = bp_site->GetID();
assert(bp_site && bp_site->ValidForThisThread(this));
-
m_breakpoint = bp_site;
SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id));
}
void
+POSIXThread::WatchNotify(const ProcessMessage &message)
+{
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
+
+ lldb::addr_t halt_addr = message.GetHWAddress();
+ if (log)
+ log->Printf ("POSIXThread::%s () Hardware Watchpoint Address = 0x%8.8"
+ PRIx64, __FUNCTION__, halt_addr);
+
+ RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX();
+ if (reg_ctx)
+ {
+ uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++)
+ {
+ if (reg_ctx->IsWatchpointHit(wp_idx))
+ {
+ // Clear the watchpoint hit here
+ reg_ctx->ClearWatchpointHits();
+ break;
+ }
+ }
+
+ if (wp_idx == num_hw_wps)
+ return;
+
+ Target &target = GetProcess()->GetTarget();
+ lldb::addr_t wp_monitor_addr = reg_ctx->GetWatchpointAddress(wp_idx);
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ lldb::WatchpointSP wp_sp = wp_list.FindByAddress(wp_monitor_addr);
+
+ if (wp_sp)
+ SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID(*this,
+ wp_sp->GetID()));
+ }
+}
+
+void
POSIXThread::TraceNotify(const ProcessMessage &message)
{
SetStopInfo (StopInfo::CreateStopReasonToTrace(*this));
diff --git a/lldb/source/Plugins/Process/POSIX/POSIXThread.h b/lldb/source/Plugins/Process/POSIX/POSIXThread.h
index 3e14652..69bca49 100644
--- a/lldb/source/Plugins/Process/POSIX/POSIXThread.h
+++ b/lldb/source/Plugins/Process/POSIX/POSIXThread.h
@@ -69,6 +69,15 @@
void Notify(const ProcessMessage &message);
+ //--------------------------------------------------------------------------
+ // These methods provide an interface to watchpoints
+ //
+ bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
private:
RegisterContextPOSIX *
GetRegisterContextPOSIX ()
@@ -92,6 +101,7 @@
GetPrivateStopReason();
void BreakNotify(const ProcessMessage &message);
+ void WatchNotify(const ProcessMessage &message);
void TraceNotify(const ProcessMessage &message);
void LimboNotify(const ProcessMessage &message);
void SignalNotify(const ProcessMessage &message);
diff --git a/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp b/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
index cefacc3..a33470b 100644
--- a/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
+++ b/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
@@ -221,6 +221,9 @@
case eBreakpointMessage:
str = "eBreakpointMessage";
break;
+ case eWatchpointMessage:
+ str = "eWatchpointMessage";
+ break;
case eCrashMessage:
str = "eCrashMessage";
break;
diff --git a/lldb/source/Plugins/Process/POSIX/ProcessMessage.h b/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
index 7eca5c8..2a4c612 100644
--- a/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
+++ b/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
@@ -29,6 +29,7 @@
eSignalDeliveredMessage,
eTraceMessage,
eBreakpointMessage,
+ eWatchpointMessage,
eCrashMessage,
eNewThreadMessage
};
@@ -104,6 +105,10 @@
return ProcessMessage(tid, eBreakpointMessage);
}
+ static ProcessMessage Watch(lldb::tid_t tid, lldb::addr_t wp_addr) {
+ return ProcessMessage(tid, eWatchpointMessage, 0, wp_addr);
+ }
+
/// Indicates that the thread @p tid crashed.
static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason,
int signo, lldb::addr_t fault_addr) {
@@ -143,6 +148,11 @@
return m_addr;
}
+ lldb::addr_t GetHWAddress() const {
+ assert(GetKind() == eWatchpointMessage);
+ return m_addr;
+ }
+
lldb::tid_t GetChildTID() const {
assert(GetKind() == eNewThreadMessage);
return m_child_tid;
diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp
index 35c365f..f010568 100644
--- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp
+++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp
@@ -14,6 +14,7 @@
// C++ Includes
// Other libraries and framework includes
+#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/State.h"
@@ -364,6 +365,7 @@
case ProcessMessage::eTraceMessage:
case ProcessMessage::eBreakpointMessage:
+ case ProcessMessage::eWatchpointMessage:
SetPrivateState(eStateStopped);
break;
@@ -546,6 +548,132 @@
return DisableSoftwareBreakpoint(bp_site);
}
+Error
+ProcessPOSIX::EnableWatchpoint(Watchpoint *wp, bool notify)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
+ watchID, (uint64_t)addr);
+ return error;
+ }
+
+ bool wp_enabled = true;
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_enabled &= thread->EnableHardwareWatchpoint(wp);
+ else
+ {
+ wp_enabled = false;
+ break;
+ }
+ }
+ if (wp_enabled)
+ {
+ wp->SetEnabled(true, notify);
+ return error;
+ }
+ else
+ {
+ // Watchpoint enabling failed on at least one
+ // of the threads so roll back all of them
+ DisableWatchpoint(wp, false);
+ error.SetErrorString("Setting hardware watchpoint failed");
+ }
+ }
+ else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Error
+ProcessPOSIX::DisableWatchpoint(Watchpoint *wp, bool notify)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (!wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.",
+ watchID, (uint64_t)addr);
+ // This is needed (for now) to keep watchpoints disabled correctly
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware())
+ {
+ bool wp_disabled = true;
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i)
+ {
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_disabled &= thread->DisableHardwareWatchpoint(wp);
+ else
+ wp_disabled = false;
+ }
+ if (wp_disabled)
+ {
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+ else
+ error.SetErrorString("Disabling hardware watchpoint failed");
+ }
+ }
+ else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Error
+ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num)
+{
+ Error error;
+ POSIXThread *thread = static_cast<POSIXThread*>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+ if (thread)
+ num = thread->NumSupportedHardwareWatchpoints();
+ else
+ error.SetErrorString("Process does not exist.");
+ return error;
+}
+
+Error
+ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num, bool &after)
+{
+ Error error = GetWatchpointSupportInfo(num);
+ // Watchpoints trigger and halt the inferior after
+ // the corresponding instruction has been executed.
+ after = true;
+ return error;
+}
+
uint32_t
ProcessPOSIX::UpdateThreadListIfNeeded()
{
diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h
index 86c4ef3..ce31d8f 100644
--- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h
+++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h
@@ -108,6 +108,18 @@
virtual lldb_private::Error
DisableBreakpointSite(lldb_private::BreakpointSite *bp_site);
+ virtual lldb_private::Error
+ EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true);
+
+ virtual lldb_private::Error
+ DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true);
+
+ virtual lldb_private::Error
+ GetWatchpointSupportInfo(uint32_t &num);
+
+ virtual lldb_private::Error
+ GetWatchpointSupportInfo(uint32_t &num, bool &after);
+
virtual uint32_t
UpdateThreadListIfNeeded();
diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp
index c49bd62..477f748 100644
--- a/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp
+++ b/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp
@@ -25,6 +25,15 @@
m_register_infos[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \
} while(false);
+#define DR_OFFSET(reg_index) \
+ (offsetof(UserArea, u_debugreg[reg_index]))
+
+#define UPDATE_DR_INFO(reg_index) \
+do { \
+ m_register_infos[dr##reg_index].byte_size = sizeof(UserArea::u_debugreg[0]); \
+ m_register_infos[dr##reg_index].byte_offset = DR_OFFSET(reg_index); \
+} while(false);
+
typedef struct _GPR
{
uint64_t r15;
@@ -155,5 +164,14 @@
UPDATE_I386_GPR_INFO(esp, rsp);
UPDATE_I386_GPR_INFO(eip, rip);
UPDATE_I386_GPR_INFO(eflags, rflags);
+
+ UPDATE_DR_INFO(0);
+ UPDATE_DR_INFO(1);
+ UPDATE_DR_INFO(2);
+ UPDATE_DR_INFO(3);
+ UPDATE_DR_INFO(4);
+ UPDATE_DR_INFO(5);
+ UPDATE_DR_INFO(6);
+ UPDATE_DR_INFO(7);
}
diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h b/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h
index f97e710..46aa9ca 100644
--- a/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h
+++ b/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h
@@ -35,6 +35,20 @@
/// @return
/// True if the operation succeeded and false otherwise.
virtual bool UpdateAfterBreakpoint() { return true; }
+
+ // Checks to see if a watchpoint specified by hw_index caused the inferior
+ // to stop.
+ virtual bool
+ IsWatchpointHit (uint32_t hw_index) { return false; }
+
+ // Resets any watchpoints that have been hit.
+ virtual bool
+ ClearWatchpointHits () { return false; }
+
+ // Returns the watchpoint address associated with a watchpoint hardware
+ // index.
+ virtual lldb::addr_t
+ GetWatchpointAddress (uint32_t hw_index) {return LLDB_INVALID_ADDRESS; }
};
#endif // #ifndef liblldb_RegisterContextPOSIX_H_
diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp
index 255cdd3..e99c257d 100644
--- a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp
+++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp
@@ -340,6 +340,11 @@
{ gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \
LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL }
+#define DEFINE_DR(reg, i) \
+ { #reg#i, NULL, 0, 0, eEncodingUint, eFormatHex, \
+ { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL }
+
#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(RegisterContext_x86_64::FPR))
static RegisterInfo
@@ -439,7 +444,17 @@
DEFINE_YMM(ymm, 12),
DEFINE_YMM(ymm, 13),
DEFINE_YMM(ymm, 14),
- DEFINE_YMM(ymm, 15)
+ DEFINE_YMM(ymm, 15),
+
+ // Debug registers for lldb internal use
+ DEFINE_DR(dr, 0),
+ DEFINE_DR(dr, 1),
+ DEFINE_DR(dr, 2),
+ DEFINE_DR(dr, 3),
+ DEFINE_DR(dr, 4),
+ DEFINE_DR(dr, 5),
+ DEFINE_DR(dr, 6),
+ DEFINE_DR(dr, 7)
};
RegisterInfo *RegisterContext_x86_64::m_register_infos = g_register_infos;
@@ -869,6 +884,27 @@
}
bool
+RegisterContext_x86_64::ReadRegister(const unsigned reg,
+ RegisterValue &value)
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(),
+ GetRegisterOffset(reg),
+ GetRegisterSize(reg),
+ value);
+}
+
+bool
+RegisterContext_x86_64::WriteRegister(const unsigned reg,
+ const RegisterValue &value)
+{
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(m_thread.GetID(),
+ GetRegisterOffset(reg),
+ value);
+}
+
+bool
RegisterContext_x86_64::UpdateAfterBreakpoint()
{
// PC points one byte past the int3 responsible for the breakpoint.
@@ -1178,6 +1214,183 @@
return LLDB_INVALID_REGNUM;
}
+uint32_t
+RegisterContext_x86_64::NumSupportedHardwareWatchpoints()
+{
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
+
+bool
+RegisterContext_x86_64::IsWatchpointVacant(uint32_t hw_index)
+{
+ bool is_vacant = false;
+ RegisterValue value;
+
+ if (ReadRegister(dr7, value))
+ {
+ uint64_t val = value.GetAsUInt64();
+ is_vacant = (val & (3 << 2*hw_index)) == 0;
+ }
+
+ return is_vacant;
+}
+
+static uint32_t
+size_and_rw_bits(size_t size, bool read, bool write)
+{
+ uint32_t rw;
+ if (read) {
+ rw = 0x3; // READ or READ/WRITE
+ } else if (write) {
+ rw = 0x1; // WRITE
+ } else {
+ assert(0 && "read and write cannot both be false");
+ }
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ default:
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ }
+}
+
+uint32_t
+RegisterContext_x86_64::SetHardwareWatchpoint(addr_t addr, size_t size,
+ bool read, bool write)
+{
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ if (num_hw_watchpoints == 0)
+ return LLDB_INVALID_INDEX32;
+
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return LLDB_INVALID_INDEX32;
+
+ if (read == false && write == false)
+ return LLDB_INVALID_INDEX32;
+
+ uint32_t hw_index = 0;
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index)
+ {
+ if (IsWatchpointVacant(hw_index))
+ break;
+ }
+
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write,
+ // 0b10: io read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ if (hw_index < num_hw_watchpoints)
+ {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(dr7, current_dr7_bits))
+ {
+ uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() |
+ (1 << (2*hw_index) |
+ size_and_rw_bits(size, read, write) <<
+ (16+4*hw_index));
+
+ if (WriteRegister(dr0 + hw_index, RegisterValue(addr)) &&
+ WriteRegister(dr7, RegisterValue(new_dr7_bits)))
+ return hw_index;
+ }
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContext_x86_64::ClearHardwareWatchpoint(uint32_t hw_index)
+{
+ if (hw_index < NumSupportedHardwareWatchpoints())
+ {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(dr7, current_dr7_bits))
+ {
+ uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() & ~(3 << (2*hw_index));
+
+ if (WriteRegister(dr7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+RegisterContext_x86_64::IsWatchpointHit(uint32_t hw_index)
+{
+ bool is_hit = false;
+
+ if (hw_index < NumSupportedHardwareWatchpoints())
+ {
+ RegisterValue value;
+
+ if (ReadRegister(dr6, value))
+ {
+ uint64_t val = value.GetAsUInt64();
+ is_hit = val & (1 << hw_index);
+ }
+ }
+
+ return is_hit;
+}
+
+addr_t
+RegisterContext_x86_64::GetWatchpointAddress(uint32_t hw_index)
+{
+ addr_t wp_monitor_addr = LLDB_INVALID_ADDRESS;
+
+ if (hw_index < NumSupportedHardwareWatchpoints())
+ {
+ if (!IsWatchpointVacant(hw_index))
+ {
+ RegisterValue value;
+
+ if (ReadRegister(dr0 + hw_index, value))
+ wp_monitor_addr = value.GetAsUInt64();
+ }
+ }
+
+ return wp_monitor_addr;
+}
+
+
+bool
+RegisterContext_x86_64::ClearWatchpointHits()
+{
+ return WriteRegister(dr6, RegisterValue((uint64_t)0));
+}
+
bool
RegisterContext_x86_64::HardwareSingleStep(bool enable)
{
diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h
index efd5f1f..8b6fa9d 100644
--- a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h
+++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h
@@ -110,6 +110,15 @@
fpu_ymm15,
k_last_avx = fpu_ymm15,
+ dr0,
+ dr1,
+ dr2,
+ dr3,
+ dr4,
+ dr5,
+ dr6,
+ dr7,
+
k_num_registers,
k_num_gpr_registers = k_last_gpr - k_first_gpr + 1,
k_num_fpr_registers = k_last_fpr - k_first_fpr + 1,
@@ -175,15 +184,37 @@
uint32_t
ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num);
+ uint32_t
+ NumSupportedHardwareWatchpoints();
+
+ uint32_t
+ SetHardwareWatchpoint(lldb::addr_t, size_t size, bool read, bool write);
+
+ bool
+ ClearHardwareWatchpoint(uint32_t hw_index);
+
bool
HardwareSingleStep(bool enable);
bool
UpdateAfterBreakpoint();
+ bool
+ IsWatchpointVacant(uint32_t hw_index);
+
+ bool
+ IsWatchpointHit (uint32_t hw_index);
+
+ lldb::addr_t
+ GetWatchpointAddress (uint32_t hw_index);
+
+ bool
+ ClearWatchpointHits();
+
//---------------------------------------------------------------------------
// Generic floating-point registers
//---------------------------------------------------------------------------
+
struct MMSReg
{
uint8_t bytes[10];
@@ -280,6 +311,12 @@
virtual const lldb_private::RegisterInfo *
GetRegisterInfo();
+ virtual bool
+ ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ virtual bool
+ WriteRegister(const unsigned reg, const lldb_private::RegisterValue &value);
+
private:
static lldb_private::RegisterInfo *m_register_infos;