Report watchpoint hits during single stepping.
authorChaoren Lin <chaorenl@google.com>
Thu, 19 Mar 2015 23:28:10 +0000 (23:28 +0000)
committerChaoren Lin <chaorenl@google.com>
Thu, 19 Mar 2015 23:28:10 +0000 (23:28 +0000)
Summary:
Reorganized NativeProcessLinux::MonitorSIGTRAP to check for watchpoint hits on
TRAP_TRACE.

Added test for stepping over watchpoints.

https://llvm.org/bugs/show_bug.cgi?id=22814

Reviewers: ovyalov, tberghammer, vharron, clayborg

Subscribers: jingham, labath, lldb-commits

Differential Revision: http://reviews.llvm.org/D8404

llvm-svn: 232784

13 files changed:
lldb/include/lldb/Host/common/NativeRegisterContext.h
lldb/source/Host/common/NativeRegisterContext.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
lldb/test/functionalities/watchpoint/step_over_watchpoint/Makefile [new file with mode: 0644]
lldb/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py [new file with mode: 0644]
lldb/test/functionalities/watchpoint/step_over_watchpoint/main.c [new file with mode: 0644]

index b2d30c2..9197e6b 100644 (file)
@@ -100,10 +100,13 @@ public:
     ClearAllHardwareWatchpoints ();
 
     virtual Error
-    IsWatchpointHit (uint8_t wp_index);
+    IsWatchpointHit(uint32_t wp_index, bool &is_hit);
 
     virtual Error
-    IsWatchpointVacant (uint32_t wp_index);
+    GetWatchpointHitIndex(uint32_t &wp_index);
+
+    virtual Error
+    IsWatchpointVacant (uint32_t wp_index, bool &is_vacant);
 
     virtual lldb::addr_t
     GetWatchpointAddress (uint32_t wp_index);
index b8f811a..77f0911 100644 (file)
@@ -302,14 +302,23 @@ NativeRegisterContext::ClearAllHardwareWatchpoints ()
 }
 
 Error
-NativeRegisterContext::IsWatchpointHit (uint8_t wp_index)
+NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit)
 {
+    is_hit = false;
     return Error ("not implemented");
 }
 
 Error
-NativeRegisterContext::IsWatchpointVacant (uint32_t wp_index)
+NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index)
 {
+    wp_index = LLDB_INVALID_INDEX32;
+    return Error ("not implemented");
+}
+
+Error
+NativeRegisterContext::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant)
+{
+    is_vacant = false;
     return Error ("not implemented");
 }
 
index b0a2d6c..fd3a62e 100644 (file)
@@ -39,6 +39,7 @@
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/LLDBAssert.h"
 #include "lldb/Utility/PseudoTerminal.h"
 
 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
@@ -2251,93 +2252,31 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
     }
 
     case 0:
