[Lldb-commits] [lldb] r245961 - Adds support for hardware watchpoints on Arm targets.

Omair Javaid via lldb-commits lldb-commits at lists.llvm.org
Tue Aug 25 11:22:05 PDT 2015


Author: omjavaid
Date: Tue Aug 25 13:22:04 2015
New Revision: 245961

URL: http://llvm.org/viewvc/llvm-project?rev=245961&view=rev
Log:
Adds support for hardware watchpoints on Arm targets.

http://reviews.llvm.org/D9703

This updated patches correct problems in arm hardware watchpoint support patch posted earlier.

This patch has been tested on samsung chromebook (ARM - Linux) and PandaBoard using basic watchpoint test application.

Also it was tested on Nexus 7 Android device.

On chromebook linux we are able to set and clear all types of watchpoints but on android we end up getting a watchpoint packet error because we are not able to call hardware watchpoint ptrace functions successfully.


Modified:
    lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
    lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp?rev=245961&r1=245960&r2=245961&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp Tue Aug 25 13:22:04 2015
@@ -13,12 +13,24 @@
 
 #include "lldb/Core/DataBufferHeap.h"
 #include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
 #include "lldb/Core/RegisterValue.h"
 
 #include "Plugins/Process/Utility/RegisterContextLinux_arm.h"
 
 #define REG_CONTEXT_SIZE (GetGPRSize() + sizeof (m_fpr))
 
+#ifndef PTRACE_GETHBPREGS
+  #define PTRACE_GETHBPREGS 29
+  #define PTRACE_SETHBPREGS 30
+#endif
+#if !defined(PTRACE_TYPE_ARG3)
+  #define PTRACE_TYPE_ARG3 void *
+#endif
+#if !defined(PTRACE_TYPE_ARG4)
+  #define PTRACE_TYPE_ARG4 void *
+#endif
+
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_linux;
@@ -138,6 +150,12 @@ NativeRegisterContextLinux_arm::NativeRe
 
     ::memset(&m_fpr, 0, sizeof (m_fpr));
     ::memset(&m_gpr_arm, 0, sizeof (m_gpr_arm));
+    ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs));
+
+    // 16 is just a maximum value, query hardware for actual watchpoint count
+    m_max_hwp_supported = 16;
+    m_max_hbp_supported = 16;
+    m_refresh_hwdebug_info = true;
 }
 
 uint32_t
@@ -360,4 +378,470 @@ NativeRegisterContextLinux_arm::IsFPR(un
     return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
 }
 
+uint32_t
+NativeRegisterContextLinux_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    Error error;
+
+    // Read hardware breakpoint and watchpoint information.
+    error = ReadHardwareDebugInfo ();
+
+    if (error.Fail())
+        return LLDB_INVALID_INDEX32;
+
+    uint32_t control_value = 0, bp_index = 0;
+
+    // Check if size has a valid hardware breakpoint length.
+    // Thumb instructions are 2-bytes but we have no way here to determine
+    // if target address is a thumb or arm instruction.
+    // TODO: Add support for setting thumb mode hardware breakpoints
+    if (size != 4 && size != 2)
+        return LLDB_INVALID_INDEX32;
+
+    // Setup control value
+    // Make the byte_mask into a valid Byte Address Select mask
+    control_value = 0xfu << 5;
+
+    // Enable this breakpoint and make it stop in privileged or user mode;
+    control_value |= 7;
+
+    // Make sure bits 1:0 are clear in our address
+    // This should be different once we support thumb here.
+    addr &= ~((lldb::addr_t)3);
+
+    // Iterate over stored hardware breakpoints
+    // Find a free bp_index or update reference count if duplicate.
+    bp_index = LLDB_INVALID_INDEX32;
+
+    for (uint32_t i = 0; i < m_max_hbp_supported; i++)
+    {
+        if ((m_hbr_regs[i].control & 1) == 0)
+        {
+            bp_index = i;  // Mark last free slot
+        }
+        else if (m_hbr_regs[i].address == addr && m_hbr_regs[i].control == control_value)
+        {
+            bp_index = i;  // Mark duplicate index
+            break;  // Stop searching here
+        }
+    }
+
+     if (bp_index == LLDB_INVALID_INDEX32)
+         return LLDB_INVALID_INDEX32;
+
+    // Add new or update existing watchpoint
+    if ((m_hbr_regs[bp_index].control & 1) == 0)
+    {
+        m_hbr_regs[bp_index].address = addr;
+        m_hbr_regs[bp_index].control = control_value;
+        m_hbr_regs[bp_index].refcount = 1;
+
+        // PTRACE call to set corresponding hardware breakpoint register.
+        error = WriteHardwareDebugRegs(eDREGTypeBREAK, bp_index);
+
+        if (error.Fail())
+            return LLDB_INVALID_INDEX32;
+    }
+    else
+        m_hbr_regs[bp_index].refcount++;
+
+    return bp_index;
+}
+
+bool
+NativeRegisterContextLinux_arm::ClearHardwareBreakpoint (uint32_t hw_idx)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    Error error;
+
+    // Read hardware breakpoint and watchpoint information.
+    error = ReadHardwareDebugInfo ();
+
+    if (error.Fail())
+        return LLDB_INVALID_INDEX32;
+
+    if (hw_idx >= m_max_hbp_supported)
+        return false;
+
+    // Update reference count if multiple references.
+    if (m_hbr_regs[hw_idx].refcount > 1)
+    {
+        m_hbr_regs[hw_idx].refcount--;
+        return true;
+    }
+    else if (m_hbr_regs[hw_idx].refcount == 1)
+    {
+        m_hbr_regs[hw_idx].control &= ~1;
+        m_hbr_regs[hw_idx].address = 0;
+        m_hbr_regs[hw_idx].refcount = 0;
+
+        // PTRACE call to clear corresponding hardware breakpoint register.
+        WriteHardwareDebugRegs(eDREGTypeBREAK, hw_idx);
+
+        if (error.Fail())
+            return LLDB_INVALID_INDEX32;
+
+        return true;
+    }
+
+    return false;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints ()
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    Error error;
+
+    // Read hardware breakpoint and watchpoint information.
+    error = ReadHardwareDebugInfo ();
+
+    if (error.Fail())
+        return LLDB_INVALID_INDEX32;
+
+    return m_max_hwp_supported;
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+    
+    Error error;
+
+    // Read hardware breakpoint and watchpoint information.
+    error = ReadHardwareDebugInfo ();
+
+    if (error.Fail())
+        return LLDB_INVALID_INDEX32;
+		
+    uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0;
+
+    // Check if we are setting watchpoint other than read/write/access
+    // Also update watchpoint flag to match Arm write-read bit configuration.
+    switch (watch_flags)
+    {
+        case 1:
+            watch_flags = 2;
+            break;
+        case 2:
+            watch_flags = 1;
+            break;
+        case 3:
+            break;
+        default:
+            return LLDB_INVALID_INDEX32;
+    }
+
+    // Can't watch zero bytes
+    // Can't watch more than 4 bytes per WVR/WCR pair
+
+    if (size == 0 || size > 4)
+        return LLDB_INVALID_INDEX32;
+
+    // We can only watch up to four bytes that follow a 4 byte aligned address
+    // per watchpoint register pair, so make sure we can properly encode this.
+    addr_word_offset = addr % 4;
+    byte_mask = ((1u << size) - 1u) << addr_word_offset;
+
+    // Check if we need multiple watchpoint register
+    if (byte_mask > 0xfu)
+        return LLDB_INVALID_INDEX32;
+
+    // Setup control value
+    // Make the byte_mask into a valid Byte Address Select mask
+    control_value = byte_mask << 5;
+
+    //Turn on appropriate watchpoint flags read or write
+    control_value |= (watch_flags << 3);
+
+    // Enable this watchpoint and make it stop in privileged or user mode;
+    control_value |= 7;
+
+    // Make sure bits 1:0 are clear in our address
+    addr &= ~((lldb::addr_t)3);
+
+    // Iterate over stored watchpoints
+    // Find a free wp_index or update reference count if duplicate.
+    wp_index = LLDB_INVALID_INDEX32;
+    for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+    {
+        if ((m_hwp_regs[i].control & 1) == 0)
+        {
+            wp_index = i; // Mark last free slot
+        }
+        else if (m_hwp_regs[i].address == addr && m_hwp_regs[i].control == control_value)
+        {
+            wp_index = i; // Mark duplicate index
+            break; // Stop searching here
+        }
+    }
+
+     if (wp_index == LLDB_INVALID_INDEX32)
+        return LLDB_INVALID_INDEX32;
+
+    // Add new or update existing watchpoint
+    if ((m_hwp_regs[wp_index].control & 1) == 0)
+    {
+        // Update watchpoint in local cache
+        m_hwp_regs[wp_index].address = addr;
+        m_hwp_regs[wp_index].control = control_value;
+        m_hwp_regs[wp_index].refcount = 1;
+
+        // PTRACE call to set corresponding watchpoint register.
+        error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
+
+        if (error.Fail())
+            return LLDB_INVALID_INDEX32;
+    }
+    else
+        m_hwp_regs[wp_index].refcount++;
+
+    return wp_index;
+}
+
+bool
+NativeRegisterContextLinux_arm::ClearHardwareWatchpoint (uint32_t wp_index)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    Error error;
+
+    // Read hardware breakpoint and watchpoint information.
+    error = ReadHardwareDebugInfo ();
+
+    if (error.Fail())
+        return LLDB_INVALID_INDEX32;
+
+    if (wp_index >= m_max_hwp_supported)
+        return false;
+
+    // Update reference count if multiple references.
+    if (m_hwp_regs[wp_index].refcount > 1)
+    {
+        m_hwp_regs[wp_index].refcount--;
+        return true;
+    }
+    else if (m_hwp_regs[wp_index].refcount == 1)
+    {
+        // Update watchpoint in local cache
+        m_hwp_regs[wp_index].control &= ~1;
+        m_hwp_regs[wp_index].address = 0;
+        m_hwp_regs[wp_index].refcount = 0;
+
+        // Ptrace call to update hardware debug registers
+        error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
+
+        if (error.Fail())
+            return false;
+
+        return true;
+    }
+
+    return false;
+}
+
+Error
+NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints ()
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    Error error;
+
+    // Read hardware breakpoint and watchpoint information.
+    error = ReadHardwareDebugInfo ();
+
+    if (error.Fail())
+        return error;
+
+    for (uint32_t i = 0; i < m_max_hwp_supported; i++)
+    {
+        if (m_hwp_regs[i].control & 0x01)
+        {
+            // Clear watchpoints in local cache
+            m_hwp_regs[i].control &= ~1;
+            m_hwp_regs[i].address = 0;
+            m_hwp_regs[i].refcount = 0;
+
+            // Ptrace call to update hardware debug registers
+            error = WriteHardwareDebugRegs(eDREGTypeWATCH, i);
+
+            if (error.Fail())
+                return error;
+        }
+    }
+
+    return Error();
+}
+
+uint32_t
+NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f)
+    {
+        case 0x01:
+            return 1;
+        case 0x03:
+            return 2;
+        case 0x07:
+            return 3;
+        case 0x0f:
+            return 4;
+        default:
+            return 0;
+    }
+}
+bool
+NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    if ((m_hwp_regs[wp_index].control & 0x1) == 0x1)
+        return true;
+    else
+        return false;
+}
+
+Error
+NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    uint32_t watch_size;
+    lldb::addr_t watch_addr;
+
+    for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index)
+    {
+        watch_size = GetWatchpointSize (wp_index);
+        watch_addr = m_hwp_regs[wp_index].address;
+
+        if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index)
+            && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size)
+        {
+            return Error();
+        }
+    }
+
+    wp_index = LLDB_INVALID_INDEX32;
+    return Error();
+}
+
+lldb::addr_t
+NativeRegisterContextLinux_arm::GetWatchpointAddress (uint32_t wp_index)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+
+    if (log)
+        log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
+
+    if (wp_index >= m_max_hwp_supported)
+        return LLDB_INVALID_ADDRESS;
+
+    if (WatchpointIsEnabled(wp_index))
+        return m_hwp_regs[wp_index].address;
+    else
+        return LLDB_INVALID_ADDRESS;
+}
+
+Error
+NativeRegisterContextLinux_arm::ReadHardwareDebugInfo()
+{
+    Error error;
+
+    if (!m_refresh_hwdebug_info)
+    {
+        return Error();
+    }
+
+    unsigned int cap_val;
+
+    error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, sizeof(unsigned int));
+
+    if (error.Fail())
+        return error;
+
+    m_max_hwp_supported = (cap_val >> 8) & 0xff;
+    m_max_hbp_supported = cap_val & 0xff;
+    m_refresh_hwdebug_info = false;
+
+    return error;
+}
+
+Error
+NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, int hwb_index)
+{
+    Error error;
+
+    lldb::addr_t *addr_buf;
+    uint32_t *ctrl_buf;
+
+    if (hwbType == eDREGTypeWATCH)
+    {
+        addr_buf = &m_hwp_regs[hwb_index].address;
+        ctrl_buf = &m_hwp_regs[hwb_index].control;
+
+        error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+                m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 1),
+                addr_buf, sizeof(unsigned int));
+
+        if (error.Fail())
+            return error;
+
+        error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+                m_thread.GetID(), (PTRACE_TYPE_ARG3) -((hwb_index << 1) + 2),
+                ctrl_buf, sizeof(unsigned int));
+    }
+    else
+    {
+        addr_buf = &m_hwp_regs[hwb_index].address;
+        ctrl_buf = &m_hwp_regs[hwb_index].control;
+
+        error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+                m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 1),
+                addr_buf, sizeof(unsigned int));
+
+        if (error.Fail())
+            return error;
+
+        error = NativeProcessLinux::PtraceWrapper(PTRACE_SETHBPREGS,
+                m_thread.GetID(), (PTRACE_TYPE_ARG3) ((hwb_index << 1) + 2),
+                ctrl_buf, sizeof(unsigned int));
+
+    }
+
+    return error;
+}
 #endif // defined(__arm__)

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h?rev=245961&r1=245960&r2=245961&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h Tue Aug 25 13:22:04 2015
@@ -48,6 +48,47 @@ namespace process_linux {
         Error
         WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
 
+        //------------------------------------------------------------------
+        // Hardware breakpoints/watchpoint mangement functions
+        //------------------------------------------------------------------
+
+        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, uint32_t watch_flags) override;
+
+        bool
+        ClearHardwareWatchpoint (uint32_t hw_index) override;
+
+        Error
+        ClearAllHardwareWatchpoints () override;
+
+        Error
+        GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;
+
+        lldb::addr_t
+        GetWatchpointAddress (uint32_t wp_index) override;
+
+        uint32_t
+        GetWatchpointSize(uint32_t wp_index);
+
+        bool
+        WatchpointIsEnabled(uint32_t wp_index);
+
+        // Debug register type select
+        enum DREGType
+        {
+            eDREGTypeWATCH = 0,
+            eDREGTypeBREAK
+        };
+
     protected:
         void*
         GetGPRBuffer() override { return &m_gpr_arm; }
@@ -94,11 +135,32 @@ namespace process_linux {
         RegInfo  m_reg_info;
         FPU m_fpr; 
 
+        // Debug register info for hardware breakpoints and watchpoints management.
+        struct DREG
+        {
+            lldb::addr_t address;  // Breakpoint/watchpoint address value.
+            uint32_t control;  // Breakpoint/watchpoint control value.
+            uint32_t refcount;  // Serves as enable/disable and refernce counter.
+        };
+
+        struct DREG m_hbr_regs[16];  // Arm native linux hardware breakpoints
+        struct DREG m_hwp_regs[16];  // Arm native linux hardware watchpoints
+
+        uint32_t m_max_hwp_supported;
+        uint32_t m_max_hbp_supported;
+        bool m_refresh_hwdebug_info;
+
         bool
         IsGPR(unsigned reg) const;
 
         bool
         IsFPR(unsigned reg) const;
+
+        Error
+        ReadHardwareDebugInfo();
+
+        Error
+        WriteHardwareDebugRegs(int hwbType, int hwb_index);
     };
 
 } // namespace process_linux

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp?rev=245961&r1=245960&r2=245961&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp Tue Aug 25 13:22:04 2015
@@ -183,6 +183,8 @@ GDBRemoteCommunicationServerCommon::Hand
 #else
     if (host_arch.GetMachine() == llvm::Triple::aarch64 ||
         host_arch.GetMachine() == llvm::Triple::aarch64_be ||
+        host_arch.GetMachine() == llvm::Triple::arm ||
+        host_arch.GetMachine() == llvm::Triple::armeb ||
         host_arch.GetMachine() == llvm::Triple::mips64 ||
         host_arch.GetMachine() == llvm::Triple::mips64el)
         response.Printf("watchpoint_exceptions_received:before;");




More information about the lldb-commits mailing list