Index: source/Breakpoint/WatchpointList.cpp =================================================================== --- source/Breakpoint/WatchpointList.cpp +++ source/Breakpoint/WatchpointList.cpp @@ -73,9 +73,12 @@ Mutex::Locker locker (m_mutex); if (!m_watchpoints.empty()) { - wp_collection::const_iterator pos, end = m_watchpoints.end(); - for (pos = m_watchpoints.begin(); pos != end; ++pos) - if ((*pos)->GetLoadAddress() == addr) { + wp_collection::const_iterator pos = m_watchpoints.begin(), end = m_watchpoints.end(); + llvm::Triple::ArchType Arch = (*pos)->GetTarget().GetArchitecture().GetMachine(); + for (pos; pos != end; ++pos) + if (((*pos)->GetLoadAddress() == addr) || + ((Arch == llvm::Triple::mips64 || Arch == llvm::Triple::mips64el) && + (((*pos)->GetLoadAddress() & ~0x7) == (addr & ~0x7)))) wp_sp = *pos; break; } Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -135,6 +135,18 @@ ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, RegisterValue &value); + /// Reads the contents from the watch registers. + /// + /// This method is provided for use by RegisterContextLinux derivatives. + Error + ReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback); + + /// Sets the contents of the watch registers. + /// + /// This method is provided for use by RegisterContextLinux derivatives. + Error + SetWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value); + /// Writes the given value to the register identified by the given /// (architecture dependent) offset. /// Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -61,6 +61,11 @@ #include #include +#ifndef PTRACE_GET_WATCH_REGS + #define PTRACE_GET_WATCH_REGS 0xd0 + #define PTRACE_SET_WATCH_REGS 0xd1 +#endif + #if defined (__arm64__) || defined (__aarch64__) // NT_PRSTATUS and NT_FPREGSET definition #include @@ -879,6 +884,60 @@ } //------------------------------------------------------------------------------ + /// @class ReadWatchPointRegOperation + /// @brief Implements NativeProcessLinux::ReadWatchPointRegisterValue. + class ReadWatchPointRegOperation : public Operation + { + public: + ReadWatchPointRegOperation ( + lldb::tid_t tid, + void* watch_readback) : + m_tid(tid), + m_watch_readback(watch_readback) + { + } + + void Execute(NativeProcessLinux *monitor) override; + + private: + lldb::tid_t m_tid; + void* m_watch_readback; + }; + + void + ReadWatchPointRegOperation::Execute(NativeProcessLinux *monitor) + { + PTRACE(PTRACE_GET_WATCH_REGS, m_tid, m_watch_readback, NULL, NULL, m_error); + } + + //------------------------------------------------------------------------------ + /// @class SetWatchPointRegOperation + /// @brief Implements NativeProcessLinux::SetWatchPointRegisterValue. + class SetWatchPointRegOperation : public Operation + { + public: + SetWatchPointRegOperation ( + lldb::tid_t tid, + void* watch_reg_value) : + m_tid(tid), + m_watch_reg_value(watch_reg_value) + { + } + + void Execute(NativeProcessLinux *monitor) override; + + private: + lldb::tid_t m_tid; + void* m_watch_reg_value; + }; + + void + SetWatchPointRegOperation::Execute(NativeProcessLinux *monitor) + { + PTRACE(PTRACE_SET_WATCH_REGS, m_tid, m_watch_reg_value, NULL, NULL, m_error); + } + + //------------------------------------------------------------------------------ /// @class ResumeOperation /// @brief Implements NativeProcessLinux::Resume. class ResumeOperation : public Operation @@ -2453,6 +2512,27 @@ break; case SI_KERNEL: +#if defined __mips__ + // For mips there is no special signal for watchpoint + // So we check for watchpoint in kernel trap + if (thread_sp) + { + // If a watchpoint was hit, report it + uint32_t wp_index; + Error error = thread_sp->GetRegisterContext()->GetWatchpointHitIndex(wp_index); + if (error.Fail() && log) + log->Printf("NativeProcessLinux::%s() " + "received error while checking for watchpoint hits, " + "pid = %" PRIu64 " error = %s", + __FUNCTION__, pid, error.AsCString()); + if (wp_index != LLDB_INVALID_INDEX32) + { + MonitorWatchpoint(pid, thread_sp, wp_index); + break; + } + } + // NO BREAK +#endif case TRAP_BRKPT: MonitorBreakpoint(pid, thread_sp); break; @@ -3866,6 +3946,22 @@ } Error +NativeProcessLinux::ReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback) +{ + ReadWatchPointRegOperation op(tid, watch_readback); + DoOperation(&op); + return op.GetError(); +} + +Error +NativeProcessLinux::SetWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value) +{ + SetWatchPointRegOperation op(tid, watch_reg_value); + DoOperation(&op); + return op.GetError(); +} + +Error NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h @@ -96,6 +96,9 @@ IsWatchpointHit (uint32_t wp_index, bool &is_hit) override; Error + GetWatchpointHitIndex(uint32_t &wp_index) override; + + Error IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) override; bool Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "NativeRegisterContextLinux_mips64.h" - +#include "lldb/Core/Log.h" #include "lldb/lldb-private-forward.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Error.h" @@ -17,6 +17,50 @@ #include "lldb/Host/common/NativeThreadProtocol.h" #include "Plugins/Process/Linux/NativeProcessLinux.h" +#include +#include + +#ifndef PTRACE_GET_WATCH_REGS +enum pt_watch_style +{ + pt_watch_style_mips32, + pt_watch_style_mips64 +}; +struct mips32_watch_regs +{ + uint32_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct mips64_watch_regs +{ + uint64_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct pt_watch_regs +{ + enum pt_watch_style style; + union + { + struct mips32_watch_regs mips32; + struct mips64_watch_regs mips64; + }; +}; +#endif + +#define W (1 << 0) +#define R (1 << 1) +#define I (1 << 2) + +#define IRW (I | R | W) + +struct pt_watch_regs default_watch_regs; + using namespace lldb_private; using namespace lldb_private::process_linux; @@ -244,11 +288,218 @@ return error; } +static uint32_t +GetWatchHi (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watchhi[index]; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watchhi[index]; + else + log->Printf("Invalid watch register style"); +} + +static void +SetWatchHi (struct pt_watch_regs *regs, uint32_t index, uint16_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + regs->mips32.watchhi[index] = value; + else if (regs->style == pt_watch_style_mips64) + regs->mips64.watchhi[index] = value; + else + log->Printf("Invalid watch register style"); +} + +static lldb::addr_t +GetWatchLo (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watchlo[index]; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watchlo[index]; + else + log->Printf("Invalid watch register style"); +} + +static void +SetWatchLo (struct pt_watch_regs *regs, uint32_t index, uint64_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + regs->mips32.watchlo[index] = (uint32_t) value; + else if (regs->style == pt_watch_style_mips64) + regs->mips64.watchlo[index] = value; + else + log->Printf("Invalid watch register style"); +} + +static uint32_t +GetIRWMask (struct pt_watch_regs *regs, uint32_t index) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watch_masks[index] & IRW; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watch_masks[index] & IRW; + else + log->Printf("Invalid watch register style"); +} + +static uint32_t +GetRegMask (struct pt_watch_regs *regs, int n) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (regs->style == pt_watch_style_mips32) + return regs->mips32.watch_masks[index] & ~IRW; + else if (regs->style == pt_watch_style_mips64) + return regs->mips64.watch_masks[index] & ~IRW; + else + log->Printf("Invalid watch register style"); +} + +static lldb::addr_t +GetRangeMask (lldb::addr_t mask) +{ + lldb::addr_t mask_bit = 1; + while (mask_bit < mask) + { + mask = mask | mask_bit; + mask_bit <<= 1; + } + return mask; +} + +static int +GetVacantWatchIndex (struct pt_watch_regs *regs, lldb::addr_t addr, uint32_t size, uint32_t irw, uint32_t num_valid) +{ + lldb::addr_t last_byte = addr + size - 1; + lldb::addr_t mask = GetRangeMask (addr ^ last_byte) | IRW; + lldb::addr_t base_addr = addr & ~mask; + + // Check if this address is already watched by previous watch points. + lldb::addr_t lo; + uint16_t hi; + uint32_t vacant_watches = 0; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo != 0 && irw == ((uint32_t) lo & irw)) + { + hi = GetWatchHi (regs, index) | IRW; + lo &= ~(lldb::addr_t) hi; + if (addr >= lo && last_byte <= (lo + hi)) + return index; + } + else + vacant_watches++; + } + + // Now try to find a vacant index + if(vacant_watches > 0) + { + vacant_watches = 0; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo == 0 + && irw == (GetIRWMask (regs, index) & irw)) + { + if (mask <= (GetRegMask (regs, index) | IRW)) + { + // It fits, we can use it. + SetWatchLo (regs, index, base_addr | irw); + SetWatchHi (regs, index, mask & ~IRW); + return index; + } + else + { + // It doesn't fit, but has the proper IRW capabilities + vacant_watches++; + } + } + } + + if (vacant_watches > 1) + { + // Split this watchpoint accross several registers + struct pt_watch_regs regs_copy; + regs_copy = *regs; + lldb::addr_t break_addr; + uint32_t segment_size; + for (uint32_t index = 0; index < num_valid; index++) + { + lo = GetWatchLo (®s_copy, index); + hi = GetRegMask (®s_copy, index) | IRW; + if (lo == 0 && irw == (hi & irw)) + { + lo = addr & ~(lldb::addr_t) hi; + break_addr = lo + hi + 1; + if (break_addr >= addr + size) + segment_size = size; + else + segment_size = break_addr - addr; + mask = GetRangeMask (addr ^ (addr + segment_size - 1)); + SetWatchLo (®s_copy, index, (addr & ~mask) | irw); + SetWatchHi (®s_copy, index, mask & ~IRW); + if (break_addr >= addr + size) + { + *regs = regs_copy; + return index; + } + size = addr + size - break_addr; + addr = break_addr; + } + } + } + } + return 0; +} + Error NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_hit) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + // reading the current state of watch regs + struct pt_watch_regs watch_readback; + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + Error error = process_p->ReadWatchPointRegisterValue(m_thread.GetID(),(void*)&watch_readback); + + + if (GetWatchHi (&watch_readback, wp_index) & (IRW)) + { + // clear hit flag in watchhi + SetWatchHi (&watch_readback, wp_index, (GetWatchHi (&watch_readback, wp_index) & ~(IRW))); + process_p->SetWatchPointRegisterValue(m_thread.GetID(), (void*)&watch_readback); + + is_hit = true; + return error; + } is_hit = false; - return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointHit not implemented"); + return error; +} + +Error +NativeRegisterContextLinux_mips64::GetWatchpointHitIndex(uint32_t &wp_index) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) + { + bool is_hit; + Error 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 Error(); } Error @@ -259,17 +510,42 @@ } bool -NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint (uint32_t wp_index) +NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + struct pt_watch_regs regs; + // First reading the current state of watch regs + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + process_p->ReadWatchPointRegisterValue(m_thread.GetID(),(void*)®s); + + if (regs.style = pt_watch_style_mips32) + { + regs.mips32.watchlo[wp_index] = default_watch_regs.mips32.watchlo[wp_index]; + regs.mips32.watchhi[wp_index] = default_watch_regs.mips32.watchhi[wp_index]; + regs.mips32.watch_masks[wp_index] = default_watch_regs.mips32.watch_masks[wp_index]; + } + else // pt_watch_style_mips64 + { + regs.mips64.watchlo[wp_index] = default_watch_regs.mips64.watchlo[wp_index]; + regs.mips64.watchhi[wp_index] = default_watch_regs.mips64.watchhi[wp_index]; + regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index]; + } + + Error error = process_p->SetWatchPointRegisterValue(m_thread.GetID(), (void*)®s); + if(!error.Fail()) + return true; return false; } Error -NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints () +NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints() { - Error error; - error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints not implemented"); - return error; + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + return process_p->SetWatchPointRegisterValue(m_thread.GetID(), (void*)&default_watch_regs); } Error @@ -285,7 +561,24 @@ NativeRegisterContextLinux_mips64::SetHardwareWatchpoint ( lldb::addr_t addr, size_t size, uint32_t watch_flags) { + struct pt_watch_regs regs; + + // First reading the current state of watch regs + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + process_p->ReadWatchPointRegisterValue(m_thread.GetID(),(void*)®s); + + // Try if a new watch point fits in this state + int index = GetVacantWatchIndex (®s, addr, size, watch_flags, NumSupportedHardwareWatchpoints()); + + // New watchpoint doesn't fit + if (!index) return LLDB_INVALID_INDEX32; + + + // It fits, so we go ahead with updating the state of watch regs + process_p->SetWatchPointRegisterValue(m_thread.GetID(),(void*)®s); + return index; } lldb::addr_t @@ -297,5 +590,33 @@ uint32_t NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints () { - return 0; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + struct pt_watch_regs regs; + static int num_valid = 0; + if (!num_valid) + { + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + { + printf ("NativeProcessProtocol is NULL"); + return 0; + } + + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + process_p->ReadWatchPointRegisterValue(m_thread.GetID(),(void*)®s); + default_watch_regs = regs; // Keeping default watch regs values for future use + switch (regs.style) + { + case pt_watch_style_mips32: + num_valid = regs.mips32.num_valid; // Using num_valid as cache + return num_valid; + case pt_watch_style_mips64: + num_valid = regs.mips64.num_valid; + return num_valid; + default: + log->Printf("NativeRegisterContextLinux_mips64::%s Error: Unrecognized watch register style", __FUNCTION__); + } + return 0; + } + return num_valid; } Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -183,7 +183,11 @@ else response.Printf("watchpoint_exceptions_received:after;"); #else - response.Printf("watchpoint_exceptions_received:after;"); + if (host_arch.GetMachine() == llvm::Triple::mips64 || + host_arch.GetMachine() == llvm::Triple::mips64el) + response.Printf("watchpoint_exceptions_received:before;"); + else + response.Printf("watchpoint_exceptions_received:after;"); #endif switch (lldb::endian::InlHostByteOrder()) Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -2108,10 +2108,25 @@ } else { - uint32_t watch_flags = - stoppoint_type == eWatchpointWrite - ? 0x1 // Write - : 0x3; // ReadWrite + uint32_t watch_flags; + switch(stoppoint_type) + { + case eWatchpointWrite: + watch_flags = 0x1; + break; + case eWatchpointRead: +#if defined(__x86_64__) + watch_flags = 0x3; +#else + watch_flags = 0x2; +#endif + break; + case eWatchpointReadWrite: + watch_flags = 0x3; + break; + default: + watch_flags = 0x1; + } // Try to set the watchpoint. const Error error = m_debugged_process_sp->SetWatchpoint (