The commit introduced an array of const objects, which libstdc++ does not like. Make the object non-const. Also fix a compiler warning while I'm in there. llvm-svn: 280697
2171 lines
93 KiB
C++
2171 lines
93 KiB
C++
//===-- StackFrame.cpp ------------------------------------------*- C++ -*-===//
|
||
//
|
||
// The LLVM Compiler Infrastructure
|
||
//
|
||
// This file is distributed under the University of Illinois Open Source
|
||
// License. See LICENSE.TXT for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
// C Includes
|
||
// C++ Includes
|
||
// Other libraries and framework includes
|
||
// Project includes
|
||
#include "lldb/Target/StackFrame.h"
|
||
#include "lldb/Core/Debugger.h"
|
||
#include "lldb/Core/Disassembler.h"
|
||
#include "lldb/Core/FormatEntity.h"
|
||
#include "lldb/Core/Mangled.h"
|
||
#include "lldb/Core/Module.h"
|
||
#include "lldb/Core/Value.h"
|
||
#include "lldb/Core/ValueObjectVariable.h"
|
||
#include "lldb/Core/ValueObjectConstResult.h"
|
||
#include "lldb/Core/ValueObjectMemory.h"
|
||
#include "lldb/Symbol/CompileUnit.h"
|
||
#include "lldb/Symbol/Function.h"
|
||
#include "lldb/Symbol/Symbol.h"
|
||
#include "lldb/Symbol/SymbolContextScope.h"
|
||
#include "lldb/Symbol/Type.h"
|
||
#include "lldb/Symbol/VariableList.h"
|
||
#include "lldb/Target/ABI.h"
|
||
#include "lldb/Target/ExecutionContext.h"
|
||
#include "lldb/Target/Process.h"
|
||
#include "lldb/Target/RegisterContext.h"
|
||
#include "lldb/Target/Target.h"
|
||
#include "lldb/Target/Thread.h"
|
||
|
||
using namespace lldb;
|
||
using namespace lldb_private;
|
||
|
||
// The first bits in the flags are reserved for the SymbolContext::Scope bits
|
||
// so we know if we have tried to look up information in our internal symbol
|
||
// context (m_sc) already.
|
||
#define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1))
|
||
#define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1)
|
||
#define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1)
|
||
#define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1)
|
||
#define RESOLVED_GLOBAL_VARIABLES (RESOLVED_VARIABLES << 1)
|
||
|
||
StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, addr_t cfa,
|
||
bool cfa_is_valid, addr_t pc, uint32_t stop_id, bool stop_id_is_valid, bool is_history_frame,
|
||
const SymbolContext *sc_ptr)
|
||
: m_thread_wp(thread_sp),
|
||
m_frame_index(frame_idx),
|
||
m_concrete_frame_index(unwind_frame_index),
|
||
m_reg_context_sp(),
|
||
m_id(pc, cfa, nullptr),
|
||
m_frame_code_addr(pc),
|
||
m_sc(),
|
||
m_flags(),
|
||
m_frame_base(),
|
||
m_frame_base_error(),
|
||
m_cfa_is_valid(cfa_is_valid),
|
||
m_stop_id(stop_id),
|
||
m_stop_id_is_valid(stop_id_is_valid),
|
||
m_is_history_frame(is_history_frame),
|
||
m_variable_list_sp(),
|
||
m_variable_list_value_objects(),
|
||
m_disassembly(),
|
||
m_mutex()
|
||
{
|
||
// If we don't have a CFA value, use the frame index for our StackID so that recursive
|
||
// functions properly aren't confused with one another on a history stack.
|
||
if (m_is_history_frame && !m_cfa_is_valid)
|
||
{
|
||
m_id.SetCFA(m_frame_index);
|
||
}
|
||
|
||
if (sc_ptr != nullptr)
|
||
{
|
||
m_sc = *sc_ptr;
|
||
m_flags.Set(m_sc.GetResolvedMask());
|
||
}
|
||
}
|
||
|
||
StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index,
|
||
const RegisterContextSP ®_context_sp, addr_t cfa, addr_t pc, const SymbolContext *sc_ptr)
|
||
: m_thread_wp(thread_sp),
|
||
m_frame_index(frame_idx),
|
||
m_concrete_frame_index(unwind_frame_index),
|
||
m_reg_context_sp(reg_context_sp),
|
||
m_id(pc, cfa, nullptr),
|
||
m_frame_code_addr(pc),
|
||
m_sc(),
|
||
m_flags(),
|
||
m_frame_base(),
|
||
m_frame_base_error(),
|
||
m_cfa_is_valid(true),
|
||
m_stop_id(0),
|
||
m_stop_id_is_valid(false),
|
||
m_is_history_frame(false),
|
||
m_variable_list_sp(),
|
||
m_variable_list_value_objects(),
|
||
m_disassembly(),
|
||
m_mutex()
|
||
{
|
||
if (sc_ptr != nullptr)
|
||
{
|
||
m_sc = *sc_ptr;
|
||
m_flags.Set(m_sc.GetResolvedMask());
|
||
}
|
||
|
||
if (reg_context_sp && !m_sc.target_sp)
|
||
{
|
||
m_sc.target_sp = reg_context_sp->CalculateTarget();
|
||
if (m_sc.target_sp)
|
||
m_flags.Set(eSymbolContextTarget);
|
||
}
|
||
}
|
||
|
||
StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index,
|
||
const RegisterContextSP ®_context_sp, addr_t cfa, const Address &pc_addr,
|
||
const SymbolContext *sc_ptr)
|
||
: m_thread_wp(thread_sp),
|
||
m_frame_index(frame_idx),
|
||
m_concrete_frame_index(unwind_frame_index),
|
||
m_reg_context_sp(reg_context_sp),
|
||
m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa, nullptr),
|
||
m_frame_code_addr(pc_addr),
|
||
m_sc(),
|
||
m_flags(),
|
||
m_frame_base(),
|
||
m_frame_base_error(),
|
||
m_cfa_is_valid(true),
|
||
m_stop_id(0),
|
||
m_stop_id_is_valid(false),
|
||
m_is_history_frame(false),
|
||
m_variable_list_sp(),
|
||
m_variable_list_value_objects(),
|
||
m_disassembly(),
|
||
m_mutex()
|
||
{
|
||
if (sc_ptr != nullptr)
|
||
{
|
||
m_sc = *sc_ptr;
|
||
m_flags.Set(m_sc.GetResolvedMask());
|
||
}
|
||
|
||
if (!m_sc.target_sp && reg_context_sp)
|
||
{
|
||
m_sc.target_sp = reg_context_sp->CalculateTarget();
|
||
if (m_sc.target_sp)
|
||
m_flags.Set(eSymbolContextTarget);
|
||
}
|
||
|
||
ModuleSP pc_module_sp(pc_addr.GetModule());
|
||
if (!m_sc.module_sp || m_sc.module_sp != pc_module_sp)
|
||
{
|
||
if (pc_module_sp)
|
||
{
|
||
m_sc.module_sp = pc_module_sp;
|
||
m_flags.Set(eSymbolContextModule);
|
||
}
|
||
else
|
||
{
|
||
m_sc.module_sp.reset();
|
||
}
|
||
}
|
||
}
|
||
|
||
StackFrame::~StackFrame() = default;
|
||
|
||
StackID&
|
||
StackFrame::GetStackID()
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
// Make sure we have resolved the StackID object's symbol context scope if
|
||
// we already haven't looked it up.
|
||
|
||
if (m_flags.IsClear (RESOLVED_FRAME_ID_SYMBOL_SCOPE))
|
||
{
|
||
if (m_id.GetSymbolContextScope ())
|
||
{
|
||
// We already have a symbol context scope, we just don't have our
|
||
// flag bit set.
|
||
m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE);
|
||
}
|
||
else
|
||
{
|
||
// Calculate the frame block and use this for the stack ID symbol
|
||
// context scope if we have one.
|
||
SymbolContextScope *scope = GetFrameBlock ();
|
||
if (scope == nullptr)
|
||
{
|
||
// We don't have a block, so use the symbol
|
||
if (m_flags.IsClear (eSymbolContextSymbol))
|
||
GetSymbolContext (eSymbolContextSymbol);
|
||
|
||
// It is ok if m_sc.symbol is nullptr here
|
||
scope = m_sc.symbol;
|
||
}
|
||
// Set the symbol context scope (the accessor will set the
|
||
// RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags).
|
||
SetSymbolContextScope (scope);
|
||
}
|
||
}
|
||
return m_id;
|
||
}
|
||
|
||
uint32_t
|
||
StackFrame::GetFrameIndex () const
|
||
{
|
||
ThreadSP thread_sp = GetThread();
|
||
if (thread_sp)
|
||
return thread_sp->GetStackFrameList()->GetVisibleStackFrameIndex(m_frame_index);
|
||
else
|
||
return m_frame_index;
|
||
}
|
||
|
||
void
|
||
StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE);
|
||
m_id.SetSymbolContextScope (symbol_scope);
|
||
}
|
||
|
||
const Address&
|
||
StackFrame::GetFrameCodeAddress()
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset())
|
||
{
|
||
m_flags.Set (RESOLVED_FRAME_CODE_ADDR);
|
||
|
||
// Resolve the PC into a temporary address because if ResolveLoadAddress
|
||
// fails to resolve the address, it will clear the address object...
|
||
ThreadSP thread_sp (GetThread());
|
||
if (thread_sp)
|
||
{
|
||
TargetSP target_sp (thread_sp->CalculateTarget());
|
||
if (target_sp)
|
||
{
|
||
if (m_frame_code_addr.SetOpcodeLoadAddress (m_frame_code_addr.GetOffset(), target_sp.get(), eAddressClassCode))
|
||
{
|
||
ModuleSP module_sp (m_frame_code_addr.GetModule());
|
||
if (module_sp)
|
||
{
|
||
m_sc.module_sp = module_sp;
|
||
m_flags.Set(eSymbolContextModule);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return m_frame_code_addr;
|
||
}
|
||
|
||
bool
|
||
StackFrame::ChangePC (addr_t pc)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
// We can't change the pc value of a history stack frame - it is immutable.
|
||
if (m_is_history_frame)
|
||
return false;
|
||
m_frame_code_addr.SetRawAddress(pc);
|
||
m_sc.Clear(false);
|
||
m_flags.Reset(0);
|
||
ThreadSP thread_sp (GetThread());
|
||
if (thread_sp)
|
||
thread_sp->ClearStackFrames ();
|
||
return true;
|
||
}
|
||
|
||
const char *
|
||
StackFrame::Disassemble ()
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
if (m_disassembly.GetSize() == 0)
|
||
{
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
Target *target = exe_ctx.GetTargetPtr();
|
||
if (target)
|
||
{
|
||
const char *plugin_name = nullptr;
|
||
const char *flavor = nullptr;
|
||
Disassembler::Disassemble (target->GetDebugger(),
|
||
target->GetArchitecture(),
|
||
plugin_name,
|
||
flavor,
|
||
exe_ctx,
|
||
0,
|
||
0,
|
||
0,
|
||
m_disassembly);
|
||
}
|
||
if (m_disassembly.GetSize() == 0)
|
||
return nullptr;
|
||
}
|
||
return m_disassembly.GetData();
|
||
}
|
||
|
||
Block *
|
||
StackFrame::GetFrameBlock ()
|
||
{
|
||
if (m_sc.block == nullptr && m_flags.IsClear(eSymbolContextBlock))
|
||
GetSymbolContext (eSymbolContextBlock);
|
||
|
||
if (m_sc.block)
|
||
{
|
||
Block *inline_block = m_sc.block->GetContainingInlinedBlock();
|
||
if (inline_block)
|
||
{
|
||
// Use the block with the inlined function info
|
||
// as the frame block we want this frame to have only the variables
|
||
// for the inlined function and its non-inlined block child blocks.
|
||
return inline_block;
|
||
}
|
||
else
|
||
{
|
||
// This block is not contained within any inlined function blocks
|
||
// with so we want to use the top most function block.
|
||
return &m_sc.function->GetBlock (false);
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
//----------------------------------------------------------------------
|
||
// Get the symbol context if we already haven't done so by resolving the
|
||
// PC address as much as possible. This way when we pass around a
|
||
// StackFrame object, everyone will have as much information as
|
||
// possible and no one will ever have to look things up manually.
|
||
//----------------------------------------------------------------------
|
||
const SymbolContext&
|
||
StackFrame::GetSymbolContext (uint32_t resolve_scope)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
// Copy our internal symbol context into "sc".
|
||
if ((m_flags.Get() & resolve_scope) != resolve_scope)
|
||
{
|
||
uint32_t resolved = 0;
|
||
|
||
// If the target was requested add that:
|
||
if (!m_sc.target_sp)
|
||
{
|
||
m_sc.target_sp = CalculateTarget();
|
||
if (m_sc.target_sp)
|
||
resolved |= eSymbolContextTarget;
|
||
}
|
||
|
||
// Resolve our PC to section offset if we haven't already done so
|
||
// and if we don't have a module. The resolved address section will
|
||
// contain the module to which it belongs
|
||
if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR))
|
||
GetFrameCodeAddress();
|
||
|
||
// If this is not frame zero, then we need to subtract 1 from the PC
|
||
// value when doing address lookups since the PC will be on the
|
||
// instruction following the function call instruction...
|
||
|
||
Address lookup_addr(GetFrameCodeAddress());
|
||
if (m_frame_index > 0 && lookup_addr.IsValid())
|
||
{
|
||
addr_t offset = lookup_addr.GetOffset();
|
||
if (offset > 0)
|
||
{
|
||
lookup_addr.SetOffset(offset - 1);
|
||
|
||
}
|
||
else
|
||
{
|
||
// lookup_addr is the start of a section. We need
|
||
// do the math on the actual load address and re-compute
|
||
// the section. We're working with a 'noreturn' function
|
||
// at the end of a section.
|
||
ThreadSP thread_sp (GetThread());
|
||
if (thread_sp)
|
||
{
|
||
TargetSP target_sp (thread_sp->CalculateTarget());
|
||
if (target_sp)
|
||
{
|
||
addr_t addr_minus_one = lookup_addr.GetLoadAddress(target_sp.get()) - 1;
|
||
lookup_addr.SetLoadAddress (addr_minus_one, target_sp.get());
|
||
}
|
||
else
|
||
{
|
||
lookup_addr.SetOffset(offset - 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (m_sc.module_sp)
|
||
{
|
||
// We have something in our stack frame symbol context, lets check
|
||
// if we haven't already tried to lookup one of those things. If we
|
||
// haven't then we will do the query.
|
||
|
||
uint32_t actual_resolve_scope = 0;
|
||
|
||
if (resolve_scope & eSymbolContextCompUnit)
|
||
{
|
||
if (m_flags.IsClear (eSymbolContextCompUnit))
|
||
{
|
||
if (m_sc.comp_unit)
|
||
resolved |= eSymbolContextCompUnit;
|
||
else
|
||
actual_resolve_scope |= eSymbolContextCompUnit;
|
||
}
|
||
}
|
||
|
||
if (resolve_scope & eSymbolContextFunction)
|
||
{
|
||
if (m_flags.IsClear (eSymbolContextFunction))
|
||
{
|
||
if (m_sc.function)
|
||
resolved |= eSymbolContextFunction;
|
||
else
|
||
actual_resolve_scope |= eSymbolContextFunction;
|
||
}
|
||
}
|
||
|
||
if (resolve_scope & eSymbolContextBlock)
|
||
{
|
||
if (m_flags.IsClear (eSymbolContextBlock))
|
||
{
|
||
if (m_sc.block)
|
||
resolved |= eSymbolContextBlock;
|
||
else
|
||
actual_resolve_scope |= eSymbolContextBlock;
|
||
}
|
||
}
|
||
|
||
if (resolve_scope & eSymbolContextSymbol)
|
||
{
|
||
if (m_flags.IsClear (eSymbolContextSymbol))
|
||
{
|
||
if (m_sc.symbol)
|
||
resolved |= eSymbolContextSymbol;
|
||
else
|
||
actual_resolve_scope |= eSymbolContextSymbol;
|
||
}
|
||
}
|
||
|
||
if (resolve_scope & eSymbolContextLineEntry)
|
||
{
|
||
if (m_flags.IsClear (eSymbolContextLineEntry))
|
||
{
|
||
if (m_sc.line_entry.IsValid())
|
||
resolved |= eSymbolContextLineEntry;
|
||
else
|
||
actual_resolve_scope |= eSymbolContextLineEntry;
|
||
}
|
||
}
|
||
|
||
if (actual_resolve_scope)
|
||
{
|
||
// We might be resolving less information than what is already
|
||
// in our current symbol context so resolve into a temporary
|
||
// symbol context "sc" so we don't clear out data we have
|
||
// already found in "m_sc"
|
||
SymbolContext sc;
|
||
// Set flags that indicate what we have tried to resolve
|
||
resolved |= m_sc.module_sp->ResolveSymbolContextForAddress (lookup_addr, actual_resolve_scope, sc);
|
||
// Only replace what we didn't already have as we may have
|
||
// information for an inlined function scope that won't match
|
||
// what a standard lookup by address would match
|
||
if ((resolved & eSymbolContextCompUnit) && m_sc.comp_unit == nullptr)
|
||
m_sc.comp_unit = sc.comp_unit;
|
||
if ((resolved & eSymbolContextFunction) && m_sc.function == nullptr)
|
||
m_sc.function = sc.function;
|
||
if ((resolved & eSymbolContextBlock) && m_sc.block == nullptr)
|
||
m_sc.block = sc.block;
|
||
if ((resolved & eSymbolContextSymbol) && m_sc.symbol == nullptr)
|
||
m_sc.symbol = sc.symbol;
|
||
if ((resolved & eSymbolContextLineEntry) && !m_sc.line_entry.IsValid())
|
||
{
|
||
m_sc.line_entry = sc.line_entry;
|
||
m_sc.line_entry.ApplyFileMappings(m_sc.target_sp);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// If we don't have a module, then we can't have the compile unit,
|
||
// function, block, line entry or symbol, so we can safely call
|
||
// ResolveSymbolContextForAddress with our symbol context member m_sc.
|
||
if (m_sc.target_sp)
|
||
{
|
||
resolved |= m_sc.target_sp->GetImages().ResolveSymbolContextForAddress (lookup_addr, resolve_scope, m_sc);
|
||
}
|
||
}
|
||
|
||
// Update our internal flags so we remember what we have tried to locate so
|
||
// we don't have to keep trying when more calls to this function are made.
|
||
// We might have dug up more information that was requested (for example
|
||
// if we were asked to only get the block, we will have gotten the
|
||
// compile unit, and function) so set any additional bits that we resolved
|
||
m_flags.Set (resolve_scope | resolved);
|
||
}
|
||
|
||
// Return the symbol context with everything that was possible to resolve
|
||
// resolved.
|
||
return m_sc;
|
||
}
|
||
|
||
VariableList *
|
||
StackFrame::GetVariableList (bool get_file_globals)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
if (m_flags.IsClear(RESOLVED_VARIABLES))
|
||
{
|
||
m_flags.Set(RESOLVED_VARIABLES);
|
||
|
||
Block *frame_block = GetFrameBlock();
|
||
|
||
if (frame_block)
|
||
{
|
||
const bool get_child_variables = true;
|
||
const bool can_create = true;
|
||
const bool stop_if_child_block_is_inlined_function = true;
|
||
m_variable_list_sp.reset(new VariableList());
|
||
frame_block->AppendBlockVariables(can_create,
|
||
get_child_variables,
|
||
stop_if_child_block_is_inlined_function,
|
||
[this](Variable* v) { return true; },
|
||
m_variable_list_sp.get());
|
||
}
|
||
}
|
||
|
||
if (m_flags.IsClear(RESOLVED_GLOBAL_VARIABLES) &&
|
||
get_file_globals)
|
||
{
|
||
m_flags.Set(RESOLVED_GLOBAL_VARIABLES);
|
||
|
||
if (m_flags.IsClear (eSymbolContextCompUnit))
|
||
GetSymbolContext (eSymbolContextCompUnit);
|
||
|
||
if (m_sc.comp_unit)
|
||
{
|
||
VariableListSP global_variable_list_sp (m_sc.comp_unit->GetVariableList(true));
|
||
if (m_variable_list_sp)
|
||
m_variable_list_sp->AddVariables (global_variable_list_sp.get());
|
||
else
|
||
m_variable_list_sp = global_variable_list_sp;
|
||
}
|
||
}
|
||
|
||
return m_variable_list_sp.get();
|
||
}
|
||
|
||
VariableListSP
|
||
StackFrame::GetInScopeVariableList (bool get_file_globals, bool must_have_valid_location)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
// We can't fetch variable information for a history stack frame.
|
||
if (m_is_history_frame)
|
||
return VariableListSP();
|
||
|
||
VariableListSP var_list_sp(new VariableList);
|
||
GetSymbolContext (eSymbolContextCompUnit | eSymbolContextBlock);
|
||
|
||
if (m_sc.block)
|
||
{
|
||
const bool can_create = true;
|
||
const bool get_parent_variables = true;
|
||
const bool stop_if_block_is_inlined_function = true;
|
||
m_sc.block->AppendVariables (can_create,
|
||
get_parent_variables,
|
||
stop_if_block_is_inlined_function,
|
||
[this, must_have_valid_location](Variable* v)
|
||
{
|
||
return v->IsInScope(this) && (!must_have_valid_location || v->LocationIsValidForFrame(this));
|
||
},
|
||
var_list_sp.get());
|
||
}
|
||
|
||
if (m_sc.comp_unit && get_file_globals)
|
||
{
|
||
VariableListSP global_variable_list_sp (m_sc.comp_unit->GetVariableList(true));
|
||
if (global_variable_list_sp)
|
||
var_list_sp->AddVariables (global_variable_list_sp.get());
|
||
}
|
||
|
||
return var_list_sp;
|
||
}
|
||
|
||
ValueObjectSP
|
||
StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
|
||
DynamicValueType use_dynamic,
|
||
uint32_t options,
|
||
VariableSP &var_sp,
|
||
Error &error)
|
||
{
|
||
// We can't fetch variable information for a history stack frame.
|
||
if (m_is_history_frame)
|
||
return ValueObjectSP();
|
||
|
||
if (var_expr_cstr && var_expr_cstr[0])
|
||
{
|
||
const bool check_ptr_vs_member = (options & eExpressionPathOptionCheckPtrVsMember) != 0;
|
||
const bool no_fragile_ivar = (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
|
||
const bool no_synth_child = (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
|
||
//const bool no_synth_array = (options & eExpressionPathOptionsNoSyntheticArrayRange) != 0;
|
||
error.Clear();
|
||
bool deref = false;
|
||
bool address_of = false;
|
||
ValueObjectSP valobj_sp;
|
||
const bool get_file_globals = true;
|
||
// When looking up a variable for an expression, we need only consider the
|
||
// variables that are in scope.
|
||
VariableListSP var_list_sp (GetInScopeVariableList (get_file_globals));
|
||
VariableList *variable_list = var_list_sp.get();
|
||
|
||
if (variable_list)
|
||
{
|
||
// If first character is a '*', then show pointer contents
|
||
const char *var_expr = var_expr_cstr;
|
||
if (var_expr[0] == '*')
|
||
{
|
||
deref = true;
|
||
var_expr++; // Skip the '*'
|
||
}
|
||
else if (var_expr[0] == '&')
|
||
{
|
||
address_of = true;
|
||
var_expr++; // Skip the '&'
|
||
}
|
||
|
||
std::string var_path (var_expr);
|
||
size_t separator_idx = var_path.find_first_of(".-[=+~|&^%#@!/?,<>{}");
|
||
StreamString var_expr_path_strm;
|
||
|
||
ConstString name_const_string;
|
||
if (separator_idx == std::string::npos)
|
||
name_const_string.SetCString (var_path.c_str());
|
||
else
|
||
name_const_string.SetCStringWithLength (var_path.c_str(), separator_idx);
|
||
|
||
var_sp = variable_list->FindVariable(name_const_string, false);
|
||
|
||
bool synthetically_added_instance_object = false;
|
||
|
||
if (var_sp)
|
||
{
|
||
var_path.erase (0, name_const_string.GetLength ());
|
||
}
|
||
|
||
if (!var_sp && (options & eExpressionPathOptionsAllowDirectIVarAccess))
|
||
{
|
||
// Check for direct ivars access which helps us with implicit
|
||
// access to ivars with the "this->" or "self->"
|
||
GetSymbolContext(eSymbolContextFunction|eSymbolContextBlock);
|
||
lldb::LanguageType method_language = eLanguageTypeUnknown;
|
||
bool is_instance_method = false;
|
||
ConstString method_object_name;
|
||
if (m_sc.GetFunctionMethodInfo (method_language, is_instance_method, method_object_name))
|
||
{
|
||
if (is_instance_method && method_object_name)
|
||
{
|
||
var_sp = variable_list->FindVariable(method_object_name);
|
||
if (var_sp)
|
||
{
|
||
separator_idx = 0;
|
||
var_path.insert(0, "->");
|
||
synthetically_added_instance_object = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!var_sp && (options & eExpressionPathOptionsInspectAnonymousUnions))
|
||
{
|
||
// Check if any anonymous unions are there which contain a variable with the name we need
|
||
for (size_t i = 0;
|
||
i < variable_list->GetSize();
|
||
i++)
|
||
{
|
||
if (VariableSP variable_sp = variable_list->GetVariableAtIndex(i))
|
||
{
|
||
if (variable_sp->GetName().IsEmpty())
|
||
{
|
||
if (Type *var_type = variable_sp->GetType())
|
||
{
|
||
if (var_type->GetForwardCompilerType().IsAnonymousType())
|
||
{
|
||
valobj_sp = GetValueObjectForFrameVariable (variable_sp, use_dynamic);
|
||
if (!valobj_sp)
|
||
return valobj_sp;
|
||
valobj_sp = valobj_sp->GetChildMemberWithName(name_const_string, true);
|
||
if (valobj_sp)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (var_sp && !valobj_sp)
|
||
{
|
||
valobj_sp = GetValueObjectForFrameVariable (var_sp, use_dynamic);
|
||
if (!valobj_sp)
|
||
return valobj_sp;
|
||
}
|
||
if (valobj_sp)
|
||
{
|
||
// We are dumping at least one child
|
||
while (separator_idx != std::string::npos)
|
||
{
|
||
// Calculate the next separator index ahead of time
|
||
ValueObjectSP child_valobj_sp;
|
||
const char separator_type = var_path[0];
|
||
switch (separator_type)
|
||
{
|
||
case '-':
|
||
if (var_path.size() >= 2 && var_path[1] != '>')
|
||
return ValueObjectSP();
|
||
|
||
if (no_fragile_ivar)
|
||
{
|
||
// Make sure we aren't trying to deref an objective
|
||
// C ivar if this is not allowed
|
||
const uint32_t pointer_type_flags = valobj_sp->GetCompilerType().GetTypeInfo(nullptr);
|
||
if ((pointer_type_flags & eTypeIsObjC) &&
|
||
(pointer_type_flags & eTypeIsPointer))
|
||
{
|
||
// This was an objective C object pointer and
|
||
// it was requested we skip any fragile ivars
|
||
// so return nothing here
|
||
return ValueObjectSP();
|
||
}
|
||
}
|
||
var_path.erase (0, 1); // Remove the '-'
|
||
LLVM_FALLTHROUGH;
|
||
case '.':
|
||
{
|
||
const bool expr_is_ptr = var_path[0] == '>';
|
||
|
||
var_path.erase (0, 1); // Remove the '.' or '>'
|
||
separator_idx = var_path.find_first_of(".-[");
|
||
ConstString child_name;
|
||
if (separator_idx == std::string::npos)
|
||
child_name.SetCString (var_path.c_str());
|
||
else
|
||
child_name.SetCStringWithLength(var_path.c_str(), separator_idx);
|
||
|
||
if (check_ptr_vs_member)
|
||
{
|
||
// We either have a pointer type and need to verify
|
||
// valobj_sp is a pointer, or we have a member of a
|
||
// class/union/struct being accessed with the . syntax
|
||
// and need to verify we don't have a pointer.
|
||
const bool actual_is_ptr = valobj_sp->IsPointerType ();
|
||
|
||
if (actual_is_ptr != expr_is_ptr)
|
||
{
|
||
// Incorrect use of "." with a pointer, or "->" with
|
||
// a class/union/struct instance or reference.
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
if (actual_is_ptr)
|
||
error.SetErrorStringWithFormat ("\"%s\" is a pointer and . was used to attempt to access \"%s\". Did you mean \"%s->%s\"?",
|
||
var_expr_path_strm.GetString().c_str(),
|
||
child_name.GetCString(),
|
||
var_expr_path_strm.GetString().c_str(),
|
||
var_path.c_str());
|
||
else
|
||
error.SetErrorStringWithFormat ("\"%s\" is not a pointer and -> was used to attempt to access \"%s\". Did you mean \"%s.%s\"?",
|
||
var_expr_path_strm.GetString().c_str(),
|
||
child_name.GetCString(),
|
||
var_expr_path_strm.GetString().c_str(),
|
||
var_path.c_str());
|
||
return ValueObjectSP();
|
||
}
|
||
}
|
||
child_valobj_sp = valobj_sp->GetChildMemberWithName (child_name, true);
|
||
if (!child_valobj_sp)
|
||
{
|
||
if (!no_synth_child)
|
||
{
|
||
child_valobj_sp = valobj_sp->GetSyntheticValue();
|
||
if (child_valobj_sp)
|
||
child_valobj_sp = child_valobj_sp->GetChildMemberWithName (child_name, true);
|
||
}
|
||
|
||
if (no_synth_child || !child_valobj_sp)
|
||
{
|
||
// No child member with name "child_name"
|
||
if (synthetically_added_instance_object)
|
||
{
|
||
// We added a "this->" or "self->" to the beginning of the expression
|
||
// and this is the first pointer ivar access, so just return the normal
|
||
// error
|
||
error.SetErrorStringWithFormat("no variable or instance variable named '%s' found in this frame",
|
||
name_const_string.GetCString());
|
||
}
|
||
else
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
if (child_name)
|
||
{
|
||
error.SetErrorStringWithFormat ("\"%s\" is not a member of \"(%s) %s\"",
|
||
child_name.GetCString(),
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
else
|
||
{
|
||
error.SetErrorStringWithFormat ("incomplete expression path after \"%s\" in \"%s\"",
|
||
var_expr_path_strm.GetString().c_str(),
|
||
var_expr_cstr);
|
||
}
|
||
}
|
||
return ValueObjectSP();
|
||
}
|
||
}
|
||
synthetically_added_instance_object = false;
|
||
// Remove the child name from the path
|
||
var_path.erase(0, child_name.GetLength());
|
||
if (use_dynamic != eNoDynamicValues)
|
||
{
|
||
ValueObjectSP dynamic_value_sp(child_valobj_sp->GetDynamicValue(use_dynamic));
|
||
if (dynamic_value_sp)
|
||
child_valobj_sp = dynamic_value_sp;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case '[':
|
||
// Array member access, or treating pointer as an array
|
||
if (var_path.size() > 2) // Need at least two brackets and a number
|
||
{
|
||
char *end = nullptr;
|
||
long child_index = ::strtol (&var_path[1], &end, 0);
|
||
if (end && *end == ']'
|
||
&& *(end-1) != '[') // this code forces an error in the case of arr[]. as bitfield[] is not a good syntax we're good to go
|
||
{
|
||
if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref)
|
||
{
|
||
// what we have is *ptr[low]. the most similar C++ syntax is to deref ptr
|
||
// and extract bit low out of it. reading array item low
|
||
// would be done by saying ptr[low], without a deref * sign
|
||
Error error;
|
||
ValueObjectSP temp(valobj_sp->Dereference(error));
|
||
if (error.Fail())
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("could not dereference \"(%s) %s\"",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
return ValueObjectSP();
|
||
}
|
||
valobj_sp = temp;
|
||
deref = false;
|
||
}
|
||
else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref)
|
||
{
|
||
// what we have is *arr[low]. the most similar C++ syntax is to get arr[0]
|
||
// (an operation that is equivalent to deref-ing arr)
|
||
// and extract bit low out of it. reading array item low
|
||
// would be done by saying arr[low], without a deref * sign
|
||
Error error;
|
||
ValueObjectSP temp(valobj_sp->GetChildAtIndex (0, true));
|
||
if (error.Fail())
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("could not get item 0 for \"(%s) %s\"",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
return ValueObjectSP();
|
||
}
|
||
valobj_sp = temp;
|
||
deref = false;
|
||
}
|
||
|
||
bool is_incomplete_array = false;
|
||
if (valobj_sp->IsPointerType ())
|
||
{
|
||
bool is_objc_pointer = true;
|
||
|
||
if (valobj_sp->GetCompilerType().GetMinimumLanguage() != eLanguageTypeObjC)
|
||
is_objc_pointer = false;
|
||
else if (!valobj_sp->GetCompilerType().IsPointerType())
|
||
is_objc_pointer = false;
|
||
|
||
if (no_synth_child && is_objc_pointer)
|
||
{
|
||
error.SetErrorStringWithFormat("\"(%s) %s\" is an Objective-C pointer, and cannot be subscripted",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
|
||
return ValueObjectSP();
|
||
}
|
||
else if (is_objc_pointer)
|
||
{
|
||
// dereferencing ObjC variables is not valid.. so let's try and recur to synthetic children
|
||
ValueObjectSP synthetic = valobj_sp->GetSyntheticValue();
|
||
if (!synthetic /* no synthetic */
|
||
|| synthetic == valobj_sp) /* synthetic is the same as the original object */
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("\"(%s) %s\" is not an array type",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
else if (static_cast<uint32_t>(child_index) >= synthetic->GetNumChildren() /* synthetic does not have that many values */)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
|
||
child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
else
|
||
{
|
||
child_valobj_sp = synthetic->GetChildAtIndex(child_index, true);
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
|
||
child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
child_valobj_sp = valobj_sp->GetSyntheticArrayMember (child_index, true);
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("failed to use pointer as array for index %ld for \"(%s) %s\"",
|
||
child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}
|
||
}
|
||
else if (valobj_sp->GetCompilerType().IsArrayType(nullptr, nullptr, &is_incomplete_array))
|
||
{
|
||
// Pass false to dynamic_value here so we can tell the difference between
|
||
// no dynamic value and no member of this type...
|
||
child_valobj_sp = valobj_sp->GetChildAtIndex (child_index, true);
|
||
if (!child_valobj_sp && (is_incomplete_array || !no_synth_child))
|
||
child_valobj_sp = valobj_sp->GetSyntheticArrayMember (child_index, true);
|
||
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
|
||
child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}
|
||
else if (valobj_sp->GetCompilerType().IsScalarType())
|
||
{
|
||
// this is a bitfield asking to display just one bit
|
||
child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, child_index, true);
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("bitfield range %ld-%ld is not valid for \"(%s) %s\"",
|
||
child_index, child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ValueObjectSP synthetic = valobj_sp->GetSyntheticValue();
|
||
if (no_synth_child /* synthetic is forbidden */ ||
|
||
!synthetic /* no synthetic */
|
||
|| synthetic == valobj_sp) /* synthetic is the same as the original object */
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("\"(%s) %s\" is not an array type",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
else if (static_cast<uint32_t>(child_index) >= synthetic->GetNumChildren() /* synthetic does not have that many values */)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
|
||
child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
else
|
||
{
|
||
child_valobj_sp = synthetic->GetChildAtIndex(child_index, true);
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
|
||
child_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!child_valobj_sp)
|
||
{
|
||
// Invalid array index...
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
// Erase the array member specification '[%i]' where
|
||
// %i is the array index
|
||
var_path.erase(0, (end - var_path.c_str()) + 1);
|
||
separator_idx = var_path.find_first_of(".-[");
|
||
if (use_dynamic != eNoDynamicValues)
|
||
{
|
||
ValueObjectSP dynamic_value_sp(child_valobj_sp->GetDynamicValue(use_dynamic));
|
||
if (dynamic_value_sp)
|
||
child_valobj_sp = dynamic_value_sp;
|
||
}
|
||
// Break out early from the switch since we were
|
||
// able to find the child member
|
||
break;
|
||
}
|
||
else if (end && *end == '-')
|
||
{
|
||
// this is most probably a BitField, let's take a look
|
||
char *real_end = nullptr;
|
||
long final_index = ::strtol (end+1, &real_end, 0);
|
||
bool expand_bitfield = true;
|
||
if (real_end && *real_end == ']')
|
||
{
|
||
// if the format given is [high-low], swap range
|
||
if (child_index > final_index)
|
||
{
|
||
long temp = child_index;
|
||
child_index = final_index;
|
||
final_index = temp;
|
||
}
|
||
|
||
if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref)
|
||
{
|
||
// what we have is *ptr[low-high]. the most similar C++ syntax is to deref ptr
|
||
// and extract bits low thru high out of it. reading array items low thru high
|
||
// would be done by saying ptr[low-high], without a deref * sign
|
||
Error error;
|
||
ValueObjectSP temp(valobj_sp->Dereference(error));
|
||
if (error.Fail())
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("could not dereference \"(%s) %s\"",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
return ValueObjectSP();
|
||
}
|
||
valobj_sp = temp;
|
||
deref = false;
|
||
}
|
||
else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref)
|
||
{
|
||
// what we have is *arr[low-high]. the most similar C++ syntax is to get arr[0]
|
||
// (an operation that is equivalent to deref-ing arr)
|
||
// and extract bits low thru high out of it. reading array items low thru high
|
||
// would be done by saying arr[low-high], without a deref * sign
|
||
Error error;
|
||
ValueObjectSP temp(valobj_sp->GetChildAtIndex (0, true));
|
||
if (error.Fail())
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("could not get item 0 for \"(%s) %s\"",
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
return ValueObjectSP();
|
||
}
|
||
valobj_sp = temp;
|
||
deref = false;
|
||
}
|
||
/*else if (valobj_sp->IsArrayType() || valobj_sp->IsPointerType())
|
||
{
|
||
child_valobj_sp = valobj_sp->GetSyntheticArrayRangeChild(child_index, final_index, true);
|
||
expand_bitfield = false;
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("array range %i-%i is not valid for \"(%s) %s\"",
|
||
child_index, final_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}*/
|
||
|
||
if (expand_bitfield)
|
||
{
|
||
child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
|
||
if (!child_valobj_sp)
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("bitfield range %ld-%ld is not valid for \"(%s) %s\"",
|
||
child_index, final_index,
|
||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||
var_expr_path_strm.GetString().c_str());
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!child_valobj_sp)
|
||
{
|
||
// Invalid bitfield range...
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
// Erase the bitfield member specification '[%i-%i]' where
|
||
// %i is the index
|
||
var_path.erase(0, (real_end - var_path.c_str()) + 1);
|
||
separator_idx = var_path.find_first_of(".-[");
|
||
if (use_dynamic != eNoDynamicValues)
|
||
{
|
||
ValueObjectSP dynamic_value_sp(child_valobj_sp->GetDynamicValue(use_dynamic));
|
||
if (dynamic_value_sp)
|
||
child_valobj_sp = dynamic_value_sp;
|
||
}
|
||
// Break out early from the switch since we were
|
||
// able to find the child member
|
||
break;
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error.SetErrorStringWithFormat("invalid square bracket encountered after \"%s\" in \"%s\"",
|
||
var_expr_path_strm.GetString().c_str(),
|
||
var_path.c_str());
|
||
}
|
||
return ValueObjectSP();
|
||
|
||
default:
|
||
// Failure...
|
||
{
|
||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||
error.SetErrorStringWithFormat ("unexpected char '%c' encountered after \"%s\" in \"%s\"",
|
||
separator_type,
|
||
var_expr_path_strm.GetString().c_str(),
|
||
var_path.c_str());
|
||
|
||
return ValueObjectSP();
|
||
}
|
||
}
|
||
|
||
if (child_valobj_sp)
|
||
valobj_sp = child_valobj_sp;
|
||
|
||
if (var_path.empty())
|
||
break;
|
||
}
|
||
if (valobj_sp)
|
||
{
|
||
if (deref)
|
||
{
|
||
ValueObjectSP deref_valobj_sp (valobj_sp->Dereference(error));
|
||
valobj_sp = deref_valobj_sp;
|
||
}
|
||
else if (address_of)
|
||
{
|
||
ValueObjectSP address_of_valobj_sp (valobj_sp->AddressOf(error));
|
||
valobj_sp = address_of_valobj_sp;
|
||
}
|
||
}
|
||
return valobj_sp;
|
||
}
|
||
else
|
||
{
|
||
error.SetErrorStringWithFormat("no variable named '%s' found in this frame",
|
||
name_const_string.GetCString());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error.SetErrorStringWithFormat("invalid variable path '%s'", var_expr_cstr);
|
||
}
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
bool
|
||
StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
if (!m_cfa_is_valid)
|
||
{
|
||
m_frame_base_error.SetErrorString("No frame base available for this historical stack frame.");
|
||
return false;
|
||
}
|
||
|
||
if (m_flags.IsClear(GOT_FRAME_BASE))
|
||
{
|
||
if (m_sc.function)
|
||
{
|
||
m_frame_base.Clear();
|
||
m_frame_base_error.Clear();
|
||
|
||
m_flags.Set(GOT_FRAME_BASE);
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
Value expr_value;
|
||
addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
|
||
if (m_sc.function->GetFrameBaseExpression().IsLocationList())
|
||
loclist_base_addr = m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (exe_ctx.GetTargetPtr());
|
||
|
||
if (m_sc.function->GetFrameBaseExpression().Evaluate(&exe_ctx,
|
||
nullptr,
|
||
nullptr,
|
||
nullptr,
|
||
loclist_base_addr,
|
||
nullptr,
|
||
nullptr,
|
||
expr_value,
|
||
&m_frame_base_error) == false)
|
||
{
|
||
// We should really have an error if evaluate returns, but in case
|
||
// we don't, lets set the error to something at least.
|
||
if (m_frame_base_error.Success())
|
||
m_frame_base_error.SetErrorString("Evaluation of the frame base expression failed.");
|
||
}
|
||
else
|
||
{
|
||
m_frame_base = expr_value.ResolveValue(&exe_ctx);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
m_frame_base_error.SetErrorString ("No function in symbol context.");
|
||
}
|
||
}
|
||
|
||
if (m_frame_base_error.Success())
|
||
frame_base = m_frame_base;
|
||
|
||
if (error_ptr)
|
||
*error_ptr = m_frame_base_error;
|
||
return m_frame_base_error.Success();
|
||
}
|
||
|
||
DWARFExpression *
|
||
StackFrame::GetFrameBaseExpression(Error *error_ptr)
|
||
{
|
||
if (!m_sc.function)
|
||
{
|
||
if (error_ptr)
|
||
{
|
||
error_ptr->SetErrorString ("No function in symbol context.");
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
return &m_sc.function->GetFrameBaseExpression();
|
||
}
|
||
|
||
RegisterContextSP
|
||
StackFrame::GetRegisterContext ()
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
if (!m_reg_context_sp)
|
||
{
|
||
ThreadSP thread_sp (GetThread());
|
||
if (thread_sp)
|
||
m_reg_context_sp = thread_sp->CreateRegisterContextForFrame (this);
|
||
}
|
||
return m_reg_context_sp;
|
||
}
|
||
|
||
bool
|
||
StackFrame::HasDebugInformation ()
|
||
{
|
||
GetSymbolContext (eSymbolContextLineEntry);
|
||
return m_sc.line_entry.IsValid();
|
||
}
|
||
|
||
ValueObjectSP
|
||
StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
ValueObjectSP valobj_sp;
|
||
if (m_is_history_frame)
|
||
{
|
||
return valobj_sp;
|
||
}
|
||
VariableList *var_list = GetVariableList (true);
|
||
if (var_list)
|
||
{
|
||
// Make sure the variable is a frame variable
|
||
const uint32_t var_idx = var_list->FindIndexForVariable (variable_sp.get());
|
||
const uint32_t num_variables = var_list->GetSize();
|
||
if (var_idx < num_variables)
|
||
{
|
||
valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex (var_idx);
|
||
if (!valobj_sp)
|
||
{
|
||
if (m_variable_list_value_objects.GetSize() < num_variables)
|
||
m_variable_list_value_objects.Resize(num_variables);
|
||
valobj_sp = ValueObjectVariable::Create (this, variable_sp);
|
||
m_variable_list_value_objects.SetValueObjectAtIndex (var_idx, valobj_sp);
|
||
}
|
||
}
|
||
}
|
||
if (use_dynamic != eNoDynamicValues && valobj_sp)
|
||
{
|
||
ValueObjectSP dynamic_sp = valobj_sp->GetDynamicValue (use_dynamic);
|
||
if (dynamic_sp)
|
||
return dynamic_sp;
|
||
}
|
||
return valobj_sp;
|
||
}
|
||
|
||
ValueObjectSP
|
||
StackFrame::TrackGlobalVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
if (m_is_history_frame)
|
||
return ValueObjectSP();
|
||
|
||
// Check to make sure we aren't already tracking this variable?
|
||
ValueObjectSP valobj_sp (GetValueObjectForFrameVariable (variable_sp, use_dynamic));
|
||
if (!valobj_sp)
|
||
{
|
||
// We aren't already tracking this global
|
||
VariableList *var_list = GetVariableList (true);
|
||
// If this frame has no variables, create a new list
|
||
if (var_list == nullptr)
|
||
m_variable_list_sp.reset (new VariableList());
|
||
|
||
// Add the global/static variable to this frame
|
||
m_variable_list_sp->AddVariable (variable_sp);
|
||
|
||
// Now make a value object for it so we can track its changes
|
||
valobj_sp = GetValueObjectForFrameVariable (variable_sp, use_dynamic);
|
||
}
|
||
return valobj_sp;
|
||
}
|
||
|
||
bool
|
||
StackFrame::IsInlined ()
|
||
{
|
||
if (m_sc.block == nullptr)
|
||
GetSymbolContext (eSymbolContextBlock);
|
||
if (m_sc.block)
|
||
return m_sc.block->GetContainingInlinedBlock() != nullptr;
|
||
return false;
|
||
}
|
||
|
||
lldb::LanguageType
|
||
StackFrame::GetLanguage ()
|
||
{
|
||
CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit;
|
||
if (cu)
|
||
return cu->GetLanguage();
|
||
return lldb::eLanguageTypeUnknown;
|
||
}
|
||
|
||
lldb::LanguageType
|
||
StackFrame::GuessLanguage ()
|
||
{
|
||
LanguageType lang_type = GetLanguage();
|
||
|
||
if (lang_type == eLanguageTypeUnknown)
|
||
{
|
||
Function *f = GetSymbolContext(eSymbolContextFunction).function;
|
||
if (f)
|
||
{
|
||
lang_type = f->GetMangled().GuessLanguage();
|
||
}
|
||
}
|
||
|
||
return lang_type;
|
||
}
|
||
|
||
namespace
|
||
{
|
||
std::pair<const Instruction::Operand *, int64_t>
|
||
GetBaseExplainingValue(const Instruction::Operand &operand,
|
||
RegisterContext ®ister_context,
|
||
lldb::addr_t value)
|
||
{
|
||
switch(operand.m_type)
|
||
{
|
||
case Instruction::Operand::Type::Dereference:
|
||
case Instruction::Operand::Type::Immediate:
|
||
case Instruction::Operand::Type::Invalid:
|
||
case Instruction::Operand::Type::Product:
|
||
// These are not currently interesting
|
||
return std::make_pair(nullptr, 0);
|
||
case Instruction::Operand::Type::Sum:
|
||
{
|
||
const Instruction::Operand *immediate_child = nullptr;
|
||
const Instruction::Operand *variable_child = nullptr;
|
||
if (operand.m_children[0].m_type == Instruction::Operand::Type::Immediate)
|
||
{
|
||
immediate_child = &operand.m_children[0];
|
||
variable_child = &operand.m_children[1];
|
||
}
|
||
else if (operand.m_children[1].m_type == Instruction::Operand::Type::Immediate)
|
||
{
|
||
immediate_child = &operand.m_children[1];
|
||
variable_child = &operand.m_children[0];
|
||
}
|
||
if (!immediate_child)
|
||
{
|
||
return std::make_pair(nullptr, 0);
|
||
}
|
||
lldb::addr_t adjusted_value = value;
|
||
if (immediate_child->m_negative)
|
||
{
|
||
adjusted_value += immediate_child->m_immediate;
|
||
}
|
||
else
|
||
{
|
||
adjusted_value -= immediate_child->m_immediate;
|
||
}
|
||
std::pair<const Instruction::Operand *, int64_t> base_and_offset = GetBaseExplainingValue(*variable_child, register_context, adjusted_value);
|
||
if (!base_and_offset.first)
|
||
{
|
||
return std::make_pair(nullptr, 0);
|
||
}
|
||
if (immediate_child->m_negative)
|
||
{
|
||
base_and_offset.second -= immediate_child->m_immediate;
|
||
}
|
||
else
|
||
{
|
||
base_and_offset.second += immediate_child->m_immediate;
|
||
}
|
||
return base_and_offset;
|
||
}
|
||
case Instruction::Operand::Type::Register:
|
||
{
|
||
const RegisterInfo *info = register_context.GetRegisterInfoByName(operand.m_register.AsCString());
|
||
if (!info)
|
||
{
|
||
return std::make_pair(nullptr, 0);
|
||
}
|
||
RegisterValue reg_value;
|
||
if (!register_context.ReadRegister(info, reg_value))
|
||
{
|
||
return std::make_pair(nullptr, 0);
|
||
}
|
||
if (reg_value.GetAsUInt64() == value)
|
||
{
|
||
return std::make_pair(&operand, 0);
|
||
}
|
||
else
|
||
{
|
||
return std::make_pair(nullptr, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
std::pair<const Instruction::Operand *, int64_t>
|
||
GetBaseExplainingDereference(const Instruction::Operand &operand,
|
||
RegisterContext ®ister_context,
|
||
lldb::addr_t addr)
|
||
{
|
||
if (operand.m_type == Instruction::Operand::Type::Dereference)
|
||
{
|
||
return GetBaseExplainingValue(operand.m_children[0],
|
||
register_context,
|
||
addr);
|
||
}
|
||
return std::make_pair(nullptr, 0);
|
||
}
|
||
};
|
||
|
||
lldb::ValueObjectSP
|
||
StackFrame::GuessValueForAddress(lldb::addr_t addr)
|
||
{
|
||
TargetSP target_sp = CalculateTarget();
|
||
|
||
const ArchSpec &target_arch = target_sp->GetArchitecture();
|
||
|
||
AddressRange pc_range;
|
||
pc_range.GetBaseAddress() = GetFrameCodeAddress();
|
||
pc_range.SetByteSize(target_arch.GetMaximumOpcodeByteSize());
|
||
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
|
||
const char *plugin_name = nullptr;
|
||
const char *flavor = nullptr;
|
||
const bool prefer_file_cache = false;
|
||
|
||
DisassemblerSP disassembler_sp = Disassembler::DisassembleRange (target_arch,
|
||
plugin_name,
|
||
flavor,
|
||
exe_ctx,
|
||
pc_range,
|
||
prefer_file_cache);
|
||
|
||
if (!disassembler_sp->GetInstructionList().GetSize())
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
InstructionSP instruction_sp = disassembler_sp->GetInstructionList().GetInstructionAtIndex(0);
|
||
|
||
llvm::SmallVector<Instruction::Operand, 3> operands;
|
||
|
||
if (!instruction_sp->ParseOperands(operands))
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
RegisterContextSP register_context_sp = GetRegisterContext();
|
||
|
||
if (!register_context_sp)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
for (const Instruction::Operand &operand : operands)
|
||
{
|
||
std::pair<const Instruction::Operand *, int64_t>
|
||
base_and_offset = GetBaseExplainingDereference(operand, *register_context_sp, addr);
|
||
|
||
if (!base_and_offset.first)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
switch (base_and_offset.first->m_type)
|
||
{
|
||
case Instruction::Operand::Type::Immediate:
|
||
{
|
||
lldb_private::Address addr;
|
||
if (target_sp->ResolveLoadAddress(base_and_offset.first->m_immediate + base_and_offset.second, addr))
|
||
{
|
||
TypeSystem *c_type_system = target_sp->GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC);
|
||
if (!c_type_system)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
else
|
||
{
|
||
CompilerType void_ptr_type = c_type_system->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar).GetPointerType();
|
||
return ValueObjectMemory::Create(this, "", addr, void_ptr_type);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
break;
|
||
}
|
||
case Instruction::Operand::Type::Register:
|
||
{
|
||
return GuessValueForRegisterAndOffset(base_and_offset.first->m_register, base_and_offset.second);
|
||
}
|
||
default:
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
}
|
||
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
namespace
|
||
{
|
||
ValueObjectSP
|
||
GetValueForOffset(StackFrame &frame, ValueObjectSP &parent, int64_t offset)
|
||
{
|
||
if (offset < 0 || uint64_t(offset) >= parent->GetByteSize())
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
if (parent->IsPointerOrReferenceType())
|
||
{
|
||
return parent;
|
||
}
|
||
|
||
for (int ci = 0, ce = parent->GetNumChildren(); ci != ce; ++ci)
|
||
{
|
||
const bool can_create = true;
|
||
ValueObjectSP child_sp = parent->GetChildAtIndex(ci, can_create);
|
||
|
||
if (!child_sp)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
int64_t child_offset = child_sp->GetByteOffset();
|
||
int64_t child_size = child_sp->GetByteSize();
|
||
|
||
if (offset >= child_offset &&
|
||
offset < (child_offset + child_size))
|
||
{
|
||
return GetValueForOffset(frame, child_sp, offset - child_offset);
|
||
}
|
||
}
|
||
|
||
if (offset == 0)
|
||
{
|
||
return parent;
|
||
}
|
||
else
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
}
|
||
|
||
ValueObjectSP
|
||
GetValueForDereferincingOffset(StackFrame &frame, ValueObjectSP &base, int64_t offset)
|
||
{
|
||
// base is a pointer to something
|
||
// offset is the thing to add to the pointer
|
||
// We return the most sensible ValueObject for the result of *(base+offset)
|
||
|
||
if (!base->IsPointerOrReferenceType())
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
Error error;
|
||
ValueObjectSP pointee = base->Dereference(error);
|
||
|
||
if (offset >= pointee->GetByteSize())
|
||
{
|
||
int64_t index = offset / pointee->GetByteSize();
|
||
offset = offset % pointee->GetByteSize();
|
||
const bool can_create = true;
|
||
pointee = base->GetSyntheticArrayMember(index, can_create);
|
||
}
|
||
|
||
if (!pointee || error.Fail())
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
return GetValueForOffset(frame, pointee, offset);
|
||
}
|
||
|
||
//------------------------------------------------------------------
|
||
/// Attempt to reconstruct the ValueObject for the address contained in a
|
||
/// given register plus an offset.
|
||
///
|
||
/// @params [in] frame
|
||
/// The current stack frame.
|
||
///
|
||
/// @params [in] reg
|
||
/// The register.
|
||
///
|
||
/// @params [in] offset
|
||
/// The offset from the register.
|
||
///
|
||
/// @param [in] disassembler
|
||
/// A disassembler containing instructions valid up to the current PC.
|
||
///
|
||
/// @param [in] variables
|
||
/// The variable list from the current frame,
|
||
///
|
||
/// @param [in] pc
|
||
/// The program counter for the instruction considered the 'user'.
|
||
///
|
||
/// @return
|
||
/// A string describing the base for the ExpressionPath. This could be a
|
||
/// variable, a register value, an argument, or a function return value.
|
||
/// The ValueObject if found. If valid, it has a valid ExpressionPath.
|
||
//------------------------------------------------------------------
|
||
lldb::ValueObjectSP
|
||
DoGuessValueAt(StackFrame &frame, ConstString reg, int64_t offset, Disassembler &disassembler, VariableList &variables, const Address &pc)
|
||
{
|
||
// Example of operation for Intel:
|
||
//
|
||
// +14: movq -0x8(%rbp), %rdi
|
||
// +18: movq 0x8(%rdi), %rdi
|
||
// +22: addl 0x4(%rdi), %eax
|
||
//
|
||
// f, a pointer to a struct, is known to be at -0x8(%rbp).
|
||
//
|
||
// DoGuessValueAt(frame, rdi, 4, dis, vars, 0x22) finds the instruction at +18 that assigns to rdi, and calls itself recursively for that dereference
|
||
// DoGuessValueAt(frame, rdi, 8, dis, vars, 0x18) finds the instruction at +14 that assigns to rdi, and calls itself recursively for that derefernece
|
||
// DoGuessValueAt(frame, rbp, -8, dis, vars, 0x14) finds "f" in the variable list.
|
||
// Returns a ValueObject for f. (That's what was stored at rbp-8 at +14)
|
||
// Returns a ValueObject for *(f+8) or f->b (That's what was stored at rdi+8 at +18)
|
||
// Returns a ValueObject for *(f->b+4) or f->b->a (That's what was stored at rdi+4 at +22)
|
||
|
||
// First, check the variable list to see if anything is at the specified location.
|
||
for (size_t vi = 0, ve = variables.GetSize(); vi != ve; ++vi)
|
||
{
|
||
VariableSP var_sp = variables.GetVariableAtIndex(vi);
|
||
DWARFExpression &dwarf_expression = var_sp->LocationExpression();
|
||
|
||
const RegisterInfo *expression_reg;
|
||
int64_t expression_offset;
|
||
ExecutionContext exe_ctx;
|
||
|
||
if (dwarf_expression.IsDereferenceOfRegister(frame, expression_reg, expression_offset))
|
||
{
|
||
if ((reg == ConstString(expression_reg->name) ||
|
||
reg == ConstString(expression_reg->alt_name)) &&
|
||
expression_offset == offset)
|
||
{
|
||
return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues);
|
||
}
|
||
}
|
||
}
|
||
|
||
bool is_in_return_register = false;
|
||
ABISP abi_sp = frame.CalculateProcess()->GetABI();
|
||
RegisterInfo return_register_info;
|
||
|
||
if (abi_sp)
|
||
{
|
||
const char *return_register_name;
|
||
const RegisterInfo *reg_info = nullptr;
|
||
if (abi_sp->GetPointerReturnRegister(return_register_name) &&
|
||
reg == ConstString(return_register_name) &&
|
||
(reg_info = frame.GetRegisterContext()->GetRegisterInfoByName(return_register_name)))
|
||
{
|
||
is_in_return_register = true;
|
||
return_register_info = *reg_info;
|
||
}
|
||
}
|
||
|
||
const uint32_t current_inst = disassembler.GetInstructionList().GetIndexOfInstructionAtAddress(pc);
|
||
if (current_inst == UINT32_MAX)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
ValueObjectSP source_path;
|
||
|
||
for (uint32_t ii = current_inst - 1; ii != (uint32_t)-1; --ii)
|
||
{
|
||
// This is not an exact algorithm, and it sacrifices accuracy for generality.
|
||
// Recognizing "mov" and "ld" instructions –– and which are their source and
|
||
// destination operands -- is something the disassembler should do for us.
|
||
InstructionSP instruction_sp = disassembler.GetInstructionList().GetInstructionAtIndex(ii);
|
||
|
||
if (is_in_return_register && instruction_sp->IsCall())
|
||
{
|
||
llvm::SmallVector<Instruction::Operand, 1> operands;
|
||
if (!instruction_sp->ParseOperands(operands) || operands.size() != 1)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
switch (operands[0].m_type)
|
||
{
|
||
default:
|
||
break;
|
||
case Instruction::Operand::Type::Immediate:
|
||
{
|
||
SymbolContext sc;
|
||
Address load_address;
|
||
if (!frame.CalculateTarget()->ResolveLoadAddress(operands[0].m_immediate, load_address))
|
||
{
|
||
break;
|
||
}
|
||
frame.CalculateTarget()->GetImages().ResolveSymbolContextForAddress(load_address, eSymbolContextFunction, sc);
|
||
if (!sc.function)
|
||
{
|
||
break;
|
||
}
|
||
CompilerType function_type = sc.function->GetCompilerType();
|
||
if (!function_type.IsFunctionType())
|
||
{
|
||
break;
|
||
}
|
||
CompilerType return_type = function_type.GetFunctionReturnType();
|
||
RegisterValue return_value;
|
||
if (!frame.GetRegisterContext()->ReadRegister(&return_register_info, return_value))
|
||
{
|
||
break;
|
||
}
|
||
std::string name_str(sc.function->GetName().AsCString("<unknown function>"));
|
||
name_str.append("()");
|
||
Address return_value_address(return_value.GetAsUInt64());
|
||
ValueObjectSP return_value_sp = ValueObjectMemory::Create(&frame, name_str.c_str(), return_value_address, return_type);
|
||
return GetValueForDereferincingOffset(frame, return_value_sp, offset);
|
||
}
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
llvm::SmallVector<Instruction::Operand, 2> operands;
|
||
if (!instruction_sp->ParseOperands(operands) || operands.size() != 2)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
Instruction::Operand *register_operand = nullptr;
|
||
Instruction::Operand *origin_operand = nullptr;
|
||
if (operands[0].m_type == Instruction::Operand::Type::Register &&
|
||
operands[0].m_clobbered == true &&
|
||
operands[0].m_register == reg)
|
||
{
|
||
register_operand = &operands[0];
|
||
origin_operand = &operands[1];
|
||
}
|
||
else if (operands[1].m_type == Instruction::Operand::Type::Register &&
|
||
operands[1].m_clobbered == true &&
|
||
operands[1].m_register == reg)
|
||
{
|
||
register_operand = &operands[1];
|
||
origin_operand = &operands[0];
|
||
}
|
||
else
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// We have an origin operand. Can we track its value down?
|
||
switch (origin_operand->m_type)
|
||
{
|
||
default:
|
||
break;
|
||
case Instruction::Operand::Type::Register:
|
||
source_path = DoGuessValueAt(frame, origin_operand->m_register, 0, disassembler, variables, instruction_sp->GetAddress());
|
||
break;
|
||
case Instruction::Operand::Type::Dereference:
|
||
{
|
||
const Instruction::Operand &pointer = origin_operand->m_children[0];
|
||
switch (pointer.m_type)
|
||
{
|
||
default:
|
||
break;
|
||
case Instruction::Operand::Type::Register:
|
||
source_path = DoGuessValueAt(frame, pointer.m_register, 0, disassembler, variables, instruction_sp->GetAddress());
|
||
if (source_path)
|
||
{
|
||
Error err;
|
||
source_path = source_path->Dereference(err);
|
||
if (!err.Success())
|
||
{
|
||
source_path.reset();
|
||
}
|
||
}
|
||
break;
|
||
case Instruction::Operand::Type::Sum:
|
||
{
|
||
const Instruction::Operand *origin_register = nullptr;
|
||
const Instruction::Operand *origin_offset = nullptr;
|
||
if (pointer.m_children.size() != 2)
|
||
{
|
||
break;
|
||
}
|
||
if (pointer.m_children[0].m_type == Instruction::Operand::Type::Register &&
|
||
pointer.m_children[1].m_type == Instruction::Operand::Type::Immediate)
|
||
{
|
||
origin_register = &pointer.m_children[0];
|
||
origin_offset = &pointer.m_children[1];
|
||
}
|
||
else if (pointer.m_children[1].m_type == Instruction::Operand::Type::Register &&
|
||
pointer.m_children[0].m_type == Instruction::Operand::Type::Immediate)
|
||
{
|
||
origin_register = &pointer.m_children[1];
|
||
origin_offset = &pointer.m_children[0];
|
||
}
|
||
if (!origin_register)
|
||
{
|
||
break;
|
||
}
|
||
int64_t signed_origin_offset = origin_offset->m_negative ? -((int64_t)origin_offset->m_immediate) : origin_offset->m_immediate;
|
||
source_path = DoGuessValueAt(frame, origin_register->m_register, signed_origin_offset, disassembler, variables, instruction_sp->GetAddress());
|
||
if (!source_path)
|
||
{
|
||
break;
|
||
}
|
||
source_path = GetValueForDereferincingOffset(frame, source_path, offset);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (source_path)
|
||
{
|
||
return source_path;
|
||
}
|
||
}
|
||
|
||
return ValueObjectSP();
|
||
}
|
||
}
|
||
|
||
lldb::ValueObjectSP
|
||
StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset)
|
||
{
|
||
TargetSP target_sp = CalculateTarget();
|
||
|
||
const ArchSpec &target_arch = target_sp->GetArchitecture();
|
||
|
||
Block *frame_block = GetFrameBlock();
|
||
|
||
if (!frame_block)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
Function *function = frame_block->CalculateSymbolContextFunction();
|
||
if (!function)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
AddressRange pc_range = function->GetAddressRange();
|
||
|
||
if (GetFrameCodeAddress().GetFileAddress() < pc_range.GetBaseAddress().GetFileAddress() ||
|
||
GetFrameCodeAddress().GetFileAddress() - pc_range.GetBaseAddress().GetFileAddress() >= pc_range.GetByteSize())
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
|
||
const char *plugin_name = nullptr;
|
||
const char *flavor = nullptr;
|
||
const bool prefer_file_cache = false;
|
||
DisassemblerSP disassembler_sp = Disassembler::DisassembleRange (target_arch,
|
||
plugin_name,
|
||
flavor,
|
||
exe_ctx,
|
||
pc_range,
|
||
prefer_file_cache);
|
||
|
||
if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize())
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
const bool get_file_globals = false;
|
||
VariableList *variables = GetVariableList(get_file_globals);
|
||
|
||
if (!variables)
|
||
{
|
||
return ValueObjectSP();
|
||
}
|
||
|
||
return DoGuessValueAt(*this, reg, offset, *disassembler_sp, *variables, GetFrameCodeAddress());
|
||
}
|
||
|
||
TargetSP
|
||
StackFrame::CalculateTarget ()
|
||
{
|
||
TargetSP target_sp;
|
||
ThreadSP thread_sp(GetThread());
|
||
if (thread_sp)
|
||
{
|
||
ProcessSP process_sp (thread_sp->CalculateProcess());
|
||
if (process_sp)
|
||
target_sp = process_sp->CalculateTarget();
|
||
}
|
||
return target_sp;
|
||
}
|
||
|
||
ProcessSP
|
||
StackFrame::CalculateProcess ()
|
||
{
|
||
ProcessSP process_sp;
|
||
ThreadSP thread_sp(GetThread());
|
||
if (thread_sp)
|
||
process_sp = thread_sp->CalculateProcess();
|
||
return process_sp;
|
||
}
|
||
|
||
ThreadSP
|
||
StackFrame::CalculateThread ()
|
||
{
|
||
return GetThread();
|
||
}
|
||
|
||
StackFrameSP
|
||
StackFrame::CalculateStackFrame ()
|
||
{
|
||
return shared_from_this();
|
||
}
|
||
|
||
void
|
||
StackFrame::CalculateExecutionContext (ExecutionContext &exe_ctx)
|
||
{
|
||
exe_ctx.SetContext (shared_from_this());
|
||
}
|
||
|
||
void
|
||
StackFrame::DumpUsingSettingsFormat (Stream *strm, const char *frame_marker)
|
||
{
|
||
if (strm == nullptr)
|
||
return;
|
||
|
||
GetSymbolContext(eSymbolContextEverything);
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
StreamString s;
|
||
|
||
if (frame_marker)
|
||
s.PutCString(frame_marker);
|
||
|
||
const FormatEntity::Entry *frame_format = nullptr;
|
||
Target *target = exe_ctx.GetTargetPtr();
|
||
if (target)
|
||
frame_format = target->GetDebugger().GetFrameFormat();
|
||
if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx, nullptr, nullptr, false, false))
|
||
{
|
||
strm->Write(s.GetData(), s.GetSize());
|
||
}
|
||
else
|
||
{
|
||
Dump (strm, true, false);
|
||
strm->EOL();
|
||
}
|
||
}
|
||
|
||
void
|
||
StackFrame::Dump (Stream *strm, bool show_frame_index, bool show_fullpaths)
|
||
{
|
||
if (strm == nullptr)
|
||
return;
|
||
|
||
if (show_frame_index)
|
||
strm->Printf("frame #%u: ", m_frame_index);
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
Target *target = exe_ctx.GetTargetPtr();
|
||
strm->Printf("0x%0*" PRIx64 " ",
|
||
target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16,
|
||
GetFrameCodeAddress().GetLoadAddress(target));
|
||
GetSymbolContext(eSymbolContextEverything);
|
||
const bool show_module = true;
|
||
const bool show_inline = true;
|
||
const bool show_function_arguments = true;
|
||
const bool show_function_name = true;
|
||
m_sc.DumpStopContext (strm,
|
||
exe_ctx.GetBestExecutionContextScope(),
|
||
GetFrameCodeAddress(),
|
||
show_fullpaths,
|
||
show_module,
|
||
show_inline,
|
||
show_function_arguments,
|
||
show_function_name);
|
||
}
|
||
|
||
void
|
||
StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
assert (GetStackID() == prev_frame.GetStackID()); // TODO: remove this after some testing
|
||
m_variable_list_sp = prev_frame.m_variable_list_sp;
|
||
m_variable_list_value_objects.Swap (prev_frame.m_variable_list_value_objects);
|
||
if (!m_disassembly.GetString().empty())
|
||
m_disassembly.GetString().swap (m_disassembly.GetString());
|
||
}
|
||
|
||
void
|
||
StackFrame::UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame)
|
||
{
|
||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||
assert (GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing
|
||
m_id.SetPC (curr_frame.m_id.GetPC()); // Update the Stack ID PC value
|
||
assert (GetThread() == curr_frame.GetThread());
|
||
m_frame_index = curr_frame.m_frame_index;
|
||
m_concrete_frame_index = curr_frame.m_concrete_frame_index;
|
||
m_reg_context_sp = curr_frame.m_reg_context_sp;
|
||
m_frame_code_addr = curr_frame.m_frame_code_addr;
|
||
assert (!m_sc.target_sp || !curr_frame.m_sc.target_sp || m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get());
|
||
assert (!m_sc.module_sp || !curr_frame.m_sc.module_sp || m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get());
|
||
assert (m_sc.comp_unit == nullptr || curr_frame.m_sc.comp_unit == nullptr || m_sc.comp_unit == curr_frame.m_sc.comp_unit);
|
||
assert (m_sc.function == nullptr || curr_frame.m_sc.function == nullptr || m_sc.function == curr_frame.m_sc.function);
|
||
m_sc = curr_frame.m_sc;
|
||
m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything);
|
||
m_flags.Set (m_sc.GetResolvedMask());
|
||
m_frame_base.Clear();
|
||
m_frame_base_error.Clear();
|
||
}
|
||
|
||
bool
|
||
StackFrame::HasCachedData () const
|
||
{
|
||
if (m_variable_list_sp)
|
||
return true;
|
||
if (m_variable_list_value_objects.GetSize() > 0)
|
||
return true;
|
||
if (!m_disassembly.GetString().empty())
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
bool
|
||
StackFrame::GetStatus (Stream& strm,
|
||
bool show_frame_info,
|
||
bool show_source,
|
||
const char *frame_marker)
|
||
{
|
||
|
||
if (show_frame_info)
|
||
{
|
||
strm.Indent();
|
||
DumpUsingSettingsFormat (&strm, frame_marker);
|
||
}
|
||
|
||
if (show_source)
|
||
{
|
||
ExecutionContext exe_ctx (shared_from_this());
|
||
bool have_source = false, have_debuginfo = false;
|
||
Debugger::StopDisassemblyType disasm_display = Debugger::eStopDisassemblyTypeNever;
|
||
Target *target = exe_ctx.GetTargetPtr();
|
||
if (target)
|
||
{
|
||
Debugger &debugger = target->GetDebugger();
|
||
const uint32_t source_lines_before = debugger.GetStopSourceLineCount(true);
|
||
const uint32_t source_lines_after = debugger.GetStopSourceLineCount(false);
|
||
disasm_display = debugger.GetStopDisassemblyDisplay ();
|
||
|
||
GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry);
|
||
if (m_sc.comp_unit && m_sc.line_entry.IsValid())
|
||
{
|
||
have_debuginfo = true;
|
||
if (source_lines_before > 0 || source_lines_after > 0)
|
||
{
|
||
size_t num_lines = target->GetSourceManager().DisplaySourceLinesWithLineNumbers (m_sc.line_entry.file,
|
||
m_sc.line_entry.line,
|
||
source_lines_before,
|
||
source_lines_after,
|
||
"->",
|
||
&strm);
|
||
if (num_lines != 0)
|
||
have_source = true;
|
||
// TODO: Give here a one time warning if source file is missing.
|
||
}
|
||
}
|
||
switch (disasm_display)
|
||
{
|
||
case Debugger::eStopDisassemblyTypeNever:
|
||
break;
|
||
|
||
case Debugger::eStopDisassemblyTypeNoDebugInfo:
|
||
if (have_debuginfo)
|
||
break;
|
||
LLVM_FALLTHROUGH;
|
||
|
||
case Debugger::eStopDisassemblyTypeNoSource:
|
||
if (have_source)
|
||
break;
|
||
LLVM_FALLTHROUGH;
|
||
|
||
case Debugger::eStopDisassemblyTypeAlways:
|
||
if (target)
|
||
{
|
||
const uint32_t disasm_lines = debugger.GetDisassemblyLineCount();
|
||
if (disasm_lines > 0)
|
||
{
|
||
const ArchSpec &target_arch = target->GetArchitecture();
|
||
AddressRange pc_range;
|
||
pc_range.GetBaseAddress() = GetFrameCodeAddress();
|
||
pc_range.SetByteSize(disasm_lines * target_arch.GetMaximumOpcodeByteSize());
|
||
const char *plugin_name = nullptr;
|
||
const char *flavor = nullptr;
|
||
Disassembler::Disassemble (target->GetDebugger(),
|
||
target_arch,
|
||
plugin_name,
|
||
flavor,
|
||
exe_ctx,
|
||
pc_range,
|
||
disasm_lines,
|
||
0,
|
||
Disassembler::eOptionMarkPCAddress,
|
||
strm);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|