ThreadPlanStepThrough.cpp   [plain text]


//===-- ThreadPlanStepThrough.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/ThreadPlanStepThrough.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/lldb-private-log.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Stream.h"
#include "lldb/Target/DynamicLoader.h"
#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;

//----------------------------------------------------------------------
// ThreadPlanStepThrough: If the current instruction is a trampoline, step through it
// If it is the beginning of the prologue of a function, step through that as well.
// FIXME: At present only handles DYLD trampolines.
//----------------------------------------------------------------------

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)
{

    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
ThreadPlanStepThrough::GetDescription (Stream *s, lldb::DescriptionLevel level)
{
    if (level == lldb::eDescriptionLevelBrief)
        s->Printf ("Step through");
    else
    {
        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)
{
    return m_sub_plan_sp.get() != NULL;
}

bool
ThreadPlanStepThrough::PlanExplainsStop ()
{
    // 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)
{
    // 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
ThreadPlanStepThrough::StopOthers ()
{
    return m_stop_others;
}

StateType
ThreadPlanStepThrough::GetPlanRunState ()
{
    return eStateStepping;
}

bool
ThreadPlanStepThrough::WillResume (StateType resume_state, bool current_plan)
{
    ThreadPlan::WillResume(resume_state, current_plan);
    return true;
}

bool
ThreadPlanStepThrough::WillStop ()
{
    return true;
}

bool
ThreadPlanStepThrough::MischiefManaged ()
{
    LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));

    // ShouldStop will call HappyToStopHere, which will set the plan to complete if
    // we're done.  So we can just check that here.

    if (!IsPlanComplete())
    {
        return false;
    }
    else
    {
        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::HitOurBackstopBreakpoint()
{
    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;
}