Files
clang-p2996/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
jeffreytan81 9faf8b5117 Lazy deference underlying object for shared/weak/unique_ptr synthetic… (#67069)
We noticed some performance issue while in lldb-vscode for grabing the
name of the SBValue. Profiling shows SBValue::GetName() can cause
synthetic children provider of shared/unique_ptr to deference underlying
object and complete it type.

This patch lazily moves the dereference from synthetic child provider's
Update() method to GetChildAtIndex() so that SBValue::GetName() won't
trigger the slow code path.

Here is the culprit slow code path:
```
...
frame #59: 0x00007ff4102e0660 liblldb.so.15`SymbolFileDWARF::CompleteType(this=<unavailable>, compiler_type=0x00007ffdd9829450) at SymbolFileDWARF.cpp:1567:25 [opt]
...
frame #67: 0x00007ff40fdf9bd4 liblldb.so.15`lldb_private::ValueObject::Dereference(this=0x0000022bb5dfe980, error=0x00007ffdd9829970) at ValueObject.cpp:2672:41 [opt]
frame #68: 0x00007ff41011bb0a liblldb.so.15`(anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::Update(this=0x000002298fb94380) at LibStdcpp.cpp:403:40 [opt]
frame #69: 0x00007ff41011af9a liblldb.so.15`lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>) [inlined] (anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(this=0x000002298fb94380, valobj_sp=<unavailable>) at LibStdcpp.cpp:371:5 [opt]
...
frame #78: 0x00007ff40fdf6e42 liblldb.so.15`lldb_private::ValueObject::CalculateSyntheticValue(this=0x000002296c66a500) at ValueObject.cpp:1836:27 [opt]
frame #79: 0x00007ff40fdf1939 liblldb.so.15`lldb_private::ValueObject::GetSyntheticValue(this=<unavailable>) at ValueObject.cpp:1867:3 [opt]
frame #80: 0x00007ff40fc89008 liblldb.so.15`ValueImpl::GetSP(this=0x0000022c71b90de0, stop_locker=0x00007ffdd9829d00, lock=0x00007ffdd9829d08, error=0x00007ffdd9829d18) at SBValue.cpp:141:46 [opt]
frame #81: 0x00007ff40fc7d82a liblldb.so.15`lldb::SBValue::GetSP(ValueLocker&) const [inlined] ValueLocker::GetLockedSP(this=0x00007ffdd9829d00, in_value=<unavailable>) at SBValue.cpp:208:21 [opt]
frame #82: 0x00007ff40fc7d817 liblldb.so.15`lldb::SBValue::GetSP(this=0x00007ffdd9829d90, locker=0x00007ffdd9829d00) const at SBValue.cpp:1047:17 [opt]
frame #83: 0x00007ff40fc7da6f liblldb.so.15`lldb::SBValue::GetName(this=0x00007ffdd9829d90) at SBValue.cpp:294:32 [opt]
...
```

Differential Revision: https://reviews.llvm.org/D159542
2023-09-21 15:45:42 -07:00

183 lines
5.4 KiB
C++

//===-- LibStdcppUniquePointer.cpp ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LibStdcpp.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/DataFormatters/TypeSynthetic.h"
#include "lldb/Utility/ConstString.h"
#include <memory>
#include <vector>
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(ConstString name) override;
bool GetSummary(Stream &stream, const TypeSummaryOptions &options);
private:
// The lifetime of a ValueObject and all its derivative ValueObjects
// (children, clones, etc.) is managed by a ClusterManager. These
// objects are only destroyed when every shared pointer to any of them
// is destroyed, so we must not store a shared pointer to any ValueObject
// derived from our backend ValueObject (since we're in the same cluster).
ValueObject* m_ptr_obj = nullptr;
ValueObject* m_obj_obj = nullptr;
ValueObject* m_del_obj = nullptr;
ValueObjectSP GetTuple();
};
} // end of anonymous namespace
LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd(
lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {
Update();
}
ValueObjectSP LibStdcppUniquePtrSyntheticFrontEnd::GetTuple() {
ValueObjectSP valobj_backend_sp = m_backend.GetSP();
if (!valobj_backend_sp)
return nullptr;
ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue();
if (!valobj_sp)
return nullptr;
ValueObjectSP obj_child_sp = valobj_sp->GetChildMemberWithName("_M_t");
if (!obj_child_sp)
return nullptr;
ValueObjectSP obj_subchild_sp = obj_child_sp->GetChildMemberWithName("_M_t");
// if there is a _M_t subchild, the tuple is found in the obj_subchild_sp
// (for libstdc++ 6.0.23).
if (obj_subchild_sp) {
return obj_subchild_sp;
}
return obj_child_sp;
}
bool LibStdcppUniquePtrSyntheticFrontEnd::Update() {
ValueObjectSP tuple_sp = GetTuple();
if (!tuple_sp)
return false;
std::unique_ptr<SyntheticChildrenFrontEnd> tuple_frontend(
LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp));
ValueObjectSP ptr_obj = tuple_frontend->GetChildAtIndex(0);
if (ptr_obj)
m_ptr_obj = ptr_obj->Clone(ConstString("pointer")).get();
// Add a 'deleter' child if there was a non-empty deleter type specified.
//
// The object might have size=1 in the TypeSystem but occupies no dedicated
// storage due to no_unique_address, so infer the actual size from the total
// size of the unique_ptr class. If sizeof(unique_ptr) == sizeof(void*) then
// the deleter is empty and should be hidden.
if (tuple_sp->GetByteSize() > ptr_obj->GetByteSize()) {
ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1);
if (del_obj)
m_del_obj = del_obj->Clone(ConstString("deleter")).get();
}
m_obj_obj = nullptr;
return false;
}
bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; }
lldb::ValueObjectSP
LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
if (idx == 0 && m_ptr_obj)
return m_ptr_obj->GetSP();
if (idx == 1 && m_del_obj)
return m_del_obj->GetSP();
if (idx == 2) {
if (m_ptr_obj && !m_obj_obj) {
Status error;
ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
if (error.Success()) {
m_obj_obj = obj_obj->Clone(ConstString("object")).get();
}
}
if (m_obj_obj)
return m_obj_obj->GetSP();
}
return lldb::ValueObjectSP();
}
size_t LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
if (m_del_obj)
return 2;
return 1;
}
size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName(
ConstString name) {
if (name == "ptr" || name == "pointer")
return 0;
if (name == "del" || name == "deleter")
return 1;
if (name == "obj" || name == "object" || name == "$$dereference$$")
return 2;
return UINT32_MAX;
}
bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary(
Stream &stream, const TypeSummaryOptions &options) {
if (!m_ptr_obj)
return false;
bool success;
uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success);
if (!success)
return false;
if (ptr_value == 0)
stream.Printf("nullptr");
else
stream.Printf("0x%" PRIx64, ptr_value);
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);
}