shafik updated this revision to Diff 169105.
shafik marked 5 inline comments as done.
shafik added a comment.
- Addressing comments
- Adding test to make sure we step through a std::function wrapping a member
variable
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,15 @@
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));
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,76 @@
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;
+}
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,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.
+}
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,77 @@
+"""
+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__)
+
+ 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)
+ 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()
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,19 @@
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
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits