From 18fe6404f9bc379045f4bce801f1c7113cddc6a0 Mon Sep 17 00:00:00 2001 From: Chaoren Lin Date: Tue, 3 Feb 2015 01:51:47 +0000 Subject: [PATCH] Implement setting and clearing watchpoints. llvm-svn: 227930 --- .../lldb/Host/common/NativeProcessProtocol.h | 5 + .../lldb/Host/common/NativeRegisterContext.h | 4 + .../lldb/Host/common/NativeWatchpointList.h | 47 ++++++ lldb/source/Host/CMakeLists.txt | 1 + lldb/source/Host/common/NativeProcessProtocol.cpp | 18 +- lldb/source/Host/common/NativeRegisterContext.cpp | 6 + lldb/source/Host/common/NativeWatchpointList.cpp | 35 ++++ .../Plugins/Process/Linux/NativeProcessLinux.cpp | 2 +- .../Linux/NativeRegisterContextLinux_x86_64.cpp | 167 +++++++++++++++++++ .../Linux/NativeRegisterContextLinux_x86_64.h | 26 +++ .../Plugins/Process/Linux/NativeThreadLinux.cpp | 99 +++++++++-- .../Plugins/Process/Linux/NativeThreadLinux.h | 10 ++ .../Process/gdb-remote/GDBRemoteCommunication.h | 10 ++ .../gdb-remote/GDBRemoteCommunicationClient.h | 9 - .../gdb-remote/GDBRemoteCommunicationServer.cpp | 184 ++++++++++++++------- .../gdb-remote/GDBRemoteCommunicationServer.h | 3 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 20 ++- 17 files changed, 555 insertions(+), 91 deletions(-) create mode 100644 lldb/include/lldb/Host/common/NativeWatchpointList.h create mode 100644 lldb/source/Host/common/NativeWatchpointList.cpp diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h index 19d8f35..83c14a5 100644 --- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -18,6 +18,7 @@ #include "lldb/Host/Mutex.h" #include "NativeBreakpointList.h" +#include "NativeWatchpointList.h" namespace lldb_private { @@ -130,6 +131,9 @@ namespace lldb_private //---------------------------------------------------------------------- // Watchpoint functions //---------------------------------------------------------------------- + virtual const NativeWatchpointList::WatchpointMap& + GetWatchpointMap () const; + virtual uint32_t GetMaxWatchpoints () const; @@ -295,6 +299,7 @@ namespace lldb_private Mutex m_delegates_mutex; std::vector m_delegates; NativeBreakpointList m_breakpoint_list; + NativeWatchpointList m_watchpoint_list; int m_terminal_fd; uint32_t m_stop_id; diff --git a/lldb/include/lldb/Host/common/NativeRegisterContext.h b/lldb/include/lldb/Host/common/NativeRegisterContext.h index 64d4d8f..e9c03e3 100644 --- a/lldb/include/lldb/Host/common/NativeRegisterContext.h +++ b/lldb/include/lldb/Host/common/NativeRegisterContext.h @@ -15,6 +15,7 @@ // Other libraries and framework includes // Project includes #include "lldb/lldb-private.h" +#include "lldb/Host/common/NativeWatchpointList.h" namespace lldb_private { @@ -95,6 +96,9 @@ public: virtual bool ClearHardwareWatchpoint (uint32_t hw_index); + virtual Error + ClearAllHardwareWatchpoints (); + virtual bool HardwareSingleStep (bool enable); diff --git a/lldb/include/lldb/Host/common/NativeWatchpointList.h b/lldb/include/lldb/Host/common/NativeWatchpointList.h new file mode 100644 index 0000000..7b310e5 --- /dev/null +++ b/lldb/include/lldb/Host/common/NativeWatchpointList.h @@ -0,0 +1,47 @@ +//===-- NativeWatchpointList.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_NativeWatchpointList_h_ +#define liblldb_NativeWatchpointList_h_ + +#include "lldb/lldb-private-forward.h" +#include "lldb/Core/Error.h" + +#include + +namespace lldb_private +{ + struct NativeWatchpoint + { + lldb::addr_t m_addr; + size_t m_size; + uint32_t m_watch_flags; + bool m_hardware; + }; + + class NativeWatchpointList + { + public: + Error + Add (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware); + + Error + Remove (lldb::addr_t addr); + + using WatchpointMap = std::map; + + const WatchpointMap& + GetWatchpointMap () const; + + private: + WatchpointMap m_watchpoints; + }; +} + +#endif // ifndef liblldb_NativeWatchpointList_h_ diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index 785812b..7d996a8 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -18,6 +18,7 @@ add_host_subdirectory(common common/MonitoringProcessLauncher.cpp common/NativeBreakpoint.cpp common/NativeBreakpointList.cpp + common/NativeWatchpointList.cpp common/NativeProcessProtocol.cpp common/NativeRegisterContext.cpp common/NativeRegisterContextRegisterInfo.cpp diff --git a/lldb/source/Host/common/NativeProcessProtocol.cpp b/lldb/source/Host/common/NativeProcessProtocol.cpp index 9c29f32a..4c770a2 100644 --- a/lldb/source/Host/common/NativeProcessProtocol.cpp +++ b/lldb/source/Host/common/NativeProcessProtocol.cpp @@ -39,6 +39,7 @@ NativeProcessProtocol::NativeProcessProtocol (lldb::pid_t pid) : m_delegates_mutex (Mutex::eMutexTypeRecursive), m_delegates (), m_breakpoint_list (), + m_watchpoint_list (), m_terminal_fd (-1), m_stop_id (0) { @@ -159,6 +160,12 @@ NativeProcessProtocol::GetByteOrder (lldb::ByteOrder &byte_order) const return true; } +const NativeWatchpointList::WatchpointMap& +NativeProcessProtocol::GetWatchpointMap () const +{ + return m_watchpoint_list.GetWatchpointMap(); +} + uint32_t NativeProcessProtocol::GetMaxWatchpoints () const { @@ -199,9 +206,6 @@ NativeProcessProtocol::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t w Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - // FIXME save the watchpoint on the set of process watchpoint vars - // so we can add them to a thread each time a new thread is registered. - // Update the thread list UpdateThreads (); @@ -261,15 +265,12 @@ NativeProcessProtocol::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t w return thread_error; } } - return Error (); + return m_watchpoint_list.Add (addr, size, watch_flags, hardware); } Error NativeProcessProtocol::RemoveWatchpoint (lldb::addr_t addr) { - // FIXME remove the watchpoint on the set of process watchpoint vars - // so we can add them to a thread each time a new thread is registered. - // Update the thread list UpdateThreads (); @@ -292,7 +293,8 @@ NativeProcessProtocol::RemoveWatchpoint (lldb::addr_t addr) overall_error = thread_error; } } - return overall_error; + const Error error = m_watchpoint_list.Remove(addr); + return overall_error.Fail() ? overall_error : error; } bool diff --git a/lldb/source/Host/common/NativeRegisterContext.cpp b/lldb/source/Host/common/NativeRegisterContext.cpp index 5722879..42a9c91 100644 --- a/lldb/source/Host/common/NativeRegisterContext.cpp +++ b/lldb/source/Host/common/NativeRegisterContext.cpp @@ -297,6 +297,12 @@ NativeRegisterContext::ClearHardwareWatchpoint (uint32_t hw_index) return false; } +Error +NativeRegisterContext::ClearAllHardwareWatchpoints () +{ + return Error ("not implemented"); +} + bool NativeRegisterContext::HardwareSingleStep (bool enable) { diff --git a/lldb/source/Host/common/NativeWatchpointList.cpp b/lldb/source/Host/common/NativeWatchpointList.cpp new file mode 100644 index 0000000..209d213 --- /dev/null +++ b/lldb/source/Host/common/NativeWatchpointList.cpp @@ -0,0 +1,35 @@ +//===-- NativeWatchpointList.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/NativeWatchpointList.h" + +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +Error +NativeWatchpointList::Add (addr_t addr, size_t size, uint32_t watch_flags, bool hardware) +{ + m_watchpoints[addr] = {addr, size, watch_flags, hardware}; + return Error (); +} + +Error +NativeWatchpointList::Remove (addr_t addr) +{ + m_watchpoints.erase(addr); + return Error (); +} + +const NativeWatchpointList::WatchpointMap& +NativeWatchpointList::GetWatchpointMap () const +{ + return m_watchpoints; +} diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 5ff30bd..01eb37b 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2316,7 +2316,7 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid) // Mark the thread as stopped at watchpoint. // The address is at (lldb::addr_t)info->si_addr if we need it. if (thread_sp) - reinterpret_cast (thread_sp.get ())->SetStoppedBySignal (SIGTRAP); + reinterpret_cast (thread_sp.get ())->SetStoppedByWatchpoint (); else { if (log) diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp index 649dfb2..80df326 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -1032,3 +1032,170 @@ NativeRegisterContextLinux_x86_64::WriteGPR() return process_p->WriteGPR (m_thread.GetID (), &m_gpr_x86_64, GetRegisterInfoInterface ().GetGPRSize ()).Success(); } +Error +NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint8_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value); + if (error.Fail()) return error; + + uint64_t status_bits = reg_value.GetAsUInt64(); + + bool is_hit = status_bits & (1 << wp_index); + + error.SetError (!is_hit, lldb::eErrorTypeInvalid); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + RegisterValue reg_value; + Error error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value); + if (error.Fail()) return error; + + uint64_t control_bits = reg_value.GetAsUInt64(); + + bool is_vacant = !(control_bits & (1 << (2 * wp_index))); + + error.SetError (!is_vacant, lldb::eErrorTypeInvalid); + + return error; +} + +Error +NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error ("Watchpoint index out of range"); + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Error ("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Error ("Invalid size for watchpoint"); + + Error error = IsWatchpointVacant (wp_index); + if (error.Fail()) return error; + + RegisterValue reg_value; + error = ReadRegisterRaw(lldb_dr7_x86_64, 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 = WriteRegister(m_reg_info.first_dr + wp_index, RegisterValue(addr)); + if (error.Fail()) return error; + + error = WriteRegister(lldb_dr7_x86_64, 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) + Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value); + if (error.Fail()) return false; + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(lldb_dr6_x86_64, 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(lldb_dr7_x86_64, 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 WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits)).Success(); +} + +Error +NativeRegisterContextLinux_x86_64::ClearAllHardwareWatchpoints() +{ + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + Error error = ReadRegisterRaw(lldb_dr6_x86_64, reg_value); + if (error.Fail()) return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(lldb_dr6_x86_64, RegisterValue(status_bits)); + if (error.Fail()) return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + error = ReadRegisterRaw(lldb_dr7_x86_64, reg_value); + if (error.Fail()) return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(lldb_dr7_x86_64, RegisterValue(control_bits)); +} + +uint32_t +NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) + if (IsWatchpointVacant(wp_index).Success()) + { + if (SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index).Fail()) + continue; + return wp_index; + } + 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; +} diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h index e624cc1..7c9d352 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -42,6 +42,32 @@ namespace lldb_private Error WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + Error + IsWatchpointHit(uint8_t wp_index); + + Error + IsWatchpointVacant(uint32_t wp_index); + + bool + ClearHardwareWatchpoint(uint32_t wp_index); + + Error + ClearAllHardwareWatchpoints (); + + Error + 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); + + lldb::addr_t + GetWatchpointAddress(uint32_t wp_index); + + uint32_t + NumSupportedHardwareWatchpoints(); + private: // Private member types. diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp index 66af7de..daa3f3e 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -10,6 +10,7 @@ #include "NativeThreadLinux.h" #include +#include #include "NativeProcessLinux.h" #include "NativeRegisterContextLinux_x86_64.h" @@ -101,8 +102,15 @@ NativeThreadLinux::GetStopReason (ThreadStopInfo &stop_info, std::string& descri if (log) LogThreadStopInfo (*log, m_stop_info, "m_stop_info in thread:"); stop_info = m_stop_info; - if (m_stop_info.reason == StopReason::eStopReasonException) - description = m_stop_description; + switch (m_stop_info.reason) + { + case StopReason::eStopReasonException: + case StopReason::eStopReasonBreakpoint: + case StopReason::eStopReasonWatchpoint: + description = m_stop_description; + default: + break; + } if (log) LogThreadStopInfo (*log, stop_info, "returned stop_info:"); @@ -209,15 +217,30 @@ NativeThreadLinux::GetRegisterContext () Error NativeThreadLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) { - // TODO implement - return Error ("not implemented"); + if (!hardware) + return Error ("not implemented"); + Error error = RemoveWatchpoint(addr); + if (error.Fail()) return error; + NativeRegisterContextSP reg_ctx = GetRegisterContext (); + uint32_t wp_index = + reg_ctx->SetHardwareWatchpoint (addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Error ("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Error (); } Error NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) { - // TODO implement - return Error ("not implemented"); + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Error (); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext()->ClearHardwareWatchpoint(wp_index)) + return Error (); + return Error ("Clearing hardware watchpoint failed."); } void @@ -243,6 +266,20 @@ NativeThreadLinux::SetRunning () m_stop_info.reason = StopReason::eStopReasonNone; m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, + // then this is a new thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) + { + const auto &watchpoint_map = GetProcess()->GetWatchpointMap(); + if (watchpoint_map.empty()) return; + GetRegisterContext()->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) + { + const auto& wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } } void @@ -313,18 +350,56 @@ NativeThreadLinux::SetStoppedByBreakpoint () m_stop_info.reason = StopReason::eStopReasonBreakpoint; m_stop_info.details.signal.signo = SIGTRAP; + m_stop_description.clear(); +} + +void +NativeThreadLinux::SetStoppedByWatchpoint () +{ + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange (new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.details.signal.signo = SIGTRAP; + + NativeRegisterContextLinux_x86_64 *reg_ctx = + reinterpret_cast (GetRegisterContext().get()); + const uint32_t num_hw_watchpoints = + reg_ctx->NumSupportedHardwareWatchpoints(); + + m_stop_description.clear (); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) + if (reg_ctx->IsWatchpointHit(wp_index).Success()) + { + std::ostringstream ostr; + ostr << reg_ctx->GetWatchpointAddress(wp_index) << " " << wp_index; + m_stop_description = ostr.str(); + return; + } + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + { + NativeProcessProtocolSP m_process_sp = m_process_wp.lock (); + lldb::pid_t pid = m_process_sp ? m_process_sp->GetID () : LLDB_INVALID_PROCESS_ID; + log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") " + "stopped by a watchpoint, but failed to find it", + pid, GetID ()); + } } bool NativeThreadLinux::IsStoppedAtBreakpoint () { - // Are we stopped? If not, this can't be a breakpoint. - if (GetState () != StateType::eStateStopped) - return false; + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} - // Was the stop reason a signal with signal number SIGTRAP? If not, not a breakpoint. - return (m_stop_info.reason == StopReason::eStopReasonBreakpoint) && - (m_stop_info.details.signal.signo == SIGTRAP); +bool +NativeThreadLinux::IsStoppedAtWatchpoint () +{ + return GetState () == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; } void diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h index 7233b30..2519b5b 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -13,6 +13,8 @@ #include "lldb/lldb-private-forward.h" #include "lldb/Host/common/NativeThreadProtocol.h" +#include + namespace lldb_private { class NativeProcessLinux; @@ -73,9 +75,15 @@ namespace lldb_private void SetStoppedByBreakpoint (); + void + SetStoppedByWatchpoint (); + bool IsStoppedAtBreakpoint (); + bool + IsStoppedAtWatchpoint (); + void SetStoppedByTrace (); @@ -101,6 +109,8 @@ namespace lldb_private ThreadStopInfo m_stop_info; NativeRegisterContextSP m_reg_context_sp; std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; }; } diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h index 60cbc2c..ee2f327 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -27,6 +27,16 @@ #include "Utility/StringExtractorGDBRemote.h" +typedef enum +{ + eStoppointInvalid = -1, + eBreakpointSoftware = 0, + eBreakpointHardware, + eWatchpointWrite, + eWatchpointRead, + eWatchpointReadWrite +} GDBStoppointType; + class ProcessGDBRemote; class GDBRemoteCommunication : public lldb_private::Communication diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index fddcd6c..d1b8389 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -21,15 +21,6 @@ #include "GDBRemoteCommunication.h" -typedef enum -{ - eBreakpointSoftware = 0, - eBreakpointHardware, - eWatchpointWrite, - eWatchpointRead, - eWatchpointReadWrite -} GDBStoppointType; - class GDBRemoteCommunicationClient : public GDBRemoteCommunication { public: diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index de3e376..3f60671 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -278,6 +278,10 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, packet_result = Handle_qPlatform_shell (packet); break; + case StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo: + packet_result = Handle_qWatchpointSupportInfo (packet); + break; + case StringExtractorGDBRemote::eServerPacketType_C: packet_result = Handle_C (packet); break; @@ -3726,8 +3730,6 @@ GDBRemoteCommunicationServer::Handle_qMemoryRegionInfo (StringExtractorGDBRemote GDBRemoteCommunicationServer::PacketResult GDBRemoteCommunicationServer::Handle_Z (StringExtractorGDBRemote &packet) { - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - // We don't support if we're not llgs. if (!IsGdbServer()) return SendUnimplementedResponse (""); @@ -3735,12 +3737,13 @@ GDBRemoteCommunicationServer::Handle_Z (StringExtractorGDBRemote &packet) // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); return SendErrorResponse (0x15); } - // Parse out software or hardware breakpoint requested. + // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos (strlen("Z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short Z packet, missing software/hardware specifier"); @@ -3748,61 +3751,85 @@ GDBRemoteCommunicationServer::Handle_Z (StringExtractorGDBRemote &packet) bool want_breakpoint = true; bool want_hardware = false; - const char breakpoint_type_char = packet.GetChar (); - switch (breakpoint_type_char) - { - case '0': want_hardware = false; want_breakpoint = true; break; - case '1': want_hardware = true; want_breakpoint = true; break; - case '2': want_breakpoint = false; break; - case '3': want_breakpoint = false; break; + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32 (eStoppointInvalid)); + switch (stoppoint_type) + { + case eBreakpointSoftware: + want_hardware = false; want_breakpoint = true; break; + case eBreakpointHardware: + want_hardware = true; want_breakpoint = true; break; + case eWatchpointWrite: + want_hardware = true; want_breakpoint = false; break; + case eWatchpointRead: + want_hardware = true; want_breakpoint = false; break; + case eWatchpointReadWrite: + want_hardware = true; want_breakpoint = false; break; default: return SendIllFormedResponse(packet, "Z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after breakpoint type"); + return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after stoppoint type"); - // FIXME implement watchpoint support. - if (!want_breakpoint) - return SendUnimplementedResponse ("watchpoint support not yet implemented"); - - // Parse out the breakpoint address. + // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short Z packet, missing address"); - const lldb::addr_t breakpoint_addr = packet.GetHexMaxU64(false, 0); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after address"); - // Parse out the breakpoint kind (i.e. size hint for opcode size). - const uint32_t kind = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); - if (kind == std::numeric_limits::max ()) - return SendIllFormedResponse(packet, "Malformed Z packet, failed to parse kind argument"); + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); + if (size == std::numeric_limits::max ()) + return SendIllFormedResponse(packet, "Malformed Z packet, failed to parse size argument"); if (want_breakpoint) { // Try to set the breakpoint. - const Error error = m_debugged_process_sp->SetBreakpoint (breakpoint_addr, kind, want_hardware); + const Error error = m_debugged_process_sp->SetBreakpoint (addr, size, want_hardware); if (error.Success ()) return SendOKResponse (); - else + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to set breakpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } + else + { + uint32_t watch_flags = 0x0; + switch (stoppoint_type) { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " failed to set breakpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x09); + case eWatchpointWrite: watch_flags = 0x1; break; + case eWatchpointRead: watch_flags = 0x3; break; + case eWatchpointReadWrite: watch_flags = 0x3; break; } - } - // FIXME fix up after watchpoints are handled. - return SendUnimplementedResponse (""); + // Try to set the watchpoint. + const Error error = m_debugged_process_sp->SetWatchpoint ( + addr, size, watch_flags, want_hardware); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to set watchpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } } GDBRemoteCommunicationServer::PacketResult GDBRemoteCommunicationServer::Handle_z (StringExtractorGDBRemote &packet) { - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - // We don't support if we're not llgs. if (!IsGdbServer()) return SendUnimplementedResponse (""); @@ -3810,66 +3837,81 @@ GDBRemoteCommunicationServer::Handle_z (StringExtractorGDBRemote &packet) // Ensure we have a process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); return SendErrorResponse (0x15); } - // Parse out software or hardware breakpoint requested. + // Parse out software or hardware breakpoint or watchpoint requested. packet.SetFilePos (strlen("z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing software/hardware specifier"); bool want_breakpoint = true; - const char breakpoint_type_char = packet.GetChar (); - switch (breakpoint_type_char) + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32 (eStoppointInvalid)); + switch (stoppoint_type) { - case '0': want_breakpoint = true; break; - case '1': want_breakpoint = true; break; - case '2': want_breakpoint = false; break; - case '3': want_breakpoint = false; break; + case eBreakpointHardware: want_breakpoint = true; break; + case eBreakpointSoftware: want_breakpoint = true; break; + case eWatchpointWrite: want_breakpoint = false; break; + case eWatchpointRead: want_breakpoint = false; break; + case eWatchpointReadWrite: want_breakpoint = false; break; default: return SendIllFormedResponse(packet, "z packet had invalid software/hardware specifier"); } if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after breakpoint type"); + return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after stoppoint type"); - // FIXME implement watchpoint support. - if (!want_breakpoint) - return SendUnimplementedResponse ("watchpoint support not yet implemented"); - - // Parse out the breakpoint address. + // Parse out the stoppoint address. if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing address"); - const lldb::addr_t breakpoint_addr = packet.GetHexMaxU64(false, 0); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after address"); - // Parse out the breakpoint kind (i.e. size hint for opcode size). - const uint32_t kind = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); - if (kind == std::numeric_limits::max ()) - return SendIllFormedResponse(packet, "Malformed z packet, failed to parse kind argument"); + /* + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits::max ()); + if (size == std::numeric_limits::max ()) + return SendIllFormedResponse(packet, "Malformed z packet, failed to parse size argument"); + */ if (want_breakpoint) { // Try to clear the breakpoint. - const Error error = m_debugged_process_sp->RemoveBreakpoint (breakpoint_addr); + const Error error = m_debugged_process_sp->RemoveBreakpoint (addr); if (error.Success ()) return SendOKResponse (); - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " failed to remove breakpoint: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x09); - } + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to remove breakpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } + else + { + // Try to clear the watchpoint. + const Error error = m_debugged_process_sp->RemoveWatchpoint (addr); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 + " failed to remove watchpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); } - - // FIXME fix up after watchpoints are handled. - return SendUnimplementedResponse (""); } GDBRemoteCommunicationServer::PacketResult @@ -4305,6 +4347,30 @@ GDBRemoteCommunicationServer::Handle_qThreadStopInfo (StringExtractorGDBRemote & return SendStopReplyPacketForThread (tid); } +GDBRemoteCommunicationServer::PacketResult +GDBRemoteCommunicationServer::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet) +{ + // Only the gdb server handles this. + if (!IsGdbServer ()) + return SendUnimplementedResponse (packet.GetStringRef ().c_str ()); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse (68); + + packet.SetFilePos(strlen("qWatchpointSupportInfo")); + if (packet.GetBytesLeft() == 0) + return SendOKResponse(); + if (packet.GetChar() != ':') + return SendErrorResponse(67); + + uint32_t num = m_debugged_process_sp->GetMaxWatchpoints(); + StreamGDBRemote response; + response.Printf ("num:%d;", num); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + void GDBRemoteCommunicationServer::FlushInferiorOutput () { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index 0a2eb0d..474bbb6 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -465,6 +465,9 @@ protected: PacketResult Handle_qThreadStopInfo (StringExtractorGDBRemote &packet); + PacketResult + Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet); + void SetCurrentThreadID (lldb::tid_t tid); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 0dc2fd0..0ef05ac 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1844,8 +1844,24 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) } else if (reason.compare("watchpoint") == 0) { - break_id_t watch_id = LLDB_INVALID_WATCH_ID; - // TODO: locate the watchpoint somehow... + StringExtractor desc_extractor(description.c_str()); + addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); + uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); + watch_id_t watch_id = LLDB_INVALID_WATCH_ID; + if (wp_addr != LLDB_INVALID_ADDRESS) + { + WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); + if (wp_sp) + { + wp_sp->SetHardwareIndex(wp_index); + watch_id = wp_sp->GetID(); + } + } + if (watch_id == LLDB_INVALID_WATCH_ID) + { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); + if (log) log->Printf ("failed to find watchpoint"); + } thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); handled = true; } -- 2.7.4