llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: David Mentler (mentlerd)

<details>
<summary>Changes</summary>

`ItaniumABIRuntime::GetTypeInfo(...)`'s type resolution relies on the demangler 
to locate the type corresponding to a vtable address. As reported in #<!-- 
-->182762 this can break when the demangler and debug info don't agree on the 
spelling of the type's name.

This PR implements an opportunistic lookup based on a support member emitted by 
clang added in #<!-- -->130255 as `_vtable$`, later renamed to `__clang_vtable` 
in #<!-- -->183617. If it fails we fall back to the demangler route.

The initial implementation aims to only support the modern spelling with DWARF.

---
Full diff: https://github.com/llvm/llvm-project/pull/210584.diff


12 Files Affected:

- (modified) lldb/include/lldb/Symbol/SymbolFile.h (+6) 
- (modified) lldb/include/lldb/Symbol/SymbolFileOnDemand.h (+4) 
- (modified) lldb/include/lldb/Symbol/Variable.h (+2) 
- (modified) 
lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp (+58) 
- (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (+14) 
- (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h (+2) 
- (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp 
(+12) 
- (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h 
(+2) 
- (modified) lldb/source/Symbol/Variable.cpp (+7) 
- (added) lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile (+3) 
- (added) 
lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
 (+35) 
- (added) lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp (+34) 


``````````diff
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h 
b/lldb/include/lldb/Symbol/SymbolFile.h
index ae6504c016d7b..b49c5ae230062 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -537,6 +537,12 @@ class SymbolFile : public PluginInterface {
 
   std::string GetObjectName() const;
 
+  /// Get the semantically innermost non-function type that encloses the
+  /// provided variable
+  virtual lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) {
+    return {};
+  }
+
 protected:
   void AssertModuleLock();
 
diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h 
b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
index 46e33189d721b..9a2e0bc03f56f 100644
--- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
+++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
@@ -253,6 +253,10 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile 
{
     return m_sym_file_impl->CopyType(other_type);
   }
 
+  lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) override {
+    return m_sym_file_impl->GetTypeEnclosingVariableUID(uid);
+  }
+
 private:
   Log *GetLog() const { return ::lldb_private::GetLog(LLDBLog::OnDemand); }
 
diff --git a/lldb/include/lldb/Symbol/Variable.h 
b/lldb/include/lldb/Symbol/Variable.h
index 1e6a36d584212..97b6750797202 100644
--- a/lldb/include/lldb/Symbol/Variable.h
+++ b/lldb/include/lldb/Symbol/Variable.h
@@ -62,6 +62,8 @@ class Variable : public UserID, public 
std::enable_shared_from_this<Variable> {
 
   Type *GetType();
 
+  lldb::TypeSP GetEnclosingType();
+
   lldb::LanguageType GetLanguage() const;
 
   lldb::ValueType GetScope() const { return m_scope; }
diff --git 
a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp 
b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
index 4d9cf31c8904d..de61e2a61102f 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABIRuntime.cpp
@@ -14,6 +14,9 @@
 #include "lldb/Expression/FunctionCaller.h"
 #include "lldb/Utility/LLDBLog.h"
 
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
+
 using namespace lldb;
 using namespace lldb_private;
 
@@ -39,6 +42,61 @@ ItaniumABIRuntime::GetTypeInfo(ValueObject &in_value,
                 ": static-type = '%s' has vtable symbol '%s'\n",
                 in_value.GetPointerValue().address,
                 in_value.GetTypeName().GetCString(), 
symbol_name.str().c_str());
+
+      // clang emits DW_TAG_variable '__clang_vtable' inside the enclosing
+      // class, try looking for that before falling back to name based type
+      // lookup
+      if (ModuleSP module_sp =
+              vtable_info.symbol->CalculateSymbolContextModule()) {
+        auto vptr = vtable_info.addr.GetLoadAddress(&m_process->GetTarget());
+
+        // NOTE: vptr points at the 'address point', not the vtable itself
+        auto vtable = vptr - m_process->GetAddressByteSize() * 2;
+
+        VariableList vars;
+
+        // SymbolFile doesn't provide an API to lookup a global variable with a
+        // specific location, so we have to resort to an unbounded name lookup
+        module_sp->FindGlobalVariables(ConstString("__clang_vtable"),
+                                       CompilerDeclContext(), -1, vars);
+
+        LLDB_LOGF(log,
+                  "0x%16.16" PRIx64 ": found %zu __clang_vtable variables\n",
+                  in_value.GetPointerValue().address, vars.GetSize());
+
+        for (lldb::VariableSP var : vars) {
+          auto valobj = ValueObjectVariable::Create(m_process, var);
+          if (valobj->GetLoadAddress() != vtable)
+            continue;
+
+          auto type_sp = var->GetEnclosingType();
+          if (!type_sp)
+            continue;
+
+          if (!TypeSystemClang::IsCXXClassType(
+                  type_sp->GetForwardCompilerType()))
+            continue;
+
+          LLDB_LOGF(log,
+                    "0x%16.16" PRIx64
+                    ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
+                    "}, type-name='%s'\n",
+                    in_value.GetPointerValue().address,
+                    in_value.GetTypeName().AsCString(""), type_sp->GetID(),
+                    type_sp->GetName().GetCString());
+
+          type_info.SetTypeSP(type_sp);
+
+          SetDynamicTypeInfo(vtable_info.addr, type_info);
+          return type_info;
+        }
+
+        LLDB_LOGF(log,
+                  "0x%16.16" PRIx64 ": __clang_vtable based lookup failed, "
+                                    "falling back to demangled typename\n",
+                  in_value.GetPointerValue().address);
+      }
+
       // We are a C++ class, that's good.  Get the class name and look it
       // up:
       llvm::StringRef class_name = symbol_name;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 8075c8f61193c..bbc1668cfed36 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -2336,6 +2336,11 @@ void SymbolFileDWARF::FindGlobalVariables(
   bool name_is_mangled = Mangled::GetManglingScheme(name.GetStringRef()) !=
                          Mangled::eManglingSchemeNone;
 
+  // Technically not a mangled name, but a support variable emitted by clang.
+  // Regardless, we need an exact lookup
+  if (name == "__clang_vtable")
+    name_is_mangled = true;
+
   if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetStringRef(),
                                                       context, basename))
     basename = name.GetStringRef();
@@ -4734,3 +4739,12 @@ DWOStats SymbolFileDWARF::GetDwoStats() {
 
   return stats;
 }
+
+lldb::TypeSP SymbolFileDWARF::GetTypeEnclosingVariableUID(lldb::user_id_t uid) 
{
+  DWARFDIE die = GetDIE(uid);
+
+  if (die.Tag() != DW_TAG_variable)
+    return nullptr;
+
+  return GetTypeForDIE(die.GetParentDeclContextDIE());
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 21a190d1b38f0..a87428fb86743 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -378,6 +378,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// Returns the DWARFIndex for this symbol, if it exists.
   DWARFIndex *getIndex() { return m_index.get(); }
 
+  lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) override;
+
 private:
   /// Find the definition DIE for the specified \c label in this
   /// SymbolFile.
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 219ab7bbf84cc..24aeae71e365e 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1642,6 +1642,18 @@ void SymbolFileDWARFDebugMap::GetCompileOptions(
   });
 }
 
+lldb::TypeSP
+SymbolFileDWARFDebugMap::GetTypeEnclosingVariableUID(lldb::user_id_t uid) {
+  lldb::TypeSP type;
+  ForEachSymbolFile("Looking up enclosing type for variable",
+                    [&](SymbolFileDWARF &oso_dwarf) {
+                      type = oso_dwarf.GetTypeEnclosingVariableUID(uid);
+                      return type ? IterationAction::Stop
+                                  : IterationAction::Continue;
+                    });
+  return type;
+}
+
 llvm::Expected<SymbolContext>
 SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) {
   const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id);
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index 48b80974882b3..39141471cc40f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -147,6 +147,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   void
   GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
 
