diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 96ca95ad233ff..1efef93b17ded 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -1163,13 +1163,20 @@ class Thread : public std::enable_shared_from_this, void CalculatePublicStopInfo(); - // Ask the thread subclass to set its stop info. - // - // Thread subclasses should call Thread::SetStopInfo(...) with the reason the - // thread stopped. - // - // \return - // True if Thread::SetStopInfo(...) was called, false otherwise. + /// Ask the thread subclass to set its stop info. + /// + /// Thread subclasses should call Thread::SetStopInfo(...) with the reason the + /// thread stopped. + /// + /// A thread that is sitting at a breakpoint site, but has not yet executed + /// the breakpoint instruction, should have a breakpoint-hit StopInfo set. + /// When execution is resumed, any thread sitting at a breakpoint site will + /// instruction-step over the breakpoint instruction silently, and we will + /// never record this breakpoint as being hit, updating the hit count, + /// possibly executing breakpoint commands or conditions. + /// + /// \return + /// True if Thread::SetStopInfo(...) was called, false otherwise. virtual bool CalculateStopInfo() = 0; // Gets the temporary resume state for a thread. diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 629b191f3117a..3dc40ee6bb9ff 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1600,6 +1600,26 @@ bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { // has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { + // If a thread is stopped at a breakpoint site, set that as the stop + // reason even if it hasn't executed the breakpoint instruction yet. + // We will silently step over the breakpoint when we resume execution + // and miss the fact that this thread hit the breakpoint. + const size_t num_thread_ids = m_thread_ids.size(); + for (size_t i = 0; i < num_thread_ids; i++) { + if (m_thread_ids[i] == thread->GetID() && m_thread_pcs.size() > i) { + addr_t pc = m_thread_pcs[i]; + lldb::BreakpointSiteSP bp_site_sp = + thread->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp) { + if (bp_site_sp->ValidForThisThread(*thread)) { + thread->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread, bp_site_sp->GetID())); + return true; + } + } + } + } thread->SetStopInfo(StopInfoSP()); } return true; @@ -1722,7 +1742,9 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( } else { bool handled = false; bool did_exec = false; - if (!reason.empty()) { + // debugserver can send reason = "none" which is equivalent + // to no reason. + if (!reason.empty() && reason != "none") { if (reason == "trace") { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = @@ -1864,11 +1886,10 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); - // If the current pc is a breakpoint site then the StopInfo should be - // set to Breakpoint even though the remote stub did not set it as such. - // This can happen when the thread is involuntarily interrupted (e.g. - // due to stops on other threads) just as it is about to execute the - // breakpoint instruction. + // If a thread is stopped at a breakpoint site, set that as the stop + // reason even if it hasn't executed the breakpoint instruction yet. + // We will silently step over the breakpoint when we resume execution + // and miss the fact that this thread hit the breakpoint. if (bp_site_sp && bp_site_sp->ValidForThisThread(*thread_sp)) { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID()));