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

- Adding support to step into the callable wrapped by libc++ std::function
- Adding test to validate that functionality


https://reviews.llvm.org/D52851

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

Index: source/Target/ThreadPlanStepThrough.cpp
===================================================================
--- source/Target/ThreadPlanStepThrough.cpp
+++ source/Target/ThreadPlanStepThrough.cpp
@@ -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,13 @@
     if (objc_runtime)
       m_sub_plan_sp =
           objc_runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others);
+
+    CPPLanguageRuntime *cpp_runtime =
+        m_thread.GetProcess()->GetCPPLanguageRuntime();
+
+    if (cpp_runtime)
+      m_sub_plan_sp =
+          cpp_runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others);
   }
 
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
Index: source/Target/CPPLanguageRuntime.cpp
===================================================================
--- source/Target/CPPLanguageRuntime.cpp
+++ source/Target/CPPLanguageRuntime.cpp
@@ -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 @@
   // 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,77 @@
 
   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.
+  // Currently due to the the:
+  //
+  //    target.process.thread.step-avoid-regexp
+  //
+  // setting we will currenly step right out of standard library code.
+  //
+  // The new 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()) {
+      ret_plan_sp.reset(new ThreadPlanRunToAddress(
+          thread, callable_info.callable_address, stop_others));
+      return ret_plan_sp;
+    } else {
+      ret_plan_sp.reset(new ThreadPlanStepInRange(thread, range_of_curr_func,
+                                                  sc, eOnlyThisThread,
+                                                  eLazyBoolYes, eLazyBoolYes));
+      return ret_plan_sp;
+    }
+  }
+
+  return ret_plan_sp;
+}
Index: packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/main.cpp
@@ -0,0 +1,44 @@
+//===-- main.cpp --------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <functional>
+
+int foo(int x, int y) {
+  return x + y - 1;
+}
+
+struct Bar {
+   int operator()() {
+       return 66 ;
+   }
+   int add_num(int i) const { return i + 3 ; }
+} ;
+
+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);
+  };
+
+  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 ;
+
+  Bar bar1 ;
+  std::function<int ()> f4( bar1 ) ;
+  std::function<int (const Bar&, int)> f5 = &Bar::add_num;
+
+  return f1(acc,acc) +     // Set break point at this line.
+         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.
+}
Index: packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py
@@ -0,0 +1,73 @@
+"""
+Test lldb data formatter subsystem.
+"""
+
+from __future__ import print_function
+
+
+import os
+import time
+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__)
+
+    def get_variable(self, name):
+        var = self.frame().FindVariable(name)
+        var.SetPreferDynamicValue(lldb.eDynamicCanRunTarget)
+        var.SetPreferSyntheticValue(True)
+        return var
+
+    @add_test_categories(["libc++"])
+    def test(self):
+        """Test that std::function as defined by libc++ is correctly printed by LLDB"""
+        self.build()
+        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+        exe = self.getBuildArtifact("a.out")
+        self.main_source = "main.cpp"
+        self.main_source_spec = lldb.SBFileSpec(self.main_source)
+
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        break_in_main = target.BreakpointCreateBySourceRegex('// Set break point at this line.', self.main_source_spec)
+        self.assertTrue(break_in_main, VALID_BREAKPOINT)
+
+        self.process = target.LaunchSimple(
+            None, None, self.get_process_working_directory())
+
+        self.assertTrue(self.process, PROCESS_IS_VALID)
+
+        threads = lldbutil.get_stopped_threads(
+            self.process, lldb.eStopReasonBreakpoint)
+        self.assertTrue(
+            len(threads) == 1,
+            "Successfully ran to breakpoint.")
+        self.thread = threads[0]
+
+        self.thread.StepInto()
+        self.assertEqual( self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), 13 ) ;
+        self.process.Continue()
+
+        self.thread.StepInto()
+        self.assertEqual( self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), 28 ) ;
+        self.process.Continue()
+
+        self.thread.StepInto()
+        self.assertEqual( self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), 31 ) ;
+        self.process.Continue()
+
+        self.thread.StepInto()
+        self.assertEqual( self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), 18 ) ;
+        self.process.Continue()
+
+        self.thread.StepInto()
+        self.assertEqual( self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), 20 ) ;
+        self.process.Continue()
Index: packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/cpp/std-function-step-into-callable/Makefile
@@ -0,0 +1,7 @@
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+CXXFLAGS += -std=c++11
+USE_LIBCPP := 1
+
+include $(LEVEL)/Makefile.rules
Index: include/lldb/Target/CPPLanguageRuntime.h
===================================================================
--- include/lldb/Target/CPPLanguageRuntime.h
+++ include/lldb/Target/CPPLanguageRuntime.h
@@ -56,6 +56,9 @@
   bool GetObjectDescription(Stream &str, Value &value,
                             ExecutionContextScope *exe_scope) override;
 
+  lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+                                                  bool stop_others);
+
 protected:
   //------------------------------------------------------------------
   // Classes that inherit from CPPLanguageRuntime can see and modify these
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to