From 419fa1b06a36336ad85f1c71fc72ffa719ceb659 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 16 Apr 2025 17:57:51 +0200 Subject: [PATCH] [lldb][DataFormatter] Surface CalculateNumChildren errors in std::vector summary (#135944) When the data-formatters happen to break (e.g., due to layout changes in libc++), there's no clear indicator of them failing from a user's perspective. E.g., for `std::vector`s we would just show: ``` (std::vector) v = size=0 {} ``` which is highly misleading, especially if `v.size()` returns a non-zero size. This patch surfaces the various errors that could occur when calculating the number of children of a vector. rdar://146964266 --- .../Language/CPlusPlus/LibCxxVector.cpp | 21 +++++++--- lldb/source/ValueObject/ValueObject.cpp | 12 ++++-- .../libcxx-simulators/invalid-vector/Makefile | 3 ++ ...taFormatterLibcxxInvalidVectorSimulator.py | 39 +++++++++++++++++++ .../libcxx-simulators/invalid-vector/main.cpp | 37 ++++++++++++++++++ 5 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index d538cac9f913..ce2261b6f03c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -83,19 +83,30 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: llvm::Expected lldb_private::formatters:: LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() { if (!m_start || !m_finish) - return 0; + 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); - if (start_val == 0 || finish_val == 0) + // A default-initialized empty vector. + if (start_val == 0 && finish_val == 0) return 0; - if (start_val >= finish_val) - return 0; + if (start_val == 0) + return llvm::createStringError("Invalid value for start of vector."); + + if (finish_val == 0) + return llvm::createStringError("Invalid value for end of vector."); + + if (start_val > finish_val) + return llvm::createStringError( + "Start of vector data begins after end pointer."); size_t num_children = (finish_val - start_val); if (num_children % m_element_size) - return 0; + return llvm::createStringError("Size not multiple of element size."); + return num_children / m_element_size; } diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index eac24353de90..8741cb734316 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -1521,10 +1521,16 @@ bool ValueObject::DumpPrintableRepresentation( str = GetLocationAsCString(); break; - case eValueObjectRepresentationStyleChildrenCount: - strm.Printf("%" PRIu64 "", (uint64_t)GetNumChildrenIgnoringErrors()); - str = strm.GetString(); + case eValueObjectRepresentationStyleChildrenCount: { + if (auto err = GetNumChildren()) { + strm.Printf("%" PRIu32, *err); + str = strm.GetString(); + } else { + strm << "error: " << toString(err.takeError()); + str = strm.GetString(); + } break; + } case eValueObjectRepresentationStyleType: str = GetTypeName().GetStringRef(); diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/Makefile new file mode 100644 index 000000000000..38cfa8105348 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp +override CXXFLAGS_EXTRAS += -std=c++14 +include Makefile.rules 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 new file mode 100644 index 000000000000..8788ea7be882 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py @@ -0,0 +1,39 @@ +""" +Test we can understand various layouts of the libc++'s std::string +""" + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import functools + + +class LibcxxInvalidVectorDataFormatterSimulatorTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) + + self.expect( + "frame variable v1", + substrs=["size=error: Invalid value for end of vector."], + ) + self.expect( + "frame variable v2", + substrs=["size=error: Invalid value for start of vector."], + ) + self.expect( + "frame variable v3", + substrs=["size=error: Start of vector data begins after end pointer."], + ) + self.expect( + "frame variable v4", + substrs=["size=error: Failed to determine start/end of vector data."], + ) + self.expect( + "frame variable v5", + substrs=["size=error: Size not multiple of element size."], + ) 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 new file mode 100644 index 000000000000..c9f04f60ec24 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp @@ -0,0 +1,37 @@ +#define COMPRESSED_PAIR_REV 2 +#include + +namespace std { +namespace __1 { +template struct vector { + T *__begin_; + T *__end_; + _LLDB_COMPRESSED_PAIR(T *, __cap_ = nullptr, void *, __alloc_); +}; +} // namespace __1 + +namespace __2 { +template struct vector {}; +} // namespace __2 + +namespace __3 { +template struct vector { + T *__begin_; + T *__end_; + _LLDB_COMPRESSED_PAIR(short *, __cap_ = nullptr, void *, __alloc_); +}; +} // namespace __3 +} // namespace std + +int main() { + int arr[] = {1, 2, 3}; + std::__1::vector v1{.__begin_ = arr, .__end_ = nullptr}; + std::__1::vector v2{.__begin_ = nullptr, .__end_ = arr}; + std::__1::vector v3{.__begin_ = &arr[2], .__end_ = arr}; + std::__2::vector v4; + + char carr[] = {'a'}; + std::__3::vector v5{.__begin_ = carr, .__end_ = carr + 1}; + + return 0; +}