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']) 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 f1 = foo; std::function 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 f3 = argc %2 ? f : g ; + + return f1(acc,acc) + f2(acc) + f3(acc+1,acc+2); // Set break point at this line. } 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: 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 ... + // ^^^^^^^^^ + // + // 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; -}