LLDB mainline
MsvcStlTree.cpp
Go to the documentation of this file.
1//===-- MsvcStlTree.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 "MsvcStl.h"
10
12#include "lldb/Utility/Status.h"
15#include "lldb/lldb-forward.h"
16#include <cstdint>
17#include <optional>
18
19using namespace lldb;
20using namespace lldb_private;
21using namespace lldb_private::formatters;
22
23// A Node looks as follows:
24// struct _Tree_node {
25// _Tree_node *_Left;
26// _Tree_node *_Parent;
27// _Tree_node *_Right;
28// char _Color;
29// char _Isnil; // true (!= 0) if head or nil node
30// value_type _Myval;
31// };
32
33namespace {
34
35class MapEntry {
36public:
37 MapEntry() = default;
38 explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
39 explicit MapEntry(ValueObject *entry)
40 : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
41
42 ValueObjectSP left() const {
43 if (!m_entry_sp)
44 return m_entry_sp;
45 return m_entry_sp->GetSyntheticChildAtOffset(
46 0, m_entry_sp->GetCompilerType(), true);
47 }
48
49 ValueObjectSP right() const {
50 if (!m_entry_sp)
51 return m_entry_sp;
52 return m_entry_sp->GetSyntheticChildAtOffset(
53 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
54 m_entry_sp->GetCompilerType(), true);
55 }
56
57 ValueObjectSP parent() const {
58 if (!m_entry_sp)
59 return m_entry_sp;
60 return m_entry_sp->GetSyntheticChildAtOffset(
61 m_entry_sp->GetProcessSP()->GetAddressByteSize(),
62 m_entry_sp->GetCompilerType(), true);
63 }
64
65 uint64_t value() const {
66 if (!m_entry_sp)
67 return 0;
68 return m_entry_sp->GetValueAsUnsigned(0);
69 }
70
71 bool is_nil() const {
72 if (!m_entry_sp)
73 return true;
74 auto isnil_sp = m_entry_sp->GetChildMemberWithName("_Isnil");
75 if (!isnil_sp)
76 return true;
77 return isnil_sp->GetValueAsUnsigned(1) != 0;
78 }
79
80 bool error() const {
81 if (!m_entry_sp)
82 return true;
83 return m_entry_sp->GetError().Fail();
84 }
85
86 bool is_nullptr() const { return (value() == 0); }
87
88 ValueObjectSP GetEntry() const { return m_entry_sp; }
89
90 void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
91
92 bool operator==(const MapEntry &rhs) const {
93 return (rhs.m_entry_sp.get() == m_entry_sp.get());
94 }
95
96private:
98};
99
100class MapIterator {
101public:
102 MapIterator(ValueObject *entry, size_t depth = 0)
103 : m_entry(entry), m_max_depth(depth) {}
104
105 MapIterator() = default;
106
107 ValueObjectSP value() { return m_entry.GetEntry(); }
108
109 ValueObjectSP advance(size_t count) {
110 ValueObjectSP fail;
111 if (m_error)
112 return fail;
113 size_t steps = 0;
114 while (count > 0) {
115 next();
116 count--, steps++;
117 if (m_error || m_entry.is_nullptr() || (steps > m_max_depth))
118 return fail;
119 }
120 return m_entry.GetEntry();
121 }
122
123private:
124 /// Mimicks _Tree_unchecked_const_iterator::operator++()
125 void next() {
126 if (m_entry.is_nullptr())
127 return;
128 MapEntry right(m_entry.right());
129 if (!right.is_nil()) {
130 m_entry = tree_min(std::move(right));
131 return;
132 }
133 size_t steps = 0;
134 MapEntry pnode(m_entry.parent());
135 while (!pnode.is_nil() &&
136 m_entry.value() == MapEntry(pnode.right()).value()) {
137 m_entry = pnode;
138 steps++;
139 if (steps > m_max_depth) {
140 m_entry = MapEntry();
141 return;
142 }
143 pnode.SetEntry(m_entry.parent());
144 }
145 m_entry = std::move(pnode);
146 }
147
148 /// Mimicks MSVC STL's _Min() algorithm (finding the leftmost node in the
149 /// subtree).
150 MapEntry tree_min(MapEntry pnode) {
151 if (pnode.is_nullptr())
152 return MapEntry();
153 MapEntry left(pnode.left());
154 size_t steps = 0;
155 while (!left.is_nil()) {
156 if (left.error()) {
157 m_error = true;
158 return MapEntry();
159 }
160 pnode = left;
161 left.SetEntry(pnode.left());
162 steps++;
163 if (steps > m_max_depth)
164 return MapEntry();
165 }
166 return pnode;
167 }
168
169 MapEntry m_entry;
170 size_t m_max_depth = 0;
171 bool m_error = false;
172};
173
174} // namespace
175
176namespace lldb_private {
177namespace formatters {
179public:
181
182 ~MsvcStlTreeSyntheticFrontEnd() override = default;
183
184 llvm::Expected<uint32_t> CalculateNumChildren() override;
185
186 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
187
188 lldb::ChildCacheState Update() override;
189
190private:
191 /// Returns the ValueObject for the _Tree_node at index \ref idx.
192 ///
193 /// \param[in] idx The child index that we're looking to get the value for.
194 ///
195 /// \param[in] max_depth The maximum search depth after which we stop trying
196 /// to find the node for.
197 ///
198 /// \returns On success, returns the ValueObjectSP corresponding to the
199 /// _Tree_node's _Myval member.
200 /// On failure, nullptr is returned.
201 ValueObjectSP GetValueAt(size_t idx, size_t max_depth);
202
203 ValueObject *m_tree = nullptr;
206 std::map<size_t, MapIterator> m_iterators;
207};
208
210public:
213
214 llvm::Expected<uint32_t> CalculateNumChildren() override {
215 if (!m_inner_sp)
216 return 0;
217 return m_inner_sp->GetNumChildren();
218 }
219
220 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
221 if (!m_inner_sp)
222 return nullptr;
223 return m_inner_sp->GetChildAtIndex(idx);
224 }
225
226 lldb::ChildCacheState Update() override;
227
228 llvm::Expected<size_t>
229 GetIndexOfChildWithName(llvm::StringRef name) override {
230 if (!m_inner_sp)
231 return llvm::createStringError("there are no children");
232 return m_inner_sp->GetIndexOfChildWithName(name);
233 }
234
236
237private:
239};
240
241} // namespace formatters
242} // namespace lldb_private
243
250
251llvm::Expected<uint32_t>
253 if (m_count != UINT32_MAX)
254 return m_count;
255
256 if (m_tree == nullptr)
257 return 0;
258
259 if (auto node_sp = m_tree->GetChildMemberWithName("_Mysize")) {
260 m_count = node_sp->GetValueAsUnsigned(0);
261 return m_count;
262 }
263
264 return llvm::createStringError("failed to read size");
265}
266
269 size_t idx, size_t max_depth) {
270 MapIterator iterator(m_begin_node, max_depth);
271
272 size_t advance_by = idx;
273 if (idx > 0) {
274 // If we have already created the iterator for the previous
275 // index, we can start from there and advance by 1.
276 auto cached_iterator = m_iterators.find(idx - 1);
277 if (cached_iterator != m_iterators.end()) {
278 iterator = cached_iterator->second;
279 advance_by = 1;
280 }
281 }
282
283 ValueObjectSP iterated_sp(iterator.advance(advance_by));
284 if (!iterated_sp)
285 // this tree is garbage - stop
286 return nullptr;
287
288 ValueObjectSP value_sp = iterated_sp->GetChildMemberWithName("_Myval");
289 if (!value_sp)
290 return nullptr;
291
292 m_iterators[idx] = iterator;
293
294 return value_sp;
295}
296
299 uint32_t idx) {
300 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
301 if (idx >= num_children)
302 return nullptr;
303
304 if (m_tree == nullptr || m_begin_node == nullptr)
305 return nullptr;
306
307 ValueObjectSP val_sp = GetValueAt(idx, /*max_depth=*/num_children);
308 if (!val_sp) {
309 // this will stop all future searches until an Update() happens
310 m_tree = nullptr;
311 return nullptr;
312 }
313
314 // at this point we have a valid pair
315 // we need to copy current_sp into a new object otherwise we will end up with
316 // all items named _Myval
317 StreamString name;
318 name.Printf("[%" PRIu64 "]", (uint64_t)idx);
319 return val_sp->Clone(name.GetString());
320}
321
325 m_tree = m_begin_node = nullptr;
326 m_iterators.clear();
327 m_tree =
328 m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myval2"}).get();
329 if (!m_tree)
331
332 m_begin_node = m_tree->GetChildAtNamePath({"_Myhead", "_Left"}).get();
333
335}
336
338 m_inner_sp = nullptr;
339 ValueObjectSP node_sp = m_backend.GetChildMemberWithName("_Ptr");
340 if (!node_sp)
341 return lldb::eRefetch;
342
343 MapEntry entry(node_sp.get());
344 if (entry.is_nil())
345 return lldb::eRefetch; // end
346
347 m_inner_sp = node_sp->GetChildMemberWithName("_Myval");
348 return lldb::eRefetch;
349}
350
352 return valobj.GetChildMemberWithName("_Ptr") != nullptr;
353}
354
356 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
357 auto valobj_sp = valobj.GetNonSyntheticValue();
358 if (!valobj_sp)
359 return false;
360 auto node_sp = valobj_sp->GetChildMemberWithName("_Ptr");
361 if (!node_sp)
362 return false;
363
364 MapEntry entry(node_sp.get());
365 if (entry.is_nil()) {
366 stream.Printf("end");
367 return true;
368 }
369
370 auto value_sp = node_sp->GetChildMemberWithName("_Myval");
371 if (!value_sp)
372 return false;
373
374 auto *summary = value_sp->GetSummaryAsCString();
375 if (summary)
376 stream << summary;
377 return true;
378}
379
383 return (valobj_sp ? new MsvcStlTreeIterSyntheticFrontEnd(valobj_sp)
384 : nullptr);
385}
386
388 return valobj.GetChildMemberWithName("_Mypair") != nullptr;
389}
390
393 lldb::ValueObjectSP valobj_sp) {
394 return (valobj_sp ? new MsvcStlTreeSyntheticFrontEnd(valobj_sp) : nullptr);
395}
ValueObjectSP right() const
Definition LibCxxMap.cpp:61
ValueObjectSP left() const
Definition LibCxxMap.cpp:54
ValueObjectSP parent() const
Definition LibCxxMap.cpp:69
void SetEntry(ValueObjectSP entry)
Definition LibCxxMap.cpp:93
MapEntry()=default
ValueObjectSP m_entry_sp
ValueObjectSP GetEntry() const
Definition LibCxxMap.cpp:91
uint64_t value() const
Definition LibCxxMap.cpp:77
bool operator==(const MapEntry &rhs) const
Definition LibCxxMap.cpp:95
bool error() const
Definition LibCxxMap.cpp:83
ValueObjectSP advance(size_t count)
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
MapEntry tree_min(MapEntry x)
Mimicks libc++'s __tree_min algorithm.
llvm::StringRef GetString() const
A stream class that can stream formatted output to a file.
Definition Stream.h:28
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)
SyntheticChildrenFrontEnd(ValueObject &backend)
virtual lldb::ValueObjectSP GetChildMemberWithName(llvm::StringRef name, bool can_create=true)
virtual lldb::ValueObjectSP GetNonSyntheticValue()
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< uint32_t > CalculateNumChildren() override
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(llvm::StringRef name) override
Determine the index of a named child.
MsvcStlTreeIterSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
llvm::Expected< uint32_t > CalculateNumChildren() override
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
ValueObjectSP GetValueAt(size_t idx, size_t max_depth)
Returns the ValueObject for the _Tree_node at index idx.
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
#define UINT32_MAX
bool IsMsvcStlMapLike(ValueObject &valobj)
bool IsMsvcStlTreeIter(ValueObject &valobj)
lldb_private::SyntheticChildrenFrontEnd * MsvcStlMapLikeSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp)
bool MsvcStlTreeIterSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
lldb_private::SyntheticChildrenFrontEnd * MsvcStlTreeIterSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp)
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