+  lldb::TypeSP GetTypeEnclosingVariableUID(lldb::user_id_t uid) override;
+
   llvm::Expected<SymbolContext>
   ResolveFunctionCallLabel(FunctionCallLabel &label) override;
 
diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp
index 70a61fc7789c9..7bf7fcdd9c3b1 100644
--- a/lldb/source/Symbol/Variable.cpp
+++ b/lldb/source/Symbol/Variable.cpp
@@ -110,6 +110,13 @@ Type *Variable::GetType() {
   return nullptr;
 }
 
+lldb::TypeSP Variable::GetEnclosingType() {
+  Type *type = GetType();
+  if (type)
+    return type->GetSymbolFile()->GetTypeEnclosingVariableUID(GetID());
+  return nullptr;
+}
+
 void Variable::Dump(Stream *s, bool show_context) const {
   s->Printf("%p: ", static_cast<const void *>(this));
   s->Indent();
diff --git a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git 
a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
new file mode 100644
index 0000000000000..fedbb9dce27de
--- /dev/null
+++ 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/TestDynamicValueComplexTypename.py
@@ -0,0 +1,35 @@
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+
+
+class DynamicValueComplexTypename(TestBase):
+
+    @skipIf(compiler=no_match("clang"))
+    @skipIf(compiler="clang", compiler_version=["<", "24.0"])  # __clang_vtable
+    @expectedFailureAll(debug_info=no_match(["dwarf", "dsym"]))  # Not 
implemented
+    @expectedFailureAll(oslist=["windows"])  # Not Itanium ABI
+    def test(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
+
+        self.checkVarType("simple", "Simple")
+        self.checkVarType("templated", "Templated<")
+        self.checkVarType("complicated", "Complicated<")
+        self.checkVarType("evil", "Evil<")
+
+    def checkVarType(self, name, type_prefix):
+        var = self.frame().FindVariable(name)
+        self.assertTrue(var.IsValid())
+
+        # Check if we recognized the dynamic type. Most names are unreasonably
+        # complex to match against exactly, but all should have a sane prefix
+        self.assertStartsWith(var.GetType().GetName(), type_prefix)
+
+        # Check if we resolved the compiler type too
+        marker = var.Dereference().GetChildAtIndex(0)
+        self.assertEqual(marker.GetName(), f"is_{name}")
+        self.assertEqual(marker.GetValueAsUnsigned(), 1)
diff --git a/lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp 
b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp
new file mode 100644
index 0000000000000..840242391f84f
--- /dev/null
+++ b/lldb/test/API/lang/cpp/dynamic-value-complex-typename/main.cpp
@@ -0,0 +1,34 @@
+
+struct Interface {
+  virtual int Foo() { return 0; }
+};
+
+struct Simple : public Interface {
+  bool is_simple = true;
+};
+
+template <typename A, typename B> struct Templated : public Interface {
+  bool is_templated = true;
+};
+
+template <typename A, int B> struct Complicated : public Interface {
+  bool is_complicated = true;
+};
+
+template <typename A> struct Evil : public Interface {
+  bool is_evil = true;
+};
+
+namespace {
+template <typename A, typename B> struct Pair {};
+} // namespace
+
+int main() {
+  auto lambda = [] {};
+
+  Interface *simple = new Simple();
+  Interface *templated = new Templated<int, double>();
+  Interface *complicated = new Complicated<Pair<Interface, int>, 42>();
+  Interface *evil = new Evil<decltype(lambda)>();
+  return 0; // break here
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/210584
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to