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 @@ -579,6 +579,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" @@ -26,6 +27,7 @@ #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include using namespace lldb; using namespace lldb_private; @@ -560,7 +562,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 +645,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 +656,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 +709,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 +719,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 +767,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 +816,83 @@ return formatStringImpl( valobj, stream, summary_options, "U"); } + +static std::tuple +LibcxxExtractStringViewData(ValueObject& valobj) { + 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 std::make_tuple(false, {}, {}); + + if (!dataobj->GetError().Success() || !sizeobj->GetError().Success()) + return std::make_tuple(false, {}, {}); + + bool success{false}; + uint64_t size = sizeobj->GetValueAsUnsigned(0, &success); + if (!success) + return std::make_tuple(false, {}, {}); + + return std::make_tuple(true,dataobj,size); +} + +template +static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + + bool success; + ValueObjectSP dataobj; + size_t size; + std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj); + + if (!success) { + stream << "Summary Unavailable"; + return true; + } + + 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) { + + bool success; + ValueObjectSP dataobj; + size_t size; + std::tie( success, dataobj, size ) = LibcxxExtractStringViewData(valobj); + + if (!success) { + stream << "Summary Unavailable"; + return true; + } + + + 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,189 @@ +# 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.line1 = line_number('main.cpp', '// Set break point at this line.') + self.line2 = line_number('main.cpp', '// Break here to look at bad string view.' ) + + @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.line1, num_expected_locations=-1) + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.line2, 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) + + # 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"') + self.expect_var_path('oops', + type='std::string_view', + summary='"Hellooo World\\n"') + + # GetSummary returns None so can't be checked by expect_var_path, so we + # use the str representation instead + null_obj = self.frame().GetValueForVariablePath('null_str') + self.assertEqual(null_obj.GetSummary(), "Summary Unavailable") + self.assertEqual(str(null_obj), + '(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"') + + self.runCmd('cont') + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + broken_obj = self.frame().GetValueForVariablePath('in_str_view') + self.assertEqual( broken_obj.GetSummary(), "Summary Unavailable" ) 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,108 @@ +#include +#include +#include + +static size_t touch_string(std::string_view &in_str_view) { + return in_str_view.size(); // Break here to look at bad string view. +} + +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; + + std::string hello = "Hellooo "; + std::string_view oops = hello + "World\n"; + + q_source[0] = 'H'; // Set break point at this line. + + std::string_view *not_a_string_view = nullptr; + touch_string(*not_a_string_view); + + return 0; +}