kastiglione created this revision. kastiglione added reviewers: teemperor, jingham, JDevlieghere. kastiglione requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
Add frame variable dereference suppport to libc++ `std::unique_ptr`. This change allows for commands like `v *thing_up` and `v thing_up->m_id`. These commands now work the same way they would with raw pointers, and as they would with expression. This is done by adding an unaccounted for child member named `$$dereference$$`. Without this change, the command would have to be written as `v *thing_up.__value_` or v thing_up.__value_->m_id` which exposes internal structure and is more clumsy to type. Additionally, the existing tests were updated. See also https://reviews.llvm.org/D97165 which added deref support for `std::shared_ptr`. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D97524 Files: lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp lldb/source/Plugins/Language/CPlusPlus/LibCxx.h lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp =================================================================== --- lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp +++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp @@ -1,13 +1,18 @@ -#include <cstdio> #include <memory> #include <string> +struct User { + int id = 30; + std::string name = "steph"; +}; + int main() { std::unique_ptr<int> up_empty; std::unique_ptr<int> up_int = std::make_unique<int>(10); std::unique_ptr<std::string> up_str = std::make_unique<std::string>("hello"); std::unique_ptr<int> &up_int_ref = up_int; std::unique_ptr<int> &&up_int_ref_ref = std::make_unique<int>(10); + std::unique_ptr<User> up_user = std::make_unique<User>(); return 0; // break here } Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py =================================================================== --- lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py +++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py @@ -3,45 +3,85 @@ """ - import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil -class LibcxUniquePtrDataFormatterTestCase(TestBase): + +class TestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @add_test_categories(["libc++"]) - def test_with_run_command(self): - """Test that that file and class static variables display correctly.""" + def test_unique_ptr_variables(self): + """Test `frame variable` output for `std::unique_ptr` types.""" self.build() - (self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here', - lldb.SBFileSpec("main.cpp", False)) + lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp") + ) - self.expect("frame variable up_empty", - substrs=['(std::unique_ptr<int, std::default_delete<int> >) up_empty = nullptr {', - '__value_ = ', - '}']) + valobj = self.expect_var_path( + "up_empty", + type="std::unique_ptr<int, std::default_delete<int> >", + summary="nullptr", + children=[ValueCheck(name="__value_")], + ) + self.assertEqual( + valobj.child[0].GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS), 0 + ) - self.expect("frame variable up_int", - substrs=['(std::unique_ptr<int, std::default_delete<int> >) up_int = 10 {', - '__value_ = ', - '}']) + self.expect( + "frame variable *up_empty", substrs=["(int) *up_empty = <parent is NULL>"] + ) - self.expect("frame variable up_int_ref", - substrs=['(std::unique_ptr<int, std::default_delete<int> > &) up_int_ref = 10: {', - '__value_ = ', - '}']) + valobj = self.expect_var_path( + "up_int", + type="std::unique_ptr<int, std::default_delete<int> >", + summary="10", + children=[ValueCheck(name="__value_")], + ) + self.assertNotEqual(valobj.child[0].unsigned, 0) - self.expect("frame variable up_int_ref_ref", - substrs=['(std::unique_ptr<int, std::default_delete<int> > &&) up_int_ref_ref = 10: {', - '__value_ = ', - '}']) + valobj = self.expect_var_path( + "up_int_ref", + type="std::unique_ptr<int, std::default_delete<int> > &", + summary="10", + children=[ValueCheck(name="__value_")], + ) + self.assertNotEqual(valobj.child[0].unsigned, 0) - self.expect("frame variable up_str", - substrs=['up_str = "hello" {', - '__value_ = ', - '}']) + valobj = self.expect_var_path( + "up_int_ref_ref", + type="std::unique_ptr<int, std::default_delete<int> > &&", + summary="10", + children=[ValueCheck(name="__value_")], + ) + self.assertNotEqual(valobj.child[0].unsigned, 0) + + valobj = self.expect_var_path( + "up_str", + type="std::unique_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >", + summary='"hello"', + children=[ValueCheck(name="__value_", summary='"hello"')], + ) + + valobj = self.expect_var_path( + "up_user", type="std::unique_ptr<User, std::default_delete<User> >" + ) + self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$") + self.assertNotEqual(valobj.child[0].unsigned, 0) + + valobj = self.expect_var_path( + "*up_user", + type="User", + children=[ + ValueCheck(name="id", value="30"), + ValueCheck(name="name", summary='"steph"'), + ], + ) + self.assertEqual(str(valobj), '(User) *__value_ = (id = 30, name = "steph")') + + self.expect_var_path("up_user->id", type="int", value="30") + self.expect_var_path("up_user->name", type="std::string", summary='"steph"') Index: lldb/source/Plugins/Language/CPlusPlus/LibCxx.h =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -124,7 +124,7 @@ ~LibcxxUniquePtrSyntheticFrontEnd() override; private: - lldb::ValueObjectSP m_compressed_pair_sp; + lldb::ValueObjectSP m_value_ptr_sp; }; SyntheticChildrenFrontEnd * Index: lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -463,7 +463,7 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) - : SyntheticChildrenFrontEnd(*valobj_sp), m_compressed_pair_sp() { + : SyntheticChildrenFrontEnd(*valobj_sp) { if (valobj_sp) Update(); } @@ -480,19 +480,27 @@ size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: CalculateNumChildren() { - return (m_compressed_pair_sp ? 1 : 0); + return (m_value_ptr_sp ? 1 : 0); } lldb::ValueObjectSP lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( size_t idx) { - if (!m_compressed_pair_sp) + if (!m_value_ptr_sp) return lldb::ValueObjectSP(); - if (idx != 0) - return lldb::ValueObjectSP(); + if (idx == 0) + return m_value_ptr_sp; - return m_compressed_pair_sp; + if (idx == 1) { + Status status; + auto value_sp = m_value_ptr_sp->Dereference(status); + if (status.Success()) { + return value_sp; + } + } + + return lldb::ValueObjectSP(); } bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { @@ -505,7 +513,7 @@ if (!ptr_sp) return false; - m_compressed_pair_sp = GetValueOfLibCXXCompressedPair(*ptr_sp); + m_value_ptr_sp = GetValueOfLibCXXCompressedPair(*ptr_sp); return false; } @@ -519,6 +527,8 @@ GetIndexOfChildWithName(ConstString name) { if (name == "__value_") return 0; + if (name == "$$dereference$$") + return 1; return UINT32_MAX; }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits