omjavaid created this revision.
Herald added subscribers: mgorny, danalbert, rengolin, aemerson.

This patch implements hardware breakpoint functionality in LLDB for AArch64 
targets. AArch64 targets supports hardware breakpoints via ptrace interface 
similar to hardware watchpoints. Hardware breakpoints provide fast way to stop 
target not requiring any memory read/write like software breakpoints.

This patch fixes areas which required tweaking to put hardware breakpoints 
implementation in place via process gdb remote. We are able to test these 
breakpoints via a simple application on Android Nexux5x devices.

I am in process of testing on various other platforms and also writing lldb 
testsuite test cases.

Still on TODO list:

1. LLDB Testsuite testcases for hardware breakpoints testing.
2. Similar implementation for Arm targets.
3. Improve stepping speed by trying to use hardware in place of software 
breakpoints which single step is being done via breakpoints.

Looking forward to upstream comments on this.

Test code in case someone wants to test:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define THREAD_COUNT 8

void *thread_function( void *ptr )
{

  int *argument = (int *) ptr;
  
  printf("Thread #%i \n", *argument);

}

int main()
{

  int argument[THREAD_COUNT];
  pthread_t thread_handle[THREAD_COUNT];
  
  int i;
  for (i = 0; i < THREAD_COUNT; i++)
  { 
    argument[i] = i;
    if (pthread_create( &thread_handle[i], NULL, thread_function, (void*) 
&argument[i]))
    {
      printf("Error - pthread_create() failed\n");
      exit(EXIT_FAILURE);
    }
  }
  
  for (i = 0; i < THREAD_COUNT; i++)
    pthread_join( thread_handle[i], NULL);
  
  exit(EXIT_SUCCESS);
  
  return 0;

}


https://reviews.llvm.org/D29669

Files:
  include/lldb/Host/common/HardwareBreakpointList.h
  include/lldb/Host/common/NativeProcessProtocol.h
  include/lldb/Host/common/NativeRegisterContext.h
  include/lldb/Host/common/NativeThreadProtocol.h
  source/Host/CMakeLists.txt
  source/Host/common/HardwareBreakpointList.cpp
  source/Host/common/NativeProcessProtocol.cpp
  source/Host/common/NativeRegisterContext.cpp
  source/Plugins/Process/Linux/NativeProcessLinux.cpp
  source/Plugins/Process/Linux/NativeProcessLinux.h
  source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
  source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
  source/Plugins/Process/Linux/NativeThreadLinux.cpp
  source/Plugins/Process/Linux/NativeThreadLinux.h
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -2531,12 +2531,14 @@
         packet, "Too short z packet, missing software/hardware specifier");
 
   bool want_breakpoint = true;
+  bool want_hardware = false;
 
   const GDBStoppointType stoppoint_type =
       GDBStoppointType(packet.GetS32(eStoppointInvalid));
   switch (stoppoint_type) {
   case eBreakpointHardware:
     want_breakpoint = true;
+    want_hardware = true;
     break;
   case eBreakpointSoftware:
     want_breakpoint = true;
@@ -2579,7 +2581,8 @@
 
   if (want_breakpoint) {
     // Try to clear the breakpoint.
-    const Error error = m_debugged_process_sp->RemoveBreakpoint(addr);
+    const Error error =
+        m_debugged_process_sp->RemoveBreakpoint(addr, want_hardware);
     if (error.Success())
       return SendOKResponse();
     Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
@@ -3037,9 +3040,14 @@
   if (packet.GetChar() != ':')
     return SendErrorResponse(67);
 
-  uint32_t num = m_debugged_process_sp->GetMaxWatchpoints();
+  uint32_t bp_max = 0, wp_max = 0;
+  if (!m_debugged_process_sp->GetHardwareDebugSupportInfo(bp_max, wp_max)) {
+    bp_max = 0;
+    wp_max = 0;
+  }
+
   StreamGDBRemote response;
-  response.Printf("num:%d;", num);
+  response.Printf("num:%d;", wp_max);
   return SendPacketNoLock(response.GetString());
 }
 
Index: source/Plugins/Process/Linux/NativeThreadLinux.h
===================================================================
--- source/Plugins/Process/Linux/NativeThreadLinux.h
+++ source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -46,6 +46,10 @@
 
   Error RemoveWatchpoint(lldb::addr_t addr) override;
 
+  Error SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+  Error RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+
 private:
   // ---------------------------------------------------------------------
   // Interface for friend classes
@@ -102,6 +106,7 @@
   std::string m_stop_description;
   using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
   WatchpointIndexMap m_watchpoint_index_map;
+  WatchpointIndexMap m_hw_break_index_map;
   llvm::Optional<SingleStepWorkaround> m_step_workaround;
 };
 
Index: source/Plugins/Process/Linux/NativeThreadLinux.cpp
===================================================================
--- source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -190,6 +190,38 @@
   return Error("Clearing hardware watchpoint failed.");
 }
 
+Error NativeThreadLinux::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) {
+  if (m_state == eStateLaunching)
+    return Error();
+
+  Error error = RemoveHardwareBreakpoint(addr);
+  if (error.Fail())
+    return error;
+
+  NativeRegisterContextSP reg_ctx = GetRegisterContext();
+  uint32_t bp_index = reg_ctx->SetHardwareBreakpoint(addr, size);
+
+  if (bp_index == LLDB_INVALID_INDEX32)
+    return Error("Setting hardware breakpoint failed.");
+
+  m_hw_break_index_map.insert({addr, bp_index});
+  return Error();
+}
+
+Error NativeThreadLinux::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+  auto bp = m_hw_break_index_map.find(addr);
+  if (bp == m_hw_break_index_map.end())
+    return Error();
+
+  uint32_t bp_index = bp->second;
+  if (GetRegisterContext()->ClearHardwareBreakpoint(bp_index)) {
+    m_hw_break_index_map.erase(bp);
+    return Error();
+  }
+
+  return Error("Clearing hardware breakpoint failed.");
+}
+
 Error NativeThreadLinux::Resume(uint32_t signo) {
   const StateType new_state = StateType::eStateRunning;
   MaybeLogStateChange(new_state);
@@ -211,6 +243,18 @@
     }
   }
 
+  // Set all active hardware breakpoint on all threads.
+  if (m_hw_break_index_map.empty()) {
+    NativeProcessLinux &process = GetProcess();
+
+    const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap();
+    GetRegisterContext()->ClearAllHardwareBreakpoints();
+    for (const auto &pair : hw_breakpoint_map) {
+      const auto &bp = pair.second;
+      SetHardwareBreakpoint(bp.m_addr, bp.m_size);
+    }
+  }
+
   intptr_t data = 0;
 
   if (signo != LLDB_INVALID_SIGNAL_NUMBER)
Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
===================================================================
--- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -46,10 +46,17 @@
   // Hardware breakpoints/watchpoint mangement functions
   //------------------------------------------------------------------
 
+  uint32_t NumSupportedHardwareBreakpoints() override;
+
   uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
 
   bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
 
+  Error ClearAllHardwareBreakpoints() override;
+
+  Error GetHardwareBreakHitIndex(uint32_t &bp_index,
+                                 lldb::addr_t trap_addr) override;
+
   uint32_t NumSupportedHardwareWatchpoints() override;
 
   uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
===================================================================
--- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -153,6 +153,7 @@
   ::memset(&m_fpr, 0, sizeof(m_fpr));
   ::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64));
   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
+  ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs));
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -360,10 +361,27 @@
   return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
 }
 
+uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+  if (log)
+    log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+  Error error;
+
+  // Read hardware breakpoint and watchpoint information.
+  error = ReadHardwareDebugInfo();
+
+  if (error.Fail())
+    return LLDB_INVALID_INDEX32;
+
+  return m_max_hbp_supported;
+}
+
 uint32_t
 NativeRegisterContextLinux_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
                                                         size_t size) {
-  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
   LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
 
   // Read hardware breakpoint and watchpoint information.
@@ -376,59 +394,51 @@
 
   // Check if size has a valid hardware breakpoint length.
   if (size != 4)
-    return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
-                                 // breakpoint
+    return LLDB_INVALID_INDEX32;
 
-  // Check 4-byte alignment for hardware breakpoint target address.
-  if (addr & 0x03)
-    return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
+  // Check breakpoint target address alignment
+  if (addr & (size - 1))
+    return LLDB_INVALID_INDEX32;
 
   // Setup control value
-  control_value = 0;
+  control_value = 0 << 3;
   control_value |= ((1 << size) - 1) << 5;
   control_value |= (2 << 1) | 1;
 
-  // Iterate over stored hardware breakpoints
-  // Find a free bp_index or update reference count if duplicate.
+  // Iterate over stored breakpoints and find a free bp_index
   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
+    } else if (m_hbr_regs[i].address == addr) {
+      return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
     }
   }
 
   if (bp_index == LLDB_INVALID_INDEX32)
     return LLDB_INVALID_INDEX32;
 
-  // Add new or update existing breakpoint
-  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);
-
-    if (error.Fail()) {
-      m_hbr_regs[bp_index].address = 0;
-      m_hbr_regs[bp_index].control &= ~1;
-      m_hbr_regs[bp_index].refcount = 0;
+  // Update breakpoint in local cache
+  m_hbr_regs[bp_index].real_addr = addr;
+  m_hbr_regs[bp_index].address = addr;
+  m_hbr_regs[bp_index].control = control_value;
 
-      return LLDB_INVALID_INDEX32;
-    }
-  } else
-    m_hbr_regs[bp_index].refcount++;
+  // PTRACE call to set corresponding hardware breakpoint register.
+  error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+  if (error.Fail()) {
+    m_hbr_regs[bp_index].address = 0;
+    m_hbr_regs[bp_index].control &= ~1;
+
+    return LLDB_INVALID_INDEX32;
+  }
 
   return bp_index;
 }
 
 bool NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint(
     uint32_t hw_idx) {
-  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
   LLDB_LOG(log, "hw_idx: {0}", hw_idx);
 
   // Read hardware breakpoint and watchpoint information.
@@ -440,35 +450,86 @@
   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) {
-    // Create a backup we can revert to in case of failure.
-    lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
-    uint32_t tempControl = m_hbr_regs[hw_idx].control;
-    uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount;
-
-    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);
-
-    if (error.Fail()) {
-      m_hbr_regs[hw_idx].control = tempControl;
-      m_hbr_regs[hw_idx].address = tempAddr;
-      m_hbr_regs[hw_idx].refcount = tempRefCount;
+  // Create a backup we can revert to in case of failure.
+  lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
+  uint32_t tempControl = m_hbr_regs[hw_idx].control;
+
+  m_hbr_regs[hw_idx].control &= ~1;
+  m_hbr_regs[hw_idx].address = 0;
+
+  // PTRACE call to clear corresponding hardware breakpoint register.
+  error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+  if (error.Fail()) {
+    m_hbr_regs[hw_idx].control = tempControl;
+    m_hbr_regs[hw_idx].address = tempAddr;
+
+    return false;
+  }
+
+  return true;
+}
+
+Error NativeRegisterContextLinux_arm64::GetHardwareBreakHitIndex(
+    uint32_t &bp_index, lldb::addr_t trap_addr) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
 
-      return false;
+  if (log)
+    log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+  lldb::addr_t break_addr;
+
+  for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
+    break_addr = m_hbr_regs[bp_index].address;
+
+    if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) {
+      m_hbr_regs[bp_index].hit_addr = trap_addr;
+      return Error();
     }
+  }
 
