[LLDB][MIPS] Handle false positives for MIPS hardware watchpoints
SUMMARY:
Last 3bits of the watchpoint address are masked by the kernel. For example, n is
at 0x120010d00 and m is 0x120010d04. When a watchpoint is set at m, then watch
exception is generated even when n is read/written. To handle this case, instruction
at PC is emulated to find the base address of the load/store instruction. This address
is then appended to the description of the stop-info packet. Client then reads this
information to check whether the user has set a watchpoint on this address.
Reviewers: jingham, clayborg
Subscribers: nitesh.jain, mohit.bhakkad, sagar, bhushan and lldb-commits
Differential Revision: http://reviews.llvm.org/D11672
llvm-svn: 244864
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp
index 917f135..6a4dfc0 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp
@@ -20,7 +20,9 @@
#include "lldb/Core/Log.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Host/HostInfo.h"
-
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-enumerations.h"
#include "Plugins/Process/Linux/NativeProcessLinux.h"
#include "Plugins/Process/Linux/Procfs.h"
#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h"
@@ -997,6 +999,106 @@
return hw_addr_map[wp_index];
}
+struct EmulatorBaton
+{
+ lldb::addr_t m_watch_hit_addr;
+ NativeProcessLinux* m_process;
+ NativeRegisterContext* m_reg_context;
+
+ EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) :
+ m_watch_hit_addr(LLDB_INVALID_ADDRESS),
+ m_process(process),
+ m_reg_context(reg_context)
+ {}
+};
+
+static size_t
+ReadMemoryCallback (EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ void *dst, size_t length)
+{
+ size_t bytes_read;
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+ emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read);
+ return bytes_read;
+}
+
+static size_t
+WriteMemoryCallback (EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length)
+{
+ return length;
+}
+
+static bool
+ReadRegisterCallback (EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info, RegisterValue ®_value)
+{
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+
+ const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo(
+ lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]);
+
+ Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+ if (error.Success())
+ return true;
+
+ return false;
+}
+
+static bool
+WriteRegisterCallback (EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info, const RegisterValue ®_value)
+{
+ if (reg_info->kinds[lldb::eRegisterKindDWARF] == gcc_dwarf_bad_mips64)
+ {
+ EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
+ emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64 ();
+ }
+
+ return true;
+}
+
+/*
+ * MIPS Linux kernel returns a masked address (last 3bits are masked)
+ * when a HW watchpoint is hit. However user may not have set a watchpoint
+ * on this address. Emulate instruction at PC and find the base address of
+ * the load/store instruction. This will give the exact address used to
+ * read/write the variable. Send this exact address to client so that
+ * it can decide to stop or continue the thread.
+*/
+lldb::addr_t
+NativeRegisterContextLinux_mips64::GetWatchpointHitAddress (uint32_t wp_index)
+{
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+
+ lldb_private::ArchSpec arch;
+ arch = GetRegisterInfoInterface().GetTargetArchitecture();
+ std::unique_ptr<EmulateInstruction> emulator_ap(
+ EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr));
+
+ if (emulator_ap == nullptr)
+ return LLDB_INVALID_ADDRESS;
+
+ EmulatorBaton baton(static_cast<NativeProcessLinux*>(m_thread.GetProcess().get()), this);
+ emulator_ap->SetBaton (&baton);
+ emulator_ap->SetReadMemCallback (&ReadMemoryCallback);
+ emulator_ap->SetReadRegCallback (&ReadRegisterCallback);
+ emulator_ap->SetWriteMemCallback (&WriteMemoryCallback);
+ emulator_ap->SetWriteRegCallback (&WriteRegisterCallback);
+
+ if (!emulator_ap->ReadInstruction())
+ return LLDB_INVALID_ADDRESS;
+
+ if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone))
+ return baton.m_watch_hit_addr;
+
+ return LLDB_INVALID_ADDRESS;
+}
+
uint32_t
NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints ()
{
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h
index 9a2e89f..1826187 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h
@@ -36,6 +36,9 @@
lldb::addr_t
GetPCfromBreakpointLocation (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS) override;
+ lldb::addr_t
+ GetWatchpointHitAddress (uint32_t wp_index) override;
+
const RegisterSet *
GetRegisterSet (uint32_t set_index) const override;
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
index 481127a..18f0e6b 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -333,6 +333,16 @@
std::ostringstream ostr;
ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " ";
ostr << wp_index;
+
+ /*
+ * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example:
+ * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then
+ * watch exception is generated even when 'n' is read/written. To handle this case,
+ * find the base address of the load/store instruction and append it in the stop-info
+ * packet.
+ */
+ ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index);
+
m_stop_description = ostr.str();
m_stop_info.reason = StopReason::eStopReasonWatchpoint;
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index f6236f0..4a9e285 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2020,6 +2020,7 @@
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);
+ addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
if (wp_addr != LLDB_INVALID_ADDRESS)
{
@@ -2035,7 +2036,7 @@
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS));
if (log) log->Printf ("failed to find watchpoint");
}
- thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id));
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id, wp_hit_addr));
handled = true;
}
else if (reason.compare("exception") == 0)