diff options
Diffstat (limited to 'lldb/source/Target/ThreadPlanStepOut.cpp')
-rw-r--r-- | lldb/source/Target/ThreadPlanStepOut.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp new file mode 100644 index 00000000000..e05a8a440a1 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOut.cpp @@ -0,0 +1,228 @@ +//===-- ThreadPlanStepOut.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepOut.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepOut: Step out of the current frame +//---------------------------------------------------------------------- + +ThreadPlanStepOut::ThreadPlanStepOut +( + Thread &thread, + SymbolContext *context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote +) : + ThreadPlan ("Step out", thread, stop_vote, run_vote), + m_step_from_context (context), + m_step_from_insn (LLDB_INVALID_ADDRESS), + m_return_addr (LLDB_INVALID_ADDRESS), + m_first_insn (first_insn), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_stop_others (stop_others) +{ + m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); + + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + if (return_frame) + { + m_return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get(); + if (return_bp != NULL) + { + return_bp->SetThreadID(m_thread.GetID()); + m_return_bp_id = return_bp->GetID(); + } + else + { + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + } + + m_stack_depth = m_thread.GetStackFrameCount(); +} + +ThreadPlanStepOut::~ThreadPlanStepOut () +{ + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) + m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id); +} + +void +ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("step out"); + else + { + s->Printf ("Stepping out from address 0x%llx to return address 0x%llx using breakpoint site %d", + (uint64_t)m_step_from_insn, + (uint64_t)m_return_addr, + m_return_bp_id); + } +} + +bool +ThreadPlanStepOut::ValidatePlan (Stream *error) +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +bool +ThreadPlanStepOut::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + { + // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened... + BreakpointSiteSP this_site = m_thread.GetProcess().GetBreakpointSiteList().FindByID (info.GetBreakpointSiteID()); + if (!this_site) + return false; + + if (this_site->IsBreakpointAtThisSite (m_return_bp_id)) + { + // If there was only one owner, then we're done. But if we also hit some + // user breakpoint on our way out, we should mark ourselves as done, but + // also not claim to explain the stop, since it is more important to report + // the user breakpoint than the step out completion. + + if (this_site->GetNumberOfOwners() == 1) + return true; + else + { + SetPlanComplete(); + return false; + } + } + else + return false; + } + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + return false; + default: + return true; + } + } + return true; +} + +bool +ThreadPlanStepOut::ShouldStop (Event *event_ptr) +{ + if (IsPlanComplete() + || m_thread.GetRegisterContext()->GetPC() == m_return_addr + || m_stack_depth > m_thread.GetStackFrameCount()) + { + SetPlanComplete(); + return true; + } + else + return false; +} + +bool +ThreadPlanStepOut::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepOut::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepOut::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + + if (current_plan) + { + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (true); + } + return true; +} + +bool +ThreadPlanStepOut::WillStop () +{ + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (false); + return true; +} + +bool +ThreadPlanStepOut::MischiefManaged () +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + { + // If I couldn't set this breakpoint, then I'm just going to jettison myself. + return true; + } + else if (IsPlanComplete()) + { + // Did I reach my breakpoint? If so I'm done. + // + // I also check the stack depth, since if we've blown past the breakpoint for some + // reason and we're now stopping for some other reason altogether, then we're done + // with this step out operation. + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step out plan."); + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} + |