blob: a1b19d1ae2adc1955a3bf518c6f9d388cf51b139 [file] [log] [blame]
//===-- 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/StopInfo.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,
uint32_t frame_idx
) :
ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote),
m_step_from_context (context),
m_step_from_insn (LLDB_INVALID_ADDRESS),
m_return_bp_id (LLDB_INVALID_BREAK_ID),
m_return_addr (LLDB_INVALID_ADDRESS),
m_first_insn (first_insn),
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?
StackFrameSP return_frame_sp (m_thread.GetStackFrameAtIndex(frame_idx + 1));
if (return_frame_sp)
{
// TODO: check for inlined frames and do the right thing...
m_return_addr = return_frame_sp->GetRegisterContext()->GetPC();
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();
}
}
m_stack_depth = m_thread.GetStackFrameCount() - frame_idx;
}
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.
StopInfoSP stop_info_sp = GetPrivateStopReason();
if (stop_info_sp)
{
StopReason reason = stop_info_sp->GetStopReason();
switch (reason)
{
case eStopReasonBreakpoint:
{
// If this is OUR breakpoint, we're fine, otherwise we don't know why this happened...
BreakpointSiteSP site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info_sp->GetValue()));
if (site_sp && site_sp->IsBreakpointAtThisSite (m_return_bp_id))
{
const uint32_t num_frames = m_thread.GetStackFrameCount();
if (m_stack_depth > num_frames)
SetPlanComplete();
// 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 (site_sp->GetNumberOfOwners() == 1)
return true;
}
return false;
}
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
return false;
default:
return true;
}
}
return true;
}
bool
ThreadPlanStepOut::ShouldStop (Event *event_ptr)
{
if (IsPlanComplete() || m_stack_depth > m_thread.GetStackFrameCount())
{
SetPlanComplete();
return true;
}
else
return false;
}
bool
ThreadPlanStepOut::StopOthers ()
{
return m_stop_others;
}
StateType
ThreadPlanStepOut::GetPlanRunState ()
{
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.
LogSP 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;
}
}