RegisterContextLLDB.cpp   [plain text]


//===-- RegisterContextLLDB.cpp --------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "RegisterContextLLDB.h"

#include "lldb/lldb-private.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/RegisterValue.h"
#include "lldb/Core/Value.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;


RegisterContextLLDB::RegisterContextLLDB 
(
    Thread& thread, 
    const SharedPtr &next_frame,
    SymbolContext& sym_ctx,
    uint32_t frame_number
) :
    RegisterContext (thread, frame_number), 
    m_thread(thread), 
    m_next_frame(next_frame),
    m_fast_unwind_plan_sp (),
    m_full_unwind_plan_sp (),
    m_all_registers_available(false),
    m_frame_type (-1),
    m_cfa (LLDB_INVALID_ADDRESS),
    m_start_pc (), 
    m_current_pc (), 
    m_current_offset (0), 
    m_current_offset_backed_up_one (0),
    m_sym_ctx(sym_ctx),
    m_sym_ctx_valid (false),
    m_frame_number (frame_number),
    m_registers()
{
    m_sym_ctx.Clear();
    m_sym_ctx_valid = false;

    if (IsFrameZero ())
    {
        InitializeZerothFrame ();
    }
    else
    {
        InitializeNonZerothFrame ();
    }

    // This same code exists over in the GetFullUnwindPlanForFrame() but it may not have been executed yet
    if (IsFrameZero() 
        || m_next_frame->m_frame_type == eSigtrampFrame 
        || m_next_frame->m_frame_type == eDebuggerFrame)
    {
        m_all_registers_available = true;
    }
}

// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently
// executing frame.

void
RegisterContextLLDB::InitializeZerothFrame()
{
    StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0));

    if (m_thread.GetRegisterContext() == NULL)
    {
        m_frame_type = eNotAValidFrame;
        return;
    }
    m_sym_ctx = frame_sp->GetSymbolContext (eSymbolContextFunction | eSymbolContextSymbol);
    m_sym_ctx_valid = true;
    AddressRange addr_range;
    m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range);
    
    m_current_pc = frame_sp->GetFrameCodeAddress();

    static ConstString g_sigtramp_name ("_sigtramp");
    if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == g_sigtramp_name) ||
        (m_sym_ctx.symbol   && m_sym_ctx.symbol->GetName()   == g_sigtramp_name))
    {
        m_frame_type = eSigtrampFrame;
    }
    else
    {
        // FIXME:  Detect eDebuggerFrame here.
        m_frame_type = eNormalFrame;
    }

    // If we were able to find a symbol/function, set addr_range to the bounds of that symbol/function.
    // else treat the current pc value as the start_pc and record no offset.
    if (addr_range.GetBaseAddress().IsValid())
    {
        m_start_pc = addr_range.GetBaseAddress();
        if (frame_sp->GetFrameCodeAddress().GetSection() == m_start_pc.GetSection())
        {
            m_current_offset = frame_sp->GetFrameCodeAddress().GetOffset() - m_start_pc.GetOffset();
        }
        else if (frame_sp->GetFrameCodeAddress().GetModule() == m_start_pc.GetModule())
        {
            // This means that whatever symbol we kicked up isn't really correct
            // as no should cross section boundaries... We really should NULL out
            // the function/symbol in this case unless there is a bad assumption
            // here due to inlined functions?
            m_current_offset = frame_sp->GetFrameCodeAddress().GetFileAddress() - m_start_pc.GetFileAddress();
        }
        m_current_offset_backed_up_one = m_current_offset;
    }
    else
    {
        m_start_pc = m_current_pc;
        m_current_offset = -1;
        m_current_offset_backed_up_one = -1;
    }

    // We've set m_frame_type and m_sym_ctx before these calls.

    m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame ();
    m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); 
    
    const UnwindPlan::Row *active_row = NULL;
    int cfa_offset = 0;
    int row_register_kind;
    if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
    {
        active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
        row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
    }

    if (active_row == NULL)
    {
        m_frame_type = eNotAValidFrame;
        return;
    }

    addr_t cfa_regval;
    if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
    {
        m_frame_type = eNotAValidFrame;
        return;
    }
    else
    {
    }
    cfa_offset = active_row->GetCFAOffset ();

    m_cfa = cfa_regval + cfa_offset;

    LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));

    // A couple of sanity checks..
    if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
    {
        if (log)
        {   
            log->Printf("%*sFrame %u could not find a valid cfa address",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
        }
        m_frame_type = eNotAValidFrame;
        return;
    }

    if (log)
    {
        log->Printf("%*sThread %d Frame %u initialized frame current pc is 0x%llx cfa is 0x%llx using %s UnwindPlan", 
                    m_frame_number < 100 ? m_frame_number : 100, "", 
                    m_thread.GetIndexID(), 
                    m_frame_number,
                    (uint64_t) m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()), 
                    (uint64_t) m_cfa,
                    m_full_unwind_plan_sp->GetSourceName().GetCString());
    }
}

// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the RegisterContextLLDB "below" it
// to provide things like its current pc value.

void
RegisterContextLLDB::InitializeNonZerothFrame()
{
    LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
    if (IsFrameZero ())
    {
        m_frame_type = eNotAValidFrame;
        return;
    }
    
    if (!m_next_frame->IsValid())
    {
        m_frame_type = eNotAValidFrame;
        return;
    }
    if (m_thread.GetRegisterContext() == NULL)
    {
        m_frame_type = eNotAValidFrame;
        return;
    }

    addr_t pc;
    if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
    {
        if (log)
        {
            log->Printf("%*sFrame %u could not get pc value",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
        }
        m_frame_type = eNotAValidFrame;
        return;
    }
    // A pc value of 0 up on the stack indicates we've hit the end of the stack
    if (pc == 0)
    {
        m_frame_type = eNotAValidFrame;
        return;
    }
    m_thread.GetProcess().GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, m_current_pc);

    // If we don't have a Module for some reason, we're not going to find symbol/function information - just
    // stick in some reasonable defaults and hope we can unwind past this frame.
    if (!m_current_pc.IsValid() || m_current_pc.GetModule() == NULL)
    {
        if (log)
        {
            log->Printf("%*sFrame %u using architectural default unwind method",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
        }
        ABI *abi = m_thread.GetProcess().GetABI().get();
        if (abi)
        {
            m_fast_unwind_plan_sp.reset ();
            m_full_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
            abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp);
            m_frame_type = eNormalFrame;
            m_all_registers_available = false;
            m_current_offset = -1;
            m_current_offset_backed_up_one = -1;
            addr_t cfa_regval;
            int row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
            const UnwindPlan::Row *row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0);
            if (row)
            {
                uint32_t cfa_regnum = row->GetCFARegister();
                int cfa_offset = row->GetCFAOffset();
                if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval))
                {
                    if (log)
                    {
                        log->Printf("%*sFrame %u failed to get cfa value",
                                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
                    }
                    m_frame_type = eNormalFrame;
                    return;
                }
                m_cfa = cfa_regval + cfa_offset;

                // A couple of sanity checks..
                if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
                {
                    if (log)
                    {
                        log->Printf("%*sFrame %u could not find a valid cfa address",
                                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
                    }
                    m_frame_type = eNotAValidFrame;
                    return;
                }
            }
            else
            {
                if (log)
                {
                    log->Printf("%*sFrame %u could not find a row for function offset zero",
                                m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
                }
                m_frame_type = eNotAValidFrame;
                return;
            }

            if (log)
            {
                log->Printf("%*sFrame %u initialized frame cfa is 0x%llx",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                            (uint64_t) m_cfa);
            }
            return;
        }
        m_frame_type = eNotAValidFrame;
        return;
    }

    // We require that eSymbolContextSymbol be successfully filled in or this context is of no use to us.
    if ((m_current_pc.GetModule()->ResolveSymbolContextForAddress (m_current_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
    {
        m_sym_ctx_valid = true;
    }

    AddressRange addr_range;
    if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range))
    {
        m_sym_ctx_valid = false;
    }

    bool decr_pc_and_recompute_addr_range = false;

    // If the symbol lookup failed...
    if (m_sym_ctx_valid == false)
       decr_pc_and_recompute_addr_range = true;

    // Or if we're in the middle of the stack (and not "above" an asynchronous event like sigtramp),
    // and our "current" pc is the start of a function...
    if (m_sym_ctx_valid
        && m_next_frame->m_frame_type != eSigtrampFrame
        && m_next_frame->m_frame_type != eDebuggerFrame
        && addr_range.GetBaseAddress().IsValid()
        && addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection()
        && addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset())
    {
        decr_pc_and_recompute_addr_range = true;
    }

    // We need to back up the pc by 1 byte and re-search for the Symbol to handle the case where the "saved pc"
    // value is pointing to the next function, e.g. if a function ends with a CALL instruction.
    // FIXME this may need to be an architectural-dependent behavior; if so we'll need to add a member function
    // to the ABI plugin and consult that.
    if (decr_pc_and_recompute_addr_range) 
    {
        Address temporary_pc(m_current_pc);
        temporary_pc.SetOffset(m_current_pc.GetOffset() - 1);
        m_sym_ctx.Clear();
        m_sym_ctx_valid = false;
        if ((m_current_pc.GetModule()->ResolveSymbolContextForAddress (temporary_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
        {
            m_sym_ctx_valid = true;
        }
        if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false,  addr_range))
        {
            m_sym_ctx_valid = false;
        }
    }

    // If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function.
    // else treat the current pc value as the start_pc and record no offset.
    if (addr_range.GetBaseAddress().IsValid())
    {
        m_start_pc = addr_range.GetBaseAddress();
        m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
        m_current_offset_backed_up_one = m_current_offset;
        if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0)
            m_current_offset_backed_up_one--;
    }
    else
    {
        m_start_pc = m_current_pc;
        m_current_offset = -1;
        m_current_offset_backed_up_one = -1;
    }

    static ConstString sigtramp_name ("_sigtramp");
    if ((m_sym_ctx.function && m_sym_ctx.function->GetMangled().GetMangledName() == sigtramp_name)
        || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetMangled().GetMangledName() == sigtramp_name))
    {
        m_frame_type = eSigtrampFrame;
    }
    else
    {
        // FIXME:  Detect eDebuggerFrame here.
        m_frame_type = eNormalFrame;
    }

    // We've set m_frame_type and m_sym_ctx before this call.
    m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame ();

    const UnwindPlan::Row *active_row = NULL;
    int cfa_offset = 0;
    int row_register_kind;

    // Try to get by with just the fast UnwindPlan if possible - the full UnwindPlan may be expensive to get
    // (e.g. if we have to parse the entire eh_frame section of an ObjectFile for the first time.)

    if (m_fast_unwind_plan_sp && m_fast_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
    {
        active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
        row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind ();
    }
    else 
    {
        m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
        if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
        {
            active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
            row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
        }
    }

    if (active_row == NULL)
    {
        m_frame_type = eNotAValidFrame;
        return;
    }

    addr_t cfa_regval;
    if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
    {
        if (log)
        {
            log->Printf("%*sFrame %u failed to get cfa reg %d/%d",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        row_register_kind, active_row->GetCFARegister());
        }
        m_frame_type = eNotAValidFrame;
        return;
    }
    cfa_offset = active_row->GetCFAOffset ();

    m_cfa = cfa_regval + cfa_offset;

    // A couple of sanity checks..
    if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
    { 
        if (log)
        {
            log->Printf("%*sFrame %u could not find a valid cfa address",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
        }
        m_frame_type = eNotAValidFrame;
        return;
    }

    if (log)
    {
        log->Printf("%*sFrame %u initialized frame current pc is 0x%llx cfa is 0x%llx", 
                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                    (uint64_t) m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()), (uint64_t) m_cfa);
    }
}


bool
RegisterContextLLDB::IsFrameZero () const
{
    if (m_next_frame.get () == NULL)
        return true;
    else
        return false;
}


// Find a fast unwind plan for this frame, if possible.
//
// On entry to this method, 
//
//   1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct, 
//   2. m_sym_ctx should already be filled in, and
//   3. m_current_pc should have the current pc value for this frame
//   4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown

UnwindPlanSP
RegisterContextLLDB::GetFastUnwindPlanForFrame ()
{
    UnwindPlanSP unwind_plan_sp;
    if (!m_current_pc.IsValid() || m_current_pc.GetModule() == NULL || m_current_pc.GetModule()->GetObjectFile() == NULL)
        return unwind_plan_sp;

    if (IsFrameZero ())
        return unwind_plan_sp;

    FuncUnwindersSP func_unwinders_sp (m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx));
    if (!func_unwinders_sp)
        return unwind_plan_sp;

    // If we're in _sigtramp(), unwinding past this frame requires special knowledge.  
    if (m_frame_type == eSigtrampFrame || m_frame_type == eDebuggerFrame)
        return unwind_plan_sp;

    unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (m_thread);
    if (unwind_plan_sp)
    {
        if (unwind_plan_sp->PlanValidAtAddress (m_current_pc))
        {
            LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
            if (log && IsLogVerbose())
            {
                const char *has_fast = "";
                if (m_fast_unwind_plan_sp)
                    has_fast = ", and has a fast UnwindPlan";
                log->Printf("%*sFrame %u frame has a fast UnwindPlan",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
            }
            m_frame_type = eNormalFrame;
            return unwind_plan_sp;
        }
        else 
        {
            unwind_plan_sp.reset();
        }
    }
    return unwind_plan_sp;
}

// On entry to this method, 
//
//   1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct, 
//   2. m_sym_ctx should already be filled in, and
//   3. m_current_pc should have the current pc value for this frame
//   4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown

UnwindPlanSP
RegisterContextLLDB::GetFullUnwindPlanForFrame ()
{
    UnwindPlanSP unwind_plan_sp;
    LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
    UnwindPlanSP arch_default_unwind_plan_sp;
    
    
    ABI *abi = m_thread.GetProcess().GetABI().get();
    if (abi)
    {
        arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
        abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
    }

    bool behaves_like_zeroth_frame = false;
    if (IsFrameZero () 
        || m_next_frame->m_frame_type == eSigtrampFrame
        || m_next_frame->m_frame_type == eDebuggerFrame)
    {
        behaves_like_zeroth_frame = true;
        // If this frame behaves like a 0th frame (currently executing or 
        // interrupted asynchronously), all registers can be retrieved.
        m_all_registers_available = true;
    }

    // No Module for the current pc, try using the architecture default unwind.
    if (!m_current_pc.IsValid() || m_current_pc.GetModule() == NULL || m_current_pc.GetModule()->GetObjectFile() == NULL)
    {
        m_frame_type = eNormalFrame;
        return arch_default_unwind_plan_sp;
    }

    FuncUnwindersSP func_unwinders_sp;
    if (m_sym_ctx_valid)
    {
        func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
    }

    // No FuncUnwinders available for this pc, try using architectural default unwind.
    if (!func_unwinders_sp)
    {
        m_frame_type = eNormalFrame;
        return arch_default_unwind_plan_sp;
    }

    // If we're in _sigtramp(), unwinding past this frame requires special knowledge.  On Mac OS X this knowledge
    // is properly encoded in the eh_frame section, so prefer that if available.
    // On other platforms we may need to provide a platform-specific UnwindPlan which encodes the details of
    // how to unwind out of sigtramp.
    if (m_frame_type == eSigtrampFrame)
    {
        m_fast_unwind_plan_sp.reset();
        unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
        if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
            return unwind_plan_sp;
    }

    
    // Typically the NonCallSite UnwindPlan is the unwind created by inspecting the assembly language instructions
    if (behaves_like_zeroth_frame)
    {
        unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
        if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
        {
            if (log && IsLogVerbose())
            {
                log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                            unwind_plan_sp->GetSourceName().GetCString());
            }
            return unwind_plan_sp;
        }
    }

    // Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites
    unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
    if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
    {
        if (log && IsLogVerbose())
        {
            log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        unwind_plan_sp->GetSourceName().GetCString());
        }
        return unwind_plan_sp;
    }
    
    // We'd prefer to use an UnwindPlan intended for call sites when we're at a call site but if we've
    // struck out on that, fall back to using the non-call-site assembly inspection UnwindPlan if possible.
    unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
    if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
    {
        if (log && IsLogVerbose())
        {
            log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        unwind_plan_sp->GetSourceName().GetCString());
        }
        return unwind_plan_sp;
    }

    // If nothing else, use the architectural default UnwindPlan and hope that does the job.
    if (log && IsLogVerbose())
    {
        log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                    arch_default_unwind_plan_sp->GetSourceName().GetCString());
    }
    return arch_default_unwind_plan_sp;
}