-    case TRAP_TRACE:
-        // We receive this on single stepping.
-        if (log)
-            log->Printf ("NativeProcessLinux::%s() received trace event, pid = %" PRIu64 " (single stepping)", __FUNCTION__, pid);
-
+    case TRAP_TRACE:  // We receive this on single stepping.
+    case TRAP_HWBKPT: // We receive this on watchpoint hit
         if (thread_sp)
         {
-            std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStoppedByTrace ();
-        }
-
-        // This thread is currently stopped.
-        NotifyThreadStop (pid);
-
-        // Here we don't have to request the rest of the threads to stop or request a deferred stop.
-        // This would have already happened at the time the Resume() with step operation was signaled.
-        // At this point, we just need to say we stopped, and the deferred notifcation will fire off
-        // once all running threads have checked in as stopped.
-        SetCurrentThreadID (pid);
-        // Tell the process we have a stop (from software breakpoint).
-        CallAfterRunningThreadsStop (pid,
-                                     [=] (lldb::tid_t signaling_tid)
-                                     {
-                                         SetState (StateType::eStateStopped, true);
-                                     });
-        break;
-
-    case SI_KERNEL:
-    case TRAP_BRKPT:
-        if (log)
-            log->Printf ("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64, __FUNCTION__, pid);
-
-        // This thread is currently stopped.
-        NotifyThreadStop (pid);
-
-        // Mark the thread as stopped at breakpoint.
-        if (thread_sp)
-        {
-            std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStoppedByBreakpoint ();
-            Error error = FixupBreakpointPCAsNeeded (thread_sp);
-            if (error.Fail ())
+            // 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)
             {
-                if (log)
-                    log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s", __FUNCTION__, pid, error.AsCString ());
+                MonitorWatchpoint(pid, thread_sp, wp_index);
+                break;
             }
         }
-        else
-        {
-            if (log)
-                log->Printf ("NativeProcessLinux::%s()  pid = %" PRIu64 ": warning, cannot process software breakpoint since no thread metadata", __FUNCTION__, pid);
-        }
-
-
-        // We need to tell all other running threads before we notify the delegate about this stop.
-        CallAfterRunningThreadsStop (pid,
-                                     [=](lldb::tid_t deferred_notification_tid)
-                                     {
-                                         SetCurrentThreadID (deferred_notification_tid);
-                                         // Tell the process we have a stop (from software breakpoint).
-                                         SetState (StateType::eStateStopped, true);
-                                     });
+        // Otherwise, report step over
+        MonitorTrace(pid, thread_sp);
         break;
 
-    case TRAP_HWBKPT:
-        if (log)
-            log->Printf ("NativeProcessLinux::%s() received watchpoint event, pid = %" PRIu64, __FUNCTION__, pid);
-
-        // This thread is currently stopped.
-        NotifyThreadStop (pid);
-
-        // Mark the thread as stopped at watchpoint.
-        // The address is at (lldb::addr_t)info->si_addr if we need it.
-        if (thread_sp)
-            std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStoppedByWatchpoint ();
-        else
-        {
-            if (log)
-                log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ": warning, cannot process hardware breakpoint since no thread metadata", __FUNCTION__, GetID (), pid);
-        }
-
-        // We need to tell all other running threads before we notify the delegate about this stop.
-        CallAfterRunningThreadsStop (pid,
-                                     [=](lldb::tid_t deferred_notification_tid)
-                                     {
-                                         SetCurrentThreadID (deferred_notification_tid);
-                                         // Tell the process we have a stop (from hardware breakpoint).
-                                         SetState (StateType::eStateStopped, true);
-                                     });
+    case SI_KERNEL:
+    case TRAP_BRKPT:
+        MonitorBreakpoint(pid, thread_sp);
         break;
 
     case SIGTRAP:
@@ -2371,6 +2310,98 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
 }
 
 void
