Skip to content

[lldb][Process] Introduce LoongArch64 hw break/watchpoint support #118770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading