-
Notifications
You must be signed in to change notification settings - Fork 11.9k
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
[lldb][test] Add a layout simulator test for std::unique_ptr #98330
Conversation
@llvm/pr-subscribers-lldb Author: Michael Buch (Michael137) ChangesThis is currently just a prototype. This is motivated by the upcoming refactor of libc++'s As this will require changes to numerous LLDB libc++ data-formatters (see early draft #96538), it would be nice to have a test-suite that will actually exercise both the old and new layout. We have a matrix bot that tests old versions of Clang (but currently those only date back to Clang-15). Having them in the test-suite will give us quicker signal on what broke. We have an existing test that exercises various layouts of This patch proposes a new Some related discussion: Full diff: https://github.com/llvm/llvm-project/pull/98330.diff 4 Files Affected:
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/compressed_pair.h b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/compressed_pair.h
new file mode 100644
index 0000000000000..ec978b8053646
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/compressed_pair.h
@@ -0,0 +1,89 @@
+#ifndef STD_LLDB_COMPRESSED_PAIR_H
+#define STD_LLDB_COMPRESSED_PAIR_H
+
+#include <__memory/compressed_pair.h>
+#include <type_traits>
+
+namespace std {
+namespace __lldb {
+
+#if COMPRESSED_PAIR_REV == 0 // Post-c88580c layout
+struct __value_init_tag {};
+struct __default_init_tag {};
+
+template <class _Tp, int _Idx,
+ bool _CanBeEmptyBase =
+ std::is_empty<_Tp>::value && !std::is_final<_Tp>::value>
+struct __compressed_pair_elem {
+ explicit __compressed_pair_elem(__default_init_tag) {}
+ explicit __compressed_pair_elem(__value_init_tag) : __value_() {}
+
+ explicit __compressed_pair_elem(_Tp __t) : __value_(__t) {}
+
+ _Tp &__get() { return __value_; }
+
+private:
+ _Tp __value_;
+};
+
+template <class _Tp, int _Idx>
+struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp {
+ explicit __compressed_pair_elem(_Tp __t) : _Tp(__t) {}
+ explicit __compressed_pair_elem(__default_init_tag) {}
+ explicit __compressed_pair_elem(__value_init_tag) : _Tp() {}
+
+ _Tp &__get() { return *this; }
+};
+
+template <class _T1, class _T2>
+class __compressed_pair : private __compressed_pair_elem<_T1, 0>,
+ private __compressed_pair_elem<_T2, 1> {
+public:
+ using _Base1 = __compressed_pair_elem<_T1, 0>;
+ using _Base2 = __compressed_pair_elem<_T2, 1>;
+
+ explicit __compressed_pair(_T1 __t1, _T2 __t2) : _Base1(__t1), _Base2(__t2) {}
+ explicit __compressed_pair()
+ : _Base1(__value_init_tag()), _Base2(__value_init_tag()) {}
+
+ template <class _U1, class _U2>
+ explicit __compressed_pair(_U1 &&__t1, _U2 &&__t2)
+ : _Base1(std::forward<_U1>(__t1)), _Base2(std::forward<_U2>(__t2)) {}
+
+ _T1 &first() { return static_cast<_Base1 &>(*this).__get(); }
+};
+#elif COMPRESSED_PAIR_REV == 1
+#define _LLDB_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
+ [[__gnu__::__aligned__(alignof(T2))]] [[no_unique_address]] T1 Initializer1; \
+ [[no_unique_address]] __compressed_pair_padding<T1> _LIBCPP_CONCAT3( \
+ __padding1_, __LINE__, _); \
+ [[no_unique_address]] T2 Initializer2; \
+ [[no_unique_address]] __compressed_pair_padding<T2> _LIBCPP_CONCAT3( \
+ __padding2_, __LINE__, _)
+
+#define _LLDB_COMPRESSED_TRIPLE(T1, Initializer1, T2, Initializer2, T3, \
+ Initializer3) \
+ [[using __gnu__: __aligned__(alignof(T2)), \
+ __aligned__(alignof(T3))]] [[no_unique_address]] T1 Initializer1; \
+ [[no_unique_address]] __compressed_pair_padding<T1> _LIBCPP_CONCAT3( \
+ __padding1_, __LINE__, _); \
+ [[no_unique_address]] T2 Initializer2; \
+ [[no_unique_address]] __compressed_pair_padding<T2> _LIBCPP_CONCAT3( \
+ __padding2_, __LINE__, _); \
+ [[no_unique_address]] T3 Initializer3; \
+ [[no_unique_address]] __compressed_pair_padding<T3> _LIBCPP_CONCAT3( \
+ __padding3_, __LINE__, _)
+#elif COMPRESSED_PAIR_REV == 2
+#define _LLDB_COMPRESSED_PAIR(T1, Name1, T2, Name2) \
+ [[no_unique_address]] T1 Name1; \
+ [[no_unique_address]] T2 Name2
+
+#define _LLDB_COMPRESSED_TRIPLE(T1, Name1, T2, Name2, T3, Name3) \
+ [[no_unique_address]] T1 Name1; \
+ [[no_unique_address]] T2 Name2; \
+ [[no_unique_address]] T3 Name3
+#endif
+} // namespace __lldb
+} // namespace std
+
+#endif // _H
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/Makefile
new file mode 100644
index 0000000000000..38cfa81053488
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/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/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py
new file mode 100644
index 0000000000000..fe3570ff99ae5
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py
@@ -0,0 +1,32 @@
+"""
+Test we can understand various layouts of the libc++'s std::unique_ptr
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import functools
+
+class LibcxxUniquePtrDataFormatterSimulatorTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def _run_test(self, defines):
+ cxxflags_extras = " ".join(["-D%s" % d for d in defines])
+ self.build(dictionary=dict(CXXFLAGS_EXTRAS=cxxflags_extras))
+ lldbutil.run_to_source_breakpoint(
+ self, "// Break here", lldb.SBFileSpec("main.cpp")
+ )
+ self.expect("frame variable var_up", substrs=["pointer ="])
+ self.expect("frame variable var_up", substrs=["deleter ="], matching=False)
+ self.expect("frame variable var_with_deleter_up", substrs=["pointer =", "deleter ="])
+
+#for r in range(3):
+for r in range(1):
+ name = "test_r%d" % r
+ defines = ["COMPRESSED_PAIR_REV=%d" % r]
+ f = functools.partialmethod(
+ LibcxxUniquePtrDataFormatterSimulatorTestCase._run_test, defines
+ )
+ setattr(LibcxxUniquePtrDataFormatterSimulatorTestCase, name, f)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp
new file mode 100644
index 0000000000000..33066febc7623
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp
@@ -0,0 +1,43 @@
+#include "../compressed_pair.h"
+
+#include <__memory/allocator_traits.h>
+
+namespace std {
+namespace __lldb {
+template <class _Tp> struct default_delete {
+ default_delete() noexcept = default;
+
+ void operator()(_Tp *__ptr) const noexcept { delete __ptr; }
+};
+
+template <class _Tp, class _Dp = default_delete<_Tp>> class unique_ptr {
+public:
+ typedef _Tp element_type;
+ typedef _Dp deleter_type;
+ typedef typename __pointer<_Tp, deleter_type>::type pointer;
+
+#if COMPRESSED_PAIR_REV == 0
+ std::__lldb::__compressed_pair<pointer, deleter_type> __ptr_;
+ explicit unique_ptr(pointer __p) noexcept
+ : __ptr_(__p, std::__lldb::__value_init_tag()) {}
+#elif COMPRESSED_PAIR_REV == 1 || COMPRESSED_PAIR_REV == 2
+ _LLDB_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_);
+ explicit unique_ptr(pointer __p) noexcept : __ptr_(__p), __deleter_() {}
+#endif
+};
+} // namespace __lldb
+} // namespace std
+
+struct StatefulDeleter {
+ StatefulDeleter() noexcept = default;
+
+ void operator()(int *__ptr) const noexcept { delete __ptr; }
+
+ int m_state = 50;
+};
+
+int main() {
+ std::__lldb::unique_ptr<int> var_up(new int(5));
+ std::__lldb::unique_ptr<int, StatefulDeleter> var_with_deleter_up(new int(5));
+ return 0; // Break here
+}
|
It's a non-trivial maintenance cost but I think the upside is quite large:
|
✅ With the latest revision this PR passed the Python code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm like this approach. It can be a lot of work, so we may not be able to get every contributor to do this, but I'd certainly encourage everyone to try.
#elif COMPRESSED_PAIR_REV == 1 | ||
#define _LLDB_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \ | ||
[[__gnu__::__aligned__(alignof(T2))]] [[no_unique_address]] T1 Initializer1; \ | ||
[[no_unique_address]] __compressed_pair_padding<T1> _LIBCPP_CONCAT3( \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are __compressed_pair_padding and _LIBCPP_CONCAT3 defined? in libc++? I don't think we want to depend on it for several reasons:
- it makes the test non-hermetic
- it means the test will not run without libc++
- libc++ folks might not like us reaching into their private headers like that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure if we really need the _LIBCPP_CONCAT3 concat thing. Since its going to produce nondeterministic (well deterministic, but it will change very easily) values, we can't really rely on it anywhere, so maybe we could just hardcode something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea good point, relying on libc++ is what we wanted to avoid here in the first place. __compressed_pair_padding
and _LIBCPP_CONCAT3
are currently not part of libc++ (but will be soon). I'll go with your suggestion
@@ -0,0 +1,43 @@ | |||
#include "../compressed_pair.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have some common includes in packages/Python/lldbsuite/test/make/
. I think it'd be better to put this there (probably in a subdirectory)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup will do, this was the easiest for the prototype. But I'll move it to packages/Python/lldbsuite/test/make/
as you suggest.
|
||
#define _LLDB_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \ | ||
[[__gnu__::__aligned__(alignof(T2))]] [[no_unique_address]] T1 Initializer1; \ | ||
[[no_unique_address]] __compressed_pair_padding<T1> __padding1_; \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now this doesn't cause any clashes. I think I've seen one instance in libc++ where we would have two compressed pairs within the same type. Can probably tackle that once we get to that? Alternatively could add another suffix
parameter to this macro for an easy way to avoid clashes
acad452
to
1996194
Compare
I removed the new compressed_pair layouts from this patch so we can land it in isolation. I'll open a separate PR that introduces those back which we can then land once libc++ actually introduces those new layouts in-tree. |
) Summary: This is motivated by the upcoming refactor of libc++'s `__compressed_pair` in llvm#76756 As this will require changes to numerous LLDB libc++ data-formatters (see early draft llvm#96538), it would be nice to have a test-suite that will actually exercise both the old and new layout. We have a matrix bot that tests old versions of Clang (but currently those only date back to Clang-15). Having them in the test-suite will give us quicker signal on what broke. We have an existing test that exercises various layouts of `std::string` over time in `TestDataFormatterLibcxxStringSimulator.py`, but that's the only STL type we have it for. This patch proposes a new `libcxx-simulators` directory which will take the same approach for all the STL types that we can feasibly support in this way (as @labath points out, for some types this might just not be possible due to their implementation complexity). Nonetheless, it'd be great to have a record of how the layout of libc++ types changed over time. Some related discussion: * llvm#97568 (comment) Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D59822424
This is motivated by the upcoming refactor of libc++'s `__compressed_pair` in #76756 As this will require changes to numerous LLDB libc++ data-formatters (see early draft #96538), it would be nice to have a test-suite that will actually exercise both the old and new layout. We have a matrix bot that tests old versions of Clang (but currently those only date back to Clang-15). Having them in the test-suite will give us quicker signal on what broke. We have an existing test that exercises various layouts of `std::string` over time in `TestDataFormatterLibcxxStringSimulator.py`, but that's the only STL type we have it for. This patch proposes a new `libcxx-simulators` directory which will take the same approach for all the STL types that we can feasibly support in this way (as @labath points out, for some types this might just not be possible due to their implementation complexity). Nonetheless, it'd be great to have a record of how the layout of libc++ types changed over time. Some related discussion: * #97568 (comment)
This is motivated by the upcoming refactor of libc++'s
__compressed_pair
in #76756As this will require changes to numerous LLDB libc++ data-formatters (see early draft #96538), it would be nice to have a test-suite that will actually exercise both the old and new layout. We have a matrix bot that tests old versions of Clang (but currently those only date back to Clang-15). Having them in the test-suite will give us quicker signal on what broke.
We have an existing test that exercises various layouts of
std::string
over time inTestDataFormatterLibcxxStringSimulator.py
, but that's the only STL type we have it for. This patch proposes a newlibcxx-simulators
directory which will take the same approach for all the STL types that we can feasibly support in this way (as @labath points out, for some types this might just not be possible due to their implementation complexity). Nonetheless, it'd be great to have a record of how the layout of libc++ types changed over time. Eventually we'll move thestd::string
simulator into this directory too.Some related discussion: