[Lldb-commits] [lldb] ae5836f - [LLDB][Process/Utility] Introduce NativeRegisterContextDBReg class

via lldb-commits lldb-commits at lists.llvm.org
Wed Dec 11 18:04:27 PST 2024


Author: wanglei
Date: 2024-12-12T10:04:24+08:00
New Revision: ae5836f6b6a8544e6226f5c1ba6b1beacfe01aef

URL: https://github.com/llvm/llvm-project/commit/ae5836f6b6a8544e6226f5c1ba6b1beacfe01aef
DIFF: https://github.com/llvm/llvm-project/commit/ae5836f6b6a8544e6226f5c1ba6b1beacfe01aef.diff

LOG: [LLDB][Process/Utility] Introduce NativeRegisterContextDBReg class

Since the setup of debug registers for AArch64 and LoongArch is similar,
we extracted the shared logic from Class:
`NativeRegisterContextDBReg_arm64`
into a new Class:
`NativeRegisterContextDBReg`.
This will simplify the subsequent implementation of hardware breakpoints
and watchpoints on LoongArch.

Reviewed By: DavidSpickett

Pull Request: https://github.com/llvm/llvm-project/pull/118043

Added: 
    lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
    lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h

Modified: 
    lldb/source/Plugins/Process/Utility/CMakeLists.txt
    lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
    lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
