[Lldb-commits] [lldb] [LLDB][Process/Utility] Introduce NativeRegisterContextDBReg class (PR #118043)

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Fri Dec 6 02:34:20 PST 2024


================
@@ -0,0 +1,400 @@
+//===-- 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.
+  Status error = ReadHardwareDebugInfo();
+
+  if (error.Fail()) {
+    LLDB_LOG(log, "failed to read debug registers");
+    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.
+  Status error = ReadHardwareDebugInfo();
+  if (error.Fail()) {
+    LLDB_LOG(log,
+             "unable to set breakpoint: failed to read debug registers: {0}");
+    return LLDB_INVALID_INDEX32;
+  }
+
+  uint32_t control_value = 0, bp_index = 0;
+
+  // Check hardware breakpoint size and address.
+  if (!ValidateBreakpoint(size, addr))
+    return LLDB_INVALID_INDEX32;
+
+  // Setup control value
+  control_value = MakeControlValue(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.Fail()) {
+    m_hbp_regs[bp_index].address = 0;
+    m_hbp_regs[bp_index].control &= ~m_hw_dbg_enable_bit;
+
+    LLDB_LOG(log,
+             "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.
+  Status error = ReadHardwareDebugInfo();
+  if (error.Fail()) {
+    LLDB_LOG(log,
+             "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.Fail()) {
+    m_hbp_regs[hw_idx].control = tempControl;
+    m_hbp_regs[hw_idx].address = tempAddr;
+
+    LLDB_LOG(log,
+             "unable to clear breakpoint: failed to write debug registers");
+    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.
+  Status error = ReadHardwareDebugInfo();
+  if (error.Fail())
+    return 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.Fail()) {
+      m_hbp_regs[i].control = tempControl;
+      m_hbp_regs[i].address = tempAddr;
+
+      return 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);
+  Status error = ReadHardwareDebugInfo();
+  if (error.Fail()) {
+    LLDB_LOG(log, "failed to read debug registers");
+    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.
+  Status error = ReadHardwareDebugInfo();
+  if (error.Fail()) {
+    LLDB_LOG(log,
+             "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;
+
+  // Check hardware watchpoint size and address.
+  if (!ValidateWatchpoint(size, addr))
+    return LLDB_INVALID_INDEX32;
+
+  // lldb::eWatchpointKindRead | lldb::eWatchpointKindWrite = 3
+  if (watch_flags > 3)
----------------
DavidSpickett wrote:

If you're going to do this, just write this in the if or use the switch case that the original code did and the `default` will handle the unknown cases.

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


More information about the lldb-commits mailing list