Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -125,6 +125,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 @@ -64,6 +64,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 @@ -948,6 +953,60 @@ PTRACE(PTRACE_SETREGSET, m_tid, (void *)&m_regset, m_buf, m_buf_size, m_error); } + //------------------------------------------------------------------------------ + /// @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. @@ -3686,6 +3745,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 @@ -15,6 +15,59 @@ #include "Plugins/Process/Utility/RegisterContext_mips64.h" #include "Plugins/Process/Utility/RegisterContextLinux_mips64.h" +#include +#include + +#ifndef PTRACE_GET_WATCH_REGS +enum pt_watch_style +{ + pt_watch_style_mips32, + pt_watch_style_mips64 +}; +struct mips32_watch_regs +{ + unsigned int watchlo[8]; + /* Lower 16 bits of watchhi. */ + unsigned short watchhi[8]; + /* Valid mask and I R W bits. + * bit 0 -- 1 if W bit is usable. + * bit 1 -- 1 if R bit is usable. + * bit 2 -- 1 if I bit is usable. + * bits 3 - 11 -- Valid watchhi mask bits. + */ + unsigned short watch_masks[8]; + /* The number of valid watch register pairs. */ + unsigned int num_valid; +} __attribute__((aligned(8))); + +struct mips64_watch_regs +{ + unsigned long long watchlo[8]; + unsigned short watchhi[8]; + unsigned short watch_masks[8]; + unsigned int 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_BIT 0 +#define R_BIT 1 +#define I_BIT 2 + +#define W_MASK (1 << W_BIT) +#define R_MASK (1 << R_BIT) +#define I_MASK (1 << I_BIT) + +#define IRW_MASK (I_MASK | R_MASK | W_MASK) namespace lldb_private { namespace process_linux { 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" @@ -244,11 +244,219 @@ return error; } +uint32_t +GetWatchHi (struct pt_watch_regs *regs, int n) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + switch (regs->style) + { + case pt_watch_style_mips32: + return regs->mips32.watchhi[n]; + case pt_watch_style_mips64: + return regs->mips64.watchhi[n]; + default: + log->Printf("Unrecognized watch register style"); + } +} + +void +SetWatchHi (struct pt_watch_regs *regs, int n, uint16_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + switch (regs->style) + { + case pt_watch_style_mips32: + regs->mips32.watchhi[n] = value; + break; + case pt_watch_style_mips64: + regs->mips64.watchhi[n] = value; + break; + default: + log->Printf("Unrecognized watch register style"); + } +} + +lldb::addr_t +GetWatchLo (struct pt_watch_regs *regs, int n) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + switch (regs->style) + { + case pt_watch_style_mips32: + return regs->mips32.watchlo[n]; + case pt_watch_style_mips64: + return regs->mips64.watchlo[n]; + default: + log->Printf("Unrecognized watch register style"); + } +} + +void +SetWatchLo (struct pt_watch_regs *regs, int n, uint32_t value) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + switch (regs->style) + { + case pt_watch_style_mips32: + regs->mips32.watchlo[n] = (uint32_t) value; + break; + case pt_watch_style_mips64: + regs->mips64.watchlo[n] = value; + break; + default: + log->Printf("Unrecognized watch register style"); + } +} + +uint32_t +GetIRWMask (struct pt_watch_regs *regs, int n) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + switch (regs->style) + { + case pt_watch_style_mips32: + return regs->mips32.watch_masks[n] & IRW_MASK; + case pt_watch_style_mips64: + return regs->mips64.watch_masks[n] & IRW_MASK; + default: + log->Printf("Unrecognized watch register style"); + } +} + +static uint32_t +GetRegMask (struct pt_watch_regs *regs, int n) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + switch (regs->style) + { + case pt_watch_style_mips32: + return regs->mips32.watch_masks[n] & ~IRW_MASK; + case pt_watch_style_mips64: + return regs->mips64.watch_masks[n] & ~IRW_MASK; + default: + log->Printf("Unrecognized watch register style"); + } +} + +static lldb::addr_t +FillMask (lldb::addr_t mask) +{ + lldb::addr_t f = 1; + + while (f && f < mask) + { + mask |= f; + f <<= 1; + } + return mask; +} + +int +GetVacantWatchIndex (struct pt_watch_regs *regs, lldb::addr_t addr, int size, uint32_t irw, int num_valid) +{ + lldb::addr_t last_byte = addr + size - 1; + lldb::addr_t mask = FillMask (addr ^ last_byte) | IRW_MASK; + 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; + int vacant_watches = 0; + for (int index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo != 0 && irw == ((uint32_t) lo & irw)) + { + hi = GetWatchHi (regs, index) | IRW_MASK; + 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 (int index = 0; index < num_valid; index++) + { + lo = GetWatchLo (regs, index); + if (lo == 0 + && irw == (GetIRWMask (regs, index) & irw)) + { + if (mask <= (GetRegMask (regs, index) | IRW_MASK)) + { + // It fits, we can use it. + SetWatchLo (regs, index, base_addr | irw); + SetWatchHi (regs, index, mask & ~IRW_MASK); + 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; + int segment_size; + for (int index = 0; index < num_valid; index++) + { + lo = GetWatchLo (®s_copy, index); + hi = GetRegMask (®s_copy, index) | IRW_MASK; + 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 = FillMask (addr ^ (addr + segment_size - 1)); + SetWatchLo (®s_copy, index, (addr & ~mask) | irw); + SetWatchHi (®s_copy, index, mask & ~IRW_MASK); + 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) & (I_MASK |R_MASK | W_MASK)) + { + is_hit = true; + return error; + } is_hit = false; - return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointHit not implemented"); + return error; } Error @@ -285,7 +493,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 +522,32 @@ 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); + 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/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 (