-    return true;
+  bp_index = LLDB_INVALID_INDEX32;
+  return Error();
+}
+
+Error NativeRegisterContextLinux_arm64::ClearAllHardwareBreakpoints() {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+  if (log)
+    log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
+
+  // Read hardware breakpoint and watchpoint information.
+  Error error = ReadHardwareDebugInfo();
+
+  if (error.Fail())
+    return error;
+
+  lldb::addr_t tempAddr = 0;
+  uint32_t tempControl = 0;
+
+  for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
+    if (m_hbr_regs[i].control & 0x01) {
+      // Create a backup we can revert to in case of failure.
+      tempAddr = m_hbr_regs[i].address;
+      tempControl = m_hbr_regs[i].control;
+
+      // Clear watchpoints in local cache
+      m_hbr_regs[i].control &= ~1;
+      m_hbr_regs[i].address = 0;
+
+      // Ptrace call to update hardware debug registers
+      error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+      if (error.Fail()) {
+        m_hbr_regs[i].control = tempControl;
+        m_hbr_regs[i].address = tempAddr;
+
+        return error;
+      }
+    }
   }
 
-  return false;
+  return Error();
 }
 
 uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints() {
@@ -478,7 +539,7 @@
   Error error = ReadHardwareDebugInfo();
 
   if (error.Fail())
-    return 0;
+    return LLDB_INVALID_INDEX32;
 
   LLDB_LOG(log, "{0}", m_max_hwp_supported);
   return m_max_hwp_supported;
Index: source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- source/Plugins/Process/Linux/NativeProcessLinux.h
+++ source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -86,6 +86,8 @@
 
   Error SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) override;
 
+  Error RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override;
+
   void DoStopIDBumped(uint32_t newBumpId) override;
 
   Error GetLoadedModuleFileSpec(const char *module_path,
Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -870,6 +870,17 @@
       break;
     }
 
+    // If a breakpoint was hit, report it
+    error = thread.GetRegisterContext()->GetHardwareBreakHitIndex(
+        wp_index, (uintptr_t)info.si_addr);
+    if (error.Fail())
+      LLDB_LOG(log, "received error while checking for watchpoint hits, pid = "
+                    "{0}, error = {1}",
+               thread.GetID(), error);
+    if (wp_index != LLDB_INVALID_INDEX32) {
+      MonitorBreakpoint(thread);
+      break;
+    }
     // Otherwise, report step over
     MonitorTrace(thread);
     break;
@@ -1719,11 +1730,18 @@
 Error NativeProcessLinux::SetBreakpoint(lldb::addr_t addr, uint32_t size,
                                         bool hardware) {
   if (hardware)
-    return Error("NativeProcessLinux does not support hardware breakpoints");
+    return SetHardwareBreakpoint(addr, size);
   else
     return SetSoftwareBreakpoint(addr, size);
 }
 
+Error NativeProcessLinux::RemoveBreakpoint(lldb::addr_t addr, bool hardware) {
+  if (hardware)
+    return RemoveHardwareBreakpoint(addr);
+  else
+    return NativeProcessProtocol::RemoveBreakpoint(addr);
+}
+
 Error NativeProcessLinux::GetSoftwareBreakpointTrapOpcode(
     size_t trap_opcode_size_hint, size_t &actual_opcode_size,
     const uint8_t *&trap_opcode_bytes) {
Index: source/Host/common/NativeRegisterContext.cpp
===================================================================
--- source/Host/common/NativeRegisterContext.cpp
+++ source/Host/common/NativeRegisterContext.cpp
@@ -246,10 +246,20 @@
   return LLDB_INVALID_INDEX32;
 }
 
+Error NativeRegisterContext::ClearAllHardwareBreakpoints() {
+  return Error("not implemented");
+}
+
 bool NativeRegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) {
   return false;
 }
 
+Error NativeRegisterContext::GetHardwareBreakHitIndex(uint32_t &bp_index,
+                                                      lldb::addr_t trap_addr) {
+  bp_index = LLDB_INVALID_INDEX32;
+  return Error("not implemented");
+}
+
 uint32_t NativeRegisterContext::NumSupportedHardwareWatchpoints() { return 0; }
 
 uint32_t NativeRegisterContext::SetHardwareWatchpoint(lldb::addr_t addr,
Index: source/Host/common/NativeProcessProtocol.cpp
===================================================================
--- source/Host/common/NativeProcessProtocol.cpp
+++ source/Host/common/NativeProcessProtocol.cpp
@@ -139,7 +139,8 @@
   return m_watchpoint_list.GetWatchpointMap();
 }
 
