Skip to content

Commit

Permalink
[lldb][Process] Introduce LoongArch64 hw break/watchpoint support
Browse files Browse the repository at this point in the history
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: llvm#118043

Reviewed By: SixWeining

Pull Request: llvm#118770
  • Loading branch information
wangleiat authored Dec 13, 2024
1 parent 3de5e8b commit 6c4e70f
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -62,6 +63,16 @@ 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));

// 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;
}
Expand Down Expand Up @@ -337,4 +348,73 @@ NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(
return expedited_reg_nums;
}

llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {
if (!m_refresh_hwdebug_info)
return llvm::Error::success();

::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, &regset,
&ioVec, ioVec.iov_len);
if (error.Fail())
return error.ToError();

m_max_hwp_supported = dreg_state.dbg_info & 0x3f;

regset = NT_LOONGARCH_HW_BREAK;
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
&ioVec, ioVec.iov_len);
if (error.Fail())
return error.ToError();

m_max_hbp_supported = dreg_state.dbg_info & 0x3f;

m_refresh_hwdebug_info = false;

return llvm::Error::success();
}

llvm::Error 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(),
&regset, &ioVec, ioVec.iov_len)
.ToError();
}
#endif // defined(__loongarch__) && __loongarch_grlen == 64
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -83,6 +86,10 @@ class NativeRegisterContextLinux_loongarch64
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;

const RegisterInfoPOSIX_loongarch64 &GetRegisterInfo() const;

llvm::Error ReadHardwareDebugInfo() override;

llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
};

} // namespace process_linux
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Plugins/Process/Utility/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===-- 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::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;
}
}

std::optional<NativeRegisterContextDBReg::WatchpointDetails>
NativeRegisterContextDBReg_loongarch::AdjustWatchpoint(
const WatchpointDetails &details) {
// LoongArch only needs to check the size; it does not need to check the
// address.
size_t size = details.size;
if (size != 1 && size != 2 && size != 4 && size != 8)
return std::nullopt;

return details;
}

uint32_t
NativeRegisterContextDBReg_loongarch::MakeBreakControlValue(size_t size) {
// Return encoded hardware breakpoint control value.
return m_hw_dbg_enable_bit;
}

uint32_t NativeRegisterContextDBReg_loongarch::MakeWatchControlValue(
size_t size, uint32_t watch_flags) {
// Encoding hardware watchpoint control value.
// Size encoded:
// case 1 : 0b11
// case 2 : 0b10
// case 4 : 0b01
// case 8 : 0b00
size_t encoded_size = (3 - llvm::Log2_32(size)) << 10;

return m_hw_dbg_enable_bit | encoded_size | (watch_flags << 8);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===-- 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:
NativeRegisterContextDBReg_loongarch()
: NativeRegisterContextDBReg(/*enable_bit=*/0x10U) {}

private:
uint32_t GetWatchpointSize(uint32_t wp_index) override;

std::optional<WatchpointDetails>
AdjustWatchpoint(const WatchpointDetails &details) override;

uint32_t MakeBreakControlValue(size_t size) override;

uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
};

} // namespace lldb_private

#endif // #ifndef lldb_NativeRegisterContextDBReg_loongarch_h
Original file line number Diff line number Diff line change
Expand Up @@ -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;");
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 6c4e70f

Please sign in to comment.