uint32_t
GetData (DataExtractor &data);
+
+ struct Operand
+ {
+ enum class Type {
+ Invalid = 0,
+ Register,
+ Immediate,
+ Dereference,
+ Sum,
+ Product
+ } m_type = Type::Invalid;
+ std::vector<const Operand> m_children;
+ lldb::addr_t m_immediate = 0;
+ ConstString m_register;
+ bool m_negative = false;
+ bool m_clobbered = false;
+
+ bool IsValid() { return m_type != Type::Invalid; }
+ };
+
+ virtual bool
+ ParseOperands (llvm::SmallVectorImpl<Operand> &operands)
+ {
+ return false;
+ }
+
+ virtual bool
+ IsCall ()
+ {
+ return false;
+ }
protected:
Address m_address; // The section offset address of this instruction
const DWARFCompileUnit* cu,
const DataExtractor& debug_loc_data,
lldb::offset_t offset);
-
+
+ bool
+ IsRegister(StackFrame &frame,
+ const RegisterInfo *®ister_info);
+
+ bool
+ IsDereferenceOfRegister(StackFrame &frame,
+ const RegisterInfo *®ister_info,
+ int64_t &offset);
protected:
//------------------------------------------------------------------
/// Pretty-prints the location expression to a stream
lldb::offset_t* offset_ptr,
lldb::addr_t& low_pc,
lldb::addr_t& high_pc);
+
+ bool
+ GetOpAndEndOffsets(StackFrame &frame,
+ lldb::offset_t &op_offset,
+ lldb::offset_t &end_offset);
//------------------------------------------------------------------
/// Classes that inherit from DWARFExpression can see and modify these
GetRegisterInfoByKind (lldb::RegisterKind reg_kind,
uint32_t reg_num,
RegisterInfo &info);
+
+ virtual bool
+ GetPointerReturnRegister (const char *&name)
+ {
+ return false;
+ }
static lldb::ABISP
FindPlugin (const ArchSpec &arch);
//------------------------------------------------------------------
bool
GetFrameBaseValue(Scalar &value, Error *error_ptr);
+
+ //------------------------------------------------------------------
+ /// Get the DWARFExpression corresponding to the Canonical Frame Address.
+ ///
+ /// Often a register (bp), but sometimes a register + offset.
+ ///
+ /// @param [out] error_ptr
+ /// If there is an error determining the CFA address, this may contain a
+ /// string explaining the failure.
+ ///
+ /// @return
+ /// Returns the corresponding DWARF expression, or NULL.
+ //------------------------------------------------------------------
+ DWARFExpression *
+ GetFrameBaseExpression(Error *error_ptr);
//------------------------------------------------------------------
/// Get the current lexical scope block for this StackFrame, if possible.
GuessLanguage ();
//------------------------------------------------------------------
+ /// Attempt to econstruct the ValueObject for a given raw address touched by
+ /// the current instruction. The ExpressionPath should indicate how to get
+ /// to this value using "frame variable."
+ ///
+ /// @params [in] addr
+ /// The raw address.
+ ///
+ /// @return
+ /// The ValueObject if found. If valid, it has a valid ExpressionPath.
+ //------------------------------------------------------------------
+ lldb::ValueObjectSP
+ GuessValueForAddress(lldb::addr_t addr);
+
+ //------------------------------------------------------------------
+ /// Attempt to reconstruct the ValueObject for the address contained in a
+ /// given register plus an offset. The ExpressionPath should indicate how to
+ /// get to this value using "frame variable."
+ ///
+ /// @params [in] reg
+ /// The name of the register.
+ ///
+ /// @params [in] offset
+ /// The offset from the register. Particularly important for sp...
+ ///
+ /// @return
+ /// The ValueObject if found. If valid, it has a valid ExpressionPath.
+ //------------------------------------------------------------------
+ lldb::ValueObjectSP
+ GuessValueForRegisterAndOffset(ConstString reg, int64_t offset);
+
+ //------------------------------------------------------------------
// lldb::ExecutionContextScope pure virtual functions
//------------------------------------------------------------------
lldb::TargetSP
void
CalculateExecutionContext(ExecutionContext &exe_ctx) override;
-
+
protected:
friend class StackFrameList;
bool
HasCachedData () const;
-
+
private:
//------------------------------------------------------------------
// For StackFrame only
static lldb::ExpressionVariableSP
GetExpressionVariable (lldb::StopInfoSP &stop_info_sp);
+
+ static lldb::ValueObjectSP
+ GetCrashingDereference (lldb::StopInfoSP &stop_info_sp, lldb::addr_t *crashing_address = nullptr);
protected:
// Perform any action that is associated with this stop. This is done as the
--- /dev/null
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for an array access
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestArray(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_array(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", substrs=["a[10]"])
--- /dev/null
+struct Foo {
+ int b;
+ int c;
+};
+
+int main() {
+ struct Foo *a = 0;
+ return a[10].c;
+}
--- /dev/null
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for dereferencing a bad reference
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestBadReference(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_bad_reference(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "f->b")
--- /dev/null
+struct Bar {
+ int c;
+ int d;
+};
+
+struct Foo {
+ int a;
+ struct Bar &b;
+};
+
+struct Foo *GetAFoo() {
+ static struct Foo f = { 0, *((Bar*)0) };
+ return &f;
+}
+
+int GetSum(struct Foo *f) {
+ return f->a + f->b.d;
+}
+
+int main() {
+ return GetSum(GetAFoo());
+}
--- /dev/null
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for a subexpression of a complicated expression
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestDiagnoseDereferenceArgument(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_diagnose_dereference_argument(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "f->b->d")
--- /dev/null
+struct Bar {
+ int c;
+ int d;
+};
+
+struct Foo {
+ int a;
+ struct Bar *b;
+};
+
+struct Foo *GetAFoo() {
+ static struct Foo f = { 0, 0 };
+ return &f;
+}
+
+int SumTwoIntegers(int x, int y) {
+ return x + y;
+}
+
+int GetSum(struct Foo *f) {
+ return SumTwoIntegers(f->a, f->b->d ? 0 : 1);
+}
+
+int main() {
+ return GetSum(GetAFoo());
+}
--- /dev/null
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for dereferencing a function argument
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestDiagnoseDereferenceArgument(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_diagnose_dereference_argument(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "f->b->d")
--- /dev/null
+struct Bar {
+ int c;
+ int d;
+};
+
+struct Foo {
+ int a;
+ struct Bar *b;
+};
+
+struct Foo *GetAFoo() {
+ static struct Foo f = { 0, 0 };
+ return &f;
+}
+
+int GetSum(struct Foo *f) {
+ return f->a + f->b->d;
+}
+
+int main() {
+ return GetSum(GetAFoo());
+}
--- /dev/null
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for dereferencing a function's return value
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestDiagnoseDereferenceFunctionReturn(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_diagnose_dereference_function_return(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", substrs = ["GetAFoo", "->b"])
+
--- /dev/null
+struct Foo {
+ int a;
+ int b;
+};
+
+struct Foo *GetAFoo() {
+ return 0;
+}
+
+int main() {
+ return GetAFoo()->b;
+}
--- /dev/null
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for dereferencing `this`
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestDiagnoseDereferenceThis(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_diagnose_dereference_this(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "this->a")
--- /dev/null
+struct Foo {
+ int a;
+ int b;
+ int Sum() { return a + b; }
+};
+
+struct Foo *GetAFoo() {
+ return (struct Foo*)0;
+}
+
+int main() {
+ struct Foo *foo = GetAFoo();
+ return foo->Sum();
+}
+
--- /dev/null
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for calling virtual methods
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestDiagnoseInheritance(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_diagnose_inheritance(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "d")
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+
+class A
+{
+public:
+ A(int a) :
+ m_a(a)
+ {
+ }
+ virtual ~A(){}
+ virtual int get2() const { return m_a; }
+ virtual int get() const { return m_a; }
+protected:
+ int m_a;
+};
+
+class B : public A
+{
+public:
+ B(int a, int b) :
+ A(a),
+ m_b(b)
+ {
+ }
+
+ ~B() override
+ {
+ }
+
+ int get2() const override
+ {
+ return m_b;
+ }
+ int get() const override
+ {
+ return m_b;
+ }
+
+protected:
+ int m_b;
+};
+
+struct C
+{
+ C(int c) : m_c(c){}
+ virtual ~C(){}
+ int m_c;
+};
+
+class D : public C, public B
+{
+public:
+ D(int a, int b, int c, int d) :
+ C(c),
+ B(a, b),
+ m_d(d)
+ {
+ }
+protected:
+ int m_d;
+};
+int main (int argc, char const *argv[], char const *envp[])
+{
+ D *good_d = new D(1, 2, 3, 4);
+ D *d = nullptr;
+ return d->get();
+}
+
--- /dev/null
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for dereferencing a local variable
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestLocalVariable(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_local_variable(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "myInt")
--- /dev/null
+int main() {
+ int *myInt = 0;
+ return *myInt;
+}
--- /dev/null
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+"""
+Test the output of `frame diagnose` for calling virtual methods
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestDiagnoseVirtualMethodCall(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessDarwin
+ def test_diagnose_virtual_method_call(self):
+ TestBase.setUp(self)
+ self.build()
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread list", "Thread should be stopped",
+ substrs = ['stopped'])
+ self.expect("frame diagnose", "Crash diagnosis was accurate", "foo")
--- /dev/null
+class Foo {
+public:
+ int a;
+ int b;
+ virtual int Sum() { return a + b; }
+};
+
+struct Foo *GetAFoo() {
+ return (struct Foo*)0;
+}
+
+int main() {
+ struct Foo *foo = GetAFoo();
+ return foo->Sum();
+}
+
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBAssert.h"
using namespace lldb;
using namespace lldb_private;
+#pragma mark CommandObjectFrameDiagnose
+
+//-------------------------------------------------------------------------
+// CommandObjectFrameInfo
+//-------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------
+// CommandObjectFrameDiagnose
+//-------------------------------------------------------------------------
+
+class CommandObjectFrameDiagnose : public CommandObjectParsed
+{
+public:
+ class CommandOptions : public Options
+ {
+ public:
+ CommandOptions() :
+ Options()
+ {
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Error
+ SetOptionValue(uint32_t option_idx, const char *option_arg,
+ ExecutionContext *execution_context) override
+ {
+ Error error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option)
+ {
+ case 'r':
+ reg = ConstString(option_arg);
+ break;
+
+ case 'a':
+ {
+ bool success = false;
+
+ address = StringConvert::ToUInt64 (option_arg, 0, 0, &success);
+ if (!success)
+ {
+ address.reset();
+ error.SetErrorStringWithFormat ("invalid address argument '%s'", option_arg);
+ }
+ }
+ break;
+
+ case 'o':
+ {
+ bool success = false;
+
+ offset = StringConvert::ToSInt64 (option_arg, 0, 0, &success);
+ if (!success)
+ {
+ offset.reset();
+ error.SetErrorStringWithFormat ("invalid offset argument '%s'", option_arg);
+ }
+ }
+ break;
+
+ default:
+ error.SetErrorStringWithFormat ("invalid short option character '%c'", short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void
+ OptionParsingStarting(ExecutionContext *execution_context) override
+ {
+ address.reset();
+ reg.reset();
+ offset.reset();
+ }
+
+ const OptionDefinition*
+ GetDefinitions () override
+ {
+ return g_option_table;
+ }
+
+ // Options table: Required for subclasses of Options.
+ static OptionDefinition g_option_table[];
+
+ // Options.
+ llvm::Optional<lldb::addr_t> address;
+ llvm::Optional<ConstString> reg;
+ llvm::Optional<int64_t> offset;
+ };
+
+ CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame diagnose",
+ "Try to determine what path path the current stop location used to get to a register or address",
+ nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options()
+ {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the argument entry.
+ arg.push_back (index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back (arg);
+ }
+
+ ~CommandObjectFrameDiagnose() override = default;
+
+ Options *
+ GetOptions () override
+ {
+ return &m_options;
+ }
+
+protected:
+ bool
+ DoExecute (Args& command, CommandReturnObject &result) override
+ {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ StackFrameSP frame_sp = thread->GetSelectedFrame();
+
+ ValueObjectSP valobj_sp;
+
+ if (m_options.address.hasValue())
+ {
+ if (m_options.reg.hasValue() || m_options.offset.hasValue())
+ {
+ result.AppendError("`frame diagnose --address` is incompatible with other arguments.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue());
+ }
+ else if (m_options.reg.hasValue())
+ {
+ valobj_sp = frame_sp->GuessValueForRegisterAndOffset(m_options.reg.getValue(), m_options.offset.getValueOr(0));
+ }
+ else
+ {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (!stop_info_sp)
+ {
+ result.AppendError("No arguments provided, and no stop info.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
+ }
+
+ if (!valobj_sp)
+ {
+ result.AppendError("No diagnosis available.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const bool qualify_cxx_base_classes = false;
+
+ DumpValueObjectOptions::DeclPrintingHelper helper = [&valobj_sp](ConstString type,
+ ConstString var,
+ const DumpValueObjectOptions &opts,
+ Stream &stream) -> bool {
+ const ValueObject::GetExpressionPathFormat format = ValueObject::GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
+ valobj_sp->GetExpressionPath(stream, qualify_cxx_base_classes, format);
+ stream.PutCString(" =");
+ return true;
+ };
+
+ DumpValueObjectOptions options;
+ options.SetDeclPrintingHelper(helper);
+ ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(), options);
+ printer.PrintValueObject();
+
+ return true;
+ }
+
+protected:
+ CommandOptions m_options;
+};
+
+OptionDefinition
+CommandObjectFrameDiagnose::CommandOptions::g_option_table[] =
+{
+ // clang-format off
+ {LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegisterName, "A register to diagnose."},
+ {LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddress, "An address to diagnose."},
+ {LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "An optional offset. Requires --register."},
+ {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr}
+ // clang-format on
+};
+
#pragma mark CommandObjectFrameInfo
//-------------------------------------------------------------------------
"Commands for selecting and examing the current thread's stack frames.",
"frame <subcommand> [<subcommand-options>]")
{
+ LoadSubCommand ("diagnose", CommandObjectSP (new CommandObjectFrameDiagnose (interpreter)));
LoadSubCommand ("info", CommandObjectSP (new CommandObjectFrameInfo (interpreter)));
LoadSubCommand ("select", CommandObjectSP (new CommandObjectFrameSelect (interpreter)));
LoadSubCommand ("variable", CommandObjectSP (new CommandObjectFrameVariable (interpreter)));
#include "lldb/Host/Endian.h"
#include "lldb/Host/Host.h"
+#include "lldb/Symbol/Function.h"
+
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
offset += loc_length;
}
}
+
+bool
+DWARFExpression::GetOpAndEndOffsets(StackFrame &frame, lldb::offset_t &op_offset, lldb::offset_t &end_offset)
+{
+ SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction);
+ if (!sc.function)
+ {
+ return false;
+ }
+
+ addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
+ {
+ return false;
+ }
+
+ addr_t pc_file_addr = frame.GetFrameCodeAddress().GetFileAddress();
+ lldb::offset_t opcodes_offset, opcodes_length;
+ if (!GetLocation(loclist_base_file_addr, pc_file_addr, opcodes_offset, opcodes_length))
+ {
+ return false;
+ }
+
+ if (opcodes_length == 0)
+ {
+ return false;
+ }
+
+ op_offset = opcodes_offset;
+ end_offset = opcodes_offset + opcodes_length;
+ return true;
+}
+
+bool
+DWARFExpression::IsRegister(StackFrame &frame,
+ const RegisterInfo *®ister_info)
+{
+ lldb::offset_t op_offset;
+ lldb::offset_t end_offset;
+ if (!GetOpAndEndOffsets(frame, op_offset, end_offset))
+ {
+ return false;
+ }
+
+ if (!m_data.ValidOffset(op_offset) || op_offset >= end_offset)
+ {
+ return false;
+ }
+
+ RegisterContextSP reg_ctx_sp = frame.GetRegisterContext();
+ if (!reg_ctx_sp)
+ {
+ return false;
+ }
+
+ DataExtractor opcodes = m_data;
+ uint8_t opcode = opcodes.GetU8(&op_offset);
+
+ if (opcode >= DW_OP_reg0 && opcode <= DW_OP_breg31)
+ {
+ register_info = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0);
+ return register_info != nullptr;
+ }
+ switch (opcode)
+ {
+ default:
+ return false;
+ case DW_OP_regx:
+ {
+ uint32_t reg_num = m_data.GetULEB128(&op_offset);
+ register_info = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
+ return register_info != nullptr;
+ }
+ }
+}
+
+bool
+DWARFExpression::IsDereferenceOfRegister(StackFrame &frame,
+ const RegisterInfo *®ister_info,
+ int64_t &offset)
+{
+ lldb::offset_t op_offset;
+ lldb::offset_t end_offset;
+ if (!GetOpAndEndOffsets(frame, op_offset, end_offset))
+ {
+ return false;
+ }
+
+ if (!m_data.ValidOffset(op_offset) || op_offset >= end_offset)
+ {
+ return false;
+ }
+
+ RegisterContextSP reg_ctx_sp = frame.GetRegisterContext();
+ if (!reg_ctx_sp)
+ {
+ return false;
+ }
+
+ DataExtractor opcodes = m_data;
+ uint8_t opcode = opcodes.GetU8(&op_offset);
+
+ switch (opcode)
+ {
+ default:
+ return false;
+ case DW_OP_bregx:
+ {
+ uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
+ int64_t breg_offset = opcodes.GetSLEB128(&op_offset);
+
+ const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
+ if (!reg_info)
+ {
+ return false;
+ }
+
+ register_info = reg_info;
+ offset = breg_offset;
+ return true;
+ }
+ case DW_OP_fbreg:
+ {
+ int64_t fbreg_offset = opcodes.GetSLEB128(&op_offset);
+
+ DWARFExpression *dwarf_expression = frame.GetFrameBaseExpression(nullptr);
+
+ if (!dwarf_expression)
+ {
+ return false;
+ }
+
+ const RegisterInfo *fbr_info;
+
+ if (!dwarf_expression->IsRegister(frame, fbr_info))
+ {
+ return false;
+ }
+
+ register_info = fbr_info;
+ offset = fbreg_offset;
+ return true;
+ }
+ }
+}
+
return g_register_infos;
}
+bool
+ABISysV_arm64::GetPointerReturnRegister (const char *&name)
+{
+ name = "x0";
+ return true;
+}
+
size_t
ABISysV_arm64::GetRedZoneSize () const
{
const lldb_private::RegisterInfo *
GetRegisterInfoArray (uint32_t &count) override;
+
+ bool
+ GetPointerReturnRegister (const char *&name) override;
//------------------------------------------------------------------
// Static Functions
return g_register_infos;
}
+bool
+ABISysV_x86_64::GetPointerReturnRegister (const char *&name)
+{
+ name = "rax";
+ return true;
+}
+
size_t
ABISysV_x86_64::GetRedZoneSize () const
{
const lldb_private::RegisterInfo *
GetRegisterInfoArray(uint32_t &count) override;
-
+
+ bool
+ GetPointerReturnRegister (const char *&name) override;
+
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
#include "lldb/Core/Address.h"
#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Stream.h"
#include "lldb/Symbol/SymbolContext.h"
m_disasm_wp (std::static_pointer_cast<DisassemblerLLVMC>(disasm.shared_from_this())),
m_does_branch (eLazyBoolCalculate),
m_has_delay_slot (eLazyBoolCalculate),
+ m_is_call (eLazyBoolCalculate),
m_is_valid (false),
m_using_file_addr (false)
{
{
return m_disasm_wp.lock();
}
+
+ static llvm::StringRef::const_iterator
+ ConsumeWhitespace(llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ while (osi != ose)
+ {
+ switch (*osi) {
+ default:
+ return osi;
+ case ' ': case '\t':
+ break;
+ }
+ ++osi;
+ }
+
+ return osi;
+ }
+
+ static std::pair<bool, llvm::StringRef::const_iterator>
+ ConsumeChar(llvm::StringRef::const_iterator osi, const char c, llvm::StringRef::const_iterator ose)
+ {
+ bool found = false;
+
+ osi = ConsumeWhitespace(osi, ose);
+ if (osi != ose && *osi == c)
+ {
+ found = true;
+ ++osi;
+ }
+
+ return std::make_pair(found, osi);
+ }
+
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseRegisterName(llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ Operand ret;
+ ret.m_type = Operand::Type::Register;
+ std::string str;
+
+ osi = ConsumeWhitespace(osi, ose);
+
+ while (osi != ose)
+ {
+ if (*osi >= '0' && *osi <= '9')
+ {
+ if (str.empty())
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ else
+ {
+ str.push_back(*osi);
+ }
+ }
+ else if (*osi >= 'a' && *osi <= 'z')
+ {
+ str.push_back(*osi);
+ }
+ else
+ {
+ switch (*osi)
+ {
+ default:
+ if (str.empty())
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ else
+ {
+ ret.m_register = ConstString(str);
+ return std::make_pair(ret, osi);
+ }
+ case '%':
+ if (!str.empty())
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ }
+ }
+ ++osi;
+ }
+
+ ret.m_register = ConstString(str);
+ return std::make_pair(ret, osi);
+ }
+
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseImmediate(llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ Operand ret;
+ ret.m_type = Operand::Type::Immediate;
+ std::string str;
+ bool is_hex = false;
+
+ osi = ConsumeWhitespace(osi, ose);
+
+ while (osi != ose)
+ {
+ if (*osi >= '0' && *osi <= '9')
+ {
+ str.push_back(*osi);
+ }
+ else if (*osi >= 'a' && *osi <= 'f')
+ {
+ if (is_hex)
+ {
+ str.push_back(*osi);
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ }
+ else
+ {
+ switch (*osi)
+ {
+ default:
+ if (str.empty())
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ else
+ {
+ ret.m_immediate = std::stoull(str, nullptr, 0);
+ return std::make_pair(ret, osi);
+ }
+ case 'x':
+ if (!str.compare("0"))
+ {
+ is_hex = true;
+ str.push_back(*osi);
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ case '#': case '$':
+ if (!str.empty())
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ case '-':
+ if (str.empty())
+ {
+ ret.m_negative = true;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+ }
+ }
+ ++osi;
+ }
+
+ ret.m_immediate = std::stoull(str, nullptr, 0);
+ return std::make_pair(ret, osi);
+ }
+
+ // -0x5(%rax,%rax,2)
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseIntelIndexedAccess (llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator = ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid())
+ {
+ osi = offset_and_iterator.second;
+ }
+
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '(', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid())
+ {
+ osi = base_and_iterator.second;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> index_and_iterator = ParseRegisterName(osi, ose);
+ if (index_and_iterator.first.IsValid())
+ {
+ osi = index_and_iterator.second;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> multiplier_and_iterator = ParseImmediate(osi, ose);
+ if (index_and_iterator.first.IsValid())
+ {
+ osi = index_and_iterator.second;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ')', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand product;
+ product.m_type = Operand::Type::Product;
+ product.m_children.push_back(index_and_iterator.first);
+ product.m_children.push_back(multiplier_and_iterator.first);
+
+ Operand index;
+ index.m_type = Operand::Type::Sum;
+ index.m_children.push_back(base_and_iterator.first);
+ index.m_children.push_back(product);
+
+ if (offset_and_iterator.first.IsValid())
+ {
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(index);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ }
+ else
+ {
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(index);
+ return std::make_pair(deref, osi);
+ }
+ }
+
+ // -0x10(%rbp)
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseIntelDerefAccess (llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator = ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid())
+ {
+ osi = offset_and_iterator.second;
+ }
+
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '(', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid())
+ {
+ osi = base_and_iterator.second;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ')', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ if (offset_and_iterator.first.IsValid())
+ {
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(base_and_iterator.first);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ }
+ else
+ {
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(base_and_iterator.first);
+ return std::make_pair(deref, osi);
+ }
+ }
+
+ // [sp, #8]!
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseARMOffsetAccess (llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '[', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid())
+ {
+ osi = base_and_iterator.second;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator = ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid())
+ {
+ osi = offset_and_iterator.second;
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ']', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(base_and_iterator.first);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ }
+
+ // [sp]
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseARMDerefAccess (llvm::StringRef::const_iterator osi, llvm::StringRef::const_iterator ose)
+ {
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '[', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid())
+ {
+ osi = base_and_iterator.second;
+ }
+ else
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ']', ose);
+ if (!found)
+ {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(base_and_iterator.first);
+ return std::make_pair(deref, osi);
+ }
+
+ static void
+ DumpOperand(const Operand &op, Stream &s)
+ {
+ switch (op.m_type)
+ {
+ case Operand::Type::Dereference:
+ s.PutCString("*");
+ DumpOperand(op.m_children[0], s);
+ break;
+ case Operand::Type::Immediate:
+ if (op.m_negative)
+ {
+ s.PutCString("-");
+ }
+ s.PutCString(std::to_string(op.m_immediate).c_str());
+ break;
+ case Operand::Type::Invalid:
+ s.PutCString("Invalid");
+ break;
+ case Operand::Type::Product:
+ s.PutCString("(");
+ DumpOperand(op.m_children[0], s);
+ s.PutCString("*");
+ DumpOperand(op.m_children[1], s);
+ s.PutCString(")");
+ break;
+ case Operand::Type::Register:
+ s.PutCString(op.m_register.AsCString());
+ break;
+ case Operand::Type::Sum:
+ s.PutCString("(");
+ DumpOperand(op.m_children[0], s);
+ s.PutCString("+");
+ DumpOperand(op.m_children[1], s);
+ s.PutCString(")");
+ break;
+ }
+ }
+
+ bool
+ ParseOperands (llvm::SmallVectorImpl<Instruction::Operand> &operands) override
+ {
+ const char *operands_string = GetOperands(nullptr);
+
+ if (!operands_string)
+ {
+ return false;
+ }
+
+ llvm::StringRef operands_ref(operands_string);
+
+ llvm::StringRef::const_iterator osi = operands_ref.begin();
+ llvm::StringRef::const_iterator ose = operands_ref.end();
+
+ while (osi != ose)
+ {
+ Operand operand;
+ llvm::StringRef::const_iterator iter;
+
+ if ((std::tie(operand, iter) = ParseIntelIndexedAccess(osi, ose), operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseIntelDerefAccess(osi, ose), operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseARMOffsetAccess(osi, ose), operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseARMDerefAccess(osi, ose), operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseRegisterName(osi, ose), operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseImmediate(osi, ose), operand.IsValid()))
+ {
+ osi = iter;
+ operands.push_back(operand);
+ }
+ else
+ {
+ return false;
+ }
+
+ std::pair<bool, llvm::StringRef::const_iterator> found_and_iter = ConsumeChar(osi, ',', ose);
+ if (found_and_iter.first)
+ {
+ osi = found_and_iter.second;
+ }
+
+ osi = ConsumeWhitespace(osi, ose);
+ }
+
+ DisassemblerSP disasm_sp = m_disasm_wp.lock();
+
+ if (disasm_sp && operands.size() > 1)
+ {
+ // TODO tie this into the MC Disassembler's notion of clobbers.
+ switch (disasm_sp->GetArchitecture().GetMachine())
+ {
+ default:
+ break;
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ operands[operands.size() - 1].m_clobbered = true;
+ break;
+ case llvm::Triple::arm:
+ operands[0].m_clobbered = true;
+ break;
+ }
+ }
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS))
+ {
+ StreamString ss;
+
+ ss.Printf("[%s] expands to %zu operands:\n", operands_string, operands.size());
+ for (const Operand &operand : operands) {
+ ss.PutCString(" ");
+ DumpOperand(operand, ss);
+ ss.PutCString("\n");
+ }
+
+ log->PutCString(ss.GetData());
+ }
+
+ return true;
+ }
+
+ bool
+ IsCall () override
+ {
+ if (m_is_call == eLazyBoolCalculate)
+ {
+ std::shared_ptr<DisassemblerLLVMC> disasm_sp(GetDisassembler());
+ if (disasm_sp)
+ {
+ disasm_sp->Lock(this, NULL);
+ DataExtractor data;
+ if (m_opcode.GetData(data))
+ {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data,
+ opcode_data_len,
+ pc,
+ inst);
+ if (inst_size == 0)
+ {
+ m_is_call = eLazyBoolNo;
+ }
+ else
+ {
+ if (mc_disasm_ptr->IsCall(inst))
+ m_is_call = eLazyBoolYes;
+ else
+ m_is_call = eLazyBoolNo;
+ }
+ }
+ disasm_sp->Unlock();
+ }
+ }
+ return m_is_call == eLazyBoolYes;
+ }
+
protected:
std::weak_ptr<DisassemblerLLVMC> m_disasm_wp;
LazyBool m_does_branch;
LazyBool m_has_delay_slot;
+ LazyBool m_is_call;
bool m_is_valid;
bool m_using_file_addr;
};
return m_instr_info_ap->get(mc_inst.getOpcode()).hasDelaySlot();
}
+bool
+DisassemblerLLVMC::LLVMCDisassembler::IsCall (llvm::MCInst &mc_inst)
+{
+ return m_instr_info_ap->get(mc_inst.getOpcode()).isCall();
+}
+
DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) :
Disassembler(arch, flavor_string),
m_exe_ctx (NULL),
void SetStyle (bool use_hex_immed, HexImmediateStyle hex_style);
bool CanBranch (llvm::MCInst &mc_inst);
bool HasDelaySlot (llvm::MCInst &mc_inst);
+ bool IsCall (llvm::MCInst &mc_inst);
bool IsValid()
{
return m_is_valid;
}
else
{
+ StopInfoSP curr_thread_stop_info_sp;
// Lock the thread list so it doesn't change on us, this is the scope for the locker:
{
ThreadList &thread_list = process_sp->GetThreadList();
ThreadSP thread;
StopReason curr_thread_stop_reason = eStopReasonInvalid;
if (curr_thread)
+ {
curr_thread_stop_reason = curr_thread->GetStopReason();
+ curr_thread_stop_info_sp = curr_thread->GetStopInfo();
+ }
if (!curr_thread ||
!curr_thread->IsValid() ||
curr_thread_stop_reason == eStopReasonInvalid ||
start_frame,
num_frames,
num_frames_with_source);
+ if (curr_thread_stop_info_sp)
+ {
+ lldb::addr_t crashing_address;
+ ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference(curr_thread_stop_info_sp, &crashing_address);
+ if (valobj_sp)
+ {
+ const bool qualify_cxx_base_classes = false;
+
+ const ValueObject::GetExpressionPathFormat format = ValueObject::GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
+ stream->PutCString("Likely cause: ");
+ valobj_sp->GetExpressionPath(*stream, qualify_cxx_base_classes, format);
+ stream->Printf(" accessed 0x%llx\n", crashing_address);
+ }
+ }
}
else
{
#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"
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 ()
{
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 || 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 ()
{
else
return ExpressionVariableSP();
}
+
+lldb::ValueObjectSP
+StopInfo::GetCrashingDereference (StopInfoSP &stop_info_sp, lldb::addr_t *crashing_address)
+{
+ if (!stop_info_sp)
+ {
+ return ValueObjectSP();
+ }
+
+ const char *description = stop_info_sp->GetDescription();
+ if (!description)
+ {
+ return ValueObjectSP();
+ }
+
+ ThreadSP thread_sp = stop_info_sp->GetThread();
+ if (!thread_sp)
+ {
+ return ValueObjectSP();
+ }
+
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+
+ if (!frame_sp)
+ {
+ return ValueObjectSP();
+ }
+
+ const char address_string[] = "address=";
+
+ const char *address_loc = strstr(description, address_string);
+ if (!address_loc)
+ {
+ return ValueObjectSP();
+ }
+
+ address_loc += (sizeof(address_string) - 1);
+
+ uint64_t address = std::stoull(address_loc, 0, 0);
+ if (crashing_address)
+ {
+ *crashing_address = address;
+ }
+
+ return frame_sp->GuessValueForAddress(address);
+}