[LLDB][MIPS] Hardware Watchpoints for MIPS

Reviewers: clayborg, jingham.
Subscribers: jaydeep, bhushan, dsanders, sagar, lldb-commits.
Differential Revision: http://reviews.llvm.org/D9142

llvm-svn: 239991
This commit is contained in:
Mohit K. Bhakkad 2015-06-18 04:53:18 +00:00
parent 58e5bdb091
commit 3579996399
4 changed files with 449 additions and 9 deletions

View File

@ -2011,6 +2011,27 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
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, NULL);
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;

View File

@ -26,6 +26,53 @@
#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h"
#include "Plugins/Process/Utility/RegisterContextLinux_mips.h"
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#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;
};
};
#define PTRACE_GET_WATCH_REGS 0xd0
#define PTRACE_SET_WATCH_REGS 0xd1
#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;
@ -281,6 +328,47 @@ namespace
const RegisterValue &m_value;
};
//------------------------------------------------------------------------------
/// @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;
};
//------------------------------------------------------------------------------
/// @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;
};
} // end of anonymous namespace
void
@ -310,6 +398,18 @@ WriteRegOperation::Execute(NativeProcessLinux *monitor)
}
}
void
ReadWatchPointRegOperation::Execute(NativeProcessLinux *monitor)
{
NativeProcessLinux::PtraceWrapper(PTRACE_GET_WATCH_REGS, m_tid, m_watch_readback, NULL, NULL, m_error);
}
void
SetWatchPointRegOperation::Execute(NativeProcessLinux *monitor)
{
NativeProcessLinux::PtraceWrapper(PTRACE_SET_WATCH_REGS, m_tid, m_watch_reg_value, NULL, NULL, m_error);
}
NativeRegisterContextLinux*
NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch,
NativeThreadProtocol &native_thread,
@ -373,6 +473,10 @@ NativeRegisterContextLinux_mips64::NativeRegisterContextLinux_mips64 (const Arch
// Clear out the FPR state.
::memset(&m_fpr, 0, sizeof(FPR_mips));
// init h/w watchpoint addr map
for (int index = 0;index <= MAX_NUM_WP; index++)
hw_addr_map[index] = LLDB_INVALID_ADDRESS;
}
uint32_t
@ -655,11 +759,216 @@ NativeRegisterContextLinux_mips64::IsFPR(uint32_t reg_index) const
return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr);
}
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, 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 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 (&regs_copy, index);
hi = GetRegMask (&regs_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 (&regs_copy, index, (addr & ~mask) | irw);
SetWatchHi (&regs_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<NativeProcessLinux*> (process_sp.get ());
Error error = process_p->DoOperation(GetReadWatchPointRegisterValueOperation(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->DoOperation(GetWriteWatchPointRegisterValueOperation(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, lldb::addr_t trap_addr) {
uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
for (wp_index = 0; wp_index < num_hw_wps; ++wp_index)
{
bool is_hit;
Error error = IsWatchpointHit(wp_index, is_hit);
if (error.Fail()) {
wp_index = LLDB_INVALID_INDEX32;
} else if (is_hit) {
return error;
}
}
wp_index = LLDB_INVALID_INDEX32;
return Error();
}
Error
@ -670,17 +979,45 @@ NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index, bool &
}
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<NativeProcessLinux*> (process_sp.get ());
process_p->DoOperation(GetReadWatchPointRegisterValueOperation(m_thread.GetID(),(void*)&regs));
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->DoOperation(GetWriteWatchPointRegisterValueOperation(m_thread.GetID(), (void*)&regs));
if(!error.Fail())
{
hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS;
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<NativeProcessLinux*> (process_sp.get ());
return process_p->DoOperation(GetWriteWatchPointRegisterValueOperation(m_thread.GetID(), (void*)&default_watch_regs));
}
Error
@ -696,19 +1033,70 @@ uint32_t
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<NativeProcessLinux*> (process_sp.get ());
process_p->DoOperation(GetReadWatchPointRegisterValueOperation(m_thread.GetID(),(void*)&regs));
// Try if a new watch point fits in this state
int index = GetVacantWatchIndex (&regs, 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->DoOperation(GetWriteWatchPointRegisterValueOperation(m_thread.GetID(), (void*)&regs));
// Storing exact address
hw_addr_map[index] = addr;
return index;
}
lldb::addr_t
NativeRegisterContextLinux_mips64::GetWatchpointAddress (uint32_t wp_index)
{
if (wp_index >= NumSupportedHardwareWatchpoints())
return LLDB_INVALID_ADDRESS;
return hw_addr_map[wp_index];
}
uint32_t
NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints ()
{
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<NativeProcessLinux*> (process_sp.get ());
process_p->DoOperation(GetReadWatchPointRegisterValueOperation(m_thread.GetID(),(void*)&regs));
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;
}
NativeProcessLinux::OperationUP
@ -728,4 +1116,16 @@ NativeRegisterContextLinux_mips64::GetWriteRegisterValueOperation(uint32_t offse
return NativeProcessLinux::OperationUP(new WriteRegOperation(m_thread.GetID(), offset, reg_name, value));
}
NativeProcessLinux::OperationUP
NativeRegisterContextLinux_mips64::GetReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback)
{
return NativeProcessLinux::OperationUP(new ReadWatchPointRegOperation(m_thread.GetID(), watch_readback));
}
NativeProcessLinux::OperationUP
NativeRegisterContextLinux_mips64::GetWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value)
{
return NativeProcessLinux::OperationUP(new SetWatchPointRegOperation(m_thread.GetID(), watch_readback));
}
#endif // defined (__mips__)

View File

@ -16,6 +16,8 @@
#include "Plugins/Process/Utility/RegisterContext_mips64.h"
#include "Plugins/Process/Utility/lldb-mips64-register-enums.h"
#define MAX_NUM_WP 8
namespace lldb_private {
namespace process_linux {
@ -55,6 +57,9 @@ namespace process_linux {
Error
IsWatchpointHit (uint32_t wp_index, bool &is_hit) override;
Error
GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;
Error
IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) override;
@ -90,6 +95,14 @@ namespace process_linux {
const char* reg_name,
const RegisterValue &value) override;
NativeProcessLinux::OperationUP
GetReadWatchPointRegisterValue(lldb::tid_t tid,
void* watch_readback);
NativeProcessLinux::OperationUP
GetWriteWatchPointRegisterValue(lldb::tid_t tid,
void* watch_readback);
bool
IsFR0();
@ -123,6 +136,8 @@ namespace process_linux {
uint64_t m_gpr_mips64[k_num_gpr_registers_mips64];
FPR_mips m_fpr;
lldb::addr_t hw_addr_map[MAX_NUM_WP];
};
} // namespace process_linux

View File

@ -185,6 +185,10 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo (StringExtractorGDBRemote &
else
response.Printf("watchpoint_exceptions_received:after;");
#else
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