Michael137 updated this revision to Diff 436077. Michael137 added a comment.
- Updated tests to avoid using `self.expect` and instead the more specific assertions from `lldbtest` and `SB API` Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D127481/new/ https://reviews.llvm.org/D127481 Files: lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxx.h lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/Makefile lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/TestDataFormatterLibcxxSpan.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/main.cpp
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/main.cpp =================================================================== --- /dev/null +++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/main.cpp @@ -0,0 +1,56 @@ +#include <array> +#include <span> +#include <stdio.h> +#include <string> +#include <vector> + +template <class T, size_t N> +void by_ref_and_ptr(std::span<T, N> &ref, std::span<T, N> *ptr) { + printf("Stop here to check by ref"); + return; +} + +int main() { + std::array numbers = {1, 12, 123, 1234, 12345}; + + using dynamic_string_span = std::span<std::string>; + + // Test span of ints + + // Full view of numbers with static extent + std::span numbers_span = numbers; + + printf("break here"); + + by_ref_and_ptr(numbers_span, &numbers_span); + + // Test spans of strings + std::vector<std::string> strings{"goofy", "is", "smart", "!!!"}; + strings.reserve(strings.size() + 1); + + // Partial view of strings with dynamic extent + dynamic_string_span strings_span{std::span{strings}.subspan(2)}; + + auto strings_span_it = strings_span.begin(); + + printf("break here"); + + // Vector size doesn't increase, span should + // print unchanged and the strings_span_it + // remains valid + strings.emplace_back("???"); + + printf("break here"); + + // Now some empty spans + std::span<int, 0> static_zero_span; + std::span<int> dynamic_zero_span; + + // Multiple spans + std::array span_arr{strings_span, strings_span}; + std::span<std::span<std::string>, 2> nested = span_arr; + + printf("break here"); + + return 0; // break here +} Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/TestDataFormatterLibcxxSpan.py =================================================================== --- /dev/null +++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/TestDataFormatterLibcxxSpan.py @@ -0,0 +1,153 @@ +""" +Test lldb data formatter subsystem for std::span +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class LibcxxSpanDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def findVariable(self, name): + var = self.frame().FindVariable(name) + self.assertTrue(var.IsValid()) + return var + + def check_size(self, var_name, size): + var = self.findVariable(var_name) + self.assertEqual(var.GetNumChildren(), size) + + def check_numbers(self, var_name): + """Helper to check that data formatter sees contents of std::span correctly""" + + expectedSize = 5 + self.check_size(var_name, expectedSize) + + self.expect_expr( + var_name, result_type=f'std::span<int, {expectedSize}>', + result_summary=f'size={expectedSize}', + result_children=[ + ValueCheck(name='[0]', value='1'), + ValueCheck(name='[1]', value='12'), + ValueCheck(name='[2]', value='123'), + ValueCheck(name='[3]', value='1234'), + ValueCheck(name='[4]', value='12345') + ]) + + # check access-by-index + self.expect_var_path(f'{var_name}[0]', type='int', value='1') + self.expect_var_path(f'{var_name}[1]', type='int', value='12') + self.expect_var_path(f'{var_name}[2]', type='int', value='123') + self.expect_var_path(f'{var_name}[3]', type='int', value='1234') + self.expect_var_path(f'{var_name}[4]', type='int', value='12345') + + @add_test_categories(['libc++']) + @skipIf(compiler='clang', compiler_version=['<', '11.0']) + def test_with_run_command(self): + """Test that std::span variables are formatted correctly when printed.""" + self.build() + (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, 'break here', lldb.SBFileSpec('main.cpp', False)) + + lldbutil.continue_to_breakpoint(process, bkpt) + + # std::span of std::array with extents known at compile-time + self.check_numbers('numbers_span') + + # check access to synthetic children for static spans + self.runCmd('type summary add --summary-string "item 0 is ${var[0]}" -x "std::span<" span') + self.expect_expr('numbers_span', + result_type='std::span<int, 5>', + result_summary='item 0 is 1') + + self.runCmd('type summary add --summary-string "item 0 is ${svar[0]}" -x "std::span<" span') + self.expect_expr('numbers_span', + result_type='std::span<int, 5>', + result_summary='item 0 is 1') + + self.runCmd('type summary delete span') + + # New span with strings + lldbutil.continue_to_breakpoint(process, bkpt) + + expectedStringSpanChildren = [ ValueCheck(name='[0]', summary='"smart"'), + ValueCheck(name='[1]', summary='"!!!"') ] + + self.expect_var_path('strings_span', + summary='size=2', + children=expectedStringSpanChildren) + + # check access to synthetic children for dynamic spans + self.runCmd('type summary add --summary-string "item 0 is ${var[0]}" dynamic_string_span') + self.expect_var_path('strings_span', summary='item 0 is "smart"') + + self.runCmd('type summary add --summary-string "item 0 is ${svar[0]}" dynamic_string_span') + self.expect_var_path('strings_span', summary='item 0 is "smart"') + + self.runCmd('type summary delete dynamic_string_span') + + # test summaries based on synthetic children + self.runCmd( + 'type summary add --summary-string "span has ${svar%#} items" -e dynamic_string_span') + + self.expect_var_path('strings_span', summary='span has 2 items') + + self.expect_var_path('strings_span', + summary='span has 2 items', + children=expectedStringSpanChildren) + + # check access-by-index + self.expect_var_path('strings_span[0]', summary='"smart"'); + self.expect_var_path('strings_span[1]', summary='"!!!"'); + + # Newly inserted value not visible to span + lldbutil.continue_to_breakpoint(process, bkpt) + + self.expect_expr('strings_span', + result_summary='span has 2 items', + result_children=expectedStringSpanChildren) + + self.runCmd('type summary delete dynamic_string_span') + + lldbutil.continue_to_breakpoint(process, bkpt) + + # Empty spans + self.expect_expr('static_zero_span', + result_type='std::span<int, 0>', + result_summary='size=0') + self.check_size('static_zero_span', 0) + + self.expect_expr('dynamic_zero_span', + result_summary='size=0') + self.check_size('dynamic_zero_span', 0) + + # Nested spans + self.expect_expr('nested', + result_summary='size=2', + result_children=[ + ValueCheck(name='[0]', summary='size=2', + children=expectedStringSpanChildren), + ValueCheck(name='[1]', summary='size=2', + children=expectedStringSpanChildren) + ]) + self.check_size('nested', 2) + + @add_test_categories(['libc++']) + @skipIf(compiler='clang', compiler_version=['<', '11.0']) + def test_ref_and_ptr(self): + """Test that std::span is correctly formatted when passed by ref and ptr""" + self.build() + (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, 'Stop here to check by ref', lldb.SBFileSpec('main.cpp', False)) + + # The reference should display the same was as the value did + self.check_numbers('ref') + + # The pointer should just show the right number of elements: + + ptrAddr = self.findVariable('ptr').GetValue() + self.expect_expr('ptr', result_type='std::span<int, 5> *', + result_summary=f'{ptrAddr} size=5') Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/Makefile =================================================================== --- /dev/null +++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/Makefile @@ -0,0 +1,7 @@ +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 + +CXXFLAGS_EXTRAS := -std=c++20 + +include Makefile.rules Index: lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -0,0 +1,154 @@ +//===-- LibCxxSpan.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/APSInt.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class LibcxxStdSpanSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdSpanSyntheticFrontEnd() override; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + /// Determines properties of the std::span<> associated with this object + // + // std::span can either be instantiated with a compile-time known + // extent or a std::dynamic_extent (this is the default if only the + // type template argument is provided). The layout of std::span + // depends on whether the extent is dynamic or not. For static + // extents (e.g., std::span<int, 9>): + // + // (std::__1::span<const int, 9>) s = { + // __data = 0x000000016fdff494 + // } + // + // For dynamic extents, e.g., std::span<int>, the layout is: + // + // (std::__1::span<const int, 18446744073709551615>) s = { + // __data = 0x000000016fdff494 + // __size = 6 + // } + // + // This function checks for a '__size' member to determine the number + // of elements in the span. If no such member exists, we get the size + // from the only other place it can be: the template argument. + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; ///< First element of span. Held, not owned. + CompilerType m_element_type{}; ///< Type of span elements. + size_t m_num_elements = 0; ///< Number of elements in span. + uint32_t m_element_size = 0; ///< Size in bytes of each span element. +}; + +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + ~LibcxxStdSpanSyntheticFrontEnd() {} + +size_t lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + CalculateNumChildren() { + return m_num_elements; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::GetChildAtIndex( + size_t idx) { + if (!m_start) + return {}; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +bool lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::Update() { + // Get element type. + ValueObjectSP data_type_finder_sp( + m_backend.GetChildMemberWithName(ConstString("__data"), true)); + if (!data_type_finder_sp) + return false; + + m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + + // Get element size. + if (llvm::Optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + + // Get data. + if (m_element_size > 0) { + m_start = data_type_finder_sp.get(); + } + + // Get number of elements. + if (auto size_sp = + m_backend.GetChildMemberWithName(ConstString("__size"), true)) { + m_num_elements = size_sp->GetValueAsUnsigned(0); + } else if (auto arg = + m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) { + + m_num_elements = arg->value.getLimitedValue(); + } + } + + return true; +} + +bool lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid() || type.GetNumTemplateArguments() != 2) + return nullptr; + return new LibcxxStdSpanSyntheticFrontEnd(valobj_sp); +} + +} // namespace formatters +} // namespace lldb_private Index: lldb/source/Plugins/Language/CPlusPlus/LibCxx.h =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -74,6 +74,10 @@ bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +/// Formatter for libc++ std::span<>. +bool LibcxxSpanSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); @@ -193,6 +197,10 @@ LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp); +SyntheticChildrenFrontEnd * +LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + } // namespace formatters } // namespace lldb_private Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -756,6 +756,12 @@ lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator, "libc++ std::atomic synthetic children", ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdSpanSyntheticFrontEndCreator, + "libc++ std::span synthetic children", + ConstString("^std::__[[:alnum:]]+::span<.+>(( )?&)?$"), stl_deref_flags, + true); cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( RegularExpression("^(std::__[[:alnum:]]+::)deque<.+>(( )?&)?$"), @@ -869,6 +875,11 @@ "libc++ std::variant summary provider", ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::span summary provider", + ConstString("^std::__[[:alnum:]]+::span<.+>(( )?&)?$"), + stl_summary_flags, true); stl_summary_flags.SetSkipPointers(true); Index: lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -11,6 +11,7 @@ LibCxxList.cpp LibCxxMap.cpp LibCxxQueue.cpp + LibCxxSpan.cpp LibCxxTuple.cpp LibCxxUnorderedMap.cpp LibCxxVariant.cpp
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits