shafik created this revision.
shafik added reviewers: jingham, davide.
Herald added a reviewer: EricWF.
Herald added a subscriber: christof.

Adding formatter summary for std::function.

  
  - Added LibcxxFunctionSummaryProvider
  - Removed LibcxxFunctionFrontEnd
  - Modified data formatter tests to test new summary functionality


https://reviews.llvm.org/D50864

Files:
  
packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py
  
packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
  source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
  source/Plugins/Language/CPlusPlus/LibCxx.cpp
  source/Plugins/Language/CPlusPlus/LibCxx.h

Index: source/Plugins/Language/CPlusPlus/LibCxx.h
===================================================================
--- source/Plugins/Language/CPlusPlus/LibCxx.h
+++ source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -32,6 +32,10 @@
     const TypeSummaryOptions
         &options); // libc++ std::shared_ptr<> and std::weak_ptr<>
 
+bool LibcxxFunctionSummaryProvider(
+    ValueObject &valobj, Stream &stream,
+    const TypeSummaryOptions &options); // libc++ std::function<>
+
 SyntheticChildrenFrontEnd *
 LibcxxVectorBoolSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                          lldb::ValueObjectSP);
@@ -124,9 +128,6 @@
 LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                               lldb::ValueObjectSP);
 
-SyntheticChildrenFrontEnd *LibcxxFunctionFrontEndCreator(CXXSyntheticChildren *,
-                                                         lldb::ValueObjectSP);
-
 SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *,
                                                       lldb::ValueObjectSP);
 
Index: source/Plugins/Language/CPlusPlus/LibCxx.cpp
===================================================================
--- source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -23,6 +23,7 @@
 #include "lldb/DataFormatters/VectorIterator.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/DataBufferHeap.h"
 #include "lldb/Utility/Endian.h"
@@ -33,6 +34,176 @@
 using namespace lldb_private;
 using namespace lldb_private::formatters;
 
+bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+
+  ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+
+  if (!valobj_sp)
+    return false;
+
+  // Member __f_ has type __base*, the contents of which will either directly
+  // hold a pointer to the callable object or vtable entry which will hold the
+  // type information need to discover the lambda being called.
+  ValueObjectSP member__f_(
+      valobj_sp->GetChildMemberWithName(ConstString("__f_"), true));
+  lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0);
+
+  ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
+  Process *process = exe_ctx.GetProcessPtr();
+
+  // We may need to increment pointers into __f_ so we need to know the size
+  uint32_t address_size = process->GetAddressByteSize();
+
+  if (process != nullptr) {
+    Status status;
+
+    // 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 false;
+    }
+
+    lldb::addr_t address_after_vtable = member__f_pointer_value + address_size;
+    lldb::addr_t possible_function_address =
+        process->ReadPointerFromMemory(address_after_vtable, status);
+
+    Target &target = process->GetTarget();
+
+    if (!target.GetSectionLoadList().IsEmpty()) {
+      Address vtable_addr_resolved;
+      SymbolContext sc;
+      Symbol *symbol;
+
+      if (target.GetSectionLoadList().ResolveLoadAddress(
+              vtable_address, vtable_addr_resolved)) {
+
+        target.GetImages().ResolveSymbolContextForAddress(
+            vtable_addr_resolved, eSymbolContextEverything, sc);
+
+        symbol = sc.symbol;
+        if (symbol != NULL) {
+          llvm::StringRef vtable_name(symbol->GetName().GetCString());
+          bool found_expected_start_string = vtable_name.startswith(
+              "vtable for std::__1::__function::__func<");
+
+          if (found_expected_start_string) {
+            // Given a vtable name, we are want to extract the first template
+            // parameter
+            //
+            //  ... __func<main::$_0, std::__1::allocator<main::$_0> ...
+            //             ^^^^^^^^^
+            //
+            // 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_first_of(',');
+
+            llvm::StringRef first_template_parameter =
+                vtable_name.slice(first_open_angle_bracket, first_comma);
+
+            Address function_address_resolved;
+
+            if (target.GetSectionLoadList().ResolveLoadAddress(
+                    possible_function_address, function_address_resolved)) {
+              target.GetImages().ResolveSymbolContextForAddress(
+                  function_address_resolved, eSymbolContextEverything, sc);
+              symbol = sc.symbol;
+            }
+
+            auto get_name = [&first_template_parameter, &symbol]() {
+              // Given:
+              //
+              //    main::$_0
+              //
+              // we want to append ::operator()()
+              if (first_template_parameter.contains("$_"))
+                return llvm::Regex::escape(first_template_parameter.str()) +
+                       R"(::operator\(\)\(.*\))";
+              ;
+
+              llvm::StringRef symbol_name = symbol->GetName().GetStringRef();
+
+              if (symbol_name.contains("__invoke")) {
+                size_t pos2 = symbol_name.find_last_of(':');
+
+                // Given:
+                //
+                //    main::$_1::__invoke(...)
+                //
+                // We want to slice off __invoke(...) and append operator()()
+                std::string lambda_operator =
+                    llvm::Regex::escape(symbol_name.slice(0, pos2 + 1).str()) +
+                    R"(operator\(\)\(.*\))";
+
+                return lambda_operator;
+              }
+
+              return first_template_parameter.str() + R"(::operator\(\)\(.*\))";
+              ;
+            };
+
+            std::string func_to_match = get_name();
+
+            SymbolContextList scl;
+
+            target.GetImages().FindFunctions(RegularExpression{func_to_match},
+                                             true, true, true, scl);
+
+            if (scl.GetSize() >= 1) {
+              SymbolContext sc2 = scl[0];
+
+              AddressRange range;
+              sc2.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 (first_template_parameter.contains("$_") ||
+                    (symbol != NULL &&
+                     symbol->GetName().GetStringRef().contains("__invoke"))) {
+                  stream.Printf(" Lambda in File %s at Line %u",
+                                line_entry.file.GetFilename().GetCString(),
+                                line_entry.line);
+                } else {
+                  stream.Printf(" Function in File %s at Line %u",
+                                line_entry.file.GetFilename().GetCString(),
+                                line_entry.line);
+                }
+
+                return true;
+              }
+            }
+            {
+              if (symbol != NULL &&
+                  !symbol->GetName().GetStringRef().startswith("vtable for")) {
+                stream.Printf(" Function = %s ",
+                              symbol->GetName().GetCString());
+                return true;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  stream.Printf(" __f_ = %llu", member__f_pointer_value);
+
+  return true;
+}
+
 bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
@@ -566,22 +737,3 @@
 
   return true;
 }
-
-class LibcxxFunctionFrontEnd : public SyntheticValueProviderFrontEnd {
-public:
-  LibcxxFunctionFrontEnd(ValueObject &backend)
-      : SyntheticValueProviderFrontEnd(backend) {}
-
-  lldb::ValueObjectSP GetSyntheticValue() override {
-    static ConstString g___f_("__f_");
-    return m_backend.GetChildMemberWithName(g___f_, true);
-  }
-};
-
-SyntheticChildrenFrontEnd *
-lldb_private::formatters::LibcxxFunctionFrontEndCreator(
-    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
-  if (valobj_sp)
-    return new LibcxxFunctionFrontEnd(*valobj_sp);
-  return nullptr;
-}
Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
===================================================================
--- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -519,6 +519,11 @@
       ConstString("^(std::__(ndk)?1::)weak_ptr<.+>(( )?&)?$"), stl_synth_flags,
       true);
 
+  AddCXXSummary(
+      cpp_category_sp, lldb_private::formatters::LibcxxFunctionSummaryProvider,
+      "libc++ std::function summary provider",
+      ConstString("^std::__(ndk)?1::function<.+>$"), stl_summary_flags, true);
+
   stl_summary_flags.SetDontShowChildren(false);
   stl_summary_flags.SetSkipPointers(false);
   AddCXXSummary(cpp_category_sp,
@@ -610,11 +615,6 @@
       "std::map iterator synthetic children",
       ConstString("^std::__(ndk)?1::__map_iterator<.+>$"), stl_synth_flags,
       true);
-
-  AddCXXSynthetic(
-      cpp_category_sp, lldb_private::formatters::LibcxxFunctionFrontEndCreator,
-      "std::function synthetic value provider",
-      ConstString("^std::__(ndk)?1::function<.+>$"), stl_synth_flags, true);
 #endif
 }
 
Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
===================================================================
--- packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
+++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
@@ -13,13 +13,18 @@
   return x + y - 1;
 }
 
-int main ()
+int main (int argc, char *argv[])
 {
   int acc = 42;
   std::function<int (int,int)> f1 = foo;
   std::function<int (int)> f2 = [acc,f1] (int x) -> int {
     return x+f1(acc,x);
   };
-    return f1(acc,acc) + f2(acc); // Set break point at this line.
+
+  auto f = [](int x, int y) { return x + y; };
+  auto g = [](int x, int y) { return x * y; } ;
+  std::function<int (int,int)> f3 =  argc %2 ? f : g ;
+
+  return f1(acc,acc) + f2(acc) + f3(acc+1,acc+2); // Set break point at this line.
 }
 
Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py
===================================================================
--- packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py
+++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/function/TestLibCxxFunction.py
@@ -40,13 +40,11 @@
                     substrs=['stopped',
                              'stop reason = breakpoint'])
 
-        f1 = self.get_variable('f1')
-        f2 = self.get_variable('f2')
+        self.expect("frame variable f1",
+                    substrs=['f1 =  Function = foo(int, int)'])
 
-        if self.TraceOn():
-            print(f1)
-        if self.TraceOn():
-            print(f2)
+        self.expect("frame variable f2",
+                    substrs=['f2 =  Lambda in File main.cpp at Line 20'])
 
-        self.assertTrue(f1.GetValueAsUnsigned(0) != 0, 'f1 has a valid value')
-        self.assertTrue(f2.GetValueAsUnsigned(0) != 0, 'f2 has a valid value')
+        self.expect("frame variable f3",
+                    substrs=['f3 =  Lambda in File main.cpp at Line 24'])
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to