224 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- 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
 | 
						|
) :
 | 
						|
    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?
 | 
						|
 | 
						|
    StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
 | 
						|
    if (return_frame)
 | 
						|
    {
 | 
						|
        // TODO: check for inlined frames and do the right thing...
 | 
						|
        m_return_addr = return_frame->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();
 | 
						|
        }
 | 
						|
        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.
 | 
						|
    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))
 | 
						|
            {
 | 
						|
                // 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;
 | 
						|
                
 | 
						|
                SetPlanComplete();
 | 
						|
            }
 | 
						|
            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::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;
 | 
						|
    }
 | 
						|
}
 | 
						|
 |