mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 10:26:06 +00:00
[lldb][Windows] WoA HW Watchpoint support in LLDB (#108072)
This PR adds support for hardware watchpoints in LLDB for AArch64 Windows targets. Windows does not provide an API to query the number of available hardware watchpoints supported by underlying hardware platform. Therefore, current implementation supports only a single hardware watchpoint, which has been verified on Windows 11 using Microsoft SQ2 and Snapdragon Elite X hardware. LLDB test suite ninja check-lldb still fails watchpoint-related tests. However, tests that do not require more than a single watchpoint pass successfully when run individually.
This commit is contained in:
parent
97b066f4e9
commit
2bffa5bf7a
@ -292,7 +292,8 @@ NativeProcessWindows::GetAuxvData() const {
|
||||
|
||||
llvm::Expected<llvm::ArrayRef<uint8_t>>
|
||||
NativeProcessWindows::GetSoftwareBreakpointTrapOpcode(size_t size_hint) {
|
||||
static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x3e, 0xd4}; // brk #0xf000
|
||||
static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x3e,
|
||||
0xd4}; // brk #0xf000
|
||||
static const uint8_t g_thumb_opcode[] = {0xfe, 0xde}; // udf #0xfe
|
||||
|
||||
switch (GetArchitecture().GetMachine()) {
|
||||
@ -309,9 +310,9 @@ NativeProcessWindows::GetSoftwareBreakpointTrapOpcode(size_t size_hint) {
|
||||
}
|
||||
|
||||
size_t NativeProcessWindows::GetSoftwareBreakpointPCOffset() {
|
||||
// Windows always reports an incremented PC after a breakpoint is hit,
|
||||
// even on ARM.
|
||||
return cantFail(GetSoftwareBreakpointTrapOpcode(0)).size();
|
||||
// Windows always reports an incremented PC after a breakpoint is hit,
|
||||
// even on ARM.
|
||||
return cantFail(GetSoftwareBreakpointTrapOpcode(0)).size();
|
||||
}
|
||||
|
||||
bool NativeProcessWindows::FindSoftwareBreakpoint(lldb::addr_t addr) {
|
||||
@ -463,6 +464,7 @@ NativeProcessWindows::OnDebugException(bool first_chance,
|
||||
switch (record.GetExceptionCode()) {
|
||||
case DWORD(STATUS_SINGLE_STEP):
|
||||
case STATUS_WX86_SINGLE_STEP: {
|
||||
#ifndef __aarch64__
|
||||
uint32_t wp_id = LLDB_INVALID_INDEX32;
|
||||
if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) {
|
||||
NativeRegisterContextWindows ®_ctx = thread->GetRegisterContext();
|
||||
@ -483,6 +485,7 @@ NativeProcessWindows::OnDebugException(bool first_chance,
|
||||
}
|
||||
}
|
||||
if (wp_id == LLDB_INVALID_INDEX32)
|
||||
#endif
|
||||
StopThread(record.GetThreadID(), StopReason::eStopReasonTrace);
|
||||
|
||||
SetState(eStateStopped, true);
|
||||
@ -492,23 +495,50 @@ NativeProcessWindows::OnDebugException(bool first_chance,
|
||||
}
|
||||
case DWORD(STATUS_BREAKPOINT):
|
||||
case STATUS_WX86_BREAKPOINT:
|
||||
if (FindSoftwareBreakpoint(record.GetExceptionAddress())) {
|
||||
LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
|
||||
record.GetExceptionAddress());
|
||||
|
||||
StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint);
|
||||
if (NativeThreadWindows *stop_thread =
|
||||
GetThreadByID(record.GetThreadID())) {
|
||||
auto ®_ctx = stop_thread->GetRegisterContext();
|
||||
const auto exception_addr = record.GetExceptionAddress();
|
||||
const auto thread_id = record.GetThreadID();
|
||||
|
||||
if (NativeThreadWindows *stop_thread =
|
||||
GetThreadByID(record.GetThreadID())) {
|
||||
auto ®ister_context = stop_thread->GetRegisterContext();
|
||||
uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset();
|
||||
if (FindSoftwareBreakpoint(exception_addr)) {
|
||||
LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
|
||||
exception_addr);
|
||||
// The current PC is AFTER the BP opcode, on all architectures.
|
||||
uint64_t pc = register_context.GetPC() - breakpoint_size;
|
||||
register_context.SetPC(pc);
|
||||
}
|
||||
reg_ctx.SetPC(reg_ctx.GetPC() - GetSoftwareBreakpointPCOffset());
|
||||
StopThread(thread_id, StopReason::eStopReasonBreakpoint);
|
||||
SetState(eStateStopped, true);
|
||||
return ExceptionResult::MaskException;
|
||||
} else {
|
||||
// This block of code will only be entered in case of a hardware
|
||||
// watchpoint or breakpoint hit on AArch64. However, we only handle
|
||||
// hardware watchpoints below as breakpoints are not yet supported.
|
||||
const std::vector<ULONG_PTR> &args = record.GetExceptionArguments();
|
||||
// Check that the ExceptionInformation array of EXCEPTION_RECORD
|
||||
// contains at least two elements: the first is a read-write flag
|
||||
// indicating the type of data access operation (read or write) while
|
||||
// the second contains the virtual address of the accessed data.
|
||||
if (args.size() >= 2) {
|
||||
uint32_t hw_id = LLDB_INVALID_INDEX32;
|
||||
Status error = reg_ctx.GetWatchpointHitIndex(hw_id, args[1]);
|
||||
if (error.Fail())
|
||||
LLDB_LOG(log,
|
||||
"received error while checking for watchpoint hits, pid = "
|
||||
"{0}, error = {1}",
|
||||
thread_id, error);
|
||||
|
||||
SetState(eStateStopped, true);
|
||||
return ExceptionResult::MaskException;
|
||||
if (hw_id != LLDB_INVALID_INDEX32) {
|
||||
std::string desc =
|
||||
formatv("{0} {1} {2}", reg_ctx.GetWatchpointAddress(hw_id),
|
||||
hw_id, exception_addr)
|
||||
.str();
|
||||
StopThread(thread_id, StopReason::eStopReasonWatchpoint, desc);
|
||||
SetState(eStateStopped, true);
|
||||
return ExceptionResult::MaskException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!initial_stop) {
|
||||
|
@ -18,10 +18,6 @@
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
NativeRegisterContextWindows::NativeRegisterContextWindows(
|
||||
NativeThreadProtocol &thread, RegisterInfoInterface *reg_info_interface_p)
|
||||
: NativeRegisterContextRegisterInfo(thread, reg_info_interface_p) {}
|
||||
|
||||
lldb::thread_t NativeRegisterContextWindows::GetThreadHandle() const {
|
||||
auto wthread = static_cast<NativeThreadWindows *>(&m_thread);
|
||||
return wthread->GetHostThread().GetNativeThread().GetSystemHandle();
|
||||
|
@ -17,12 +17,9 @@ namespace lldb_private {
|
||||
|
||||
class NativeThreadWindows;
|
||||
|
||||
class NativeRegisterContextWindows : public NativeRegisterContextRegisterInfo {
|
||||
class NativeRegisterContextWindows
|
||||
: public virtual NativeRegisterContextRegisterInfo {
|
||||
public:
|
||||
NativeRegisterContextWindows(
|
||||
NativeThreadProtocol &native_thread,
|
||||
RegisterInfoInterface *reg_info_interface_p);
|
||||
|
||||
static std::unique_ptr<NativeRegisterContextWindows>
|
||||
CreateHostNativeRegisterContextWindows(const ArchSpec &target_arch,
|
||||
NativeThreadProtocol &native_thread);
|
||||
|
@ -88,8 +88,8 @@ static Status SetWoW64ThreadContextHelper(lldb::thread_t thread_handle,
|
||||
|
||||
NativeRegisterContextWindows_WoW64::NativeRegisterContextWindows_WoW64(
|
||||
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
|
||||
: NativeRegisterContextWindows(native_thread,
|
||||
CreateRegisterInfoInterface(target_arch)) {}
|
||||
: NativeRegisterContextRegisterInfo(
|
||||
native_thread, CreateRegisterInfoInterface(target_arch)) {}
|
||||
|
||||
bool NativeRegisterContextWindows_WoW64::IsGPR(uint32_t reg_index) const {
|
||||
return (reg_index >= k_first_gpr_i386 && reg_index < k_first_alias_i386);
|
||||
|
@ -128,8 +128,8 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
|
||||
|
||||
NativeRegisterContextWindows_arm::NativeRegisterContextWindows_arm(
|
||||
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
|
||||
: NativeRegisterContextWindows(native_thread,
|
||||
CreateRegisterInfoInterface(target_arch)) {}
|
||||
: NativeRegisterContextRegisterInfo(
|
||||
native_thread, CreateRegisterInfoInterface(target_arch)) {}
|
||||
|
||||
bool NativeRegisterContextWindows_arm::IsGPR(uint32_t reg_index) const {
|
||||
return (reg_index >= k_first_gpr_arm && reg_index <= k_last_gpr_arm);
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "NativeRegisterContextWindows_arm64.h"
|
||||
#include "NativeThreadWindows.h"
|
||||
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
|
||||
#include "ProcessWindowsLog.h"
|
||||
#include "lldb/Host/HostInfo.h"
|
||||
#include "lldb/Host/HostThread.h"
|
||||
@ -143,8 +142,13 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
|
||||
|
||||
NativeRegisterContextWindows_arm64::NativeRegisterContextWindows_arm64(
|
||||
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
|
||||
: NativeRegisterContextWindows(native_thread,
|
||||
CreateRegisterInfoInterface(target_arch)) {}
|
||||
: NativeRegisterContextRegisterInfo(
|
||||
native_thread, CreateRegisterInfoInterface(target_arch)) {
|
||||
// Currently, there is no API to query the maximum supported hardware
|
||||
// breakpoints and watchpoints on Windows. The values set below are based
|
||||
// on tests conducted on Windows 11 with Snapdragon Elite X hardware.
|
||||
m_max_hwp_supported = 1;
|
||||
}
|
||||
|
||||
bool NativeRegisterContextWindows_arm64::IsGPR(uint32_t reg_index) const {
|
||||
return (reg_index >= k_first_gpr_arm64 && reg_index <= k_last_gpr_arm64);
|
||||
@ -709,48 +713,37 @@ Status NativeRegisterContextWindows_arm64::WriteAllRegisterValues(
|
||||
return SetThreadContextHelper(GetThreadHandle(), &tls_context);
|
||||
}
|
||||
|
||||
Status NativeRegisterContextWindows_arm64::IsWatchpointHit(uint32_t wp_index,
|
||||
bool &is_hit) {
|
||||
return Status::FromErrorString("unimplemented");
|
||||
llvm::Error NativeRegisterContextWindows_arm64::ReadHardwareDebugInfo() {
|
||||
::CONTEXT tls_context;
|
||||
Status error = GetThreadContextHelper(GetThreadHandle(), &tls_context,
|
||||
CONTEXT_DEBUG_REGISTERS);
|
||||
if (error.Fail())
|
||||
return error.ToError();
|
||||
|
||||
for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
|
||||
m_hwp_regs[i].address = tls_context.Wvr[i];
|
||||
m_hwp_regs[i].control = tls_context.Wcr[i];
|
||||
}
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
Status NativeRegisterContextWindows_arm64::GetWatchpointHitIndex(
|
||||
uint32_t &wp_index, lldb::addr_t trap_addr) {
|
||||
return Status::FromErrorString("unimplemented");
|
||||
}
|
||||
llvm::Error
|
||||
NativeRegisterContextWindows_arm64::WriteHardwareDebugRegs(DREGType hwbType) {
|
||||
::CONTEXT tls_context;
|
||||
Status error = GetThreadContextHelper(GetThreadHandle(), &tls_context,
|
||||
CONTEXT_DEBUG_REGISTERS);
|
||||
if (error.Fail())
|
||||
return error.ToError();
|
||||
|
||||
Status NativeRegisterContextWindows_arm64::IsWatchpointVacant(uint32_t wp_index,
|
||||
bool &is_vacant) {
|
||||
return Status::FromErrorString("unimplemented");
|
||||
}
|
||||
if (hwbType == eDREGTypeWATCH) {
|
||||
for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
|
||||
tls_context.Wvr[i] = m_hwp_regs[i].address;
|
||||
tls_context.Wcr[i] = m_hwp_regs[i].control;
|
||||
}
|
||||
}
|
||||
|
||||
Status NativeRegisterContextWindows_arm64::SetHardwareWatchpointWithIndex(
|
||||
lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
|
||||
return Status::FromErrorString("unimplemented");
|
||||
}
|
||||
|
||||
bool NativeRegisterContextWindows_arm64::ClearHardwareWatchpoint(
|
||||
uint32_t wp_index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Status NativeRegisterContextWindows_arm64::ClearAllHardwareWatchpoints() {
|
||||
return Status::FromErrorString("unimplemented");
|
||||
}
|
||||
|
||||
uint32_t NativeRegisterContextWindows_arm64::SetHardwareWatchpoint(
|
||||
lldb::addr_t addr, size_t size, uint32_t watch_flags) {
|
||||
return LLDB_INVALID_INDEX32;
|
||||
}
|
||||
|
||||
lldb::addr_t
|
||||
NativeRegisterContextWindows_arm64::GetWatchpointAddress(uint32_t wp_index) {
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
uint32_t NativeRegisterContextWindows_arm64::NumSupportedHardwareWatchpoints() {
|
||||
// Not implemented
|
||||
return 0;
|
||||
return SetThreadContextHelper(GetThreadHandle(), &tls_context).ToError();
|
||||
}
|
||||
|
||||
#endif // defined(__aarch64__) || defined(_M_ARM64)
|
||||
|
@ -10,6 +10,8 @@
|
||||
#ifndef liblldb_NativeRegisterContextWindows_arm64_h_
|
||||
#define liblldb_NativeRegisterContextWindows_arm64_h_
|
||||
|
||||
#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h"
|
||||
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
|
||||
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
|
||||
|
||||
#include "NativeRegisterContextWindows.h"
|
||||
@ -18,7 +20,9 @@ namespace lldb_private {
|
||||
|
||||
class NativeThreadWindows;
|
||||
|
||||
class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
|
||||
class NativeRegisterContextWindows_arm64
|
||||
: public NativeRegisterContextWindows,
|
||||
public NativeRegisterContextDBReg_arm64 {
|
||||
public:
|
||||
NativeRegisterContextWindows_arm64(const ArchSpec &target_arch,
|
||||
NativeThreadProtocol &native_thread);
|
||||
@ -37,28 +41,6 @@ public:
|
||||
|
||||
Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
|
||||
|
||||
Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
|
||||
|
||||
Status GetWatchpointHitIndex(uint32_t &wp_index,
|
||||
lldb::addr_t trap_addr) override;
|
||||
|
||||
Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
|
||||
|
||||
bool ClearHardwareWatchpoint(uint32_t wp_index) override;
|
||||
|
||||
Status ClearAllHardwareWatchpoints() override;
|
||||
|
||||
Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
|
||||
uint32_t watch_flags,
|
||||
uint32_t wp_index);
|
||||
|
||||
uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
|
||||
uint32_t watch_flags) override;
|
||||
|
||||
lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
|
||||
|
||||
uint32_t NumSupportedHardwareWatchpoints() override;
|
||||
|
||||
protected:
|
||||
Status GPRRead(const uint32_t reg, RegisterValue ®_value);
|
||||
|
||||
@ -72,6 +54,10 @@ private:
|
||||
bool IsGPR(uint32_t reg_index) const;
|
||||
|
||||
bool IsFPR(uint32_t reg_index) const;
|
||||
|
||||
llvm::Error ReadHardwareDebugInfo() override;
|
||||
|
||||
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
@ -92,8 +92,8 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
|
||||
|
||||
NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386(
|
||||
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
|
||||
: NativeRegisterContextWindows(native_thread,
|
||||
CreateRegisterInfoInterface(target_arch)) {}
|
||||
: NativeRegisterContextRegisterInfo(
|
||||
native_thread, CreateRegisterInfoInterface(target_arch)) {}
|
||||
|
||||
bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const {
|
||||
return (reg_index < k_first_alias_i386);
|
||||
|
@ -110,8 +110,8 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
|
||||
|
||||
NativeRegisterContextWindows_x86_64::NativeRegisterContextWindows_x86_64(
|
||||
const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
|
||||
: NativeRegisterContextWindows(native_thread,
|
||||
CreateRegisterInfoInterface(target_arch)) {}
|
||||
: NativeRegisterContextRegisterInfo(
|
||||
native_thread, CreateRegisterInfoInterface(target_arch)) {}
|
||||
|
||||
bool NativeRegisterContextWindows_x86_64::IsGPR(uint32_t reg_index) const {
|
||||
return (reg_index >= k_first_gpr_x86_64 && reg_index < k_first_alias_x86_64);
|
||||
|
@ -135,6 +135,10 @@ Changes to LLDB
|
||||
|
||||
* When building LLDB with Python support, the minimum version of Python is now
|
||||
3.8.
|
||||
* LLDB now supports hardware watchpoints for AArch64 Windows targets. Windows
|
||||
does not provide API to query the number of supported hardware watchpoints.
|
||||
Therefore current implementation allows only 1 watchpoint, as tested with
|
||||
Windows 11 on the Microsoft SQ2 and Snapdragon Elite X platforms.
|
||||
|
||||
Changes to BOLT
|
||||
---------------------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user