summaryrefslogtreecommitdiffstats
path: root/lldb/source/Target/StackFrame.cpp
diff options
context:
space:
mode:
authorSean Callanan <scallanan@apple.com>2016-09-06 04:48:36 +0000
committerSean Callanan <scallanan@apple.com>2016-09-06 04:48:36 +0000
commit4740a734bb4a4f20ae4895ded32585e54bb87afb (patch)
tree9884944c80582b90d8ff4fdbf64fd8716766f8e2 /lldb/source/Target/StackFrame.cpp
parent24dac6afe29ccbd4455dc44d6d6bc85b04bfbc31 (diff)
downloadbcm5719-llvm-4740a734bb4a4f20ae4895ded32585e54bb87afb.tar.gz
bcm5719-llvm-4740a734bb4a4f20ae4895ded32585e54bb87afb.zip
Added the "frame diagnose" command and use its output to make crash info better.
When a process stops due to a crash, we get the crashing instruction and the crashing memory location (if there is one). From the user's perspective it is often unclear what the reason for the crash is in a symbolic sense. To address this, I have added new fuctionality to StackFrame to parse the disassembly and reconstruct the sequence of dereferneces and offsets that were applied to a known variable (or fuction retrn value) to obtain the invalid pointer. This makes use of enhancements in the disassembler, as well as new information provided by the DWARF expression infrastructure, and is exposed through a "frame diagnose" command. It is also used to provide symbolic information, when available, in the event of a crash. The algorithm is very rudimentary, and it needs a bunch of work, including - better parsing for assembly, preferably with help from LLVM - support for non-Apple platforms - cleanup of the algorithm core, preferably to make it all work in terms of Operands instead of register/offset pairs - improvement of the GetExpressioPath() logic to make prettier expression paths, and - better handling of vtables. I welcome all suggestios, improvements, and testcases. llvm-svn: 280692
Diffstat (limited to 'lldb/source/Target/StackFrame.cpp')
-rw-r--r--lldb/source/Target/StackFrame.cpp583
1 files changed, 583 insertions, 0 deletions
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 7110051d28e..aaf0a4d7ac7 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -20,12 +20,14 @@
#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"
@@ -1236,6 +1238,21 @@ StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr)
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 ()
{
@@ -1354,6 +1371,572 @@ StackFrame::GuessLanguage ()
return lang_type;
}
+namespace
+{
+ std::pair<const Instruction::Operand *, int64_t>
+ GetBaseExplainingValue(const Instruction::Operand &operand,
+ RegisterContext &register_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 &register_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 || 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 ()
{
OpenPOWER on IntegriCloud