[llvm-branch-commits] [lldb][Process] Introduce LoongArch64 hw break/watchpoint support (PR #118770)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Dec 5 01:13:11 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: wanglei (wangleiat)
<details>
<summary>Changes</summary>
This patch adds support for setting/clearing hardware watchpoints and
breakpoints on LoongArch 64-bit hardware.
Refer to the following document for the hw break/watchpoint:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
Fix Failed Tests:
lldb-shell :: Subprocess/clone-follow-child-wp.test
lldb-shell :: Subprocess/clone-follow-parent-wp.test
lldb-shell :: Subprocess/fork-follow-child-wp.test
lldb-shell :: Subprocess/fork-follow-parent-wp.test
lldb-shell :: Subprocess/vfork-follow-child-wp.test
lldb-shell :: Subprocess/vfork-follow-parent-wp.test
lldb-shell :: Watchpoint/ExpressionLanguage.test
Depends on: #<!-- -->118043
---
Full diff: https://github.com/llvm/llvm-project/pull/118770.diff
7 Files Affected:
- (modified) lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp (+82)
- (modified) lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h (+8-1)
- (modified) lldb/source/Plugins/Process/Utility/CMakeLists.txt (+1)
- (added) lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp (+170)
- (added) lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h (+29)
- (modified) lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp (+2-1)
- (modified) lldb/source/Target/Process.cpp (+2-1)
``````````diff
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp
index f4d1bb297049da..12bd9e2104fae7 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp
@@ -11,6 +11,7 @@
#include "NativeRegisterContextLinux_loongarch64.h"
#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/linux/Ptrace.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
@@ -62,6 +63,19 @@ NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64(
::memset(&m_fpr, 0, sizeof(m_fpr));
::memset(&m_gpr, 0, sizeof(m_gpr));
+ ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
+ ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
+
+ // CTRL_PLV3_ENABLE, used to enable breakpoint/watchpoint
+ m_hw_dbg_enable_bit = 0x10;
+
+ // Refer to:
+ // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
+ // 14 is just a maximum value, query hardware for actual watchpoint count.
+ m_max_hwp_supported = 14;
+ m_max_hbp_supported = 14;
+ m_refresh_hwdebug_info = true;
+
m_gpr_is_valid = false;
m_fpu_is_valid = false;
}
@@ -337,4 +351,72 @@ NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(
return expedited_reg_nums;
}
+Status NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {
+ if (!m_refresh_hwdebug_info)
+ return Status();
+
+ ::pid_t tid = m_thread.GetID();
+
+ int regset = NT_LOONGARCH_HW_WATCH;
+ struct iovec ioVec;
+ struct user_watch_state dreg_state;
+ Status error;
+
+ ioVec.iov_base = &dreg_state;
+ ioVec.iov_len = sizeof(dreg_state);
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set,
+ &ioVec, ioVec.iov_len);
+ if (error.Fail())
+ return error;
+
+ m_max_hwp_supported = dreg_state.dbg_info & 0x3f;
+
+ regset = NT_LOONGARCH_HW_BREAK;
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set,
+ &ioVec, ioVec.iov_len);
+ if (error.Fail())
+ return error;
+
+ m_max_hbp_supported = dreg_state.dbg_info & 0x3f;
+
+ m_refresh_hwdebug_info = false;
+
+ return error;
+}
+
+Status NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs(
+ DREGType hwbType) {
+ struct iovec ioVec;
+ struct user_watch_state dreg_state;
+ int regset;
+
+ memset(&dreg_state, 0, sizeof(dreg_state));
+ ioVec.iov_base = &dreg_state;
+
+ switch (hwbType) {
+ case eDREGTypeWATCH:
+ regset = NT_LOONGARCH_HW_WATCH;
+ ioVec.iov_len = sizeof(dreg_state.dbg_info) +
+ (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
+
+ for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
+ dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
+ dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
+ }
+ break;
+ case eDREGTypeBREAK:
+ regset = NT_LOONGARCH_HW_BREAK;
+ ioVec.iov_len = sizeof(dreg_state.dbg_info) +
+ (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
+
+ for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
+ dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;
+ dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;
+ }
+ break;
+ }
+
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
+ ®set, &ioVec, ioVec.iov_len);
+}
#endif // defined(__loongarch__) && __loongarch_grlen == 64
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h
index 0a6084ff4206db..b00f039734bd6a 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h
@@ -12,6 +12,7 @@
#define lldb_NativeRegisterContextLinux_loongarch64_h
#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h"
#include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h"
#include <asm/ptrace.h>
@@ -22,7 +23,8 @@ namespace process_linux {
class NativeProcessLinux;
class NativeRegisterContextLinux_loongarch64
- : public NativeRegisterContextLinux {
+ : public NativeRegisterContextLinux,
+ public NativeRegisterContextDBReg_loongarch {
public:
NativeRegisterContextLinux_loongarch64(
const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
@@ -71,6 +73,7 @@ class NativeRegisterContextLinux_loongarch64
private:
bool m_gpr_is_valid;
bool m_fpu_is_valid;
+ bool m_refresh_hwdebug_info;
RegisterInfoPOSIX_loongarch64::GPR m_gpr;
@@ -83,6 +86,10 @@ class NativeRegisterContextLinux_loongarch64
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
const RegisterInfoPOSIX_loongarch64 &GetRegisterInfo() const;
+
+ Status ReadHardwareDebugInfo() override;
+
+ Status WriteHardwareDebugRegs(DREGType hwbType) override;
};
} // namespace process_linux
diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
index 0526c95503175e..0e1a5069d4409e 100644
--- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
@@ -11,6 +11,7 @@ add_lldb_library(lldbPluginProcessUtility
NativeProcessSoftwareSingleStep.cpp
NativeRegisterContextDBReg.cpp
NativeRegisterContextDBReg_arm64.cpp
+ NativeRegisterContextDBReg_loongarch.cpp
NativeRegisterContextDBReg_x86.cpp
NativeRegisterContextRegisterInfo.cpp
NetBSDSignals.cpp
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp
new file mode 100644
index 00000000000000..6438b02d50d1e8
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp
@@ -0,0 +1,170 @@
+//===-- NativeRegisterContextDBReg_loongarch.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_loongarch.h"
+
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+
+using namespace lldb_private;
+
+uint32_t
+NativeRegisterContextDBReg_loongarch::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");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ uint32_t bp_index = 0;
+
+ // Check if size has a valid hardware breakpoint length.
+ if (size != 4)
+ return LLDB_INVALID_INDEX32; // Invalid size for a LoongArch 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.
+
+ // 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].address = addr;
+ m_hbp_regs[bp_index].control = m_hw_dbg_enable_bit;
+
+ // 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");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ return bp_index;
+}
+
+uint32_t
+NativeRegisterContextDBReg_loongarch::GetWatchpointSize(uint32_t wp_index) {
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOG(log, "wp_index: {0}", wp_index);
+
+ switch ((m_hwp_regs[wp_index].control >> 10) & 0x3) {
+ case 0x0:
+ return 8;
+ case 0x1:
+ return 4;
+ case 0x2:
+ return 2;
+ case 0x3:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+uint32_t NativeRegisterContextDBReg_loongarch::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");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ uint32_t control_value = 0, wp_index = 0;
+
+ // Check if we are setting watchpoint other than read/write/access Update
+ // watchpoint flag to match loongarch64 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;
+ }
+
+ // Check if size has a valid hardware watchpoint length.
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ return LLDB_INVALID_INDEX32;
+
+ // Encode appropriate control register bits for the specified size
+ // size encoded:
+ // case 1 : 0b11
+ // case 2 : 0b10
+ // case 4 : 0b01
+ // case 8 : 0b00
+ auto EncodeSizeBits = [](int size) {
+ return (3 - llvm::Log2_32(size)) << 10;
+ };
+
+ // Setup control value
+ control_value = m_hw_dbg_enable_bit | EncodeSizeBits(size);
+ control_value |= watch_flags << 8;
+
+ // 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
+ // Note: `NativeRegisterContextDBReg::GetWatchpointAddress` return `real_addr`
+ m_hwp_regs[wp_index].real_addr = 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.Fail()) {
+ m_hwp_regs[wp_index].address = 0;
+ m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit;
+
+ LLDB_LOG(log, "unable to set watchpoint: failed to write debug registers");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ return wp_index;
+}
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h
new file mode 100644
index 00000000000000..dbf23343eb1a10
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h
@@ -0,0 +1,29 @@
+//===-- NativeRegisterContextDBReg_loongarch.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_loongarch_h
+#define lldb_NativeRegisterContextDBReg_loongarch_h
+
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
+
+namespace lldb_private {
+
+class NativeRegisterContextDBReg_loongarch : public NativeRegisterContextDBReg {
+public:
+ uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags) override;
+
+private:
+ uint32_t GetWatchpointSize(uint32_t wp_index) override;
+};
+
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextDBReg_loongarch_h
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
index 324db3db7eb4c7..c2fe05cad566ef 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -231,7 +231,8 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo(
host_arch.GetMachine() == llvm::Triple::aarch64_32 ||
host_arch.GetMachine() == llvm::Triple::aarch64_be ||
host_arch.GetMachine() == llvm::Triple::arm ||
- host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS())
+ host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS() ||
+ host_arch.GetTriple().isLoongArch())
response.Printf("watchpoint_exceptions_received:before;");
else
response.Printf("watchpoint_exceptions_received:after;");
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index db33525978a16a..68485a40a3fcce 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2517,7 +2517,8 @@ bool Process::GetWatchpointReportedAfter() {
llvm::Triple triple = arch.GetTriple();
if (triple.isMIPS() || triple.isPPC64() || triple.isRISCV() ||
- triple.isAArch64() || triple.isArmMClass() || triple.isARM())
+ triple.isAArch64() || triple.isArmMClass() || triple.isARM() ||
+ triple.isLoongArch())
reported_after = false;
return reported_after;
``````````
</details>
https://github.com/llvm/llvm-project/pull/118770
More information about the llvm-branch-commits
mailing list