Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
39642 views
//===-- LibCxxUnorderedMap.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/ConstString.h"16#include "lldb/Utility/DataBufferHeap.h"17#include "lldb/Utility/Endian.h"18#include "lldb/Utility/Status.h"19#include "lldb/Utility/Stream.h"20#include "llvm/ADT/StringRef.h"2122using namespace lldb;23using namespace lldb_private;24using namespace lldb_private::formatters;2526namespace lldb_private {27namespace formatters {28class LibcxxStdUnorderedMapSyntheticFrontEnd29: public SyntheticChildrenFrontEnd {30public:31LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);3233~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;3435llvm::Expected<uint32_t> CalculateNumChildren() override;3637lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;3839lldb::ChildCacheState Update() override;4041bool MightHaveChildren() override;4243size_t GetIndexOfChildWithName(ConstString name) override;4445private:46CompilerType m_element_type;47CompilerType m_node_type;48ValueObject *m_tree = nullptr;49size_t m_num_elements = 0;50ValueObject *m_next_element = nullptr;51std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;52};5354class LibCxxUnorderedMapIteratorSyntheticFrontEnd55: public SyntheticChildrenFrontEnd {56public:57LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);5859~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default;6061llvm::Expected<uint32_t> CalculateNumChildren() override;6263lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;6465lldb::ChildCacheState Update() override;6667bool MightHaveChildren() override;6869size_t GetIndexOfChildWithName(ConstString name) override;7071private:72lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair73///< that the iterator currently points74///< to.75};7677} // namespace formatters78} // namespace lldb_private7980lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::81LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)82: SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(),83m_elements_cache() {84if (valobj_sp)85Update();86}8788llvm::Expected<uint32_t> lldb_private::formatters::89LibcxxStdUnorderedMapSyntheticFrontEnd::CalculateNumChildren() {90return m_num_elements;91}9293static void consumeInlineNamespace(llvm::StringRef &name) {94// Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::95auto scratch = name;96if (scratch.consume_front("__") && std::isalnum(scratch[0])) {97scratch = scratch.drop_while([](char c) { return std::isalnum(c); });98if (scratch.consume_front("::")) {99// Successfully consumed a namespace.100name = scratch;101}102}103}104105static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {106llvm::StringRef name = type_name.GetStringRef();107// The type name may be prefixed with `std::__<inline-namespace>::`.108if (name.consume_front("std::"))109consumeInlineNamespace(name);110return name.consume_front(type) && name.starts_with("<");111}112113static bool isUnorderedMap(ConstString type_name) {114return isStdTemplate(type_name, "unordered_map") ||115isStdTemplate(type_name, "unordered_multimap");116}117118lldb::ValueObjectSP lldb_private::formatters::119LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {120if (idx >= CalculateNumChildrenIgnoringErrors())121return lldb::ValueObjectSP();122if (m_tree == nullptr)123return lldb::ValueObjectSP();124125while (idx >= m_elements_cache.size()) {126if (m_next_element == nullptr)127return lldb::ValueObjectSP();128129Status error;130ValueObjectSP node_sp = m_next_element->Dereference(error);131if (!node_sp || error.Fail())132return lldb::ValueObjectSP();133134ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_");135ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_");136if (!hash_sp || !value_sp) {137if (!m_element_type) {138auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});139if (!p1_sp)140return nullptr;141142ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);143if (!first_sp)144return nullptr;145146m_element_type = first_sp->GetCompilerType();147m_element_type = m_element_type.GetTypeTemplateArgument(0);148m_element_type = m_element_type.GetPointeeType();149m_node_type = m_element_type;150m_element_type = m_element_type.GetTypeTemplateArgument(0);151// This synthetic provider is used for both unordered_(multi)map and152// unordered_(multi)set. For unordered_map, the element type has an153// additional type layer, an internal struct (`__hash_value_type`)154// that wraps a std::pair. Peel away the internal wrapper type - whose155// structure is of no value to users, to expose the std::pair. This156// matches the structure returned by the std::map synthetic provider.157if (isUnorderedMap(m_backend.GetTypeName())) {158std::string name;159CompilerType field_type = m_element_type.GetFieldAtIndex(1600, name, nullptr, nullptr, nullptr);161CompilerType actual_type = field_type.GetTypedefedType();162if (isStdTemplate(actual_type.GetTypeName(), "pair"))163m_element_type = actual_type;164}165}166if (!m_node_type)167return nullptr;168node_sp = m_next_element->Cast(m_node_type.GetPointerType())169->Dereference(error);170if (!node_sp || error.Fail())171return nullptr;172173hash_sp = node_sp->GetChildMemberWithName("__hash_");174if (!hash_sp)175return nullptr;176177value_sp = node_sp->GetChildMemberWithName("__value_");178if (!value_sp) {179// clang-format off180// Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an181// anonymous union.182// Child 0: __hash_node_base base class183// Child 1: __hash_184// Child 2: anonymous union185// clang-format on186auto anon_union_sp = node_sp->GetChildAtIndex(2);187if (!anon_union_sp)188return nullptr;189190value_sp = anon_union_sp->GetChildMemberWithName("__value_");191if (!value_sp)192return nullptr;193}194}195m_elements_cache.push_back(196{value_sp.get(), hash_sp->GetValueAsUnsigned(0)});197m_next_element = node_sp->GetChildMemberWithName("__next_").get();198if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)199m_next_element = nullptr;200}201202std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];203if (!val_hash.first)204return lldb::ValueObjectSP();205StreamString stream;206stream.Printf("[%" PRIu64 "]", (uint64_t)idx);207DataExtractor data;208Status error;209val_hash.first->GetData(data, error);210if (error.Fail())211return lldb::ValueObjectSP();212const bool thread_and_frame_only_if_stopped = true;213ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(214thread_and_frame_only_if_stopped);215return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,216m_element_type);217}218219lldb::ChildCacheState220lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() {221m_num_elements = 0;222m_next_element = nullptr;223m_elements_cache.clear();224ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_");225if (!table_sp)226return lldb::ChildCacheState::eRefetch;227228ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_");229if (!p2_sp)230return lldb::ChildCacheState::eRefetch;231232ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp);233if (!num_elements_sp)234return lldb::ChildCacheState::eRefetch;235236ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_");237if (!p1_sp)238return lldb::ChildCacheState::eRefetch;239240ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);241if (!value_sp)242return lldb::ChildCacheState::eRefetch;243244m_tree = value_sp->GetChildMemberWithName("__next_").get();245if (m_tree == nullptr)246return lldb::ChildCacheState::eRefetch;247248m_num_elements = num_elements_sp->GetValueAsUnsigned(0);249250if (m_num_elements > 0)251m_next_element = m_tree;252253return lldb::ChildCacheState::eRefetch;254}255256bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::257MightHaveChildren() {258return true;259}260261size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::262GetIndexOfChildWithName(ConstString name) {263return ExtractIndexFromString(name.GetCString());264}265266SyntheticChildrenFrontEnd *267lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(268CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {269return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)270: nullptr);271}272273lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::274LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)275: SyntheticChildrenFrontEnd(*valobj_sp) {276if (valobj_sp)277Update();278}279280lldb::ChildCacheState lldb_private::formatters::281LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() {282m_pair_sp.reset();283284ValueObjectSP valobj_sp = m_backend.GetSP();285if (!valobj_sp)286return lldb::ChildCacheState::eRefetch;287288TargetSP target_sp(valobj_sp->GetTargetSP());289290if (!target_sp)291return lldb::ChildCacheState::eRefetch;292293// Get the unordered_map::iterator294// m_backend is an 'unordered_map::iterator', aka a295// '__hash_map_iterator<__hash_table::iterator>'296//297// __hash_map_iterator::__i_ is a __hash_table::iterator (aka298// __hash_iterator<__node_pointer>)299auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_");300if (!hash_iter_sp)301return lldb::ChildCacheState::eRefetch;302303// Type is '__hash_iterator<__node_pointer>'304auto hash_iter_type = hash_iter_sp->GetCompilerType();305if (!hash_iter_type.IsValid())306return lldb::ChildCacheState::eRefetch;307308// Type is '__node_pointer'309auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0);310if (!node_pointer_type.IsValid())311return lldb::ChildCacheState::eRefetch;312313// Cast the __hash_iterator to a __node_pointer (which stores our key/value314// pair)315auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type);316if (!hash_node_sp)317return lldb::ChildCacheState::eRefetch;318319auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_");320if (!key_value_sp) {321// clang-format off322// Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an323// anonymous union.324// Child 0: __hash_node_base base class325// Child 1: __hash_326// Child 2: anonymous union327// clang-format on328auto anon_union_sp = hash_node_sp->GetChildAtIndex(2);329if (!anon_union_sp)330return lldb::ChildCacheState::eRefetch;331332key_value_sp = anon_union_sp->GetChildMemberWithName("__value_");333if (!key_value_sp)334return lldb::ChildCacheState::eRefetch;335}336337// Create the synthetic child, which is a pair where the key and value can be338// retrieved by querying the synthetic frontend for339// GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")340// respectively.341//342// std::unordered_map stores the actual key/value pair in343// __hash_value_type::__cc_ (or previously __cc).344auto potential_child_sp = key_value_sp->Clone(ConstString("pair"));345if (potential_child_sp)346if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1)347if (auto child0_sp = potential_child_sp->GetChildAtIndex(0);348child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")349potential_child_sp = child0_sp->Clone(ConstString("pair"));350351m_pair_sp = potential_child_sp;352353return lldb::ChildCacheState::eRefetch;354}355356llvm::Expected<uint32_t> lldb_private::formatters::357LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() {358return 2;359}360361lldb::ValueObjectSP lldb_private::formatters::362LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {363if (m_pair_sp)364return m_pair_sp->GetChildAtIndex(idx);365return lldb::ValueObjectSP();366}367368bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::369MightHaveChildren() {370return true;371}372373size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::374GetIndexOfChildWithName(ConstString name) {375if (name == "first")376return 0;377if (name == "second")378return 1;379return UINT32_MAX;380}381382SyntheticChildrenFrontEnd *383lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(384CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {385return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp)386: nullptr);387}388389390