+NativeProcessLinux::MonitorTrace(lldb::pid_t pid, NativeThreadProtocolSP thread_sp)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+    if (log)
+        log->Printf("NativeProcessLinux::%s() received trace event, pid = %" PRIu64 " (single stepping)",
+                __FUNCTION__, pid);
+
+    if (thread_sp)
+        std::static_pointer_cast<NativeThreadLinux>(thread_sp)->SetStoppedByTrace();
+
+    // This thread is currently stopped.
+    NotifyThreadStop(pid);
+
+    // Here we don't have to request the rest of the threads to stop or request a deferred stop.
+    // This would have already happened at the time the Resume() with step operation was signaled.
+    // At this point, we just need to say we stopped, and the deferred notifcation will fire off
+    // once all running threads have checked in as stopped.
+    SetCurrentThreadID(pid);
+    // Tell the process we have a stop (from software breakpoint).
+    CallAfterRunningThreadsStop(pid,
+                                [=](lldb::tid_t signaling_tid)
+                                {
+                                   SetState(StateType::eStateStopped, true);
+                                });
+}
+
+void
+NativeProcessLinux::MonitorBreakpoint(lldb::pid_t pid, NativeThreadProtocolSP thread_sp)
+{
+    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
+    if (log)
+        log->Printf("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64,
+                __FUNCTION__, pid);
+
+    // This thread is currently stopped.
+    NotifyThreadStop(pid);
+
+    // Mark the thread as stopped at breakpoint.
+    if (thread_sp)
+    {
+        std::static_pointer_cast<NativeThreadLinux>(thread_sp)->SetStoppedByBreakpoint();
+        Error error = FixupBreakpointPCAsNeeded(thread_sp);
+        if (error.Fail())
+            if (log)
+                log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s",
+                        __FUNCTION__, pid, error.AsCString());
+    }
+    else
+        if (log)
+            log->Printf("NativeProcessLinux::%s()  pid = %" PRIu64 ": "
+                    "warning, cannot process software breakpoint since no thread metadata",
+                    __FUNCTION__, pid);
+
+
+    // We need to tell all other running threads before we notify the delegate about this stop.
+    CallAfterRunningThreadsStop(pid,
+                                [=](lldb::tid_t deferred_notification_tid)
+                                {
+                                    SetCurrentThreadID(deferred_notification_tid);
+                                    // Tell the process we have a stop (from software breakpoint).
+                                    SetState(StateType::eStateStopped, true);
+                                });
+}
+
+void
+NativeProcessLinux::MonitorWatchpoint(lldb::pid_t pid, NativeThreadProtocolSP thread_sp, uint32_t wp_index)
+{
+    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS));
+    if (log)
+        log->Printf("NativeProcessLinux::%s() received watchpoint event, "
+                    "pid = %" PRIu64 ", wp_index = %" PRIu32,
+                    __FUNCTION__, pid, wp_index);
+
+    // This thread is currently stopped.
+    NotifyThreadStop(pid);
+
+    // Mark the thread as stopped at watchpoint.
+    // The address is at (lldb::addr_t)info->si_addr if we need it.
+    lldbassert(thread_sp && "thread_sp cannot be NULL");
+    std::static_pointer_cast<NativeThreadLinux>(thread_sp)->SetStoppedByWatchpoint(wp_index);
+
+    // We need to tell all other running threads before we notify the delegate about this stop.
+    CallAfterRunningThreadsStop(pid,
+                                [=](lldb::tid_t deferred_notification_tid)
+                                {
+                                    SetCurrentThreadID(deferred_notification_tid);
+                                    // Tell the process we have a stop (from watchpoint).
+                                    SetState(StateType::eStateStopped, true);
+                                });
+}
+
+void
 NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool exited)
 {
     assert (info && "null info");
index a2ffd6c..07220e9 100644 (file)
@@ -296,6 +296,15 @@ namespace lldb_private
         MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid);
 
         void
+        MonitorTrace(lldb::pid_t pid, NativeThreadProtocolSP thread_sp);
+
+        void
+        MonitorBreakpoint(lldb::pid_t pid, NativeThreadProtocolSP thread_sp);
+
+        void
+        MonitorWatchpoint(lldb::pid_t pid, NativeThreadProtocolSP thread_sp, uint32_t wp_index);
+
+        void
         MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool exited);
 
 #if 0
index 67363ca..3d798e6 100644 (file)
@@ -155,19 +155,17 @@ NativeRegisterContextLinux_mips64::WriteAllRegisterValues (const lldb::DataBuffe
 }
 
 Error
-NativeRegisterContextLinux_mips64::IsWatchpointHit (uint8_t wp_index)
+NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_hit)
 {
-    Error error;
-    error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointHit not implemented");
-    return error;
+    is_hit = false;
+    return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointHit not implemented");
 }
 
 Error
-NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index)
+NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant)
 {
-    Error error;
-    error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointVacant not implemented");
-    return error;
+    is_vacant = false;
+    return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointVacant not implemented");
 }
 
 bool
index b783343..35218b7 100644 (file)
@@ -91,10 +91,10 @@ namespace lldb_private
         WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
 
         Error
-        IsWatchpointHit (uint8_t wp_index) override;
+        IsWatchpointHit (uint32_t wp_index, bool &is_hit) override;
 
         Error
-        IsWatchpointVacant (uint32_t wp_index) override;
+        IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) override;
 
         bool
         ClearHardwareWatchpoint (uint32_t wp_index) override;
index fa6dfd6..06ad5f2 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "NativeRegisterContextLinux_x86_64.h"
 
+#include "lldb/Core/Log.h"
 #include "lldb/lldb-private-forward.h"
 #include "lldb/Core/DataBufferHeap.h"
 #include "lldb/Core/Error.h"
@@ -1046,39 +1047,61 @@ NativeRegisterContextLinux_x86_64::WriteGPR()
 }
 
 Error
-NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint8_t wp_index)
+NativeRegisterContextLinux_x86_64::IsWatchpointHit(uint32_t wp_index, bool &is_hit)
 {
     if (wp_index >= NumSupportedHardwareWatchpoints())
-        return Error ("Watchpoint index out of range");
+        return Error("Watchpoint index out of range");
 
     RegisterValue reg_value;
     Error error = ReadRegisterRaw(m_reg_info.first_dr + 6, reg_value);
-    if (error.Fail()) return error;
+    if (error.Fail())
+    {
+        is_hit = false;
+        return error;
+    }
 
     uint64_t status_bits = reg_value.GetAsUInt64();
 
-    bool is_hit = status_bits & (1 << wp_index);
-
-    error.SetError (!is_hit, lldb::eErrorTypeInvalid);
+    is_hit = status_bits & (1 << wp_index);
 
     return error;
 }
 
 Error
-NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index)
+NativeRegisterContextLinux_x86_64::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
+NativeRegisterContextLinux_x86_64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant)
 {
     if (wp_index >= NumSupportedHardwareWatchpoints())
         return Error ("Watchpoint index out of range");
 
     RegisterValue reg_value;
     Error error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value);
-    if (error.Fail()) return error;
+    if (error.Fail())
+    {
+        is_vacant = false;
+        return error;
+    }
 
     uint64_t control_bits = reg_value.GetAsUInt64();
 
-    bool is_vacant = !(control_bits & (1 << (2 * wp_index)));
-
-    error.SetError (!is_vacant, lldb::eErrorTypeInvalid);
+    is_vacant = !(control_bits & (1 << (2 * wp_index)));
 
     return error;
 }
@@ -1096,8 +1119,10 @@ NativeRegisterContextLinux_x86_64::SetHardwareWatchpointWithIndex(
     if (size != 1 && size != 2 && size != 4 && size != 8)
         return Error ("Invalid size for watchpoint");
 
-    Error error = IsWatchpointVacant (wp_index);
+    bool is_vacant;
+    Error error = IsWatchpointVacant (wp_index, is_vacant);
     if (error.Fail()) return error;
+    if (!is_vacant) return Error("Watchpoint index not vacant");
 
     RegisterValue reg_value;
     error = ReadRegisterRaw(m_reg_info.first_dr + 7, reg_value);
@@ -1184,14 +1209,24 @@ uint32_t
 NativeRegisterContextLinux_x86_64::SetHardwareWatchpoint(
         lldb::addr_t addr, size_t size, uint32_t watch_flags)
 {
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
     const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
     for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index)
-        if (IsWatchpointVacant(wp_index).Success())
+    {
+        bool is_vacant;
+        Error error = IsWatchpointVacant(wp_index, is_vacant);
+        if (is_vacant)
         {
-            if (SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index).Fail())
-                continue;
-            return wp_index;
+            error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);
+            if (error.Success())
+                return wp_index;
         }
+        if (error.Fail() && log)
+        {
+            log->Printf("NativeRegisterContextLinux_x86_64::%s Error: %s",
+                    __FUNCTION__, error.AsCString());
+        }
+    }
     return LLDB_INVALID_INDEX32;
 }
 
index 0e9d721..e183118 100644 (file)
@@ -46,10 +46,13 @@ namespace lldb_private
         WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override;
 
         Error
-        IsWatchpointHit(uint8_t wp_index) override;
+        IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
 
         Error
-        IsWatchpointVacant(uint32_t wp_index) override;
+        GetWatchpointHitIndex(uint32_t &wp_index) override;
+
+        Error
+        IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
 
         bool
         ClearHardwareWatchpoint(uint32_t wp_index) override;
index 148f55f..51597f8 100644 (file)
@@ -22,6 +22,7 @@
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Host/HostNativeThread.h"
+#include "lldb/Utility/LLDBAssert.h"
 #include "lldb/lldb-enumerations.h"
 
 #include "llvm/ADT/SmallString.h"
@@ -379,53 +380,23 @@ NativeThreadLinux::SetStoppedByBreakpoint ()
 }
 
 void
-NativeThreadLinux::SetStoppedByWatchpoint ()
+NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index)
 {
-    Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
-    lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
-    if (log)
-    {
-        NativeProcessProtocolSP process_sp = m_process_wp.lock ();
-        if (process_sp)
-            pid = process_sp->GetID ();
-    }
-
     const StateType new_state = StateType::eStateStopped;
     MaybeLogStateChange (new_state);
     m_state = new_state;
-
-    NativeRegisterContextSP reg_ctx = GetRegisterContext ();
-    const uint32_t num_hw_watchpoints = reg_ctx->NumSupportedHardwareWatchpoints ();
-
     m_stop_description.clear ();
-    for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index)
-    {
-        if (reg_ctx->IsWatchpointHit (wp_index).Success())
-        {
-            if (log)
-                log->Printf ("NativeThreadLinux:%s (pid=%" PRIu64 ", tid=%" PRIu64 ") watchpoint found with idx: %u",
-                             __FUNCTION__, pid, GetID (), wp_index);
-
-            std::ostringstream ostr;
-            ostr << reg_ctx->GetWatchpointAddress (wp_index) << " " << wp_index;
-            m_stop_description = ostr.str();
-
-            m_stop_info.reason = StopReason::eStopReasonWatchpoint;
-            m_stop_info.details.signal.signo = SIGTRAP;
-            return;
-        }
-    }
 
-    // The process reported a watchpoint was hit, but we haven't found the
-    // watchpoint. Assume that a stopped by trace is reported as a hardware
-    // watchpoint what happens on some linux kernels (e.g.: android-arm64
-    // platfrom-21).
+    lldbassert(wp_index != LLDB_INVALID_INDEX32 &&
+               "wp_index cannot be invalid");
 
-    if (log)
-        log->Printf ("NativeThreadLinux:%s (pid=%" PRIu64 ", tid=%" PRIu64 ") none of the watchpoint was hit.",
-                     __FUNCTION__, pid, GetID ());
+    std::ostringstream ostr;
+    ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " ";
+    ostr << wp_index;
+    m_stop_description = ostr.str();
 
-    SetStoppedByTrace ();
+    m_stop_info.reason = StopReason::eStopReasonWatchpoint;
+    m_stop_info.details.signal.signo = SIGTRAP;
 }
 
 bool
index 2519b5b..c77dcbe 100644 (file)
@@ -76,7 +76,7 @@ namespace lldb_private
         SetStoppedByBreakpoint ();
 
         void
-        SetStoppedByWatchpoint ();
+        SetStoppedByWatchpoint (uint32_t wp_index);
 
         bool
         IsStoppedAtBreakpoint ();