index 308ea29e31ad77..0526c95503175e 100644
--- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
@@ -9,6 +9,7 @@ add_lldb_library(lldbPluginProcessUtility
   LinuxSignals.cpp
   MemoryTagManagerAArch64MTE.cpp
   NativeProcessSoftwareSingleStep.cpp
+  NativeRegisterContextDBReg.cpp
   NativeRegisterContextDBReg_arm64.cpp
   NativeRegisterContextDBReg_x86.cpp
   NativeRegisterContextRegisterInfo.cpp

diff  --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
new file mode 100644
index 00000000000000..1e0c1a5db09ca4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
@@ -0,0 +1,409 @@
+//===-- NativeRegisterContextDBReg.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeRegisterContextDBReg.h"
+
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+
+using namespace lldb_private;
+
+uint32_t NativeRegisterContextDBReg::NumSupportedHardwareBreakpoints() {
+  Log *log = GetLog(LLDBLog::Breakpoints);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+
+  if (error) {
+    LLDB_LOG_ERROR(log, std::move(error),
+                   "failed to read debug registers: {0}");
+    return 0;
+  }
+
+  LLDB_LOG(log, "{0}", m_max_hbp_supported);
+  return m_max_hbp_supported;
+}
+
+uint32_t NativeRegisterContextDBReg::SetHardwareBreakpoint(lldb::addr_t addr,
+                                                           size_t size) {
+  Log *log = GetLog(LLDBLog::Breakpoints);
+  LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error) {
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to set breakpoint: failed to read debug registers: {0}");
+    return LLDB_INVALID_INDEX32;
+  }
+
+  uint32_t control_value = 0, bp_index = 0;
+
+  if (!ValidateBreakpoint(size, addr))
+    return LLDB_INVALID_INDEX32;
+
+  control_value = MakeBreakControlValue(size);
+
+  // 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 (!BreakpointIsEnabled(i))
+      bp_index = i; // Mark last free slot
+    else if (m_hbp_regs[i].address == addr)
+      return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
+  }
+
+  if (bp_index == LLDB_INVALID_INDEX32)
+    return LLDB_INVALID_INDEX32;
+
+  // Update breakpoint in local cache
+  m_hbp_regs[bp_index].real_addr = addr;
+  m_hbp_regs[bp_index].address = addr;
+  m_hbp_regs[bp_index].control = control_value;
+
+  // PTRACE call to set corresponding hardware breakpoint register.
+  error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+  if (error) {
+    m_hbp_regs[bp_index].address = 0;
+    m_hbp_regs[bp_index].control &= ~m_hw_dbg_enable_bit;
+
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to set breakpoint: failed to write debug registers: {0}");
+    return LLDB_INVALID_INDEX32;
+  }
+
+  return bp_index;
+}
+
+bool NativeRegisterContextDBReg::ClearHardwareBreakpoint(uint32_t hw_idx) {
+  Log *log = GetLog(LLDBLog::Breakpoints);
+  LLDB_LOG(log, "hw_idx: {0}", hw_idx);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error) {
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to clear breakpoint: failed to read debug registers: {0}");
+    return false;
+  }
+
+  if (hw_idx >= m_max_hbp_supported)
+    return false;
+
+  // Create a backup we can revert to in case of failure.
+  lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;
+  uint32_t tempControl = m_hbp_regs[hw_idx].control;
+
+  m_hbp_regs[hw_idx].control &= ~m_hw_dbg_enable_bit;
+  m_hbp_regs[hw_idx].address = 0;
+
+  // PTRACE call to clear corresponding hardware breakpoint register.
+  error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+  if (error) {
+    m_hbp_regs[hw_idx].control = tempControl;
+    m_hbp_regs[hw_idx].address = tempAddr;
+
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to clear breakpoint: failed to write debug registers: {0}");
+    return false;
+  }
+
+  return true;
+}
+
+Status
+NativeRegisterContextDBReg::GetHardwareBreakHitIndex(uint32_t &bp_index,
+                                                     lldb::addr_t trap_addr) {
+  Log *log = GetLog(LLDBLog::Breakpoints);
+
+  LLDB_LOGF(log, "NativeRegisterContextDBReg::%s()", __FUNCTION__);
+
+  lldb::addr_t break_addr;
+
+  for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
+    break_addr = m_hbp_regs[bp_index].address;
+
+    if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {
+      m_hbp_regs[bp_index].hit_addr = trap_addr;
+      return Status();
+    }
+  }
+
+  bp_index = LLDB_INVALID_INDEX32;
+  return Status();
+}
+
+Status NativeRegisterContextDBReg::ClearAllHardwareBreakpoints() {
+  Log *log = GetLog(LLDBLog::Breakpoints);
+
+  LLDB_LOGF(log, "NativeRegisterContextDBReg::%s()", __FUNCTION__);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error)
+    return Status::FromError(std::move(error));
+
+  for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
+    if (!BreakpointIsEnabled(i))
+      continue;
+    // Create a backup we can revert to in case of failure.
+    lldb::addr_t tempAddr = m_hbp_regs[i].address;
+    uint32_t tempControl = m_hbp_regs[i].control;
+
+    // Clear watchpoints in local cache
+    m_hbp_regs[i].control &= ~m_hw_dbg_enable_bit;
+    m_hbp_regs[i].address = 0;
+
+    // Ptrace call to update hardware debug registers
+    error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+    if (error) {
+      m_hbp_regs[i].control = tempControl;
+      m_hbp_regs[i].address = tempAddr;
+
+      return Status::FromError(std::move(error));
+    }
+  }
+
+  return Status();
+}
+
+bool NativeRegisterContextDBReg::BreakpointIsEnabled(uint32_t bp_index) {
+  return ((m_hbp_regs[bp_index].control & m_hw_dbg_enable_bit) != 0);
+}
+
+uint32_t NativeRegisterContextDBReg::NumSupportedHardwareWatchpoints() {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error) {
+    LLDB_LOG_ERROR(log, std::move(error),
+                   "failed to read debug registers: {0}");
+    return 0;
+  }
+
+  return m_max_hwp_supported;
+}
+
+uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint(
+    lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
+           watch_flags);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error) {
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to set watchpoint: failed to read debug registers: {0}");
+    return LLDB_INVALID_INDEX32;
+  }
+
+  uint32_t control_value = 0, wp_index = 0;
+  lldb::addr_t real_addr = addr;
+  WatchpointDetails details{size, addr};
+
+  auto adjusted = AdjustWatchpoint(details);
+  if (adjusted == std::nullopt)
+    return LLDB_INVALID_INDEX32;
+  size = adjusted->size;
+  addr = adjusted->addr;
+
+  // Check if we are setting watchpoint other than read/write/access Also
+  // update watchpoint flag to match AArch64/LoongArch write-read bit
+  // configuration.
+  switch (watch_flags) {
+  case lldb::eWatchpointKindWrite:
+    watch_flags = 2;
+    break;
+  case lldb::eWatchpointKindRead:
+    watch_flags = 1;
+    break;
+  case (lldb::eWatchpointKindRead | lldb::eWatchpointKindWrite):
+    break;
+  default:
+    return LLDB_INVALID_INDEX32;
+  }
+
+  control_value = MakeWatchControlValue(size, watch_flags);
+
+  // Iterate over stored watchpoints and find a free wp_index
+  wp_index = LLDB_INVALID_INDEX32;
+  for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
+    if (!WatchpointIsEnabled(i))
+      wp_index = i; // Mark last free slot
+    else if (m_hwp_regs[i].address == addr) {
+      return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
+    }
+  }
+
+  if (wp_index == LLDB_INVALID_INDEX32)
+    return LLDB_INVALID_INDEX32;
+
+  // Update watchpoint in local cache
+  m_hwp_regs[wp_index].real_addr = real_addr;
+  m_hwp_regs[wp_index].address = addr;
+  m_hwp_regs[wp_index].control = control_value;
+
+  // PTRACE call to set corresponding watchpoint register.
+  error = WriteHardwareDebugRegs(eDREGTypeWATCH);
+
+  if (error) {
+    m_hwp_regs[wp_index].address = 0;
+    m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit;
+
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to set watchpoint: failed to write debug registers: {0}");
+    return LLDB_INVALID_INDEX32;
+  }
+
+  return wp_index;
+}
+
+bool NativeRegisterContextDBReg::ClearHardwareWatchpoint(uint32_t wp_index) {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  LLDB_LOG(log, "wp_index: {0}", wp_index);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error) {
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to set watchpoint: failed to read debug registers: {0}");
+    return LLDB_INVALID_INDEX32;
+  }
+
+  if (wp_index >= m_max_hwp_supported)
+    return false;
+
+  // Create a backup we can revert to in case of failure.
+  lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
+  uint32_t tempControl = m_hwp_regs[wp_index].control;
+
+  // Update watchpoint in local cache
+  m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit;
+  m_hwp_regs[wp_index].address = 0;
+
+  // Ptrace call to update hardware debug registers
+  error = WriteHardwareDebugRegs(eDREGTypeWATCH);
+
+  if (error) {
+    m_hwp_regs[wp_index].control = tempControl;
+    m_hwp_regs[wp_index].address = tempAddr;
+
+    LLDB_LOG_ERROR(
+        log, std::move(error),
+        "unable to clear watchpoint: failed to read debug registers: {0}");
+    return false;
+  }
+
+  return true;
+}
+
+Status NativeRegisterContextDBReg::ClearAllHardwareWatchpoints() {
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error)
+    return Status::FromError(std::move(error));
+
+  for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
+    if (!WatchpointIsEnabled(i))
+      continue;
+    // Create a backup we can revert to in case of failure.
+    lldb::addr_t tempAddr = m_hwp_regs[i].address;
+    uint32_t tempControl = m_hwp_regs[i].control;
+
+    // Clear watchpoints in local cache
+    m_hwp_regs[i].control = 0;
+    m_hwp_regs[i].address = 0;
+
+    // Ptrace call to update hardware debug registers
+    error = WriteHardwareDebugRegs(eDREGTypeWATCH);
+
+    if (error) {
+      m_hwp_regs[i].control = tempControl;
+      m_hwp_regs[i].address = tempAddr;
+
+      return Status::FromError(std::move(error));
+    }
+  }
+
+  return Status();
+}
+
+Status
+NativeRegisterContextDBReg::GetWatchpointHitIndex(uint32_t &wp_index,
+                                                  lldb::addr_t trap_addr) {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
+
+  // Read hardware breakpoint and watchpoint information.
+  llvm::Error error = ReadHardwareDebugInfo();
+  if (error)
+    return Status::FromError(std::move(error));
+
+  // AArch64 need mask off ignored bits from watchpoint trap address.
+  trap_addr = FixWatchpointHitAddress(trap_addr);
+
+  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 (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
+        trap_addr < watch_addr + watch_size) {
+      m_hwp_regs[wp_index].hit_addr = trap_addr;
+      return Status();
+    }
+  }
+
+  wp_index = LLDB_INVALID_INDEX32;
+  return Status();
+}
+
+bool NativeRegisterContextDBReg::WatchpointIsEnabled(uint32_t wp_index) {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  LLDB_LOG(log, "wp_index: {0}", wp_index);
+  return ((m_hwp_regs[wp_index].control & m_hw_dbg_enable_bit) != 0);
+}
+
+lldb::addr_t
+NativeRegisterContextDBReg::GetWatchpointAddress(uint32_t wp_index) {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  LLDB_LOG(log, "wp_index: {0}", wp_index);
+
+  if (wp_index >= m_max_hwp_supported)
+    return LLDB_INVALID_ADDRESS;
+
+  if (WatchpointIsEnabled(wp_index))
+    return m_hwp_regs[wp_index].real_addr;
+  return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t
+NativeRegisterContextDBReg::GetWatchpointHitAddress(uint32_t wp_index) {
+  Log *log = GetLog(LLDBLog::Watchpoints);
+  LLDB_LOG(log, "wp_index: {0}", wp_index);
+
+  if (wp_index >= m_max_hwp_supported)
+    return LLDB_INVALID_ADDRESS;
+
+  if (WatchpointIsEnabled(wp_index))
+    return m_hwp_regs[wp_index].hit_addr;
+  return LLDB_INVALID_ADDRESS;
+}

diff  --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
new file mode 100644
index 00000000000000..e17a700f7dad7e
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
@@ -0,0 +1,101 @@
+//===-- NativeRegisterContextDBReg.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_NativeRegisterContextDBReg_h
+#define lldb_NativeRegisterContextDBReg_h
+
+#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+
+#include <array>
+
+// Common utilities for hardware breakpoints and hardware watchpoints on AArch64
+// and LoongArch.
+
+namespace lldb_private {
+
+class NativeRegisterContextDBReg
+    : public virtual NativeRegisterContextRegisterInfo {
+public:
+  explicit NativeRegisterContextDBReg(uint32_t enable_bit)
+      : m_hw_dbg_enable_bit(enable_bit) {}
+
+  uint32_t NumSupportedHardwareBreakpoints() override;
+
+  uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+  bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
+
+  Status ClearAllHardwareBreakpoints() override;
+
+  Status 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,
+                                 uint32_t watch_flags) override;
+
+  bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+  Status ClearAllHardwareWatchpoints() override;
+
+  Status GetWatchpointHitIndex(uint32_t &wp_index,
+                               lldb::addr_t trap_addr) override;
+
+  lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override;
+
+  lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
+
+protected:
+  // Debug register type select
+  enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK };
+
+  /// Debug register info for hardware breakpoints and watchpoints management.
+  struct DREG {
+    lldb::addr_t address;  // Breakpoint/watchpoint address value.
+    lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception
+                           // occurred.
+    lldb::addr_t real_addr; // Address value that should cause target to stop.
+    uint32_t control;       // Breakpoint/watchpoint control value.
+  };
+
+  std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints
+  std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints
+
+  uint32_t m_max_hbp_supported;
+  uint32_t m_max_hwp_supported;
+  const uint32_t m_hw_dbg_enable_bit;
+
+  bool WatchpointIsEnabled(uint32_t wp_index);
+  bool BreakpointIsEnabled(uint32_t bp_index);
+
+  // On AArch64 and Loongarch the hardware breakpoint length size is 4, and the
+  // target address must 4-byte alignment.
+  bool ValidateBreakpoint(size_t size, lldb::addr_t addr) {
+    return (size == 4) && !(addr & 0x3);
+  }
+  struct WatchpointDetails {
+    size_t size;
+    lldb::addr_t addr;
+  };
+  virtual std::optional<WatchpointDetails>
+  AdjustWatchpoint(const WatchpointDetails &details) = 0;
+  virtual uint32_t MakeBreakControlValue(size_t size) = 0;
+  virtual uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) = 0;
+  virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0;
+
+  virtual llvm::Error ReadHardwareDebugInfo() = 0;
+  virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0;
+  virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) {
+    return hit_addr;
+  }
+};
+
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextDBReg_h

