https://github.com/mentlerd updated https://github.com/llvm/llvm-project/pull/111892
>From b1e6178b1130135262884d99262716fcc0ada86e Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Mon, 7 Oct 2024 21:46:50 +0200 Subject: [PATCH 1/8] Make existing tests break --- .../data-formatter-stl/libcxx/function/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp index ef7c97470652fc..b17aba3fbbc70e 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp @@ -1,5 +1,10 @@ #include <functional> +template<typename = bool, typename = int> +struct Dummy { + // Used to make lambda host function's symbol more complex +}; + int foo(int x, int y) { return x + y - 1; } @@ -18,7 +23,7 @@ struct Bar { } } ; -int foo2() { +int foo2(Dummy<> dummy = {}) { auto f = [](int x) { return x+1; }; >From d976756f47781d58c0367835a56b5b8e9130191b Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Wed, 9 Oct 2024 20:40:03 +0200 Subject: [PATCH 2/8] Expose conversion from DeclContext to Decl --- lldb/include/lldb/Symbol/CompilerDeclContext.h | 2 ++ lldb/include/lldb/Symbol/TypeSystem.h | 4 ++++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 7 +++++++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h | 2 ++ lldb/source/Symbol/CompilerDeclContext.cpp | 6 ++++++ 5 files changed, 21 insertions(+) diff --git a/lldb/include/lldb/Symbol/CompilerDeclContext.h b/lldb/include/lldb/Symbol/CompilerDeclContext.h index 89b4a9787688bc..3954ddd8e52087 100644 --- a/lldb/include/lldb/Symbol/CompilerDeclContext.h +++ b/lldb/include/lldb/Symbol/CompilerDeclContext.h @@ -110,6 +110,8 @@ class CompilerDeclContext { ConstString GetScopeQualifiedName() const; + CompilerDecl GetDecl() const; + private: TypeSystem *m_type_system = nullptr; void *m_opaque_decl_ctx = nullptr; diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 7d48f9b316138c..4b531a83dd5fbb 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -134,6 +134,10 @@ class TypeSystem : public PluginInterface, virtual lldb::LanguageType DeclContextGetLanguage(void *opaque_decl_ctx) = 0; + virtual CompilerDecl DeclContextGetDecl(void *opaque_decl_ctx) { + return CompilerDecl(); + } + /// Returns the direct parent context of specified type virtual CompilerDeclContext GetCompilerDeclContextForType(const CompilerType &type); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index fe0c53a7e9a3ea..678ac8d65331b7 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -9595,6 +9595,13 @@ TypeSystemClang::DeclContextGetLanguage(void *opaque_decl_ctx) { return eLanguageTypeUnknown; } +CompilerDecl TypeSystemClang::DeclContextGetDecl(void *opaque_decl_ctx) { + if (auto *decl_ctx = (clang::DeclContext *)opaque_decl_ctx) + if (auto* decl = dyn_cast_or_null<clang::Decl>(decl_ctx)) + return CompilerDecl(this, decl); + return CompilerDecl(); +} + static bool IsClangDeclContext(const CompilerDeclContext &dc) { return dc.IsValid() && isa<TypeSystemClang>(dc.GetTypeSystem()); } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index e39aedec7e3902..e7ac6e320f20b7 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -595,6 +595,8 @@ class TypeSystemClang : public TypeSystem { lldb::LanguageType DeclContextGetLanguage(void *opaque_decl_ctx) override; + CompilerDecl DeclContextGetDecl(void *opaque_decl_ctx) override; + std::vector<lldb_private::CompilerContext> DeclContextGetCompilerContext(void *opaque_decl_ctx) override; diff --git a/lldb/source/Symbol/CompilerDeclContext.cpp b/lldb/source/Symbol/CompilerDeclContext.cpp index b40a08e9b1953d..7a72c4301b0b5c 100644 --- a/lldb/source/Symbol/CompilerDeclContext.cpp +++ b/lldb/source/Symbol/CompilerDeclContext.cpp @@ -34,6 +34,12 @@ ConstString CompilerDeclContext::GetScopeQualifiedName() const { return ConstString(); } +CompilerDecl CompilerDeclContext::GetDecl() const { + if (IsValid()) + return m_type_system->DeclContextGetDecl(m_opaque_decl_ctx); + return CompilerDecl(); +} + bool CompilerDeclContext::IsClassMethod() { if (IsValid()) return m_type_system->DeclContextIsClassMethod(m_opaque_decl_ctx); >From 2e75bb0aac608d626fba5878315e9118bfcf9c8a Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Wed, 9 Oct 2024 20:41:04 +0200 Subject: [PATCH 3/8] IsPolymorphicClass already checks for record types and handles pointers to typenames --- .../CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp index 4c547afe30fe81..4ac1e565cb2619 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -191,14 +191,6 @@ llvm::Error ItaniumABILanguageRuntime::TypeHasVTable(CompilerType 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, >From 248be8c68221edc03ee0691b3b2c83eda0789324 Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Wed, 9 Oct 2024 22:23:48 +0200 Subject: [PATCH 4/8] Digging for wrapped callable works --- .../CPlusPlus/CPPLanguageRuntime.cpp | 132 +++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index faa05e8f834ea1..7167cd4dc75f90 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -174,6 +174,137 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( if (!valobj_sp) return optional_info; + // std::function has many variants, try to disambiguate + ValueObjectSP func_as_base_ptr; + { + ValueObjectSP outer_f = valobj_sp->GetChildMemberWithName("__f_"); + + if (!outer_f) + return optional_info; // Unrecognized implementation + + if (outer_f->IsPointerType()) { + // git: 3e519524c118651123eecf60c2bbc5d65ad9bac3 + // + // class function<_Rp()> { + // aligned_storage<3*sizeof(void*)>::type __buf_; + // __base<_Rp>* __f_; + // } + + func_as_base_ptr = std::move(outer_f); + } else if (auto inner_f = outer_f->GetChildMemberWithName("__f_")) { + // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394 + // + // class function<_Rp(_ArgTypes...)> { + // __value_func<_Rp(_ArgTypes...)> __f_; + // } + // + // class __value_func<_Rp(_ArgTypes...)> { + // aligned_storage<3 * sizeof(void*)>::type __buf_; + // __base<_Rp(_ArgTypes...)>* __f_; + // } + + func_as_base_ptr = std::move(inner_f); + } else + return optional_info; // Unrecognized implementation + } + + // __base<...> is a pure virtual class with an interface to create/copy/destroy/invoke + // the underlying value. This interface is implemented by partial specializations of the + // __func<_Fp, _Alloc, ...> template where _Fp is the wrapped functor object + Status status; + ValueObjectSP func_as_base = func_as_base_ptr->Dereference(status); + if (status.Fail()) + return optional_info; + + // First we'll try to extract the __func<...> template instantiation's type by looking up + // the declarations of the member function pointers in it's vtable + CompilerType func_type; + { + ValueObjectSP vtable = func_as_base->GetVTable(); + + llvm::Expected<uint32_t> num_entries = vtable->GetNumChildren(); + if (num_entries.takeError()) + return optional_info; + + // __base is pure virtual, __func is final. All member function pointers are equally + // good candidates to find the enclosing class. + // + // In practice the first two vtable entries point to artificial destructors which the + // type system refuses to elaborate as their artificial specifications are not added + // to the enclosing class' declaration context. This causes various warnings, and dont + // get us any closer to the concrete type thus we skip them. + for (uint32_t idx = 2; idx < *num_entries; idx++) { + ValueObjectSP entry = vtable->GetChildAtIndex(idx); + + // Points to a potentially interesting member function + addr_t mfunc_load_addr = entry->GetValueAsUnsigned(0); + if (!mfunc_load_addr) + continue; + + Address mfunc_symbol_addr; + if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, mfunc_symbol_addr)) + continue; + + Function* func = mfunc_symbol_addr.CalculateSymbolContextFunction(); + if (!func) + continue; + + CompilerDeclContext mfunc_decl_ctx = func->GetDeclContext(); + if (!mfunc_decl_ctx.IsClassMethod()) + continue; + + // Member functions are contained in their enclosing class' decl context + CompilerDeclContext mfunc_parent = mfunc_decl_ctx.GetDecl().GetDeclContext(); + if (!mfunc_parent.IsValid()) + continue; + + func_type = mfunc_parent.GetDecl().GetType(); + break; + } + } + + // Regardless of what std::function wraps we are looking for the load address of a function to call + std::optional<addr_t> target_func_load_addr; + + if (CompilerType callable_type = func_type.GetTypeTemplateArgument(0)) { + if (callable_type.IsFunctionPointerType() || callable_type.IsMemberFunctionPointerType()) { + // TODO: The previous implementation just does raw pointer arithmetic and reads + // 'a pointer' to a function right after the vtable. + // + // What is the preferred approach? Go digging for the compressed_pair.first in __func + // or assume layout citing ABI compatibility requirements? + } else if (callable_type.IsRecordType()) { + // Target is a lambda, or a generic callable. Search for a single operator() overload + std::optional<ConstString> mangled_func_name; + + for (uint32_t idx = 0; idx < callable_type.GetNumMemberFunctions(); idx++) { + TypeMemberFunctionImpl mfunc = callable_type.GetMemberFunctionAtIndex(idx); + + if (mfunc.GetKind() != eMemberFunctionKindInstanceMethod) + continue; + + if (mfunc.GetName() != "operator()") + continue; + + if (mangled_func_name) + return optional_info; // Cannot resolve ambiguous target + + mangled_func_name = mfunc.GetMangledName(); + } + + // TODO: The SymbolFile did a bunch of work to reconstruct `callable_type`, + // including it's member functions. Surely it knows there they are loaded? + } + } else { + // TODO: What if we don't have debug info for callable_type? Do we fallback to + // treating the std::function as wrapping a function/member function pointer + // due to lack of options, or give up to avoid guessing wrong? + } + + if (!target_func_load_addr) + return optional_info; + + // Member __f_ has type __base*, the contents of which will hold: // 1) a vtable entry which may hold type information needed to discover the // lambda being called @@ -232,7 +363,6 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( return optional_info; uint32_t address_size = process->GetAddressByteSize(); - Status status; // First item pointed to by __f_ should be the pointer to the vtable for // a __base object. >From e7612f5c6417ababebfb7509d1dba697f07250db Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Thu, 10 Oct 2024 23:08:01 +0200 Subject: [PATCH 5/8] RecordType callable identification works, more questions --- .../CPlusPlus/CPPLanguageRuntime.cpp | 113 ++++++++++++------ 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 7167cd4dc75f90..a3c719528e4e4b 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -219,6 +219,7 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( // First we'll try to extract the __func<...> template instantiation's type by looking up // the declarations of the member function pointers in it's vtable CompilerType func_type; + Address func_method_addr; { ValueObjectSP vtable = func_as_base->GetVTable(); @@ -241,11 +242,10 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( if (!mfunc_load_addr) continue; - Address mfunc_symbol_addr; - if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, mfunc_symbol_addr)) + if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, func_method_addr)) continue; - Function* func = mfunc_symbol_addr.CalculateSymbolContextFunction(); + Function* func = func_method_addr.CalculateSymbolContextFunction(); if (!func) continue; @@ -263,47 +263,86 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( } } - // Regardless of what std::function wraps we are looking for the load address of a function to call - std::optional<addr_t> target_func_load_addr; - - if (CompilerType callable_type = func_type.GetTypeTemplateArgument(0)) { - if (callable_type.IsFunctionPointerType() || callable_type.IsMemberFunctionPointerType()) { - // TODO: The previous implementation just does raw pointer arithmetic and reads - // 'a pointer' to a function right after the vtable. - // - // What is the preferred approach? Go digging for the compressed_pair.first in __func - // or assume layout citing ABI compatibility requirements? - } else if (callable_type.IsRecordType()) { - // Target is a lambda, or a generic callable. Search for a single operator() overload - std::optional<ConstString> mangled_func_name; + CompilerType callable_type = func_type.GetTypeTemplateArgument(0); + if (!callable_type) + return optional_info; + + if (callable_type.IsFunctionPointerType() || callable_type.IsMemberFunctionPointerType()) { + // TODO: The previous implementation just does raw pointer arithmetic and reads + // 'a pointer' to a function right after the vtable. + // + // What is the preferred approach? Go digging for the compressed_pair.first in __func + // or assume layout citing ABI compatibility requirements? + } else if (callable_type.IsRecordType()) { + // Target is a lambda, or a generic callable. Search for a single operator() overload + std::optional<ConstString> mangled_func_name; + + // TODO: I am still not sure whether it is a good idea to reconstruct the full type + // here.. it seems there are handy FindFunctions that could perhaps to a good job + // at locating candidates. However even when limiting the search to the decl_ctx of + // the class the code seems to iterate over way more DIEs than I expected. What to do? + + // TODO: Because we have access to the type we know a _lot_ about callable_type, we + // could even extract a ValueObjectSP to it if we wanted. It would be cool to make + // std::function have a synt children provider showing the wrapped lambda/callable! + + for (uint32_t idx = 0; idx < callable_type.GetNumMemberFunctions(); idx++) { + TypeMemberFunctionImpl mfunc = callable_type.GetMemberFunctionAtIndex(idx); - for (uint32_t idx = 0; idx < callable_type.GetNumMemberFunctions(); idx++) { - TypeMemberFunctionImpl mfunc = callable_type.GetMemberFunctionAtIndex(idx); - - if (mfunc.GetKind() != eMemberFunctionKindInstanceMethod) - continue; - - if (mfunc.GetName() != "operator()") - continue; - - if (mangled_func_name) - return optional_info; // Cannot resolve ambiguous target + if (mfunc.GetKind() != eMemberFunctionKindInstanceMethod) + continue; + + if (mfunc.GetName() != "operator()") + continue; - mangled_func_name = mfunc.GetMangledName(); - } + if (mangled_func_name) + return optional_info; // Cannot resolve ambiguous target + + mangled_func_name = mfunc.GetMangledName(); + } + + // Locate the symbol context corresponding to the target function + SymbolContext sc; + { + // We'll assume that callable_type is in the same module as the vtable + ModuleSP mod = func_method_addr.CalculateSymbolContextModule(); + + // Limit our lookup to callable_type + CompilerDeclContext decl_ctx = callable_type.GetTypeSystem()->GetCompilerDeclContextForType(callable_type); - // TODO: The SymbolFile did a bunch of work to reconstruct `callable_type`, - // including it's member functions. Surely it knows there they are loaded? + SymbolContextList list; + mod->FindFunctions(*mangled_func_name, decl_ctx, eFunctionNameTypeFull, {}, list); + + if (list.GetSize() != 1) + return optional_info; + + list.GetContextAtIndex(0, sc); } - } else { - // TODO: What if we don't have debug info for callable_type? Do we fallback to - // treating the std::function as wrapping a function/member function pointer - // due to lack of options, or give up to avoid guessing wrong? + + // TODO: This feels a bit clunky, I am probably misusing the API? FindFunctions returns me + // SymbolContexts with the .function set but not .symbol ... At first glance it seemed like + // if we know the function there must be a symbol too! + if (!sc.function) + return optional_info; + + Symbol* symbol = sc.function->GetAddressRange().GetBaseAddress().CalculateSymbolContextSymbol(); + if (!symbol) + return optional_info; + + return LibCppStdFunctionCallableInfo { + .callable_symbol = *symbol, + .callable_address = symbol->GetAddress(), + .callable_line_entry = sc.GetFunctionStartLineEntry(), + + // TODO: Can't tell lambdas apart from generic callables.. do we really need to? + // Is it important to have the correct qualification in the summary? + .callable_case = LibCppStdFunctionCallableCase::Lambda + }; } - if (!target_func_load_addr) + // Unrecognized callable type - skip the original implementation for now + if (!callable_type.IsVoidType()) return optional_info; - // Member __f_ has type __base*, the contents of which will hold: // 1) a vtable entry which may hold type information needed to discover the >From a6b489d95cf4be1e779c7aa5092f4e2dd75f239d Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Thu, 10 Oct 2024 23:48:23 +0200 Subject: [PATCH 6/8] Accessing template type arguments fully completes __func, enumerating members is free --- .../Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index a3c719528e4e4b..019f4fa67e1722 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -277,11 +277,6 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( // Target is a lambda, or a generic callable. Search for a single operator() overload std::optional<ConstString> mangled_func_name; - // TODO: I am still not sure whether it is a good idea to reconstruct the full type - // here.. it seems there are handy FindFunctions that could perhaps to a good job - // at locating candidates. However even when limiting the search to the decl_ctx of - // the class the code seems to iterate over way more DIEs than I expected. What to do? - // TODO: Because we have access to the type we know a _lot_ about callable_type, we // could even extract a ValueObjectSP to it if we wanted. It would be cool to make // std::function have a synt children provider showing the wrapped lambda/callable! >From 2cce9366dc5e15e41539a198e472846e012118f5 Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Fri, 11 Oct 2024 00:58:39 +0200 Subject: [PATCH 7/8] Extract callable ValueObject, symbolicate raw function pointers --- .../CPlusPlus/CPPLanguageRuntime.cpp | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 019f4fa67e1722..1837fb79f6c5d3 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -267,20 +267,82 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( if (!callable_type) return optional_info; - if (callable_type.IsFunctionPointerType() || callable_type.IsMemberFunctionPointerType()) { - // TODO: The previous implementation just does raw pointer arithmetic and reads - // 'a pointer' to a function right after the vtable. + // Now that the __func is a known type we can dig for the wrapped callable + ValueObjectSP callable; + { + // class __func<_Fp, _Alloc, _Rp(_ArgTypes...)> : __base<_Rp(_ArgTypes...)> { + // __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_; + // } + // + // class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> { + // __compressed_pair<_Fp, _Ap> __f_; + // } // - // What is the preferred approach? Go digging for the compressed_pair.first in __func - // or assume layout citing ABI compatibility requirements? + // class __compressed_pair : __compressed_pair_elem<_T1, 0>, + // __compressed_pair_elem<_T2, 1> { + // } + // + // struct __compressed_pair_elem { + // _Tp __value_; + // } + ValueObjectSP alloc_func = func_as_base->Cast(func_type); + if (!alloc_func) + return optional_info; + + ValueObjectSP pair = alloc_func->GetChildAtNamePath({"__f_", "__f_"}); + if (!pair) + return optional_info; + + if (callable_type.IsRecordType() && callable_type.GetNumFields() == 0) { + // callable_type is an empty class, and has been optimized away! Serve a dummy + callable = valobj_sp->CreateValueObjectFromAddress("__value_", + pair->GetLoadAddress(), + pair->GetExecutionContextRef(), + callable_type); + } else { + ValueObjectSP elem0 = pair->GetChildAtIndex(0); + if (!elem0) + return optional_info; + + callable = elem0->GetChildMemberWithName("__value_"); + if (!callable) + return optional_info; + } + } + + if (callable_type.IsFunctionPointerType()) { + addr_t target_load_addr = callable->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + ModuleSP mod = func_method_addr.CalculateSymbolContextModule(); + + Address callable_addr; + if (!mod->ResolveFileAddress(target_load_addr, callable_addr)) + return optional_info; + + SymbolContext sc; + mod->ResolveSymbolContextForAddress(callable_addr, eSymbolContextSymbol | eSymbolContextLineEntry, sc); + + if (!sc.symbol) + return optional_info; + + return LibCppStdFunctionCallableInfo { + .callable_symbol = *sc.symbol, + .callable_address = sc.symbol->GetAddress(), + .callable_line_entry = sc.line_entry, + .callable_case = LibCppStdFunctionCallableCase::FreeOrMemberFunction + }; + } else if (callable_type.IsMemberFunctionPointerType()) { + // TODO: Member function's unsigned value comes back as invalid! I am guessing + // the ValueObject wants to let me know that this is not necessarily as simple + // as that.. I remember reading something in the Itanium ABI about member + // pointers taking up two pointers of space. Perhaps that's why it is not legal + // to read their underlying value raw? + + printf("Curious!"); } else if (callable_type.IsRecordType()) { // Target is a lambda, or a generic callable. Search for a single operator() overload std::optional<ConstString> mangled_func_name; - // TODO: Because we have access to the type we know a _lot_ about callable_type, we - // could even extract a ValueObjectSP to it if we wanted. It would be cool to make - // std::function have a synt children provider showing the wrapped lambda/callable! - for (uint32_t idx = 0; idx < callable_type.GetNumMemberFunctions(); idx++) { TypeMemberFunctionImpl mfunc = callable_type.GetMemberFunctionAtIndex(idx); >From a26c7221c943d102741da9c89871bd02ea888cbd Mon Sep 17 00:00:00 2001 From: David Mentler <david.ment...@shapr3d.com> Date: Fri, 11 Oct 2024 22:59:52 +0200 Subject: [PATCH 8/8] Cleanup pass 1 --- .../CPlusPlus/CPPLanguageRuntime.cpp | 594 ++++++------------ .../CPlusPlus/CPPLanguageRuntime.h | 15 +- 2 files changed, 207 insertions(+), 402 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 1837fb79f6c5d3..390efda6271443 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -125,115 +125,104 @@ CPPLanguageRuntime::GetObjectDescription(Stream &str, Value &value, return llvm::createStringError("C++ does not support object descriptions"); } -bool contains_lambda_identifier(llvm::StringRef &str_ref) { - return str_ref.contains("$_") || str_ref.contains("'lambda'"); -} - -CPPLanguageRuntime::LibCppStdFunctionCallableInfo -line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol, - llvm::StringRef first_template_param_sref, bool has_invoke) { - - CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info; - - AddressRange range; - sc.GetAddressRange(eSymbolContextEverything, 0, false, range); - - Address address = range.GetBaseAddress(); - - Address addr; - if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target), - addr)) { - LineEntry line_entry; - addr.CalculateSymbolContextLineEntry(line_entry); - - if (contains_lambda_identifier(first_template_param_sref) || has_invoke) { - // Case 1 and 2 - optional_info.callable_case = lldb_private::CPPLanguageRuntime:: - LibCppStdFunctionCallableCase::Lambda; - } else { - // Case 3 - optional_info.callable_case = lldb_private::CPPLanguageRuntime:: - LibCppStdFunctionCallableCase::CallableObject; - } - - optional_info.callable_symbol = *symbol; - optional_info.callable_line_entry = line_entry; - optional_info.callable_address = addr; - } - - return optional_info; -} - -CPPLanguageRuntime::LibCppStdFunctionCallableInfo -CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( - lldb::ValueObjectSP &valobj_sp) { - LLDB_SCOPED_TIMER(); - - LibCppStdFunctionCallableInfo optional_info; - +llvm::Expected<CPPLanguageRuntime::UnwrappedLibCppFunction> +CPPLanguageRuntime::UnwrapLibCppFunction(lldb::ValueObjectSP &valobj_sp) { if (!valobj_sp) - return optional_info; - - // std::function has many variants, try to disambiguate - ValueObjectSP func_as_base_ptr; + return llvm::createStringError("valobj in invalid state"); + + // std::function has many variants, try to disambiguate going from most + // recent to oldest known implementation + ValueObjectSP base_ptr; { ValueObjectSP outer_f = valobj_sp->GetChildMemberWithName("__f_"); - - if (!outer_f) - return optional_info; // Unrecognized implementation - - if (outer_f->IsPointerType()) { - // git: 3e519524c118651123eecf60c2bbc5d65ad9bac3 - // - // class function<_Rp()> { - // aligned_storage<3*sizeof(void*)>::type __buf_; - // __base<_Rp>* __f_; - // } - - func_as_base_ptr = std::move(outer_f); - } else if (auto inner_f = outer_f->GetChildMemberWithName("__f_")) { - // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394 + + if (!base_ptr) { + // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394 __functional/function.h // + // template<class _Rp, class ..._ArgTypes> // class function<_Rp(_ArgTypes...)> { - // __value_func<_Rp(_ArgTypes...)> __f_; + // #ifndef _LIBCPP_ABI_OPTIMIZED_FUNCTION + // typedef __function::__value_func<_Rp(_ArgTypes...)> __func; + // #else + // typedef __function::__policy_func<_Rp(_ArgTypes...)> __func; + // #endif + // + // __func __f_; // } // + // template <class _Fp> class __value_func; + // template <class _Fp> class __policy_func; + // + // template <class _Rp, class... _ArgTypes> // class __value_func<_Rp(_ArgTypes...)> { - // aligned_storage<3 * sizeof(void*)>::type __buf_; - // __base<_Rp(_ArgTypes...)>* __f_; + // typename aligned_storage<3 * sizeof(void*)>::type __buf_; + // + // typedef __base<_Rp(_ArgTypes...)> __func; + // __func* __f_; // } - - func_as_base_ptr = std::move(inner_f); - } else - return optional_info; // Unrecognized implementation + // + // template <class _Rp, class... _ArgTypes> + // class __policy_func<_Rp(_ArgTypes...)> { + // __policy_storage __buf_; + // + // typedef __function::__policy_invoker<_Rp(_ArgTypes...)> __invoker; + // __invoker __invoker_; + // + // const __policy* __policy_; + // } + if (auto outer_f = valobj_sp->GetChildMemberWithName("__f_")) { + if (auto inner_f = outer_f->GetChildMemberWithName("__f_")) + base_ptr = std::move(inner_f); + } else if (valobj_sp->GetChildMemberWithName("__invoker_")) + return llvm::createStringError("__policy_func implementation is " + "not supported"); + } + + if (!base_ptr) { + // git: 3e519524c118651123eecf60c2bbc5d65ad9bac3 include/__functional_03 + // + // class function<_Rp()> { + // aligned_storage<3*sizeof(void*)>::type __buf_; + // __base<_Rp>* __f_; + // } + if (auto outer_f = valobj_sp->GetChildMemberWithName("__f_")) + base_ptr = std::move(outer_f); + } + + if (!base_ptr) + return llvm::createStringError("unrecognized implementation"); } - - // __base<...> is a pure virtual class with an interface to create/copy/destroy/invoke - // the underlying value. This interface is implemented by partial specializations of the - // __func<_Fp, _Alloc, ...> template where _Fp is the wrapped functor object + + // __base<...> is a pure virtual class with an interface to manage the + // the wrapped value. This interface is implemented by partial specializations + // of the __func<_Fp, _Alloc, ...> template where _Fp is the wrapped callable + // + // We'll try to extract the concrete __func type pointed to by base_ptr by + // analysing it's vtable. Status status; - ValueObjectSP func_as_base = func_as_base_ptr->Dereference(status); + ValueObjectSP base = base_ptr->Dereference(status); if (status.Fail()) - return optional_info; + return llvm::createStringError("failed to dereference __base pointer"); - // First we'll try to extract the __func<...> template instantiation's type by looking up - // the declarations of the member function pointers in it's vtable + Address virtual_method_addr; CompilerType func_type; - Address func_method_addr; { - ValueObjectSP vtable = func_as_base->GetVTable(); + ValueObjectSP vtable = base->GetVTable(); llvm::Expected<uint32_t> num_entries = vtable->GetNumChildren(); - if (num_entries.takeError()) - return optional_info; + if (auto error = num_entries.takeError()) + return error; - // __base is pure virtual, __func is final. All member function pointers are equally - // good candidates to find the enclosing class. + // __base is pure virtual, __func is final. All member function pointers + // are equally good candidates to find the enclosing class. + // + // In practice the first two vtable entries point to artificial destructors + // which the type system refuses to elaborate as their artificial + // specifications are not added to the enclosing class' declaration + // context. // - // In practice the first two vtable entries point to artificial destructors which the - // type system refuses to elaborate as their artificial specifications are not added - // to the enclosing class' declaration context. This causes various warnings, and dont - // get us any closer to the concrete type thus we skip them. + // This causes various warnings, and don't get us any closer to the + // concrete type thus we skip them. for (uint32_t idx = 2; idx < *num_entries; idx++) { ValueObjectSP entry = vtable->GetChildAtIndex(idx); @@ -242,86 +231,132 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( if (!mfunc_load_addr) continue; - if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, func_method_addr)) + if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, + virtual_method_addr)) continue; - Function* func = func_method_addr.CalculateSymbolContextFunction(); + Function* func = virtual_method_addr.CalculateSymbolContextFunction(); if (!func) continue; - CompilerDeclContext mfunc_decl_ctx = func->GetDeclContext(); - if (!mfunc_decl_ctx.IsClassMethod()) + CompilerDeclContext decl_ctx = func->GetDeclContext(); + if (!decl_ctx.IsClassMethod()) continue; // Member functions are contained in their enclosing class' decl context - CompilerDeclContext mfunc_parent = mfunc_decl_ctx.GetDecl().GetDeclContext(); - if (!mfunc_parent.IsValid()) + CompilerDeclContext enclosing_class = decl_ctx.GetDecl().GetDeclContext(); + if (!enclosing_class.IsValid()) continue; - func_type = mfunc_parent.GetDecl().GetType(); + func_type = enclosing_class.GetDecl().GetType(); break; } } + if (!func_type) + return llvm::createStringError("failed to find suitable virtual function " + "to determine __func type"); + + ValueObjectSP func = base->Cast(func_type); + if (!func) + return llvm::createStringError("failed to cast __base to __func type"); + + // Now that the __func is a known type we can dig for the wrapped callable CompilerType callable_type = func_type.GetTypeTemplateArgument(0); if (!callable_type) + return llvm::createStringError("failed to get wrapped callable type from " + "first template parameter"); + + // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394 + // + // class __func<_Fp, _Alloc, _Rp(_ArgTypes...)> + // : __base<_Rp(_ArgTypes...)> { + // __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_; + // } + // + // class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> { + // __compressed_pair<_Fp, _Ap> __f_; + // } + // + // class __compressed_pair : __compressed_pair_elem<_T1, 0>, + // __compressed_pair_elem<_T2, 1> { + // } + // + // struct __compressed_pair_elem { + // _Tp __value_; + // } + ValueObjectSP pair = func->GetChildAtNamePath({"__f_", "__f_"}); + if (!pair) + return llvm::createStringError("__compressed_pair is not where expected"); + + // The callable may be an empty class in which case the empty base class + // optimization will eliminate it completely from the type hierarchy + // + // Serve a dummy value which for all intents and purposes is just as good + if (callable_type.IsRecordType() && callable_type.GetNumFields() == 0) + return UnwrappedLibCppFunction { + .callable = valobj_sp->CreateValueObjectFromAddress( + "__value_", + pair->GetLoadAddress(), + pair->GetExecutionContextRef(), + callable_type + ), + .in_module = virtual_method_addr.CalculateSymbolContextModule() + }; + + ValueObjectSP elem0 = pair->GetChildAtIndex(0); + if (!elem0) + return llvm::createStringError("__compressed_pair element 0 not where " + "expected"); + + ValueObjectSP callable = elem0->GetChildMemberWithName("__value_"); + if (!callable) + return llvm::createStringError("__compressed_pair element value not " + "where expected"); + + return UnwrappedLibCppFunction { + .callable = std::move(callable), + .in_module = virtual_method_addr.CalculateSymbolContextModule(), + }; +} + +CPPLanguageRuntime::LibCppStdFunctionCallableInfo +CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( + lldb::ValueObjectSP &valobj_sp) { + LLDB_SCOPED_TIMER(); + + LibCppStdFunctionCallableInfo optional_info; + + auto unwrap_r = UnwrapLibCppFunction(valobj_sp); + if (unwrap_r.takeError()) return optional_info; - // Now that the __func is a known type we can dig for the wrapped callable - ValueObjectSP callable; - { - // class __func<_Fp, _Alloc, _Rp(_ArgTypes...)> : __base<_Rp(_ArgTypes...)> { - // __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_; - // } - // - // class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> { - // __compressed_pair<_Fp, _Ap> __f_; - // } - // - // class __compressed_pair : __compressed_pair_elem<_T1, 0>, - // __compressed_pair_elem<_T2, 1> { - // } - // - // struct __compressed_pair_elem { - // _Tp __value_; - // } - ValueObjectSP alloc_func = func_as_base->Cast(func_type); - if (!alloc_func) - return optional_info; + UnwrappedLibCppFunction& wrapped = unwrap_r.get(); + + CompilerType callable_t = wrapped.callable->GetCompilerType(); + + if (callable_t.IsFunctionPointerType() || + callable_t.IsMemberFunctionPointerType()) { + // Target is a standard function pointer, or member function pointer. + // In either case on Itanium both contain a function address + AddressType addr_type; + addr_t addr = wrapped.callable->GetPointerValue(&addr_type); - ValueObjectSP pair = alloc_func->GetChildAtNamePath({"__f_", "__f_"}); - if (!pair) + if (!addr || addr == LLDB_INVALID_ADDRESS) return optional_info; - if (callable_type.IsRecordType() && callable_type.GetNumFields() == 0) { - // callable_type is an empty class, and has been optimized away! Serve a dummy - callable = valobj_sp->CreateValueObjectFromAddress("__value_", - pair->GetLoadAddress(), - pair->GetExecutionContextRef(), - callable_type); - } else { - ValueObjectSP elem0 = pair->GetChildAtIndex(0); - if (!elem0) - return optional_info; - - callable = elem0->GetChildMemberWithName("__value_"); - if (!callable) - return optional_info; - } - } - - if (callable_type.IsFunctionPointerType()) { - addr_t target_load_addr = callable->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); - - ModuleSP mod = func_method_addr.CalculateSymbolContextModule(); + if (addr_type != eAddressTypeLoad) + return optional_info; Address callable_addr; - if (!mod->ResolveFileAddress(target_load_addr, callable_addr)) + if (!wrapped.in_module->ResolveFileAddress(addr, callable_addr)) return optional_info; SymbolContext sc; - mod->ResolveSymbolContextForAddress(callable_addr, eSymbolContextSymbol | eSymbolContextLineEntry, sc); - + wrapped.in_module->ResolveSymbolContextForAddress(callable_addr, + eSymbolContextSymbol | + eSymbolContextLineEntry, + sc); if (!sc.symbol) return optional_info; @@ -331,20 +366,13 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( .callable_line_entry = sc.line_entry, .callable_case = LibCppStdFunctionCallableCase::FreeOrMemberFunction }; - } else if (callable_type.IsMemberFunctionPointerType()) { - // TODO: Member function's unsigned value comes back as invalid! I am guessing - // the ValueObject wants to let me know that this is not necessarily as simple - // as that.. I remember reading something in the Itanium ABI about member - // pointers taking up two pointers of space. Perhaps that's why it is not legal - // to read their underlying value raw? - - printf("Curious!"); - } else if (callable_type.IsRecordType()) { - // Target is a lambda, or a generic callable. Search for a single operator() overload + } else if (callable_t.IsRecordType()) { + // Target is a lambda, or a generic callable. Search for a single + // operator() overload and assume it is the target std::optional<ConstString> mangled_func_name; - for (uint32_t idx = 0; idx < callable_type.GetNumMemberFunctions(); idx++) { - TypeMemberFunctionImpl mfunc = callable_type.GetMemberFunctionAtIndex(idx); + for (uint32_t idx = 0; idx < callable_t.GetNumMemberFunctions(); idx++) { + TypeMemberFunctionImpl mfunc = callable_t.GetMemberFunctionAtIndex(idx); if (mfunc.GetKind() != eMemberFunctionKindInstanceMethod) continue; @@ -361,28 +389,28 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( // Locate the symbol context corresponding to the target function SymbolContext sc; { - // We'll assume that callable_type is in the same module as the vtable - ModuleSP mod = func_method_addr.CalculateSymbolContextModule(); - // Limit our lookup to callable_type - CompilerDeclContext decl_ctx = callable_type.GetTypeSystem()->GetCompilerDeclContextForType(callable_type); + CompilerDeclContext decl_ctx = callable_t.GetTypeSystem() + ->GetCompilerDeclContextForType(callable_t); SymbolContextList list; - mod->FindFunctions(*mangled_func_name, decl_ctx, eFunctionNameTypeFull, {}, list); - + wrapped.in_module->FindFunctions(*mangled_func_name, decl_ctx, + eFunctionNameTypeFull, {}, list); if (list.GetSize() != 1) return optional_info; list.GetContextAtIndex(0, sc); } - // TODO: This feels a bit clunky, I am probably misusing the API? FindFunctions returns me - // SymbolContexts with the .function set but not .symbol ... At first glance it seemed like - // if we know the function there must be a symbol too! + // TODO: This feels a bit clunky, I am probably misusing the API? + // FindFunctions returns me SymbolContexts with the .function set but not + // .symbol ... At first glance it seemed like if we know the function there + // must be a symbol too! if (!sc.function) return optional_info; - Symbol* symbol = sc.function->GetAddressRange().GetBaseAddress().CalculateSymbolContextSymbol(); + Symbol* symbol = sc.function->GetAddressRange().GetBaseAddress() + .CalculateSymbolContextSymbol(); if (!symbol) return optional_info; @@ -391,240 +419,14 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( .callable_address = symbol->GetAddress(), .callable_line_entry = sc.GetFunctionStartLineEntry(), - // TODO: Can't tell lambdas apart from generic callables.. do we really need to? - // Is it important to have the correct qualification in the summary? + // TODO: Can't tell lambdas apart from generic callables.. do we really + // need to? Is it important to have the correct qualification in the + // summary? .callable_case = LibCppStdFunctionCallableCase::Lambda }; } - // Unrecognized callable type - skip the original implementation for now - if (!callable_type.IsVoidType()) - return optional_info; - - // Member __f_ has type __base*, the contents of which will hold: - // 1) a vtable entry which may hold type information needed to discover the - // lambda being called - // 2) possibly hold a pointer to the callable object - // e.g. - // - // (lldb) frame var -R f_display - // (std::__1::function<void (int)>) f_display = { - // __buf_ = { - // … - // } - // __f_ = 0x00007ffeefbffa00 - // } - // (lldb) memory read -fA 0x00007ffeefbffa00 - // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ... - // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ... - // - // We will be handling five cases below, std::function is wrapping: - // - // 1) a lambda we know at compile time. We will obtain the name of the lambda - // from the first template pameter from __func's vtable. We will look up - // the lambda's operator()() and obtain the line table entry. - // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method - // will be stored after the vtable. We will obtain the lambdas name from - // this entry and lookup operator()() and obtain the line table entry. - // 3) a callable object via operator()(). We will obtain the name of the - // object from the first template parameter from __func's vtable. We will - // look up the objects operator()() and obtain the line table entry. - // 4) a member function. A pointer to the function will stored after the - // we will obtain the name from this pointer. - // 5) a free function. A pointer to the function will stored after the vtable - // we will obtain the name from this pointer. - ValueObjectSP member_f_(valobj_sp->GetChildMemberWithName("__f_")); - - if (member_f_) { - ValueObjectSP sub_member_f_(member_f_->GetChildMemberWithName("__f_")); - - if (sub_member_f_) - member_f_ = sub_member_f_; - } - - if (!member_f_) - return optional_info; - - lldb::addr_t member_f_pointer_value = member_f_->GetValueAsUnsigned(0); - - optional_info.member_f_pointer_value = member_f_pointer_value; - - if (!member_f_pointer_value) - return optional_info; - - ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); - Process *process = exe_ctx.GetProcessPtr(); - - if (process == nullptr) - return optional_info; - - uint32_t address_size = process->GetAddressByteSize(); - - // First item pointed to by __f_ should be the pointer to the vtable for - // a __base object. - lldb::addr_t vtable_address = - process->ReadPointerFromMemory(member_f_pointer_value, status); - - if (status.Fail()) - return optional_info; - - lldb::addr_t vtable_address_first_entry = - process->ReadPointerFromMemory(vtable_address + address_size, status); - - if (status.Fail()) - return optional_info; - - lldb::addr_t address_after_vtable = member_f_pointer_value + address_size; - // As commented above we may not have a function pointer but if we do we will - // need it. - lldb::addr_t possible_function_address = - process->ReadPointerFromMemory(address_after_vtable, status); - - if (status.Fail()) - return optional_info; - - Target &target = process->GetTarget(); - - if (target.GetSectionLoadList().IsEmpty()) - return optional_info; - - Address vtable_first_entry_resolved; - - if (!target.GetSectionLoadList().ResolveLoadAddress( - vtable_address_first_entry, vtable_first_entry_resolved)) - return optional_info; - - Address vtable_addr_resolved; - SymbolContext sc; - Symbol *symbol = nullptr; - - if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address, - vtable_addr_resolved)) - return optional_info; - - target.GetImages().ResolveSymbolContextForAddress( - vtable_addr_resolved, eSymbolContextEverything, sc); - symbol = sc.symbol; - - if (symbol == nullptr) - return optional_info; - - llvm::StringRef vtable_name(symbol->GetName().GetStringRef()); - bool found_expected_start_string = - vtable_name.starts_with("vtable for std::__1::__function::__func<"); - - if (!found_expected_start_string) - return optional_info; - - // Given case 1 or 3 we have a vtable name, we are want to extract the first - // template parameter - // - // ... __func<main::$_0, std::__1::allocator<main::$_0> ... - // ^^^^^^^^^ - // - // We could see names such as: - // main::$_0 - // Bar::add_num2(int)::'lambda'(int) - // Bar - // - // We do this by find the first < and , and extracting in between. - // - // This covers the case of the lambda known at compile time. - size_t first_open_angle_bracket = vtable_name.find('<') + 1; - size_t first_comma = vtable_name.find(','); - - llvm::StringRef first_template_parameter = - vtable_name.slice(first_open_angle_bracket, first_comma); - - Address function_address_resolved; - - // Setup for cases 2, 4 and 5 we have a pointer to a function after the - // vtable. We will use a process of elimination to drop through each case - // and obtain the data we need. - if (target.GetSectionLoadList().ResolveLoadAddress( - possible_function_address, function_address_resolved)) { - target.GetImages().ResolveSymbolContextForAddress( - function_address_resolved, eSymbolContextEverything, sc); - symbol = sc.symbol; - } - - // These conditions are used several times to simplify statements later on. - bool has_invoke = - (symbol ? symbol->GetName().GetStringRef().contains("__invoke") : false); - auto calculate_symbol_context_helper = [](auto &t, - SymbolContextList &sc_list) { - SymbolContext sc; - t->CalculateSymbolContext(&sc); - sc_list.Append(sc); - }; - - // Case 2 - if (has_invoke) { - SymbolContextList scl; - calculate_symbol_context_helper(symbol, scl); - - return line_entry_helper(target, scl[0], symbol, first_template_parameter, - has_invoke); - } - - // Case 4 or 5 - if (symbol && !symbol->GetName().GetStringRef().starts_with("vtable for") && - !contains_lambda_identifier(first_template_parameter) && !has_invoke) { - optional_info.callable_case = - LibCppStdFunctionCallableCase::FreeOrMemberFunction; - optional_info.callable_address = function_address_resolved; - optional_info.callable_symbol = *symbol; - - return optional_info; - } - - std::string func_to_match = first_template_parameter.str(); - - auto it = CallableLookupCache.find(func_to_match); - if (it != CallableLookupCache.end()) - return it->second; - - SymbolContextList scl; - - CompileUnit *vtable_cu = - vtable_first_entry_resolved.CalculateSymbolContextCompileUnit(); - llvm::StringRef name_to_use = func_to_match; - - // Case 3, we have a callable object instead of a lambda - // - // TODO - // We currently don't support this case a callable object may have multiple - // operator()() varying on const/non-const and number of arguments and we - // don't have a way to currently distinguish them so we will bail out now. - if (!contains_lambda_identifier(name_to_use)) - return optional_info; - - if (vtable_cu && !has_invoke) { - lldb::FunctionSP func_sp = - vtable_cu->FindFunction([name_to_use](const FunctionSP &f) { - auto name = f->GetName().GetStringRef(); - if (name.starts_with(name_to_use) && name.contains("operator")) - return true; - - return false; - }); - - if (func_sp) { - calculate_symbol_context_helper(func_sp, scl); - } - } - - if (symbol == nullptr) - return optional_info; - - // Case 1 or 3 - if (scl.GetSize() >= 1) { - optional_info = line_entry_helper(target, scl[0], symbol, - first_template_parameter, has_invoke); - } - - CallableLookupCache[func_to_match] = optional_info; - + // Unrecognized callable type return optional_info; } diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h index 57cfe28245808c..0368e752ff33c0 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h @@ -28,6 +28,14 @@ class CPPLanguageRuntime : public LanguageRuntime { Invalid }; + struct UnwrappedLibCppFunction { + lldb::ValueObjectSP callable; + lldb::ModuleSP in_module; + }; + + llvm::Expected<UnwrappedLibCppFunction> + UnwrapLibCppFunction(lldb::ValueObjectSP &valobj_sp); + struct LibCppStdFunctionCallableInfo { Symbol callable_symbol; Address callable_address; @@ -37,6 +45,7 @@ class CPPLanguageRuntime : public LanguageRuntime { LibCppStdFunctionCallableCase::Invalid; }; + LibCppStdFunctionCallableInfo FindLibCppStdFunctionCallableInfo(lldb::ValueObjectSP &valobj_sp); @@ -81,12 +90,6 @@ class CPPLanguageRuntime : public LanguageRuntime { protected: // Classes that inherit from CPPLanguageRuntime can see and modify these CPPLanguageRuntime(Process *process); - -private: - using OperatorStringToCallableInfoMap = - llvm::StringMap<CPPLanguageRuntime::LibCppStdFunctionCallableInfo>; - - OperatorStringToCallableInfoMap CallableLookupCache; }; } // namespace lldb_private _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits