//===-- LibStdcppSmartPointer.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 "lldb/Core/ConstString.h" #include "lldb/Core/ValueObject.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/DataFormatters/TypeSynthetic.h" #include "lldb/Target/Target.h" #include #include using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; namespace { class SharedPtrFrontEnd : public SyntheticChildrenFrontEnd { public: explicit SharedPtrFrontEnd(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_use_obj; ValueObjectSP m_weak_obj; uint8_t m_ptr_size = 0; lldb::ByteOrder m_byte_order = lldb::eByteOrderInvalid; bool IsEmpty(); bool IsValid(); }; } // end of anonymous namespace SharedPtrFrontEnd::SharedPtrFrontEnd(lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp) { Update(); } bool SharedPtrFrontEnd::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; TargetSP target_sp(valobj_sp->GetTargetSP()); if (!target_sp) return false; m_byte_order = target_sp->GetArchitecture().GetByteOrder(); m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize(); m_ptr_obj = valobj_sp->GetChildMemberWithName(ConstString("_M_ptr"), true); m_use_obj = valobj_sp->GetChildAtNamePath({ConstString("_M_refcount"), ConstString("_M_pi"), ConstString("_M_use_count")}); m_weak_obj = valobj_sp->GetChildAtNamePath({ConstString("_M_refcount"), ConstString("_M_pi"), ConstString("_M_weak_count")}); // libstdc++ implements the weak usage count in a way that it is offset by 1 // if the strong count is not 0 (as part of a preformance optimization). We // want to undo this before showing the weak count to the user as an offseted // weak count would be very confusing. if (m_use_obj && m_weak_obj && m_use_obj->GetValueAsUnsigned(0) > 0) { bool success = false; uint64_t count = m_weak_obj->GetValueAsUnsigned(0, &success) - 1; if (success) { auto data = std::make_shared(&count, sizeof(count)); m_weak_obj = CreateValueObjectFromData( "weak_count", DataExtractor(data, m_byte_order, m_ptr_size), m_weak_obj->GetExecutionContextRef(), m_weak_obj->GetCompilerType()); } } if (m_ptr_obj && !IsEmpty()) { Error error; m_obj_obj = m_ptr_obj->Dereference(error); if (error.Success()) { m_obj_obj->SetName(ConstString("object")); } } return false; } bool SharedPtrFrontEnd::MightHaveChildren() { return true; } lldb::ValueObjectSP SharedPtrFrontEnd::GetChildAtIndex(size_t idx) { if (idx == 0) return m_obj_obj; if (idx == 1) return m_ptr_obj; if (idx == 2) return m_use_obj; if (idx == 3) return m_weak_obj; return lldb::ValueObjectSP(); } size_t SharedPtrFrontEnd::CalculateNumChildren() { if (IsEmpty()) return 0; return 1; } size_t SharedPtrFrontEnd::GetIndexOfChildWithName(const ConstString &name) { if (name == ConstString("obj") || name == ConstString("object")) return 0; if (name == ConstString("ptr") || name == ConstString("pointer") || name == ConstString("_M_ptr")) return 1; if (name == ConstString("cnt") || name == ConstString("count") || name == ConstString("use_count") || name == ConstString("strong") || name == ConstString("_M_use_count")) return 2; if (name == ConstString("weak") || name == ConstString("weak_count") || name == ConstString("_M_weak_count")) return 3; return UINT32_MAX; } bool SharedPtrFrontEnd::GetSummary(Stream &stream, const TypeSummaryOptions &options) { if (!IsValid()) return false; if (IsEmpty()) { 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)); } if (m_use_obj && m_use_obj->GetError().Success()) stream.Printf(" strong=%" PRIu64, m_use_obj->GetValueAsUnsigned(0)); if (m_weak_obj && m_weak_obj->GetError().Success()) stream.Printf(" weak=%" PRIu64, m_weak_obj->GetValueAsUnsigned(0)); return true; } bool SharedPtrFrontEnd::IsValid() { return m_ptr_obj != nullptr; } bool SharedPtrFrontEnd::IsEmpty() { return !IsValid() || m_ptr_obj->GetValueAsUnsigned(0) == 0 || (m_use_obj && m_use_obj->GetValueAsUnsigned(0) == 0); } SyntheticChildrenFrontEnd * lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { return valobj_sp ? new SharedPtrFrontEnd(valobj_sp) : nullptr; } bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { SharedPtrFrontEnd formatter(valobj.GetSP()); return formatter.GetSummary(stream, options); }