diff  --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
index f1d0756b3ed9c5..f2f772ad615c92 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
@@ -14,246 +14,33 @@
 
 using namespace lldb_private;
 
-// E (bit 0), used to enable breakpoint/watchpoint
-constexpr uint32_t g_enable_bit = 1;
-// PAC (bits 2:1): 0b10
-constexpr uint32_t g_pac_bits = (2 << 1);
-
-// Returns appropriate control register bits for the specified size
-static constexpr inline uint64_t GetSizeBits(int size) {
-  // BAS (bits 12:5) hold a bit-mask of addresses to watch
-  // e.g. 0b00000001 means 1 byte at address
-  //      0b00000011 means 2 bytes (addr..addr+1)
-  //      ...
-  //      0b11111111 means 8 bytes (addr..addr+7)
-  return ((1 << size) - 1) << 5;
-}
-
-uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {
-  Log *log = GetLog(LLDBLog::Breakpoints);
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error) {
-    LLDB_LOG_ERROR(log, std::move(error),
-                   "failed to read debug registers: {0}");
-    return 0;
-  }
-
-  return m_max_hbp_supported;
-}
-
 uint32_t
-NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
-                                                        size_t size) {
-  Log *log = GetLog(LLDBLog::Breakpoints);
-  LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
-
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error) {
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to set breakpoint: failed to read debug registers: {0}");
-    return LLDB_INVALID_INDEX32;
-  }
-
-  uint32_t control_value = 0, bp_index = 0;
-
-  // Check if size has a valid hardware breakpoint length.
-  if (size != 4)
-    return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
-                                 // breakpoint
-
-  // Check 4-byte alignment for hardware breakpoint target address.
-  if (addr & 0x03)
-    return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
-
-  // Setup control value
-  control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
-
-  // 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 (!BreakpointIsEnabled(i))
-      bp_index = i; // Mark last free slot
-    else if (m_hbp_regs[i].address == addr)
-      return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
-  }
-
-  if (bp_index == LLDB_INVALID_INDEX32)
-    return LLDB_INVALID_INDEX32;
-
-  // Update breakpoint in local cache
-  m_hbp_regs[bp_index].real_addr = addr;
-  m_hbp_regs[bp_index].address = addr;
-  m_hbp_regs[bp_index].control = control_value;
-
-  // PTRACE call to set corresponding hardware breakpoint register.
-  error = WriteHardwareDebugRegs(eDREGTypeBREAK);
-
-  if (error) {
-    m_hbp_regs[bp_index].address = 0;
-    m_hbp_regs[bp_index].control &= ~1;
-
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to set breakpoint: failed to write debug registers: {0}");
-    return LLDB_INVALID_INDEX32;
-  }
-
-  return bp_index;
-}
-
-bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(
-    uint32_t hw_idx) {
-  Log *log = GetLog(LLDBLog::Breakpoints);
-  LLDB_LOG(log, "hw_idx: {0}", hw_idx);
-
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error) {
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to clear breakpoint: failed to read debug registers: {0}");
-    return false;
-  }
-
-  if (hw_idx >= m_max_hbp_supported)
-    return false;
-
-  // Create a backup we can revert to in case of failure.
-  lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;
-  uint32_t tempControl = m_hbp_regs[hw_idx].control;
-
-  m_hbp_regs[hw_idx].control &= ~g_enable_bit;
-  m_hbp_regs[hw_idx].address = 0;
-
-  // PTRACE call to clear corresponding hardware breakpoint register.
-  error = WriteHardwareDebugRegs(eDREGTypeBREAK);
-
-  if (error) {
-    m_hbp_regs[hw_idx].control = tempControl;
-    m_hbp_regs[hw_idx].address = tempAddr;
-
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to clear breakpoint: failed to write debug registers: {0}");
-    return false;
-  }
-
-  return true;
-}
-
-Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(
-    uint32_t &bp_index, lldb::addr_t trap_addr) {
-  Log *log = GetLog(LLDBLog::Breakpoints);
-
-  LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
-
-  lldb::addr_t break_addr;
-
-  for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
-    break_addr = m_hbp_regs[bp_index].address;
-
-    if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {
-      m_hbp_regs[bp_index].hit_addr = trap_addr;
-      return Status();
-    }
-  }
-
-  bp_index = LLDB_INVALID_INDEX32;
-  return Status();
-}
-
-Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {
-  Log *log = GetLog(LLDBLog::Breakpoints);
-
-  LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
-
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error)
-    return Status::FromError(std::move(error));
-
-  for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
-    if (BreakpointIsEnabled(i)) {
-      // Create a backup we can revert to in case of failure.
-      lldb::addr_t tempAddr = m_hbp_regs[i].address;
-      uint32_t tempControl = m_hbp_regs[i].control;
-
-      // Clear watchpoints in local cache
-      m_hbp_regs[i].control &= ~g_enable_bit;
-      m_hbp_regs[i].address = 0;
-
-      // Ptrace call to update hardware debug registers
-      error = WriteHardwareDebugRegs(eDREGTypeBREAK);
-
-      if (error) {
-        m_hbp_regs[i].control = tempControl;
-        m_hbp_regs[i].address = tempAddr;
-
-        return Status::FromError(std::move(error));
-      }
-    }
-  }
-
-  return Status();
-}
-
-bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) {
-  if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0)
-    return true;
-  else
-    return false;
-}
-
-uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error) {
-    LLDB_LOG_ERROR(log, std::move(error),
-                   "failed to read debug registers: {0}");
-    return 0;
-  }
-
-  return m_max_hwp_supported;
-}
-
-uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
-    lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
   Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