-uint32_t NativeProcessProtocol::GetMaxWatchpoints() const {
+bool NativeProcessProtocol::GetHardwareDebugSupportInfo(
+    uint32_t &breakpoints, uint32_t &watchpoints) const {
   // This default implementation will return the number of
   // *hardware* breakpoints available.  MacOSX and other OS
   // implementations that support software breakpoints will want to
@@ -154,19 +155,22 @@
       log->Warning("NativeProcessProtocol::%s (): failed to find a thread to "
                    "grab a NativeRegisterContext!",
                    __FUNCTION__);
-    return 0;
+    return false;
   }
 
   NativeRegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
   if (!reg_ctx_sp) {
     if (log)
       log->Warning("NativeProcessProtocol::%s (): failed to get a "
                    "RegisterContextNativeProcess from the first thread!",
                    __FUNCTION__);
-    return 0;
+    return false;
   }
 
-  return reg_ctx_sp->NumSupportedHardwareWatchpoints();
+  watchpoints = reg_ctx_sp->NumSupportedHardwareWatchpoints();
+  breakpoints = reg_ctx_sp->NumSupportedHardwareBreakpoints();
+
+  return true;
 }
 
 Error NativeProcessProtocol::SetWatchpoint(lldb::addr_t addr, size_t size,
@@ -263,6 +267,90 @@
   return overall_error.Fail() ? overall_error : error;
 }
 
+const HardwareBreakpointList::HardwareBreakpointMap &
+NativeProcessProtocol::GetHardwareBreakpointMap() const {
+  return m_hw_breakpoint_list.GetHardwareBreakpointMap();
+}
+
+Error NativeProcessProtocol::SetHardwareBreakpoint(lldb::addr_t addr,
+                                                   size_t size) {
+  // This default implementation assumes setting a hardware breakpoint for
+  // this process will require setting same hardware breakpoint for each
+  // of its existing threads. New thread will do the same once created.
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  // Exit here if the no of process threads are greater than available
+  // hardware breakpoint slots on target.
+  size_t no_of_threads = UpdateThreads();
+  uint32_t breakpoints = 0, watchpoints = 0;
+  bool info_success = GetHardwareDebugSupportInfo(breakpoints, watchpoints);
+
+  if (!info_success || breakpoints < no_of_threads)
+    return Error("Target does not have required no of hardware breakpoints");
+
+  // Vector below stores all thread pointer for which we have we successfully
+  // set this hardware breakpoint. If any of the current process threads fails
+  // to set this hardware breakpoint then roll back and remove this breakpoint
+  // for all the threads that had already set it successfully.
+  std::vector<NativeThreadProtocolSP> breakpoint_established_threads;
+
+  // Request to set a hardware breakpoint for each of current process threads.
+  std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+  for (auto thread_sp : m_threads) {
+    assert(thread_sp && "thread list should not have a NULL thread!");
+    if (!thread_sp)
+      continue;
+
+    Error thread_error = thread_sp->SetHardwareBreakpoint(addr, size);
+    if (thread_error.Success()) {
+      // Remember that we set this breakpoint successfully in
+      // case we need to clear it later.
+      breakpoint_established_threads.push_back(thread_sp);
+    } else {
+      // Unset the breakpoint for each thread we successfully
+      // set so that we get back to a consistent state of "not
+      // set" for this hardware breakpoint.
+      for (auto rollback_thread_sp : breakpoint_established_threads) {
+        Error remove_error = rollback_thread_sp->RemoveHardwareBreakpoint(addr);
+        if (remove_error.Fail() && log) {
+          log->Warning("NativeProcessProtocol::%s (): RemoveHardwareBreakpoint"
+                       " failed for pid=%" PRIu64 ", tid=%" PRIu64 ": %s",
+                       __FUNCTION__, GetID(), rollback_thread_sp->GetID(),
+                       remove_error.AsCString());
+        }
+      }
+
+      return thread_error;
+    }
+  }
+  return m_hw_breakpoint_list.Add(addr, size);
+}
+
+Error NativeProcessProtocol::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+  // Update the thread list
+  UpdateThreads();
+
+  Error overall_error;
+
+  std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+  for (auto thread_sp : m_threads) {
+    assert(thread_sp && "thread list should not have a NULL thread!");
+    if (!thread_sp)
+      continue;
+
+    const Error thread_error = thread_sp->RemoveHardwareBreakpoint(addr);
+    if (thread_error.Fail()) {
+      // Keep track of the first thread error if any threads
+      // fail. We want to try to remove the watchpoint from
+      // every thread, though, even if one or more have errors.
+      if (!overall_error.Fail())
+        overall_error = thread_error;
+    }
+  }
+  const Error error = m_hw_breakpoint_list.Remove(addr);
+  return overall_error.Fail() ? overall_error : error;
+}
+
 bool NativeProcessProtocol::RegisterNativeDelegate(
     NativeDelegate &native_delegate) {
   std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex);
@@ -339,8 +427,12 @@
       });
 }
 
-Error NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr) {
-  return m_breakpoint_list.DecRef(addr);
+Error NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr,
+                                              bool hardware) {
+  if (hardware)
+    return RemoveHardwareBreakpoint(addr);
+  else
+    return m_breakpoint_list.DecRef(addr);
 }
 
 Error NativeProcessProtocol::EnableBreakpoint(lldb::addr_t addr) {
Index: source/Host/common/HardwareBreakpointList.cpp
===================================================================
--- source/Host/common/HardwareBreakpointList.cpp
+++ source/Host/common/HardwareBreakpointList.cpp
@@ -0,0 +1,30 @@
+//===-- HardwareBreakpointList.cpp ------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/HardwareBreakpointList.h"
+
+#include "lldb/Core/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Error HardwareBreakpointList::Add(addr_t addr, size_t size) {
+  m_hw_breakpoints[addr] = {addr, size};
+  return Error();
+}
+
+Error HardwareBreakpointList::Remove(addr_t addr) {
+  m_hw_breakpoints.erase(addr);
+  return Error();
+}
+
+const HardwareBreakpointList::HardwareBreakpointMap &
+HardwareBreakpointList::GetHardwareBreakpointMap() const {
+  return m_hw_breakpoints;
+}
Index: source/Host/CMakeLists.txt
===================================================================
--- source/Host/CMakeLists.txt
+++ source/Host/CMakeLists.txt
@@ -9,6 +9,7 @@
   common/FileSpec.cpp
   common/FileSystem.cpp
   common/GetOptInc.cpp
+  common/HardwareBreakpointList.cpp
   common/Host.cpp
   common/HostInfoBase.cpp
   common/HostNativeThreadBase.cpp
Index: include/lldb/Host/common/NativeThreadProtocol.h
===================================================================
--- include/lldb/Host/common/NativeThreadProtocol.h
+++ include/lldb/Host/common/NativeThreadProtocol.h
@@ -56,6 +56,13 @@
 
   virtual Error RemoveWatchpoint(lldb::addr_t addr) = 0;
 
+  // ---------------------------------------------------------------------
+  // Thread-specific Hardware Breakpoint routines
+  // ---------------------------------------------------------------------
+  virtual Error SetHardwareBreakpoint(lldb::addr_t addr, size_t size) = 0;
+
+  virtual Error RemoveHardwareBreakpoint(lldb::addr_t addr) = 0;
+
 protected:
   NativeProcessProtocolWP m_process_wp;
   lldb::tid_t m_tid;
