[Lldb-commits] [lldb] 7fbd427 - Add the ability to get a C++ vtable ValueObject from another ValueObj… (#67599)

via lldb-commits lldb-commits at lists.llvm.org
Mon Oct 30 17:46:23 PDT 2023


Author: Greg Clayton
Date: 2023-10-30T17:46:18-07:00
New Revision: 7fbd427f5ebea4a4ebf25747758851875bb7e173

URL: https://github.com/llvm/llvm-project/commit/7fbd427f5ebea4a4ebf25747758851875bb7e173
DIFF: https://github.com/llvm/llvm-project/commit/7fbd427f5ebea4a4ebf25747758851875bb7e173.diff

LOG: Add the ability to get a C++ vtable ValueObject from another ValueObj… (#67599)

Add the ability to get a C++ vtable ValueObject from another
ValueObject.

This patch adds the ability to ask a ValueObject for a ValueObject that
represents the virtual function table for a C++ class. If the
ValueObject is not a C++ class with a vtable, a valid ValueObject value
will be returned that contains an appropriate error. If it is successful
a valid ValueObject that represents vtable will be returned. The
ValueObject that is returned will have a name that matches the demangled
value for a C++ vtable mangled name like "vtable for <class-name>". It
will have N children, one for each virtual function pointer. Each
child's value is the function pointer itself, the summary is the
symbolication of this function pointer, and the type will be a valid
function pointer from the debug info if there is debug information
corresponding to the virtual function pointer.

The vtable SBValue will have the following:
- SBValue::GetName() returns "vtable for <class>"
- SBValue::GetValue() returns a string representation of the vtable
address
- SBValue::GetSummary() returns NULL
- SBValue::GetType() returns a type appropriate for a uintptr_t type for
the current process
- SBValue::GetLoadAddress() returns the address of the vtable adderess
- SBValue::GetValueAsUnsigned(...) returns the vtable address
- SBValue::GetNumChildren() returns the number of virtual function
pointers in the vtable
- SBValue::GetChildAtIndex(...) returns a SBValue that represents a
virtual function pointer

The child SBValue objects that represent a virtual function pointer has
the following values:
- SBValue::GetName() returns "[%u]" where %u is the vtable function
pointer index
- SBValue::GetValue() returns a string representation of the virtual
function pointer
- SBValue::GetSummary() returns a symbolicated respresentation of the
virtual function pointer
- SBValue::GetType() returns the function prototype type if there is
debug info, or a generic funtion prototype if there is no debug info
- SBValue::GetLoadAddress() returns the address of the virtual function
pointer
- SBValue::GetValueAsUnsigned(...) returns the virtual function pointer
- SBValue::GetNumChildren() returns 0
- SBValue::GetChildAtIndex(...) returns invalid SBValue for any index

Examples of using this API via python:

```
(lldb) script vtable = lldb.frame.FindVariable("shape_ptr").GetVTable()
(lldb) script vtable
vtable for Shape = 0x0000000100004088 {
  [0] = 0x0000000100003d20 a.out`Shape::~Shape() at main.cpp:3
  [1] = 0x0000000100003e4c a.out`Shape::~Shape() at main.cpp:3
  [2] = 0x0000000100003e7c a.out`Shape::area() at main.cpp:4
  [3] = 0x0000000100003e3c a.out`Shape::optional() at main.cpp:7
}
(lldb) script c = vtable.GetChildAtIndex(0)
(lldb) script c
(void ()) [0] = 0x0000000100003d20 a.out`Shape::~Shape() at main.cpp:3
```

Added: 
    lldb/include/lldb/Core/ValueObjectVTable.h
    lldb/source/Core/ValueObjectVTable.cpp
    lldb/test/API/functionalities/vtable/Makefile
    lldb/test/API/functionalities/vtable/TestVTableValue.py
    lldb/test/API/functionalities/vtable/main.cpp

Modified: 
    lldb/bindings/interface/SBTypeDocstrings.i
    lldb/include/lldb/API/SBValue.h
    lldb/include/lldb/Core/ValueObject.h
    lldb/include/lldb/Core/ValueObjectChild.h
    lldb/include/lldb/Symbol/Type.h
    lldb/include/lldb/Symbol/TypeSystem.h
    lldb/include/lldb/Target/LanguageRuntime.h
    lldb/include/lldb/lldb-enumerations.h
    lldb/source/API/SBValue.cpp
    lldb/source/Commands/CommandObjectFrame.cpp
    lldb/source/Core/CMakeLists.txt
    lldb/source/Core/ValueObject.cpp
    lldb/source/DataFormatters/CXXFunctionPointer.cpp
    lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
    lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
    lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
    lldb/source/Symbol/Type.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBTypeDocstrings.i b/lldb/bindings/interface/SBTypeDocstrings.i
index c49e9647ba0463c..b056354922094f7 100644
--- a/lldb/bindings/interface/SBTypeDocstrings.i
+++ b/lldb/bindings/interface/SBTypeDocstrings.i
@@ -139,19 +139,6 @@ SBType supports the eq/ne operator. For example,::
     "
 ) lldb::SBType::IsReferenceType;
 
-%feature("docstring",
-    "Returns true if this type is a function type.
-
-    Language-specific behaviour:
-
-    * C: Returns true for types that represent functions. Note that function
-      pointers are not function types (but their `GetPointeeType()` are function
-      types).
-    * C++: Same as in C.
-    * Objective-C: Returns false for all types.
-    "
-) lldb::SBType::IsPolymorphicClass;
-
 %feature("docstring",
     "Returns true if this type is a polymorphic type.
 

diff  --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index b66c2d5642b6f95..bbcccaab51aaee1 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -374,6 +374,52 @@ class LLDB_API SBValue {
   lldb::SBWatchpoint WatchPointee(bool resolve_location, bool read, bool write,
                                   SBError &error);
 
+  /// If this value represents a C++ class that has a vtable, return an value
+  /// that represents the virtual function table.
+  ///
+  /// SBValue::GetError() will be in the success state if this value represents
+  /// a C++ class with a vtable, or an appropriate error describing that the
+  /// object isn't a C++ class with a vtable or not a C++ class.
+  ///
+  /// SBValue::GetName() will be the demangled symbol name for the virtual
+  /// function table like "vtable for <classname>".
+  ///
+  /// SBValue::GetValue() will be the address of the first vtable entry if the
+  /// current SBValue is a class with a vtable, or nothing the current SBValue
+  /// is not a C++ class or not a C++ class that has a vtable.
+  ///
+  /// SBValue::GetValueAtUnsigned(...) will return the address of the first
+  /// vtable entry.
+  ///
+  /// SBValue::GetLoadAddress() will return the address of the vtable pointer
+  /// found in the parent SBValue.
+  ///
+  /// SBValue::GetNumChildren() will return the number of virtual function
+  /// pointers in the vtable, or zero on error.
+  ///
+  /// SBValue::GetChildAtIndex(...) will return each virtual function pointer
+  /// as a SBValue object.
+  ///
+  /// The child SBValue objects will have the following values:
+  ///
+  /// SBValue::GetError() will indicate success if the vtable entry was
+  /// successfully read from memory, or an error if not.
+  ///
+  /// SBValue::GetName() will be the vtable function index in the form "[%u]"
+  /// where %u is the index.
+  ///
+  /// SBValue::GetValue() will be the virtual function pointer value as a
+  /// string.
+  ///
+  /// SBValue::GetValueAtUnsigned(...) will return the virtual function
+  /// pointer value.
+  ///
+  /// SBValue::GetLoadAddress() will return the address of the virtual function
+  /// pointer.
+  ///
+  /// SBValue::GetNumChildren() returns 0
+  lldb::SBValue GetVTable();
+
 protected:
   friend class SBBlock;
   friend class SBFrame;

diff  --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h
index 3af94f0a86e2fcc..20b3086138457f7 100644
--- a/lldb/include/lldb/Core/ValueObject.h
+++ b/lldb/include/lldb/Core/ValueObject.h
@@ -620,6 +620,10 @@ class ValueObject {
   virtual lldb::ValueObjectSP CastPointerType(const char *name,
                                               lldb::TypeSP &type_sp);
 
+  /// If this object represents a C++ class with a vtable, return an object
+  /// that represents the virtual function table. If the object isn't a class
+  /// with a vtable, return a valid ValueObject with the error set correctly.
+  lldb::ValueObjectSP GetVTable();
   // The backing bits of this value object were updated, clear any descriptive
   // string, so we know we have to refetch them.
   void ValueUpdated() {

diff  --git a/lldb/include/lldb/Core/ValueObjectChild.h b/lldb/include/lldb/Core/ValueObjectChild.h
index 07b37aa8a405f7e..46b14e6840f0dc3 100644
--- a/lldb/include/lldb/Core/ValueObjectChild.h
+++ b/lldb/include/lldb/Core/ValueObjectChild.h
@@ -73,6 +73,7 @@ class ValueObjectChild : public ValueObject {
   friend class ValueObject;
   friend class ValueObjectConstResult;
   friend class ValueObjectConstResultImpl;
+  friend class ValueObjectVTable;
 
   ValueObjectChild(ValueObject &parent, const CompilerType &compiler_type,
                    ConstString name, uint64_t byte_size,

diff  --git a/lldb/include/lldb/Core/ValueObjectVTable.h b/lldb/include/lldb/Core/ValueObjectVTable.h
new file mode 100644
index 000000000000000..217ff8d0d334ce6
--- /dev/null
+++ b/lldb/include/lldb/Core/ValueObjectVTable.h
@@ -0,0 +1,105 @@
+//===-- ValueObjectVTable.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_CORE_VALUEOBJECTVTABLE_H
+#define LLDB_CORE_VALUEOBJECTVTABLE_H
+
+#include "lldb/Core/ValueObject.h"
+
+namespace lldb_private {
+
+/// A class that represents a virtual function table for a C++ class.
+///
+/// ValueObject::GetError() will be in the success state if this value
+/// represents a C++ class with a vtable, or an appropriate error describing
+/// that the object isn't a C++ class with a vtable or not a C++ class.
+///
+/// ValueObject::GetName() will be the demangled symbol name for the virtual
+/// function table like "vtable for <classname>".
+///
+/// ValueObject::GetValueAsCString() will be the address of the first vtable
+/// entry if the current ValueObject is a class with a vtable, or nothing the
+/// current ValueObject is not a C++ class or not a C++ class that has a
+/// vtable.
+///
+/// ValueObject::GetValueAtUnsigned(...) will return the address of the first
+/// vtable entry.
+///
+/// ValueObject::GetAddressOf() will return the address of the vtable pointer
+/// found in the parent ValueObject.
+///
+/// ValueObject::GetNumChildren() will return the number of virtual function
+/// pointers in the vtable, or zero on error.
+///
+/// ValueObject::GetChildAtIndex(...) will return each virtual function pointer
+/// as a ValueObject object.
+///
+/// The child ValueObjects will have the following values:
+///
+/// ValueObject::GetError() will indicate success if the vtable entry was
+/// successfully read from memory, or an error if not.
+///
+/// ValueObject::GetName() will be the vtable function index in the form "[%u]"
+/// where %u is the index.
+///
+/// ValueObject::GetValueAsCString() will be the virtual function pointer value
+///
+/// ValueObject::GetValueAtUnsigned(...) will return the virtual function
+/// pointer value.
+///
+/// ValueObject::GetAddressOf() will return the address of the virtual function
+/// pointer.
+///
+/// ValueObject::GetNumChildren() returns 0
+class ValueObjectVTable : public ValueObject {
+public:
+  ~ValueObjectVTable() override;
+
+  static lldb::ValueObjectSP Create(ValueObject &parent);
+
+  std::optional<uint64_t> GetByteSize() override;
+
+  size_t CalculateNumChildren(uint32_t max) override;
+
+  ValueObject *CreateChildAtIndex(size_t idx, bool synthetic_array_member,
+                                  int32_t synthetic_index) override;
+
+  lldb::ValueType GetValueType() const override;
+
+  ConstString GetTypeName() override;
+
+  ConstString GetQualifiedTypeName() override;
+
+  ConstString GetDisplayTypeName() override;
+
+  bool IsInScope() override;
+
+protected:
+  bool UpdateValue() override;
+
+  CompilerType GetCompilerTypeImpl() override;
+
+  /// The symbol for the C++ virtual function table.
+  const Symbol *m_vtable_symbol = nullptr;
+  /// Cache the number of vtable children when we update the value.
+  uint32_t m_num_vtable_entries = 0;
+  /// Cache the address size in bytes to avoid checking with the process to
+  /// many times.
+  uint32_t m_addr_size = 0;
+
+private:
+  ValueObjectVTable(ValueObject &parent);
+
+  // For ValueObject only
+  ValueObjectVTable(const ValueObjectVTable &) = delete;
+  const ValueObjectVTable &operator=(const ValueObjectVTable &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_CORE_VALUEOBJECTVTABLE_H

diff  --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h
index c5ef1f53021207a..15edbea3cc7ae73 100644
--- a/lldb/include/lldb/Symbol/Type.h
+++ b/lldb/include/lldb/Symbol/Type.h
@@ -420,6 +420,8 @@ class TypeAndOrName {
 
   void SetName(const char *type_name_cstr);
 
+  void SetName(llvm::StringRef name);
+
   void SetTypeSP(lldb::TypeSP type_sp);
 
   void SetCompilerType(CompilerType compiler_type);

diff  --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 5ac16be3347ff89..cd5004a3f34db42 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -437,6 +437,10 @@ class TypeSystem : public PluginInterface,
 
   virtual CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) = 0;
 
+  virtual CompilerType CreateGenericFunctionPrototype() {
+    return CompilerType();
+  }
+
   virtual CompilerType
   GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding,
                                       size_t bit_size) = 0;

diff  --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h
index eff79a0bf0d0622..a2a9c0163f082fb 100644
--- a/lldb/include/lldb/Target/LanguageRuntime.h
+++ b/lldb/include/lldb/Target/LanguageRuntime.h
@@ -78,6 +78,32 @@ class LanguageRuntime : public Runtime, public PluginInterface {
   virtual bool GetObjectDescription(Stream &str, Value &value,
                                     ExecutionContextScope *exe_scope) = 0;
 
+
+  struct VTableInfo {
+    Address addr; /// Address of the vtable's virtual function table
+    Symbol *symbol; /// The vtable symbol from the symbol table
+  };
+  /// Get the vtable information for a given value.
+  ///
+  /// \param[in] in_value
+  ///     The value object to try and extract the VTableInfo from.
+  ///
+  /// \param[in] check_type
+  ///     If true, the compiler type of \a in_value will be checked to see if
+  ///     it is an instance to, or pointer or reference to a class or struct
+  ///     that has a vtable. If the type doesn't meet the requirements, an
+  ///     error will be returned explaining why the type isn't suitable.
+  ///
+  /// \return
+  ///     An error if anything goes wrong while trying to extract the vtable
+  ///     or if \a check_type is true and the type doesn't have a vtable.
+  virtual llvm::Expected<VTableInfo> GetVTableInfo(ValueObject &in_value,
+                                                   bool check_type) {
+    return llvm::createStringError(
+        std::errc::invalid_argument,
+        "language doesn't support getting vtable information");
+  }
+
   // this call should return true if it could set the name and/or the type
   virtual bool GetDynamicTypeAndAddress(ValueObject &in_value,
                                         lldb::DynamicValueType use_dynamic,

diff  --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 206ff4ed7e6ad05..633a3ee696c2087 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -322,7 +322,9 @@ enum ValueType {
   eValueTypeRegister = 5,         ///< stack frame register value
   eValueTypeRegisterSet = 6, ///< A collection of stack frame register values
   eValueTypeConstResult = 7, ///< constant result variables
-  eValueTypeVariableThreadLocal = 8 ///< thread local storage variable
+  eValueTypeVariableThreadLocal = 8, ///< thread local storage variable
+  eValueTypeVTable = 9,              ///< virtual function table
+  eValueTypeVTableEntry = 10, ///< function pointer in virtual function table
 };
 
 /// Token size/granularities for Input Readers.

diff  --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index e14f1196c6316b9..34d01d759ba55a4 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -114,7 +114,7 @@ class ValueImpl {
 
     Target *target = value_sp->GetTargetSP().get();
     // If this ValueObject holds an error, then it is valuable for that.
-    if (value_sp->GetError().Fail()) 
+    if (value_sp->GetError().Fail())
       return value_sp;
 
     if (!target)
@@ -1038,8 +1038,8 @@ lldb::ValueObjectSP SBValue::GetSP(ValueLocker &locker) const {
   // IsValid means that the SBValue has a value in it.  But that's not the
   // only time that ValueObjects are useful.  We also want to return the value
   // if there's an error state in it.
-  if (!m_opaque_sp || (!m_opaque_sp->IsValid() 
-      && (m_opaque_sp->GetRootSP() 
+  if (!m_opaque_sp || (!m_opaque_sp->IsValid()
+      && (m_opaque_sp->GetRootSP()
           && !m_opaque_sp->GetRootSP()->GetError().Fail()))) {
     locker.GetError().SetErrorString("No value");
     return ValueObjectSP();
@@ -1505,3 +1505,14 @@ lldb::SBValue SBValue::Persist() {
   }
   return persisted_sb;
 }
+
+lldb::SBValue SBValue::GetVTable() {
+  SBValue vtable_sb;
+  ValueLocker locker;
+  lldb::ValueObjectSP value_sp(GetSP(locker));
+  if (!value_sp)
+    return vtable_sb;
+
+  vtable_sb.SetSP(value_sp->GetVTable());
+  return vtable_sb;
+}

diff  --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 1fad638f214536d..e7cb861c2b01c63 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -490,6 +490,8 @@ may even involve JITing and running code in the target program.)");
     case eValueTypeRegisterSet:
     case eValueTypeConstResult:
     case eValueTypeVariableThreadLocal:
+    case eValueTypeVTable:
+    case eValueTypeVTableEntry:
       return false;
     }
   }

diff  --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index 9073e3e9b2ee33d..10525ac39e6ef50 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -71,6 +71,7 @@ add_lldb_library(lldbCore
   ValueObjectSyntheticFilter.cpp
   ValueObjectUpdater.cpp
   ValueObjectVariable.cpp
+  ValueObjectVTable.cpp
 
   DEPENDS
     clang-tablegen-targets

diff  --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index ebfc1cf4d6fe9e1..bdb1bef633d8fb1 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Core/ValueObjectDynamicValue.h"
 #include "lldb/Core/ValueObjectMemory.h"
 #include "lldb/Core/ValueObjectSyntheticFilter.h"
+#include "lldb/Core/ValueObjectVTable.h"
 #include "lldb/DataFormatters/DataVisualization.h"
 #include "lldb/DataFormatters/DumpValueObjectOptions.h"
 #include "lldb/DataFormatters/FormatManager.h"
@@ -3155,3 +3156,7 @@ ValueObjectSP ValueObject::Persist() {
 
   return persistent_var_sp->GetValueObject();
 }
+
+lldb::ValueObjectSP ValueObject::GetVTable() {
+  return ValueObjectVTable::Create(*this);
+}

diff  --git a/lldb/source/Core/ValueObjectVTable.cpp b/lldb/source/Core/ValueObjectVTable.cpp
new file mode 100644
index 000000000000000..177ae4167a1d455
--- /dev/null
+++ b/lldb/source/Core/ValueObjectVTable.cpp
@@ -0,0 +1,274 @@
+//===-- ValueObjectVTable.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectVTable.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObjectChild.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ValueObjectVTableChild : public ValueObject {
+public:
+  ValueObjectVTableChild(ValueObject &parent, uint32_t func_idx,
+                         uint64_t addr_size)
+      : ValueObject(parent), m_func_idx(func_idx), m_addr_size(addr_size) {
+    SetFormat(eFormatPointer);
+    SetName(ConstString(llvm::formatv("[{0}]", func_idx).str()));
+  }
+
+  ~ValueObjectVTableChild() override = default;
+
+  std::optional<uint64_t> GetByteSize() override { return m_addr_size; };
+
+  size_t CalculateNumChildren(uint32_t max) override { return 0; };
+
+  ValueType GetValueType() const override { return eValueTypeVTableEntry; };
+
+  bool IsInScope() override {
+    if (ValueObject *parent = GetParent())
+      return parent->IsInScope();
+    return false;
+  };
+
+protected:
+  bool UpdateValue() override {
+    SetValueIsValid(false);
+    m_value.Clear();
+    ValueObject *parent = GetParent();
+    if (!parent) {
+      m_error.SetErrorString("owning vtable object not valid");
+      return false;
+    }
+
+    addr_t parent_addr = parent->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+    if (parent_addr == LLDB_INVALID_ADDRESS) {
+      m_error.SetErrorString("invalid vtable address");
+      return false;
+    }
+
+    ProcessSP process_sp = GetProcessSP();
+    if (!process_sp) {
+      m_error.SetErrorString("no process");
+      return false;
+    }
+
+    TargetSP target_sp = GetTargetSP();
+    if (!target_sp) {
+      m_error.SetErrorString("no target");
+      return false;
+    }
+
+    // Each `vtable_entry_addr` points to the function pointer.
+    addr_t vtable_entry_addr = parent_addr + m_func_idx * m_addr_size;
+    addr_t vfunc_ptr =
+        process_sp->ReadPointerFromMemory(vtable_entry_addr, m_error);
+    if (m_error.Fail()) {
+      m_error.SetErrorStringWithFormat(
+          "failed to read virtual function entry 0x%16.16" PRIx64,
+          vtable_entry_addr);
+      return false;
+    }
+
+
+    // Set our value to be the load address of the function pointer in memory
+    // and our type to be the function pointer type.
+    m_value.SetValueType(Value::ValueType::LoadAddress);
+    m_value.GetScalar() = vtable_entry_addr;
+
+    // See if our resolved address points to a function in the debug info. If
+    // it does, then we can report the type as a function prototype for this
+    // function.
+    Function *function = nullptr;
+    Address resolved_vfunc_ptr_address;
+    target_sp->ResolveLoadAddress(vfunc_ptr, resolved_vfunc_ptr_address);
+    if (resolved_vfunc_ptr_address.IsValid())
+      function = resolved_vfunc_ptr_address.CalculateSymbolContextFunction();
+    if (function) {
+      m_value.SetCompilerType(function->GetCompilerType().GetPointerType());
+    } else {
+      // Set our value's compiler type to a generic function protoype so that
+      // it displays as a hex function pointer for the value and the summary
+      // will display the address description.
+
+      // Get the original type that this vtable is based off of so we can get
+      // the language from it correctly.
+      ValueObject *val = parent->GetParent();
+      auto type_system = target_sp->GetScratchTypeSystemForLanguage(
+            val ? val->GetObjectRuntimeLanguage() : eLanguageTypeC_plus_plus);
+      if (type_system) {
+        m_value.SetCompilerType(
+            (*type_system)->CreateGenericFunctionPrototype().GetPointerType());
+      } else {
+        consumeError(type_system.takeError());
+      }
+    }
+
+    // Now read our value into m_data so that our we can use the default
+    // summary provider for C++ for function pointers which will get the
+    // address description for our function pointer.
+    if (m_error.Success()) {
+      const bool thread_and_frame_only_if_stopped = true;
+      ExecutionContext exe_ctx(
+        GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped));
+      m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+    }
+    SetValueDidChange(true);
+    SetValueIsValid(true);
+    return true;
+  };
+
+  CompilerType GetCompilerTypeImpl() override {
+    return m_value.GetCompilerType();
+  };
+
+  const uint32_t m_func_idx;
+  const uint64_t m_addr_size;
+
+private:
+  // For ValueObject only
+  ValueObjectVTableChild(const ValueObjectVTableChild &) = delete;
+  const ValueObjectVTableChild &
+  operator=(const ValueObjectVTableChild &) = delete;
+};
+
+ValueObjectSP ValueObjectVTable::Create(ValueObject &parent) {
+  return (new ValueObjectVTable(parent))->GetSP();
+}
+
+ValueObjectVTable::ValueObjectVTable(ValueObject &parent)
+    : ValueObject(parent) {
+  SetFormat(eFormatPointer);
+}
+
+std::optional<uint64_t> ValueObjectVTable::GetByteSize() {
+  if (m_vtable_symbol)
+    return m_vtable_symbol->GetByteSize();
+  return std::nullopt;
+}
+
+size_t ValueObjectVTable::CalculateNumChildren(uint32_t max) {
+  if (UpdateValueIfNeeded(false))
+    return m_num_vtable_entries <= max ? m_num_vtable_entries : max;
+  return 0;
+}
+
+ValueType ValueObjectVTable::GetValueType() const { return eValueTypeVTable; }
+
+ConstString ValueObjectVTable::GetTypeName() {
+  if (m_vtable_symbol)
+    return m_vtable_symbol->GetName();
+  return ConstString();
+}
+
+ConstString ValueObjectVTable::GetQualifiedTypeName() { return GetTypeName(); }
+
+ConstString ValueObjectVTable::GetDisplayTypeName() {
+  if (m_vtable_symbol)
+    return m_vtable_symbol->GetDisplayName();
+  return ConstString();
+}
+
+bool ValueObjectVTable::IsInScope() { return GetParent()->IsInScope(); }
+
+ValueObject *ValueObjectVTable::CreateChildAtIndex(size_t idx,
+                                                   bool synthetic_array_member,
+                                                   int32_t synthetic_index) {
+  if (synthetic_array_member)
+    return nullptr;
+  return new ValueObjectVTableChild(*this, idx, m_addr_size);
+}
+
+bool ValueObjectVTable::UpdateValue() {
+  m_error.Clear();
+  m_flags.m_children_count_valid = false;
+  SetValueIsValid(false);
+  m_num_vtable_entries = 0;
+  ValueObject *parent = GetParent();
+  if (!parent) {
+    m_error.SetErrorString("no parent object");
+    return false;
+  }
+
+  ProcessSP process_sp = GetProcessSP();
+  if (!process_sp) {
+    m_error.SetErrorString("no process");
+    return false;
+  }
+
+  const LanguageType language = parent->GetObjectRuntimeLanguage();
+  LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(language);
+
+  if (language_runtime == nullptr) {
+    m_error.SetErrorStringWithFormat(
+        "no language runtime support for the language \"%s\"",
+        Language::GetNameForLanguageType(language));
+    return false;
+  }
+
+  // Get the vtable information from the language runtime.
+  llvm::Expected<LanguageRuntime::VTableInfo> vtable_info_or_err =
+      language_runtime->GetVTableInfo(*parent, /*check_type=*/true);
+  if (!vtable_info_or_err) {
+    m_error = vtable_info_or_err.takeError();
+    return false;
+  }
+
+  TargetSP target_sp = GetTargetSP();
+  const addr_t vtable_start_addr =
+      vtable_info_or_err->addr.GetLoadAddress(target_sp.get());
+
+  m_vtable_symbol = vtable_info_or_err->symbol;
+  if (!m_vtable_symbol) {
+    m_error.SetErrorStringWithFormat(
+        "no vtable symbol found containing 0x%" PRIx64, vtable_start_addr);
+    return false;
+  }
+
+  // Now that we know it's a vtable, we update the object's state.
+  SetName(GetTypeName());
+
+  // Calculate the number of entries
+  if (!m_vtable_symbol->GetByteSizeIsValid()) {
+    m_error.SetErrorStringWithFormat(
+        "vtable symbol \"%s\" doesn't have a valid size",
+        m_vtable_symbol->GetMangled().GetDemangledName().GetCString());
+    return false;
+  }
+
+  m_addr_size = process_sp->GetAddressByteSize();
+  const addr_t vtable_end_addr =
+      m_vtable_symbol->GetLoadAddress(target_sp.get()) +
+      m_vtable_symbol->GetByteSize();
+  m_num_vtable_entries = (vtable_end_addr - vtable_start_addr) / m_addr_size;
+
+  m_value.SetValueType(Value::ValueType::LoadAddress);
+  m_value.GetScalar() = parent->GetAddressOf();
+  auto type_system_or_err =
+        target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+  if (type_system_or_err) {
+    m_value.SetCompilerType(
+        (*type_system_or_err)->GetBasicTypeFromAST(eBasicTypeUnsignedLong));
+  } else {
+    consumeError(type_system_or_err.takeError());
+  }
+  SetValueDidChange(true);
+  SetValueIsValid(true);
+  return true;
+}
+
+CompilerType ValueObjectVTable::GetCompilerTypeImpl() { return CompilerType(); }
+
+ValueObjectVTable::~ValueObjectVTable() = default;

diff  --git a/lldb/source/DataFormatters/CXXFunctionPointer.cpp b/lldb/source/DataFormatters/CXXFunctionPointer.cpp
index d7df280e56efb0d..6543433d17ff45f 100644
--- a/lldb/source/DataFormatters/CXXFunctionPointer.cpp
+++ b/lldb/source/DataFormatters/CXXFunctionPointer.cpp
@@ -13,6 +13,7 @@
 #include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
 
 #include <string>
 
@@ -76,7 +77,10 @@ bool lldb_private::formatters::CXXFunctionPointerSummaryProvider(
     }
   }
   if (sstr.GetSize() > 0) {
-    stream.Printf("(%s)", sstr.GetData());
+    if (valobj.GetValueType() == lldb::eValueTypeVTableEntry)
+      stream.PutCString(sstr.GetData());
+    else
+      stream.Printf("(%s)", sstr.GetData());
     return true;
   } else
     return false;

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 8b8d330799cb6c7..f4537b4133b93e0 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -45,6 +45,7 @@
 #include "LibCxxVariant.h"
 #include "LibStdcpp.h"
 #include "MSVCUndecoratedNameParser.h"
+#include "lldb/lldb-enumerations.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -1415,7 +1416,8 @@ CPlusPlusLanguage::GetHardcodedSummaries() {
                   lldb_private::formatters::CXXFunctionPointerSummaryProvider,
                   "Function pointer summary provider"));
           if (CompilerType CT = valobj.GetCompilerType();
-              CT.IsFunctionPointerType() || CT.IsMemberFunctionPointerType()) {
+              CT.IsFunctionPointerType() || CT.IsMemberFunctionPointerType() ||
+              valobj.GetValueType() == lldb::eValueTypeVTableEntry) {
             return formatter_sp;
           }
           return nullptr;

diff  --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
index 53e856bf3514e06..17c8b43578691c0 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -54,134 +54,228 @@ bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
                                                           check_objc);
 }
 
-TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress(
-    ValueObject &in_value, lldb::addr_t original_ptr,
-    lldb::addr_t vtable_load_addr) {
-  if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS) {
-    // Find the symbol that contains the "vtable_load_addr" address
-    Address vtable_addr;
-    Target &target = m_process->GetTarget();
-    if (!target.GetSectionLoadList().IsEmpty()) {
-      if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr,
-                                                         vtable_addr)) {
-        // See if we have cached info for this type already
-        TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr);
-        if (type_info)
-          return type_info;
-
-        SymbolContext sc;
-        target.GetImages().ResolveSymbolContextForAddress(
-            vtable_addr, eSymbolContextSymbol, sc);
-        Symbol *symbol = sc.symbol;
-        if (symbol != nullptr) {
-          const char *name =
-              symbol->GetMangled().GetDemangledName().AsCString();
-          if (name && strstr(name, vtable_demangled_prefix) == name) {
-            Log *log = GetLog(LLDBLog::Object);
-            LLDB_LOGF(log,
-                      "0x%16.16" PRIx64
-                      ": static-type = '%s' has vtable symbol '%s'\n",
-                      original_ptr, in_value.GetTypeName().GetCString(), name);
-            // We are a C++ class, that's good.  Get the class name and look it
-            // up:
-            const char *class_name = name + strlen(vtable_demangled_prefix);
-            // We know the class name is absolute, so tell FindTypes that by
-            // prefixing it with the root namespace:
-            std::string lookup_name("::");
-            lookup_name.append(class_name);
-            
-            type_info.SetName(class_name);
-            const bool exact_match = true;
-            TypeList class_types;
-
-            // First look in the module that the vtable symbol came from and
-            // look for a single exact match.
-            llvm::DenseSet<SymbolFile *> searched_symbol_files;
-            if (sc.module_sp)
-              sc.module_sp->FindTypes(ConstString(lookup_name), exact_match, 1,
+TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfo(
+    ValueObject &in_value, const VTableInfo &vtable_info) {
+  if (vtable_info.addr.IsSectionOffset()) {
+    // See if we have cached info for this type already
+    TypeAndOrName type_info = GetDynamicTypeInfo(vtable_info.addr);
+    if (type_info)
+      return type_info;
+
+    if (vtable_info.symbol) {
+      Log *log = GetLog(LLDBLog::Object);
+      llvm::StringRef symbol_name =
+          vtable_info.symbol->GetMangled().GetDemangledName().GetStringRef();
+      LLDB_LOGF(log,
+                "0x%16.16" PRIx64
+                ": static-type = '%s' has vtable symbol '%s'\n",
+                in_value.GetPointerValue(),
+                in_value.GetTypeName().GetCString(),
+                symbol_name.str().c_str());
+      // We are a C++ class, that's good.  Get the class name and look it
+      // up:
+      llvm::StringRef class_name = symbol_name;
+      class_name.consume_front(vtable_demangled_prefix);
+      // We know the class name is absolute, so tell FindTypes that by
+      // prefixing it with the root namespace:
+      std::string lookup_name("::");
+      lookup_name.append(class_name.data(), class_name.size());
+
+      type_info.SetName(class_name);
+      const bool exact_match = true;
+      TypeList class_types;
+
+      // First look in the module that the vtable symbol came from and
+      // look for a single exact match.
+      llvm::DenseSet<SymbolFile *> searched_symbol_files;
+      ModuleSP module_sp = vtable_info.symbol->CalculateSymbolContextModule();
+      if (module_sp)
+        module_sp->FindTypes(ConstString(lookup_name), exact_match, 1,
+                              searched_symbol_files, class_types);
+
+      // If we didn't find a symbol, then move on to the entire module
+      // list in the target and get as many unique matches as possible
+      Target &target = m_process->GetTarget();
+      if (class_types.Empty())
+        target.GetImages().FindTypes(nullptr, ConstString(lookup_name),
+                                      exact_match, UINT32_MAX,
                                       searched_symbol_files, class_types);
 
-            // If we didn't find a symbol, then move on to the entire module
-            // list in the target and get as many unique matches as possible
-            if (class_types.Empty())
-              target.GetImages().FindTypes(nullptr, ConstString(lookup_name),
-                                           exact_match, UINT32_MAX,
-                                           searched_symbol_files, class_types);
-
-            lldb::TypeSP type_sp;
-            if (class_types.Empty()) {
-              LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n",
-                        original_ptr);
-              return TypeAndOrName();
+      lldb::TypeSP type_sp;
+      if (class_types.Empty()) {
+        LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n",
+                  in_value.GetPointerValue());
+        return TypeAndOrName();
+      }
+      if (class_types.GetSize() == 1) {
+        type_sp = class_types.GetTypeAtIndex(0);
+        if (type_sp) {
+          if (TypeSystemClang::IsCXXClassType(
+                  type_sp->GetForwardCompilerType())) {
+            LLDB_LOGF(
+                log,
+                "0x%16.16" PRIx64
+                ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
+                "}, type-name='%s'\n",
+                in_value.GetPointerValue(), in_value.GetTypeName().AsCString(),
+                type_sp->GetID(), type_sp->GetName().GetCString());
+            type_info.SetTypeSP(type_sp);
+          }
+        }
+      } else {
+        size_t i;
+        if (log) {
+          for (i = 0; i < class_types.GetSize(); i++) {
+            type_sp = class_types.GetTypeAtIndex(i);
+            if (type_sp) {
+              LLDB_LOGF(
+                  log,
+                  "0x%16.16" PRIx64
+                  ": static-type = '%s' has multiple matching dynamic "
+                  "types: uid={0x%" PRIx64 "}, type-name='%s'\n",
+                  in_value.GetPointerValue(),
+                  in_value.GetTypeName().AsCString(),
+                  type_sp->GetID(), type_sp->GetName().GetCString());
             }
-            if (class_types.GetSize() == 1) {
-              type_sp = class_types.GetTypeAtIndex(0);
-              if (type_sp) {
-                if (TypeSystemClang::IsCXXClassType(
-                        type_sp->GetForwardCompilerType())) {
-                  LLDB_LOGF(
-                      log,
-                      "0x%16.16" PRIx64
-                      ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
-                      "}, type-name='%s'\n",
-                      original_ptr, in_value.GetTypeName().AsCString(),
-                      type_sp->GetID(), type_sp->GetName().GetCString());
-                  type_info.SetTypeSP(type_sp);
-                }
-              }
-            } else {
-              size_t i;
-              if (log) {
-                for (i = 0; i < class_types.GetSize(); i++) {
-                  type_sp = class_types.GetTypeAtIndex(i);
-                  if (type_sp) {
-                    LLDB_LOGF(
-                        log,
-                        "0x%16.16" PRIx64
-                        ": static-type = '%s' has multiple matching dynamic "
-                        "types: uid={0x%" PRIx64 "}, type-name='%s'\n",
-                        original_ptr, in_value.GetTypeName().AsCString(),
-                        type_sp->GetID(), type_sp->GetName().GetCString());
-                  }
-                }
-              }
-
-              for (i = 0; i < class_types.GetSize(); i++) {
-                type_sp = class_types.GetTypeAtIndex(i);
-                if (type_sp) {
-                  if (TypeSystemClang::IsCXXClassType(
-                          type_sp->GetForwardCompilerType())) {
-                    LLDB_LOGF(
-                        log,
-                        "0x%16.16" PRIx64 ": static-type = '%s' has multiple "
-                        "matching dynamic types, picking "
-                        "this one: uid={0x%" PRIx64 "}, type-name='%s'\n",
-                        original_ptr, in_value.GetTypeName().AsCString(),
-                        type_sp->GetID(), type_sp->GetName().GetCString());
-                    type_info.SetTypeSP(type_sp);
-                  }
-                }
-              }
-
-              if (log) {
-                LLDB_LOGF(log,
-                          "0x%16.16" PRIx64
-                          ": static-type = '%s' has multiple matching dynamic "
-                          "types, didn't find a C++ match\n",
-                          original_ptr, in_value.GetTypeName().AsCString());
-              }
+          }
+        }
+
+        for (i = 0; i < class_types.GetSize(); i++) {
+          type_sp = class_types.GetTypeAtIndex(i);
+          if (type_sp) {
+            if (TypeSystemClang::IsCXXClassType(
+                    type_sp->GetForwardCompilerType())) {
+              LLDB_LOGF(
+                  log,
+                  "0x%16.16" PRIx64 ": static-type = '%s' has multiple "
+                  "matching dynamic types, picking "
+                  "this one: uid={0x%" PRIx64 "}, type-name='%s'\n",
+                  in_value.GetPointerValue(),
+                  in_value.GetTypeName().AsCString(),
+                  type_sp->GetID(), type_sp->GetName().GetCString());
+              type_info.SetTypeSP(type_sp);
             }
-            if (type_info)
-              SetDynamicTypeInfo(vtable_addr, type_info);
-            return type_info;
           }
         }
+
+        if (log) {
+          LLDB_LOGF(log,
+                    "0x%16.16" PRIx64
+                    ": static-type = '%s' has multiple matching dynamic "
+                    "types, didn't find a C++ match\n",
+                    in_value.GetPointerValue(),
+                    in_value.GetTypeName().AsCString());
+        }
       }
+      if (type_info)
+        SetDynamicTypeInfo(vtable_info.addr, type_info);
+      return type_info;
     }
   }
   return TypeAndOrName();
 }
 
+llvm::Error ItaniumABILanguageRuntime::TypeHasVTable(CompilerType type) {
+  // Check to make sure the class has a vtable.
+  CompilerType original_type = type;
+  if (type.IsPointerOrReferenceType()) {
+    CompilerType pointee_type = type.GetPointeeType();
+    if (pointee_type)
+      type = pointee_type;
+  }
+
+  // Make sure this is a class or a struct first by checking the type class
+  // bitfield that gets returned.
+  if ((type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) {
+    return llvm::createStringError(std::errc::invalid_argument,
+        "type \"%s\" is not a class or struct or a pointer to one",
+        original_type.GetTypeName().AsCString("<invalid>"));
+  }
+
+  // Check if the type has virtual functions by asking it if it is polymorphic.
+  if (!type.IsPolymorphicClass()) {
+    return llvm::createStringError(std::errc::invalid_argument,
+        "type \"%s\" doesn't have a vtable",
+        type.GetTypeName().AsCString("<invalid>"));
+  }
+  return llvm::Error::success();
+}
+
+// This function can accept both pointers or references to classes as well as
+// instances of classes. If you are using this function during dynamic type
+// detection, only valid ValueObjects that return true to
+// CouldHaveDynamicValue(...) should call this function and \a check_type
+// should be set to false. This function is also used by ValueObjectVTable
+// and is can pass in instances of classes which is not suitable for dynamic
+// type detection, these cases should pass true for \a check_type.
+llvm::Expected<LanguageRuntime::VTableInfo>
+ ItaniumABILanguageRuntime::GetVTableInfo(ValueObject &in_value,
+                                          bool check_type) {
+
+  CompilerType type = in_value.GetCompilerType();
+  if (check_type) {
+    if (llvm::Error err = TypeHasVTable(type))
+      return std::move(err);
+  }
+  ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
+  Process *process = exe_ctx.GetProcessPtr();
+  if (process == nullptr)
+    return llvm::createStringError(std::errc::invalid_argument,
+                                   "invalid process");
+
+  AddressType address_type;
+  lldb::addr_t original_ptr = LLDB_INVALID_ADDRESS;
+  if (type.IsPointerOrReferenceType())
+    original_ptr = in_value.GetPointerValue(&address_type);
+  else
+    original_ptr = in_value.GetAddressOf(/*scalar_is_load_address=*/true,
+                                         &address_type);
+  if (original_ptr == LLDB_INVALID_ADDRESS || address_type != eAddressTypeLoad)
+    return llvm::createStringError(std::errc::invalid_argument,
+                                   "failed to get the address of the value");
+
+  Status error;
+  const lldb::addr_t vtable_load_addr =
+      process->ReadPointerFromMemory(original_ptr, error);
+
+  if (!error.Success() || vtable_load_addr == LLDB_INVALID_ADDRESS)
+    return llvm::createStringError(std::errc::invalid_argument,
+        "failed to read vtable pointer from memory at 0x%" PRIx64,
+        original_ptr);
+;
+
+  // Find the symbol that contains the "vtable_load_addr" address
+  Address vtable_addr;
+  if (!process->GetTarget().ResolveLoadAddress(vtable_load_addr, vtable_addr))
+    return llvm::createStringError(std::errc::invalid_argument,
+                                   "failed to resolve vtable pointer 0x%"
+                                   PRIx64 "to a section", vtable_load_addr);
+
+  // Check our cache first to see if we already have this info
+  {
+    std::lock_guard<std::mutex> locker(m_mutex);
+    auto pos = m_vtable_info_map.find(vtable_addr);
+    if (pos != m_vtable_info_map.end())
+      return pos->second;
+  }
+
+  Symbol *symbol = vtable_addr.CalculateSymbolContextSymbol();
+  if (symbol == nullptr)
+    return llvm::createStringError(std::errc::invalid_argument,
+                                   "no symbol found for 0x%" PRIx64,
+                                   vtable_load_addr);
+  llvm::StringRef name = symbol->GetMangled().GetDemangledName().GetStringRef();
+  if (name.startswith(vtable_demangled_prefix)) {
+    VTableInfo info = {vtable_addr, symbol};
+    std::lock_guard<std::mutex> locker(m_mutex);
+    auto pos = m_vtable_info_map[vtable_addr] = info;
+    return info;
+  }
+  return llvm::createStringError(std::errc::invalid_argument,
+      "symbol found that contains 0x%" PRIx64 " is not a vtable symbol",
+      vtable_load_addr);
+}
+
 bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
     ValueObject &in_value, lldb::DynamicValueType use_dynamic,
     TypeAndOrName &class_type_or_name, Address &dynamic_address,
@@ -198,33 +292,23 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
   class_type_or_name.Clear();
   value_type = Value::ValueType::Scalar;
 
-  // Only a pointer or reference type can have a 
diff erent dynamic and static
-  // type:
   if (!CouldHaveDynamicValue(in_value))
     return false;
 
-  // First job, pull out the address at 0 offset from the object.
-  AddressType address_type;
-  lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type);
-  if (original_ptr == LLDB_INVALID_ADDRESS)
-    return false;
-
-  ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
-
-  Process *process = exe_ctx.GetProcessPtr();
-
-  if (process == nullptr)
-    return false;
-
-  Status error;
-  const lldb::addr_t vtable_address_point =
-      process->ReadPointerFromMemory(original_ptr, error);
-
-  if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS)
+  // Check if we have a vtable pointer in this value. If we don't it will
+  // return an error, else it will return a valid resolved address. We don't
+  // want GetVTableInfo to check the type since we accept void * as a possible
+  // dynamic type and that won't pass the type check. We already checked the
+  // type above in CouldHaveDynamicValue(...).
+  llvm::Expected<VTableInfo> vtable_info_or_err =
+      GetVTableInfo(in_value, /*check_type=*/false);
+  if (!vtable_info_or_err) {
+    llvm::consumeError(vtable_info_or_err.takeError());
     return false;
+  }
 
-  class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr,
-                                                    vtable_address_point);
+  const VTableInfo &vtable_info = vtable_info_or_err.get();
+  class_type_or_name = GetTypeInfo(in_value, vtable_info);
 
   if (!class_type_or_name)
     return false;
@@ -244,22 +328,27 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
   }
 
   // The offset_to_top is two pointers above the vtable pointer.
-  const uint32_t addr_byte_size = process->GetAddressByteSize();
+  Target &target = m_process->GetTarget();
+  const addr_t vtable_load_addr = vtable_info.addr.GetLoadAddress(&target);
+  if (vtable_load_addr == LLDB_INVALID_ADDRESS)
+    return false;
+  const uint32_t addr_byte_size = m_process->GetAddressByteSize();
   const lldb::addr_t offset_to_top_location =
-      vtable_address_point - 2 * addr_byte_size;
+      vtable_load_addr - 2 * addr_byte_size;
   // Watch for underflow, offset_to_top_location should be less than
-  // vtable_address_point
-  if (offset_to_top_location >= vtable_address_point)
+  // vtable_load_addr
+  if (offset_to_top_location >= vtable_load_addr)
     return false;
-  const int64_t offset_to_top = process->ReadSignedIntegerFromMemory(
+  Status error;
+  const int64_t offset_to_top = m_process->ReadSignedIntegerFromMemory(
       offset_to_top_location, addr_byte_size, INT64_MIN, error);
 
   if (offset_to_top == INT64_MIN)
     return false;
   // So the dynamic type is a value that starts at offset_to_top above
   // the original address.
-  lldb::addr_t dynamic_addr = original_ptr + offset_to_top;
-  if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+  lldb::addr_t dynamic_addr = in_value.GetPointerValue() + offset_to_top;
+  if (!m_process->GetTarget().ResolveLoadAddress(
           dynamic_addr, dynamic_address)) {
     dynamic_address.SetRawAddress(dynamic_addr);
   }
@@ -582,10 +671,10 @@ ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
   ValueObjectSP exception = ValueObject::CreateValueObjectFromData(
       "exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx,
       voidstar);
-  ValueObjectSP dyn_exception 
+  ValueObjectSP dyn_exception
       = exception->GetDynamicValue(eDynamicDontRunTarget);
   // If we succeed in making a dynamic value, return that:
-  if (dyn_exception) 
+  if (dyn_exception)
      return dyn_exception;
 
   return exception;
@@ -593,7 +682,7 @@ ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
 
 TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
     const lldb_private::Address &vtable_addr) {
-  std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+  std::lock_guard<std::mutex> locker(m_mutex);
   DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr);
   if (pos == m_dynamic_type_map.end())
     return TypeAndOrName();
@@ -603,6 +692,6 @@ TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
 
 void ItaniumABILanguageRuntime::SetDynamicTypeInfo(
     const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) {
-  std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+  std::lock_guard<std::mutex> locker(m_mutex);
   m_dynamic_type_map[vtable_addr] = type_info;
 }

diff  --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
index ca8d5ab1a93a1b9..0f7e73cfee07546 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
@@ -47,6 +47,10 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
     return runtime->isA(&ID);
   }
 
+
+  llvm::Expected<LanguageRuntime::VTableInfo>
+  GetVTableInfo(ValueObject &in_value, bool check_type) override;
+
   bool GetDynamicTypeAndAddress(ValueObject &in_value,
                                 lldb::DynamicValueType use_dynamic,
                                 TypeAndOrName &class_type_or_name,
@@ -71,7 +75,7 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
                           bool catch_bp, bool throw_bp) override;
 
   lldb::SearchFilterSP CreateExceptionSearchFilter() override;
-  
+
   lldb::ValueObjectSP GetExceptionObjectForThread(
       lldb::ThreadSP thread_sp) override;
 
@@ -89,24 +93,33 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
 
 private:
   typedef std::map<lldb_private::Address, TypeAndOrName> DynamicTypeCache;
+  typedef std::map<lldb_private::Address, VTableInfo> VTableInfoCache;
 
   ItaniumABILanguageRuntime(Process *process)
       : // Call CreateInstance instead.
-        lldb_private::CPPLanguageRuntime(process), m_cxx_exception_bp_sp(),
-        m_dynamic_type_map(), m_dynamic_type_map_mutex() {}
+        lldb_private::CPPLanguageRuntime(process) {}
 
   lldb::BreakpointSP m_cxx_exception_bp_sp;
   DynamicTypeCache m_dynamic_type_map;
-  std::mutex m_dynamic_type_map_mutex;
+  VTableInfoCache m_vtable_info_map;
+  std::mutex m_mutex;
 
-  TypeAndOrName GetTypeInfoFromVTableAddress(ValueObject &in_value,
-                                             lldb::addr_t original_ptr,
-                                             lldb::addr_t vtable_addr);
+  TypeAndOrName GetTypeInfo(ValueObject &in_value,
+                            const VTableInfo &vtable_info);
 
   TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr);
 
   void SetDynamicTypeInfo(const lldb_private::Address &vtable_addr,
                           const TypeAndOrName &type_info);
+
+  // Check if a compiler type has a vtable.
+  //
+  // If the compiler type is a pointer or a reference, this function will check
+  // if the pointee type has a vtable, else it will check the type passed in.
+  //
+  // Returns an error if the type of the value doesn't have a vtable with an
+  // explanation why, or returns an Error::success() if the type has a vtable.
+  llvm::Error TypeHasVTable(CompilerType compiler_type);
 };
 
 } // namespace lldb_private

diff  --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index df06ba0ed952af7..f037708efc38007 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -3557,8 +3557,15 @@ bool TypeSystemClang::IsPolymorphicClass(lldb::opaque_compiler_type_t type) {
         if (record_decl) {
           const clang::CXXRecordDecl *cxx_record_decl =
               llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
-          if (cxx_record_decl)
-            return cxx_record_decl->isPolymorphic();
+          if (cxx_record_decl) {
+            // We can't just call is isPolymorphic() here because that just
+            // means the current class has virtual functions, it doesn't check
+            // if any inherited classes have virtual functions. The doc string
+            // in SBType::IsPolymorphicClass() says it is looking for both
+            // if the class has virtual methods or if any bases do, so this
+            // should be more correct.
+            return cxx_record_decl->isDynamicClass();
+          }
         }
       }
       break;
@@ -4708,6 +4715,21 @@ TypeSystemClang::GetTypedefedType(lldb::opaque_compiler_type_t type) {
 CompilerType TypeSystemClang::GetBasicTypeFromAST(lldb::BasicType basic_type) {
   return TypeSystemClang::GetBasicType(basic_type);
 }
+
+CompilerType TypeSystemClang::CreateGenericFunctionPrototype() {
+  clang::ASTContext &ast = getASTContext();
+  const FunctionType::ExtInfo generic_ext_info(
+    /*noReturn=*/false,
+    /*hasRegParm=*/false,
+    /*regParm=*/0,
+    CallingConv::CC_C,
+    /*producesResult=*/false,
+    /*noCallerSavedRegs=*/false,
+    /*NoCfCheck=*/false,
+    /*cmseNSCall=*/false);
+  QualType func_type = ast.getFunctionNoProtoType(ast.VoidTy, generic_ext_info);
+  return GetType(func_type);
+}
 // Exploring the type
 
 const llvm::fltSemantics &
@@ -4824,7 +4846,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
 
   case clang::Type::FunctionNoProto:
   case clang::Type::FunctionProto:
-    break;
+    return lldb::eEncodingUint;
 
   case clang::Type::IncompleteArray:
   case clang::Type::VariableArray:

diff  --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 66e59ec985fb890..0ec2d026e996105 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -802,6 +802,10 @@ class TypeSystemClang : public TypeSystem {
   // Create related types using the current type's AST
   CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override;
 
+  // Create a generic function prototype that can be used in ValuObject types
+  // to correctly display a function pointer with the right value and summary.
+  CompilerType CreateGenericFunctionPrototype() override;
+
   // Exploring the type
 
   const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) override;

diff  --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index 006a843bacd2e12..54eeace93b96487 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -748,6 +748,10 @@ void TypeAndOrName::SetName(const char *type_name_cstr) {
   m_type_name.SetCString(type_name_cstr);
 }
 
+void TypeAndOrName::SetName(llvm::StringRef type_name) {
+  m_type_name.SetString(type_name);
+}
+
 void TypeAndOrName::SetTypeSP(lldb::TypeSP type_sp) {
   if (type_sp) {
     m_compiler_type = type_sp->GetForwardCompilerType();

diff  --git a/lldb/test/API/functionalities/vtable/Makefile b/lldb/test/API/functionalities/vtable/Makefile
new file mode 100644
index 000000000000000..99998b20bcb0502
--- /dev/null
+++ b/lldb/test/API/functionalities/vtable/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/functionalities/vtable/TestVTableValue.py b/lldb/test/API/functionalities/vtable/TestVTableValue.py
new file mode 100644
index 000000000000000..5b243e0646f4c28
--- /dev/null
+++ b/lldb/test/API/functionalities/vtable/TestVTableValue.py
@@ -0,0 +1,186 @@
+"""
+Make sure the getting a variable path works and doesn't crash.
+"""
+
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+class TestVTableValue(TestBase):
+    # If your test case doesn't stress debug info, then
+    # set this to true.  That way it won't be run once for
+    # each debug info format.
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipUnlessPlatform(["linux", "macosx"])
+    def test_vtable(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "At the end", lldb.SBFileSpec("main.cpp")
+        )
+
+        # Test a shape instance to make sure we get the vtable correctly.
+        shape = self.frame().FindVariable("shape")
+        vtable = shape.GetVTable()
+        self.assertEquals(vtable.GetName(), "vtable for Shape")
+        self.assertEquals(vtable.GetTypeName(), "vtable for Shape")
+        # Make sure we have the right number of virtual functions in our vtable
+        # for the shape class.
+        self.assertEquals(vtable.GetNumChildren(), 4)
+
+        # Verify vtable address
+        vtable_addr = vtable.GetValueAsUnsigned(0)
+        expected_addr = self.expected_vtable_addr(shape)
+        self.assertEquals(vtable_addr, expected_addr)
+
+        for (idx, vtable_entry) in enumerate(vtable.children):
+            self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+
+        # Test a shape reference to make sure we get the vtable correctly.
+        shape = self.frame().FindVariable("shape_ref")
+        vtable = shape.GetVTable()
+        self.assertEquals(vtable.GetName(), "vtable for Shape")
+        self.assertEquals(vtable.GetTypeName(), "vtable for Shape")
+        # Make sure we have the right number of virtual functions in our vtable
+        # for the shape class.
+        self.assertEquals(vtable.GetNumChildren(), 4)
+
+        # Verify vtable address
+        vtable_addr = vtable.GetValueAsUnsigned(0)
+        expected_addr = self.expected_vtable_addr(shape)
+        self.assertEquals(vtable_addr, expected_addr)
+
+        for (idx, vtable_entry) in enumerate(vtable.children):
+            self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+
+
+        # Test we get the right vtable for the Rectangle instance.
+        rect = self.frame().FindVariable("rect")
+        vtable = rect.GetVTable()
+        self.assertEquals(vtable.GetName(), "vtable for Rectangle")
+        self.assertEquals(vtable.GetTypeName(), "vtable for Rectangle")
+
+        # Make sure we have the right number of virtual functions in our vtable
+        # with the extra virtual function added by the Rectangle class
+        self.assertEquals(vtable.GetNumChildren(), 5)
+
+        # Verify vtable address
+        vtable_addr = vtable.GetValueAsUnsigned()
+        expected_addr = self.expected_vtable_addr(rect)
+        self.assertEquals(vtable_addr, expected_addr)
+
+        for (idx, vtable_entry) in enumerate(vtable.children):
+            self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
+
+    @skipUnlessPlatform(["linux", "macosx"])
+    def test_base_class_ptr(self):
+        self.build()
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "Shape is Rectangle", lldb.SBFileSpec("main.cpp")
+        )
+
+        shape = self.frame().FindVariable("shape")
+        rect = self.frame().FindVariable("rect")
+
+        shape_ptr = self.frame().FindVariable("shape_ptr")
+        shape_ptr_vtable = shape_ptr.GetVTable()
+        self.assertEquals(shape_ptr_vtable.GetName(), "vtable for Rectangle")
+        self.assertEquals(shape_ptr_vtable.GetNumChildren(), 5)
+        self.assertEquals(shape_ptr.GetValueAsUnsigned(0),
+                          rect.GetLoadAddress())
+        lldbutil.continue_to_source_breakpoint(
+            self, process, "Shape is Shape", lldb.SBFileSpec("main.cpp")
+        )
+        self.assertEquals(shape_ptr.GetValueAsUnsigned(0),
+                          shape.GetLoadAddress())
+        self.assertEquals(shape_ptr_vtable.GetNumChildren(), 4)
+        self.assertEquals(shape_ptr_vtable.GetName(), "vtable for Shape")
+
+    @skipUnlessPlatform(["linux", "macosx"])
+    def test_no_vtable(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "At the end", lldb.SBFileSpec("main.cpp")
+        )
+
+        var = self.frame().FindVariable("not_virtual")
+        self.assertEqual(var.GetVTable().GetError().GetCString(),
+                         'type "NotVirtual" doesn\'t have a vtable')
+
+        var = self.frame().FindVariable("argc")
+        self.assertEqual(var.GetVTable().GetError().GetCString(),
+                         'no language runtime support for the language "c"')
+
+    @skipUnlessPlatform(["linux", "macosx"])
+    def test_overwrite_vtable(self):
+        self.build()
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "At the end", lldb.SBFileSpec("main.cpp")
+        )
+
+        # Test a shape instance to make sure we get the vtable correctly.
+        shape = self.frame().FindVariable("shape")
+        vtable = shape.GetVTable()
+        self.assertEquals(vtable.GetName(), "vtable for Shape")
+        self.assertEquals(vtable.GetTypeName(), "vtable for Shape")
+        # Make sure we have the right number of virtual functions in our vtable
+        # for the shape class.
+        self.assertEquals(vtable.GetNumChildren(), 4)
+
+        # Overwrite the first entry in the vtable and make sure we can still
+        # see the bogus value which should have no summary
+        vtable_addr = vtable.GetValueAsUnsigned()
+        data = str("\x01\x01\x01\x01\x01\x01\x01\x01")
+        error = lldb.SBError()
+        process.WriteMemory(vtable_addr, data, error)
+
+        scribbled_child = vtable.GetChildAtIndex(0)
+        self.assertEquals(scribbled_child.GetValueAsUnsigned(0),
+                          0x0101010101010101)
+        self.assertEquals(scribbled_child.GetSummary(), None)
+
+    def expected_vtable_addr(self, var: lldb.SBValue) -> int:
+        load_addr = var.GetLoadAddress()
+        read_from_memory_error = lldb.SBError()
+        vtable_addr = self.process().ReadPointerFromMemory(
+            load_addr, read_from_memory_error
+        )
+        self.assertTrue(read_from_memory_error.Success())
+        return vtable_addr
+
+    def expected_vtable_entry_func_ptr(self, vtable_addr: int, idx: int):
+        vtable_entry_addr = vtable_addr + idx * self.process().GetAddressByteSize()
+        read_func_ptr_error = lldb.SBError()
+        func_ptr = self.process().ReadPointerFromMemory(vtable_entry_addr,
+                                                        read_func_ptr_error)
+        self.assertTrue(read_func_ptr_error.Success())
+        return func_ptr
+
+    def verify_vtable_entry(self, vtable_entry: lldb.SBValue, vtable_addr: int,
+                            idx: int):
+        """Verify the vtable entry looks something like:
+
+        (double ()) [0] = 0x0000000100003a10 a.out`Rectangle::Area() at main.cpp:14
+
+        """
+        # Check function ptr
+        vtable_entry_func_ptr = vtable_entry.GetValueAsUnsigned(0)
+        self.assertEquals(
+            vtable_entry_func_ptr,
+            self.expected_vtable_entry_func_ptr(vtable_addr, idx),
+        )
+
+        sb_addr = self.target().ResolveLoadAddress(vtable_entry_func_ptr)
+        sym_ctx = sb_addr.GetSymbolContext(lldb.eSymbolContextEverything)
+
+        # Make sure the type is the same as the function type
+        func_type = sym_ctx.GetFunction().GetType()
+        if func_type.IsValid():
+            self.assertEquals(vtable_entry.GetType(),
+                              func_type.GetPointerType())
+
+        # The summary should be the address description of the function pointer
+        summary = vtable_entry.GetSummary()
+        self.assertEquals(str(sb_addr), summary)

diff  --git a/lldb/test/API/functionalities/vtable/main.cpp b/lldb/test/API/functionalities/vtable/main.cpp
new file mode 100644
index 000000000000000..498a5765a3f6ff7
--- /dev/null
+++ b/lldb/test/API/functionalities/vtable/main.cpp
@@ -0,0 +1,38 @@
+class Shape {
+public:
+  virtual double Area() { return 1.0; }
+  virtual double Perimeter() { return 1.0; }
+  // Note that destructors generate two entries in the vtable: base object
+  // destructor and deleting destructor.
+  virtual ~Shape() = default;
+};
+
+class Rectangle : public Shape {
+public:
+  ~Rectangle() override = default;
+  double Area() override { return 2.0; }
+  double Perimeter() override { return 2.0; }
+  virtual void RectangleOnly() {}
+  // This *shouldn't* show up in the vtable.
+  void RectangleSpecific() { return; }
+};
+
+// Make a class that looks like it would be virtual because the first ivar is
+// a virtual class and if we inspect memory at the address of this class it
+// would appear to be a virtual class. We need to make sure we don't get a
+// valid vtable from this object.
+class NotVirtual {
+  Rectangle m_rect;
+public:
+  NotVirtual() = default;
+};
+
+int main(int argc, const char **argv) {
+  Shape shape;
+  Rectangle rect;
+  Shape *shape_ptr = ▭
+  Shape &shape_ref = shape;
+  shape_ptr = &shape; // Shape is Rectangle
+  NotVirtual not_virtual; // Shape is Shape
+  return 0; // At the end
+}


        


More information about the lldb-commits mailing list