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
43private:
46 llvm::Expected<size_t> CalculateNumChildrenImpl(ValueObject &table);
47
50 ValueObject *m_tree = nullptr;
51 size_t m_num_elements = 0;
53 std::vector<ValueObject *> m_elements_cache;
54};
55
58public:
60
62
63 llvm::Expected<uint32_t> CalculateNumChildren() override;
64
65 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
66
68
69 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
70
71private:
72 lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair
73 ///< that the iterator currently points
74 ///< to.
75};
76
77} // namespace formatters
78} // namespace lldb_private
79
87
92
93static bool isUnorderedMap(ConstString type_name) {
94 return isStdTemplate(type_name, "unordered_map") ||
95 isStdTemplate(type_name, "unordered_multimap");
96}
97
99 GetElementType(CompilerType table_type) {
100 auto element_type =
101 table_type.GetDirectNestedTypeWithName("value_type").GetTypedefedType();
102
103 // In newer unordered_map layouts, the std::pair element type isn't wrapped
104 // in any helper types. So return it directly.
105 if (isStdTemplate(element_type.GetTypeName(), "pair"))
106 return element_type;
107
108 // This synthetic provider is used for both unordered_(multi)map and
109 // unordered_(multi)set. For older unordered_map layouts, the element type has
110 // an additional type layer, an internal struct (`__hash_value_type`) that
111 // wraps a std::pair. Peel away the internal wrapper type - whose structure is
112 // of no value to users, to expose the std::pair. This matches the structure
113 // returned by the std::map synthetic provider.
114 CompilerType backend_type = m_backend.GetCompilerType();
115 if (backend_type.IsPointerOrReferenceType())
116 backend_type = backend_type.GetPointeeType();
117
118 if (isUnorderedMap(backend_type.GetCanonicalType().GetTypeName())) {
119 std::string name;
120 CompilerType field_type =
121 element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
122 CompilerType actual_type = field_type.GetTypedefedType();
123 if (isStdTemplate(actual_type.GetTypeName(), "pair"))
124 return actual_type;
125 }
126
127 return element_type;
128}
129
131 GetNodeType() {
132 auto table_sp = m_backend.GetChildMemberWithName("__table_");
133 if (!table_sp)
134 return {};
135
136 auto [node_sp, is_compressed_pair] =
137 GetValueOrOldCompressedPair(*table_sp, "__first_node_", "__p1_");
138 if (is_compressed_pair)
139 node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
140
141 if (!node_sp)
142 return {};
143
144 return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType();
145}
146
150 return lldb::ValueObjectSP();
151 if (m_tree == nullptr)
152 return lldb::ValueObjectSP();
153
154 while (idx >= m_elements_cache.size()) {
155 if (m_next_element == nullptr)
156 return lldb::ValueObjectSP();
157
159 ValueObjectSP node_sp = m_next_element->Dereference(error);
160 if (!node_sp || error.Fail())
161 return lldb::ValueObjectSP();
162
163 ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_");
164 ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_");
165 if (!hash_sp || !value_sp) {
166 node_sp = m_next_element->Cast(m_node_type.GetPointerType())
167 ->Dereference(error);
168 if (!node_sp || error.Fail())
169 return nullptr;
170
171 hash_sp = node_sp->GetChildMemberWithName("__hash_");
172 if (!hash_sp)
173 return nullptr;
174
175 value_sp = node_sp->GetChildMemberWithName("__value_");
176 if (!value_sp) {
177 // clang-format off
178 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
179 // anonymous union.
180 // Child 0: __hash_node_base base class
181 // Child 1: __hash_
182 // Child 2: anonymous union
183 // clang-format on
184 auto anon_union_sp = node_sp->GetChildAtIndex(2);
185 if (!anon_union_sp)
186 return nullptr;
187
188 value_sp = anon_union_sp->GetChildMemberWithName("__value_");
189 if (!value_sp)
190 return nullptr;
191 }
192 }
193 m_elements_cache.push_back(value_sp.get());
194 m_next_element = node_sp->GetChildMemberWithName("__next_").get();
195 if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
196 m_next_element = nullptr;
197 }
198
199 ValueObject *val_hash = m_elements_cache[idx];
200 if (!val_hash)
201 return lldb::ValueObjectSP();
202 StreamString stream;
203 stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
204 DataExtractor data;
206 val_hash->GetData(data, error);
207 if (error.Fail())
208 return lldb::ValueObjectSP();
209 const bool thread_and_frame_only_if_stopped = true;
210 ExecutionContext exe_ctx =
211 val_hash->GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped);
212 return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
214}
215
216llvm::Expected<size_t>
219 auto [size_sp, is_compressed_pair] =
220 GetValueOrOldCompressedPair(table, "__size_", "__p2_");
221 if (!is_compressed_pair && size_sp)
222 return size_sp->GetValueAsUnsigned(0);
223
224 if (!is_compressed_pair)
225 return llvm::createStringError("Unsupported std::unordered_map layout.");
226
227 ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp);
228
229 if (!num_elements_sp)
230 return llvm::createStringError(
231 "Unexpected std::unordered_map layout: failed to retrieve first member "
232 "in old __compressed_pair layout.");
233
234 return num_elements_sp->GetValueAsUnsigned(0);
235}
236
238 auto [tree_sp, is_compressed_pair] =
239 GetValueOrOldCompressedPair(table, "__first_node_", "__p1_");
240 if (is_compressed_pair)
241 tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp);
242
243 if (!tree_sp)
244 return nullptr;
245
246 return tree_sp->GetChildMemberWithName("__next_");
247}
248
251 m_num_elements = 0;
252 m_next_element = nullptr;
253 m_elements_cache.clear();
254 ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_");
255 if (!table_sp)
257
259 if (!m_node_type)
261
262 m_element_type = GetElementType(table_sp->GetCompilerType());
263 if (!m_element_type)
265
266 ValueObjectSP tree_sp = GetTreePointer(*table_sp);
267 if (!tree_sp)
269
270 m_tree = tree_sp.get();
271
272 if (auto num_elems_or_err = CalculateNumChildrenImpl(*table_sp))
273 m_num_elements = *num_elems_or_err;
274 else {
276 num_elems_or_err.takeError(), "{0}");
278 }
279
280 if (m_num_elements > 0)
282
284}
285
289 return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
290 : nullptr);
291}
292
299
302 m_pair_sp.reset();
303
304 ValueObjectSP valobj_sp = m_backend.GetSP();
305 if (!valobj_sp)
307
308 TargetSP target_sp(valobj_sp->GetTargetSP());
309
310 if (!target_sp)
312
313 // Get the unordered_map::iterator
314 // m_backend is an 'unordered_map::iterator', aka a
315 // '__hash_map_iterator<__hash_table::iterator>'
316 //
317 // __hash_map_iterator::__i_ is a __hash_table::iterator (aka
318 // __hash_iterator<__node_pointer>)
319 auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
320 if (!hash_iter_sp)
322
323 // Type is '__hash_iterator<__node_pointer>'
324 auto hash_iter_type = hash_iter_sp->GetCompilerType();
325 if (!hash_iter_type.IsValid())
327
328 // Type is '__node_pointer'
329 auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0);
330 if (!node_pointer_type.IsValid())
332
333 // Cast the __hash_iterator to a __node_pointer (which stores our key/value
334 // pair)
335 auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type);
336 if (!hash_node_sp)
338
339 auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_");
340 if (!key_value_sp) {
341 // clang-format off
342 // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an
343 // anonymous union.
344 // Child 0: __hash_node_base base class
345 // Child 1: __hash_
346 // Child 2: anonymous union
347 // clang-format on
348 auto anon_union_sp = hash_node_sp->GetChildAtIndex(2);
349 if (!anon_union_sp)
351
352 key_value_sp = anon_union_sp->GetChildMemberWithName("__value_");
353 if (!key_value_sp)
355 }
356
357 // Create the synthetic child, which is a pair where the key and value can be
358 // retrieved by querying the synthetic frontend for
359 // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
360 // respectively.
361 //
362 // std::unordered_map stores the actual key/value pair in
363 // __hash_value_type::__cc_ (or previously __cc).
364 auto potential_child_sp = key_value_sp->Clone(ConstString("pair"));
365 if (potential_child_sp)
366 if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1)
367 if (auto child0_sp = potential_child_sp->GetChildAtIndex(0);
368 child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")
369 potential_child_sp = child0_sp->Clone(ConstString("pair"));
370
371 m_pair_sp = potential_child_sp;
372
374}
375
380
387
388llvm::Expected<size_t>
391 if (name == "first")
392 return 0;
393 if (name == "second")
394 return 1;
395 return llvm::createStringError("Type has no child named '%s'",
396 name.AsCString());
397}
398
402 return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp)
403 : nullptr);
404}
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.
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
Determine the index of a named child.
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)
SyntheticChildrenFrontEnd * LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
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, 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