diff --git a/lldb/include/lldb/Target/ThreadPlanStepInRange.h b/lldb/include/lldb/Target/ThreadPlanStepInRange.h index 641a04b3521b..2d3ca2d03e97 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepInRange.h +++ b/lldb/include/lldb/Target/ThreadPlanStepInRange.h @@ -42,6 +42,9 @@ public: ShouldStop (Event *event_ptr); void SetAvoidRegexp(const char *name); + + virtual bool + PlanExplainsStop (); static ThreadPlan * DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton); diff --git a/lldb/include/lldb/Target/ThreadPlanStepOut.h b/lldb/include/lldb/Target/ThreadPlanStepOut.h index 0512e9378686..c2273db81e7c 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepOut.h +++ b/lldb/include/lldb/Target/ThreadPlanStepOut.h @@ -19,8 +19,6 @@ namespace lldb_private { -class ThreadPlanStepOverRange; - class ThreadPlanStepOut : public ThreadPlan { public: diff --git a/lldb/include/lldb/Target/ThreadPlanStepOverRange.h b/lldb/include/lldb/Target/ThreadPlanStepOverRange.h index 8190b81d14f4..b57bddb4b3f1 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepOverRange.h +++ b/lldb/include/lldb/Target/ThreadPlanStepOverRange.h @@ -33,6 +33,7 @@ public: virtual ~ThreadPlanStepOverRange (); + virtual bool PlanExplainsStop (); virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); virtual bool ShouldStop (Event *event_ptr); virtual bool diff --git a/lldb/include/lldb/Target/ThreadPlanStepRange.h b/lldb/include/lldb/Target/ThreadPlanStepRange.h index dd3359f3bf49..88675c8d293d 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepRange.h +++ b/lldb/include/lldb/Target/ThreadPlanStepRange.h @@ -36,7 +36,6 @@ public: virtual void GetDescription (Stream *s, lldb::DescriptionLevel level) = 0; virtual bool ValidatePlan (Stream *error); - virtual bool PlanExplainsStop (); virtual bool ShouldStop (Event *event_ptr) = 0; virtual Vote ShouldReportStop (Event *event_ptr); virtual bool StopOthers (); diff --git a/lldb/include/lldb/Target/ThreadPlanStepThrough.h b/lldb/include/lldb/Target/ThreadPlanStepThrough.h index 247b6f29bed6..6e7009d180b4 100644 --- a/lldb/include/lldb/Target/ThreadPlanStepThrough.h +++ b/lldb/include/lldb/Target/ThreadPlanStepThrough.h @@ -33,21 +33,29 @@ public: virtual bool WillResume (lldb::StateType resume_state, bool current_plan); virtual bool WillStop (); virtual bool MischiefManaged (); + virtual void DidPush(); protected: ThreadPlanStepThrough (Thread &thread, bool stop_others); - bool - HappyToStopHere (); + void + LookForPlanToStepThroughFromCurrentPC (); + + bool + HitOurBackstopBreakpoint(); private: friend ThreadPlan * Thread::QueueThreadPlanForStepThrough (bool abort_other_plans, bool stop_others); - lldb::addr_t m_start_address; - bool m_stop_others; + lldb::ThreadPlanSP m_sub_plan_sp; + lldb::addr_t m_start_address; + lldb::break_id_t m_backstop_bkpt_id; + lldb::addr_t m_backstop_addr; + size_t m_stack_depth; + bool m_stop_others; DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepThrough); diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index b51356a579f8..e4415b7b73eb 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -272,7 +272,59 @@ Thread::ShouldStop (Event* event_ptr) // The top most plan always gets to do the trace log... current_plan->DoTraceLog (); - if (current_plan->PlanExplainsStop()) + // If the base plan doesn't understand why we stopped, then we have to find a plan that does. + // If that plan is still working, then we don't need to do any more work. If the plan that explains + // the stop is done, then we should pop all the plans below it, and pop it, and then let the plans above it decide + // whether they still need to do more work. + + bool done_processing_current_plan = false; + + if (!current_plan->PlanExplainsStop()) + { + if (current_plan->TracerExplainsStop()) + { + done_processing_current_plan = true; + should_stop = false; + } + else + { + // If the current plan doesn't explain the stop, then, find one that + // does and let it handle the situation. + ThreadPlan *plan_ptr = current_plan; + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL) + { + if (plan_ptr->PlanExplainsStop()) + { + should_stop = plan_ptr->ShouldStop (event_ptr); + + // plan_ptr explains the stop, next check whether plan_ptr is done, if so, then we should take it + // and all the plans below it off the stack. + + if (plan_ptr->MischiefManaged()) + { + // We're going to pop the plans up to AND INCLUDING the plan that explains the stop. + plan_ptr = GetPreviousPlan(plan_ptr); + + do + { + if (should_stop) + current_plan->WillStop(); + PopPlan(); + } + while ((current_plan = GetCurrentPlan()) != plan_ptr); + done_processing_current_plan = false; + } + else + done_processing_current_plan = true; + + break; + } + + } + } + } + + if (!done_processing_current_plan) { bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr); @@ -333,25 +385,6 @@ Thread::ShouldStop (Event* event_ptr) if (over_ride_stop) should_stop = false; } - else if (current_plan->TracerExplainsStop()) - { - should_stop = false; - } - else - { - // If the current plan doesn't explain the stop, then, find one that - // does and let it handle the situation. - ThreadPlan *plan_ptr = current_plan; - while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL) - { - if (plan_ptr->PlanExplainsStop()) - { - should_stop = plan_ptr->ShouldStop (event_ptr); - break; - } - - } - } if (log) { @@ -797,22 +830,10 @@ Thread::QueueThreadPlanForStepOut ThreadPlan * Thread::QueueThreadPlanForStepThrough (bool abort_other_plans, bool stop_other_threads) { - // Try the dynamic loader first: - ThreadPlanSP thread_plan_sp(GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (*this, stop_other_threads)); - // If that didn't come up with anything, try the ObjC runtime plugin: - if (thread_plan_sp.get() == NULL) - { - ObjCLanguageRuntime *objc_runtime = GetProcess().GetObjCLanguageRuntime(); - if (objc_runtime) - thread_plan_sp = objc_runtime->GetStepThroughTrampolinePlan (*this, stop_other_threads); - } - - if (thread_plan_sp.get() == NULL) - { - thread_plan_sp.reset(new ThreadPlanStepThrough (*this, stop_other_threads)); - if (thread_plan_sp && !thread_plan_sp->ValidatePlan (NULL)) - return NULL; - } + ThreadPlanSP thread_plan_sp(new ThreadPlanStepThrough (*this, stop_other_threads)); + if (!thread_plan_sp || !thread_plan_sp->ValidatePlan (NULL)) + return NULL; + QueueThreadPlan (thread_plan_sp, abort_other_plans); return thread_plan_sp.get(); } diff --git a/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/lldb/source/Target/ThreadPlanShouldStopHere.cpp index a297dd84d57a..9528e629b343 100644 --- a/lldb/source/Target/ThreadPlanShouldStopHere.cpp +++ b/lldb/source/Target/ThreadPlanShouldStopHere.cpp @@ -7,8 +7,10 @@ // //===----------------------------------------------------------------------===// +#include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlanShouldStopHere.h" +#include "lldb/Core/Log.h" using namespace lldb; using namespace lldb_private; @@ -47,7 +49,26 @@ ThreadPlan * ThreadPlanShouldStopHere::InvokeShouldStopHereCallback () { if (m_callback) - return m_callback (m_owner, m_flags, m_baton); + { + ThreadPlan *return_plan = m_callback (m_owner, m_flags, m_baton); + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + { + lldb::addr_t current_addr = m_owner->GetThread().GetRegisterContext()->GetPC(0); + + if (return_plan) + { + StreamString s; + return_plan->GetDescription (&s, lldb::eDescriptionLevelFull); + log->Printf ("ShouldStopHere callback found a step out plan from 0x%llx: %s.", current_addr, s.GetData()); + } + else + { + log->Printf ("ShouldStopHere callback didn't find a step out plan from: 0x%llx.", current_addr); + } + } + return return_plan; + } else return NULL; } diff --git a/lldb/source/Target/ThreadPlanStepInRange.cpp b/lldb/source/Target/ThreadPlanStepInRange.cpp index 9d3bd24ec734..b1c1bbc1d61d 100644 --- a/lldb/source/Target/ThreadPlanStepInRange.cpp +++ b/lldb/source/Target/ThreadPlanStepInRange.cpp @@ -67,6 +67,37 @@ ThreadPlanStepInRange::GetDescription (Stream *s, lldb::DescriptionLevel level) } } +bool +ThreadPlanStepInRange::PlanExplainsStop () +{ + // We always explain a stop. Either we've just done a single step, in which + // case we'll do our ordinary processing, or we stopped for some + // reason that isn't handled by our sub-plans, in which case we want to just stop right + // away. + + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + StopInfoSP stop_info_sp = GetPrivateStopReason(); + if (stop_info_sp) + { + StopReason reason = stop_info_sp->GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + if (log) + log->PutCString ("ThreadPlanStepInRange got asked if it explains the stop for some reason other than step."); + SetPlanComplete(); + break; + default: + break; + } + } + return true; +} + bool ThreadPlanStepInRange::ShouldStop (Event *event_ptr) { @@ -81,6 +112,9 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr) log->Printf("ThreadPlanStepInRange reached %s.", s.GetData()); } + if (IsPlanComplete()) + return true; + // If we're still in the range, keep going. if (InRange()) return false; diff --git a/lldb/source/Target/ThreadPlanStepOverRange.cpp b/lldb/source/Target/ThreadPlanStepOverRange.cpp index 57ea97bc3150..0df01561c213 100644 --- a/lldb/source/Target/ThreadPlanStepOverRange.cpp +++ b/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -62,6 +62,31 @@ ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level } } +bool +ThreadPlanStepOverRange::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + StopInfoSP stop_info_sp = GetPrivateStopReason(); + if (stop_info_sp) + { + StopReason reason = stop_info_sp->GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + return false; + default: + return true; + } + } + return true; +} + + bool ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) { diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp index 3e87ffeb8e59..5bde93514f39 100644 --- a/lldb/source/Target/ThreadPlanStepRange.cpp +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -63,30 +63,6 @@ ThreadPlanStepRange::ValidatePlan (Stream *error) return true; } -bool -ThreadPlanStepRange::PlanExplainsStop () -{ - // We don't explain signals or breakpoints (breakpoints that handle stepping in or - // out will be handled by a child plan. - StopInfoSP stop_info_sp = GetPrivateStopReason(); - if (stop_info_sp) - { - StopReason reason = stop_info_sp->GetStopReason(); - - switch (reason) - { - case eStopReasonBreakpoint: - case eStopReasonWatchpoint: - case eStopReasonSignal: - case eStopReasonException: - return false; - default: - return true; - } - } - return true; -} - Vote ThreadPlanStepRange::ShouldReportStop (Event *event_ptr) { diff --git a/lldb/source/Target/ThreadPlanStepThrough.cpp b/lldb/source/Target/ThreadPlanStepThrough.cpp index c7257a762f6e..606cfd190ef7 100644 --- a/lldb/source/Target/ThreadPlanStepThrough.cpp +++ b/lldb/source/Target/ThreadPlanStepThrough.cpp @@ -20,6 +20,8 @@ #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Breakpoint/Breakpoint.h" using namespace lldb; using namespace lldb_private; @@ -33,13 +35,86 @@ using namespace lldb_private; ThreadPlanStepThrough::ThreadPlanStepThrough (Thread &thread, bool stop_others) : ThreadPlan (ThreadPlan::eKindStepThrough, "Step through trampolines and prologues", thread, eVoteNoOpinion, eVoteNoOpinion), m_start_address (0), + m_backstop_bkpt_id (LLDB_INVALID_BREAK_ID), + m_backstop_addr(LLDB_INVALID_ADDRESS), + m_stack_depth (0), m_stop_others (stop_others) { - m_start_address = GetThread().GetRegisterContext()->GetPC(0); + + LookForPlanToStepThroughFromCurrentPC(); + + // If we don't get a valid step through plan, don't bother to set up a backstop. + if (m_sub_plan_sp) + { + m_start_address = GetThread().GetRegisterContext()->GetPC(0); + m_stack_depth = m_thread.GetStackFrameCount() - 1; + + // We are going to return back to the concrete frame 1, we might pass by some inlined code that we're in + // the middle of by doing this, but it's easier than trying to figure out where the inlined code might return to. + + StackFrameSP return_frame_sp (m_thread.GetFrameWithConcreteFrameIndex(1)); + + if (return_frame_sp) + { + m_backstop_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress(&m_thread.GetProcess().GetTarget()); + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_backstop_addr, true).get(); + if (return_bp != NULL) + { + return_bp->SetThreadID(m_thread.GetID()); + m_backstop_bkpt_id = return_bp->GetID(); + } + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + { + log->Printf ("Setting backstop breakpoint %d at address: 0x%llx", m_backstop_bkpt_id, m_backstop_addr); + } + } + } } ThreadPlanStepThrough::~ThreadPlanStepThrough () { + if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) + { + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_backstop_bkpt_id); + m_backstop_bkpt_id = LLDB_INVALID_BREAK_ID; + } +} + +void +ThreadPlanStepThrough::DidPush () +{ + if (m_sub_plan_sp) + PushPlan(m_sub_plan_sp); +} + +void +ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC() +{ + m_sub_plan_sp = m_thread.GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others); + // If that didn't come up with anything, try the ObjC runtime plugin: + if (!m_sub_plan_sp.get()) + { + ObjCLanguageRuntime *objc_runtime = m_thread.GetProcess().GetObjCLanguageRuntime(); + if (objc_runtime) + m_sub_plan_sp = objc_runtime->GetStepThroughTrampolinePlan (m_thread, m_stop_others); + } + + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + { + lldb::addr_t current_address = GetThread().GetRegisterContext()->GetPC(0); + if (m_sub_plan_sp) + { + StreamString s; + m_sub_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull); + log->Printf ("Found step through plan from 0x%llx: %s", current_address, s.GetData()); + } + else + { + log->Printf ("Couldn't find step through plan from address 0x%llx.", current_address); + } + } } void @@ -49,30 +124,80 @@ ThreadPlanStepThrough::GetDescription (Stream *s, lldb::DescriptionLevel level) s->Printf ("Step through"); else { - s->Printf ("Stepping through trampoline code from: "); + s->PutCString ("Stepping through trampoline code from: "); s->Address(m_start_address, sizeof (addr_t)); + if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) + { + s->Printf (" with backstop breakpoint id: %d at address: ", m_backstop_bkpt_id); + s->Address (m_backstop_addr, sizeof (addr_t)); + } + else + s->PutCString (" unable to set a backstop breakpoint."); } } bool ThreadPlanStepThrough::ValidatePlan (Stream *error) { - if (HappyToStopHere()) - return false; - else - return true; + return m_sub_plan_sp.get() != NULL; } bool ThreadPlanStepThrough::PlanExplainsStop () { - return true; + // If we have a sub-plan, it will have been asked first if we explain the stop, and + // we won't get asked. The only time we would be the one directly asked this question + // is if we hit our backstop breakpoint. + + if (HitOurBackstopBreakpoint()) + return true; + else + return false; } bool ThreadPlanStepThrough::ShouldStop (Event *event_ptr) { - return true; + // If we've already marked ourselves done, then we're done... + if (IsPlanComplete()) + return true; + + // If we don't have a sub-plan, then we're also done (can't see how we would ever get here + // without a plan, but just in case. + + if (!m_sub_plan_sp) + { + SetPlanComplete(); + return true; + } + + // First, did we hit the backstop breakpoint? + if (HitOurBackstopBreakpoint()) + { + SetPlanComplete(); + return true; + } + + + // If the current sub plan is not done, we don't want to stop. Actually, we probably won't + // ever get here in this state, since we generally won't get asked any questions if out + // current sub-plan is not done... + if (!m_sub_plan_sp->IsPlanComplete()) + return false; + + // Next see if there is a specific step through plan at our current pc (these might + // chain, for instance stepping through a dylib trampoline to the objc dispatch function...) + LookForPlanToStepThroughFromCurrentPC(); + if (m_sub_plan_sp) + { + PushPlan (m_sub_plan_sp); + return false; + } + else + { + SetPlanComplete(); + return true; + } } bool @@ -91,20 +216,6 @@ bool ThreadPlanStepThrough::WillResume (StateType resume_state, bool current_plan) { ThreadPlan::WillResume(resume_state, current_plan); - if (current_plan) - { - ThreadPlanSP sub_plan_sp(m_thread.GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others)); - // If that didn't come up with anything, try the ObjC runtime plugin: - if (sub_plan_sp == NULL) - { - ObjCLanguageRuntime *objc_runtime = m_thread.GetProcess().GetObjCLanguageRuntime(); - if (objc_runtime) - sub_plan_sp = objc_runtime->GetStepThroughTrampolinePlan (m_thread, m_stop_others); - } - - if (sub_plan_sp != NULL) - PushPlan (sub_plan_sp); - } return true; } @@ -119,11 +230,11 @@ ThreadPlanStepThrough::MischiefManaged () { LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - // Stop if we're happy with the place we've landed... + // ShouldStop will call HappyToStopHere, which will set the plan to complete if + // we're done. So we can just check that here. - if (!HappyToStopHere()) + if (!IsPlanComplete()) { - // If we are still at the PC we were trying to step over. return false; } else @@ -131,16 +242,35 @@ ThreadPlanStepThrough::MischiefManaged () if (log) log->Printf("Completed step through step plan."); ThreadPlan::MischiefManaged (); + if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) + { + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_backstop_bkpt_id); + m_backstop_bkpt_id = LLDB_INVALID_BREAK_ID; + } return true; } } bool -ThreadPlanStepThrough::HappyToStopHere() +ThreadPlanStepThrough::HitOurBackstopBreakpoint() { - // This should again ask the various trampolines whether we are still at a - // trampoline point, and if so, continue through the possibly nested trampolines. - - return true; + StopInfoSP stop_info_sp(m_thread.GetStopInfo()); + if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) + { + break_id_t stop_value = (break_id_t) stop_info_sp->GetValue(); + BreakpointSiteSP cur_site_sp = m_thread.GetProcess().GetBreakpointSiteList().FindByID(stop_value); + if (cur_site_sp && cur_site_sp->IsBreakpointAtThisSite(m_backstop_bkpt_id)) + { + size_t current_stack_depth = m_thread.GetStackFrameCount(); + if (current_stack_depth == m_stack_depth) + { + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->PutCString ("ThreadPlanStepThrough hit backstop breakpoint."); + return true; + } + } + } + return false; }