Index: lldb/include/lldb/lldb-defines.h =================================================================== --- lldb/include/lldb/lldb-defines.h +++ lldb/include/lldb/lldb-defines.h @@ -59,6 +59,11 @@ #define LLDB_WATCH_TYPE_IS_VALID(type) \ ((type | LLDB_WATCH_TYPE_READ) || (type | LLDB_WATCH_TYPE_WRITE)) +// Watchpoint types in gdb-remote protocol +#define LLDB_GDB_WATCH_TYPE_WRITE 1 +#define LLDB_GDB_WATCH_TYPE_READ 2 +#define LLDB_GDB_WATCH_TYPE_READ_WRITE 3 + // Generic Register Numbers #define LLDB_REGNUM_GENERIC_PC 0 // Program Counter #define LLDB_REGNUM_GENERIC_SP 1 // Stack Pointer Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h @@ -15,11 +15,8 @@ namespace lldb_private { namespace process_linux { -class NativeRegisterContextLinux : public NativeRegisterContextRegisterInfo { +class NativeRegisterContextLinux : public virtual NativeRegisterContextRegisterInfo { public: - NativeRegisterContextLinux(NativeThreadProtocol &native_thread, - RegisterInfoInterface *reg_info_interface_p); - // This function is implemented in the NativeRegisterContextLinux_* subclasses // to create a new instance of the host specific NativeRegisterContextLinux. // The implementations can't collide as only one NativeRegisterContextLinux_* Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp @@ -19,11 +19,6 @@ using namespace lldb_private; using namespace lldb_private::process_linux; -NativeRegisterContextLinux::NativeRegisterContextLinux( - NativeThreadProtocol &native_thread, - RegisterInfoInterface *reg_info_interface_p) - : NativeRegisterContextRegisterInfo(native_thread, reg_info_interface_p) {} - lldb::ByteOrder NativeRegisterContextLinux::GetByteOrder() const { return m_thread.GetProcess().GetByteOrder(); } 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,8 +293,7 @@ NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread) - : NativeRegisterContextLinux(native_thread, - CreateRegisterInfoInterface(target_arch)), + : NativeRegisterContextRegisterInfo(native_thread, CreateRegisterInfoInterface(target_arch)), m_xstate_type(XStateType::Invalid), m_ymm_set(), m_mpx_set(), m_reg_info(), m_gpr_x86_64() { // Set up data about ranges of valid registers. @@ -578,7 +577,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 +866,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 +1008,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 : public virtual 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: + const RegisterInfo *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,269 @@ +//===-- 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; + +// Returns mask/value for status bit of wp_index in DR6 +static inline uint64_t GetStatusBit(uint32_t wp_index) { + // DR6: ...BBBB + // 3210 <- status bits for bp./wp. i; 1 if hit + return 1 << wp_index; +} + +// Returns mask/value for global enable bit of wp_index in DR7 +static inline uint64_t GetEnableBit(uint32_t wp_index) { + // DR7: ...GLGLGLGL + // 33221100 <- global/local enable for bp./wp.; 1 if enabled + // we use global bits because NetBSD kernel does not preserve local + // bits reliably; Linux seems fine with either + return 1 << (2 * wp_index + 1); +} + +// Returns mask for both enable bits of wp_index in DR7 +static inline uint64_t GetBothEnableBitMask(uint32_t wp_index) { + // DR7: ...GLGLGLGL + // 33221100 <- global/local enable for bp./wp.; 1 if enabled + return 3 << (2 * wp_index + 1); +} + +// Returns value for type bits of wp_index in DR7 +static inline uint64_t GetWatchTypeBits(uint32_t watch_flags, uint32_t wp_index) { + // DR7: + // bit: 3322222222221111... + // 1098765432109876... + // val: SSTTSSTTSSTTSSTT... + // wp.: 3333222211110000... + // + // where T - type is 01 for write, 11 for r/w + return watch_flags << (16 + 4 * wp_index); +} + +// Returns value for size bits of wp_index in DR7 +static inline uint64_t GetWatchSizeBits(uint32_t size, uint32_t wp_index) { + // DR7: + // bit: 3322222222221111... + // 1098765432109876... + // val: SSTTSSTTSSTTSSTT... + // wp.: 3333222211110000... + // + // where S - size is: + // 00 for 1 byte + // 01 for 2 bytes + // 10 for 8 bytes + // 11 for 4 bytes + return (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); +} + +// Returns bitmask for all bits controlling wp_index in DR7 +static inline uint64_t GetWatchControlBitmask(uint32_t wp_index) { + // DR7: + // bit: 33222222222211111111110000000000 + // 10987654321098765432109876543210 + // val: SSTTSSTTSSTTSSTTxxxxxxGLGLGLGLGL + // wp.: 3333222211110000xxxxxxEE33221100 + return GetBothEnableBitMask(wp_index) | (0xF << (16 + 4 * wp_index)); +} + +// Bit mask for control bits regarding all watchpoints. +static constexpr uint64_t watchpoint_all_control_bit_mask = 0xFFFF00FF; + +const RegisterInfo *WatchpointMixin_x86::GetDR(int num) const { + assert(num >= 0 && num <= 7); + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfoAtIndex(lldb_dr0_i386 + num); + case llvm::Triple::x86_64: + return GetRegisterInfoAtIndex(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 dr6; + Status error = ReadRegister(GetDR(6), dr6); + if (error.Fail()) + is_hit = false; + else + is_hit = dr6.GetAsUInt64() & GetStatusBit(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 dr7; + Status error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + is_vacant = false; + else + is_vacant = !(dr7.GetAsUInt64() & GetEnableBit(wp_index)); + + 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 == LLDB_GDB_WATCH_TYPE_READ) + watch_flags = LLDB_GDB_WATCH_TYPE_READ_WRITE; + + if (watch_flags != LLDB_GDB_WATCH_TYPE_WRITE && watch_flags != LLDB_GDB_WATCH_TYPE_READ_WRITE) + 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 dr7, drN; + error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + return error; + error = ReadRegister(GetDR(wp_index), drN); + if (error.Fail()) + return error; + + uint64_t control_bits = dr7.GetAsUInt64() & ~GetWatchControlBitmask(wp_index); + control_bits |= GetEnableBit(wp_index) | GetWatchTypeBits(watch_flags, wp_index) | GetWatchSizeBits(size, wp_index); + + // Clear dr6 if address or bits changed (i.e. we're not reenabling the same + // watchpoint). This can not be done when clearing watchpoints since + // the gdb-remote protocol repeatedly clears and readds watchpoints on all + // program threads, effectively clearing pending events on NetBSD. + // NB: enable bits in dr7 are always 0 here since we're (re)adding it + if (drN.GetAsUInt64() != addr || + (dr7.GetAsUInt64() & GetWatchControlBitmask(wp_index)) != (GetWatchTypeBits(watch_flags, wp_index) | GetWatchSizeBits(size, wp_index))) { + ClearWatchpointHit(wp_index); + + // We skip update to drN if neither address nor mode changed. + error = WriteRegister(GetDR(wp_index), RegisterValue(addr)); + if (error.Fail()) + return error; + } + + error = WriteRegister(GetDR(7), RegisterValue(control_bits)); + if (error.Fail()) + return error; + + return error; +} + +bool WatchpointMixin_x86::ClearHardwareWatchpoint( + uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + RegisterValue dr7; + Status error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + return false; + + return WriteRegister(GetDR(7), RegisterValue(dr7.GetAsUInt64() & ~GetBothEnableBitMask(wp_index))).Success(); +} + +Status WatchpointMixin_x86::ClearWatchpointHit(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue dr6; + Status error = ReadRegister(GetDR(6), dr6); + if (error.Fail()) + return error; + + return WriteRegister(GetDR(6), RegisterValue(dr6.GetAsUInt64() & ~GetStatusBit(wp_index))); +} + +Status WatchpointMixin_x86::ClearAllHardwareWatchpoints() { + RegisterValue dr7; + Status error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + return error; + return WriteRegister(GetDR(7), RegisterValue(dr7.GetAsUInt64() & ~watchpoint_all_control_bit_mask)); +} + +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 drN; + if (ReadRegister(GetDR(wp_index), drN).Fail()) + return LLDB_INVALID_ADDRESS; + return drN.GetAsUInt64(); +} + +uint32_t WatchpointMixin_x86::NumSupportedHardwareWatchpoints() { + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + +#endif // defined(__i386__) || defined(__x86_64__) Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -2618,17 +2618,17 @@ want_breakpoint = true; break; case eWatchpointWrite: - watch_flags = 1; + watch_flags = LLDB_GDB_WATCH_TYPE_WRITE; want_hardware = true; want_breakpoint = false; break; case eWatchpointRead: - watch_flags = 2; + watch_flags = LLDB_GDB_WATCH_TYPE_READ; want_hardware = true; want_breakpoint = false; break; case eWatchpointReadWrite: - watch_flags = 3; + watch_flags = LLDB_GDB_WATCH_TYPE_READ_WRITE; want_hardware = true; want_breakpoint = false; break;