diff --git a/lldb/test/functionalities/watchpoint/step_over_watchpoint/Makefile b/lldb/test/functionalities/watchpoint/step_over_watchpoint/Makefile
new file mode 100644 (file)
index 0000000..b09a579
--- /dev/null
@@ -0,0 +1,5 @@
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py b/lldb/test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py
new file mode 100644 (file)
index 0000000..30947f4
--- /dev/null
@@ -0,0 +1,124 @@
+"""Test stepping over watchpoints."""
+
+import unittest2
+import lldb
+import lldbutil
+from lldbtest import *
+
+
+class TestStepOverWatchpoint(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def getCategories(self):
+        return ['basic_process']
+
+    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+    @dsym_test
+    def test_with_dsym(self):
+        """Test stepping over watchpoints."""
+        self.buildDsym()
+        self.step_over_watchpoint()
+
+    @dwarf_test
+    def test_with_dwarf(self):
+        """Test stepping over watchpoints."""
+        self.buildDwarf()
+        self.step_over_watchpoint()
+
+    def setUp(self):
+        TestBase.setUp(self)
+
+    def step_inst_for_watchpoint(self, wp_id):
+        watchpoint_hit = False
+        current_line = self.frame().GetLineEntry().GetLine()
+        while self.frame().GetLineEntry().GetLine() == current_line:
+            self.thread().StepInstruction(False)  # step_over=False
+            stop_reason = self.thread().GetStopReason()
+            if stop_reason == lldb.eStopReasonWatchpoint:
+                self.assertFalse(watchpoint_hit, "Watchpoint already hit.")
+                expected_stop_desc = "watchpoint %d" % wp_id
+                actual_stop_desc = self.thread().GetStopDescription(20)
+                self.assertTrue(actual_stop_desc == expected_stop_desc,
+                                "Watchpoint ID didn't match.")
+                watchpoint_hit = True
+            else:
+                self.assertTrue(stop_reason == lldb.eStopReasonPlanComplete,
+                                STOPPED_DUE_TO_STEP_IN)
+        self.assertTrue(watchpoint_hit, "Watchpoint never hit.")
+
+    def step_over_watchpoint(self):
+        """Test stepping over watchpoints."""
+        exe = os.path.join(os.getcwd(), 'a.out')
+
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(self.target, VALID_TARGET)
+
+        lldbutil.run_break_set_by_symbol(self, 'main')
+
+        process = target.LaunchSimple(None, None,
+                                      self.get_process_working_directory())
+        self.assertTrue(process.IsValid(), PROCESS_IS_VALID)
+        self.assertTrue(process.GetState() == lldb.eStateStopped,
+                        PROCESS_STOPPED)
+
+        thread = lldbutil.get_stopped_thread(process,
+                                             lldb.eStopReasonBreakpoint)
+        self.assertTrue(thread.IsValid(), "Failed to get thread.")
+
+        frame = thread.GetFrameAtIndex(0)
+        self.assertTrue(frame.IsValid(), "Failed to get frame.")
+
+        read_value = frame.FindValue('g_watch_me_read',
+                                     lldb.eValueTypeVariableGlobal)
+        self.assertTrue(read_value.IsValid(), "Failed to find read value.")
+
+        error = lldb.SBError()
+
+        # resolve_location=True, read=True, write=False
+        read_watchpoint = read_value.Watch(True, True, False, error)
+        self.assertTrue(error.Success(),
+                        "Error while setting watchpoint: %s" %
+                        error.GetCString())
+        self.assertTrue(read_watchpoint, "Failed to set read watchpoint.")
+
+        write_value = frame.FindValue('g_watch_me_write',
+                                      lldb.eValueTypeVariableGlobal)
+        self.assertTrue(write_value, "Failed to find write value.")
+
+        # resolve_location=True, read=False, write=True
+        write_watchpoint = write_value.Watch(True, False, True, error)
+        self.assertTrue(read_watchpoint, "Failed to set write watchpoint.")
+        self.assertTrue(error.Success(),
+                        "Error while setting watchpoint: %s" %
+                        error.GetCString())
+
+        thread.StepOver()
+        self.assertTrue(thread.GetStopReason() == lldb.eStopReasonWatchpoint,
+                        STOPPED_DUE_TO_WATCHPOINT)
+        self.assertTrue(thread.GetStopDescription(20) == 'watchpoint 1')
+
+        process.Continue()
+        self.assertTrue(process.GetState() == lldb.eStateStopped,
+                        PROCESS_STOPPED)
+        self.assertTrue(thread.GetStopDescription(20) == 'step over')
+
+        self.step_inst_for_watchpoint(1)
+
+        thread.StepOver()
+        self.assertTrue(thread.GetStopReason() == lldb.eStopReasonWatchpoint,
+                        STOPPED_DUE_TO_WATCHPOINT)
+        self.assertTrue(thread.GetStopDescription(20) == 'watchpoint 2')
+
+        process.Continue()
+        self.assertTrue(process.GetState() == lldb.eStateStopped,
+                        PROCESS_STOPPED)
+        self.assertTrue(thread.GetStopDescription(20) == 'step over')
+
+        self.step_inst_for_watchpoint(2)
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()
diff --git a/lldb/test/functionalities/watchpoint/step_over_watchpoint/main.c b/lldb/test/functionalities/watchpoint/step_over_watchpoint/main.c
new file mode 100644 (file)
index 0000000..2d87d9a
--- /dev/null
@@ -0,0 +1,19 @@
+char g_watch_me_read;
+char g_watch_me_write;
+char g_temp;
+
+void watch_read() {
+    g_temp = g_watch_me_read;
+}
+
+void watch_write() {
+    g_watch_me_write = g_temp;
+}
+
+int main() {
+    watch_read();
+    g_temp = g_watch_me_read;
+    watch_write();
+    g_watch_me_write = g_temp;
+    return 0;
+}