diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py index 8c11770899f7..0daa95f36d0d 100644 --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -13,7 +13,7 @@ class StdListSynthProvider: logger = lldb.formatters.Logger.Logger() self.valobj = valobj self.count = None - logger >> "Providing synthetic children for a map named " + str(valobj.GetName()) + logger >> "Providing synthetic children for a list named " + str(valobj.GetName()) def next_node(self,node): logger = lldb.formatters.Logger.Logger() @@ -21,11 +21,20 @@ class StdListSynthProvider: def is_valid(self,node): logger = lldb.formatters.Logger.Logger() - return self.value(self.next_node(node)) != self.node_address + valid = self.value(self.next_node(node)) != self.node_address + if valid: + logger >> "%s is valid" % str(self.valobj.GetName()) + else: + logger >> "synthetic value is not valid" + return valid def value(self,node): logger = lldb.formatters.Logger.Logger() - return node.GetValueAsUnsigned() + value = node.GetValueAsUnsigned() + logger >> "synthetic value for {}: {}".format( + str(self.valobj.GetName()), + value) + return value # Floyd's cycle-finding algorithm # try to detect if this list has a loop @@ -49,7 +58,12 @@ class StdListSynthProvider: def num_children(self): logger = lldb.formatters.Logger.Logger() - if self.count == None: + if self.count is None: + # libstdc++ 6.0.21 added dedicated count field. + count_child = self.node.GetChildMemberWithName('_M_data') + if count_child and count_child.IsValid(): + self.count = count_child.GetValueAsUnsigned(0) + if self.count is None: self.count = self.num_children_impl() return self.count @@ -117,10 +131,10 @@ class StdListSynthProvider: self.count = None try: impl = self.valobj.GetChildMemberWithName('_M_impl') - node = impl.GetChildMemberWithName('_M_node') + self.node = impl.GetChildMemberWithName('_M_node') self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) - self.next = node.GetChildMemberWithName('_M_next') - self.prev = node.GetChildMemberWithName('_M_prev') + self.next = self.node.GetChildMemberWithName('_M_next') + self.prev = self.node.GetChildMemberWithName('_M_prev') self.data_type = self.extract_type() self.data_size = self.data_type.GetByteSize() except: diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index f09e40378903..b5634ec4d246 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -545,7 +545,14 @@ LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) lldb::TypeSummaryImplSP std_string_summary_sp(new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}")); - + + lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, + LibStdcppStringSummaryProvider, + "libstdc++ c++11 std::string summary provider")); + lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, + LibStdcppWStringSummaryProvider, + "libstdc++ c++11 std::wstring summary provider")); + cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::string"), std_string_summary_sp); cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string"), @@ -554,7 +561,12 @@ LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) std_string_summary_sp); cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string, std::allocator >"), std_string_summary_sp); - + + cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__cxx11::string"), + cxx11_string_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__cxx11::basic_string, std::allocator >"), + cxx11_string_summary_sp); + // making sure we force-pick the summary for printing wstring (_M_p is a wchar_t*) lldb::TypeSummaryImplSP std_wstring_summary_sp(new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}")); @@ -567,8 +579,12 @@ LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) std_wstring_summary_sp); cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string, std::allocator >"), std_wstring_summary_sp); - - + + cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__cxx11::wstring"), + cxx11_wstring_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__cxx11::basic_string, std::allocator >"), + cxx11_wstring_summary_sp); + #ifndef LLDB_DISABLE_PYTHON SyntheticChildren::Flags stl_synth_flags; @@ -580,9 +596,16 @@ LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider"))); - cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); +#if 0 + // With only this, I get std::list showing the content, all children on the same line. + // With this and the section below, I see one child element per line. + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::__cxx11::_List_base<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); +#endif stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(true); cpp_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), @@ -591,10 +614,16 @@ LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) cpp_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); - cpp_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + cpp_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$")), TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); - +#if 0 + // With this, I get std::list showing one child per line. Requires the change above to get anything, though. + cpp_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::__cxx11::_List_base<.+>(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); +#endif + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true); AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp index 14f7527d73a2..ed89c5c84ea3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/StringPrinter.h" #include "lldb/DataFormatters/VectorIterator.h" #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" @@ -256,3 +257,117 @@ lldb_private::formatters::VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithNa lldb_private::formatters::VectorIteratorSyntheticFrontEnd::~VectorIteratorSyntheticFrontEnd () { } + +bool +lldb_private::formatters::LibStdcppStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ + const bool scalar_is_load_addr = true; + AddressType addr_type; + lldb::addr_t addr_of_string = valobj.GetAddressOf(scalar_is_load_addr, &addr_type); + if (addr_of_string != LLDB_INVALID_ADDRESS) + { + switch (addr_type) + { + case eAddressTypeLoad: + { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + Error error; + lldb::addr_t addr_of_data = process_sp->ReadPointerFromMemory(addr_of_string, error); + if (error.Fail() || addr_of_data == 0 || addr_of_data == LLDB_INVALID_ADDRESS) + return false; + options.SetLocation(addr_of_data); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetNeedsZeroTermination(false); + options.SetBinaryZeroIsTerminator(true); + lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory(addr_of_string + process_sp->GetAddressByteSize(), error); + if (error.Fail()) + return false; + options.SetSourceSize(size_of_data); + + if (!StringPrinter::ReadStringAndDumpToStream(options)) + { + stream.Printf("Summary Unavailable"); + return true; + } + else + return true; + } + break; + case eAddressTypeHost: + break; + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + } + } + return false; +} + +bool +lldb_private::formatters::LibStdcppWStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ + const bool scalar_is_load_addr = true; + AddressType addr_type; + lldb::addr_t addr_of_string = valobj.GetAddressOf(scalar_is_load_addr, &addr_type); + if (addr_of_string != LLDB_INVALID_ADDRESS) + { + switch (addr_type) + { + case eAddressTypeLoad: + { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + CompilerType wchar_compiler_type = valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + const uint32_t wchar_size = wchar_compiler_type.GetBitSize(nullptr); // Safe to pass NULL for exe_scope here + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + Error error; + lldb::addr_t addr_of_data = process_sp->ReadPointerFromMemory(addr_of_string, error); + if (error.Fail() || addr_of_data == 0 || addr_of_data == LLDB_INVALID_ADDRESS) + return false; + options.SetLocation(addr_of_data); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetNeedsZeroTermination(false); + options.SetBinaryZeroIsTerminator(false); + lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory(addr_of_string + process_sp->GetAddressByteSize(), error); + if (error.Fail()) + return false; + options.SetSourceSize(size_of_data); + options.SetPrefixToken("L"); + + switch (wchar_size) + { + case 8: + return StringPrinter::ReadStringAndDumpToStream(options); + case 16: + return StringPrinter::ReadStringAndDumpToStream(options); + case 32: + return StringPrinter::ReadStringAndDumpToStream(options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; + } + break; + case eAddressTypeHost: + break; + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + } + } + return false; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h index 21b2ea44fc80..347856a1695c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -18,6 +18,12 @@ namespace lldb_private { namespace formatters { + bool + LibStdcppStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options); // libcstdc++ c++11 std::string + + bool + LibStdcppWStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options); // libcstdc++ c++11 std::wstring + SyntheticChildrenFrontEnd* LibstdcppMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); SyntheticChildrenFrontEnd* LibStdcppVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp index 8b872398d4e3..6a1266528d5e 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/loop/main.cpp @@ -15,6 +15,7 @@ typedef std::list int_list; int main() { +#ifdef LLDB_USING_LIBCPP int_list *numbers_list = new int_list{1,2,3,4,5,6,7,8,9,10}; auto *third_elem = numbers_list->__end_.__next_->__next_->__next_; // Set break point at this line. @@ -22,6 +23,7 @@ int main() auto *fifth_elem = third_elem->__next_->__next_; assert(fifth_elem->__value_ == 5); fifth_elem->__next_ = third_elem; +#endif // Any attempt to free the list will probably crash the program. Let's just leak it. return 0; // Set second break point at this line. diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py index b34e0e430bfc..1a13571c4d6a 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/TestDataFormatterStdString.py @@ -47,21 +47,21 @@ class StdStringDataFormatterTestCase(TestBase): # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) - self.expect("frame variable", - substrs = ['(std::wstring) s = L"hello world! מזל טוב!"', - '(std::wstring) S = L"!!!!"', - '(const wchar_t *) mazeltov = 0x','L"מזל טוב"', - '(std::string) q = "hello world"', - '(std::string) Q = "quite a long std::strin with lots of info inside it"']) + var_s = self.frame().FindVariable('s') + var_S = self.frame().FindVariable('S') + var_mazeltov = self.frame().FindVariable('mazeltov') + var_q = self.frame().FindVariable('q') + var_Q = self.frame().FindVariable('Q') - self.runCmd("n") + self.assertTrue(var_s.GetSummary() == 'L"hello world! מזל טוב!"', "s summary wrong") + self.assertTrue(var_S.GetSummary() == 'L"!!!!"', "S summary wrong") + self.assertTrue(var_mazeltov.GetSummary() == 'L"מזל טוב"', "mazeltov summary wrong") + self.assertTrue(var_q.GetSummary() == '"hello world"', "q summary wrong") + self.assertTrue(var_Q.GetSummary() == '"quite a long std::strin with lots of info inside it"', "Q summary wrong") - self.expect("frame variable", - substrs = ['(std::wstring) s = L"hello world! מזל טוב!"', - '(std::wstring) S = L"!!!!!"', - '(const wchar_t *) mazeltov = 0x','L"מזל טוב"', - '(std::string) q = "hello world"', - '(std::string) Q = "quite a long std::strin with lots of info inside it"']) + self.runCmd("next") + + self.assertTrue(var_S.GetSummary() == 'L"!!!!!"', "new S summary wrong") if __name__ == '__main__': import atexit diff --git a/lldb/test/make/Makefile.rules b/lldb/test/make/Makefile.rules index 796a7a0453ee..e72b2d0931e8 100644 --- a/lldb/test/make/Makefile.rules +++ b/lldb/test/make/Makefile.rules @@ -296,12 +296,12 @@ ifeq (1,$(USE_LIBCPP)) ifeq "$(OS)" "Linux" # This is the default install location on Ubuntu 14.04 ifneq ($(wildcard /usr/include/c++/v1/.),) - CXXFLAGS += -stdlib=libc++ + CXXFLAGS += -stdlib=libc++ -DLLDB_USING_LIBCPP LDFLAGS += -stdlib=libc++ CXXFLAGS += -I/usr/include/c++/v1 endif else - CXXFLAGS += -stdlib=libc++ + CXXFLAGS += -stdlib=libc++ -DLLDB_USING_LIBCPP LDFLAGS += -stdlib=libc++ endif endif