https://github.com/cjdb updated https://github.com/llvm/llvm-project/pull/202438
>From 1e0ea28e5101ac228c3cead2d047e795faea3eb0 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Fri, 5 Jun 2026 22:49:49 +0000 Subject: [PATCH 1/8] Teach LLDB's pretty-printer about libc++'s various `std::vector` layouts PR #155330 changes `std::vector` from unconditionally using three pointers to represent its layout to potentially using three pointers or a begin pointer and two integers. This commit changes LLDB so that it can robustly work with the legacy vector layout, the new pointer layout, and the new size-based layout. --- .../Language/CPlusPlus/LibCxxVector.cpp | 60 +++--- ...taFormatterLibcxxInvalidVectorSimulator.py | 76 +++++++ .../libcxx-simulators/invalid-vector/main.cpp | 190 ++++++++++++++++-- .../libcxx-simulators/vector/Makefile | 3 + .../TestDataFormatterLibcxxVectorSimulator.py | 44 ++++ .../libcxx-simulators/vector/main.cpp | 78 +++++++ 6 files changed, 413 insertions(+), 38 deletions(-) create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index 95d12e8ee4f06..bf9010a304197 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -37,6 +37,8 @@ class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; private: + lldb::ChildCacheState UpdateVectorWithLayoutSubobject(ValueObject *layout); + ValueObject *m_start = nullptr; ValueObject *m_finish = nullptr; CompilerType m_element_type; @@ -126,40 +128,50 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex( m_element_type); } -static ValueObjectSP GetDataPointer(ValueObject &root) { - auto [cap_sp, is_compressed_pair] = - GetValueOrOldCompressedPair(root, "__cap_", "__end_cap_"); - if (!cap_sp) - return nullptr; - - if (is_compressed_pair) - return GetFirstValueOfLibCXXCompressedPair(*cap_sp); - - return cap_sp; -} - lldb::ChildCacheState lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() { m_start = m_finish = nullptr; - ValueObjectSP data_sp(GetDataPointer(m_backend)); - if (!data_sp) + // Determine if this version of libc++'s `std::vector` uses `__vector_layout`. + ValueObjectSP layout_sp = m_backend.GetChildMemberWithName("__layout_"); + ValueObject *target = layout_sp ? layout_sp.get() : &m_backend; + + ValueObjectSP begin_sp = target->GetChildMemberWithName("__begin_"); + if (!begin_sp) return lldb::ChildCacheState::eRefetch; - m_element_type = data_sp->GetCompilerType().GetPointeeType(); + m_element_type = begin_sp->GetCompilerType().GetPointeeType(); llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr); - if (!size_or_err) + if (!size_or_err) { LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(), "{0}"); - else { - m_element_size = *size_or_err; - - if (m_element_size > 0) { - // store raw pointers or end up with a circular dependency - m_start = m_backend.GetChildMemberWithName("__begin_").get(); - m_finish = m_backend.GetChildMemberWithName("__end_").get(); - } + return lldb::ChildCacheState::eRefetch; + } + + m_element_size = *size_or_err; + if (m_element_size == 0) { + return lldb::ChildCacheState::eRefetch; } + + // store raw pointers or end up with a circular dependency + m_start = begin_sp.get(); + + if (ValueObjectSP end_sp = target->GetChildMemberWithName("__end_")) { + m_finish = end_sp.get(); + return lldb::ChildCacheState::eRefetch; + } + + ValueObjectSP size_sp = target->GetChildMemberWithName("__size_"); + if (!size_sp || !size_sp->GetCompilerType().IsInteger()) + return lldb::ChildCacheState::eRefetch; + + uint64_t begin_addr = m_start->GetValueAsUnsigned(0); + uint64_t size = size_sp->GetValueAsUnsigned(0); + uint64_t end_addr = begin_addr + size * m_element_size; + m_finish = CreateChildValueObjectFromAddress( + "__end_", end_addr, m_backend.GetExecutionContextRef(), + m_start->GetCompilerType(), false) + .get(); return lldb::ChildCacheState::eRefetch; } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py index c3d51a49c3f5b..e4783746b3499 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py @@ -37,5 +37,81 @@ def test(self): ) self.expect( "frame variable v5", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v6", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v7", + substrs=["size=error: invalid value for end of vector"], + ) + self.expect( + "frame variable v8", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v9", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v10", + substrs=["size=error: invalid value for end of vector"], + ) + self.expect( + "frame variable v11", + substrs=["size=error: invalid value for start of vector"], + ) + self.expect( + "frame variable v12", + substrs=["size=error: start of vector data begins after end pointer"], + ) + self.expect( + "frame variable v13", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v14", + substrs=["size=error: invalid value for end of vector"], + ) + self.expect( + "frame variable v15", + substrs=["size=1"], + ) + self.expect( + "frame variable v16", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v17", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v18", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v19", substrs=["size=error: size not multiple of element size"], ) + self.expect( + "frame variable v20", + substrs=["size=error: size not multiple of element size"], + ) + self.expect( + "frame variable v21", + substrs=["size=1"], + ) + self.expect( + "frame variable v23", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v24", + substrs=["size=error: failed to determine start/end of vector data"], + ) + self.expect( + "frame variable v25", + substrs=["size=error: failed to determine start/end of vector data"], + ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp index 5943b35deab8b..f7a7e13356557 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp @@ -1,37 +1,199 @@ #define COMPRESSED_PAIR_REV 4 #include <libcxx-simulators-common/compressed_pair.h> +#include <stddef.h> namespace std { -inline namespace __1 { +inline namespace __ValidLegacyVector { template <typename T> struct vector { T *__begin_; T *__end_; - _LLDB_COMPRESSED_PAIR(T *, __cap_ = nullptr, void *, __alloc_); }; -} // namespace __1 +} // namespace __ValidLegacyVector -inline namespace __2 { -template <typename T> struct vector {}; -} // namespace __2 +inline namespace __LegacyVectorMissingBegin { +template <typename T> struct vector { + T *__end_; +}; +} // namespace __LegacyVectorMissingBegin -inline namespace __3 { +inline namespace __LegacyVectorNonPointerBegin { template <typename T> struct vector { + int __begin_; + T *__end_; +}; +} // namespace __LegacyVectorNonPointerBegin + +inline namespace __LegacyMissingEnd { +template <typename T> struct vector { + T *__begin_; +}; +} // namespace __LegacyMissingEnd + +inline namespace __LegacyVectorNonPointerEnd { +template <typename T> struct vector { + T *__begin_; + size_t __end_; +}; +} // namespace __LegacyVectorNonPointerEnd + +inline namespace __LegacyVectorSizeBased { +template <typename T> struct vector { + T *__begin_; + size_t __end_; +}; +} // namespace __LegacyVectorSizeBased + +inline namespace __ValidPointerLayout { +template <typename T> struct __vector_layout { T *__begin_; T *__end_; - _LLDB_COMPRESSED_PAIR(short *, __cap_ = nullptr, void *, __alloc_); }; -} // namespace __3 + +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __ValidPointerLayout + +inline namespace __PointerLayoutNonPointerBegin { +template <typename T> struct __vector_layout { + size_t __begin_; + T *__end_; +}; + +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __PointerLayoutNonPointerBegin + +inline namespace __PointerLayoutNonPointerEnd { +template <typename T> struct __vector_layout { + T *__begin_; + size_t __end_; +}; + +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __PointerLayoutNonPointerEnd + +inline namespace __LayoutStructMissingBegin { +template <typename T> struct __vector_layout { + // LLDB short-circuits when it can't find `__begin_`, so other members aren't + // required for this type. +}; + +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __LayoutStructMissingBegin + +inline namespace __LayoutStructMissingSecondMember { +template <typename T> struct __vector_layout { + T *__begin_; +}; + +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __LayoutStructMissingSecondMember + +inline namespace __ValidSizeLayout { +template <typename T> struct __vector_layout { + T *__begin_; + size_t __size_; +}; +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __ValidSizeLayout + +inline namespace __SizeLayoutMissingBegin { +template <typename T> struct __vector_layout { + size_t __size_; +}; +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __SizeLayoutMissingBegin + +inline namespace __SizeLayoutNonPointerBegin { +template <typename T> struct __vector_layout { + size_t __begin_; + size_t __size_; +}; +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __SizeLayoutNonPointerBegin + +inline namespace __SizeLayoutNonIntegerSize { +template <typename T> struct __vector_layout { + T *__begin_; + T *__size_; +}; +template <typename T> struct vector { + __vector_layout<T> __layout_; +}; +} // namespace __SizeLayoutNonIntegerSize } // namespace std int main() { int arr[] = {1, 2, 3}; - std::__1::vector<int> v1{.__begin_ = arr, .__end_ = nullptr}; - std::__1::vector<int> v2{.__begin_ = nullptr, .__end_ = arr}; - std::__1::vector<int> v3{.__begin_ = &arr[2], .__end_ = arr}; - std::__2::vector<int> v4; + std::__ValidLegacyVector::vector<int> v1{.__begin_ = arr, .__end_ = nullptr}; + std::__ValidLegacyVector::vector<int> v2{.__begin_ = nullptr, .__end_ = arr}; + std::__ValidLegacyVector::vector<int> v3{.__begin_ = &arr[2], .__end_ = arr}; + std::__LegacyVectorMissingBegin::vector<int> v4{.__end_ = arr}; + std::__LegacyMissingEnd::vector<int> v5{.__begin_ = arr}; + std::__LegacyVectorNonPointerBegin::vector<int> v6{.__begin_ = 0, + .__end_ = arr}; + std::__LegacyVectorNonPointerEnd::vector<int> v7{.__begin_ = arr, + .__end_ = 0}; + + std::__LayoutStructMissingBegin::vector<int> v8{.__layout_ = {}}; + std::__LayoutStructMissingSecondMember::vector<int> v9{ + .__layout_ = {.__begin_ = arr}}; + + std::__ValidPointerLayout::vector<int> v10{ + .__layout_ = {.__begin_ = arr, .__end_ = nullptr}}; + std::__ValidPointerLayout::vector<int> v11{ + .__layout_ = {.__begin_ = nullptr, .__end_ = arr}}; + std::__ValidPointerLayout::vector<int> v12{ + .__layout_ = {.__begin_ = &arr[2], .__end_ = arr}}; + + std::__PointerLayoutNonPointerBegin::vector<int> v13{ + .__layout_ = {.__begin_ = 0, .__end_ = arr}}; + std::__PointerLayoutNonPointerEnd::vector<int> v14{ + .__layout_ = {.__begin_ = arr, .__end_ = 0}}; + + std::__ValidSizeLayout::vector<int> v15{ + .__layout_ = {.__begin_ = arr, .__size_ = 1}}; + + std::__SizeLayoutMissingBegin::vector<int> v16{.__layout_ = {.__size_ = 1}}; + std::__SizeLayoutNonPointerBegin::vector<int> v17{ + .__layout_ = {.__begin_ = 0, .__size_ = 0}}; + std::__SizeLayoutNonIntegerSize::vector<int> v18{ + .__layout_ = {.__begin_ = arr, .__size_ = 0}}; char carr[] = {'a'}; - std::__3::vector<char> v5{.__begin_ = carr, .__end_ = carr + 1}; + std::__ValidLegacyVector::vector<short> v19{ + .__begin_ = reinterpret_cast<short *>(carr), + .__end_ = reinterpret_cast<short *>(carr + 1)}; + std::__ValidPointerLayout::vector<short> v20{ + .__layout_ = {.__begin_ = reinterpret_cast<short *>(carr), + .__end_ = reinterpret_cast<short *>(carr + 1)}}; + std::__ValidSizeLayout::vector<short> v21{ + .__layout_ = {.__begin_ = reinterpret_cast<short *>(carr), .__size_ = 1}}; + + struct ZeroSizeStruct { + int x[0]; + }; + static_assert(sizeof(ZeroSizeStruct) == 0); + std::__ValidLegacyVector::vector<ZeroSizeStruct> v23{.__begin_ = nullptr, + .__end_ = nullptr}; + std::__ValidPointerLayout::vector<ZeroSizeStruct> v24{ + .__layout_ = {.__begin_ = nullptr, .__end_ = nullptr}}; + std::__ValidSizeLayout::vector<ZeroSizeStruct> v25{ + .__layout_ = {.__begin_ = nullptr, .__size_ = 0}}; return 0; } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/Makefile new file mode 100644 index 0000000000000..8ce653ffd6871 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp +override CXXFLAGS_EXTRAS += -std=c++11 +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py new file mode 100644 index 0000000000000..46b22ad473191 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py @@ -0,0 +1,44 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class LibcxxVectorDataFormatterSimulatorTestCase(TestBase): + SHARED_BUILD_TESTCASE = False + NO_DEBUG_INFO_TESTCASE = True + test_cases = { + "LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER": 0, + "LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT": 1, + "LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT": 2, + } + + def _run_test(self, test_case): + cxxflags_extras = f"-DLLDB_TEST_CASE={test_case}" + self.build(dictionary=dict(CXXFLAGS_EXTRAS=cxxflags_extras)) + lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.cpp")) + + self.expect( + "frame variable v0", + substrs=["size=0"], + ) + self.expect( + "frame variable v1", + substrs=["size=1", "[0] = 10"], + ) + self.expect( + "frame variable v2", + substrs=["size=2", "[0] = -10", "[1] = -20"], + ) + self.expect( + "frame variable v3", + substrs=["size=3", "[0] = 56", "[1] = 10", "[2] = 87"], + ) + + def test_without_layout_member(self): + self._run_test(self.test_cases["LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER"]) + + def test_with_pointer_layout(self): + self._run_test(self.test_cases["LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT"]) + + def test_with_size_layout(self): + self._run_test(self.test_cases["LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT"]) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp new file mode 100644 index 0000000000000..9bfbddba63238 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp @@ -0,0 +1,78 @@ +#include <stddef.h> + +#define LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER 0 +#define LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT 1 +#define LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT 2 +#define LLDB_TEST_VECTOR_WITH_LAYOUT_MISSING_DATA_MEMBERS 3 + +#ifndef LLDB_TEST_CASE +#error LLDB_TEST_CASE must be defined as an integer +#endif + +namespace std { +namespace __lldb { + +#if LLDB_TEST_CASE == LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER +template <typename T> class vector { +public: + typedef T *pointer; + + vector(pointer begin, size_t size) + : __begin_(begin), __end_(begin + size) {} + +private: + pointer __begin_; + pointer __end_; + // __cap_ and __alloc_ aren't used, so they've been removed for simplicity. +}; +#elif LLDB_TEST_CASE == LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT +template <typename T> struct __vector_layout { + T *__begin_; + T *__end_; +}; + +template <typename T> class vector { +public: + vector(T *begin, size_t size) : __layout_{begin, begin + size} {} + +private: + __vector_layout<T> __layout_; +}; + +#elif LLDB_TEST_CASE == LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT +template <typename T> struct __vector_layout { + T *__begin_; + size_t __size_; +}; + +template <typename T> class vector { +public: + vector(T *begin, size_t size) : __layout_{begin, size} {} + +private: + __vector_layout<T> __layout_; +}; + +#else +#error LLDB_TEST_CASE defined out-of-range +#undef LLDB_TEST_CASE +#endif + +} // namespace __lldb +} // namespace std + +int main() { +#ifdef LLDB_TEST_CASE + int a1[] = {10}; + std::__lldb::vector<int> v0(a1, 0); + std::__lldb::vector<int> v1(a1, 1); + + int a2[] = {-10, -20}; + std::__lldb::vector<int> v2(a2, 2); + + int a3[] = {56, 10, 87}; + std::__lldb::vector<int> v3(a3, 3); + + return 0; // break here +#endif +} >From f86c4a9b8ddc24de8e0801730917ffd86e464664 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Mon, 8 Jun 2026 21:45:36 +0000 Subject: [PATCH 2/8] Fix CI issues --- .../TestDataFormatterLibcxxInvalidVectorSimulator.py | 10 ++++++++-- .../libcxx-simulators/invalid-vector/main.cpp | 2 ++ .../vector/TestDataFormatterLibcxxVectorSimulator.py | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py index e4783746b3499..0d21cc5717c20 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py @@ -13,9 +13,8 @@ class LibcxxInvalidVectorDataFormatterSimulatorTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipIf(compiler="clang", compiler_version=['<', '18.0']) - def test(self): + def test_most(self): self.build() lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) @@ -103,6 +102,13 @@ def test(self): "frame variable v21", substrs=["size=1"], ) + + @skipIf(compiler="clang", compiler_version=["<", "18.0"]) + @skipIfWindows + def test_zero_sized_struct_extension(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) + self.expect( "frame variable v23", substrs=["size=error: failed to determine start/end of vector data"], diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp index f7a7e13356557..cc4a3d4a771f8 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp @@ -184,6 +184,7 @@ int main() { std::__ValidSizeLayout::vector<short> v21{ .__layout_ = {.__begin_ = reinterpret_cast<short *>(carr), .__size_ = 1}}; +#ifndef _WIN32 struct ZeroSizeStruct { int x[0]; }; @@ -195,5 +196,6 @@ int main() { .__layout_ = {.__begin_ = nullptr, .__end_ = nullptr}}; std::__ValidSizeLayout::vector<ZeroSizeStruct> v25{ .__layout_ = {.__begin_ = nullptr, .__size_ = 0}}; +#endif return 0; } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py index 46b22ad473191..9a480fb1b8d25 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py @@ -15,7 +15,9 @@ class LibcxxVectorDataFormatterSimulatorTestCase(TestBase): def _run_test(self, test_case): cxxflags_extras = f"-DLLDB_TEST_CASE={test_case}" self.build(dictionary=dict(CXXFLAGS_EXTRAS=cxxflags_extras)) - lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.cpp")) + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp") + ) self.expect( "frame variable v0", >From 6eb35d03fe69e91f7bdc378ffbd6d26c375860be Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Mon, 8 Jun 2026 21:56:31 +0000 Subject: [PATCH 3/8] formats patch and updates test config --- .../TestDataFormatterLibcxxVectorSimulator.py | 62 ++++++++++++------- .../libcxx-simulators/vector/main.cpp | 47 +++++++------- 2 files changed, 64 insertions(+), 45 deletions(-) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py index 9a480fb1b8d25..7d9aa8b829c0b 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/TestDataFormatterLibcxxVectorSimulator.py @@ -3,44 +3,62 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil + class LibcxxVectorDataFormatterSimulatorTestCase(TestBase): SHARED_BUILD_TESTCASE = False NO_DEBUG_INFO_TESTCASE = True - test_cases = { - "LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER": 0, - "LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT": 1, - "LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT": 2, - } - def _run_test(self, test_case): - cxxflags_extras = f"-DLLDB_TEST_CASE={test_case}" - self.build(dictionary=dict(CXXFLAGS_EXTRAS=cxxflags_extras)) - lldbutil.run_to_source_breakpoint( - self, "break here", lldb.SBFileSpec("main.cpp") - ) + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) self.expect( - "frame variable v0", + "frame variable legacy_layout0", substrs=["size=0"], ) self.expect( - "frame variable v1", + "frame variable legacy_layout1", substrs=["size=1", "[0] = 10"], ) self.expect( - "frame variable v2", + "frame variable legacy_layout2", substrs=["size=2", "[0] = -10", "[1] = -20"], ) self.expect( - "frame variable v3", + "frame variable legacy_layout3", substrs=["size=3", "[0] = 56", "[1] = 10", "[2] = 87"], ) - def test_without_layout_member(self): - self._run_test(self.test_cases["LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER"]) - - def test_with_pointer_layout(self): - self._run_test(self.test_cases["LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT"]) + self.expect( + "frame variable pointer_based_layout0", + substrs=["size=0"], + ) + self.expect( + "frame variable pointer_based_layout1", + substrs=["size=1", "[0] = 10"], + ) + self.expect( + "frame variable pointer_based_layout2", + substrs=["size=2", "[0] = -10", "[1] = -20"], + ) + self.expect( + "frame variable pointer_based_layout3", + substrs=["size=3", "[0] = 56", "[1] = 10", "[2] = 87"], + ) - def test_with_size_layout(self): - self._run_test(self.test_cases["LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT"]) + self.expect( + "frame variable size_based_layout0", + substrs=["size=0"], + ) + self.expect( + "frame variable size_based_layout1", + substrs=["size=1", "[0] = 10"], + ) + self.expect( + "frame variable size_based_layout2", + substrs=["size=2", "[0] = -10", "[1] = -20"], + ) + self.expect( + "frame variable size_based_layout3", + substrs=["size=3", "[0] = 56", "[1] = 10", "[2] = 87"], + ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp index 9bfbddba63238..02cc67bc41112 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp @@ -10,22 +10,21 @@ #endif namespace std { -namespace __lldb { - -#if LLDB_TEST_CASE == LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER +inline namespace __LegacyLayout { template <typename T> class vector { public: typedef T *pointer; - vector(pointer begin, size_t size) - : __begin_(begin), __end_(begin + size) {} + vector(pointer begin, size_t size) : __begin_(begin), __end_(begin + size) {} private: pointer __begin_; pointer __end_; // __cap_ and __alloc_ aren't used, so they've been removed for simplicity. }; -#elif LLDB_TEST_CASE == LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT +} // namespace __LegacyLayout + +inline namespace __PointerBasedLayout { template <typename T> struct __vector_layout { T *__begin_; T *__end_; @@ -38,8 +37,9 @@ template <typename T> class vector { private: __vector_layout<T> __layout_; }; +} // namespace __PointerBasedLayout -#elif LLDB_TEST_CASE == LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT +inline namespace __SizeBasedLayout { template <typename T> struct __vector_layout { T *__begin_; size_t __size_; @@ -52,27 +52,28 @@ template <typename T> class vector { private: __vector_layout<T> __layout_; }; - -#else -#error LLDB_TEST_CASE defined out-of-range -#undef LLDB_TEST_CASE -#endif - -} // namespace __lldb +} // namespace __SizeBasedLayout } // namespace std int main() { -#ifdef LLDB_TEST_CASE int a1[] = {10}; - std::__lldb::vector<int> v0(a1, 0); - std::__lldb::vector<int> v1(a1, 1); - int a2[] = {-10, -20}; - std::__lldb::vector<int> v2(a2, 2); - int a3[] = {56, 10, 87}; - std::__lldb::vector<int> v3(a3, 3); - return 0; // break here -#endif + std::__LegacyLayout::vector<int> legacy_layout0(a1, 0); + std::__LegacyLayout::vector<int> legacy_layout1(a1, 1); + std::__LegacyLayout::vector<int> legacy_layout2(a2, 2); + std::__LegacyLayout::vector<int> legacy_layout3(a3, 3); + + std::__PointerBasedLayout::vector<int> pointer_based_layout0(a1, 0); + std::__PointerBasedLayout::vector<int> pointer_based_layout1(a1, 1); + std::__PointerBasedLayout::vector<int> pointer_based_layout2(a2, 2); + std::__PointerBasedLayout::vector<int> pointer_based_layout3(a3, 3); + + std::__SizeBasedLayout::vector<int> size_based_layout0(a1, 0); + std::__SizeBasedLayout::vector<int> size_based_layout1(a1, 1); + std::__SizeBasedLayout::vector<int> size_based_layout2(a2, 2); + std::__SizeBasedLayout::vector<int> size_based_layout3(a3, 3); + + return 0; } >From 597a8e0b076c45279bfd009d3a92b04988adcb8c Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Mon, 8 Jun 2026 22:38:56 +0000 Subject: [PATCH 4/8] removes macros from previous implementation --- .../data-formatter-stl/libcxx-simulators/vector/main.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp index 02cc67bc41112..535faebac4157 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp @@ -1,14 +1,5 @@ #include <stddef.h> -#define LLDB_TEST_VECTOR_WITHOUT_LAYOUT_DATA_MEMBER 0 -#define LLDB_TEST_VECTOR_WITH_POINTER_LAYOUT 1 -#define LLDB_TEST_VECTOR_WITH_SIZE_LAYOUT 2 -#define LLDB_TEST_VECTOR_WITH_LAYOUT_MISSING_DATA_MEMBERS 3 - -#ifndef LLDB_TEST_CASE -#error LLDB_TEST_CASE must be defined as an integer -#endif - namespace std { inline namespace __LegacyLayout { template <typename T> class vector { >From a7c326ea06aaa5b6170a6b7ee40c823aee98a090 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Wed, 10 Jun 2026 00:51:13 +0000 Subject: [PATCH 5/8] change how we compute size --- .../Language/CPlusPlus/LibCxxVector.cpp | 50 ++++++++++++------- ...taFormatterLibcxxInvalidVectorSimulator.py | 2 +- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index bf9010a304197..c11fdd5415982 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -13,6 +13,7 @@ #include "lldb/ValueObject/ValueObject.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorExtras.h" #include <optional> @@ -41,6 +42,8 @@ class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { ValueObject *m_start = nullptr; ValueObject *m_finish = nullptr; + enum class VectorLayout : bool { Pointer, Size }; + VectorLayout m_layout; CompilerType m_element_type; uint32_t m_element_size = 0; }; @@ -83,14 +86,9 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: // delete m_finish; } -llvm::Expected<uint32_t> lldb_private::formatters:: - LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() { - if (!m_start || !m_finish) - return llvm::createStringError( - "failed to determine start/end of vector data"); - - uint64_t start_val = m_start->GetValueAsUnsigned(0); - uint64_t finish_val = m_finish->GetValueAsUnsigned(0); +static llvm::Expected<uint32_t> CalculateNumChildrenUsingPointerArithmetic(ValueObject* begin, ValueObject* end, uint64_t value_type_size) { + uint64_t start_val = begin->GetValueAsUnsigned(0); + uint64_t finish_val = end->GetValueAsUnsigned(0); // A default-initialized empty vector. if (start_val == 0 && finish_val == 0) @@ -107,10 +105,30 @@ llvm::Expected<uint32_t> lldb_private::formatters:: "start of vector data begins after end pointer"); size_t num_children = (finish_val - start_val); - if (num_children % m_element_size) + if (num_children % value_type_size) return llvm::createStringError("size not multiple of element size"); - return num_children / m_element_size; + return num_children / value_type_size; +} + +static llvm::Expected<uint32_t> GetNumChildren(ValueObject* size) { + if (!size->GetCompilerType().IsInteger()) + return llvm::createStringError("size data member must be a built-in integer type"); + return size->GetValueAsUnsigned(0); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() { + if (!m_start || !m_finish) + return llvm::createStringError( + "failed to determine start/end of vector data"); + + switch (m_layout) { + case VectorLayout::Pointer: + return CalculateNumChildrenUsingPointerArithmetic(m_start, m_finish, m_element_size); + case VectorLayout::Size: + return GetNumChildren(m_finish); + } } lldb::ValueObjectSP @@ -158,20 +176,16 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() { if (ValueObjectSP end_sp = target->GetChildMemberWithName("__end_")) { m_finish = end_sp.get(); + m_layout = VectorLayout::Pointer; return lldb::ChildCacheState::eRefetch; } ValueObjectSP size_sp = target->GetChildMemberWithName("__size_"); - if (!size_sp || !size_sp->GetCompilerType().IsInteger()) + if (!size_sp) return lldb::ChildCacheState::eRefetch; - uint64_t begin_addr = m_start->GetValueAsUnsigned(0); - uint64_t size = size_sp->GetValueAsUnsigned(0); - uint64_t end_addr = begin_addr + size * m_element_size; - m_finish = CreateChildValueObjectFromAddress( - "__end_", end_addr, m_backend.GetExecutionContextRef(), - m_start->GetCompilerType(), false) - .get(); + m_finish = size_sp.get(); + m_layout = VectorLayout::Size; return lldb::ChildCacheState::eRefetch; } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py index 0d21cc5717c20..e79d0fe13de8c 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py @@ -88,7 +88,7 @@ def test_most(self): ) self.expect( "frame variable v18", - substrs=["size=error: failed to determine start/end of vector data"], + substrs=["size=error: size data member must be a built-in integer type"], ) self.expect( "frame variable v19", >From fc35d1b8dbcad3f4e428182b1176e492f38a43d7 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Wed, 10 Jun 2026 00:55:15 +0000 Subject: [PATCH 6/8] run clang-format --- .../Language/CPlusPlus/LibCxxVector.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index c11fdd5415982..7a455bd71ad8e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -86,7 +86,9 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: // delete m_finish; } -static llvm::Expected<uint32_t> CalculateNumChildrenUsingPointerArithmetic(ValueObject* begin, ValueObject* end, uint64_t value_type_size) { +static llvm::Expected<uint32_t> +CalculateNumChildrenUsingPointerArithmetic(ValueObject *begin, ValueObject *end, + uint64_t value_type_size) { uint64_t start_val = begin->GetValueAsUnsigned(0); uint64_t finish_val = end->GetValueAsUnsigned(0); @@ -111,9 +113,10 @@ static llvm::Expected<uint32_t> CalculateNumChildrenUsingPointerArithmetic(Value return num_children / value_type_size; } -static llvm::Expected<uint32_t> GetNumChildren(ValueObject* size) { +static llvm::Expected<uint32_t> GetNumChildren(ValueObject *size) { if (!size->GetCompilerType().IsInteger()) - return llvm::createStringError("size data member must be a built-in integer type"); + return llvm::createStringError( + "size data member must be a built-in integer type"); return size->GetValueAsUnsigned(0); } @@ -124,10 +127,11 @@ llvm::Expected<uint32_t> lldb_private::formatters:: "failed to determine start/end of vector data"); switch (m_layout) { - case VectorLayout::Pointer: - return CalculateNumChildrenUsingPointerArithmetic(m_start, m_finish, m_element_size); - case VectorLayout::Size: - return GetNumChildren(m_finish); + case VectorLayout::Pointer: + return CalculateNumChildrenUsingPointerArithmetic(m_start, m_finish, + m_element_size); + case VectorLayout::Size: + return GetNumChildren(m_finish); } } >From 142ab729f05112d3e97fb8d2a04ca5940b07cbb8 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Thu, 11 Jun 2026 20:36:01 +0000 Subject: [PATCH 7/8] Document how vector is represented in libc++ --- .../libcxx-simulators/vector/main.cpp | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp index 535faebac4157..b46793ce5c9d7 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/vector/main.cpp @@ -1,3 +1,17 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// libc++ has changed how `std::vector` is represented over time. This file +// provides approximations of these representations for testing the vector +// pretty-printer. This lets us test that LLDB is able to handle all iterations +// of libc++'s `std::vector`. + #include <stddef.h> namespace std { @@ -11,11 +25,25 @@ template <typename T> class vector { private: pointer __begin_; pointer __end_; - // __cap_ and __alloc_ aren't used, so they've been removed for simplicity. + + // libc++ changed how the capacity member and allocator were stored in + // 27c8338. LLDB only relies on `__begin_` and `__end_`. Adding the capacity + // and allocator members in their different formats doesn't add test coverage, + // but may convince the reader that it does. As such, we don't provide two + // legacy layouts. + // + // Before 27c83382d83dce0f33ae67abb3bc94977cb3031f: + // __compressed_pair<size_type, __storage_allocator> __cap_alloc_; + // + // Since 27c83382d83dce0f33ae67abb3bc94977cb3031f: + // _LIBCPP_COMPRESSED_PAIR(pointer, __cap_ = nullptr, allocator_type, + // __alloc_); }; } // namespace __LegacyLayout inline namespace __PointerBasedLayout { +// `__PointerBasedLayout::__vector_layout` is structurally equal to +// `__LegacyLayout::vector`. template <typename T> struct __vector_layout { T *__begin_; T *__end_; >From dd341a55d31950732b27350375ebdc7674a45301 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella <[email protected]> Date: Thu, 11 Jun 2026 20:46:01 +0000 Subject: [PATCH 8/8] Document what `m_finish` can point to --- lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index 7a455bd71ad8e..4966eb1d75b20 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -41,6 +41,10 @@ class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { lldb::ChildCacheState UpdateVectorWithLayoutSubobject(ValueObject *layout); ValueObject *m_start = nullptr; + + // m_finish may point to a pointer (`__end_`) or an integer (`__size_`) + // depending on how libc++'s vector is implemented. Interpreting what is + // pointed to is done using `m_layout`. ValueObject *m_finish = nullptr; enum class VectorLayout : bool { Pointer, Size }; VectorLayout m_layout; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
