diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -583,6 +583,51 @@ "std::__[[:alnum:]]+::allocator >$"), stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + ConstString("^std::__[[:alnum:]]+::string_view$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + ConstString("^std::__[[:alnum:]]+::basic_string_view >$"), + stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + ConstString("^std::__[[:alnum:]]+::basic_string_view >$"), + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16, + "std::u16string_view summary provider", + ConstString("^std::__[[:alnum:]]+::basic_string_view >$"), + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32, + "std::u32string_view summary provider", + ConstString("^std::__[[:alnum:]]+::basic_string_view >$"), + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringViewSummaryProvider, + "std::wstring_view summary provider", + ConstString("^std::__[[:alnum:]]+::wstring_view$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringViewSummaryProvider, + "std::wstring_view summary provider", + ConstString("^std::__[[:alnum:]]+::basic_string_view >$"), + stl_summary_flags, true); + SyntheticChildren::Flags stl_synth_flags; stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( false); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -34,6 +34,22 @@ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // libc++ std::wstring +bool LibcxxStringViewSummaryProviderASCII( + ValueObject &valueObj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::string_view + +bool LibcxxStringViewSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u16string_view + +bool LibcxxStringViewSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u32string_view + +bool LibcxxWStringViewSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::wstring_view + bool LibcxxOptionalSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // libc++ std::optional<> diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -19,6 +19,7 @@ #include "lldb/Target/ProcessStructReader.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/Status.h" @@ -560,7 +561,7 @@ return {}; ValueObjectSP layout_decider( - D->GetChildAtIndexPath(llvm::ArrayRef({0, 0}))); + D->GetChildAtIndexPath(llvm::ArrayRef({0, 0}))); // this child should exist if (!layout_decider) @@ -643,16 +644,10 @@ return std::make_pair(size, location_sp); } -bool lldb_private::formatters::LibcxxWStringSummaryProvider( - ValueObject &valobj, Stream &stream, - const TypeSummaryOptions &summary_options) { - auto string_info = ExtractLibcxxStringInfo(valobj); - if (!string_info) - return false; - uint64_t size; - ValueObjectSP location_sp; - std::tie(size, location_sp) = *string_info; - +static bool +LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + ValueObjectSP location_sp, size_t size) { if (size == 0) { stream.Printf("L\"\""); return true; @@ -660,7 +655,6 @@ if (!location_sp) return false; - StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); @@ -714,10 +708,9 @@ return false; } -template -bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, - const TypeSummaryOptions &summary_options, - std::string prefix_token) { +bool lldb_private::formatters::LibcxxWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { auto string_info = ExtractLibcxxStringInfo(valobj); if (!string_info) return false; @@ -725,6 +718,17 @@ ValueObjectSP location_sp; std::tie(size, location_sp) = *string_info; + return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, + location_sp, size); +} + +template +static bool +LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, ValueObjectSP location_sp, + uint64_t size) { + if (size == 0) { stream.Printf("\"\""); return true; @@ -762,6 +766,21 @@ return StringPrinter::ReadBufferAndDumpToStream(options); } +template +static bool +LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + auto string_info = ExtractLibcxxStringInfo(valobj); + if (!string_info) + return false; + uint64_t size; + ValueObjectSP location_sp; + std::tie(size, location_sp) = *string_info; + + return LibcxxStringSummaryProvider( + valobj, stream, summary_options, prefix_token, location_sp, size); +} template static bool formatStringImpl(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &summary_options, @@ -796,3 +815,62 @@ return formatStringImpl( valobj, stream, summary_options, "U"); } + +template +static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + + // extract the size/location from the string_view object + ConstString g_data_name("__data"); + ConstString g_size_name("__size"); + auto dataobj = valobj.GetChildMemberWithName(g_data_name, true); + auto sizeobj = valobj.GetChildMemberWithName(g_size_name, true); + + if (!dataobj || !sizeobj) + return false; + + uint64_t size = sizeobj->GetValueAsUnsigned(0); + + return LibcxxStringSummaryProvider( + valobj, stream, summary_options, prefix_token, dataobj, size); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl( + valobj, stream, summary_options, ""); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl( + valobj, stream, summary_options, "u"); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl( + valobj, stream, summary_options, "U"); +} + +bool lldb_private::formatters::LibcxxWStringViewSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + + ConstString g_data_name("__data"); + ConstString g_size_name("__size"); + auto dataobj = valobj.GetChildMemberWithName(g_data_name, true); + auto sizeobj = valobj.GetChildMemberWithName(g_size_name, true); + + if (!dataobj || !sizeobj) + return false; + + uint64_t size = sizeobj->GetValueAsUnsigned(0); + + return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, + dataobj, size); +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/Makefile @@ -0,0 +1,6 @@ +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 + +CXXFLAGS_EXTRAS := -std=c++11 -O0 +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py @@ -0,0 +1,180 @@ +# coding=utf8 +""" +Test lldb data formatter subsystem. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class LibcxxStringViewDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + self.namespace = 'std' + + @add_test_categories(["libc++"]) + @expectedFailureAll(bugnumber="llvm.org/pr36109", debug_info="gmodules", triple=".*-android") + # Inline namespace is randomly ignored as Clang due to broken lookup inside + # the std namespace. + @expectedFailureAll(debug_info="gmodules") + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd( + "settings set target.max-children-count 256", + check=False) + + is_64_bit = self.process().GetAddressByteSize() == 8 + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect_var_path( 'wempty', + type = 'std::wstring_view', + summary = 'L""') + self.expect_var_path( 's', + type = 'std::wstring_view', + summary = 'L"hello world! מזל טוב!"') + self.expect_var_path( 'S', + type = 'std::wstring_view', + summary = 'L"!!!!"') + self.expect_var_path( 'empty', + type = 'std::string_view', + summary = '""') + self.expect_var_path( 'q_source', + type = 'std::string', + summary = '"hello world"') + self.expect_var_path( 'q', + type = 'std::string_view', + summary = '"hello world"') + self.expect_var_path( 'Q', + type = 'std::string_view', + summary = '"quite a long std::strin with lots of info inside it"') + self.expect_var_path( 'IHaveEmbeddedZeros', + type = 'std::string_view', + summary = '"a\\0b\\0c\\0d"') + self.expect_var_path( 'IHaveEmbeddedZerosToo', + type = 'std::wstring_view', + summary = 'L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"') + self.expect_var_path( 'u16_string', + type = 'std::u16string_view', + summary = 'u"ß水氶"') + self.expect_var_path( 'u16_empty', + type = 'std::u16string_view', + summary = '""') + self.expect_var_path( 'u32_string', + type = 'std::u32string_view', + summary = 'U"🍄🍅🍆🍌"') + self.expect_var_path( 'u32_empty', + type = 'std::u32string_view', + summary = '""') + self.expect_var_path( 'uchar_source', + type = 'std::basic_string, std::allocator >', + summary = '"aaaaaaaaaa"' ) + self.expect_var_path( 'uchar', + type = 'std::basic_string_view >', + summary = '"aaaaa"' ) + + # NOTE: API GetSummary returns '::', but frame variable displays + # 'nullptr' but frame variable returns "nullptr" + self.expect_var_path( 'null_str', + type = 'std::string_view *', + summary = '""' ) + self.expect( "frame variable", substrs = [ + '(std::string_view *) null_str = nullptr' + ] ) + + self.runCmd("n") + + TheVeryLongOne = self.frame().FindVariable("TheVeryLongOne") + summaryOptions = lldb.SBTypeSummaryOptions() + summaryOptions.SetCapping(lldb.eTypeSummaryUncapped) + uncappedSummaryStream = lldb.SBStream() + TheVeryLongOne.GetSummary(uncappedSummaryStream, summaryOptions) + uncappedSummary = uncappedSummaryStream.GetData() + self.assertTrue(uncappedSummary.find("someText") > 0, + "uncappedSummary does not include the full string") + summaryOptions.SetCapping(lldb.eTypeSummaryCapped) + cappedSummaryStream = lldb.SBStream() + TheVeryLongOne.GetSummary(cappedSummaryStream, summaryOptions) + cappedSummary = cappedSummaryStream.GetData() + self.assertTrue( + cappedSummary.find("someText") <= 0, + "cappedSummary includes the full string") + + self.expect_expr("s", result_type="std::wstring_view", result_summary='L"hello world! מזל טוב!"') + + self.expect_var_path( 'wempty', + type = 'std::wstring_view', + summary = 'L""') + self.expect_var_path( 's', + type = 'std::wstring_view', + summary = 'L"hello world! מזל טוב!"') + self.expect_var_path( 'S', + type = 'std::wstring_view', + summary = 'L"!!!!"') + self.expect_var_path( 'empty', + type = 'std::string_view', + summary = '""') + self.expect_var_path( 'q_source', + type = 'std::string', + summary = '"Hello world"') + self.expect_var_path( 'q', + type = 'std::string_view', + summary = '"Hello world"') + self.expect_var_path( 'Q', + type = 'std::string_view', + summary = '"quite a long std::strin with lots of info inside it"') + self.expect_var_path( 'IHaveEmbeddedZeros', + type = 'std::string_view', + summary = '"a\\0b\\0c\\0d"') + self.expect_var_path( 'IHaveEmbeddedZerosToo', + type = 'std::wstring_view', + summary = 'L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"') + self.expect_var_path( 'u16_string', + type = 'std::u16string_view', + summary = 'u"ß水氶"') + self.expect_var_path( 'u16_empty', + type = 'std::u16string_view', + summary = '""') + self.expect_var_path( 'u32_string', + type = 'std::u32string_view', + summary = 'U"🍄🍅🍆🍌"') + self.expect_var_path( 'u32_empty', + type = 'std::u32string_view', + summary = '""') + self.expect_var_path( 'uchar_source', + type = 'std::basic_string, std::allocator >', + summary = '"aaaaaaaaaa"' ) + self.expect_var_path( 'uchar', + type = 'std::basic_string_view >', + summary = '"aaaaa"' ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/main.cpp @@ -0,0 +1,97 @@ +#include +#include +#include + +int main() { + std::wstring_view wempty(L""); + std::wstring_view s(L"hello world! מזל טוב!"); + std::wstring_view S(L"!!!!"); + std::string_view empty(""); + std::string q_source = "hello world"; + std::string_view q(q_source); + std::string_view Q("quite a long std::strin with lots of info inside it"); + std::string_view TheVeryLongOne( + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "9012345678901234567890123456789012345678901234567890someText123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890123456789012" + "345678901234567890123456789012345678901234567890123456789012345678901234" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "789012345678901234567890123456789012345678901234567890123456789012345678" + "901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); + std::string_view IHaveEmbeddedZeros("a\0b\0c\0d", 7); + std::wstring_view IHaveEmbeddedZerosToo( + L"hello world!\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監", 38); + std::u16string_view u16_string(u"ß水氶"); + std::u16string_view u16_empty(u""); + std::u32string_view u32_string(U"🍄🍅🍆🍌"); + std::u32string_view u32_empty(U""); + std::basic_string uchar_source(10, 'a'); + std::basic_string_view uchar(uchar_source.data(), 5); + std::string_view *null_str = nullptr; + + q_source[0] = 'H'; // Set break point at this line. + return 0; +}