[Lldb-commits] [lldb] r344371 - Adding support to step into the callable wrapped by libc++ std::function

Shafik Yaghmour via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 12 10:20:39 PDT 2018


Author: shafik
Date: Fri Oct 12 10:20:39 2018
New Revision: 344371

URL: http://llvm.org/viewvc/llvm-project?rev=344371&view=rev
Log:
Adding support to step into the callable wrapped by libc++ std::function

rdar://problem/14365983

Differential Revision: https://reviews.llvm.org/D52851

Added:
    lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/
    lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py
    lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp
Modified:
    lldb/trunk/include/lldb/Target/CPPLanguageRuntime.h
    lldb/trunk/source/Target/CPPLanguageRuntime.cpp
    lldb/trunk/source/Target/ThreadPlanStepThrough.cpp

Modified: lldb/trunk/include/lldb/Target/CPPLanguageRuntime.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/CPPLanguageRuntime.h?rev=344371&r1=344370&r2=344371&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/CPPLanguageRuntime.h (original)
+++ lldb/trunk/include/lldb/Target/CPPLanguageRuntime.h Fri Oct 12 10:20:39 2018
@@ -56,6 +56,19 @@ public:
   bool GetObjectDescription(Stream &str, Value &value,
                             ExecutionContextScope *exe_scope) override;
 
+  /// Obtain a ThreadPlan to get us into C++ constructs such as std::function.
+  ///
+  /// @param[in] thread
+  ///     Curent thrad of execution.
+  ///
+  /// @param[in] stop_others
+  ///     True if other threads should pause during execution.
+  ///
+  /// @return
+  ///      A ThreadPlan Shared pointer
+  lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+                                                  bool stop_others);
+
 protected:
   //------------------------------------------------------------------
   // Classes that inherit from CPPLanguageRuntime can see and modify these

Added: lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile?rev=344371&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile Fri Oct 12 10:20:39 2018
@@ -0,0 +1,7 @@
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+CXXFLAGS += -std=c++11
+USE_LIBCPP := 1
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py?rev=344371&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py Fri Oct 12 10:20:39 2018
@@ -0,0 +1,71 @@
+"""
+Test stepping into std::function
+"""
+
+from __future__ import print_function
+
+
+import lldb
+import sys
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibCxxFunctionTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @add_test_categories(["libc++"])
+    def test(self):
+        """Test that std::function as defined by libc++ is correctly printed by LLDB"""
+        self.build()
+
+        self.main_source = "main.cpp"
+        self.main_source_spec = lldb.SBFileSpec(self.main_source)
+        self.source_foo_line = line_number(
+            self.main_source, '// Source foo start line')
+        self.source_lambda_f2_line = line_number(
+            self.main_source, '// Source lambda used by f2 start line')
+        self.source_lambda_f3_line = line_number(
+            self.main_source, '// Source lambda used by f3 start line')
+        self.source_bar_operator_line = line_number(
+            self.main_source, '// Source Bar::operator()() start line')
+        self.source_bar_add_num_line = line_number(
+            self.main_source, '// Source Bar::add_num start line')
+        self.source_main_invoking_f1 = line_number(
+            self.main_source, '// Source main invoking f1')
+
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "// Set break point at this line.", self.main_source_spec)
+
+        thread.StepInto()
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.source_main_invoking_f1 ) ;
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec().GetFilename(), self.main_source) ;
+
+        thread.StepInto()
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.source_foo_line ) ;
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec().GetFilename(), self.main_source) ;
+        process.Continue()
+
+        thread.StepInto()
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.source_lambda_f2_line ) ;
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec().GetFilename(), self.main_source) ;
+        process.Continue()
+
+        thread.StepInto()
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.source_lambda_f3_line ) ;
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec().GetFilename(), self.main_source) ;
+        process.Continue()
+
+        thread.StepInto()
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.source_bar_operator_line ) ;
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec().GetFilename(), self.main_source) ;
+        process.Continue()
+
+        thread.StepInto()
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.source_bar_add_num_line ) ;
+        self.assertEqual( thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec().GetFilename(), self.main_source) ;
+        process.Continue()

Added: lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp?rev=344371&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp Fri Oct 12 10:20:39 2018
@@ -0,0 +1,38 @@
+#include <functional>
+
+int foo(int x, int y) {
+  return x + y - 1; // Source foo start line
+}
+
+struct Bar {
+   int operator()() {
+       return 66 ; // Source Bar::operator()() start line
+   }
+   int add_num(int i) const { return i + 3 ; } // Source Bar::add_num start line
+   int num_ = 0 ;
+} ;
+
+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); // Source lambda used by f2 start line
+  };
+
+  auto f = [](int x, int y) { return x + y; }; // Source lambda used by f3 start line
+  auto g = [](int x, int y) { return x * y; } ;
+  std::function<int (int,int)> f3 =  argc %2 ? f : g ;
+
+  Bar bar1 ;
+  std::function<int ()> f4( bar1 ) ;
+  std::function<int (const Bar&, int)> f5 = &Bar::add_num;
+  std::function<int(Bar const&)> f_mem = &Bar::num_;
+
+  return f_mem(bar1) +     // Set break point at this line.
+         f1(acc,acc) +     // Source main invoking f1
+         f2(acc) +         // Set break point at this line.
+         f3(acc+1,acc+2) + // Set break point at this line. 
+         f4() +            // Set break point at this line. 
+         f5(bar1, 10);     // Set break point at this line.
+}