-           watch_flags);
-
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error) {
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to set watchpoint: failed to read debug registers: {0}");
-    return LLDB_INVALID_INDEX32;
-  }
-
-  uint32_t control_value = 0, wp_index = 0;
-  lldb::addr_t real_addr = addr;
+  LLDB_LOG(log, "wp_index: {0}", wp_index);
 
-  // Check if we are setting watchpoint other than read/write/access Also
-  // update watchpoint flag to match AArch64 write-read bit configuration.
-  switch (watch_flags) {
-  case 1:
-    watch_flags = 2;
-    break;
-  case 2:
-    watch_flags = 1;
-    break;
-  case 3:
-    break;
+  switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
+  case 0x01:
+    return 1;
+  case 0x03:
+    return 2;
+  case 0x0f:
+    return 4;
+  case 0xff:
+    return 8;
   default:
-    return LLDB_INVALID_INDEX32;
+    return 0;
   }
+}
 
+std::optional<NativeRegisterContextDBReg::WatchpointDetails>
+NativeRegisterContextDBReg_arm64::AdjustWatchpoint(
+    const WatchpointDetails &details) {
+  size_t size = details.size;
+  lldb::addr_t addr = details.addr;
   // Check if size has a valid hardware watchpoint length.
   if (size != 1 && size != 2 && size != 4 && size != 8)
-    return LLDB_INVALID_INDEX32;
+    return std::nullopt;
 
   // Check 8-byte alignment for hardware watchpoint target address. Below is a
   // hack to recalculate address and size in order to make sure we can watch
@@ -262,8 +49,9 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
     uint8_t watch_mask = (addr & 0x07) + size;
 
     if (watch_mask > 0x08)
-      return LLDB_INVALID_INDEX32;
-    else if (watch_mask <= 0x02)
+      return std::nullopt;
+
+    if (watch_mask <= 0x02)
       size = 2;
     else if (watch_mask <= 0x04)
       size = 4;
@@ -272,199 +60,37 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
 
     addr = addr & (~0x07);
   }
