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