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
