Skip to content

[lldb][DataFormatter] Surface CalculateNumChildren errors in std::vector summary (#135944) #10498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
namespace std {
namespace __lldb {

// Post-c88580c layout
#if __has_cpp_attribute(msvc::no_unique_address)
#define _LLDB_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#elif __has_cpp_attribute(no_unique_address)
#define _LLDB_NO_UNIQUE_ADDRESS [[__no_unique_address__]]
#endif

#if COMPRESSED_PAIR_REV == 0 // Post-c88580c layout
struct __value_init_tag {};
struct __default_init_tag {};

Expand Down Expand Up @@ -52,6 +58,54 @@ class __compressed_pair : private __compressed_pair_elem<_T1, 0>,

_T1 &first() { return static_cast<_Base1 &>(*this).__get(); }
};
#elif COMPRESSED_PAIR_REV == 1
// From libc++ datasizeof.h
template <class _Tp> struct _FirstPaddingByte {
_LLDB_NO_UNIQUE_ADDRESS _Tp __v_;
char __first_padding_byte_;
};

template <class _Tp>
inline const size_t __datasizeof_v =
__builtin_offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_);

template <class _Tp>
struct __lldb_is_final : public integral_constant<bool, __is_final(_Tp)> {};

template <class _ToPad> class __compressed_pair_padding {
char __padding_[((is_empty<_ToPad>::value &&
!__lldb_is_final<_ToPad>::value) ||
is_reference<_ToPad>::value)
? 0
: sizeof(_ToPad) - __datasizeof_v<_ToPad>];
};

#define _LLDB_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
[[__gnu__::__aligned__( \
alignof(T2))]] _LLDB_NO_UNIQUE_ADDRESS T1 Initializer1; \
_LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T1> __padding1_; \
_LLDB_NO_UNIQUE_ADDRESS T2 Initializer2; \
_LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T2> __padding2_;

#define _LLDB_COMPRESSED_TRIPLE(T1, Initializer1, T2, Initializer2, T3, \
Initializer3) \
[[using __gnu__: __aligned__(alignof(T2)), \
__aligned__(alignof(T3))]] _LLDB_NO_UNIQUE_ADDRESS T1 Initializer1; \
_LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T1> __padding1_; \
_LLDB_NO_UNIQUE_ADDRESS T2 Initializer2; \
_LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T2> __padding2_; \
_LLDB_NO_UNIQUE_ADDRESS T3 Initializer3; \
_LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T3> __padding3_;
#elif COMPRESSED_PAIR_REV == 2
#define _LLDB_COMPRESSED_PAIR(T1, Name1, T2, Name2) \
_LLDB_NO_UNIQUE_ADDRESS T1 Name1; \
_LLDB_NO_UNIQUE_ADDRESS T2 Name2

#define _LLDB_COMPRESSED_TRIPLE(T1, Name1, T2, Name2, T3, Name3) \
_LLDB_NO_UNIQUE_ADDRESS T1 Name1; \
_LLDB_NO_UNIQUE_ADDRESS T2 Name2; \
_LLDB_NO_UNIQUE_ADDRESS T3 Name3
#endif
} // namespace __lldb
} // namespace std

Expand Down
21 changes: 16 additions & 5 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,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;
}

Expand Down
12 changes: 9 additions & 3 deletions lldb/source/ValueObject/ValueObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1589,10 +1589,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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
override CXXFLAGS_EXTRAS += -std=c++20
include Makefile.rules
Original file line number Diff line number Diff line change
@@ -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."],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#define COMPRESSED_PAIR_REV 2
#include <libcxx-simulators-common/compressed_pair.h>

namespace std {
inline namespace __1 {
template <typename T> struct vector {
T *__begin_;
T *__end_;
_LLDB_COMPRESSED_PAIR(T *, __cap_ = nullptr, void *, __alloc_);
};
} // namespace __1

inline namespace __2 {
template <typename T> struct vector {};
} // namespace __2

inline 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
class LibcxxStringDataFormatterSimulatorTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True

@skipIfDarwin
@skipIfWindows
def _run_test(self, defines):
cxxflags_extras = " ".join(["-D%s" % d for d in defines])
self.build(dictionary=dict(CXXFLAGS_EXTRAS=cxxflags_extras))
Expand All @@ -22,15 +24,19 @@ def _run_test(self, defines):
self.expect_var_path("shortstring", summary='"short"')
self.expect_var_path("longstring", summary='"I am a very long string"')

self.expect_expr("shortstring", result_summary='"short"')
self.expect_expr("longstring", result_summary='"I am a very long string"')


for v in [None, "ALTERNATE_LAYOUT"]:
for r in range(5):
name = "test_r%d" % r
defines = ["REVISION=%d" % r]
if v:
name += "_" + v
defines += [v]
f = functools.partialmethod(
LibcxxStringDataFormatterSimulatorTestCase._run_test, defines
)
setattr(LibcxxStringDataFormatterSimulatorTestCase, name, f)
for r in range(6):
for c in range(3):
name = "test_r%d_c%d" % (r, c)
defines = ["REVISION=%d" % r, "COMPRESSED_PAIR_REV=%d" % c]
if v:
name += "_" + v
defines += [v]
f = functools.partialmethod(
LibcxxStringDataFormatterSimulatorTestCase._run_test, defines
)
setattr(LibcxxStringDataFormatterSimulatorTestCase, name, f)
Loading