-
-  // Setup control value
-  control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
-  control_value |= watch_flags << 3;
-
-  // Iterate over stored watchpoints and find a free wp_index
-  wp_index = LLDB_INVALID_INDEX32;
-  for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
-    if (!WatchpointIsEnabled(i))
-      wp_index = i; // Mark last free slot
-    else if (m_hwp_regs[i].address == addr) {
-      return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
-    }
-  }
-
-  if (wp_index == LLDB_INVALID_INDEX32)
-    return LLDB_INVALID_INDEX32;
-
-  // Update watchpoint in local cache
-  m_hwp_regs[wp_index].real_addr = real_addr;
-  m_hwp_regs[wp_index].address = addr;
-  m_hwp_regs[wp_index].control = control_value;
-
-  // PTRACE call to set corresponding watchpoint register.
-  error = WriteHardwareDebugRegs(eDREGTypeWATCH);
-
-  if (error) {
-    m_hwp_regs[wp_index].address = 0;
-    m_hwp_regs[wp_index].control &= ~g_enable_bit;
-
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to set watchpoint: failed to write debug registers: {0}");
-    return LLDB_INVALID_INDEX32;
-  }
-
-  return wp_index;
-}
-
-bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(
-    uint32_t wp_index) {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "wp_index: {0}", wp_index);
-
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error) {
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to clear watchpoint: failed to read debug registers: {0}");
-    return false;
-  }
-
-  if (wp_index >= m_max_hwp_supported)
-    return false;
-
-  // Create a backup we can revert to in case of failure.
-  lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
-  uint32_t tempControl = m_hwp_regs[wp_index].control;
-
-  // Update watchpoint in local cache
-  m_hwp_regs[wp_index].control &= ~g_enable_bit;
-  m_hwp_regs[wp_index].address = 0;
-
-  // Ptrace call to update hardware debug registers
-  error = WriteHardwareDebugRegs(eDREGTypeWATCH);
-
-  if (error) {
-    m_hwp_regs[wp_index].control = tempControl;
-    m_hwp_regs[wp_index].address = tempAddr;
-
-    LLDB_LOG_ERROR(
-        log, std::move(error),
-        "unable to clear watchpoint: failed to write debug registers: {0}");
-    return false;
-  }
-
-  return true;
+  return WatchpointDetails{size, addr};
 }
 
-Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error)
-    return Status::FromError(std::move(error));
+uint32_t NativeRegisterContextDBReg_arm64::MakeBreakControlValue(size_t size) {
+  // PAC (bits 2:1): 0b10
+  const uint32_t pac_bits = 2 << 1;
 
-  for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
-    if (WatchpointIsEnabled(i)) {
-      // Create a backup we can revert to in case of failure.
-      lldb::addr_t tempAddr = m_hwp_regs[i].address;
-      uint32_t tempControl = m_hwp_regs[i].control;
-
-      // Clear watchpoints in local cache
-      m_hwp_regs[i].control &= ~g_enable_bit;
-      m_hwp_regs[i].address = 0;
-
-      // Ptrace call to update hardware debug registers
-      error = WriteHardwareDebugRegs(eDREGTypeWATCH);
-
-      if (error) {
-        m_hwp_regs[i].control = tempControl;
-        m_hwp_regs[i].address = tempAddr;
-
-        return Status::FromError(std::move(error));
-      }
-    }
-  }
+  // BAS (bits 12:5) hold a bit-mask of addresses to watch
+  // e.g. 0b00000001 means 1 byte at address
+  //      0b00000011 means 2 bytes (addr..addr+1)
+  //      ...
+  //      0b11111111 means 8 bytes (addr..addr+7)
+  size_t encoded_size = ((1 << size) - 1) << 5;
 
-  return Status();
+  // Return encoded hardware breakpoint control value.
+  return m_hw_dbg_enable_bit | pac_bits | encoded_size;
 }
 
 uint32_t
-NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "wp_index: {0}", wp_index);
+NativeRegisterContextDBReg_arm64::MakeWatchControlValue(size_t size,
+                                                        uint32_t watch_flags) {
+  // PAC (bits 2:1): 0b10
+  const uint32_t pac_bits = 2 << 1;
 
-  switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
-  case 0x01:
-    return 1;
-  case 0x03:
-    return 2;
-  case 0x0f:
-    return 4;
-  case 0xff:
-    return 8;
-  default:
-    return 0;
-  }
-}
-
-bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "wp_index: {0}", wp_index);
-
-  if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0)
-    return true;
-  else
-    return false;
-}
-
-Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
-    uint32_t &wp_index, lldb::addr_t trap_addr) {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
-
-  // Read hardware breakpoint and watchpoint information.
-  llvm::Error error = ReadHardwareDebugInfo();
-  if (error)
-    return Status::FromError(std::move(error));
-
-  // Mask off ignored bits from watchpoint trap address.
-  trap_addr = FixWatchpointHitAddress(trap_addr);
-
-  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 (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
-        trap_addr < watch_addr + watch_size) {
-      m_hwp_regs[wp_index].hit_addr = trap_addr;
-      return Status();
-    }
-  }
-
-  wp_index = LLDB_INVALID_INDEX32;
-  return Status();
-}
-
-lldb::addr_t
-NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "wp_index: {0}", wp_index);
-
-  if (wp_index >= m_max_hwp_supported)
-    return LLDB_INVALID_ADDRESS;
-
-  if (WatchpointIsEnabled(wp_index))
-    return m_hwp_regs[wp_index].real_addr;
-  return LLDB_INVALID_ADDRESS;
-}
-
-lldb::addr_t
-NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
-  Log *log = GetLog(LLDBLog::Watchpoints);
-  LLDB_LOG(log, "wp_index: {0}", wp_index);
-
-  if (wp_index >= m_max_hwp_supported)
-    return LLDB_INVALID_ADDRESS;
+  // BAS (bits 12:5) hold a bit-mask of addresses to watch
+  // e.g. 0b00000001 means 1 byte at address
+  //      0b00000011 means 2 bytes (addr..addr+1)
+  //      ...
+  //      0b11111111 means 8 bytes (addr..addr+7)
+  size_t encoded_size = ((1 << size) - 1) << 5;
 
