Added the "frame diagnose" command and use its output to make crash info better.
authorSean Callanan <scallanan@apple.com>
Tue, 6 Sep 2016 04:48:36 +0000 (04:48 +0000)
committerSean Callanan <scallanan@apple.com>
Tue, 6 Sep 2016 04:48:36 +0000 (04:48 +0000)
When a process stops due to a crash, we get the crashing instruction and the
crashing memory location (if there is one).  From the user's perspective it is
often unclear what the reason for the crash is in a symbolic sense.

To address this, I have added new fuctionality to StackFrame to parse the
disassembly and reconstruct the sequence of dereferneces and offsets that were
applied to a known variable (or fuction retrn value) to obtain the invalid
pointer.

This makes use of enhancements in the disassembler, as well as new information
provided by the DWARF expression infrastructure, and is exposed through a
"frame diagnose" command.  It is also used to provide symbolic information, when
available, in the event of a crash.

The algorithm is very rudimentary, and it needs a bunch of work, including
  - better parsing for assembly, preferably with help from LLVM
  - support for non-Apple platforms
  - cleanup of the algorithm core, preferably to make it all work in terms of
    Operands instead of register/offset pairs
  - improvement of the GetExpressioPath() logic to make prettier expression
    paths, and
  - better handling of vtables.
I welcome all suggestios, improvements, and testcases.

llvm-svn: 280692

