Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBSTDCPP := 1 +CFLAGS_EXTRAS += $(NO_LIMIT_DEBUG_INFO_FLAGS) + +include $(LEVEL)/Makefile.rules Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py @@ -0,0 +1,61 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + +import os +import time +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class StdUniquePtrDataFormatterTestCase(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD + @skipIfWindows # libstdcpp not ported to Windows + @skipIfDarwin # doesn't compile on Darwin + def test_with_run_command(self): + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp( + self, "Set break point at this line.") + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + frame = self.frame() + self.assertTrue(frame.IsValid()) + + self.expect("frame variable nup", substrs=['nup = nullptr']) + self.expect("frame variable iup", substrs=['iup = 123', 'object = 123']) + self.expect("frame variable sup", substrs=['sup = "foobar"', 'object = "foobar"']) + + self.expect("frame variable ndp", substrs=['ndp = nullptr']) + self.expect("frame variable idp", substrs=['idp = 456', 'object = 456', 'deleter = ', 'a = 1', 'b = 2']) + self.expect("frame variable sdp", substrs=['sdp = "baz"', 'object = "baz"', 'deleter = ', 'a = 3', 'b = 4']) + + self.assertEqual(123, frame.GetValueForVariablePath("iup.object").GetValueAsUnsigned()) + self.assertFalse(frame.GetValueForVariablePath("iup.deleter").IsValid()) + + self.assertEqual('"foobar"', frame.GetValueForVariablePath("sup.object").GetSummary()) + self.assertFalse(frame.GetValueForVariablePath("sup.deleter").IsValid()) + + self.assertEqual(456, frame.GetValueForVariablePath("idp.object").GetValueAsUnsigned()) + self.assertEqual('"baz"', frame.GetValueForVariablePath("sdp.object").GetSummary()) + + idp_deleter = frame.GetValueForVariablePath("idp.deleter") + self.assertTrue(idp_deleter.IsValid()) + self.assertEqual(1, idp_deleter.GetChildMemberWithName("a").GetValueAsUnsigned()) + self.assertEqual(2, idp_deleter.GetChildMemberWithName("b").GetValueAsUnsigned()) + + sdp_deleter = frame.GetValueForVariablePath("sdp.deleter") + self.assertTrue(sdp_deleter.IsValid()) + self.assertEqual(3, sdp_deleter.GetChildMemberWithName("a").GetValueAsUnsigned()) + self.assertEqual(4, sdp_deleter.GetChildMemberWithName("b").GetValueAsUnsigned()) Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +struct Deleter { + void operator()(void *) {} + + int a; + int b; +}; + +int main() { + std::unique_ptr nup; + std::unique_ptr iup(new int{123}); + std::unique_ptr sup(new std::string("foobar")); + + std::unique_ptr ndp; + std::unique_ptr idp(new int{456}, Deleter{1, 2}); + std::unique_ptr sdp(new std::string("baz"), + Deleter{3, 4}); + + return 0; // Set break point at this line. +} Index: lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ lldb/trunk/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -12,4 +12,5 @@ LibStdcpp.cpp LibStdcppSmartPointer.cpp LibStdcppTuple.cpp + LibStdcppUniquePointer.cpp ) Index: lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ lldb/trunk/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -803,6 +803,11 @@ AddCXXSynthetic( cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator, + "std::unique_ptr synthetic children", + ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, "std::shared_ptr synthetic children", ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true); @@ -818,6 +823,11 @@ stl_synth_flags, true); AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePointerSummaryProvider, + "libstdc++ std::unique_ptr summary provider", + ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, "libstdc++ std::shared_ptr summary provider", ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, Index: lldb/trunk/source/Plugins/Language/CPlusPlus/LibStdcpp.h =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ lldb/trunk/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -31,6 +31,10 @@ const TypeSummaryOptions &options); // libstdc++ std::shared_ptr<> and std::weak_ptr<> +bool LibStdcppUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libstdc++ std::unique_ptr<> + SyntheticChildrenFrontEnd * LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); @@ -46,6 +50,11 @@ SyntheticChildrenFrontEnd * LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + } // namespace formatters } // namespace lldb_private Index: lldb/trunk/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp =================================================================== --- lldb/trunk/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp +++ lldb/trunk/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp @@ -0,0 +1,160 @@ +//===-- LibStdcppUniquePointer.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LibStdcpp.h" + +#include +#include + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(const ConstString &name) override; + + bool GetSummary(Stream &stream, const TypeSummaryOptions &options); + +private: + ValueObjectSP m_ptr_obj; + ValueObjectSP m_obj_obj; + ValueObjectSP m_del_obj; +}; + +} // end of anonymous namespace + +LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP valobj_backend_sp = m_backend.GetSP(); + if (!valobj_backend_sp) + return false; + + ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue(); + if (!valobj_sp) + return false; + + ValueObjectSP tuple_sp = + valobj_sp->GetChildMemberWithName(ConstString("_M_t"), true); + if (!tuple_sp) + return false; + + std::unique_ptr tuple_frontend( + LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp)); + + m_ptr_obj = tuple_frontend->GetChildAtIndex(0); + if (m_ptr_obj) + m_ptr_obj->SetName(ConstString("pointer")); + + m_del_obj = tuple_frontend->GetChildAtIndex(1); + if (m_del_obj) + m_del_obj->SetName(ConstString("deleter")); + + if (m_ptr_obj) { + Error error; + m_obj_obj = m_ptr_obj->Dereference(error); + if (error.Success()) { + m_obj_obj->SetName(ConstString("object")); + } + } + + return false; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (idx == 0) + return m_obj_obj; + if (idx == 1) + return m_del_obj; + if (idx == 2) + return m_ptr_obj; + return lldb::ValueObjectSP(); +} + +size_t LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() { + if (m_del_obj) + return 2; + if (m_ptr_obj && m_ptr_obj->GetValueAsUnsigned(0) != 0) + return 1; + return 0; +} + +size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName( + const ConstString &name) { + if (name == ConstString("obj") || name == ConstString("object")) + return 0; + if (name == ConstString("del") || name == ConstString("deleter")) + return 1; + if (name == ConstString("ptr") || name == ConstString("pointer")) + return 2; + return UINT32_MAX; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary( + Stream &stream, const TypeSummaryOptions &options) { + if (!m_ptr_obj) + return false; + + if (m_ptr_obj->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + } else { + Error error; + bool print_pointee = false; + if (m_obj_obj) { + if (m_obj_obj->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::ePrintableRepresentationSpecialCasesDisable, + false)) { + print_pointee = true; + } + } + if (!print_pointee) + stream.Printf("ptr = 0x%" PRIx64, m_ptr_obj->GetValueAsUnsigned(0)); + } + return true; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppUniquePtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +bool lldb_private::formatters::LibStdcppUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + LibStdcppUniquePtrSyntheticFrontEnd formatter(valobj.GetSP()); + return formatter.GetSummary(stream, options); +}