Check in the native lldb unwinder.

Not yet enabled as the default unwinder but there are no known
backtrace problems with the code at this point.

Added 'log enable lldb unwind' to help diagnose backtrace problems;
this output needs a little refining but it's a good first step.

eh_frame information is currently read unconditionally - the code
is structured to allow this to be delayed until it's actually needed.
There is a performance hit when you have to parse the eh_frame
information for any largeish executable/library so it's necessary
to avoid if possible.

It's confusing having both the UnwindPlan::RegisterLocation struct
and the RegisterConextLLDB::RegisterLocation struct, I need to rename
one of them.

The writing of registers isn't done in the RegisterConextLLDB subclass
yet; neither is the running of complex DWARF expressions from eh_frame
(e.g. used for _sigtramp on Mac OS X).

llvm-svn: 117256
This commit is contained in:
Jason Molenda 2010-10-25 11:12:07 +00:00
parent 829b21fb02
commit ab4f1924db
16 changed files with 1714 additions and 34 deletions

View File

@ -4,6 +4,7 @@
#include "lldb/lldb-private.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/ConstString.h"
#include <map>
#include <vector>
@ -69,18 +70,32 @@ public:
void SetSame();
bool IsSame () const { return m_type == isSame; }
bool IsUnspecified () const { return m_type == unspecified; }
bool IsCFAPlusOffset () const { return m_type == isCFAPlusOffset; }
bool IsAtCFAPlusOffset () const { return m_type == atCFAPlusOffset; }
bool IsInOtherRegister () const { return m_type == inOtherRegister; }
bool IsAtDWARFExpression () const { return m_type == atDWARFExpression; }
bool IsDWARFExpression () const { return m_type == isDWARFExpression; }
void SetAtCFAPlusOffset (int32_t offset);
void SetIsCFAPlusOffset (int32_t offset);
void SetInRegister (uint32_t reg_num);
uint32_t GetRegisterNumber () const { return m_location.reg_num; }
RestoreType GetLocationType () const { return m_type; }
int32_t GetOffset () const { return m_location.offset; }
uint32_t GetRegNum () const { return m_location.reg_num; }
void GetDWARFExpr (const uint8_t **opcodes, uint16_t& len) const { *opcodes = m_location.expr.opcodes; len = m_location.expr.length; }
void
@ -159,6 +174,13 @@ public:
m_cfa_offset = offset;
}
// Return the number of registers we have locations for
int
GetRegisterCount () const
{
return m_register_locations.size();
}
void
Clear ();
@ -176,13 +198,20 @@ public:
public:
UnwindPlan () : m_register_kind(-1), m_row_list(), m_plan_valid_address_range() { }
UnwindPlan () : m_register_kind(-1), m_row_list(), m_plan_valid_address_range(), m_source_name()
{
m_plan_valid_address_range.SetByteSize (0);
}
void Dump (Stream& s, Thread* thread) const;
void
AppendRow (const Row& row);
// Returns a pointer to the best row for the given offset into the function's instructions.
// If offset is -1 it indicates that the function start is unknown - the final row in the UnwindPlan is returned.
// In practice, the UnwindPlan for a function with no known start address will be the architectural default
// UnwindPlan which will only have one row.
const Row*
GetRowForFunctionOffset (int offset) const;
@ -207,6 +236,12 @@ public:
const UnwindPlan::Row&
GetRowAtIndex (uint32_t idx) const;
lldb_private::ConstString
GetSourceName () const;
void
SetSourceName (const char *);
int
GetRowCount () const;
@ -217,6 +252,7 @@ private:
AddressRange m_plan_valid_address_range;
uint32_t m_register_kind; // The RegisterKind these register numbers are in terms of - will need to be
// translated to lldb native reg nums at unwind time
lldb_private::ConstString m_source_name; // for logging, where this UnwindPlan originated from
}; // class UnwindPlan
} // namespace lldb_private

View File

@ -28,6 +28,8 @@ public:
//------------------------------------------------------------------
RegisterContext (Thread &thread, StackFrame *frame);
RegisterContext (Thread &thread);
virtual
~RegisterContext ();
@ -49,18 +51,12 @@ public:
virtual const lldb::RegisterSet *
GetRegisterSet (uint32_t reg_set) = 0;
virtual bool
ReadRegisterValue (uint32_t reg, Scalar &value) = 0;
virtual bool
ReadRegisterBytes (uint32_t reg, DataExtractor &data) = 0;
virtual bool
ReadAllRegisterValues (lldb::DataBufferSP &data_sp) = 0;
virtual bool
WriteRegisterValue (uint32_t reg, const Scalar &value) = 0;
virtual bool
WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset = 0) = 0;
@ -73,6 +69,12 @@ public:
//------------------------------------------------------------------
// Subclasses can override these functions if desired
//------------------------------------------------------------------
virtual bool
ReadRegisterValue (uint32_t reg, Scalar &value);
virtual bool
WriteRegisterValue (uint32_t reg, const Scalar &value);
virtual uint32_t
NumSupportedHardwareBreakpoints ();
@ -143,7 +145,10 @@ public:
WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval);
bool
ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t target_regnum);
ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t& target_regnum);
void
SetStackFrame (StackFrame *frame);
//------------------------------------------------------------------
// lldb::ExecutionContextScope pure virtual functions

View File

@ -34,6 +34,7 @@
#define LIBLLDB_LOG_COMMUNICATION (1u << 12)
#define LIBLLDB_LOG_CONNECTION (1u << 13)
#define LIBLLDB_LOG_HOST (1u << 14)
#define LIBLLDB_LOG_UNWIND (1u << 15)
#define LIBLLDB_LOG_ALL (UINT32_MAX)
#define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\
LIBLLDB_LOG_THREAD |\

View File

@ -406,6 +406,10 @@
9AC70390117675270086C050 /* SBInstructionList.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AC7038F117675270086C050 /* SBInstructionList.h */; settings = {ATTRIBUTES = (Public, ); }; };
9AC703AF117675410086C050 /* SBInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703AE117675410086C050 /* SBInstruction.cpp */; };
9AC703B1117675490086C050 /* SBInstructionList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703B0117675490086C050 /* SBInstructionList.cpp */; };
AF68D2561255416E002FF25B /* RegisterContextLLDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF68D2541255416E002FF25B /* RegisterContextLLDB.cpp */; };
AF68D2571255416E002FF25B /* RegisterContextLLDB.h in Headers */ = {isa = PBXBuildFile; fileRef = AF68D2551255416E002FF25B /* RegisterContextLLDB.h */; };
AF68D3311255A111002FF25B /* UnwindLLDB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF68D32F1255A110002FF25B /* UnwindLLDB.cpp */; };
AF68D3321255A111002FF25B /* UnwindLLDB.h in Headers */ = {isa = PBXBuildFile; fileRef = AF68D3301255A110002FF25B /* UnwindLLDB.h */; };
AF94005911C03F6500085DB9 /* SymbolVendor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF94005711C03F6500085DB9 /* SymbolVendor.cpp */; };
/* End PBXBuildFile section */
@ -1130,6 +1134,10 @@
9AF16A9E11402D69007A7B3F /* SBBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBBreakpoint.h; path = include/lldb/API/SBBreakpoint.h; sourceTree = "<group>"; };
9AF16CC611408686007A7B3F /* SBBreakpointLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBBreakpointLocation.h; path = include/lldb/API/SBBreakpointLocation.h; sourceTree = "<group>"; };
9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBreakpointLocation.cpp; path = source/API/SBBreakpointLocation.cpp; sourceTree = "<group>"; };
AF68D2541255416E002FF25B /* RegisterContextLLDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextLLDB.cpp; path = Utility/RegisterContextLLDB.cpp; sourceTree = "<group>"; };
AF68D2551255416E002FF25B /* RegisterContextLLDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextLLDB.h; path = Utility/RegisterContextLLDB.h; sourceTree = "<group>"; };
AF68D32F1255A110002FF25B /* UnwindLLDB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindLLDB.cpp; path = Utility/UnwindLLDB.cpp; sourceTree = "<group>"; };
AF68D3301255A110002FF25B /* UnwindLLDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnwindLLDB.h; path = Utility/UnwindLLDB.h; sourceTree = "<group>"; };
AF94005711C03F6500085DB9 /* SymbolVendor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolVendor.cpp; path = source/Symbol/SymbolVendor.cpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -1646,6 +1654,10 @@
26B4666E11A2080F00CF6220 /* Utility */ = {
isa = PBXGroup;
children = (
AF68D32F1255A110002FF25B /* UnwindLLDB.cpp */,
AF68D3301255A110002FF25B /* UnwindLLDB.h */,
AF68D2541255416E002FF25B /* RegisterContextLLDB.cpp */,
AF68D2551255416E002FF25B /* RegisterContextLLDB.h */,
96A6D9C71249D98800250B38 /* ArchVolatileRegs-x86.cpp */,
96A6D9C81249D98800250B38 /* ArchVolatileRegs-x86.h */,
961FAC1C12360C7D00F93A47 /* ArchDefaultUnwindPlan-x86.cpp */,
@ -2417,6 +2429,8 @@
4C139EA6124A8B03000BFF8D /* AppleObjCRuntimeV2.h in Headers */,
4C0A91D912511CB900CA6636 /* AppleObjCTrampolineHandler.h in Headers */,
4C0A91DB12511CB900CA6636 /* AppleThreadPlanStepThroughObjCTrampoline.h in Headers */,
AF68D2571255416E002FF25B /* RegisterContextLLDB.h in Headers */,
AF68D3321255A111002FF25B /* UnwindLLDB.h in Headers */,
26424E3F125986D30016D82C /* ValueObjectConstResult.h in Headers */,
4C1AB23F1263E61100D0F04A /* ThreadPlanTestCondition.h in Headers */,
);
@ -2917,6 +2931,8 @@
4C139EA5124A8B03000BFF8D /* AppleObjCRuntimeV2.cpp in Sources */,
4C0A91D812511CB900CA6636 /* AppleObjCTrampolineHandler.cpp in Sources */,
4C0A91DA12511CB900CA6636 /* AppleThreadPlanStepThroughObjCTrampoline.cpp in Sources */,
AF68D2561255416E002FF25B /* RegisterContextLLDB.cpp in Sources */,
AF68D3311255A111002FF25B /* UnwindLLDB.cpp in Sources */,
26424E3D125986CB0016D82C /* ValueObjectConstResult.cpp in Sources */,
4C1AB23B1263E5F400D0F04A /* ThreadPlanTestCondition.cpp in Sources */,
);

View File

@ -64,6 +64,7 @@ ArchDefaultUnwindPlan_x86::ArchDefaultUnwindPlan_x86(int cpu) :
row.SetRegisterInfo (LLDB_REGNUM_GENERIC_SP, regloc);
m_32bit_default.AppendRow (row);
m_32bit_default.SetSourceName ("architectural default");
row.Clear();
@ -80,6 +81,7 @@ ArchDefaultUnwindPlan_x86::ArchDefaultUnwindPlan_x86(int cpu) :
row.SetRegisterInfo (LLDB_REGNUM_GENERIC_SP, regloc);
m_64bit_default.AppendRow (row);
m_64bit_default.SetSourceName ("architectural default");
}

View File

@ -0,0 +1,986 @@
//===-- 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 "lldb/lldb-private.h"
#include "RegisterContextLLDB.h"
#include "lldb/Target/Thread.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/ArchDefaultUnwindPlan.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Utility/ArchVolatileRegs.h"
#include "lldb/Core/Log.h"
using namespace lldb;
using namespace lldb_private;
RegisterContextLLDB::RegisterContextLLDB (Thread& thread,
const RegisterContextSP &next_frame,
SymbolContext& sym_ctx,
int frame_number) :
RegisterContext (thread), m_thread(thread), m_next_frame(next_frame),
m_zeroth_frame(false), m_sym_ctx(sym_ctx), m_all_registers_available(false), m_registers(),
m_cfa (LLDB_INVALID_ADDRESS), m_start_pc (), m_frame_number (frame_number)
{
m_base_reg_ctx = m_thread.GetRegisterContext();
if (m_next_frame.get() == NULL)
{
InitializeZerothFrame ();
}
else
{
InitializeNonZerothFrame ();
}
}
// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently
// executing frame.
void
RegisterContextLLDB::InitializeZerothFrame()
{
m_zeroth_frame = true;
StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0));
if (m_base_reg_ctx == NULL)
{
m_frame_type = eNotAValidFrame;
return;
}
m_sym_ctx = frame_sp->GetSymbolContext (eSymbolContextEverything);
const AddressRange *addr_range_ptr;
if (m_sym_ctx.function)
addr_range_ptr = &m_sym_ctx.function->GetAddressRange();
else if (m_sym_ctx.symbol)
addr_range_ptr = m_sym_ctx.symbol->GetAddressRangePtr();
Address current_pc = frame_sp->GetFrameCodeAddress();
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;
}
// 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_ptr)
{
m_start_pc = addr_range_ptr->GetBaseAddress();
m_current_offset = frame_sp->GetFrameCodeAddress().GetOffset() - m_start_pc.GetOffset();
}
else
{
m_start_pc = current_pc;
m_current_offset = -1;
}
// We've set m_frame_type, m_zeroth_frame, and m_sym_ctx before this call.
// This call sets the m_all_registers_available, m_fast_unwind_plan, and m_full_unwind_plan member variables.
GetUnwindPlansForFrame (current_pc);
const UnwindPlan::Row *active_row = NULL;
int cfa_offset = 0;
int row_register_kind;
if (m_full_unwind_plan && m_full_unwind_plan->PlanValidAtAddress (current_pc))
{
active_row = m_full_unwind_plan->GetRowForFunctionOffset (m_current_offset);
row_register_kind = m_full_unwind_plan->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;
Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
if (log)
{
log->Printf("%*sThread %u Frame %d initialized frame current pc is 0x%llx cfa is 0x%llx",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
(uint64_t) m_cfa, (uint64_t) current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()));
}
}
// 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()
{
Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
if (m_next_frame.get() == NULL)
{
m_frame_type = eNotAValidFrame;
return;
}
if (!((RegisterContextLLDB*)m_next_frame.get())->IsValid())
{
m_frame_type = eNotAValidFrame;
return;
}
if (m_base_reg_ctx == NULL)
{
m_frame_type = eNotAValidFrame;
return;
}
m_zeroth_frame = false;
addr_t pc;
if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
{
if (log)
{
log->Printf("%*sThread %u Frame %d could not get pc value",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number);
}
m_frame_type = eNotAValidFrame;
return;
}
Address current_pc;
m_thread.GetProcess().GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, 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 (!current_pc.IsValid() || current_pc.GetModule() == NULL)
{
if (log)
{
log->Printf("%*sThread %u Frame %d using architectural default unwind method",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number);
}
ArchSpec arch = m_thread.GetProcess().GetTarget().GetArchitecture ();
ArchDefaultUnwindPlan *arch_default = ArchDefaultUnwindPlan::FindPlugin (arch);
if (arch_default)
{
m_fast_unwind_plan = NULL;
m_full_unwind_plan = arch_default->GetArchDefaultUnwindPlan (m_thread, current_pc);
m_frame_type = eNormalFrame;
m_all_registers_available = false;
m_current_offset = -1;
addr_t cfa_regval;
int row_register_kind = m_full_unwind_plan->GetRegisterKind ();
uint32_t cfa_regnum = m_full_unwind_plan->GetRowForFunctionOffset(0)->GetCFARegister();
int cfa_offset = m_full_unwind_plan->GetRowForFunctionOffset(0)->GetCFAOffset();
if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval))
{
if (log)
{
log->Printf("%*sThread %u Frame %d failed to get cfa value",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number);
}
m_frame_type = eNormalFrame;
return;
}
m_cfa = cfa_regval + cfa_offset;
if (log)
{
log->Printf("%*sThread %u Frame %d initialized frame current pc is 0x%llx cfa is 0x%llx",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
(uint64_t) m_cfa, (uint64_t) current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()));
}
return;
}
m_frame_type = eNotAValidFrame;
return;
}
// set up our m_sym_ctx SymbolContext
current_pc.GetModule()->ResolveSymbolContextForAddress (current_pc, eSymbolContextFunction | eSymbolContextSymbol, m_sym_ctx);
const AddressRange *addr_range_ptr;
if (m_sym_ctx.function)
addr_range_ptr = &m_sym_ctx.function->GetAddressRange();
else if (m_sym_ctx.symbol)
addr_range_ptr = m_sym_ctx.symbol->GetAddressRangePtr();
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;
}
// 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_ptr)
{
m_start_pc = addr_range_ptr->GetBaseAddress();
m_current_offset = current_pc.GetOffset() - m_start_pc.GetOffset();
}
else
{
m_start_pc = current_pc;
m_current_offset = -1;
}
// We've set m_frame_type, m_zeroth_frame, and m_sym_ctx before this call.
// This call sets the m_all_registers_available, m_fast_unwind_plan, and m_full_unwind_plan member variables.
GetUnwindPlansForFrame (current_pc);
const UnwindPlan::Row *active_row = NULL;
int cfa_offset = 0;
int row_register_kind;
if (m_fast_unwind_plan && m_fast_unwind_plan->PlanValidAtAddress (current_pc))
{
active_row = m_fast_unwind_plan->GetRowForFunctionOffset (m_current_offset);
row_register_kind = m_fast_unwind_plan->GetRegisterKind ();
}
else if (m_full_unwind_plan && m_full_unwind_plan->PlanValidAtAddress (current_pc))
{
active_row = m_full_unwind_plan->GetRowForFunctionOffset (m_current_offset);
row_register_kind = m_full_unwind_plan->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("%*sThread %u Frame %d failed to get cfa reg %d/%d",
m_frame_number, "", m_thread.GetIndexID(), 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;
if (log)
{
log->Printf("%*sThread %u Frame %d initialized frame current pc is 0x%llx cfa is 0x%llx",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
(uint64_t) m_cfa, (uint64_t) current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()));
}
}
// On entry to this method,
//
// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame
// if either of those are correct, and
// 2. m_zeroth_frame should be set to true if this is frame 0 and
// 3. m_sym_ctx should already be filled in.
//
// On exit this function will have set
//
// a. m_all_registers_available (true if we can provide any requested register, false if only a subset are provided)
// b. m_fast_unwind_plan (fast unwind plan that walks the stack while filling in only minimal registers, may be NULL)
// c. m_full_unwind_plan (full unwind plan that can provide all registers possible, will *not* be NULL)
//
// The argument current_pc should be the current pc value in the function.
void
RegisterContextLLDB::GetUnwindPlansForFrame (Address current_pc)
{
UnwindPlan *arch_default_up = NULL;
ArchSpec arch = m_thread.GetProcess().GetTarget().GetArchitecture ();
ArchDefaultUnwindPlan *arch_default = ArchDefaultUnwindPlan::FindPlugin (arch);
if (arch_default)
{
arch_default_up = arch_default->GetArchDefaultUnwindPlan (m_thread, current_pc);
}
bool behaves_like_zeroth_frame = false;
if (m_zeroth_frame)
{
behaves_like_zeroth_frame = true;
}
if (m_next_frame.get() && ((RegisterContextLLDB*) m_next_frame.get())->m_frame_type == eSigtrampFrame)
{
behaves_like_zeroth_frame = true;
}
if (m_next_frame.get() && ((RegisterContextLLDB*) m_next_frame.get())->m_frame_type == eDebuggerFrame)
{
behaves_like_zeroth_frame = true;
}
if (behaves_like_zeroth_frame)
{
m_all_registers_available = true;
}
else
{
// If we need to implement gdb's decrement-pc-value-by-one-before-function-check macro, it would be here.
// current_pc.SetOffset (current_pc.GetOffset() - 1);
m_all_registers_available = false;
}
// No Module for the current pc, try using the architecture default unwind.
if (current_pc.GetModule() == NULL || current_pc.GetModule()->GetObjectFile() == NULL)
{
m_fast_unwind_plan = NULL;
m_full_unwind_plan = arch_default_up;
m_frame_type = eNormalFrame;
return;
}
FuncUnwindersSP fu;
if (current_pc.GetModule() && current_pc.GetModule()->GetObjectFile())
{
fu = current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (current_pc, m_sym_ctx);
}
// No FuncUnwinders available for this pc, try using architectural default unwind.
if (fu.get() == NULL)
{
m_fast_unwind_plan = NULL;
m_full_unwind_plan = arch_default_up;
m_frame_type = eNormalFrame;
return;
}
// 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.
if (m_frame_type == eSigtrampFrame)
{
m_fast_unwind_plan = NULL;
UnwindPlan *up = fu->GetUnwindPlanAtCallSite ();
if (up->PlanValidAtAddress (current_pc))
{
m_fast_unwind_plan = NULL;
m_full_unwind_plan = up;
return;
}
}
UnwindPlan *fast, *callsite, *noncallsite;
fast = callsite = noncallsite = NULL;
if (fu->GetUnwindPlanFastUnwind (m_thread)
&& fu->GetUnwindPlanFastUnwind (m_thread)->PlanValidAtAddress (current_pc))
{
fast = fu->GetUnwindPlanFastUnwind (m_thread);
}
// Typically this is the unwind created by inspecting the assembly language instructions
if (fu->GetUnwindPlanAtNonCallSite (m_thread)
&& fu->GetUnwindPlanAtNonCallSite (m_thread)->PlanValidAtAddress (current_pc))
{
noncallsite = fu->GetUnwindPlanAtNonCallSite (m_thread);
}
// Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites
if (fu->GetUnwindPlanAtCallSite ()
&& fu->GetUnwindPlanAtCallSite ()->PlanValidAtAddress (current_pc))
{
callsite = fu->GetUnwindPlanAtCallSite ();
}
m_fast_unwind_plan = NULL;
m_full_unwind_plan = NULL;
if (fast)
{
m_fast_unwind_plan = fast;
}
if (behaves_like_zeroth_frame && noncallsite)
{
m_full_unwind_plan = noncallsite;
}
else
{
if (callsite)
{
m_full_unwind_plan = callsite;
}
else
{
m_full_unwind_plan = noncallsite;
}
}
if (m_full_unwind_plan == NULL)
{
m_full_unwind_plan = arch_default_up;
}
Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
if (log)
{
const char *has_fast = "";
if (m_fast_unwind_plan)
has_fast = ", and has a fast UnwindPlan";
log->Printf("%*sThread %u Frame %d frame uses %s for full UnwindPlan%s",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
m_full_unwind_plan->GetSourceName().GetCString(), has_fast);
}
return;
}
void
RegisterContextLLDB::Invalidate ()
{
m_frame_type = eNotAValidFrame;
}
size_t
RegisterContextLLDB::GetRegisterCount ()
{
return m_base_reg_ctx->GetRegisterCount();
}
const RegisterInfo *
RegisterContextLLDB::GetRegisterInfoAtIndex (uint32_t reg)
{
return m_base_reg_ctx->GetRegisterInfoAtIndex (reg);
}
size_t
RegisterContextLLDB::GetRegisterSetCount ()
{
return m_base_reg_ctx->GetRegisterSetCount ();
}
const RegisterSet *
RegisterContextLLDB::GetRegisterSet (uint32_t reg_set)
{
return m_base_reg_ctx->GetRegisterSet (reg_set);
}
uint32_t
RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
{
return m_base_reg_ctx->ConvertRegisterKindToRegisterNumber (kind, num);
}
bool
RegisterContextLLDB::ReadRegisterBytesFromRegisterLocation (uint32_t regnum, RegisterLocation regloc, DataExtractor &data)
{
if (!IsValid())
return false;
if (regloc.type == eRegisterInRegister)
{
data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
data.SetByteOrder (m_thread.GetProcess().GetByteOrder());
if (m_next_frame.get() == NULL)
{
return m_base_reg_ctx->ReadRegisterBytes (regloc.location.register_number, data);
}
else
{
return m_next_frame->ReadRegisterBytes (regloc.location.register_number, data);
}
}
if (regloc.type == eRegisterNotSaved)
{
return false;
}
if (regloc.type == eRegisterSavedAtHostMemoryLocation)
{
assert ("FIXME debugger inferior function call unwind");
}
if (regloc.type != eRegisterSavedAtMemoryLocation)
{
assert ("Unknown RegisterLocation type.");
}
const RegisterInfo *reg_info = m_base_reg_ctx->GetRegisterInfoAtIndex (regnum);
DataBufferSP data_sp (new DataBufferHeap (reg_info->byte_size, 0));
data.SetData (data_sp, 0, reg_info->byte_size);
data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
if (regloc.type == eRegisterValueInferred)
{
data.SetByteOrder (eByteOrderHost);
switch (reg_info->byte_size)
{
case 1:
{
uint8_t val = regloc.location.register_value;
memcpy (data_sp->GetBytes(), &val, sizeof (val));
data.SetByteOrder (eByteOrderHost);
return true;
}
case 2:
{
uint16_t val = regloc.location.register_value;
memcpy (data_sp->GetBytes(), &val, sizeof (val));
data.SetByteOrder (eByteOrderHost);
return true;
}
case 4:
{
uint32_t val = regloc.location.register_value;
memcpy (data_sp->GetBytes(), &val, sizeof (val));
data.SetByteOrder (eByteOrderHost);
return true;
}
case 8:
{
uint64_t val = regloc.location.register_value;
memcpy (data_sp->GetBytes(), &val, sizeof (val));
data.SetByteOrder (eByteOrderHost);
return true;
}
}
return false;
}
assert (regloc.type == eRegisterSavedAtMemoryLocation);
Error error;
data.SetByteOrder (m_thread.GetProcess().GetByteOrder());
if (!m_thread.GetProcess().ReadMemory (regloc.location.target_memory_location, data_sp->GetBytes(), reg_info->byte_size, error))
return false;
return true;
}
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)
{
Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
// Have we already found this register location?
std::map<uint32_t, RegisterLocation>::const_iterator iterator;
if (m_registers.size() > 0)
{
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_base_reg_ctx->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 (.register_value)
assert (sizeof (addr_t) <= sizeof (uint64_t));
regloc.type = eRegisterValueInferred;
regloc.location.register_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;
if (m_fast_unwind_plan)
{
const UnwindPlan::Row *active_row = m_fast_unwind_plan->GetRowForFunctionOffset (m_current_offset);
uint32_t row_regnum;
if (!m_base_reg_ctx->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, m_fast_unwind_plan->GetRegisterKind(), row_regnum))
{
if (log)
{
log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
return false;
}
if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
{
if (log)
{
log->Printf("%*sThread %u Frame %d supplying caller's saved reg %d's location using FastUnwindPlan",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
have_unwindplan_regloc = true;
}
}
else if (m_full_unwind_plan)
{
const UnwindPlan::Row *active_row = m_full_unwind_plan->GetRowForFunctionOffset (m_current_offset);
uint32_t row_regnum;
if (!m_base_reg_ctx->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, m_full_unwind_plan->GetRegisterKind(), row_regnum))
{
if (log)
{
log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
return false;
}
if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
{
have_unwindplan_regloc = true;
if (log)
{
log->Printf("%*sThread %u Frame %d supplying caller's saved reg %d's location using %s UnwindPlan",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum, m_full_unwind_plan->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.
ArchSpec arch = m_thread.GetProcess().GetTarget().GetArchitecture ();
ArchVolatileRegs *volatile_regs = ArchVolatileRegs::FindPlugin (arch);
if (volatile_regs && volatile_regs->RegisterIsVolatile (m_thread, lldb_regnum))
{
if (log)
{
log->Printf("%*sThread %u Frame %d did not supply reg location for %d because it is volatile",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
return false;
}
if (m_next_frame.get())
{
return ((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_regnum, regloc);
}
else
{
// 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;
if (log)
{
log->Printf("%*sThread %u Frame %d register %d is in the thread's live register context",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
return true;
}
if (log)
{
log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), 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("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
return false;
}
if (unwindplan_regloc.IsSame())
{
if (m_next_frame.get())
{
return ((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_regnum, regloc);
}
else
{
if (log)
{
log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_regnum);
}
return false;
}
}
if (unwindplan_regloc.IsCFAPlusOffset())
{
int offset = unwindplan_regloc.GetOffset();
regloc.type = eRegisterValueInferred;
regloc.location.register_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_base_reg_ctx->ConvertBetweenRegisterKinds (m_full_unwind_plan->GetRegisterKind(), unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb))
{
if (log)
{
log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), 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 (log)
{
log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
m_frame_number, "", m_thread.GetIndexID(), 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.
// Assumes m_base_reg_ctx has been set
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_base_reg_ctx->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindLLDB, lldb_regnum))
{
return false;
}
uint32_t offset = 0;
DataExtractor data;
data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
data.SetByteOrder (m_thread.GetProcess().GetByteOrder());
// if this is frame 0 (currently executing frame), get the requested reg contents from the actual thread registers
if (m_next_frame.get() == NULL)
{
if (m_base_reg_ctx->ReadRegisterBytes (lldb_regnum, data))
{
data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
value = data.GetAddress (&offset);
return true;
}
else
{
return false;
}
}
RegisterLocation regloc;
if (!((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_regnum, regloc))
{
return false;
}
if (!ReadRegisterBytesFromRegisterLocation (lldb_regnum, regloc, data))
{
return false;
}
data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
value = data.GetAddress (&offset);
return true;
}
// Find the value of a register in THIS frame
bool
RegisterContextLLDB::ReadRegisterBytes (uint32_t lldb_reg, DataExtractor& data)
{
Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
if (!IsValid())
return false;
if (log)
{
log->Printf("%*sThread %u Frame %d looking for register saved location for reg %d",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_reg);
}
// If this is the 0th frame, hand this over to the live register context
if (m_next_frame.get() == NULL)
{
if (log)
{
log->Printf("%*sThread %u Frame %d passing along to the live register context for reg %d",
m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
lldb_reg);
}
return m_base_reg_ctx->ReadRegisterBytes (lldb_reg, data);
}
RegisterLocation regloc;
// Find out where the NEXT frame saved THIS frame's register contents
if (!((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_reg, regloc))
return false;
return ReadRegisterBytesFromRegisterLocation (lldb_reg, regloc, data);
}
bool
RegisterContextLLDB::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
{
assert ("not yet implemented"); // FIXME
return false;
}
bool
RegisterContextLLDB::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
{
assert ("not yet implemented"); // FIXME
return false;
}
bool
RegisterContextLLDB::WriteAllRegisterValues (const lldb::DataBufferSP& data_sp)
{
assert ("not yet implemented"); // FIXME
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 GetPC (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::GetPC (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;
}
}

View File

@ -0,0 +1,177 @@
//===-- RegisterContextLLDB.h --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef lldb_RegisterContextLLDB_h_
#define lldb_RegisterContextLLDB_h_
#include <vector>
#include "lldb/lldb-private.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Symbol/SymbolContext.h"
class RegisterContextLLDB : public lldb_private::RegisterContext
{
public:
RegisterContextLLDB (lldb_private::Thread &thread,
const lldb::RegisterContextSP& next_frame,
lldb_private::SymbolContext& sym_ctx,
int frame_number);
///
// pure virtual functions from the base class that we must implement
///
virtual
~RegisterContextLLDB () { }
virtual void
Invalidate ();
virtual size_t
GetRegisterCount ();
virtual const lldb::RegisterInfo *
GetRegisterInfoAtIndex (uint32_t reg);
virtual size_t
GetRegisterSetCount ();
virtual const lldb::RegisterSet *
GetRegisterSet (uint32_t reg_set);
virtual bool
ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
virtual bool
ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
virtual bool
WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0);
virtual bool
WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
virtual uint32_t
ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
bool
IsValid () const;
bool
GetCFA (lldb::addr_t& cfa);
bool
GetStartPC (lldb::addr_t& start_pc);
bool
GetPC (lldb::addr_t& start_pc);
private:
typedef enum FrameType
{
eNormalFrame,
eSigtrampFrame,
eDebuggerFrame, // a debugger inferior function call frame; we get caller's registers from debugger
eNotAValidFrame // this frame is invalid for some reason - most likely it is past the top (end) of the stack
};
enum RegisterLocationTypes
{
eRegisterNotSaved = 0, // register was not preserved by callee. If volatile reg, is unavailable
eRegisterSavedAtMemoryLocation, // register is saved at a specific word of target mem (target_memory_location)
eRegisterInRegister, // register is available in a (possible other) register (register_number)
eRegisterSavedAtHostMemoryLocation, // register is saved at a word in lldb's address space
eRegisterValueInferred // register val was computed (and is in register_value)
};
struct RegisterLocation
{
int type;
union
{
lldb::addr_t target_memory_location;
uint32_t register_number; // in eRegisterKindLLDB register numbering system
void* host_memory_location;
uint64_t register_value; // eRegisterValueInferred - e.g. stack pointer == cfa + offset
} location;
};
void
InitializeZerothFrame ();
void
InitializeNonZerothFrame();
// Provide a location for where THIS function saved the CALLER's register value
// Or a frame "below" this one savedit, i.e. a function called by this one, preserved a register that this
// function didn't modify/use.
//
// The RegisterLocation type may be set to eRegisterNotAvailable -- this will happen for a volatile register
// bieng queried mid-stack. Instead of floating frame 0's contents of that register up the stack (which may
// or may not be the value of that reg when the function was executing), we won't return any value.
//
// If a non-volatile register (a "preserved" register) is requested mid-stack and no frames "below" the requested
// stack have saved the register anywhere, it is safe to assume that frame 0's register values are still the same
// as the requesting frame's.
//
bool
SavedLocationForRegister (uint32_t lldb_regnum, RegisterLocation &regloc);
bool
ReadRegisterBytesFromRegisterLocation (uint32_t regnum, RegisterLocation regloc, lldb_private::DataExtractor &data);
bool
WriteRegisterBytesFromRegisterLocation (uint32_t regnum, RegisterLocation regloc, lldb_private::Scalar value);
// Get the contents of a general purpose (address-size) register for this frame
// (usually retrieved from the m_next_frame)
// m_base_reg_ectx and m_next_frame should both be initialized appropriately before calling.
bool
ReadGPRValue (int register_kind, uint32_t regnum, lldb::addr_t &value);
void
GetUnwindPlansForFrame (lldb_private::Address current_pc);
lldb_private::Thread& m_thread;
lldb::RegisterContextSP m_next_frame;
lldb_private::RegisterContext *m_base_reg_ctx; // RegisterContext of frame 0 (live register values only)
///
// The following tell us how to retrieve the CALLER's register values (ie the "previous" frame, aka the frame above)
// i.e. where THIS frame saved them
///
lldb_private::UnwindPlan *m_fast_unwind_plan; // may be NULL
lldb_private::UnwindPlan *m_full_unwind_plan;
bool m_zeroth_frame; // Is this the bottom-most, i.e. currently executing, frame?
bool m_all_registers_available; // Can we retrieve all regs or just nonvolatile regs?
int m_frame_type; // enum FrameType
int m_current_offset; // how far into the function we've executed; -1 if unknown
lldb_private::SymbolContext& m_sym_ctx;
int m_frame_number; // What stack frame level this frame is - used for debug logging
lldb::addr_t m_cfa;
lldb_private::Address m_start_pc;
std::map<uint32_t, RegisterLocation> m_registers; // where to find reg values for this frame
//------------------------------------------------------------------
// For RegisterContextLLDB only
//------------------------------------------------------------------
DISALLOW_COPY_AND_ASSIGN (RegisterContextLLDB);
};
#endif // lldb_RegisterContextLLDB_h_

View File

@ -125,7 +125,7 @@ public:
bool get_non_call_site_unwind_plan (UnwindPlan &unwind_plan);
bool get_fast_unwind_plan (UnwindPlan &unwind_plan);
bool get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan);
bool find_first_non_prologue_insn (Address &address);
@ -166,7 +166,7 @@ private:
AssemblyParse_x86::AssemblyParse_x86 (Target& target, Thread* thread, int cpu, AddressRange func) :
m_target (target), m_thread (thread), m_cpu(cpu), m_func_bounds(func)
{
{
int *initialized_flag = NULL;
m_lldb_ip_regnum = m_lldb_sp_regnum = m_lldb_fp_regnum = -1;
if (cpu == k_i386)
@ -489,7 +489,7 @@ AssemblyParse_x86::instruction_length (Address addr, int &length)
if (EDGetDisassembler (&disasm, "i386-apple-darwin", kEDAssemblySyntaxX86ATT) != 0)
{
false;
return false;
}
uint64_t addr_offset = addr.GetOffset();
@ -498,7 +498,7 @@ AssemblyParse_x86::instruction_length (Address addr, int &length)
arg.target = &m_target;
if (EDCreateInsts (&cur_insn, 1, disasm, read_byte_for_edis, addr_offset, &arg) != 1)
{
false;
return false;
}
length = EDInstByteSize (cur_insn);
EDReleaseInst (cur_insn);
@ -566,12 +566,11 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan)
goto loopnext;
}
// This is the start() function (or a pthread equivalent), it starts with a pushl $0x0 which puts the
// saved pc value of 0 on the stack. In this case we want to pretend we didn't see a stack movement at all --
// normally the saved pc value is already on the stack by the time the function starts executing.
if (push_0_pattern_p ())
{
row.SetOffset (current_func_text_offset + insn_len);
current_sp_bytes_offset_from_cfa += m_wordsize;
row.SetCFAOffset (current_sp_bytes_offset_from_cfa);
unwind_plan.AppendRow (row);
goto loopnext;
}
@ -648,14 +647,78 @@ loopnext:
current_func_text_offset += insn_len;
}
unwind_plan.SetSourceName ("assembly insn profiling");
return true;
}
/* The "fast unwind plan" is valid for functions that follow the usual convention of
using the frame pointer register (ebp, rbp), i.e. the function prologue looks like
push %rbp [0x55]
mov %rsp,%rbp [0x48 0x89 0xe5] (this is a 2-byte insn seq on i386)
*/
bool
AssemblyParse_x86::get_fast_unwind_plan (UnwindPlan &unwind_plan)
AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan)
{
UnwindPlan up;
return false;
UnwindPlan::Row row;
UnwindPlan::Row::RegisterLocation pc_reginfo;
UnwindPlan::Row::RegisterLocation sp_reginfo;
UnwindPlan::Row::RegisterLocation fp_reginfo;
unwind_plan.SetRegisterKind (eRegisterKindLLDB);
if (!func.GetBaseAddress().IsValid())
return false;
uint8_t bytebuf[4];
Error error;
if (m_target.ReadMemory (func.GetBaseAddress(), bytebuf, sizeof (bytebuf), error) == -1)
return false;
uint8_t i386_prologue[] = {0x55, 0x89, 0xe5};
uint8_t x86_64_prologue[] = {0x55, 0x48, 0x89, 0xe5};
int prologue_size;
if (memcmp (bytebuf, i386_prologue, sizeof (i386_prologue)) == 0)
{
prologue_size = sizeof (i386_prologue);
}
else if (memcmp (bytebuf, x86_64_prologue, sizeof (x86_64_prologue)) == 0)
{
prologue_size = sizeof (x86_64_prologue);
}
else
{
return false;
}
pc_reginfo.SetAtCFAPlusOffset (-m_wordsize);
row.SetRegisterInfo (m_lldb_ip_regnum, pc_reginfo);
sp_reginfo.SetIsCFAPlusOffset (0);
row.SetRegisterInfo (m_lldb_sp_regnum, sp_reginfo);
// Zero instructions into the function
row.SetCFARegister (m_lldb_sp_regnum);
row.SetCFAOffset (m_wordsize);
row.SetOffset (0);
unwind_plan.AppendRow (row);
// push %rbp has executed - stack moved, rbp now saved
row.SetCFAOffset (2 * m_wordsize);
fp_reginfo.SetAtCFAPlusOffset (2 * -m_wordsize);
row.SetRegisterInfo (m_lldb_fp_regnum, fp_reginfo);
row.SetOffset (1);
unwind_plan.AppendRow (row);
// mov %rsp, %rbp has executed
row.SetCFARegister (m_lldb_fp_regnum);
row.SetCFAOffset (2 * m_wordsize);
row.SetOffset (prologue_size); /// 3 or 4 bytes depending on arch
unwind_plan.AppendRow (row);
unwind_plan.SetPlanValidAddressRange (func);
return true;
}
bool
@ -712,7 +775,7 @@ bool
UnwindAssemblyProfiler_x86::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan)
{
AssemblyParse_x86 asm_parse(thread.GetProcess().GetTarget(), &thread, m_cpu, func);
return asm_parse.get_fast_unwind_plan (unwind_plan);
return asm_parse.get_fast_unwind_plan (func, unwind_plan);
}
bool

View File

@ -0,0 +1,141 @@
//===-- UnwindLLDB.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/Thread.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Utility/ArchDefaultUnwindPlan.h"
#include "UnwindLLDB.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Core/Log.h"
using namespace lldb;
using namespace lldb_private;
UnwindLLDB::UnwindLLDB (Thread &thread) :
Unwind (thread),
m_frames()
{
}
uint32_t
UnwindLLDB::GetFrameCount()
{
Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
if (m_frames.empty())
{
// First, set up the 0th (initial) frame
Cursor first_cursor;
RegisterContextSP no_frame; // an empty shared pointer
RegisterContextLLDB *first_register_ctx = new RegisterContextLLDB(m_thread, no_frame, first_cursor.sctx, 0);
if (!first_register_ctx->IsValid())
{
delete first_register_ctx;
return 0;
}
if (!first_register_ctx->GetCFA (first_cursor.cfa))
{
delete first_register_ctx;
return 0;
}
if (!first_register_ctx->GetPC (first_cursor.start_pc))
{
delete first_register_ctx;
return 0;
}
// Reuse the StackFrame provided by the processor native machine context for the first frame
first_register_ctx->SetStackFrame (m_thread.GetStackFrameAtIndex(0).get());
RegisterContextSP temp_rcs(first_register_ctx);
first_cursor.reg_ctx = temp_rcs;
m_frames.push_back (first_cursor);
// Now walk up the rest of the stack
while (1)
{
Cursor cursor;
RegisterContextLLDB *register_ctx;
int cur_idx = m_frames.size ();
register_ctx = new RegisterContextLLDB (m_thread, m_frames[cur_idx - 1].reg_ctx, cursor.sctx, cur_idx);
if (!register_ctx->IsValid())
{
delete register_ctx;
if (log)
{
log->Printf("%*sThread %u Frame %d invalid RegisterContext for this frame, stopping stack walk",
cur_idx, "", m_thread.GetIndexID(), cur_idx);
}
break;
}
if (!register_ctx->GetCFA (cursor.cfa))
{
delete register_ctx;
if (log)
{
log->Printf("%*sThread %u Frame %d did not get CFA for this frame, stopping stack walk",
cur_idx, "", m_thread.GetIndexID(), cur_idx);
}
break;
}
if (!register_ctx->GetPC (cursor.start_pc))
{
delete register_ctx;
if (log)
{
log->Printf("%*sThread %u Frame %d did not get PC for this frame, stopping stack walk",
cur_idx, "", m_thread.GetIndexID(), cur_idx);
}
break;
}
RegisterContextSP temp_rcs(register_ctx);
StackFrame *frame = new StackFrame(cur_idx, cur_idx, m_thread, temp_rcs, cursor.cfa, cursor.start_pc, &cursor.sctx);
register_ctx->SetStackFrame (frame);
cursor.reg_ctx = temp_rcs;
m_frames.push_back (cursor);
}
}
return m_frames.size ();
}
bool
UnwindLLDB::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc)
{
// FIXME don't get the entire stack if it isn't needed.
if (m_frames.size() == 0)
GetFrameCount();
if (idx < m_frames.size ())
{
cfa = m_frames[idx].cfa;
pc = m_frames[idx].start_pc;
return true;
}
return false;
}
RegisterContext *
UnwindLLDB::CreateRegisterContextForFrame (StackFrame *frame)
{
uint32_t idx = frame->GetFrameIndex ();
// FIXME don't get the entire stack if it isn't needed.
if (m_frames.size() == 0)
GetFrameCount();
if (idx == 0)
{
return m_thread.GetRegisterContext();
}
if (idx < m_frames.size ())
return m_frames[idx].reg_ctx.get();
return NULL;
}

View File

@ -0,0 +1,70 @@
//===-- UnwindLLDB.h --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef lldb_UnwindLLDB_h_
#define lldb_UnwindLLDB_h_
#include "lldb/lldb-private.h"
#include "lldb/Target/Unwind.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "RegisterContextLLDB.h"
#include "lldb/Target/RegisterContext.h"
#include <vector>
namespace lldb_private {
class UnwindLLDB : public lldb_private::Unwind
{
public:
UnwindLLDB (lldb_private::Thread &thread);
virtual
~UnwindLLDB() { }
void
Clear()
{
m_frames.clear();
}
virtual uint32_t
GetFrameCount();
bool
GetFrameInfoAtIndex (uint32_t frame_idx,
lldb::addr_t& cfa,
lldb::addr_t& start_pc);
lldb_private::RegisterContext *
CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
private:
struct Cursor
{
lldb::addr_t start_pc; // The start address of the function/symbol for this frame - current pc if unknown
lldb::addr_t cfa; // The canonical frame address for this stack frame
lldb_private::SymbolContext sctx; // A symbol context we'll contribute to & provide to the StackFrame creation
lldb::RegisterContextSP reg_ctx; // These are all RegisterContextLLDB's
Cursor () : start_pc (LLDB_INVALID_ADDRESS), cfa (LLDB_INVALID_ADDRESS), sctx(), reg_ctx() { }
};
std::vector<Cursor> m_frames;
//------------------------------------------------------------------
// For UnwindLLDB only
//------------------------------------------------------------------
DISALLOW_COPY_AND_ASSIGN (UnwindLLDB);
};
}
#endif // lldb_UnwindLLDB_h_

View File

@ -347,7 +347,14 @@ DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t offset, Address startaddr, Unwi
// is relative to the FDE offset, into a __eh_frame section
// offset
if (m_is_eh_frame)
{
unwind_plan.SetSourceName ("eh_frame CFI");
cie_offset = current_entry + 4 - cie_offset;
}
else
{
unwind_plan.SetSourceName ("DWARF CFI");
}
const CIE *cie = GetCIE (cie_offset);
assert (cie != NULL);

View File

@ -71,9 +71,14 @@ UnwindPlan*
FuncUnwinders::GetUnwindPlanAtNonCallSite (Thread& thread)
{
if (m_unwind_at_non_call_site != NULL)
m_unwind_at_non_call_site;
m_unwind_at_non_call_site = new UnwindPlan;
m_assembly_profiler->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *m_unwind_at_non_call_site);
return m_unwind_at_non_call_site;
UnwindPlan *up = new UnwindPlan;
if (!m_assembly_profiler->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *up))
{
delete up;
return NULL;
}
m_unwind_at_non_call_site = up;
return m_unwind_at_non_call_site;
}
@ -82,8 +87,13 @@ FuncUnwinders::GetUnwindPlanFastUnwind (Thread& thread)
{
if (m_fast_unwind != NULL)
return m_fast_unwind;
m_fast_unwind = new UnwindPlan;
m_assembly_profiler->GetFastUnwindPlan (m_range, thread, *m_fast_unwind);
UnwindPlan *up = new UnwindPlan;
if (!m_assembly_profiler->GetFastUnwindPlan (m_range, thread, *up))
{
delete up;
return NULL;
}
m_fast_unwind = up;
return m_fast_unwind;
}

View File

@ -11,6 +11,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Thread.h"
#include "lldb/Core/ConstString.h"
using namespace lldb;
using namespace lldb_private;
@ -220,6 +221,10 @@ const UnwindPlan::Row *
UnwindPlan::GetRowForFunctionOffset (int offset) const
{
const UnwindPlan::Row *rowp = NULL;
if (offset == -1 && m_row_list.size() > 0)
{
return &m_row_list[m_row_list.size() - 1];
}
for (int i = 0; i < m_row_list.size(); ++i)
{
if (m_row_list[i].GetOffset() <= offset)
@ -268,7 +273,10 @@ UnwindPlan::GetRegisterKind (void) const
void
UnwindPlan::SetPlanValidAddressRange (const AddressRange& range)
{
m_plan_valid_address_range = range;
if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
{
m_plan_valid_address_range = range;
}
// .GetBaseAddress() = addr;
// m_plan_valid_address_range.SetByteSize (range.GetByteSize());
}
@ -276,7 +284,7 @@ UnwindPlan::SetPlanValidAddressRange (const AddressRange& range)
bool
UnwindPlan::PlanValidAtAddress (Address addr)
{
if (!m_plan_valid_address_range.GetBaseAddress().IsValid())
if (!m_plan_valid_address_range.GetBaseAddress().IsValid() || m_plan_valid_address_range.GetByteSize() == 0)
return true;
if (m_plan_valid_address_range.ContainsFileAddress (addr))
@ -288,9 +296,20 @@ UnwindPlan::PlanValidAtAddress (Address addr)
void
UnwindPlan::Dump (Stream& s, Thread *thread) const
{
s.Printf ("Address range of this UnwindPlan: ");
m_plan_valid_address_range.Dump (&s, &thread->GetProcess().GetTarget(), Address::DumpStyleSectionNameOffset);
s.Printf ("\n");
if (!m_source_name.IsEmpty())
{
s.Printf ("This UnwindPlan originally sourced from %s\n", m_source_name.GetCString());
}
if (m_plan_valid_address_range.GetBaseAddress().IsValid() && m_plan_valid_address_range.GetByteSize() > 0)
{
s.Printf ("Address range of this UnwindPlan: ");
m_plan_valid_address_range.Dump (&s, &thread->GetProcess().GetTarget(), Address::DumpStyleSectionNameOffset);
s.Printf ("\n");
}
else
{
s.Printf ("No valid address range recorded for this UnwindPlan.\n");
}
s.Printf ("UnwindPlan register kind %d", m_register_kind);
switch (m_register_kind)
{
@ -308,3 +327,15 @@ UnwindPlan::Dump (Stream& s, Thread *thread) const
m_row_list[i].Dump(s, m_register_kind, thread);
}
}
void
UnwindPlan::SetSourceName (const char *source)
{
m_source_name = ConstString (source);
}
ConstString
UnwindPlan::GetSourceName () const
{
return m_source_name;
}

View File

@ -12,6 +12,7 @@
// Other libraries and framework includes
// Project includes
#include "lldb/Target/RegisterContext.h"
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/StackFrame.h"
@ -21,7 +22,7 @@ using namespace lldb;
using namespace lldb_private;
//----------------------------------------------------------------------
// RegisterContext constructor
// RegisterContext constructors
//----------------------------------------------------------------------
RegisterContext::RegisterContext (Thread &thread, StackFrame *frame) :
m_thread (thread),
@ -29,6 +30,12 @@ RegisterContext::RegisterContext (Thread &thread, StackFrame *frame) :
{
}
RegisterContext::RegisterContext (Thread &thread) :
m_thread (thread),
m_frame (NULL)
{
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
@ -200,6 +207,12 @@ RegisterContext::HardwareSingleStep (bool enable)
return false;
}
void
RegisterContext::SetStackFrame (StackFrame *frame)
{
m_frame = frame;
}
Target *
RegisterContext::CalculateTarget ()
{
@ -236,7 +249,7 @@ RegisterContext::CalculateExecutionContext (ExecutionContext &exe_ctx)
bool
RegisterContext::ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t target_regnum)
RegisterContext::ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t& target_regnum)
{
const uint32_t num_registers = GetRegisterCount();
for (uint32_t reg = 0; reg < num_registers; ++reg)
@ -258,3 +271,121 @@ RegisterContext::ConvertBetweenRegisterKinds (int source_rk, uint32_t source_reg
}
return false;
}
bool
RegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value)
{
DataExtractor data;
if (!ReadRegisterBytes (reg, data))
return false;
const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
uint32_t offset = 0;
switch (reg_info->encoding)
{
case eEncodingUint:
switch (reg_info->byte_size)
{
case 1:
{
value = data.GetU8 (&offset);
return true;
}
case 2:
{
value = data.GetU16 (&offset);
return true;
}
case 4:
{
value = data.GetU32 (&offset);
return true;
}
case 8:
{
value = data.GetU64 (&offset);
return true;
}
}
break;
case eEncodingSint:
switch (reg_info->byte_size)
{
case 1:
{
int8_t v;
if (data.ExtractBytes (0, sizeof (int8_t), eByteOrderHost, &v) != sizeof (int8_t))
return false;
value = v;
return true;
}
case 2:
{
int16_t v;
if (data.ExtractBytes (0, sizeof (int16_t), eByteOrderHost, &v) != sizeof (int16_t))
return false;
value = v;
return true;
}
case 4:
{
int32_t v;
if (data.ExtractBytes (0, sizeof (int32_t), eByteOrderHost, &v) != sizeof (int32_t))
return false;
value = v;
return true;
}
case 8:
{
int64_t v;
if (data.ExtractBytes (0, sizeof (int64_t), eByteOrderHost, &v) != sizeof (int64_t))
return false;
value = v;
return true;
}
}
break;
case eEncodingIEEE754:
switch (reg_info->byte_size)
{
case sizeof (float):
{
float v;
if (data.ExtractBytes (0, sizeof (float), eByteOrderHost, &v) != sizeof (float))
return false;
value = v;
return true;
}
case sizeof (double):
{
double v;
if (data.ExtractBytes (0, sizeof (double), eByteOrderHost, &v) != sizeof (double))
return false;
value = v;
return true;
}
case sizeof (long double):
{
double v;
if (data.ExtractBytes (0, sizeof (long double), eByteOrderHost, &v) != sizeof (long double))
return false;
value = v;
return true;
}
}
break;
}
return false;
}
bool
RegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value)
{
DataExtractor data;
if (!value.IsValid())
return false;
if (!value.GetData (data))
return false;
return WriteRegisterBytes (reg, data);
}

View File

@ -152,6 +152,7 @@ lldb_private::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &ar
else if (strcasestr(arg, "comm") == arg) flag_bits |= LIBLLDB_LOG_COMMUNICATION;
else if (strcasestr(arg, "conn") == arg) flag_bits |= LIBLLDB_LOG_CONNECTION;
else if (strcasestr(arg, "host") == arg) flag_bits |= LIBLLDB_LOG_HOST;
else if (strcasestr(arg, "unwind") == arg) flag_bits |= LIBLLDB_LOG_UNWIND;
else
{
feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
@ -186,6 +187,7 @@ lldb_private::ListLogCategories (Stream *strm)
"\tshlib - log shared library related activities\n"
"\tstate - log private and public process state changes\n"
"\tstep - log step related activities\n"
"\tunwind - log stack unwind activities\n"
"\tverbose - enable verbose loggging\n"
"\twatch - log watchpoint related activities\n");
}

View File

@ -69,6 +69,7 @@ lldb_private::Initialize ()
SymbolFileDWARFDebugMap::Initialize();
SymbolFileSymtab::Initialize();
UnwindAssemblyProfiler_x86::Initialize();
ArchDefaultUnwindPlan_x86::Initialize();
ArchVolatileRegs_x86::Initialize();
#ifdef __APPLE__
@ -110,6 +111,7 @@ lldb_private::Terminate ()
SymbolFileDWARFDebugMap::Terminate();
SymbolFileSymtab::Terminate();
UnwindAssemblyProfiler_x86::Terminate();
ArchDefaultUnwindPlan_x86::Terminate();
ArchVolatileRegs_x86::Terminate();
#ifdef __APPLE__