[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 &reg_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 &reg_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)