-  if (WatchpointIsEnabled(wp_index))
-    return m_hwp_regs[wp_index].hit_addr;
-  return LLDB_INVALID_ADDRESS;
+  // Return encoded hardware watchpoint control value.
+  return m_hw_dbg_enable_bit | pac_bits | encoded_size | (watch_flags << 3);
 }

diff  --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
index f8246ff4d71873..42d6bacf648615 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
@@ -9,79 +9,24 @@
 #ifndef lldb_NativeRegisterContextDBReg_arm64_h
 #define lldb_NativeRegisterContextDBReg_arm64_h
 
-#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
-
-#include <array>
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
 
 namespace lldb_private {
 
-class NativeRegisterContextDBReg_arm64
-    : public virtual NativeRegisterContextRegisterInfo {
+class NativeRegisterContextDBReg_arm64 : public NativeRegisterContextDBReg {
 public:
-  uint32_t NumSupportedHardwareBreakpoints() override;
-
-  uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
-
-  bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
-
-  Status ClearAllHardwareBreakpoints() override;
-
-  Status GetHardwareBreakHitIndex(uint32_t &bp_index,
-                                  lldb::addr_t trap_addr) override;
-
-  bool BreakpointIsEnabled(uint32_t bp_index);
-
-  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;
-
-  Status ClearAllHardwareWatchpoints() override;
-
-  Status GetWatchpointHitIndex(uint32_t &wp_index,
-                               lldb::addr_t trap_addr) override;
-
-  lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) 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 };
+  NativeRegisterContextDBReg_arm64()
+      : NativeRegisterContextDBReg(/*enable_bit=*/0x1U) {}
 
-protected:
-  /// Debug register info for hardware breakpoints and watchpoints management.
-  /// Watchpoints: For a user requested size 4 at addr 0x1004, where BAS
-  /// watchpoints are at doubleword (8-byte) alignment.
-  ///   \a real_addr is 0x1004
-  ///   \a address is 0x1000
-  ///   size is 8
-  ///   If a one-byte write to 0x1006 is the most recent watchpoint trap,
-  ///   \a hit_addr is 0x1006
-  struct DREG {
-    lldb::addr_t address;  // Breakpoint/watchpoint address value.
-    lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception
-                           // occurred.
-    lldb::addr_t real_addr; // Address value that should cause target to stop.
-    uint32_t control;       // Breakpoint/watchpoint control value.
-  };
+private:
+  uint32_t GetWatchpointSize(uint32_t wp_index) override;
 
-  std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints
-  std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints
+  std::optional<WatchpointDetails>
+  AdjustWatchpoint(const WatchpointDetails &details) override;
 
-  uint32_t m_max_hbp_supported;
-  uint32_t m_max_hwp_supported;
+  uint32_t MakeBreakControlValue(size_t size) override;
 
-  virtual llvm::Error ReadHardwareDebugInfo() = 0;
-  virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0;
-  virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) {
-    return hit_addr;
-  }
+  uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
 };
 
 } // namespace lldb_private


        


More information about the lldb-commits mailing list