Index: include/lldb/Host/common/NativeRegisterContext.h =================================================================== --- include/lldb/Host/common/NativeRegisterContext.h +++ include/lldb/Host/common/NativeRegisterContext.h @@ -100,10 +100,13 @@ 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: source/Host/common/NativeRegisterContext.cpp =================================================================== --- source/Host/common/NativeRegisterContext.cpp +++ source/Host/common/NativeRegisterContext.cpp @@ -304,13 +304,19 @@ } Error -NativeRegisterContext::IsWatchpointHit (uint8_t wp_index) +NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { return Error ("not implemented"); } Error -NativeRegisterContext::IsWatchpointVacant (uint32_t wp_index) +NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index) +{ + return Error ("not implemented"); +} + +Error +NativeRegisterContext::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) { return Error ("not implemented"); } Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -296,6 +296,15 @@ 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); + + void MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool exited); #if 0 Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2253,91 +2253,37 @@ 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); - if (thread_sp) { - std::static_pointer_cast (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); + // If a watchpoint was hit, report that instead + uint32_t wp_index; + thread_sp->GetRegisterContext()->GetWatchpointHitIndex(wp_index); + if (wp_index != LLDB_INVALID_INDEX32) + { + MonitorWatchpoint(pid, thread_sp); + break; + } - // Mark the thread as stopped at breakpoint. - if (thread_sp) - { - std::static_pointer_cast (thread_sp)->SetStoppedByBreakpoint (); - Error error = FixupBreakpointPCAsNeeded (thread_sp); - if (error.Fail ()) + // If a breakpoint was hit, report that instead + addr_t pc = thread_sp->GetRegisterContext()->GetPC(LLDB_INVALID_ADDRESS); + NativeBreakpointSP bp_sp; + m_breakpoint_list.GetBreakpoint(pc, bp_sp); + if (bp_sp && bp_sp->IsEnabled()) { - if (log) - log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s", __FUNCTION__, pid, error.AsCString ()); + MonitorBreakpoint(pid, thread_sp); + break; } } - else - { - if (log) - log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 ": warning, cannot process software breakpoint since no thread metadata", __FUNCTION__, pid); - } - + MonitorTrace(pid, thread_sp); + break; - // 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); - }); + case SI_KERNEL: + case TRAP_BRKPT: + MonitorBreakpoint(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 (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); - }); + MonitorWatchpoint(pid, thread_sp); break; case SIGTRAP: @@ -2371,6 +2317,102 @@ } 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(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(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) +{ + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS)); + 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(thread_sp)->SetStoppedByWatchpoint(); + else + if (log) + log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ": " + "warning, cannot process watchpoint 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 watchpoint). + SetState(StateType::eStateStopped, true); + }); +} + +void NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool exited) { assert (info && "null info"); Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h @@ -91,10 +91,10 @@ 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: source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -155,7 +155,7 @@ } 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"); @@ -163,7 +163,7 @@ } 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"); Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -46,10 +46,13 @@ 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: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -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 @@ } 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 @@ 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 @@ 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: source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -396,25 +396,23 @@ 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) + uint32_t wp_index; + Error error = reg_ctx->GetWatchpointHitIndex(wp_index); + if (wp_index != LLDB_INVALID_INDEX32) { - 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); + 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(); + 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; - } + 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 @@ -423,8 +421,9 @@ // platfrom-21). if (log) - log->Printf ("NativeThreadLinux:%s (pid=%" PRIu64 ", tid=%" PRIu64 ") none of the watchpoint was hit.", - __FUNCTION__, pid, GetID ()); + log->Printf ("NativeThreadLinux:%s (pid=%" PRIu64 ", tid=%" PRIu64 ") " + "none of the watchpoint was hit. Error: %s", + __FUNCTION__, pid, GetID (), error.AsCString()); SetStoppedByTrace (); } Index: test/functionalities/watchpoint/step_over_watchpoint/Makefile =================================================================== --- /dev/null +++ test/functionalities/watchpoint/step_over_watchpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py =================================================================== --- /dev/null +++ test/functionalities/watchpoint/step_over_watchpoint/TestStepOverWatchpoint.py @@ -0,0 +1,82 @@ +"""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_over_watchpoint(self): + """Test stepping over watchpoints.""" + exe = os.path.join(os.getcwd(), 'a.out') + + self.runCmd("target create " + exe, CURRENT_EXECUTABLE_SET) + self.runCmd("breakpoint set --name main", BREAKPOINT_CREATED) + + self.runCmd("process launch", RUN_SUCCEEDED) + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + self.expect("watchpoint set variable --watch read g_watch_me_read", + WATCHPOINT_CREATED, + substrs=['Watchpoint created', 'type = r']) + + self.expect("watchpoint set variable --watch write g_watch_me_write", + WATCHPOINT_CREATED, + substrs=['Watchpoint created', 'type = w']) + + self.expect("thread step-over") + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stopped', 'watch_read', + 'stop reason = watchpoint 1']) + + self.expect("process continue", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = step over']) + + self.expect("thread step-over") + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stopped', 'stop reason = watchpoint 1']) + + self.expect("process continue", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = step over']) + + self.expect("thread step-over") + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stopped', 'watch_write', + 'stop reason = watchpoint 2']) + + self.expect("process continue", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = step over']) + + self.expect("thread step-over") + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stopped', 'stop reason = watchpoint 2']) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: test/functionalities/watchpoint/step_over_watchpoint/main.c =================================================================== --- /dev/null +++ test/functionalities/watchpoint/step_over_watchpoint/main.c @@ -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; +}