diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index bfa99de81ea4..7b11f480e97c 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -182,6 +182,37 @@ public: 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 diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h index d984a419c5c9..af3df8cab926 100644 --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -435,7 +435,15 @@ public: 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 @@ -475,6 +483,11 @@ protected: 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 diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h index cd0b57e61ff8..6d82af0cd934 100644 --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -147,6 +147,12 @@ public: 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); diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index b3cc57f176ca..907e88905f02 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -218,6 +218,21 @@ public: //------------------------------------------------------------------ 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. @@ -484,6 +499,37 @@ public: lldb::LanguageType 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 //------------------------------------------------------------------ @@ -501,7 +547,7 @@ public: void CalculateExecutionContext(ExecutionContext &exe_ctx) override; - + protected: friend class StackFrameList; @@ -516,7 +562,7 @@ protected: bool HasCachedData () const; - + private: //------------------------------------------------------------------ // For StackFrame only diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h index dfc9860b604a..7fc071174012 100644 --- a/lldb/include/lldb/Target/StopInfo.h +++ b/lldb/include/lldb/Target/StopInfo.h @@ -185,6 +185,9 @@ public: 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 diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/TestArray.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/TestArray.py new file mode 100644 index 000000000000..0f1c109676cd --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/TestArray.py @@ -0,0 +1,25 @@ +""" +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]"]) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/main.c b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/main.c new file mode 100644 index 000000000000..95c6515e5f54 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/main.c @@ -0,0 +1,9 @@ +struct Foo { + int b; + int c; +}; + +int main() { + struct Foo *a = 0; + return a[10].c; +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/TestBadReference.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/TestBadReference.py new file mode 100644 index 000000000000..e90e8668f7f9 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/TestBadReference.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/main.cpp new file mode 100644 index 000000000000..2f61152e3985 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/main.cpp @@ -0,0 +1,22 @@ +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()); +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/TestComplicatedExpression.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/TestComplicatedExpression.py new file mode 100644 index 000000000000..bd25a29fab01 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/TestComplicatedExpression.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/main.c b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/main.c new file mode 100644 index 000000000000..147aae946140 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/main.c @@ -0,0 +1,26 @@ +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()); +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/TestDiagnoseDereferenceArgument.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/TestDiagnoseDereferenceArgument.py new file mode 100644 index 000000000000..28872929b916 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/TestDiagnoseDereferenceArgument.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/main.c b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/main.c new file mode 100644 index 000000000000..0ec23b13be16 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/main.c @@ -0,0 +1,22 @@ +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()); +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/TestDiagnoseDereferenceFunctionReturn.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/TestDiagnoseDereferenceFunctionReturn.py new file mode 100644 index 000000000000..597ced3c0d9b --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/TestDiagnoseDereferenceFunctionReturn.py @@ -0,0 +1,26 @@ +""" +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"]) + diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/main.c b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/main.c new file mode 100644 index 000000000000..420e6f21de6b --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/main.c @@ -0,0 +1,12 @@ +struct Foo { + int a; + int b; +}; + +struct Foo *GetAFoo() { + return 0; +} + +int main() { + return GetAFoo()->b; +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/TestDiagnoseDereferenceThis.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/TestDiagnoseDereferenceThis.py new file mode 100644 index 000000000000..fe71e528d8f8 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/TestDiagnoseDereferenceThis.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/main.cpp new file mode 100644 index 000000000000..1f177230ed90 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/main.cpp @@ -0,0 +1,15 @@ +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(); +} + diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/TestDiagnoseInheritance.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/TestDiagnoseInheritance.py new file mode 100644 index 000000000000..1438747c1a1c --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/TestDiagnoseInheritance.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/main.cpp new file mode 100644 index 000000000000..78cac2c89653 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/main.cpp @@ -0,0 +1,69 @@ +#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(); +} + diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/Makefile new file mode 100644 index 000000000000..b09a579159d4 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/TestLocalVariable.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/TestLocalVariable.py new file mode 100644 index 000000000000..cdddd8483a6b --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/TestLocalVariable.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/main.c b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/main.c new file mode 100644 index 000000000000..33307beb0703 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/main.c @@ -0,0 +1,4 @@ +int main() { + int *myInt = 0; + return *myInt; +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/TestDiagnoseDereferenceVirtualMethodCall.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/TestDiagnoseDereferenceVirtualMethodCall.py new file mode 100644 index 000000000000..6f7e55b46296 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/TestDiagnoseDereferenceVirtualMethodCall.py @@ -0,0 +1,25 @@ +""" +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") diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/main.cpp new file mode 100644 index 000000000000..2a03dc11bf29 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/main.cpp @@ -0,0 +1,16 @@ +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(); +} + diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 935fee80a6af..d508bf486347 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -43,12 +43,214 @@ #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 //------------------------------------------------------------------------- @@ -612,6 +814,7 @@ CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(CommandInterpreter &int "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))); diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index b81d60e9b065..23a922c491b2 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -30,6 +30,8 @@ #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" @@ -3336,3 +3338,149 @@ DWARFExpression::PrintDWARFLocationList(Stream &s, 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; + } + } +} + diff --git a/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp index ac5151afdee5..32dc28e2c7dc 100644 --- a/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp +++ b/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp @@ -204,6 +204,13 @@ ABISysV_arm64::GetRegisterInfoArray (uint32_t &count) return g_register_infos; } +bool +ABISysV_arm64::GetPointerReturnRegister (const char *&name) +{ + name = "x0"; + return true; +} + size_t ABISysV_arm64::GetRedZoneSize () const { diff --git a/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h b/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h index e36f87e744f4..548d42a87802 100644 --- a/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h +++ b/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h @@ -81,6 +81,9 @@ public: const lldb_private::RegisterInfo * GetRegisterInfoArray (uint32_t &count) override; + + bool + GetPointerReturnRegister (const char *&name) override; //------------------------------------------------------------------ // Static Functions diff --git a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp index cc568d6f6359..89b39a1a7527 100644 --- a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp +++ b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -211,6 +211,13 @@ ABISysV_x86_64::GetRegisterInfoArray (uint32_t &count) return g_register_infos; } +bool +ABISysV_x86_64::GetPointerReturnRegister (const char *&name) +{ + name = "rax"; + return true; +} + size_t ABISysV_x86_64::GetRedZoneSize () const { diff --git a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h index 07b57abaf57c..2adf22d109ce 100644 --- a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h +++ b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -84,7 +84,10 @@ public: const lldb_private::RegisterInfo * GetRegisterInfoArray(uint32_t &count) override; - + + bool + GetPointerReturnRegister (const char *&name) override; + //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp index 4d24cf1ab6de..61a57ef3fcdd 100644 --- a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -31,6 +31,7 @@ #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" @@ -56,6 +57,7 @@ public: 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) { @@ -467,11 +469,569 @@ public: { 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; }; @@ -611,6 +1171,12 @@ DisassemblerLLVMC::LLVMCDisassembler::HasDelaySlot (llvm::MCInst &mc_inst) 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), diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h index e8f09a4d3abb..f2f54d393990 100644 --- a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h @@ -55,6 +55,7 @@ class DisassemblerLLVMC : public lldb_private::Disassembler 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; diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index f5f8660a1225..b40502995b65 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1150,6 +1150,7 @@ Process::HandleProcessStateChangedEvent (const EventSP &event_sp, } 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(); @@ -1159,7 +1160,10 @@ Process::HandleProcessStateChangedEvent (const EventSP &event_sp, 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 || @@ -1244,6 +1248,20 @@ Process::HandleProcessStateChangedEvent (const EventSP &event_sp, 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 { diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 7110051d28e4..aaf0a4d7ac7d 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 ®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 () { diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 7c244363ffd2..085c0f181c0e 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -1219,3 +1219,49 @@ StopInfo::GetExpressionVariable(StopInfoSP &stop_info_sp) 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); +}