[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<int>) 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
This commit is contained in:
Michael Buch
2025-04-16 17:57:51 +02:00
committed by GitHub
parent 0045b82a42
commit 419fa1b06a
5 changed files with 104 additions and 8 deletions

View File

@@ -83,19 +83,30 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
llvm::Expected<uint32_t> 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;
}

View File

@@ -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();

View File

@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
override CXXFLAGS_EXTRAS += -std=c++14
include Makefile.rules

View File

@@ -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."],
)

View File

@@ -0,0 +1,37 @@
#define COMPRESSED_PAIR_REV 2
#include <libcxx-simulators-common/compressed_pair.h>
namespace std {
namespace __1 {
template <typename T> struct vector {
T *__begin_;
T *__end_;
_LLDB_COMPRESSED_PAIR(T *, __cap_ = nullptr, void *, __alloc_);
};
} // namespace __1
namespace __2 {
template <typename T> struct vector {};
} // namespace __2
namespace __3 {
template <typename T> 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<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;
char carr[] = {'a'};
std::__3::vector<char> v5{.__begin_ = carr, .__end_ = carr + 1};
return 0;
}