Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
39642 views
//===-- LibCxxMap.cpp -----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "LibCxx.h"910#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"11#include "lldb/Core/ValueObject.h"12#include "lldb/Core/ValueObjectConstResult.h"13#include "lldb/DataFormatters/FormattersHelpers.h"14#include "lldb/Target/Target.h"15#include "lldb/Utility/DataBufferHeap.h"16#include "lldb/Utility/Endian.h"17#include "lldb/Utility/Status.h"18#include "lldb/Utility/Stream.h"19#include "lldb/lldb-enumerations.h"20#include "lldb/lldb-forward.h"21#include <cstdint>22#include <locale>23#include <optional>2425using namespace lldb;26using namespace lldb_private;27using namespace lldb_private::formatters;2829// The flattened layout of the std::__tree_iterator::__ptr_ looks30// as follows:31//32// The following shows the contiguous block of memory:33//34// +-----------------------------+ class __tree_end_node35// __ptr_ | pointer __left_; |36// +-----------------------------+ class __tree_node_base37// | pointer __right_; |38// | __parent_pointer __parent_; |39// | bool __is_black_; |40// +-----------------------------+ class __tree_node41// | __node_value_type __value_; | <<< our key/value pair42// +-----------------------------+43//44// where __ptr_ has type __iter_pointer.4546class MapEntry {47public:48MapEntry() = default;49explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}50explicit MapEntry(ValueObject *entry)51: m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}5253ValueObjectSP left() const {54if (!m_entry_sp)55return m_entry_sp;56return m_entry_sp->GetSyntheticChildAtOffset(570, m_entry_sp->GetCompilerType(), true);58}5960ValueObjectSP right() const {61if (!m_entry_sp)62return m_entry_sp;63return m_entry_sp->GetSyntheticChildAtOffset(64m_entry_sp->GetProcessSP()->GetAddressByteSize(),65m_entry_sp->GetCompilerType(), true);66}6768ValueObjectSP parent() const {69if (!m_entry_sp)70return m_entry_sp;71return m_entry_sp->GetSyntheticChildAtOffset(722 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),73m_entry_sp->GetCompilerType(), true);74}7576uint64_t value() const {77if (!m_entry_sp)78return 0;79return m_entry_sp->GetValueAsUnsigned(0);80}8182bool error() const {83if (!m_entry_sp)84return true;85return m_entry_sp->GetError().Fail();86}8788bool null() const { return (value() == 0); }8990ValueObjectSP GetEntry() const { return m_entry_sp; }9192void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }9394bool operator==(const MapEntry &rhs) const {95return (rhs.m_entry_sp.get() == m_entry_sp.get());96}9798private:99ValueObjectSP m_entry_sp;100};101102class MapIterator {103public:104MapIterator(ValueObject *entry, size_t depth = 0)105: m_entry(entry), m_max_depth(depth), m_error(false) {}106107MapIterator() = default;108109ValueObjectSP value() { return m_entry.GetEntry(); }110111ValueObjectSP advance(size_t count) {112ValueObjectSP fail;113if (m_error)114return fail;115size_t steps = 0;116while (count > 0) {117next();118count--, steps++;119if (m_error || m_entry.null() || (steps > m_max_depth))120return fail;121}122return m_entry.GetEntry();123}124125private:126/// Mimicks libc++'s __tree_next algorithm, which libc++ uses127/// in its __tree_iteartor::operator++.128void next() {129if (m_entry.null())130return;131MapEntry right(m_entry.right());132if (!right.null()) {133m_entry = tree_min(std::move(right));134return;135}136size_t steps = 0;137while (!is_left_child(m_entry)) {138if (m_entry.error()) {139m_error = true;140return;141}142m_entry.SetEntry(m_entry.parent());143steps++;144if (steps > m_max_depth) {145m_entry = MapEntry();146return;147}148}149m_entry = MapEntry(m_entry.parent());150}151152/// Mimicks libc++'s __tree_min algorithm.153MapEntry tree_min(MapEntry x) {154if (x.null())155return MapEntry();156MapEntry left(x.left());157size_t steps = 0;158while (!left.null()) {159if (left.error()) {160m_error = true;161return MapEntry();162}163x = left;164left.SetEntry(x.left());165steps++;166if (steps > m_max_depth)167return MapEntry();168}169return x;170}171172bool is_left_child(const MapEntry &x) {173if (x.null())174return false;175MapEntry rhs(x.parent());176rhs.SetEntry(rhs.left());177return x.value() == rhs.value();178}179180MapEntry m_entry;181size_t m_max_depth = 0;182bool m_error = false;183};184185namespace lldb_private {186namespace formatters {187class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {188public:189LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);190191~LibcxxStdMapSyntheticFrontEnd() override = default;192193llvm::Expected<uint32_t> CalculateNumChildren() override;194195lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;196197lldb::ChildCacheState Update() override;198199bool MightHaveChildren() override;200201size_t GetIndexOfChildWithName(ConstString name) override;202203private:204/// Returns the ValueObject for the __tree_node type that205/// holds the key/value pair of the node at index \ref idx.206///207/// \param[in] idx The child index that we're looking to get208/// the key/value pair for.209///210/// \param[in] max_depth The maximum search depth after which211/// we stop trying to find the key/value212/// pair for.213///214/// \returns On success, returns the ValueObjectSP corresponding215/// to the __tree_node's __value_ member (which holds216/// the key/value pair the formatter wants to display).217/// On failure, will return nullptr.218ValueObjectSP GetKeyValuePair(size_t idx, size_t max_depth);219220ValueObject *m_tree = nullptr;221ValueObject *m_root_node = nullptr;222CompilerType m_node_ptr_type;223size_t m_count = UINT32_MAX;224std::map<size_t, MapIterator> m_iterators;225};226227class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {228public:229LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);230231llvm::Expected<uint32_t> CalculateNumChildren() override;232233lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;234235lldb::ChildCacheState Update() override;236237bool MightHaveChildren() override;238239size_t GetIndexOfChildWithName(ConstString name) override;240241~LibCxxMapIteratorSyntheticFrontEnd() override = default;242243private:244ValueObjectSP m_pair_sp = nullptr;245};246} // namespace formatters247} // namespace lldb_private248249lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::250LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)251: SyntheticChildrenFrontEnd(*valobj_sp) {252if (valobj_sp)253Update();254}255256llvm::Expected<uint32_t> lldb_private::formatters::257LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {258if (m_count != UINT32_MAX)259return m_count;260261if (m_tree == nullptr)262return 0;263264ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_"));265if (!size_node)266return 0;267268size_node = GetFirstValueOfLibCXXCompressedPair(*size_node);269270if (!size_node)271return 0;272273m_count = size_node->GetValueAsUnsigned(0);274return m_count;275}276277ValueObjectSP278lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetKeyValuePair(279size_t idx, size_t max_depth) {280MapIterator iterator(m_root_node, max_depth);281282size_t advance_by = idx;283if (idx > 0) {284// If we have already created the iterator for the previous285// index, we can start from there and advance by 1.286auto cached_iterator = m_iterators.find(idx - 1);287if (cached_iterator != m_iterators.end()) {288iterator = cached_iterator->second;289advance_by = 1;290}291}292293ValueObjectSP iterated_sp(iterator.advance(advance_by));294if (!iterated_sp)295// this tree is garbage - stop296return nullptr;297298if (!m_node_ptr_type.IsValid())299return nullptr;300301// iterated_sp is a __iter_pointer at this point.302// We can cast it to a __node_pointer (which is what libc++ does).303auto value_type_sp = iterated_sp->Cast(m_node_ptr_type);304if (!value_type_sp)305return nullptr;306307// Finally, get the key/value pair.308value_type_sp = value_type_sp->GetChildMemberWithName("__value_");309if (!value_type_sp)310return nullptr;311312m_iterators[idx] = iterator;313314return value_type_sp;315}316317lldb::ValueObjectSP318lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex(319uint32_t idx) {320static ConstString g_cc_("__cc_"), g_cc("__cc");321static ConstString g_nc("__nc");322uint32_t num_children = CalculateNumChildrenIgnoringErrors();323if (idx >= num_children)324return nullptr;325326if (m_tree == nullptr || m_root_node == nullptr)327return nullptr;328329ValueObjectSP key_val_sp = GetKeyValuePair(idx, /*max_depth=*/num_children);330if (!key_val_sp) {331// this will stop all future searches until an Update() happens332m_tree = nullptr;333return nullptr;334}335336// at this point we have a valid337// we need to copy current_sp into a new object otherwise we will end up with338// all items named __value_339StreamString name;340name.Printf("[%" PRIu64 "]", (uint64_t)idx);341auto potential_child_sp = key_val_sp->Clone(ConstString(name.GetString()));342if (potential_child_sp) {343switch (potential_child_sp->GetNumChildrenIgnoringErrors()) {344case 1: {345auto child0_sp = potential_child_sp->GetChildAtIndex(0);346if (child0_sp &&347(child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc))348potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));349break;350}351case 2: {352auto child0_sp = potential_child_sp->GetChildAtIndex(0);353auto child1_sp = potential_child_sp->GetChildAtIndex(1);354if (child0_sp &&355(child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc) &&356child1_sp && child1_sp->GetName() == g_nc)357potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));358break;359}360}361}362return potential_child_sp;363}364365lldb::ChildCacheState366lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {367m_count = UINT32_MAX;368m_tree = m_root_node = nullptr;369m_iterators.clear();370m_tree = m_backend.GetChildMemberWithName("__tree_").get();371if (!m_tree)372return lldb::ChildCacheState::eRefetch;373m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();374m_node_ptr_type =375m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer");376377return lldb::ChildCacheState::eRefetch;378}379380bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::381MightHaveChildren() {382return true;383}384385size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::386GetIndexOfChildWithName(ConstString name) {387return ExtractIndexFromString(name.GetCString());388}389390SyntheticChildrenFrontEnd *391lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(392CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {393return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr);394}395396lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::397LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)398: SyntheticChildrenFrontEnd(*valobj_sp) {399if (valobj_sp)400Update();401}402403lldb::ChildCacheState404lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {405m_pair_sp.reset();406407ValueObjectSP valobj_sp = m_backend.GetSP();408if (!valobj_sp)409return lldb::ChildCacheState::eRefetch;410411TargetSP target_sp(valobj_sp->GetTargetSP());412if (!target_sp)413return lldb::ChildCacheState::eRefetch;414415// m_backend is a std::map::iterator416// ...which is a __map_iterator<__tree_iterator<..., __node_pointer, ...>>417//418// Then, __map_iterator::__i_ is a __tree_iterator419auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_");420if (!tree_iter_sp)421return lldb::ChildCacheState::eRefetch;422423// Type is __tree_iterator::__node_pointer424// (We could alternatively also get this from the template argument)425auto node_pointer_type =426tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName(427"__node_pointer");428if (!node_pointer_type.IsValid())429return lldb::ChildCacheState::eRefetch;430431// __ptr_ is a __tree_iterator::__iter_pointer432auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_");433if (!iter_pointer_sp)434return lldb::ChildCacheState::eRefetch;435436// Cast the __iter_pointer to a __node_pointer (which stores our key/value437// pair)438auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type);439if (!node_pointer_sp)440return lldb::ChildCacheState::eRefetch;441442auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_");443if (!key_value_sp)444return lldb::ChildCacheState::eRefetch;445446// Create the synthetic child, which is a pair where the key and value can be447// retrieved by querying the synthetic frontend for448// GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")449// respectively.450//451// std::map stores the actual key/value pair in value_type::__cc_ (or452// previously __cc).453key_value_sp = key_value_sp->Clone(ConstString("pair"));454if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) {455auto child0_sp = key_value_sp->GetChildAtIndex(0);456if (child0_sp &&457(child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc"))458key_value_sp = child0_sp->Clone(ConstString("pair"));459}460461m_pair_sp = key_value_sp;462463return lldb::ChildCacheState::eRefetch;464}465466llvm::Expected<uint32_t> lldb_private::formatters::467LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() {468return 2;469}470471lldb::ValueObjectSP472lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(473uint32_t idx) {474if (!m_pair_sp)475return nullptr;476477return m_pair_sp->GetChildAtIndex(idx);478}479480bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::481MightHaveChildren() {482return true;483}484485size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::486GetIndexOfChildWithName(ConstString name) {487if (!m_pair_sp)488return UINT32_MAX;489490return m_pair_sp->GetIndexOfChildWithName(name);491}492493SyntheticChildrenFrontEnd *494lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(495CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {496return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)497: nullptr);498}499500501