void
RegisterContextLLDB::InvalidateAllRegisters ()
{
    m_frame_type = eNotAValidFrame;
}

size_t
RegisterContextLLDB::GetRegisterCount ()
{
    return m_thread.GetRegisterContext()->GetRegisterCount();
}

const RegisterInfo *
RegisterContextLLDB::GetRegisterInfoAtIndex (uint32_t reg)
{
    return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg);
}

size_t
RegisterContextLLDB::GetRegisterSetCount ()
{
    return m_thread.GetRegisterContext()->GetRegisterSetCount ();
}

const RegisterSet *
RegisterContextLLDB::GetRegisterSet (uint32_t reg_set)
{
    return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
}

uint32_t
RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
{
    return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num);
}

bool
RegisterContextLLDB::ReadRegisterValueFromRegisterLocation (RegisterLocation regloc, 
                                                            const RegisterInfo *reg_info,
                                                            RegisterValue &value)
{
    if (!IsValid())
        return false;
    bool success = false;

    switch (regloc.type)
    {
    case eRegisterInRegister:
        {
            const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
            if (IsFrameZero ()) 
            {
                success = m_thread.GetRegisterContext()->ReadRegister (other_reg_info, value);
            }
            else
            {
                success = m_next_frame->ReadRegister (other_reg_info, value);
            }
        }
        break;
    case eRegisterValueInferred:
        success = value.SetUInt (regloc.location.inferred_value, reg_info->byte_size);
        break;
            
    case eRegisterNotSaved:
        break;
    case eRegisterSavedAtHostMemoryLocation:
        assert ("FIXME debugger inferior function call unwind");
        break;
    case eRegisterSavedAtMemoryLocation:
        {
            Error error (ReadRegisterValueFromMemory(reg_info, 
                                                     regloc.location.target_memory_location, 
                                                     reg_info->byte_size, 
                                                     value));
            success = error.Success();
        }
        break;
    default:
        assert ("Unknown RegisterLocation type.");
        break;
    }
    return success;
}

bool
RegisterContextLLDB::WriteRegisterValueToRegisterLocation (RegisterLocation regloc, 
                                                           const RegisterInfo *reg_info,
                                                           const RegisterValue &value)
{
    if (!IsValid())
        return false;

    bool success = false;
    
    switch (regloc.type)
    {
        case eRegisterInRegister:
            {
                const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
                if (IsFrameZero ()) 
                {
                    success = m_thread.GetRegisterContext()->WriteRegister (other_reg_info, value);
                }
                else
                {
                    success = m_next_frame->WriteRegister (other_reg_info, value);
                }
            }
            break;
        case eRegisterValueInferred:
        case eRegisterNotSaved:
            break;
        case eRegisterSavedAtHostMemoryLocation:
            assert ("FIXME debugger inferior function call unwind");
            break;
        case eRegisterSavedAtMemoryLocation:
            {
                Error error (WriteRegisterValueToMemory (reg_info, 
                                                         regloc.location.target_memory_location, 
                                                         reg_info->byte_size, 
                                                         value));
                success = error.Success();
            }
            break;
        default:
            assert ("Unknown RegisterLocation type.");
            break;
    }
    return success;
}


bool
RegisterContextLLDB::IsValid () const
{
    return m_frame_type != eNotAValidFrame;
}

// Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value?

bool
RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, RegisterLocation &regloc)
{
    LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));

    // Have we already found this register location?
    if (!m_registers.empty())
    {
        std::map<uint32_t, RegisterLocation>::const_iterator iterator;
        iterator = m_registers.find (lldb_regnum);
        if (iterator != m_registers.end())
        {
            regloc = iterator->second;
            return true;
        }
    }

    // Are we looking for the CALLER's stack pointer?  The stack pointer is defined to be the same as THIS frame's
    // CFA so just return the CFA value.  This is true on x86-32/x86-64 at least.
    uint32_t sp_regnum;
    if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, eRegisterKindLLDB, sp_regnum)
        && sp_regnum == lldb_regnum)
    {
        // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value)
        assert (sizeof (addr_t) <= sizeof (uint64_t));
        regloc.type = eRegisterValueInferred;
        regloc.location.inferred_value = m_cfa;
        m_registers[lldb_regnum] = regloc;
        return true;
    }

    // Look through the available UnwindPlans for the register location.

    UnwindPlan::Row::RegisterLocation unwindplan_regloc;
    bool have_unwindplan_regloc = false;
    int unwindplan_registerkind = -1;

    if (m_fast_unwind_plan_sp)
    {
        const UnwindPlan::Row *active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
        unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind ();
        uint32_t row_regnum;
        if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum))
        {
            if (log)
            {
                log->Printf("%*sFrame %u could not convert lldb regnum %d into %d RegisterKind reg numbering scheme",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                            lldb_regnum, (int) unwindplan_registerkind);
            }
            return false;
        }
        if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
        {
            if (log)
            {
                log->Printf("%*sFrame %u supplying caller's saved reg %d's location using FastUnwindPlan",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                            lldb_regnum);
            }
            have_unwindplan_regloc = true;
        }
    }

    if (!have_unwindplan_regloc)
    {
        // m_full_unwind_plan_sp being NULL means that we haven't tried to find a full UnwindPlan yet
        if (!m_full_unwind_plan_sp)
            m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();

        if (m_full_unwind_plan_sp)
        {
            const UnwindPlan::Row *active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
            unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind ();
            uint32_t row_regnum;
            if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum))
            {
                if (log)
                {
                    if (unwindplan_registerkind == eRegisterKindGeneric)
                        log->Printf("%*sFrame %u could not convert lldb regnum %d into eRegisterKindGeneric reg numbering scheme",
                                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                                    lldb_regnum);
                    else
                        log->Printf("%*sFrame %u could not convert lldb regnum %d into %d RegisterKind reg numbering scheme",
                                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                                    lldb_regnum, (int) unwindplan_registerkind);
                }
                return false;
            }

            if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
            {
                have_unwindplan_regloc = true;
                if (log && IsLogVerbose ())
                {                
                    log->Printf("%*sFrame %u supplying caller's saved reg %d's location using %s UnwindPlan",
                                m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                                lldb_regnum, m_full_unwind_plan_sp->GetSourceName().GetCString());
                }
            }
        }
    }

    if (have_unwindplan_regloc == false)
    {
        // If a volatile register is being requested, we don't want to forward m_next_frame's register contents 
        // up the stack -- the register is not retrievable at this frame.
        ABI *abi = m_thread.GetProcess().GetABI().get();
        if (abi)
        {
            const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
            if (reg_info && abi->RegisterIsVolatile (reg_info))
            {
                if (log)
                {
                    log->Printf("%*sFrame %u did not supply reg location for %d because it is volatile",
                                m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                                lldb_regnum);
                }
                return false;
            }  
        }

        if (IsFrameZero ())
        {
            // This is frame 0 - we should return the actual live register context value
            RegisterLocation new_regloc;
            new_regloc.type = eRegisterInRegister;
            new_regloc.location.register_number = lldb_regnum;
            m_registers[lldb_regnum] = new_regloc;
            regloc = new_regloc;
            return true;
        }
        else
        {
            return m_next_frame->SavedLocationForRegister (lldb_regnum, regloc);
        }
        if (log)
        {
            log->Printf("%*sFrame %u could not supply caller's reg %d location",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        lldb_regnum);
        }
        return false;
    }

    // unwindplan_regloc has valid contents about where to retrieve the register
    if (unwindplan_regloc.IsUnspecified())
    {
        RegisterLocation new_regloc;
        new_regloc.type = eRegisterNotSaved;
        m_registers[lldb_regnum] = new_regloc;
        if (log)
        {
            log->Printf("%*sFrame %u could not supply caller's reg %d location",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        lldb_regnum);
        }
        return false;
    }

    if (unwindplan_regloc.IsSame())
    {
        if (IsFrameZero ())
        {
            if (log)
            {
                log->Printf("%*sFrame %u could not supply caller's reg %d location",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                            lldb_regnum);
            }
            return false;
        }
        else
        {
            return m_next_frame->SavedLocationForRegister (lldb_regnum, regloc);
        }
    }

    if (unwindplan_regloc.IsCFAPlusOffset())
    {
        int offset = unwindplan_regloc.GetOffset();
        regloc.type = eRegisterValueInferred;
        regloc.location.inferred_value = m_cfa + offset;
        m_registers[lldb_regnum] = regloc;
        return true;
    }

    if (unwindplan_regloc.IsAtCFAPlusOffset())
    {
        int offset = unwindplan_regloc.GetOffset();
        regloc.type = eRegisterSavedAtMemoryLocation;
        regloc.location.target_memory_location = m_cfa + offset;
        m_registers[lldb_regnum] = regloc;
        return true;
    }

    if (unwindplan_regloc.IsInOtherRegister())
    {
        uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
        uint32_t row_regnum_in_lldb;
        if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb))
        {
            if (log)
            {
                log->Printf("%*sFrame %u could not supply caller's reg %d location",
                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                            lldb_regnum);
            }
            return false;
        }
        regloc.type = eRegisterInRegister;
        regloc.location.register_number = row_regnum_in_lldb;
        m_registers[lldb_regnum] = regloc;
        return true;
    }

    if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression())
    {
        DataExtractor dwarfdata (unwindplan_regloc.GetDWARFExpressionBytes(), 
                                 unwindplan_regloc.GetDWARFExpressionLength(), 
                                 m_thread.GetProcess().GetByteOrder(), m_thread.GetProcess().GetAddressByteSize());
        DWARFExpression dwarfexpr (dwarfdata, 0, unwindplan_regloc.GetDWARFExpressionLength());
        dwarfexpr.SetRegisterKind (unwindplan_registerkind);
        ExecutionContext exe_ctx (&m_thread.GetProcess(), &m_thread, NULL);
        Value result;
        Error error;
        if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, NULL, this, 0, NULL, result, &error))
        {
            addr_t val;
            val = result.GetScalar().ULongLong();
            if (unwindplan_regloc.IsDWARFExpression())
             {
                regloc.type = eRegisterValueInferred;
                regloc.location.inferred_value = val;
                m_registers[lldb_regnum] = regloc;
                return true;
            }
            else
            {
               regloc.type = eRegisterSavedAtMemoryLocation;
               regloc.location.target_memory_location = val;
               m_registers[lldb_regnum] = regloc;
               return true;
            }
        }
        if (log)
        {
            log->Printf("%*sFrame %u tried to use IsDWARFExpression or IsAtDWARFExpression for reg %d but failed",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        lldb_regnum);
        }
        return false;
    }

    if (log)
    {
        log->Printf("%*sFrame %u could not supply caller's reg %d location",
                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                    lldb_regnum);
    }

    
    // assert ("UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported.");
    return false;
}

