Index: include/lldb/Host/common/HardwareBreakpointList.h =================================================================== --- include/lldb/Host/common/HardwareBreakpointList.h +++ include/lldb/Host/common/HardwareBreakpointList.h @@ -0,0 +1,39 @@ +//===-- HardwareBreakpointList.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_HardwareBreakpointList_h_ +#define liblldb_HardwareBreakpointList_h_ + +#include "lldb/Utility/Error.h" +#include "lldb/lldb-private-forward.h" + +#include + +namespace lldb_private { +struct HardwareBreakpoint { + lldb::addr_t m_addr; + size_t m_size; +}; + +class HardwareBreakpointList { +public: + Error Add(lldb::addr_t addr, size_t size); + + Error Remove(lldb::addr_t addr); + + using HardwareBreakpointMap = std::map; + + const HardwareBreakpointMap &GetHardwareBreakpointMap() const; + +private: + HardwareBreakpointMap m_hw_breakpoints; +}; +} + +#endif // ifndef liblldb_HardwareBreakpointList_h_ Index: include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- include/lldb/Host/common/NativeProcessProtocol.h +++ include/lldb/Host/common/NativeProcessProtocol.h @@ -19,6 +19,7 @@ #include "lldb/lldb-types.h" #include "llvm/ADT/StringRef.h" +#include "HardwareBreakpointList.h" #include "NativeBreakpointList.h" #include "NativeWatchpointList.h" @@ -99,18 +100,29 @@ virtual Error SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) = 0; - virtual Error RemoveBreakpoint(lldb::addr_t addr); + virtual Error RemoveBreakpoint(lldb::addr_t addr, bool hardware = false); virtual Error EnableBreakpoint(lldb::addr_t addr); virtual Error DisableBreakpoint(lldb::addr_t addr); //---------------------------------------------------------------------- + // Hardware Breakpoint functions + //---------------------------------------------------------------------- + virtual const HardwareBreakpointList::HardwareBreakpointMap & + GetHardwareBreakpointMap() const; + + virtual Error SetHardwareBreakpoint(lldb::addr_t addr, size_t size); + + virtual Error RemoveHardwareBreakpoint(lldb::addr_t addr); + + //---------------------------------------------------------------------- // Watchpoint functions //---------------------------------------------------------------------- virtual const NativeWatchpointList::WatchpointMap &GetWatchpointMap() const; - virtual uint32_t GetMaxWatchpoints() const; + virtual bool GetHardwareDebugSupportInfo(uint32_t &breakpoints, + uint32_t &watchpoints) const; virtual Error SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware); @@ -305,6 +317,7 @@ std::vector m_delegates; NativeBreakpointList m_breakpoint_list; NativeWatchpointList m_watchpoint_list; + HardwareBreakpointList m_hw_breakpoint_list; int m_terminal_fd; uint32_t m_stop_id; Index: include/lldb/Host/common/NativeRegisterContext.h =================================================================== --- include/lldb/Host/common/NativeRegisterContext.h +++ include/lldb/Host/common/NativeRegisterContext.h @@ -75,6 +75,11 @@ virtual bool ClearHardwareBreakpoint(uint32_t hw_idx); + virtual Error ClearAllHardwareBreakpoints(); + + virtual Error GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr); + virtual uint32_t NumSupportedHardwareWatchpoints(); virtual uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, Index: include/lldb/Host/common/NativeThreadProtocol.h =================================================================== --- include/lldb/Host/common/NativeThreadProtocol.h +++ include/lldb/Host/common/NativeThreadProtocol.h @@ -56,6 +56,13 @@ virtual Error RemoveWatchpoint(lldb::addr_t addr) = 0; + // --------------------------------------------------------------------- + // Thread-specific Hardware Breakpoint routines + // --------------------------------------------------------------------- + virtual Error SetHardwareBreakpoint(lldb::addr_t addr, size_t size) = 0; + + virtual Error RemoveHardwareBreakpoint(lldb::addr_t addr) = 0; + protected: NativeProcessProtocolWP m_process_wp; lldb::tid_t m_tid; Index: source/Host/CMakeLists.txt =================================================================== --- source/Host/CMakeLists.txt +++ source/Host/CMakeLists.txt @@ -9,6 +9,7 @@ common/FileSpec.cpp common/FileSystem.cpp common/GetOptInc.cpp + common/HardwareBreakpointList.cpp common/Host.cpp common/HostInfoBase.cpp common/HostNativeThreadBase.cpp Index: source/Host/common/HardwareBreakpointList.cpp =================================================================== --- source/Host/common/HardwareBreakpointList.cpp +++ source/Host/common/HardwareBreakpointList.cpp @@ -0,0 +1,30 @@ +//===-- HardwareBreakpointList.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/common/HardwareBreakpointList.h" + +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +Error HardwareBreakpointList::Add(addr_t addr, size_t size) { + m_hw_breakpoints[addr] = {addr, size}; + return Error(); +} + +Error HardwareBreakpointList::Remove(addr_t addr) { + m_hw_breakpoints.erase(addr); + return Error(); +} + +const HardwareBreakpointList::HardwareBreakpointMap & +HardwareBreakpointList::GetHardwareBreakpointMap() const { + return m_hw_breakpoints; +} Index: source/Host/common/NativeProcessProtocol.cpp =================================================================== --- source/Host/common/NativeProcessProtocol.cpp +++ source/Host/common/NativeProcessProtocol.cpp @@ -139,7 +139,8 @@ return m_watchpoint_list.GetWatchpointMap(); } -uint32_t NativeProcessProtocol::GetMaxWatchpoints() const { +bool NativeProcessProtocol::GetHardwareDebugSupportInfo( + uint32_t &breakpoints, uint32_t &watchpoints) const { // This default implementation will return the number of // *hardware* breakpoints available. MacOSX and other OS // implementations that support software breakpoints will want to @@ -154,7 +155,7 @@ log->Warning("NativeProcessProtocol::%s (): failed to find a thread to " "grab a NativeRegisterContext!", __FUNCTION__); - return 0; + return false; } NativeRegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); @@ -163,10 +164,13 @@ log->Warning("NativeProcessProtocol::%s (): failed to get a " "RegisterContextNativeProcess from the first thread!", __FUNCTION__); - return 0; + return false; } - return reg_ctx_sp->NumSupportedHardwareWatchpoints(); + watchpoints = reg_ctx_sp->NumSupportedHardwareWatchpoints(); + breakpoints = reg_ctx_sp->NumSupportedHardwareBreakpoints(); + + return true; } Error NativeProcessProtocol::SetWatchpoint(lldb::addr_t addr, size_t size, @@ -263,6 +267,90 @@ return overall_error.Fail() ? overall_error : error; } +const HardwareBreakpointList::HardwareBreakpointMap & +NativeProcessProtocol::GetHardwareBreakpointMap() const { + return m_hw_breakpoint_list.GetHardwareBreakpointMap(); +} + +Error NativeProcessProtocol::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + // This default implementation assumes setting a hardware breakpoint for + // this process will require setting same hardware breakpoint for each + // of its existing threads. New thread will do the same once created. + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Exit here if the no of process threads are greater than available + // hardware breakpoint slots on target. + size_t no_of_threads = UpdateThreads(); + uint32_t breakpoints = 0, watchpoints = 0; + bool info_success = GetHardwareDebugSupportInfo(breakpoints, watchpoints); + + if (!info_success || breakpoints < no_of_threads) + return Error("Target does not have required no of hardware breakpoints"); + + // Vector below stores all thread pointer for which we have we successfully + // set this hardware breakpoint. If any of the current process threads fails + // to set this hardware breakpoint then roll back and remove this breakpoint + // for all the threads that had already set it successfully. + std::vector breakpoint_established_threads; + + // Request to set a hardware breakpoint for each of current process threads. + std::lock_guard guard(m_threads_mutex); + for (auto thread_sp : m_threads) { + assert(thread_sp && "thread list should not have a NULL thread!"); + if (!thread_sp) + continue; + + Error thread_error = thread_sp->SetHardwareBreakpoint(addr, size); + if (thread_error.Success()) { + // Remember that we set this breakpoint successfully in + // case we need to clear it later. + breakpoint_established_threads.push_back(thread_sp); + } else { + // Unset the breakpoint for each thread we successfully + // set so that we get back to a consistent state of "not + // set" for this hardware breakpoint. + for (auto rollback_thread_sp : breakpoint_established_threads) { + Error remove_error = rollback_thread_sp->RemoveHardwareBreakpoint(addr); + if (remove_error.Fail() && log) { + log->Warning("NativeProcessProtocol::%s (): RemoveHardwareBreakpoint" + " failed for pid=%" PRIu64 ", tid=%" PRIu64 ": %s", + __FUNCTION__, GetID(), rollback_thread_sp->GetID(), + remove_error.AsCString()); + } + } + + return thread_error; + } + } + return m_hw_breakpoint_list.Add(addr, size); +} + +Error NativeProcessProtocol::RemoveHardwareBreakpoint(lldb::addr_t addr) { + // Update the thread list + UpdateThreads(); + + Error overall_error; + + std::lock_guard guard(m_threads_mutex); + for (auto thread_sp : m_threads) { + assert(thread_sp && "thread list should not have a NULL thread!"); + if (!thread_sp) + continue; + + const Error thread_error = thread_sp->RemoveHardwareBreakpoint(addr); + if (thread_error.Fail()) { + // Keep track of the first thread error if any threads + // fail. We want to try to remove the watchpoint from + // every thread, though, even if one or more have errors. + if (!overall_error.Fail()) + overall_error = thread_error; + } + } + const Error error = m_hw_breakpoint_list.Remove(addr); + return overall_error.Fail() ? overall_error : error; +} + bool NativeProcessProtocol::RegisterNativeDelegate( NativeDelegate &native_delegate) { std::lock_guard guard(m_delegates_mutex); @@ -339,8 +427,12 @@ }); } -Error NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr) { - return m_breakpoint_list.DecRef(addr); +Error NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr, + bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + else + return m_breakpoint_list.DecRef(addr); } Error NativeProcessProtocol::EnableBreakpoint(lldb::addr_t addr) { Index: source/Host/common/NativeRegisterContext.cpp =================================================================== --- source/Host/common/NativeRegisterContext.cpp +++ source/Host/common/NativeRegisterContext.cpp @@ -246,10 +246,20 @@ return LLDB_INVALID_INDEX32; } +Error NativeRegisterContext::ClearAllHardwareBreakpoints() { + return Error("not implemented"); +} + bool NativeRegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) { return false; } +Error NativeRegisterContext::GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr) { + bp_index = LLDB_INVALID_INDEX32; + return Error("not implemented"); +} + uint32_t NativeRegisterContext::NumSupportedHardwareWatchpoints() { return 0; } uint32_t NativeRegisterContext::SetHardwareWatchpoint(lldb::addr_t addr, Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -86,6 +86,8 @@ Error SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) override; + Error RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; + void DoStopIDBumped(uint32_t newBumpId) override; Error GetLoadedModuleFileSpec(const char *module_path, Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -870,6 +870,17 @@ break; } + // If a breakpoint was hit, report it + error = thread.GetRegisterContext()->GetHardwareBreakHitIndex( + wp_index, (uintptr_t)info.si_addr); + if (error.Fail()) + LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " + "{0}, error = {1}", + thread.GetID(), error); + if (wp_index != LLDB_INVALID_INDEX32) { + MonitorBreakpoint(thread); + break; + } // Otherwise, report step over MonitorTrace(thread); break; @@ -1719,11 +1730,18 @@ Error NativeProcessLinux::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { if (hardware) - return Error("NativeProcessLinux does not support hardware breakpoints"); + return SetHardwareBreakpoint(addr, size); else return SetSoftwareBreakpoint(addr, size); } +Error NativeProcessLinux::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + else + return NativeProcessProtocol::RemoveBreakpoint(addr); +} + Error NativeProcessLinux::GetSoftwareBreakpointTrapOpcode( size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) { Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -46,10 +46,17 @@ // Hardware breakpoints/watchpoint mangement functions //------------------------------------------------------------------ + uint32_t NumSupportedHardwareBreakpoints() override; + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + Error ClearAllHardwareBreakpoints() override; + + Error GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr) override; + uint32_t NumSupportedHardwareWatchpoints() override; uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -153,6 +153,7 @@ ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); + ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs)); // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -360,10 +361,27 @@ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); } +uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + Error error; + + // Read hardware breakpoint and watchpoint information. + error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + return m_max_hbp_supported; +} + uint32_t NativeRegisterContextLinux_arm64::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); // Read hardware breakpoint and watchpoint information. @@ -376,59 +394,51 @@ // Check if size has a valid hardware breakpoint length. if (size != 4) - return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware - // breakpoint + return LLDB_INVALID_INDEX32; - // Check 4-byte alignment for hardware breakpoint target address. - if (addr & 0x03) - return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. + // Check breakpoint target address alignment + if (addr & (size - 1)) + return LLDB_INVALID_INDEX32; // Setup control value - control_value = 0; + control_value = 0 << 3; control_value |= ((1 << size) - 1) << 5; control_value |= (2 << 1) | 1; - // Iterate over stored hardware breakpoints - // Find a free bp_index or update reference count if duplicate. + // Iterate over stored breakpoints and find a free bp_index bp_index = LLDB_INVALID_INDEX32; for (uint32_t i = 0; i < m_max_hbp_supported; i++) { if ((m_hbr_regs[i].control & 1) == 0) { bp_index = i; // Mark last free slot - } else if (m_hbr_regs[i].address == addr && - m_hbr_regs[i].control == control_value) { - bp_index = i; // Mark duplicate index - break; // Stop searching here + } else if (m_hbr_regs[i].address == addr) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. } } if (bp_index == LLDB_INVALID_INDEX32) return LLDB_INVALID_INDEX32; - // Add new or update existing breakpoint - if ((m_hbr_regs[bp_index].control & 1) == 0) { - m_hbr_regs[bp_index].address = addr; - m_hbr_regs[bp_index].control = control_value; - m_hbr_regs[bp_index].refcount = 1; - - // PTRACE call to set corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error.Fail()) { - m_hbr_regs[bp_index].address = 0; - m_hbr_regs[bp_index].control &= ~1; - m_hbr_regs[bp_index].refcount = 0; + // Update breakpoint in local cache + m_hbr_regs[bp_index].real_addr = addr; + m_hbr_regs[bp_index].address = addr; + m_hbr_regs[bp_index].control = control_value; - return LLDB_INVALID_INDEX32; - } - } else - m_hbr_regs[bp_index].refcount++; + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbr_regs[bp_index].address = 0; + m_hbr_regs[bp_index].control &= ~1; + + return LLDB_INVALID_INDEX32; + } return bp_index; } bool NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint( uint32_t hw_idx) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); LLDB_LOG(log, "hw_idx: {0}", hw_idx); // Read hardware breakpoint and watchpoint information. @@ -440,35 +450,86 @@ if (hw_idx >= m_max_hbp_supported) return false; - // Update reference count if multiple references. - if (m_hbr_regs[hw_idx].refcount > 1) { - m_hbr_regs[hw_idx].refcount--; - return true; - } else if (m_hbr_regs[hw_idx].refcount == 1) { - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; - uint32_t tempControl = m_hbr_regs[hw_idx].control; - uint32_t tempRefCount = m_hbr_regs[hw_idx].refcount; - - m_hbr_regs[hw_idx].control &= ~1; - m_hbr_regs[hw_idx].address = 0; - m_hbr_regs[hw_idx].refcount = 0; - - // PTRACE call to clear corresponding hardware breakpoint register. - WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error.Fail()) { - m_hbr_regs[hw_idx].control = tempControl; - m_hbr_regs[hw_idx].address = tempAddr; - m_hbr_regs[hw_idx].refcount = tempRefCount; + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; + uint32_t tempControl = m_hbr_regs[hw_idx].control; + + m_hbr_regs[hw_idx].control &= ~1; + m_hbr_regs[hw_idx].address = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbr_regs[hw_idx].control = tempControl; + m_hbr_regs[hw_idx].address = tempAddr; + + return false; + } + + return true; +} + +Error NativeRegisterContextLinux_arm64::GetHardwareBreakHitIndex( + uint32_t &bp_index, lldb::addr_t trap_addr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - return false; + if (log) + log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + lldb::addr_t break_addr; + + for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { + break_addr = m_hbr_regs[bp_index].address; + + if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) { + m_hbr_regs[bp_index].hit_addr = trap_addr; + return Error(); } + } - return true; + bp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error NativeRegisterContextLinux_arm64::ClearAllHardwareBreakpoints() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + // Read hardware breakpoint and watchpoint information. + Error error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return error; + + lldb::addr_t tempAddr = 0; + uint32_t tempControl = 0; + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + if (m_hbr_regs[i].control & 0x01) { + // Create a backup we can revert to in case of failure. + tempAddr = m_hbr_regs[i].address; + tempControl = m_hbr_regs[i].control; + + // Clear watchpoints in local cache + m_hbr_regs[i].control &= ~1; + m_hbr_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbr_regs[i].control = tempControl; + m_hbr_regs[i].address = tempAddr; + + return error; + } + } } - return false; + return Error(); } uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints() { @@ -478,7 +539,7 @@ Error error = ReadHardwareDebugInfo(); if (error.Fail()) - return 0; + return LLDB_INVALID_INDEX32; LLDB_LOG(log, "{0}", m_max_hwp_supported); return m_max_hwp_supported; Index: source/Plugins/Process/Linux/NativeThreadLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.h +++ source/Plugins/Process/Linux/NativeThreadLinux.h @@ -46,6 +46,10 @@ Error RemoveWatchpoint(lldb::addr_t addr) override; + Error SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Error RemoveHardwareBreakpoint(lldb::addr_t addr) override; + private: // --------------------------------------------------------------------- // Interface for friend classes @@ -102,6 +106,7 @@ std::string m_stop_description; using WatchpointIndexMap = std::map; WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; llvm::Optional m_step_workaround; }; Index: source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -190,6 +190,38 @@ return Error("Clearing hardware watchpoint failed."); } +Error NativeThreadLinux::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { + if (m_state == eStateLaunching) + return Error(); + + Error error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + NativeRegisterContextSP reg_ctx = GetRegisterContext(); + uint32_t bp_index = reg_ctx->SetHardwareBreakpoint(addr, size); + + if (bp_index == LLDB_INVALID_INDEX32) + return Error("Setting hardware breakpoint failed."); + + m_hw_break_index_map.insert({addr, bp_index}); + return Error(); +} + +Error NativeThreadLinux::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Error(); + + uint32_t bp_index = bp->second; + if (GetRegisterContext()->ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Error(); + } + + return Error("Clearing hardware breakpoint failed."); +} + Error NativeThreadLinux::Resume(uint32_t signo) { const StateType new_state = StateType::eStateRunning; MaybeLogStateChange(new_state); @@ -211,6 +243,18 @@ } } + // Set all active hardware breakpoint on all threads. + if (m_hw_break_index_map.empty()) { + NativeProcessLinux &process = GetProcess(); + + const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap(); + GetRegisterContext()->ClearAllHardwareBreakpoints(); + for (const auto &pair : hw_breakpoint_map) { + const auto &bp = pair.second; + SetHardwareBreakpoint(bp.m_addr, bp.m_size); + } + } + intptr_t data = 0; if (signo != LLDB_INVALID_SIGNAL_NUMBER) Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -2531,12 +2531,14 @@ packet, "Too short z packet, missing software/hardware specifier"); bool want_breakpoint = true; + bool want_hardware = false; const GDBStoppointType stoppoint_type = GDBStoppointType(packet.GetS32(eStoppointInvalid)); switch (stoppoint_type) { case eBreakpointHardware: want_breakpoint = true; + want_hardware = true; break; case eBreakpointSoftware: want_breakpoint = true; @@ -2579,7 +2581,8 @@ if (want_breakpoint) { // Try to clear the breakpoint. - const Error error = m_debugged_process_sp->RemoveBreakpoint(addr); + const Error error = + m_debugged_process_sp->RemoveBreakpoint(addr, want_hardware); if (error.Success()) return SendOKResponse(); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); @@ -3037,9 +3040,14 @@ if (packet.GetChar() != ':') return SendErrorResponse(67); - uint32_t num = m_debugged_process_sp->GetMaxWatchpoints(); + uint32_t bp_max = 0, wp_max = 0; + if (!m_debugged_process_sp->GetHardwareDebugSupportInfo(bp_max, wp_max)) { + bp_max = 0; + wp_max = 0; + } + StreamGDBRemote response; - response.Printf("num:%d;", num); + response.Printf("num:%d;", wp_max); return SendPacketNoLock(response.GetString()); }