[Lldb-commits] [lldb] [LLDB] FindLibCppStdFunctionCallableInfo improvements (PR #111892)

David Mentler via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 11 14:45:18 PDT 2024


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.mentler at 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.mentler at 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.mentler at 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.mentler at 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.mentler at 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.mentler at 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.mentler at 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.mentler at 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



More information about the lldb-commits mailing list