1 //===-- LibCxxUnorderedMap.cpp --------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "LibCxx.h" 10 11 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 12 #include "lldb/Core/ValueObject.h" 13 #include "lldb/Core/ValueObjectConstResult.h" 14 #include "lldb/DataFormatters/FormattersHelpers.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Utility/ConstString.h" 17 #include "lldb/Utility/DataBufferHeap.h" 18 #include "lldb/Utility/Endian.h" 19 #include "lldb/Utility/Status.h" 20 #include "lldb/Utility/Stream.h" 21 #include "llvm/ADT/StringRef.h" 22 23 using namespace lldb; 24 using namespace lldb_private; 25 using namespace lldb_private::formatters; 26 27 namespace lldb_private { 28 namespace formatters { 29 class LibcxxStdUnorderedMapSyntheticFrontEnd 30 : public SyntheticChildrenFrontEnd { 31 public: 32 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 33 34 ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default; 35 36 uint32_t CalculateNumChildren() override; 37 38 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 39 40 lldb::ChildCacheState Update() override; 41 42 bool MightHaveChildren() override; 43 44 size_t GetIndexOfChildWithName(ConstString name) override; 45 46 private: 47 CompilerType m_element_type; 48 CompilerType m_node_type; 49 ValueObject *m_tree = nullptr; 50 size_t m_num_elements = 0; 51 ValueObject *m_next_element = nullptr; 52 std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; 53 }; 54 } // namespace formatters 55 } // namespace lldb_private 56 57 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 58 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 59 : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), 60 m_elements_cache() { 61 if (valobj_sp) 62 Update(); 63 } 64 65 uint32_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 66 CalculateNumChildren() { 67 return m_num_elements; 68 } 69 70 static void consumeInlineNamespace(llvm::StringRef &name) { 71 // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: 72 auto scratch = name; 73 if (scratch.consume_front("__") && std::isalnum(scratch[0])) { 74 scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); 75 if (scratch.consume_front("::")) { 76 // Successfully consumed a namespace. 77 name = scratch; 78 } 79 } 80 } 81 82 static bool isStdTemplate(ConstString type_name, llvm::StringRef type) { 83 llvm::StringRef name = type_name.GetStringRef(); 84 // The type name may be prefixed with `std::__<inline-namespace>::`. 85 if (name.consume_front("std::")) 86 consumeInlineNamespace(name); 87 return name.consume_front(type) && name.starts_with("<"); 88 } 89 90 static bool isUnorderedMap(ConstString type_name) { 91 return isStdTemplate(type_name, "unordered_map") || 92 isStdTemplate(type_name, "unordered_multimap"); 93 } 94 95 lldb::ValueObjectSP lldb_private::formatters:: 96 LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) { 97 if (idx >= CalculateNumChildren()) 98 return lldb::ValueObjectSP(); 99 if (m_tree == nullptr) 100 return lldb::ValueObjectSP(); 101 102 while (idx >= m_elements_cache.size()) { 103 if (m_next_element == nullptr) 104 return lldb::ValueObjectSP(); 105 106 Status error; 107 ValueObjectSP node_sp = m_next_element->Dereference(error); 108 if (!node_sp || error.Fail()) 109 return lldb::ValueObjectSP(); 110 111 ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_"); 112 ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_"); 113 if (!hash_sp || !value_sp) { 114 if (!m_element_type) { 115 auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); 116 if (!p1_sp) 117 return nullptr; 118 119 ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); 120 if (!first_sp) 121 return nullptr; 122 123 m_element_type = first_sp->GetCompilerType(); 124 m_element_type = m_element_type.GetTypeTemplateArgument(0); 125 m_element_type = m_element_type.GetPointeeType(); 126 m_node_type = m_element_type; 127 m_element_type = m_element_type.GetTypeTemplateArgument(0); 128 // This synthetic provider is used for both unordered_(multi)map and 129 // unordered_(multi)set. For unordered_map, the element type has an 130 // additional type layer, an internal struct (`__hash_value_type`) 131 // that wraps a std::pair. Peel away the internal wrapper type - whose 132 // structure is of no value to users, to expose the std::pair. This 133 // matches the structure returned by the std::map synthetic provider. 134 if (isUnorderedMap(m_backend.GetTypeName())) { 135 std::string name; 136 CompilerType field_type = m_element_type.GetFieldAtIndex( 137 0, name, nullptr, nullptr, nullptr); 138 CompilerType actual_type = field_type.GetTypedefedType(); 139 if (isStdTemplate(actual_type.GetTypeName(), "pair")) 140 m_element_type = actual_type; 141 } 142 } 143 if (!m_node_type) 144 return nullptr; 145 node_sp = m_next_element->Cast(m_node_type.GetPointerType()) 146 ->Dereference(error); 147 if (!node_sp || error.Fail()) 148 return nullptr; 149 150 hash_sp = node_sp->GetChildMemberWithName("__hash_"); 151 if (!hash_sp) 152 return nullptr; 153 154 value_sp = node_sp->GetChildMemberWithName("__value_"); 155 if (!value_sp) { 156 // clang-format off 157 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an 158 // anonymous union. 159 // Child 0: __hash_node_base base class 160 // Child 1: __hash_ 161 // Child 2: anonymous union 162 // clang-format on 163 auto anon_union_sp = node_sp->GetChildAtIndex(2); 164 if (!anon_union_sp) 165 return nullptr; 166 167 value_sp = anon_union_sp->GetChildMemberWithName("__value_"); 168 if (!value_sp) 169 return nullptr; 170 } 171 } 172 m_elements_cache.push_back( 173 {value_sp.get(), hash_sp->GetValueAsUnsigned(0)}); 174 m_next_element = node_sp->GetChildMemberWithName("__next_").get(); 175 if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0) 176 m_next_element = nullptr; 177 } 178 179 std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx]; 180 if (!val_hash.first) 181 return lldb::ValueObjectSP(); 182 StreamString stream; 183 stream.Printf("[%" PRIu64 "]", (uint64_t)idx); 184 DataExtractor data; 185 Status error; 186 val_hash.first->GetData(data, error); 187 if (error.Fail()) 188 return lldb::ValueObjectSP(); 189 const bool thread_and_frame_only_if_stopped = true; 190 ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock( 191 thread_and_frame_only_if_stopped); 192 return CreateValueObjectFromData(stream.GetString(), data, exe_ctx, 193 m_element_type); 194 } 195 196 lldb::ChildCacheState 197 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { 198 m_num_elements = 0; 199 m_next_element = nullptr; 200 m_elements_cache.clear(); 201 ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_"); 202 if (!table_sp) 203 return lldb::ChildCacheState::eRefetch; 204 205 ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_"); 206 if (!p2_sp) 207 return lldb::ChildCacheState::eRefetch; 208 209 ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); 210 if (!num_elements_sp) 211 return lldb::ChildCacheState::eRefetch; 212 213 ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_"); 214 if (!p1_sp) 215 return lldb::ChildCacheState::eRefetch; 216 217 ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); 218 if (!value_sp) 219 return lldb::ChildCacheState::eRefetch; 220 221 m_tree = value_sp->GetChildMemberWithName("__next_").get(); 222 if (m_tree == nullptr) 223 return lldb::ChildCacheState::eRefetch; 224 225 m_num_elements = num_elements_sp->GetValueAsUnsigned(0); 226 227 if (m_num_elements > 0) 228 m_next_element = m_tree; 229 230 return lldb::ChildCacheState::eRefetch; 231 } 232 233 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 234 MightHaveChildren() { 235 return true; 236 } 237 238 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 239 GetIndexOfChildWithName(ConstString name) { 240 return ExtractIndexFromString(name.GetCString()); 241 } 242 243 SyntheticChildrenFrontEnd * 244 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( 245 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 246 return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) 247 : nullptr); 248 } 249