LLDB mainline
LibCxxUnorderedMap.cpp
Go to the documentation of this file.
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
13#include "lldb/Target/Target.h"
16#include "lldb/Utility/Endian.h"
17#include "lldb/Utility/Status.h"
18#include "lldb/Utility/Stream.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Support/Error.h"
23
24using namespace lldb;
25using namespace lldb_private;
26using namespace lldb_private::formatters;
27
28namespace lldb_private {
29namespace formatters {
32public:
34
36
37 llvm::Expected<uint32_t> CalculateNumChildren() override;
38
39 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
40
42
43 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
44
45private:
48 llvm::Expected<size_t> CalculateNumChildrenImpl(ValueObject &table);
49
52 ValueObject *m_tree = nullptr;
53 size_t m_num_elements = 0;
55 std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
56};
57
60public:
62
64
65 llvm::Expected<uint32_t> CalculateNumChildren() override;
66
67 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
68
70
71 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
72
73private:
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
89
94
95static bool isUnorderedMap(ConstString type_name) {
96 return isStdTemplate(type_name, "unordered_map") ||
97 isStdTemplate(type_name, "unordered_multimap");
98}
99
101 GetElementType(CompilerType table_type) {
102 auto element_type =
103 table_type.GetDirectNestedTypeWithName("value_type").GetTypedefedType();
104
105 // In newer unordered_map layouts, the std::pair element type isn't wrapped
106 // in any helper types. So return it directly.
107 if (isStdTemplate(element_type.GetTypeName(), "pair"))
108 return element_type;
109
110 // This synthetic provider is used for both unordered_(multi)map and
111 // unordered_(multi)set. For older unordered_map layouts, the element type has
112 // an additional type layer, an internal struct (`__hash_value_type`) that
113 // wraps a std::pair. Peel away the internal wrapper type - whose structure is
114 // of no value to users, to expose the std::pair. This matches the structure
115 // returned by the std::map synthetic provider.
116 CompilerType backend_type = m_backend.GetCompilerType();
117 if (backend_type.IsPointerOrReferenceType())
118 backend_type = backend_type.GetPointeeType();
119
120 if (isUnorderedMap(backend_type.GetCanonicalType().GetTypeName())) {
121 std::string name;
122 CompilerType field_type =
123 element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
124 CompilerType actual_type = field_type.GetTypedefedType();
125 if (isStdTemplate(actual_type.GetTypeName(), "pair"))
126 return actual_type;
127 }
128
129 return element_type;
130}
131
133 GetNodeType() {
134 auto table_sp = m_backend.GetChildMemberWithName("__table_");
135 if (!table_sp)
136 return {};
137
138 auto [node_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
139 *table_sp, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
140 if (is_compressed_pair)
141 node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
142
143 if (!node_sp)
144 return {};
145
146 return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType();
147}
148
152 return lldb::ValueObjectSP();
153 if (m_tree == nullptr)
154 return lldb::ValueObjectSP();
155
156 while (idx >= m_elements_cache.size()) {
157 if (m_next_element == nullptr)
158 return lldb::ValueObjectSP();
159
161 ValueObjectSP node_sp = m_next_element->Dereference(error);
162 if (!node_sp || error.Fail())
163 return lldb::ValueObjectSP();
164
165 ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_");
166 ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_");
167 if (!hash_sp || !value_sp) {
168 node_sp = m_next_element->Cast(m_node_type.GetPointerType())
169 ->Dereference(error);
170 if (!node_sp || error.Fail())
171 return nullptr;
172
173 hash_sp = node_sp->GetChildMemberWithName("__hash_");
174 if (!hash_sp)
175 return nullptr;
176
177 value_sp = node_sp->GetChildMemberWithName("__value_");
178 if (!value_sp) {
179 // clang-format off
180 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
181 // anonymous union.
182 // Child 0: __hash_node_base base class
183 // Child 1: __hash_
184 // Child 2: anonymous union
185 // clang-format on
186 auto anon_union_sp = node_sp->GetChildAtIndex(2);
187 if (!anon_union_sp)
188 return nullptr;
189
190 value_sp = anon_union_sp->GetChildMemberWithName("__value_");
191 if (!value_sp)
192 return nullptr;
193 }
194 }
195 m_elements_cache.push_back(
196 {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
197 m_next_element = node_sp->GetChildMemberWithName("__next_").get();
198 if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
199 m_next_element = nullptr;
200 }
201
202 std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
203 if (!val_hash.first)
204 return lldb::ValueObjectSP();
205 StreamString stream;
206 stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
207 DataExtractor data;
209 val_hash.first->GetData(data, error);
210 if (error.Fail())
211 return lldb::ValueObjectSP();
212 const bool thread_and_frame_only_if_stopped = true;
213 ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
214 thread_and_frame_only_if_stopped);
215 return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
217}
218
219llvm::Expected<size_t>
222 auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
223 table, /*anon_struct_idx=*/2, "__size_", "__p2_");
224 if (!is_compressed_pair && size_sp)
225 return size_sp->GetValueAsUnsigned(0);
226
227 if (!is_compressed_pair)
228 return llvm::createStringError("Unsupported std::unordered_map layout.");
229
230 ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp);
231
232 if (!num_elements_sp)
233 return llvm::createStringError(
234 "Unexpected std::unordered_map layout: failed to retrieve first member "
235 "in old __compressed_pair layout.");
236
237 return num_elements_sp->GetValueAsUnsigned(0);
238}
239
241 auto [tree_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
242 table, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
243 if (is_compressed_pair)
244 tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp);
245
246 if (!tree_sp)
247 return nullptr;
248
249 return tree_sp->GetChildMemberWithName("__next_");
250}
251
254 m_num_elements = 0;
255 m_next_element = nullptr;
256 m_elements_cache.clear();
257 ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_");
258 if (!table_sp)
260
262 if (!m_node_type)
264
265 m_element_type = GetElementType(table_sp->GetCompilerType());
266 if (!m_element_type)
268
269 ValueObjectSP tree_sp = GetTreePointer(*table_sp);
270 if (!tree_sp)
272
273 m_tree = tree_sp.get();
274
275 if (auto num_elems_or_err = CalculateNumChildrenImpl(*table_sp))
276 m_num_elements = *num_elems_or_err;
277 else {
279 num_elems_or_err.takeError(), "{0}");
281 }
282
283 if (m_num_elements > 0)
285
287}
288
289llvm::Expected<size_t>
292 auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
293 if (!optional_idx) {
294 return llvm::createStringError("Type has no child named '%s'",
295 name.AsCString());
296 }
297 return *optional_idx;
298}
299
306
313
316 m_pair_sp.reset();
317
318 ValueObjectSP valobj_sp = m_backend.GetSP();
319 if (!valobj_sp)
321
322 TargetSP target_sp(valobj_sp->GetTargetSP());
323
324 if (!target_sp)
326
327 // Get the unordered_map::iterator
328 // m_backend is an 'unordered_map::iterator', aka a
329 // '__hash_map_iterator<__hash_table::iterator>'
330 //
331 // __hash_map_iterator::__i_ is a __hash_table::iterator (aka
332 // __hash_iterator<__node_pointer>)
333 auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
334 if (!hash_iter_sp)
336
337 // Type is '__hash_iterator<__node_pointer>'
338 auto hash_iter_type = hash_iter_sp->GetCompilerType();
339 if (!hash_iter_type.IsValid())
341
342 // Type is '__node_pointer'
343 auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0);
344 if (!node_pointer_type.IsValid())
346
347 // Cast the __hash_iterator to a __node_pointer (which stores our key/value
348 // pair)
349 auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type);
350 if (!hash_node_sp)
352
353 auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_");
354 if (!key_value_sp) {
355 // clang-format off
356 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
357 // anonymous union.
358 // Child 0: __hash_node_base base class
359 // Child 1: __hash_
360 // Child 2: anonymous union
361 // clang-format on
362 auto anon_union_sp = hash_node_sp->GetChildAtIndex(2);
363 if (!anon_union_sp)
365
366 key_value_sp = anon_union_sp->GetChildMemberWithName("__value_");
367 if (!key_value_sp)
369 }
370
371 // Create the synthetic child, which is a pair where the key and value can be
372 // retrieved by querying the synthetic frontend for
373 // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
374 // respectively.
375 //
376 // std::unordered_map stores the actual key/value pair in
377 // __hash_value_type::__cc_ (or previously __cc).
378 auto potential_child_sp = key_value_sp->Clone(ConstString("pair"));
379 if (potential_child_sp)
380 if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1)
381 if (auto child0_sp = potential_child_sp->GetChildAtIndex(0);
382 child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")
383 potential_child_sp = child0_sp->Clone(ConstString("pair"));
384
385 m_pair_sp = potential_child_sp;
386
388}
389
394
401
402llvm::Expected<size_t>
405 if (name == "first")
406 return 0;
407 if (name == "second")
408 return 1;
409 return llvm::createStringError("Type has no child named '%s'",
410 name.AsCString());
411}
412
static llvm::raw_ostream & error(Stream &strm)
static ValueObjectSP GetTreePointer(ValueObject &table)
static bool isUnorderedMap(ConstString type_name)
#define LLDB_LOG_ERRORV(log, error,...)
Definition Log.h:408
Generic representation of a type in a programming language.
CompilerType GetFieldAtIndex(size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) const
CompilerType GetDirectNestedTypeWithName(llvm::StringRef name) const
ConstString GetTypeName(bool BaseOnly=false) const
CompilerType GetTypedefedType() const
If the current object represents a typedef type, get the underlying type.
CompilerType GetPointeeType() const
If this type is a pointer type, return the type that the pointer points to, else return an invalid ty...
CompilerType GetCanonicalType() const
bool IsPointerOrReferenceType(CompilerType *pointee_type=nullptr) const
A uniqued constant string class.
Definition ConstString.h:40
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
const char * GetCString() const
Get the string value as a C string.
An data extractor class.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
An error handling class.
Definition Status.h:118
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
uint32_t CalculateNumChildrenIgnoringErrors(uint32_t max=UINT32_MAX)
lldb::ValueObjectSP CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type)
SyntheticChildrenFrontEnd(ValueObject &backend)
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ValueObjectSP m_pair_sp
ValueObject for the key/value pair that the iterator currently points to.
std::vector< std::pair< ValueObject *, uint64_t > > m_elements_cache
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
llvm::Expected< size_t > CalculateNumChildrenImpl(ValueObject &table)
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
SyntheticChildrenFrontEnd * LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
std::optional< size_t > ExtractIndexFromString(const char *item_name)
bool isStdTemplate(ConstString type_name, llvm::StringRef type)
Definition LibCxx.cpp:54
lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair)
Definition LibCxx.cpp:75
std::pair< lldb::ValueObjectSP, bool > GetValueOrOldCompressedPair(ValueObject &obj, size_t anon_struct_idx, llvm::StringRef child_name, llvm::StringRef compressed_pair_name)
Returns the ValueObjectSP of the child of obj.
Definition LibCxx.cpp:106
SyntheticChildrenFrontEnd * LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
ChildCacheState
Specifies if children need to be re-computed after a call to SyntheticChildrenFrontEnd::Update.
@ eRefetch
Children need to be recomputed dynamically.
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
std::shared_ptr< lldb_private::Target > TargetSP