| 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/DataFormatters/FormattersHelpers.h" |
| 13 | #include "lldb/Target/Target.h" |
| 14 | #include "lldb/Utility/ConstString.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/ValueObject/ValueObject.h" |
| 20 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
| 21 | #include "llvm/ADT/StringRef.h" |
| 22 | #include "llvm/Support/Error.h" |
| 23 | |
| 24 | using namespace lldb; |
| 25 | using namespace lldb_private; |
| 26 | using namespace lldb_private::formatters; |
| 27 | |
| 28 | namespace lldb_private { |
| 29 | namespace formatters { |
| 30 | class LibcxxStdUnorderedMapSyntheticFrontEnd |
| 31 | : public SyntheticChildrenFrontEnd { |
| 32 | public: |
| 33 | LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
| 34 | |
| 35 | ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default; |
| 36 | |
| 37 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
| 38 | |
| 39 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
| 40 | |
| 41 | lldb::ChildCacheState Update() override; |
| 42 | |
| 43 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; |
| 44 | |
| 45 | private: |
| 46 | CompilerType GetNodeType(); |
| 47 | CompilerType GetElementType(CompilerType table_type); |
| 48 | llvm::Expected<size_t> CalculateNumChildrenImpl(ValueObject &table); |
| 49 | |
| 50 | CompilerType m_element_type; |
| 51 | CompilerType m_node_type; |
| 52 | ValueObject *m_tree = nullptr; |
| 53 | size_t m_num_elements = 0; |
| 54 | ValueObject *m_next_element = nullptr; |
| 55 | std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; |
| 56 | }; |
| 57 | |
| 58 | class LibCxxUnorderedMapIteratorSyntheticFrontEnd |
| 59 | : public SyntheticChildrenFrontEnd { |
| 60 | public: |
| 61 | LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
| 62 | |
| 63 | ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default; |
| 64 | |
| 65 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
| 66 | |
| 67 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
| 68 | |
| 69 | lldb::ChildCacheState Update() override; |
| 70 | |
| 71 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; |
| 72 | |
| 73 | private: |
| 74 | lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair |
| 75 | ///< that the iterator currently points |
| 76 | ///< to. |
| 77 | }; |
| 78 | |
| 79 | } // namespace formatters |
| 80 | } // namespace lldb_private |
| 81 | |
| 82 | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
| 83 | LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
| 84 | : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), |
| 85 | m_elements_cache() { |
| 86 | if (valobj_sp) |
| 87 | Update(); |
| 88 | } |
| 89 | |
| 90 | llvm::Expected<uint32_t> lldb_private::formatters:: |
| 91 | LibcxxStdUnorderedMapSyntheticFrontEnd::CalculateNumChildren() { |
| 92 | return m_num_elements; |
| 93 | } |
| 94 | |
| 95 | static bool isUnorderedMap(ConstString type_name) { |
| 96 | return isStdTemplate(type_name, type: "unordered_map" ) || |
| 97 | isStdTemplate(type_name, type: "unordered_multimap" ); |
| 98 | } |
| 99 | |
| 100 | CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
| 101 | GetElementType(CompilerType table_type) { |
| 102 | auto element_type = table_type.GetTypedefedType().GetTypeTemplateArgument(idx: 0); |
| 103 | |
| 104 | // This synthetic provider is used for both unordered_(multi)map and |
| 105 | // unordered_(multi)set. For unordered_map, the element type has an |
| 106 | // additional type layer, an internal struct (`__hash_value_type`) |
| 107 | // that wraps a std::pair. Peel away the internal wrapper type - whose |
| 108 | // structure is of no value to users, to expose the std::pair. This |
| 109 | // matches the structure returned by the std::map synthetic provider. |
| 110 | if (isUnorderedMap( |
| 111 | type_name: m_backend.GetCompilerType().GetCanonicalType().GetTypeName())) { |
| 112 | std::string name; |
| 113 | CompilerType field_type = |
| 114 | element_type.GetFieldAtIndex(idx: 0, name, bit_offset_ptr: nullptr, bitfield_bit_size_ptr: nullptr, is_bitfield_ptr: nullptr); |
| 115 | CompilerType actual_type = field_type.GetTypedefedType(); |
| 116 | if (isStdTemplate(type_name: actual_type.GetTypeName(), type: "pair" )) |
| 117 | return actual_type; |
| 118 | } |
| 119 | |
| 120 | return element_type; |
| 121 | } |
| 122 | |
| 123 | CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
| 124 | GetNodeType() { |
| 125 | auto node_sp = m_backend.GetChildAtNamePath(names: {"__table_" , "__first_node_" }); |
| 126 | |
| 127 | if (!node_sp) { |
| 128 | auto p1_sp = m_backend.GetChildAtNamePath(names: {"__table_" , "__p1_" }); |
| 129 | if (!p1_sp) |
| 130 | return {}; |
| 131 | |
| 132 | if (!isOldCompressedPairLayout(pair_obj&: *p1_sp)) |
| 133 | return {}; |
| 134 | |
| 135 | node_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *p1_sp); |
| 136 | if (!node_sp) |
| 137 | return {}; |
| 138 | } |
| 139 | |
| 140 | assert(node_sp); |
| 141 | |
| 142 | return node_sp->GetCompilerType().GetTypeTemplateArgument(idx: 0).GetPointeeType(); |
| 143 | } |
| 144 | |
| 145 | lldb::ValueObjectSP lldb_private::formatters:: |
| 146 | LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { |
| 147 | if (idx >= CalculateNumChildrenIgnoringErrors()) |
| 148 | return lldb::ValueObjectSP(); |
| 149 | if (m_tree == nullptr) |
| 150 | return lldb::ValueObjectSP(); |
| 151 | |
| 152 | while (idx >= m_elements_cache.size()) { |
| 153 | if (m_next_element == nullptr) |
| 154 | return lldb::ValueObjectSP(); |
| 155 | |
| 156 | Status error; |
| 157 | ValueObjectSP node_sp = m_next_element->Dereference(error); |
| 158 | if (!node_sp || error.Fail()) |
| 159 | return lldb::ValueObjectSP(); |
| 160 | |
| 161 | ValueObjectSP value_sp = node_sp->GetChildMemberWithName(name: "__value_" ); |
| 162 | ValueObjectSP hash_sp = node_sp->GetChildMemberWithName(name: "__hash_" ); |
| 163 | if (!hash_sp || !value_sp) { |
| 164 | node_sp = m_next_element->Cast(compiler_type: m_node_type.GetPointerType()) |
| 165 | ->Dereference(error); |
| 166 | if (!node_sp || error.Fail()) |
| 167 | return nullptr; |
| 168 | |
| 169 | hash_sp = node_sp->GetChildMemberWithName(name: "__hash_" ); |
| 170 | if (!hash_sp) |
| 171 | return nullptr; |
| 172 | |
| 173 | value_sp = node_sp->GetChildMemberWithName(name: "__value_" ); |
| 174 | if (!value_sp) { |
| 175 | // clang-format off |
| 176 | // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an |
| 177 | // anonymous union. |
| 178 | // Child 0: __hash_node_base base class |
| 179 | // Child 1: __hash_ |
| 180 | // Child 2: anonymous union |
| 181 | // clang-format on |
| 182 | auto anon_union_sp = node_sp->GetChildAtIndex(idx: 2); |
| 183 | if (!anon_union_sp) |
| 184 | return nullptr; |
| 185 | |
| 186 | value_sp = anon_union_sp->GetChildMemberWithName(name: "__value_" ); |
| 187 | if (!value_sp) |
| 188 | return nullptr; |
| 189 | } |
| 190 | } |
| 191 | m_elements_cache.push_back( |
| 192 | x: {value_sp.get(), hash_sp->GetValueAsUnsigned(fail_value: 0)}); |
| 193 | m_next_element = node_sp->GetChildMemberWithName(name: "__next_" ).get(); |
| 194 | if (!m_next_element || m_next_element->GetValueAsUnsigned(fail_value: 0) == 0) |
| 195 | m_next_element = nullptr; |
| 196 | } |
| 197 | |
| 198 | std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx]; |
| 199 | if (!val_hash.first) |
| 200 | return lldb::ValueObjectSP(); |
| 201 | StreamString stream; |
| 202 | stream.Printf(format: "[%" PRIu64 "]" , (uint64_t)idx); |
| 203 | DataExtractor data; |
| 204 | Status error; |
| 205 | val_hash.first->GetData(data, error); |
| 206 | if (error.Fail()) |
| 207 | return lldb::ValueObjectSP(); |
| 208 | const bool thread_and_frame_only_if_stopped = true; |
| 209 | ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock( |
| 210 | thread_and_frame_only_if_stopped); |
| 211 | return CreateValueObjectFromData(name: stream.GetString(), data, exe_ctx, |
| 212 | type: m_element_type); |
| 213 | } |
| 214 | |
| 215 | llvm::Expected<size_t> |
| 216 | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
| 217 | CalculateNumChildrenImpl(ValueObject &table) { |
| 218 | if (auto size_sp = table.GetChildMemberWithName(name: "__size_" )) |
| 219 | return size_sp->GetValueAsUnsigned(fail_value: 0); |
| 220 | |
| 221 | ValueObjectSP p2_sp = table.GetChildMemberWithName(name: "__p2_" ); |
| 222 | if (!p2_sp) |
| 223 | return llvm::createStringError( |
| 224 | Fmt: "Unexpected std::unordered_map layout: __p2_ member not found." ); |
| 225 | |
| 226 | if (!isOldCompressedPairLayout(pair_obj&: *p2_sp)) |
| 227 | return llvm::createStringError(Fmt: "Unexpected std::unordered_map layout: old " |
| 228 | "__compressed_pair layout not found." ); |
| 229 | |
| 230 | ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *p2_sp); |
| 231 | |
| 232 | if (!num_elements_sp) |
| 233 | return llvm::createStringError( |
| 234 | Fmt: "Unexpected std::unordered_map layout: failed to retrieve first member " |
| 235 | "in old __compressed_pair layout." ); |
| 236 | |
| 237 | return num_elements_sp->GetValueAsUnsigned(fail_value: 0); |
| 238 | } |
| 239 | |
| 240 | static ValueObjectSP GetTreePointer(ValueObject &table) { |
| 241 | ValueObjectSP tree_sp = table.GetChildMemberWithName(name: "__first_node_" ); |
| 242 | if (!tree_sp) { |
| 243 | ValueObjectSP p1_sp = table.GetChildMemberWithName(name: "__p1_" ); |
| 244 | if (!p1_sp) |
| 245 | return nullptr; |
| 246 | |
| 247 | if (!isOldCompressedPairLayout(pair_obj&: *p1_sp)) |
| 248 | return nullptr; |
| 249 | |
| 250 | tree_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *p1_sp); |
| 251 | if (!tree_sp) |
| 252 | return nullptr; |
| 253 | } |
| 254 | |
| 255 | return tree_sp->GetChildMemberWithName(name: "__next_" ); |
| 256 | } |
| 257 | |
| 258 | lldb::ChildCacheState |
| 259 | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { |
| 260 | m_num_elements = 0; |
| 261 | m_next_element = nullptr; |
| 262 | m_elements_cache.clear(); |
| 263 | ValueObjectSP table_sp = m_backend.GetChildMemberWithName(name: "__table_" ); |
| 264 | if (!table_sp) |
| 265 | return lldb::ChildCacheState::eRefetch; |
| 266 | |
| 267 | m_node_type = GetNodeType(); |
| 268 | if (!m_node_type) |
| 269 | return lldb::ChildCacheState::eRefetch; |
| 270 | |
| 271 | m_element_type = GetElementType(table_type: table_sp->GetCompilerType()); |
| 272 | if (!m_element_type) |
| 273 | return lldb::ChildCacheState::eRefetch; |
| 274 | |
| 275 | ValueObjectSP tree_sp = GetTreePointer(table&: *table_sp); |
| 276 | if (!tree_sp) |
| 277 | return lldb::ChildCacheState::eRefetch; |
| 278 | |
| 279 | m_tree = tree_sp.get(); |
| 280 | |
| 281 | if (auto num_elems_or_err = CalculateNumChildrenImpl(table&: *table_sp)) |
| 282 | m_num_elements = *num_elems_or_err; |
| 283 | else { |
| 284 | LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), |
| 285 | num_elems_or_err.takeError(), "{0}" ); |
| 286 | return lldb::ChildCacheState::eRefetch; |
| 287 | } |
| 288 | |
| 289 | if (m_num_elements > 0) |
| 290 | m_next_element = m_tree; |
| 291 | |
| 292 | return lldb::ChildCacheState::eRefetch; |
| 293 | } |
| 294 | |
| 295 | llvm::Expected<size_t> |
| 296 | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: |
| 297 | GetIndexOfChildWithName(ConstString name) { |
| 298 | auto optional_idx = formatters::ExtractIndexFromString(item_name: name.GetCString()); |
| 299 | if (!optional_idx) { |
| 300 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
| 301 | Vals: name.AsCString()); |
| 302 | } |
| 303 | return *optional_idx; |
| 304 | } |
| 305 | |
| 306 | SyntheticChildrenFrontEnd * |
| 307 | lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( |
| 308 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
| 309 | return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) |
| 310 | : nullptr); |
| 311 | } |
| 312 | |
| 313 | lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: |
| 314 | LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
| 315 | : SyntheticChildrenFrontEnd(*valobj_sp) { |
| 316 | if (valobj_sp) |
| 317 | Update(); |
| 318 | } |
| 319 | |
| 320 | lldb::ChildCacheState lldb_private::formatters:: |
| 321 | LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() { |
| 322 | m_pair_sp.reset(); |
| 323 | |
| 324 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
| 325 | if (!valobj_sp) |
| 326 | return lldb::ChildCacheState::eRefetch; |
| 327 | |
| 328 | TargetSP target_sp(valobj_sp->GetTargetSP()); |
| 329 | |
| 330 | if (!target_sp) |
| 331 | return lldb::ChildCacheState::eRefetch; |
| 332 | |
| 333 | // Get the unordered_map::iterator |
| 334 | // m_backend is an 'unordered_map::iterator', aka a |
| 335 | // '__hash_map_iterator<__hash_table::iterator>' |
| 336 | // |
| 337 | // __hash_map_iterator::__i_ is a __hash_table::iterator (aka |
| 338 | // __hash_iterator<__node_pointer>) |
| 339 | auto hash_iter_sp = valobj_sp->GetChildMemberWithName(name: "__i_" ); |
| 340 | if (!hash_iter_sp) |
| 341 | return lldb::ChildCacheState::eRefetch; |
| 342 | |
| 343 | // Type is '__hash_iterator<__node_pointer>' |
| 344 | auto hash_iter_type = hash_iter_sp->GetCompilerType(); |
| 345 | if (!hash_iter_type.IsValid()) |
| 346 | return lldb::ChildCacheState::eRefetch; |
| 347 | |
| 348 | // Type is '__node_pointer' |
| 349 | auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(idx: 0); |
| 350 | if (!node_pointer_type.IsValid()) |
| 351 | return lldb::ChildCacheState::eRefetch; |
| 352 | |
| 353 | // Cast the __hash_iterator to a __node_pointer (which stores our key/value |
| 354 | // pair) |
| 355 | auto hash_node_sp = hash_iter_sp->Cast(compiler_type: node_pointer_type); |
| 356 | if (!hash_node_sp) |
| 357 | return lldb::ChildCacheState::eRefetch; |
| 358 | |
| 359 | auto key_value_sp = hash_node_sp->GetChildMemberWithName(name: "__value_" ); |
| 360 | if (!key_value_sp) { |
| 361 | // clang-format off |
| 362 | // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an |
| 363 | // anonymous union. |
| 364 | // Child 0: __hash_node_base base class |
| 365 | // Child 1: __hash_ |
| 366 | // Child 2: anonymous union |
| 367 | // clang-format on |
| 368 | auto anon_union_sp = hash_node_sp->GetChildAtIndex(idx: 2); |
| 369 | if (!anon_union_sp) |
| 370 | return lldb::ChildCacheState::eRefetch; |
| 371 | |
| 372 | key_value_sp = anon_union_sp->GetChildMemberWithName(name: "__value_" ); |
| 373 | if (!key_value_sp) |
| 374 | return lldb::ChildCacheState::eRefetch; |
| 375 | } |
| 376 | |
| 377 | // Create the synthetic child, which is a pair where the key and value can be |
| 378 | // retrieved by querying the synthetic frontend for |
| 379 | // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second") |
| 380 | // respectively. |
| 381 | // |
| 382 | // std::unordered_map stores the actual key/value pair in |
| 383 | // __hash_value_type::__cc_ (or previously __cc). |
| 384 | auto potential_child_sp = key_value_sp->Clone(new_name: ConstString("pair" )); |
| 385 | if (potential_child_sp) |
| 386 | if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1) |
| 387 | if (auto child0_sp = potential_child_sp->GetChildAtIndex(idx: 0); |
| 388 | child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc" ) |
| 389 | potential_child_sp = child0_sp->Clone(new_name: ConstString("pair" )); |
| 390 | |
| 391 | m_pair_sp = potential_child_sp; |
| 392 | |
| 393 | return lldb::ChildCacheState::eRefetch; |
| 394 | } |
| 395 | |
| 396 | llvm::Expected<uint32_t> lldb_private::formatters:: |
| 397 | LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() { |
| 398 | return 2; |
| 399 | } |
| 400 | |
| 401 | lldb::ValueObjectSP lldb_private::formatters:: |
| 402 | LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { |
| 403 | if (m_pair_sp) |
| 404 | return m_pair_sp->GetChildAtIndex(idx); |
| 405 | return lldb::ValueObjectSP(); |
| 406 | } |
| 407 | |
| 408 | llvm::Expected<size_t> |
| 409 | lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: |
| 410 | GetIndexOfChildWithName(ConstString name) { |
| 411 | if (name == "first" ) |
| 412 | return 0; |
| 413 | if (name == "second" ) |
| 414 | return 1; |
| 415 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
| 416 | Vals: name.AsCString()); |
| 417 | } |
| 418 | |
| 419 | SyntheticChildrenFrontEnd * |
| 420 | lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator( |
| 421 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
| 422 | return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp) |
| 423 | : nullptr); |
| 424 | } |
| 425 | |