Index: include/lldb/Host/common/NativeRegisterContext.h
===================================================================
--- include/lldb/Host/common/NativeRegisterContext.h
+++ include/lldb/Host/common/NativeRegisterContext.h
@@ -75,6 +75,11 @@
 
   virtual bool ClearHardwareBreakpoint(uint32_t hw_idx);
 
+  virtual Error ClearAllHardwareBreakpoints();
+
+  virtual Error GetHardwareBreakHitIndex(uint32_t &bp_index,
+                                         lldb::addr_t trap_addr);
+
   virtual uint32_t NumSupportedHardwareWatchpoints();
 
   virtual uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
Index: include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- include/lldb/Host/common/NativeProcessProtocol.h
+++ include/lldb/Host/common/NativeProcessProtocol.h
@@ -19,6 +19,7 @@
 #include "lldb/lldb-types.h"
 #include "llvm/ADT/StringRef.h"
 
+#include "HardwareBreakpointList.h"
 #include "NativeBreakpointList.h"
 #include "NativeWatchpointList.h"
 
@@ -99,18 +100,29 @@
   virtual Error SetBreakpoint(lldb::addr_t addr, uint32_t size,
                               bool hardware) = 0;
 
-  virtual Error RemoveBreakpoint(lldb::addr_t addr);
+  virtual Error RemoveBreakpoint(lldb::addr_t addr, bool hardware = false);
 
   virtual Error EnableBreakpoint(lldb::addr_t addr);
 
   virtual Error DisableBreakpoint(lldb::addr_t addr);
 
   //----------------------------------------------------------------------
+  // Hardware Breakpoint functions
+  //----------------------------------------------------------------------
+  virtual const HardwareBreakpointList::HardwareBreakpointMap &
+  GetHardwareBreakpointMap() const;
+
+  virtual Error SetHardwareBreakpoint(lldb::addr_t addr, size_t size);
+
+  virtual Error RemoveHardwareBreakpoint(lldb::addr_t addr);
+
+  //----------------------------------------------------------------------
   // Watchpoint functions
   //----------------------------------------------------------------------
   virtual const NativeWatchpointList::WatchpointMap &GetWatchpointMap() const;
 
-  virtual uint32_t GetMaxWatchpoints() const;
+  virtual bool GetHardwareDebugSupportInfo(uint32_t &breakpoints,
+                                           uint32_t &watchpoints) const;
 
   virtual Error SetWatchpoint(lldb::addr_t addr, size_t size,
                               uint32_t watch_flags, bool hardware);
@@ -305,6 +317,7 @@
   std::vector<NativeDelegate *> m_delegates;
   NativeBreakpointList m_breakpoint_list;
   NativeWatchpointList m_watchpoint_list;
+  HardwareBreakpointList m_hw_breakpoint_list;
   int m_terminal_fd;
   uint32_t m_stop_id;
 
Index: include/lldb/Host/common/HardwareBreakpointList.h
===================================================================
--- include/lldb/Host/common/HardwareBreakpointList.h
+++ include/lldb/Host/common/HardwareBreakpointList.h
@@ -0,0 +1,39 @@
+//===-- HardwareBreakpointList.h --------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_HardwareBreakpointList_h_
+#define liblldb_HardwareBreakpointList_h_
+
+#include "lldb/Utility/Error.h"
+#include "lldb/lldb-private-forward.h"
+
+#include <map>
+
+namespace lldb_private {
+struct HardwareBreakpoint {
+  lldb::addr_t m_addr;
+  size_t m_size;
+};
+
+class HardwareBreakpointList {
+public:
+  Error Add(lldb::addr_t addr, size_t size);
+
+  Error Remove(lldb::addr_t addr);
+
+  using HardwareBreakpointMap = std::map<lldb::addr_t, HardwareBreakpoint>;
+
+  const HardwareBreakpointMap &GetHardwareBreakpointMap() const;
+
+private:
+  HardwareBreakpointMap m_hw_breakpoints;
+};
+}
+
+#endif // ifndef liblldb_HardwareBreakpointList_h_
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to