LLDB mainline
LibCxxMap.cpp
Go to the documentation of this file.
1//===-- LibCxxMap.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
12
14#include "lldb/Target/Target.h"
16#include "lldb/Utility/Endian.h"
17#include "lldb/Utility/Status.h"
18#include "lldb/Utility/Stream.h"
22#include "lldb/lldb-forward.h"
23#include "llvm/Support/ErrorExtras.h"
24#include <cstdint>
25#include <locale>
26#include <optional>
27
28using namespace lldb;
29using namespace lldb_private;
30using namespace lldb_private::formatters;
31
32// The flattened layout of the std::__tree_iterator::__ptr_ looks
33// as follows:
34//
35// The following shows the contiguous block of memory:
36//
37// +-----------------------------+ class __tree_end_node
38// __ptr_ | pointer __left_; |
39// +-----------------------------+ class __tree_node_base
40// | pointer __right_; |
41// | __parent_pointer __parent_; |
42// | bool __is_black_; |
43// +-----------------------------+ class __tree_node
44// | __node_value_type __value_; | <<< our key/value pair
45// +-----------------------------+
46//
47// where __ptr_ has type __iter_pointer.
48
49class MapEntry {
50public:
51 MapEntry() = default;
52 explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
53 explicit MapEntry(ValueObject *entry)
54 : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
55
57 if (!m_entry_sp)
58 return m_entry_sp;
59 return m_entry_sp->GetSyntheticChildAtOffset(
60 0, m_entry_sp->GetCompilerType(), true);
61 }
62
64 if (!m_entry_sp)
65 return m_entry_sp;
66 return m_entry_sp->GetSyntheticChildAtOffset(
67 m_entry_sp->GetProcessSP()->GetAddressByteSize(),
68 m_entry_sp->GetCompilerType(), true);
69 }
70
72 if (!m_entry_sp)
73 return m_entry_sp;
74 return m_entry_sp->GetSyntheticChildAtOffset(
75 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
76 m_entry_sp->GetCompilerType(), true);
77 }
78
79 uint64_t value() const {
80 if (!m_entry_sp)
81 return 0;
82 return m_entry_sp->GetValueAsUnsigned(0);
83 }
84
85 bool error() const {
86 if (!m_entry_sp)
87 return true;
88 return m_entry_sp->GetError().Fail();
89 }
90
91 bool null() const { return (value() == 0); }
92
93 ValueObjectSP GetEntry() const { return m_entry_sp; }
94
95 void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
96
97 bool operator==(const MapEntry &rhs) const {
98 return (rhs.m_entry_sp.get() == m_entry_sp.get());
99 }
100
101private:
103};
104
106public:
107 MapIterator(ValueObject *entry, size_t depth = 0)
108 : m_entry(entry), m_max_depth(depth), m_error(false) {}
109
110 MapIterator() = default;
111
112 ValueObjectSP value() { return m_entry.GetEntry(); }
113
114 ValueObjectSP advance(size_t count) {
115 ValueObjectSP fail;
116 if (m_error)
117 return fail;
118 size_t steps = 0;
119 while (count > 0) {
120 next();
121 count--, steps++;
122 if (m_error || m_entry.null() || (steps > m_max_depth))
123 return fail;
124 }
125 return m_entry.GetEntry();
126 }
127
128private:
129 /// Mimicks libc++'s __tree_next algorithm, which libc++ uses
130 /// in its __tree_iteartor::operator++.
131 void next() {
132 if (m_entry.null())
133 return;
134 MapEntry right(m_entry.right());
135 if (!right.null()) {
136 m_entry = tree_min(std::move(right));
137 return;
138 }
139 size_t steps = 0;
140 while (!is_left_child(m_entry)) {
141 if (m_entry.error()) {
142 m_error = true;
143 return;
144 }
145 m_entry.SetEntry(m_entry.parent());
146 steps++;
147 if (steps > m_max_depth) {
148 m_entry = MapEntry();
149 return;
150 }
151 }
152 m_entry = MapEntry(m_entry.parent());
153 }
154
155 /// Mimicks libc++'s __tree_min algorithm.
157 if (x.null())
158 return MapEntry();
159 MapEntry left(x.left());
160 size_t steps = 0;
161 while (!left.null()) {
162 if (left.error()) {
163 m_error = true;
164 return MapEntry();
165 }
166 x = left;
167 left.SetEntry(x.left());
168 steps++;
169 if (steps > m_max_depth)
170 return MapEntry();
171 }
172 return x;
173 }
174
175 bool is_left_child(const MapEntry &x) {
176 if (x.null())
177 return false;
178 MapEntry rhs(x.parent());
179 rhs.SetEntry(rhs.left());
180 return x.value() == rhs.value();
181 }
182
184 size_t m_max_depth = 0;
185 bool m_error = false;
186};
187
188namespace lldb_private {
189namespace formatters {
191public:
193
194 ~LibcxxStdMapSyntheticFrontEnd() override = default;
195
196 llvm::Expected<uint32_t> CalculateNumChildren() override;
197
198 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
199
200 lldb::ChildCacheState Update() override;
201
202private:
203 llvm::Expected<uint32_t>
205
206 /// Returns the ValueObject for the __tree_node type that
207 /// holds the key/value pair of the node at index \ref idx.
208 ///
209 /// \param[in] idx The child index that we're looking to get
210 /// the key/value pair for.
211 ///
212 /// \param[in] max_depth The maximum search depth after which
213 /// we stop trying to find the key/value
214 /// pair for.
215 ///
216 /// \returns On success, returns the ValueObjectSP corresponding
217 /// to the __tree_node's __value_ member (which holds
218 /// the key/value pair the formatter wants to display).
219 /// On failure, will return nullptr.
220 ValueObjectSP GetKeyValuePair(size_t idx, size_t max_depth);
221
222 ValueObject *m_tree = nullptr;
226 std::map<size_t, MapIterator> m_iterators;
227};
228
230public:
232
233 llvm::Expected<uint32_t> CalculateNumChildren() override;
234
235 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
236
237 lldb::ChildCacheState Update() override;
238
239 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
240
242
243private:
245};
246} // namespace formatters
247} // namespace lldb_private
248
255
256llvm::Expected<uint32_t>
259 auto node_sp = GetFirstValueOfLibCXXCompressedPair(pair);
260
261 if (!node_sp)
262 return 0;
263
264 m_count = node_sp->GetValueAsUnsigned(0);
265
266 return m_count;
267}
268
269llvm::Expected<uint32_t> lldb_private::formatters::
271 if (m_count != UINT32_MAX)
272 return m_count;
273
274 if (m_tree == nullptr)
275 return 0;
276
277 auto [size_sp, is_compressed_pair] =
278 GetValueOrOldCompressedPair(*m_tree, "__size_", "__pair3_");
279 if (!size_sp)
280 return llvm::createStringError("unexpected std::map layout");
281
282 if (is_compressed_pair)
284
285 m_count = size_sp->GetValueAsUnsigned(0);
286 return m_count;
287}
288
291 size_t idx, size_t max_depth) {
292 MapIterator iterator(m_root_node, max_depth);
293
294 size_t advance_by = idx;
295 if (idx > 0) {
296 // If we have already created the iterator for the previous
297 // index, we can start from there and advance by 1.
298 auto cached_iterator = m_iterators.find(idx - 1);
299 if (cached_iterator != m_iterators.end()) {
300 iterator = cached_iterator->second;
301 advance_by = 1;
302 }
303 }
304
305 ValueObjectSP iterated_sp(iterator.advance(advance_by));
306 if (!iterated_sp)
307 // this tree is garbage - stop
308 return nullptr;
309
310 if (!m_node_ptr_type.IsValid())
311 return nullptr;
312
313 // iterated_sp is a __iter_pointer at this point.
314 // We can cast it to a __node_pointer (which is what libc++ does).
315 auto value_type_sp = iterated_sp->Cast(m_node_ptr_type);
316 if (!value_type_sp)
317 return nullptr;
318
319 // Finally, get the key/value pair.
320 value_type_sp = value_type_sp->GetChildMemberWithName("__value_");
321 if (!value_type_sp)
322 return nullptr;
323
324 m_iterators[idx] = iterator;
325
326 return value_type_sp;
327}
328
331 uint32_t idx) {
332 static ConstString g_cc_("__cc_"), g_cc("__cc");
333 static ConstString g_nc("__nc");
334 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
335 if (idx >= num_children)
336 return nullptr;
337
338 if (m_tree == nullptr || m_root_node == nullptr)
339 return nullptr;
340
341 ValueObjectSP key_val_sp = GetKeyValuePair(idx, /*max_depth=*/num_children);
342 if (!key_val_sp) {
343 // this will stop all future searches until an Update() happens
344 m_tree = nullptr;
345 return nullptr;
346 }
347
348 // at this point we have a valid
349 // we need to copy current_sp into a new object otherwise we will end up with
350 // all items named __value_
351 StreamString name;
352 name.Printf("[%" PRIu64 "]", (uint64_t)idx);
353 auto potential_child_sp = key_val_sp->Clone(ConstString(name.GetString()));
354 if (potential_child_sp) {
355 switch (potential_child_sp->GetNumChildrenIgnoringErrors()) {
356 case 1: {
357 auto child0_sp = potential_child_sp->GetChildAtIndex(0);
358 if (child0_sp &&
359 (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc))
360 potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
361 break;
362 }
363 case 2: {
364 auto child0_sp = potential_child_sp->GetChildAtIndex(0);
365 auto child1_sp = potential_child_sp->GetChildAtIndex(1);
366 if (child0_sp &&
367 (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc) &&
368 child1_sp && child1_sp->GetName() == g_nc)
369 potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
370 break;
371 }
372 }
373 }
374 return potential_child_sp;
375}
376
380 m_tree = m_root_node = nullptr;
381 m_iterators.clear();
382 m_tree = m_backend.GetChildMemberWithName("__tree_").get();
383 if (!m_tree)
385
386 m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
388 m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer");
389
391}
392
396 return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr);
397}
398
405
408 m_pair_sp.reset();
409
410 ValueObjectSP valobj_sp = m_backend.GetSP();
411 if (!valobj_sp)
413
414 TargetSP target_sp(valobj_sp->GetTargetSP());
415 if (!target_sp)
417
418 // m_backend is a std::map::iterator
419 // ...which is a __map_iterator<__tree_iterator<..., __node_pointer, ...>>
420 //
421 // Then, __map_iterator::__i_ is a __tree_iterator
422 auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
423 if (!tree_iter_sp)
425
426 // Type is __tree_iterator::__node_pointer
427 // (We could alternatively also get this from the template argument)
428 auto node_pointer_type =
429 tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName(
430 "__node_pointer");
431 if (!node_pointer_type.IsValid())
433
434 // __ptr_ is a __tree_iterator::__iter_pointer
435 auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_");
436 if (!iter_pointer_sp)
438
439 // Cast the __iter_pointer to a __node_pointer (which stores our key/value
440 // pair)
441 auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type);
442 if (!node_pointer_sp)
444
445 auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_");
446 if (!key_value_sp)
448
449 // Create the synthetic child, which is a pair where the key and value can be
450 // retrieved by querying the synthetic frontend for
451 // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
452 // respectively.
453 //
454 // std::map stores the actual key/value pair in value_type::__cc_ (or
455 // previously __cc).
456 key_value_sp = key_value_sp->Clone(ConstString("pair"));
457 if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) {
458 auto child0_sp = key_value_sp->GetChildAtIndex(0);
459 if (child0_sp &&
460 (child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc"))
461 key_value_sp = child0_sp->Clone(ConstString("pair"));
462 }
463
464 m_pair_sp = key_value_sp;
465
467}
468
473
476 uint32_t idx) {
477 if (!m_pair_sp)
478 return nullptr;
479
480 return m_pair_sp->GetChildAtIndex(idx);
481}
482
483llvm::Expected<size_t>
486 if (!m_pair_sp)
487 return llvm::createStringErrorV("type has no child named '{0}'", name);
488
489 return m_pair_sp->GetIndexOfChildWithName(name);
490}
491
495 return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
496 : nullptr);
497}
ValueObjectSP right() const
Definition LibCxxMap.cpp:63
bool null() const
Definition LibCxxMap.cpp:91
ValueObjectSP left() const
Definition LibCxxMap.cpp:56
MapEntry(ValueObject *entry)
Definition LibCxxMap.cpp:53
ValueObjectSP parent() const
Definition LibCxxMap.cpp:71
void SetEntry(ValueObjectSP entry)
Definition LibCxxMap.cpp:95
MapEntry()=default
ValueObjectSP m_entry_sp
ValueObjectSP GetEntry() const
Definition LibCxxMap.cpp:93
uint64_t value() const
Definition LibCxxMap.cpp:79
bool operator==(const MapEntry &rhs) const
Definition LibCxxMap.cpp:97
MapEntry(ValueObjectSP entry_sp)
Definition LibCxxMap.cpp:52
bool error() const
Definition LibCxxMap.cpp:85
ValueObjectSP advance(size_t count)
MapIterator(ValueObject *entry, size_t depth=0)
ValueObjectSP value()
size_t m_max_depth
MapEntry m_entry
void next()
Mimicks libc++'s __tree_next algorithm, which libc++ uses in its __tree_iteartor::operator++.
MapIterator()=default
bool is_left_child(const MapEntry &x)
MapEntry tree_min(MapEntry x)
Mimicks libc++'s __tree_min algorithm.
Generic representation of a type in a programming language.
A uniqued constant string class.
Definition ConstString.h:40
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)
SyntheticChildrenFrontEnd(ValueObject &backend)
llvm::Expected< uint32_t > CalculateNumChildren() override
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
Determine the index of a named child.
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< uint32_t > CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair)
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< uint32_t > CalculateNumChildren() override
LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
ValueObjectSP GetKeyValuePair(size_t idx, size_t max_depth)
Returns the ValueObject for the __tree_node type that holds the key/value pair of the node at index i...
#define UINT32_MAX
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 * LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
SyntheticChildrenFrontEnd * LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
A class that represents a running process on the host machine.
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