// Retrieve a general purpose register value for THIS from, as saved by the NEXT frame, i.e. the frame that
// this frame called.  e.g.
//
//  foo () { }
//  bar () { foo (); }
//  main () { bar (); }
//
//  stopped in foo() so
//     frame 0 - foo
//     frame 1 - bar
//     frame 2 - main
//  and this RegisterContext is for frame 1 (bar) - if we want to get the pc value for frame 1, we need to ask
//  where frame 0 (the "next" frame) saved that and retrieve the value.

bool
RegisterContextLLDB::ReadGPRValue (int register_kind, uint32_t regnum, addr_t &value)
{
    if (!IsValid())
        return false;

    uint32_t lldb_regnum;
    if (register_kind == eRegisterKindLLDB)
    {
        lldb_regnum = regnum;
    }
    else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindLLDB, lldb_regnum))
    {
        return false;
    }

    const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
    RegisterValue reg_value;
    // if this is frame 0 (currently executing frame), get the requested reg contents from the actual thread registers
    if (IsFrameZero ())
    {
        if (m_thread.GetRegisterContext()->ReadRegister (reg_info, reg_value))
        {
            value = reg_value.GetAsUInt64();
            return true;
        }
        return false;
    }

    RegisterLocation regloc;
    if (!m_next_frame->SavedLocationForRegister (lldb_regnum, regloc))
    {
        return false;
    }
    if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value))
    {
        value = reg_value.GetAsUInt64();
        return true;
    }
    return false;
}

// Find the value of a register in THIS frame

bool
RegisterContextLLDB::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value)
{
    LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
    if (!IsValid())
        return false;

    const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
    if (log && IsLogVerbose ())
    {
        log->Printf("%*sFrame %u looking for register saved location for reg %d",
                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                    lldb_regnum);
    }

    // If this is the 0th frame, hand this over to the live register context
    if (IsFrameZero ())
    {
        if (log)
        {
            log->Printf("%*sFrame %u passing along to the live register context for reg %d",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        lldb_regnum);
        }
        return m_thread.GetRegisterContext()->ReadRegister (reg_info, value);
    }

    RegisterLocation regloc;
    // Find out where the NEXT frame saved THIS frame's register contents
    if (!m_next_frame->SavedLocationForRegister (lldb_regnum, regloc))
        return false;

    return ReadRegisterValueFromRegisterLocation (regloc, reg_info, value);
}

bool
RegisterContextLLDB::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &value)
{
    LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
    if (!IsValid())
        return false;

    const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
    if (log && IsLogVerbose ())
    {
        log->Printf("%*sFrame %u looking for register saved location for reg %d",
                    m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                    lldb_regnum);
    }

    // If this is the 0th frame, hand this over to the live register context
    if (IsFrameZero ())
    {
        if (log)
        {
            log->Printf("%*sFrame %u passing along to the live register context for reg %d",
                        m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
                        lldb_regnum);
        }
        return m_thread.GetRegisterContext()->WriteRegister (reg_info, value);
    }

    RegisterLocation regloc;
    // Find out where the NEXT frame saved THIS frame's register contents
    if (!m_next_frame->SavedLocationForRegister (lldb_regnum, regloc))
        return false;

    return WriteRegisterValueToRegisterLocation (regloc, reg_info, value);
}

// Don't need to implement this one
bool
RegisterContextLLDB::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
{
    return false;
}

// Don't need to implement this one
bool
RegisterContextLLDB::WriteAllRegisterValues (const lldb::DataBufferSP& data_sp)
{
    return false;
}

// Retrieve the pc value for THIS from

bool
RegisterContextLLDB::GetCFA (addr_t& cfa)
{
    if (!IsValid())
    {
        return false;
    }
    if (m_cfa == LLDB_INVALID_ADDRESS)
    {
        return false;
    }
    cfa = m_cfa;
    return true;
}

// Retrieve the address of the start of the function of THIS frame

bool
RegisterContextLLDB::GetStartPC (addr_t& start_pc)
{
    if (!IsValid())
        return false;
    if (!m_start_pc.IsValid())
    {
        return ReadPC (start_pc); 
    }
    start_pc = m_start_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget());
    return true;
}

// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.

bool
RegisterContextLLDB::ReadPC (addr_t& pc)
{
    if (!IsValid())
        return false;
    if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
    {
        // A pc value of 0 or 1 is impossible in the middle of the stack -- it indicates the end of a stack walk.
        // On the currently executing frame (or such a frame interrupted asynchronously by sigtramp et al) this may
        // occur if code has jumped through a NULL pointer -- we want to be able to unwind past that frame to help
        // find the bug.

        if (m_all_registers_available == false 
            && (pc == 0 || pc == 1))
        {
            return false;
        }
        else 
        {
            return true;
        }
    }
    else
    {
        return false;
    }
}