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<ValueObject *> 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(value_sp.get());
196 m_next_element = node_sp->GetChildMemberWithName("__next_").get();
197 if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
198 m_next_element = nullptr;
199 }
200
201 ValueObject *val_hash = m_elements_cache[idx];
202 if (!val_hash)
203 return lldb::ValueObjectSP();
204 StreamString stream;
205 stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
206 DataExtractor data;
208 val_hash->GetData(data, error);
209 if (error.Fail())
210 return lldb::ValueObjectSP();
211 const bool thread_and_frame_only_if_stopped = true;
212 ExecutionContext exe_ctx =
213 val_hash->GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped);
214 return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
216}
217
218llvm::Expected<size_t>
221 auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
222 table, /*anon_struct_idx=*/2, "__size_", "__p2_");
223 if (!is_compressed_pair && size_sp)
224 return size_sp->GetValueAsUnsigned(0);
225
226 if (!is_compressed_pair)
227 return llvm::createStringError("Unsupported std::unordered_map layout.");
228
229 ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp);
230
231 if (!num_elements_sp)
232 return llvm::createStringError(
233 "Unexpected std::unordered_map layout: failed to retrieve first member "
234 "in old __compressed_pair layout.");
235
236 return num_elements_sp->GetValueAsUnsigned(0);
237}
238
240 auto [tree_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
241 table, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
242 if (is_compressed_pair)
243 tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp);
244
245 if (!tree_sp)
246 return nullptr;
247
248 return tree_sp->GetChildMemberWithName("__next_");
249}
250
253 m_num_elements = 0;
254 m_next_element = nullptr;
255 m_elements_cache.clear();
256 ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_");
257 if (!table_sp)
259
261 if (!m_node_type)
263
264 m_element_type = GetElementType(table_sp->GetCompilerType());
265 if (!m_element_type)
267
268 ValueObjectSP tree_sp = GetTreePointer(*table_sp);
269 if (!tree_sp)
271
272 m_tree = tree_sp.get();
273
274 if (auto num_elems_or_err = CalculateNumChildrenImpl(*table_sp))
275 m_num_elements = *num_elems_or_err;
276 else {
278 num_elems_or_err.takeError(), "{0}");
280 }
281
282 if (m_num_elements > 0)
284
286}
287
288llvm::Expected<size_t>
291 auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
292 if (!optional_idx) {
293 return llvm::createStringError("Type has no child named '%s'",
294 name.AsCString());
295 }
296 return *optional_idx;
297}
298
305
312
315 m_pair_sp.reset();
316
317 ValueObjectSP valobj_sp = m_backend.GetSP();
318 if (!valobj_sp)
320
321 TargetSP target_sp(valobj_sp->GetTargetSP());
322
323 if (!target_sp)
325
326 // Get the unordered_map::iterator
327 // m_backend is an 'unordered_map::iterator', aka a
328 // '__hash_map_iterator<__hash_table::iterator>'
329 //
330 // __hash_map_iterator::__i_ is a __hash_table::iterator (aka
331 // __hash_iterator<__node_pointer>)
332 auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
333 if (!hash_iter_sp)
335
336 // Type is '__hash_iterator<__node_pointer>'
337 auto hash_iter_type = hash_iter_sp->GetCompilerType();
338 if (!hash_iter_type.IsValid())
340
341 // Type is '__node_pointer'
342 auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0);
343 if (!node_pointer_type.IsValid())
345
346 // Cast the __hash_iterator to a __node_pointer (which stores our key/value
347 // pair)
348 auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type);
349 if (!hash_node_sp)
351
352 auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_");
353 if (!key_value_sp) {
354 // clang-format off
355 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
356 // anonymous union.
357 // Child 0: __hash_node_base base class
358 // Child 1: __hash_
359 // Child 2: anonymous union
360 // clang-format on
361 auto anon_union_sp = hash_node_sp->GetChildAtIndex(2);
362 if (!anon_union_sp)
364
365 key_value_sp = anon_union_sp->GetChildMemberWithName("__value_");
366 if (!key_value_sp)
368 }
369
370 // Create the synthetic child, which is a pair where the key and value can be
371 // retrieved by querying the synthetic frontend for
372 // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
373 // respectively.
374 //
375 // std::unordered_map stores the actual key/value pair in
376 // __hash_value_type::__cc_ (or previously __cc).
377 auto potential_child_sp = key_value_sp->Clone(ConstString("pair"));
378 if (potential_child_sp)
379 if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1)
380 if (auto child0_sp = potential_child_sp->GetChildAtIndex(0);
381 child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")
382 potential_child_sp = child0_sp->Clone(ConstString("pair"));
383
384 m_pair_sp = potential_child_sp;
385
387}
388
393
400
401llvm::Expected<size_t>
404 if (name == "first")
405 return 0;
406 if (name == "second")
407 return 1;
408 return llvm::createStringError("Type has no child named '%s'",
409 name.AsCString());
410}
411
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.
ExecutionContext Lock(bool thread_and_frame_only_if_stopped) const
Create an ExecutionContext object from this object.
"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)
virtual uint64_t GetData(DataExtractor &data, Status &error)
const ExecutionContextRef & GetExecutionContextRef() const
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.
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