Modified: lldb/trunk/source/Target/CPPLanguageRuntime.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/CPPLanguageRuntime.cpp?rev=344371&r1=344370&r2=344371&view=diff
==============================================================================
--- lldb/trunk/source/Target/CPPLanguageRuntime.cpp (original)
+++ lldb/trunk/source/Target/CPPLanguageRuntime.cpp Fri Oct 12 10:20:39 2018
@@ -26,6 +26,7 @@
 #include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/StackFrame.h"
 #include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -158,7 +159,6 @@ CPPLanguageRuntime::FindLibCppStdFunctio
   // 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(',');
 
@@ -262,3 +262,76 @@ CPPLanguageRuntime::FindLibCppStdFunctio
 
   return optional_info;
 }
+
+lldb::ThreadPlanSP
+CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread,
+                                                 bool stop_others) {
+  ThreadPlanSP ret_plan_sp;
+
+  lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
+
+  TargetSP target_sp(thread.CalculateTarget());
+
+  if (target_sp->GetSectionLoadList().IsEmpty())
+    return ret_plan_sp;
+
+  Address pc_addr_resolved;
+  SymbolContext sc;
+  Symbol *symbol;
+
+  if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc,
+                                                          pc_addr_resolved))
+    return ret_plan_sp;
+
+  target_sp->GetImages().ResolveSymbolContextForAddress(
+      pc_addr_resolved, eSymbolContextEverything, sc);
+  symbol = sc.symbol;
+
+  if (symbol == nullptr)
+    return ret_plan_sp;
+
+  llvm::StringRef function_name(symbol->GetName().GetCString());
+
+  // Handling the case where we are attempting to step into std::function.
+  // The behavior will be that we will attempt to obtain the wrapped
+  // callable via FindLibCppStdFunctionCallableInfo() and if we find it we
+  // will return a ThreadPlanRunToAddress to the callable. Therefore we will
+  // step into the wrapped callable.
+  //
+  bool found_expected_start_string =
+      function_name.startswith("std::__1::function<");
+
+  if (!found_expected_start_string)
+    return ret_plan_sp;
+
+  AddressRange range_of_curr_func;
+  sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func);
+
+  StackFrameSP frame = thread.GetStackFrameAtIndex(0);
+
+  if (frame) {
+    ValueObjectSP value_sp = frame->FindVariable(ConstString("this"));
+
+    CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
+        FindLibCppStdFunctionCallableInfo(value_sp);
+
+    if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid &&
+        value_sp->GetValueIsValid()) {
+      // We found the std::function wrapped callable and we have its address.
+      // We now create a ThreadPlan to run to the callable.
+      ret_plan_sp.reset(new ThreadPlanRunToAddress(
+          thread, callable_info.callable_address, stop_others));
+      return ret_plan_sp;
+    } else {
+      // We are in std::function but we could not obtain the callable.
+      // We create a ThreadPlan to keep stepping through using the address range
+      // of the current function.
+      ret_plan_sp.reset(new ThreadPlanStepInRange(thread, range_of_curr_func,
+                                                  sc, eOnlyThisThread,
+                                                  eLazyBoolYes, eLazyBoolYes));
+      return ret_plan_sp;
+    }
+  }
+
+  return ret_plan_sp;
+}

Modified: lldb/trunk/source/Target/ThreadPlanStepThrough.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/ThreadPlanStepThrough.cpp?rev=344371&r1=344370&r2=344371&view=diff
==============================================================================
--- lldb/trunk/source/Target/ThreadPlanStepThrough.cpp (original)
+++ lldb/trunk/source/Target/ThreadPlanStepThrough.cpp Fri Oct 12 10:20:39 2018
@@ -13,6 +13,7 @@
 // Project includes
 #include "lldb/Target/ThreadPlanStepThrough.h"
 #include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Target/CPPLanguageRuntime.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/ObjCLanguageRuntime.h"
 #include "lldb/Target/Process.h"
@@ -95,6 +96,15 @@ void ThreadPlanStepThrough::LookForPlanT
     if (objc_runtime)
       m_sub_plan_sp =
           objc_runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others);
+
+    CPPLanguageRuntime *cpp_runtime =
+        m_thread.GetProcess()->GetCPPLanguageRuntime();
+
+    // If the ObjC runtime did not provide us with a step though plan then if we
+    // have it check the C++ runtime for a step though plan.
+    if (!m_sub_plan_sp.get() && cpp_runtime)
+      m_sub_plan_sp =
+          cpp_runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others);
   }
 
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));




More information about the lldb-commits mailing list