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;