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 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: 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/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 + +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 f1 = foo; + std::function 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 f3 = argc %2 ? f : g ; + + Bar bar1 ; + std::function f4( bar1 ) ; + std::function 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: 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: 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));