43 files changed:
lldb/include/lldb/Core/Disassembler.h
lldb/include/lldb/Expression/DWARFExpression.h
lldb/include/lldb/Target/ABI.h
lldb/include/lldb/Target/StackFrame.h
lldb/include/lldb/Target/StopInfo.h
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/TestArray.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/array/main.c [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/TestBadReference.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/bad-reference/main.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/TestComplicatedExpression.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/complicated-expression/main.c [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/TestDiagnoseDereferenceArgument.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-argument/main.c [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/TestDiagnoseDereferenceFunctionReturn.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-function-return/main.c [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/TestDiagnoseDereferenceThis.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/dereference-this/main.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/TestDiagnoseInheritance.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/inheritance/main.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/TestLocalVariable.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/local-variable/main.c [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/TestDiagnoseDereferenceVirtualMethodCall.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/functionalities/frame-diagnose/virtual-method-call/main.cpp [new file with mode: 0644]
lldb/source/Commands/CommandObjectFrame.cpp
lldb/source/Expression/DWARFExpression.cpp
lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp
lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h
lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
lldb/source/Target/Process.cpp
lldb/source/Target/StackFrame.cpp
lldb/source/Target/StopInfo.cpp

index bfa99de81ea4a42d306cb887ab7cff6ffee14789..7b11f480e97c628b3c7e1a4266dfee0cb803a219 100644 (file)
@@ -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
index d984a419c5c92bee5f419606f941ba2dc723eaf0..af3df8cab9266b8d3f5aeeb005e6301dacb55ba6 100644 (file)
@@ -435,7 +435,15 @@ public:
                            const DWARFCompileUnit* cu,
                            const DataExtractor& debug_loc_data,
                            lldb::offset_t offset);
-
+    
+    bool
+    IsRegister(StackFrame &frame,
+               const RegisterInfo *&register_info);
+    
+    bool
+    IsDereferenceOfRegister(StackFrame &frame,
+                            const RegisterInfo *&register_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
index cd0b57e61ff86bc1dc84f490e2d3b8479de798df..6d82af0cd934863c5e53b028fbdc0a89825ce2bf 100644 (file)
@@ -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);
index b3cc57f176ca44ac892d77dfd46c8ca53869fe22..907e88905f02a51cd0131a77b9739b01a2ed4a38 100644 (file)
@@ -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
index dfc9860b604aa2c4c0c1d76a7cee8941ba8e4a2c..7fc07117401215269884e6e3c7959f914d31ab4a 100644 (file)
@@ -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 (file)
index 0000000..b09a579
--- /dev/null
@@ -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 (file)
index 0000000..0f1c109
--- /dev/null
@@ -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 (file)
index 0000000..95c6515
--- /dev/null
@@ -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 (file)
index 0000000..314f1cb
--- /dev/null
@@ -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 (file)
index 0000000..e90e866
--- /dev/null
@@ -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 (file)
index 0000000..2f61152
--- /dev/null
@@ -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 (file)
index 0000000..b09a579
--- /dev/null
@@ -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 (file)
index 0000000..bd25a29
--- /dev/null
@@ -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 (file)
index 0000000..147aae9
--- /dev/null
@@ -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 (file)
index 0000000..b09a579
--- /dev/null
@@ -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 (file)
index 0000000..2887292
--- /dev/null
@@ -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 (file)
index 0000000..0ec23b1
--- /dev/null
@@ -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 (file)
index 0000000..314f1cb
--- /dev/null
@@ -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 (file)
index 0000000..597ced3
--- /dev/null
@@ -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 (file)
index 0000000..420e6f2
--- /dev/null
@@ -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 (file)
index 0000000..314f1cb
--- /dev/null
@@ -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 (file)
index 0000000..fe71e52
--- /dev/null
@@ -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 (file)
index 0000000..1f17723
--- /dev/null
@@ -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 (file)
index 0000000..314f1cb
--- /dev/null
@@ -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 (file)
index 0000000..1438747
--- /dev/null
@@ -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 (file)
index 0000000..78cac2c
--- /dev/null
@@ -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 (file)
index 0000000..b09a579
--- /dev/null
@@ -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 (file)
index 0000000..cdddd84
--- /dev/null
@@ -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 (file)
index 0000000..33307be
--- /dev/null
@@ -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 (file)
index 0000000..314f1cb
--- /dev/null
@@ -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 (file)
index 0000000..6f7e55b
--- /dev/null
@@ -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 (file)
index 0000000..2a03dc1
--- /dev/null
@@ -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();
+}
+
index 935fee80a6af421fc3cb787f9074126413c85a91..d508bf486347ce0648b78be771dabd8ed33f2763 100644 (file)
 #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)));
index b81d60e9b065bcca55073fa696fb71fa10e0cc34..23a922c491b2c5a0958b0d3eeb10f2d48c9d5938 100644 (file)
@@ -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 *&register_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 *&register_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;
+        }
+    }
+}
+
index ac5151afdee5b3f406f20075950063ba4da88df7..32dc28e2c7dc84a819f1b0b9553aff4f6e89da4a 100644 (file)
@@ -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
 {
index e36f87e744f409640fd823980e16c3fb65bc51cf..548d42a878021e3758a6aeeda046c472688d59e8 100644 (file)
@@ -81,6 +81,9 @@ public:
 
     const lldb_private::RegisterInfo *
     GetRegisterInfoArray (uint32_t &count) override;
+    
+    bool
+    GetPointerReturnRegister (const char *&name) override;
 
     //------------------------------------------------------------------
     // Static Functions
index cc568d6f63594104220659083de623ad8691252a..89b39a1a7527a5de31db266a5bfc5733073806d9 100644 (file)
@@ -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
 {
index 07b57abaf57cdbeacf81aa78195537ae550859c1..2adf22d109ce5322f4d21807a6a116cda4e3804d 100644 (file)
@@ -84,7 +84,10 @@ public:
 
     const lldb_private::RegisterInfo *
     GetRegisterInfoArray(uint32_t &count) override;
-
+    
+    bool
+    GetPointerReturnRegister (const char *&name) override;
+    
     //------------------------------------------------------------------
     // Static Functions
     //------------------------------------------------------------------
index 4d24cf1ab6de37a9210dafa01f677c6a50c9aa75..61a57ef3fcdd872bd0ea90718e254fec3073f3a4 100644 (file)
@@ -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),
index e8f09a4d3abb2ef7aeeaa9276599656c50487186..f2f54d3939906781d7104725fcfafd47651dd5bb 100644 (file)
@@ -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;
index f5f8660a1225a120c78a14faec3129f4390bdf44..b40502995b652aece115738807eb5d5364d3a37a 100644 (file)
@@ -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
                     {
index 7110051d28e489970f44a1d76bc8f0b7e5081524..aaf0a4d7ac7db5181afd13f158b17628ec4606fb 100644 (file)
 #include "lldb/Core/Value.h"
 #include "lldb/Core/ValueObjectVariable.h"
 #include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
 #include "lldb/Symbol/CompileUnit.h"
 #include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/Symbol.h"
 #include "lldb/Symbol/SymbolContextScope.h"
 #include "lldb/Symbol/Type.h"
 #include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/RegisterContext.h"
@@ -1236,6 +1238,21 @@ StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr)
     return m_frame_base_error.Success();
 }
 
+DWARFExpression *
+StackFrame::GetFrameBaseExpression(Error *error_ptr)
+{
+    if (!m_sc.function)
+    {
+        if (error_ptr)
+        {
+            error_ptr->SetErrorString ("No function in symbol context.");
+        }
+        return nullptr;
+    }
+
+    return &m_sc.function->GetFrameBaseExpression();
+}
+
 RegisterContextSP
 StackFrame::GetRegisterContext ()
 {
@@ -1354,6 +1371,572 @@ StackFrame::GuessLanguage ()
     return lang_type;
 }
 
+namespace
+{
+    std::pair<const Instruction::Operand *, int64_t>
+    GetBaseExplainingValue(const Instruction::Operand &operand,
+                           RegisterContext &register_context,
+                           lldb::addr_t value)
+    {
+        switch(operand.m_type)
+        {
+        case Instruction::Operand::Type::Dereference:
+        case Instruction::Operand::Type::Immediate:
+        case Instruction::Operand::Type::Invalid:
+        case Instruction::Operand::Type::Product:
+            // These are not currently interesting
+            return std::make_pair(nullptr, 0);
+        case Instruction::Operand::Type::Sum:
+            {
+                const Instruction::Operand *immediate_child = nullptr;
+                const Instruction::Operand *variable_child = nullptr;
+                if (operand.m_children[0].m_type == Instruction::Operand::Type::Immediate)
+                {
+                    immediate_child = &operand.m_children[0];
+                    variable_child = &operand.m_children[1];
+                }
+                else if (operand.m_children[1].m_type == Instruction::Operand::Type::Immediate)
+                {
+                    immediate_child = &operand.m_children[1];
+                    variable_child = &operand.m_children[0];
+                }
+                if (!immediate_child)
+                {
+                    return std::make_pair(nullptr, 0);
+                }
+                lldb::addr_t adjusted_value = value;
+                if (immediate_child->m_negative)
+                {
+                    adjusted_value += immediate_child->m_immediate;
+                }
+                else
+                {
+                    adjusted_value -= immediate_child->m_immediate;
+                }
+                std::pair<const Instruction::Operand *, int64_t> base_and_offset = GetBaseExplainingValue(*variable_child, register_context, adjusted_value);
+                if (!base_and_offset.first)
+                {
+                    return std::make_pair(nullptr, 0);
+                }
+                if (immediate_child->m_negative)
+                {
+                    base_and_offset.second -= immediate_child->m_immediate;
+                }
+                else
+                {
+                    base_and_offset.second += immediate_child->m_immediate;
+                }
+                return base_and_offset;
+            }
+        case Instruction::Operand::Type::Register:
+            {
+                const RegisterInfo *info = register_context.GetRegisterInfoByName(operand.m_register.AsCString());
+                if (!info)
+                {
+                    return std::make_pair(nullptr, 0);
+                }
+                RegisterValue reg_value;
+                if (!register_context.ReadRegister(info, reg_value))
+                {
+                    return std::make_pair(nullptr, 0);
+                }
+                if (reg_value.GetAsUInt64() == value)
+                {
+                    return std::make_pair(&operand, 0);
+                }
+                else
+                {
+                    return std::make_pair(nullptr, 0);
+                }
+            }
+        }
+    }
+    
+    std::pair<const Instruction::Operand *, int64_t>
+    GetBaseExplainingDereference(const Instruction::Operand &operand,
+                                 RegisterContext &register_context,
+                                 lldb::addr_t addr)
+    {
+        if (operand.m_type == Instruction::Operand::Type::Dereference)
+        {
+            return GetBaseExplainingValue(operand.m_children[0],
+                                          register_context,
+                                          addr);
+        }
+        return std::make_pair(nullptr, 0);
+    }
+};
+
+lldb::ValueObjectSP
+StackFrame::GuessValueForAddress(lldb::addr_t addr)
+{
+    TargetSP target_sp = CalculateTarget();
+    
+    const ArchSpec &target_arch = target_sp->GetArchitecture();
+    
+    AddressRange pc_range;
+    pc_range.GetBaseAddress() = GetFrameCodeAddress();
+    pc_range.SetByteSize(target_arch.GetMaximumOpcodeByteSize());
+    
+    ExecutionContext exe_ctx (shared_from_this());
+    
+    const char *plugin_name = nullptr;
+    const char *flavor = nullptr;
+    const bool prefer_file_cache = false;
+    
+    DisassemblerSP disassembler_sp = Disassembler::DisassembleRange (target_arch,
+                                                                     plugin_name,
+                                                                     flavor,
+                                                                     exe_ctx,
+                                                                     pc_range,
+                                                                     prefer_file_cache);
+    
+    if (!disassembler_sp->GetInstructionList().GetSize())
+    {
+        return ValueObjectSP();
+    }
+    
+    InstructionSP instruction_sp = disassembler_sp->GetInstructionList().GetInstructionAtIndex(0);
+    
+    llvm::SmallVector<Instruction::Operand, 3> operands;
+    
+    if (!instruction_sp->ParseOperands(operands))
+    {
+        return ValueObjectSP();
+    }
+    
+    RegisterContextSP register_context_sp = GetRegisterContext();
+    
+    if (!register_context_sp)
+    {
+        return ValueObjectSP();
+    }
+    
+    for (const Instruction::Operand &operand : operands)
+    {
+        std::pair<const Instruction::Operand *, int64_t>
+            base_and_offset = GetBaseExplainingDereference(operand, *register_context_sp, addr);
+        
+        if (!base_and_offset.first)
+        {
+            continue;
+        }
+        
+        switch (base_and_offset.first->m_type)
+        {
+        case Instruction::Operand::Type::Immediate:
+            {
+                lldb_private::Address addr;
+                if (target_sp->ResolveLoadAddress(base_and_offset.first->m_immediate + base_and_offset.second, addr))
+                {
+                    TypeSystem *c_type_system = target_sp->GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC);
+                    if (!c_type_system)
+                    {
+                        return ValueObjectSP();
+                    }
+                    else
+                    {
+                        CompilerType void_ptr_type = c_type_system->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar).GetPointerType();
+                        return ValueObjectMemory::Create(this, "", addr, void_ptr_type);
+                    }
+                }
+                else
+                {
+                    return ValueObjectSP();
+                }
+                break;
+            }
+        case Instruction::Operand::Type::Register:
+            {
+                return GuessValueForRegisterAndOffset(base_and_offset.first->m_register, base_and_offset.second);
+            }
+        default:
+            return ValueObjectSP();
+        }
+        
+    }
+
+    return ValueObjectSP();
+}
+
+namespace
+{
+    ValueObjectSP
+    GetValueForOffset(StackFrame &frame, ValueObjectSP &parent, int64_t offset)
+    {
+        if (offset < 0 || offset >= parent->GetByteSize())
+        {
+            return ValueObjectSP();
+        }
+        
+        if (parent->IsPointerOrReferenceType())
+        {
+            return parent;
+        }
+        
+        for (int ci = 0, ce = parent->GetNumChildren(); ci != ce; ++ci)
+        {
+            const bool can_create = true;
+            ValueObjectSP child_sp = parent->GetChildAtIndex(ci, can_create);
+            
+            if (!child_sp)
+            {
+                return ValueObjectSP();
+            }
+            
+            int64_t child_offset = child_sp->GetByteOffset();
+            int64_t child_size = child_sp->GetByteSize();
+            
+            if (offset >= child_offset &&
+                offset < (child_offset + child_size))
+            {
+                return GetValueForOffset(frame, child_sp, offset - child_offset);
+            }
+        }
+        
+        if (offset == 0)
+        {
+            return parent;
+        }
+        else
+        {
+            return ValueObjectSP();
+        }
+    }
+    
+    ValueObjectSP
+    GetValueForDereferincingOffset(StackFrame &frame, ValueObjectSP &base, int64_t offset)
+    {
+        // base is a pointer to something
+        // offset is the thing to add to the pointer
+        // We return the most sensible ValueObject for the result of *(base+offset)
+
+        if (!base->IsPointerOrReferenceType())
+        {
+            return ValueObjectSP();
+        }
+    
+        Error error;
+        ValueObjectSP pointee = base->Dereference(error);
+        
+        if (offset >= pointee->GetByteSize())
+        {
+            int64_t index = offset / pointee->GetByteSize();
+            offset = offset % pointee->GetByteSize();
+            const bool can_create = true;
+            pointee = base->GetSyntheticArrayMember(index, can_create);
+        }
+        
+        if (!pointee || error.Fail())
+        {
+            return ValueObjectSP();
+        }
+        
+        return GetValueForOffset(frame, pointee, offset);
+    }
+    
+    //------------------------------------------------------------------
+    /// Attempt to reconstruct the ValueObject for the address contained in a
+    /// given register plus an offset.
+    ///
+    /// @params [in] frame
+    ///   The current stack frame.
+    ///
+    /// @params [in] reg
+    ///   The register.
+    ///
+    /// @params [in] offset
+    ///   The offset from the register.
+    ///
+    /// @param [in] disassembler
+    ///   A disassembler containing instructions valid up to the current PC.
+    ///
+    /// @param [in] variables
+    ///   The variable list from the current frame,
+    ///
+    /// @param [in] pc
+    ///   The program counter for the instruction considered the 'user'.
+    ///
+    /// @return
+    ///   A string describing the base for the ExpressionPath.  This could be a
+    ///     variable, a register value, an argument, or a function return value.
+    ///   The ValueObject if found.  If valid, it has a valid ExpressionPath.
+    //------------------------------------------------------------------
+    lldb::ValueObjectSP
+    DoGuessValueAt(StackFrame &frame, ConstString reg, int64_t offset, Disassembler &disassembler, VariableList &variables, const Address &pc)
+    {
+        // Example of operation for Intel:
+        //
+        // +14: movq   -0x8(%rbp), %rdi
+        // +18: movq   0x8(%rdi), %rdi
+        // +22: addl   0x4(%rdi), %eax
+        //
+        // f, a pointer to a struct, is known to be at -0x8(%rbp).
+        //
+        // DoGuessValueAt(frame, rdi, 4, dis, vars, 0x22) finds the instruction at +18 that assigns to rdi, and calls itself recursively for that dereference
+        //   DoGuessValueAt(frame, rdi, 8, dis, vars, 0x18) finds the instruction at +14 that assigns to rdi, and calls itself recursively for that derefernece
+        //     DoGuessValueAt(frame, rbp, -8, dis, vars, 0x14) finds "f" in the variable list.
+        //     Returns a ValueObject for f.  (That's what was stored at rbp-8 at +14)
+        //   Returns a ValueObject for *(f+8) or f->b (That's what was stored at rdi+8 at +18)
+        // Returns a ValueObject for *(f->b+4) or f->b->a (That's what was stored at rdi+4 at +22)
+        
+        // First, check the variable list to see if anything is at the specified location.
+        for (size_t vi = 0, ve = variables.GetSize(); vi != ve; ++vi)
+        {
+            VariableSP var_sp = variables.GetVariableAtIndex(vi);
+            DWARFExpression &dwarf_expression = var_sp->LocationExpression();
+            
+            const RegisterInfo *expression_reg;
+            int64_t expression_offset;
+            ExecutionContext exe_ctx;
+            
+            if (dwarf_expression.IsDereferenceOfRegister(frame, expression_reg, expression_offset))
+            {
+                if ((reg == ConstString(expression_reg->name) ||
+                     reg == ConstString(expression_reg->alt_name)) &&
+                    expression_offset == offset)
+                {
+                    return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues);
+                }
+            }
+        }
+        
+        bool is_in_return_register = false;
+        ABISP abi_sp = frame.CalculateProcess()->GetABI();
+        RegisterInfo return_register_info;
+
+        if (abi_sp)
+        {
+            const char *return_register_name;
+            const RegisterInfo *reg_info = nullptr;
+            if (abi_sp->GetPointerReturnRegister(return_register_name) &&
+                reg == ConstString(return_register_name) &&
+                (reg_info = frame.GetRegisterContext()->GetRegisterInfoByName(return_register_name)))
+            {
+                is_in_return_register = true;
+                return_register_info = *reg_info;
+            }
+        }
+        
+        const uint32_t current_inst = disassembler.GetInstructionList().GetIndexOfInstructionAtAddress(pc);
+        if (current_inst == UINT32_MAX)
+        {
+            return ValueObjectSP();
+        }
+        
+        ValueObjectSP source_path;
+        
+        for (uint32_t ii = current_inst - 1; ii != (uint32_t)-1; --ii)
+        {
+            // This is not an exact algorithm, and it sacrifices accuracy for generality.
+            // Recognizing "mov" and "ld" instructions –– and which are their source and
+            // destination operands -- is something the disassembler should do for us.
+            InstructionSP instruction_sp = disassembler.GetInstructionList().GetInstructionAtIndex(ii);
+            
+            if (is_in_return_register && instruction_sp->IsCall())
+            {
+                llvm::SmallVector<Instruction::Operand, 1> operands;
+                if (!instruction_sp->ParseOperands(operands) || operands.size() != 1)
+                {
+                    continue;
+                }
+                
+                switch (operands[0].m_type)
+                {
+                default:
+                    break;
+                case Instruction::Operand::Type::Immediate:
+                    {
+                        SymbolContext sc;
+                        Address load_address;
+                        if (!frame.CalculateTarget()->ResolveLoadAddress(operands[0].m_immediate, load_address))
+                        {
+                            break;
+                        }
+                        frame.CalculateTarget()->GetImages().ResolveSymbolContextForAddress(load_address, eSymbolContextFunction, sc);
+                        if (!sc.function)
+                        {
+                            break;
+                        }
+                        CompilerType function_type = sc.function->GetCompilerType();
+                        if (!function_type.IsFunctionType())
+                        {
+                            break;
+                        }
+                        CompilerType return_type = function_type.GetFunctionReturnType();
+                        RegisterValue return_value;
+                        if (!frame.GetRegisterContext()->ReadRegister(&return_register_info, return_value))
+                        {
+                            break;
+                        }
+                        std::string name_str(sc.function->GetName().AsCString("<unknown function>"));
+                        name_str.append("()");
+                        Address return_value_address(return_value.GetAsUInt64());
+                        ValueObjectSP return_value_sp = ValueObjectMemory::Create(&frame, name_str.c_str(), return_value_address, return_type);
+                        return GetValueForDereferincingOffset(frame, return_value_sp, offset);
+                    }
+                }
+
+                continue;
+            }
+            
+            llvm::SmallVector<Instruction::Operand, 2> operands;
+            if (!instruction_sp->ParseOperands(operands) || operands.size() != 2)
+            {
+                continue;
+            }
+            
+            Instruction::Operand *register_operand = nullptr;
+            Instruction::Operand *origin_operand = nullptr;
+            if (operands[0].m_type == Instruction::Operand::Type::Register &&
+                operands[0].m_clobbered == true &&
+                operands[0].m_register == reg)
+            {
+                register_operand = &operands[0];
+                origin_operand = &operands[1];
+            }
+            else if (operands[1].m_type == Instruction::Operand::Type::Register &&
+                     operands[1].m_clobbered == true &&
+                     operands[1].m_register == reg)
+            {
+                register_operand = &operands[1];
+                origin_operand = &operands[0];
+            }
+            else
+            {
+                continue;
+            }
+            
+            // We have an origin operand.  Can we track its value down?
+            switch (origin_operand->m_type)
+            {
+            default:
+                break;
+            case Instruction::Operand::Type::Register:
+                source_path = DoGuessValueAt(frame, origin_operand->m_register, 0, disassembler, variables, instruction_sp->GetAddress());
+                break;
+            case Instruction::Operand::Type::Dereference:
+                {
+                    const Instruction::Operand &pointer = origin_operand->m_children[0];
+                    switch (pointer.m_type)
+                    {
+                    default:
+                        break;
+                    case Instruction::Operand::Type::Register:
+                        source_path = DoGuessValueAt(frame, pointer.m_register, 0, disassembler, variables, instruction_sp->GetAddress());
+                        if (source_path)
+                        {
+                            Error err;
+                            source_path = source_path->Dereference(err);
+                            if (!err.Success())
+                            {
+                                source_path.reset();
+                            }
+                        }
+                        break;
+                    case Instruction::Operand::Type::Sum:
+                        {
+                            const Instruction::Operand *origin_register = nullptr;
+                            const Instruction::Operand *origin_offset = nullptr;
+                            if (pointer.m_children.size() != 2)
+                            {
+                                break;
+                            }
+                            if (pointer.m_children[0].m_type == Instruction::Operand::Type::Register &&
+                                pointer.m_children[1].m_type == Instruction::Operand::Type::Immediate)
+                            {
+                                origin_register = &pointer.m_children[0];
+                                origin_offset = &pointer.m_children[1];
+                            }
+                            else if (pointer.m_children[1].m_type == Instruction::Operand::Type::Register &&
+                                     pointer.m_children[0].m_type == Instruction::Operand::Type::Immediate)
+                            {
+                                origin_register = &pointer.m_children[1];
+                                origin_offset = &pointer.m_children[0];
+                            }
+                            if (!origin_register)
+                            {
+                                break;
+                            }
+                            int64_t signed_origin_offset = origin_offset->m_negative ? -((int64_t)origin_offset->m_immediate) : origin_offset->m_immediate;
+                            source_path = DoGuessValueAt(frame, origin_register->m_register, signed_origin_offset, disassembler, variables, instruction_sp->GetAddress());
+                            if (!source_path)
+                            {
+                                break;
+                            }
+                            source_path = GetValueForDereferincingOffset(frame, source_path, offset);
+                            break;
+                        }
+                    }
+                }
+            }
+            
+            if (source_path)
+            {
+                return source_path;
+            }
+        }
+        
+        return ValueObjectSP();
+    }
+}
+
+lldb::ValueObjectSP
+StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset)
+{
+    TargetSP target_sp = CalculateTarget();
+    
+    const ArchSpec &target_arch = target_sp->GetArchitecture();
+
+    Block *frame_block = GetFrameBlock();
+    
+    if (!frame_block)
+    {
+        return ValueObjectSP();
+    }
+
+    Function *function = frame_block->CalculateSymbolContextFunction();
+    if (!function)
+    {
+        return ValueObjectSP();
+    }
+    
+    AddressRange pc_range = function->GetAddressRange();
+
+    if (GetFrameCodeAddress().GetFileAddress() < pc_range.GetBaseAddress().GetFileAddress() ||
+        GetFrameCodeAddress().GetFileAddress() - pc_range.GetBaseAddress().GetFileAddress() >= pc_range.GetByteSize())
+    {
+        return ValueObjectSP();
+    }
+    
+    ExecutionContext exe_ctx (shared_from_this());
+    
+    const char *plugin_name = nullptr;
+    const char *flavor = nullptr;
+    const bool prefer_file_cache = false;
+    DisassemblerSP disassembler_sp = Disassembler::DisassembleRange (target_arch,
+                                                                     plugin_name,
+                                                                     flavor,
+                                                                     exe_ctx,
+                                                                     pc_range,
+                                                                     prefer_file_cache);
+    
+    if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize())
+    {
+        return ValueObjectSP();
+    }
+    
+    const bool get_file_globals = false;
+    VariableList *variables = GetVariableList(get_file_globals);
+    
+    if (!variables)
+    {
+        return ValueObjectSP();
+    }
+    
+    return DoGuessValueAt(*this, reg, offset, *disassembler_sp, *variables, GetFrameCodeAddress());
+}
+
 TargetSP
 StackFrame::CalculateTarget ()
 {
index 7c244363ffd26c1e7401620550f33b7a32f328d9..085c0f181c0e0eec7fc58e6b6459532bb41b998a 100644 (file)
@@ -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);
+}