Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h @@ -15,7 +15,7 @@ namespace lldb_private { namespace process_linux { -class NativeRegisterContextLinux : public NativeRegisterContextRegisterInfo { +class NativeRegisterContextLinux : virtual public NativeRegisterContextRegisterInfo { public: NativeRegisterContextLinux(NativeThreadProtocol &native_thread, RegisterInfoInterface *reg_info_interface_p); Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -13,6 +13,7 @@ #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" #include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/WatchpointMixin_x86.h" #include "Plugins/Process/Utility/lldb-x86-register-enums.h" #include @@ -21,7 +22,7 @@ class NativeProcessLinux; -class NativeRegisterContextLinux_x86_64 : public NativeRegisterContextLinux { +class NativeRegisterContextLinux_x86_64 : public NativeRegisterContextLinux, public WatchpointMixin_x86 { public: NativeRegisterContextLinux_x86_64(const ArchSpec &target_arch, NativeThreadProtocol &native_thread); @@ -42,28 +43,6 @@ 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; - llvm::Optional GetSyscallData() override; llvm::Optional GetMmapData() override; @@ -128,7 +107,7 @@ bool IsRegisterSetAvailable(uint32_t set_index) const; - bool IsGPR(uint32_t reg_index) const; + bool IsGPROrDR(uint32_t reg_index) const; bool IsFPR(uint32_t reg_index) const; Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -293,7 +293,8 @@ NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread) - : NativeRegisterContextLinux(native_thread, + : NativeRegisterContextRegisterInfo(native_thread, CreateRegisterInfoInterface(target_arch)), + NativeRegisterContextLinux(native_thread, CreateRegisterInfoInterface(target_arch)), m_xstate_type(XStateType::Invalid), m_ymm_set(), m_mpx_set(), m_reg_info(), m_gpr_x86_64() { @@ -578,7 +579,7 @@ UpdateXSTATEforWrite(reg_index); - if (IsGPR(reg_index)) + if (IsGPROrDR(reg_index)) return WriteRegisterRaw(reg_index, reg_value); if (IsFPR(reg_index) || IsAVX(reg_index) || IsMPX(reg_index)) { @@ -867,9 +868,9 @@ return false; } -bool NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const { +bool NativeRegisterContextLinux_x86_64::IsGPROrDR(uint32_t reg_index) const { // GPRs come first. - return reg_index <= m_reg_info.last_gpr; + return reg_index <= m_reg_info.last_gpr || reg_index >= m_reg_info.first_dr; } bool NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const { @@ -1009,210 +1010,6 @@ return true; } -Status NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint32_t wp_index, - bool &is_hit) { - if (wp_index >= NumSupportedHardwareWatchpoints()) - return Status("Watchpoint index out of range"); - - RegisterValue reg_value; - Status error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); - if (error.Fail()) { - is_hit = false; - return error; - } - - uint64_t status_bits = reg_value.GetAsUInt64(); - - is_hit = status_bits & (1 << wp_index); - - return error; -} - -Status NativeRegisterContextLinux_x86_64::GetWatchpointHitIndex( - uint32_t &wp_index, lldb::addr_t trap_addr) { - uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); - for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { - bool is_hit; - Status error = IsWatchpointHit(wp_index, is_hit); - if (error.Fail()) { - wp_index = LLDB_INVALID_INDEX32; - return error; - } else if (is_hit) { - return error; - } - } - wp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -Status NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index, - bool &is_vacant) { - if (wp_index >= NumSupportedHardwareWatchpoints()) - return Status("Watchpoint index out of range"); - - RegisterValue reg_value; - Status error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); - if (error.Fail()) { - is_vacant = false; - return error; - } - - uint64_t control_bits = reg_value.GetAsUInt64(); - - is_vacant = !(control_bits & (1 << (2 * wp_index))); - - return error; -} - -Status NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex( - lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { - - if (wp_index >= NumSupportedHardwareWatchpoints()) - return Status("Watchpoint index out of range"); - - // Read only watchpoints aren't supported on x86_64. Fall back to read/write - // waitchpoints instead. - // TODO: Add logic to detect when a write happens and ignore that watchpoint - // hit. - if (watch_flags == 0x2) - watch_flags = 0x3; - - if (watch_flags != 0x1 && watch_flags != 0x3) - return Status("Invalid read/write bits for watchpoint"); - - if (size != 1 && size != 2 && size != 4 && size != 8) - return Status("Invalid size for watchpoint"); - - bool is_vacant; - Status error = IsWatchpointVacant(wp_index, is_vacant); - if (error.Fail()) - return error; - if (!is_vacant) - return Status("Watchpoint index not vacant"); - - RegisterValue reg_value; - error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); - if (error.Fail()) - return error; - - // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 - uint64_t enable_bit = 1 << (2 * wp_index); - - // set bits 16-17, 20-21, 24-25, or 28-29 - // with 0b01 for write, and 0b11 for read/write - uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); - - // set bits 18-19, 22-23, 26-27, or 30-31 - // with 0b00, 0b01, 0b10, or 0b11 - // for 1, 2, 8 (if supported), or 4 bytes, respectively - uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); - - uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); - - uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; - - control_bits |= enable_bit | rw_bits | size_bits; - - error = WriteRegisterRaw(m_reg_info.first_dr + wp_index, RegisterValue(addr)); - if (error.Fail()) - return error; - - error = - WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)); - if (error.Fail()) - return error; - - error.Clear(); - return error; -} - -bool NativeRegisterContextLinux_x86_64::ClearHardwareWatchpoint( - uint32_t wp_index) { - if (wp_index >= NumSupportedHardwareWatchpoints()) - return false; - - RegisterValue reg_value; - - // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of - // the debug status register (DR6) - Status error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); - if (error.Fail()) - return false; - uint64_t bit_mask = 1 << wp_index; - uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; - error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits)); - if (error.Fail()) - return false; - - // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19}, - // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register - // (DR7) - error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); - if (error.Fail()) - return false; - bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); - uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; - return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)) - .Success(); -} - -Status NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints() { - RegisterValue reg_value; - - // clear bits {0-4} of the debug status register (DR6) - Status error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value); - if (error.Fail()) - return error; - uint64_t bit_mask = 0xF; - uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; - error = WriteRegisterRaw(m_reg_info.first_dr + 6, RegisterValue(status_bits)); - if (error.Fail()) - return error; - - // clear bits {0-7,16-31} of the debug control register (DR7) - error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value); - if (error.Fail()) - return error; - bit_mask = 0xFF | (0xFFFF << 16); - uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; - return WriteRegisterRaw(m_reg_info.first_dr + 7, RegisterValue(control_bits)); -} - -uint32_t NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint( - lldb::addr_t addr, size_t size, uint32_t watch_flags) { - Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); - const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); - for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) { - bool is_vacant; - Status error = IsWatchpointVacant(wp_index, is_vacant); - if (is_vacant) { - error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); - if (error.Success()) - return wp_index; - } - if (error.Fail() && log) { - LLDB_LOGF(log, "NativeRegisterContextLinux_x86_64::%s Error: %s", - __FUNCTION__, error.AsCString()); - } - } - return LLDB_INVALID_INDEX32; -} - -lldb::addr_t -NativeRegisterContextLinux_x86_64::GetWatchpointAddress(uint32_t wp_index) { - if (wp_index >= NumSupportedHardwareWatchpoints()) - return LLDB_INVALID_ADDRESS; - RegisterValue reg_value; - if (ReadRegisterRaw(m_reg_info.first_dr + wp_index, reg_value).Fail()) - return LLDB_INVALID_ADDRESS; - return reg_value.GetAsUInt64(); -} - -uint32_t NativeRegisterContextLinux_x86_64::NumSupportedHardwareWatchpoints() { - // Available debug address registers: dr0, dr1, dr2, dr3 - return 4; -} - uint32_t NativeRegisterContextLinux_x86_64::GetPtraceOffset(uint32_t reg_index) { // If register is MPX, remove extra factor from gdb offset Index: lldb/source/Plugins/Process/Utility/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -49,6 +49,7 @@ RegisterInfoPOSIX_ppc64le.cpp StopInfoMachException.cpp ThreadMemory.cpp + WatchpointMixin_x86.cpp LINK_LIBS lldbBreakpoint Index: lldb/source/Plugins/Process/Utility/WatchpointMixin_x86.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Utility/WatchpointMixin_x86.h @@ -0,0 +1,53 @@ +//===-- WatchpointMixin_x86.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#ifndef lldb_WatchpointMixin_x86_h +#define lldb_WatchpointMixin_x86_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +namespace lldb_private { + +class WatchpointMixin_x86 : virtual public NativeRegisterContextRegisterInfo { +public: + 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; + + // TODO: do we need to call it explicitly like on netbsd? + Status ClearWatchpointHit(uint32_t wp_index); + + 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; + +private: + int GetDR(int num) const; +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_WatchpointMixin_x86_h + +#endif // defined(__i386__) || defined(__x86_64__) Index: lldb/source/Plugins/Process/Utility/WatchpointMixin_x86.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Utility/WatchpointMixin_x86.cpp @@ -0,0 +1,261 @@ +//===-- WatchpointMixin_x86.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#include "WatchpointMixin_x86.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +using namespace lldb_private; + +int WatchpointMixin_x86::GetDR(int num) const { + assert(num >= 0 && num <= 7); + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return lldb_dr0_i386 + num; + case llvm::Triple::x86_64: + return lldb_dr0_x86_64 + num; + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +Status WatchpointMixin_x86::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(GetDR(6)); + Status error = ReadRegister(reg_info, reg_value); + if (error.Fail()) { + is_hit = false; + return error; + } + + uint64_t status_bits = reg_value.GetAsUInt64(); + + is_hit = status_bits & (1 << wp_index); + + return error; +} + +Status WatchpointMixin_x86::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { + bool is_hit; + Status error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + return error; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +Status WatchpointMixin_x86::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(GetDR(7)); + Status error = ReadRegister(reg_info, reg_value); + if (error.Fail()) { + is_vacant = false; + return error; + } + + uint64_t control_bits = reg_value.GetAsUInt64(); + + is_vacant = !(control_bits & (1 << (2 * wp_index + 1))); + + return error; +} + +Status WatchpointMixin_x86::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + // Read only watchpoints aren't supported on x86_64. Fall back to read/write + // waitchpoints instead. + // TODO: Add logic to detect when a write happens and ignore that watchpoint + // hit. + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Status("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Status("Invalid size for watchpoint"); + + bool is_vacant; + Status error = IsWatchpointVacant(wp_index, is_vacant); + if (error.Fail()) + return error; + if (!is_vacant) + return Status("Watchpoint index not vacant"); + + const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7)); + RegisterValue dr7_value; + error = ReadRegister(reg_info_dr7, dr7_value); + if (error.Fail()) + return error; + + // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1 << (2 * wp_index + 1); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = dr7_value.GetAsUInt64() & ~bit_mask; + + control_bits |= enable_bit | rw_bits | size_bits; + + const RegisterInfo *const reg_info_drN = + GetRegisterInfoAtIndex(GetDR(wp_index)); + RegisterValue drN_value; + error = ReadRegister(reg_info_drN, drN_value); + if (error.Fail()) + return error; + + // clear dr6 if address or bits changed (i.e. we're not reenabling the same + // watchpoint) + if (drN_value.GetAsUInt64() != addr || + (dr7_value.GetAsUInt64() & bit_mask) != (rw_bits | size_bits)) { + ClearWatchpointHit(wp_index); + + error = WriteRegister(reg_info_drN, RegisterValue(addr)); + if (error.Fail()) + return error; + } + + error = WriteRegister(reg_info_dr7, RegisterValue(control_bits)); + if (error.Fail()) + return error; + + error.Clear(); + return error; +} + +bool WatchpointMixin_x86::ClearHardwareWatchpoint( + uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0-1, 2-3, 4-5 + // or 6-7 of the debug control register (DR7) + const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7)); + RegisterValue reg_value; + Status error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return false; + uint64_t bit_mask = 0x3 << (2 * wp_index); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + + return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success(); +} + +Status WatchpointMixin_x86::ClearWatchpointHit(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + // for watchpoints 0, 1, 2, or 3, respectively, check bits 0, 1, 2, or 3 of + // the debug status register (DR6) + const RegisterInfo *const reg_info_dr6 = GetRegisterInfoAtIndex(GetDR(6)); + RegisterValue reg_value; + Status error = ReadRegister(reg_info_dr6, reg_value); + if (error.Fail()) + return error; + + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(reg_info_dr6, RegisterValue(status_bits)); +} + +Status WatchpointMixin_x86::ClearAllHardwareWatchpoints() { + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + const RegisterInfo *const reg_info_dr6 = GetRegisterInfoAtIndex(GetDR(6)); + Status error = ReadRegister(reg_info_dr6, reg_value); + if (error.Fail()) + return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(reg_info_dr6, RegisterValue(status_bits)); + if (error.Fail()) + return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7)); + error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(reg_info_dr7, RegisterValue(control_bits)); +} + +uint32_t WatchpointMixin_x86::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) { + bool is_vacant; + Status error = IsWatchpointVacant(wp_index, is_vacant); + if (is_vacant) { + error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); + if (error.Success()) + return wp_index; + } + if (error.Fail() && log) { + LLDB_LOGF(log, "WatchpointMixin_x86::%s Error: %s", + __FUNCTION__, error.AsCString()); + } + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +WatchpointMixin_x86::GetWatchpointAddress(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue reg_value; + const RegisterInfo *const reg_info_drN = + GetRegisterInfoAtIndex(GetDR(wp_index)); + if (ReadRegister(reg_info_drN, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + return reg_value.GetAsUInt64(); +} + +uint32_t WatchpointMixin_x86::NumSupportedHardwareWatchpoints() { + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + +#endif // defined(__i386__) || defined(__x86_64__)