LLDB mainline
GenericList.cpp
Go to the documentation of this file.
1//===-- GenericList.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#include "MsvcStl.h"
11
13#include "lldb/Target/Target.h"
14#include "lldb/Utility/Status.h"
18
19using namespace lldb;
20using namespace lldb_private;
21using namespace lldb_private::formatters;
22
23namespace {
24
25enum class StlType {
26 LibCxx,
27 MsvcStl,
28};
29
30template <StlType Stl> class ListEntry {
31public:
32 ListEntry() = default;
33 ListEntry(ValueObjectSP entry_sp) : m_entry_sp(std::move(entry_sp)) {}
34 ListEntry(ValueObject *entry)
35 : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
36
37 uint64_t value() const {
38 if (!m_entry_sp)
39 return 0;
40 return m_entry_sp->GetValueAsUnsigned(0);
41 }
42
43 ListEntry next();
44 ListEntry prev();
45
46 bool null() { return (value() == 0); }
47
48 explicit operator bool() { return GetEntry() && !null(); }
49
50 ValueObjectSP GetEntry() { return m_entry_sp; }
51
52 void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
53
54 bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); }
55
56 bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); }
57
58private:
59 ValueObjectSP m_entry_sp;
60};
61
62template <> ListEntry<StlType::LibCxx> ListEntry<StlType::LibCxx>::next() {
63 if (!m_entry_sp)
64 return ListEntry();
65 return ListEntry(m_entry_sp->GetChildMemberWithName("__next_"));
66}
67
68template <> ListEntry<StlType::LibCxx> ListEntry<StlType::LibCxx>::prev() {
69 if (!m_entry_sp)
70 return ListEntry();
71 return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_"));
72}
73
74template <> ListEntry<StlType::MsvcStl> ListEntry<StlType::MsvcStl>::next() {
75 if (!m_entry_sp)
76 return ListEntry();
77 return ListEntry(m_entry_sp->GetChildMemberWithName("_Next"));
78}
79
80template <> ListEntry<StlType::MsvcStl> ListEntry<StlType::MsvcStl>::prev() {
81 if (!m_entry_sp)
82 return ListEntry();
83 return ListEntry(m_entry_sp->GetChildMemberWithName("_Prev"));
84}
85
86template <StlType Stl> class ListIterator {
87public:
88 ListIterator() = default;
89 ListIterator(ListEntry<Stl> entry) : m_entry(std::move(entry)) {}
90 ListIterator(ValueObjectSP entry) : m_entry(std::move(entry)) {}
91 ListIterator(ValueObject *entry) : m_entry(entry) {}
92
93 ValueObjectSP value() { return m_entry.GetEntry(); }
94
95 ValueObjectSP advance(size_t count) {
96 if (count == 0)
97 return m_entry.GetEntry();
98 if (count == 1) {
99 next();
100 return m_entry.GetEntry();
101 }
102 while (count > 0) {
103 next();
104 count--;
105 if (m_entry.null())
106 return lldb::ValueObjectSP();
107 }
108 return m_entry.GetEntry();
109 }
110
111 bool operator==(const ListIterator &rhs) const {
112 return (rhs.m_entry == m_entry);
113 }
114
115protected:
116 void next() { m_entry = m_entry.next(); }
117
118 void prev() { m_entry = m_entry.prev(); }
119
120private:
121 ListEntry<Stl> m_entry;
122};
123
124template <StlType Stl>
125class AbstractListFrontEnd : public SyntheticChildrenFrontEnd {
126public:
127 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
128 auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
129 if (!optional_idx) {
130 return llvm::createStringError("Type has no child named '%s'",
131 name.AsCString());
132 }
133 return *optional_idx;
134 }
135 lldb::ChildCacheState Update() override;
136
137protected:
138 AbstractListFrontEnd(ValueObject &valobj)
139 : SyntheticChildrenFrontEnd(valobj) {}
140
141 size_t m_count = 0;
142 ValueObject *m_head = nullptr;
143
144 static constexpr bool g_use_loop_detect = true;
145 size_t m_loop_detected = 0; // The number of elements that have had loop
146 // detection run over them.
147 ListEntry<Stl> m_slow_runner; // Used for loop detection
148 ListEntry<Stl> m_fast_runner; // Used for loop detection
149
150 size_t m_list_capping_size = 0;
151 CompilerType m_element_type;
152 std::map<size_t, ListIterator<Stl>> m_iterators;
153
154 bool HasLoop(size_t count);
155 ValueObjectSP GetItem(size_t idx);
156};
157
158class LibCxxForwardListFrontEnd : public AbstractListFrontEnd<StlType::LibCxx> {
159public:
160 LibCxxForwardListFrontEnd(ValueObject &valobj);
161
162 llvm::Expected<uint32_t> CalculateNumChildren() override;
163 ValueObjectSP GetChildAtIndex(uint32_t idx) override;
164 lldb::ChildCacheState Update() override;
165};
166
167class LibCxxListFrontEnd : public AbstractListFrontEnd<StlType::LibCxx> {
168public:
169 LibCxxListFrontEnd(lldb::ValueObjectSP valobj_sp);
170
171 llvm::Expected<uint32_t> CalculateNumChildren() override;
172
173 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
174
175 lldb::ChildCacheState Update() override;
176
177private:
178 lldb::addr_t m_node_address = 0;
179 ValueObject *m_tail = nullptr;
180};
181
182class MsvcStlForwardListFrontEnd
183 : public AbstractListFrontEnd<StlType::MsvcStl> {
184public:
185 MsvcStlForwardListFrontEnd(ValueObject &valobj);
186
187 llvm::Expected<uint32_t> CalculateNumChildren() override;
188 ValueObjectSP GetChildAtIndex(uint32_t idx) override;
189 lldb::ChildCacheState Update() override;
190};
191
192class MsvcStlListFrontEnd : public AbstractListFrontEnd<StlType::MsvcStl> {
193public:
194 MsvcStlListFrontEnd(lldb::ValueObjectSP valobj_sp);
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 ValueObject *m_tail = nullptr;
204};
205
206/// Gets the (forward-)list element type from the head node instead of the
207/// template arguments. This is needed with PDB as it doesn't have info about
208/// the template arguments.
209CompilerType GetMsvcStlElementTypeFromHead(ValueObject &head) {
210 auto val_sp = head.GetChildMemberWithName("_Myval");
211 if (val_sp)
212 return val_sp->GetCompilerType();
213 return CompilerType();
214}
215
216} // end anonymous namespace
217
218template <StlType Stl>
219lldb::ChildCacheState AbstractListFrontEnd<Stl>::Update() {
220 m_loop_detected = 0;
221 m_count = UINT32_MAX;
222 m_head = nullptr;
223 m_list_capping_size = 0;
224 m_slow_runner.SetEntry(nullptr);
225 m_fast_runner.SetEntry(nullptr);
226 m_iterators.clear();
227
228 if (m_backend.GetTargetSP())
229 m_list_capping_size =
230 m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
231 if (m_list_capping_size == 0)
232 m_list_capping_size = 255;
233
234 CompilerType list_type = m_backend.GetCompilerType();
235 if (list_type.IsReferenceType())
236 list_type = list_type.GetNonReferenceType();
237
238 if (list_type.GetNumTemplateArguments() == 0)
240 m_element_type = list_type.GetTypeTemplateArgument(0);
241
243}
244
245template <StlType Stl> bool AbstractListFrontEnd<Stl>::HasLoop(size_t count) {
246 if (!g_use_loop_detect)
247 return false;
248 // don't bother checking for a loop if we won't actually need to jump nodes
249 if (m_count < 2)
250 return false;
251
252 if (m_loop_detected == 0) {
253 // This is the first time we are being run (after the last update). Set up
254 // the loop invariant for the first element.
255 m_slow_runner = ListEntry<Stl>(m_head).next();
256 m_fast_runner = m_slow_runner.next();
257 m_loop_detected = 1;
258 }
259
260 // Loop invariant:
261 // Loop detection has been run over the first m_loop_detected elements. If
262 // m_slow_runner == m_fast_runner then the loop has been detected after
263 // m_loop_detected elements.
264 const size_t steps_to_run = std::min(count, m_count);
265 while (m_loop_detected < steps_to_run && m_slow_runner && m_fast_runner &&
266 m_slow_runner != m_fast_runner) {
267
268 m_slow_runner = m_slow_runner.next();
269 m_fast_runner = m_fast_runner.next().next();
270 m_loop_detected++;
271 }
272 if (count <= m_loop_detected)
273 return false; // No loop in the first m_loop_detected elements.
274 if (!m_slow_runner || !m_fast_runner)
275 return false; // Reached the end of the list. Definitely no loops.
276 return m_slow_runner == m_fast_runner;
277}
278
279template <StlType Stl>
280ValueObjectSP AbstractListFrontEnd<Stl>::GetItem(size_t idx) {
281 size_t advance = idx;
282 ListIterator<Stl> current(m_head);
283 if (idx > 0) {
284 auto cached_iterator = m_iterators.find(idx - 1);
285 if (cached_iterator != m_iterators.end()) {
286 current = cached_iterator->second;
287 advance = 1;
288 }
289 }
290 ValueObjectSP value_sp = current.advance(advance);
291 m_iterators[idx] = current;
292 return value_sp;
293}
294
295LibCxxForwardListFrontEnd::LibCxxForwardListFrontEnd(ValueObject &valobj)
296 : AbstractListFrontEnd(valobj) {
297 Update();
298}
299
300llvm::Expected<uint32_t> LibCxxForwardListFrontEnd::CalculateNumChildren() {
301 if (m_count != UINT32_MAX)
302 return m_count;
303
304 ListEntry<StlType::LibCxx> current(m_head);
305 m_count = 0;
306 while (current && m_count < m_list_capping_size) {
307 ++m_count;
308 current = current.next();
309 }
310 return m_count;
311}
312
313ValueObjectSP LibCxxForwardListFrontEnd::GetChildAtIndex(uint32_t idx) {
314 if (idx >= CalculateNumChildrenIgnoringErrors())
315 return nullptr;
316
317 if (!m_head)
318 return nullptr;
319
320 if (HasLoop(idx + 1))
321 return nullptr;
322
323 ValueObjectSP current_sp = GetItem(idx);
324 if (!current_sp)
325 return nullptr;
326
327 current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child
328 if (!current_sp)
329 return nullptr;
330
331 // we need to copy current_sp into a new object otherwise we will end up with
332 // all items named __value_
333 DataExtractor data;
335 current_sp->GetData(data, error);
336 if (error.Fail())
337 return nullptr;
338
339 return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data,
340 m_backend.GetExecutionContextRef(),
341 m_element_type);
342}
343
344lldb::ChildCacheState LibCxxForwardListFrontEnd::Update() {
345 AbstractListFrontEnd::Update();
346
347 Status err;
348 ValueObjectSP backend_addr(m_backend.AddressOf(err));
349 if (err.Fail() || !backend_addr)
351
352 auto list_base_sp = m_backend.GetChildAtIndex(0);
353 if (!list_base_sp)
355
356 // Anonymous strucutre index is in base class at index 0.
357 auto [impl_sp, is_compressed_pair] =
358 GetValueOrOldCompressedPair(*list_base_sp, /*anon_struct_idx=*/0,
359 "__before_begin_", "__before_begin_");
360 if (!impl_sp)
361 return ChildCacheState::eRefetch;
362
363 if (is_compressed_pair)
364 impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
365
366 if (!impl_sp)
367 return ChildCacheState::eRefetch;
368
369 m_head = impl_sp->GetChildMemberWithName("__next_").get();
370
371 return ChildCacheState::eRefetch;
372}
373
374LibCxxListFrontEnd::LibCxxListFrontEnd(lldb::ValueObjectSP valobj_sp)
375 : AbstractListFrontEnd(*valobj_sp) {
376 if (valobj_sp)
377 Update();
378}
379
380llvm::Expected<uint32_t> LibCxxListFrontEnd::CalculateNumChildren() {
381 if (m_count != UINT32_MAX)
382 return m_count;
383 if (!m_head || !m_tail || m_node_address == 0)
384 return 0;
385
386 auto [size_node_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
387 m_backend, /*anon_struct_idx=*/1, "__size_", "__size_alloc_");
388 if (is_compressed_pair)
389 size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp);
390
391 if (size_node_sp)
392 m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX);
393
394 if (m_count != UINT32_MAX)
395 return m_count;
396
397 uint64_t next_val = m_head->GetValueAsUnsigned(0);
398 uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
399 if (next_val == 0 || prev_val == 0)
400 return 0;
401 if (next_val == m_node_address)
402 return 0;
403 if (next_val == prev_val)
404 return 1;
405 uint64_t size = 2;
406 ListEntry<StlType::LibCxx> current(m_head);
407 while (current.next() && current.next().value() != m_node_address) {
408 size++;
409 current = current.next();
410 if (size > m_list_capping_size)
411 break;
412 }
413 return m_count = (size - 1);
414}
415
416lldb::ValueObjectSP LibCxxListFrontEnd::GetChildAtIndex(uint32_t idx) {
417 static ConstString g_value("__value_");
418 static ConstString g_next("__next_");
419
420 if (idx >= CalculateNumChildrenIgnoringErrors())
421 return lldb::ValueObjectSP();
422
423 if (!m_head || !m_tail || m_node_address == 0)
424 return lldb::ValueObjectSP();
425
426 if (HasLoop(idx + 1))
427 return lldb::ValueObjectSP();
428
429 ValueObjectSP current_sp = GetItem(idx);
430 if (!current_sp)
431 return lldb::ValueObjectSP();
432
433 current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child
434 if (!current_sp)
435 return lldb::ValueObjectSP();
436
437 if (current_sp->GetName() == g_next) {
438 ProcessSP process_sp(current_sp->GetProcessSP());
439 if (!process_sp)
440 return lldb::ValueObjectSP();
441
442 // if we grabbed the __next_ pointer, then the child is one pointer deep-er
443 lldb::addr_t addr = current_sp->GetParent()->GetPointerValue().address;
444 addr = addr + 2 * process_sp->GetAddressByteSize();
445 ExecutionContext exe_ctx(process_sp);
446 current_sp =
447 CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type);
448 if (!current_sp)
449 return lldb::ValueObjectSP();
450 }
451
452 // we need to copy current_sp into a new object otherwise we will end up with
453 // all items named __value_
454 DataExtractor data;
456 current_sp->GetData(data, error);
457 if (error.Fail())
458 return lldb::ValueObjectSP();
459
460 StreamString name;
461 name.Printf("[%" PRIu64 "]", (uint64_t)idx);
462 return CreateValueObjectFromData(name.GetString(), data,
463 m_backend.GetExecutionContextRef(),
464 m_element_type);
465}
466
467lldb::ChildCacheState LibCxxListFrontEnd::Update() {
468 AbstractListFrontEnd::Update();
469 m_tail = nullptr;
470 m_node_address = 0;
471
472 Status err;
473 ValueObjectSP backend_addr(m_backend.AddressOf(err));
474 if (err.Fail() || !backend_addr)
476 m_node_address = backend_addr->GetValueAsUnsigned(0);
477 if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
479 ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__end_"));
480 if (!impl_sp)
482 m_head = impl_sp->GetChildMemberWithName("__next_").get();
483 m_tail = impl_sp->GetChildMemberWithName("__prev_").get();
485}
486
487MsvcStlForwardListFrontEnd::MsvcStlForwardListFrontEnd(ValueObject &valobj)
488 : AbstractListFrontEnd(valobj) {
489 Update();
490}
491
492llvm::Expected<uint32_t> MsvcStlForwardListFrontEnd::CalculateNumChildren() {
493 if (m_count != UINT32_MAX)
494 return m_count;
495
496 ListEntry<StlType::MsvcStl> current(m_head);
497 m_count = 0;
498 while (current && m_count < m_list_capping_size) {
499 ++m_count;
500 current = current.next();
501 }
502 return m_count;
503}
504
505ValueObjectSP MsvcStlForwardListFrontEnd::GetChildAtIndex(uint32_t idx) {
506 if (idx >= CalculateNumChildrenIgnoringErrors())
507 return nullptr;
508
509 if (!m_head)
510 return nullptr;
511
512 if (HasLoop(idx + 1))
513 return nullptr;
514
515 ValueObjectSP current_sp = GetItem(idx);
516 if (!current_sp)
517 return nullptr;
518
519 current_sp = current_sp->GetChildAtIndex(1); // get the _Myval child
520 if (!current_sp)
521 return nullptr;
522
523 // we need to copy current_sp into a new object otherwise we will end up with
524 // all items named _Myval
525 DataExtractor data;
527 current_sp->GetData(data, error);
528 if (error.Fail())
529 return nullptr;
530
531 return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data,
532 m_backend.GetExecutionContextRef(),
533 m_element_type);
534}
535
536lldb::ChildCacheState MsvcStlForwardListFrontEnd::Update() {
537 AbstractListFrontEnd::Update();
538
539 if (auto head_sp =
540 m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myhead"}))
541 m_head = head_sp.get();
542
543 // With PDB, we can't get the element type from the template arguments
544 if (!m_element_type && m_head)
545 m_element_type = GetMsvcStlElementTypeFromHead(*m_head);
546
547 return ChildCacheState::eRefetch;
548}
549
550MsvcStlListFrontEnd::MsvcStlListFrontEnd(lldb::ValueObjectSP valobj_sp)
551 : AbstractListFrontEnd(*valobj_sp) {
552 if (valobj_sp)
553 Update();
554}
555
556llvm::Expected<uint32_t> MsvcStlListFrontEnd::CalculateNumChildren() {
557 if (m_count != UINT32_MAX)
558 return m_count;
559 if (!m_head || !m_tail)
560 return 0;
561
562 auto size_sp =
563 m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Mysize"});
564 if (!size_sp)
565 return llvm::createStringError("Failed to resolve size.");
566
567 m_count = size_sp->GetValueAsUnsigned(UINT32_MAX);
568 if (m_count == UINT32_MAX)
569 return llvm::createStringError("Failed to read size value.");
570
571 return m_count;
572}
573
574lldb::ValueObjectSP MsvcStlListFrontEnd::GetChildAtIndex(uint32_t idx) {
575 if (idx >= CalculateNumChildrenIgnoringErrors())
576 return lldb::ValueObjectSP();
577
578 if (!m_head || !m_tail)
579 return lldb::ValueObjectSP();
580
581 if (HasLoop(idx + 1))
582 return lldb::ValueObjectSP();
583
584 ValueObjectSP current_sp = GetItem(idx);
585 if (!current_sp)
586 return lldb::ValueObjectSP();
587
588 current_sp = current_sp->GetChildAtIndex(2); // get the _Myval child
589 if (!current_sp)
590 return lldb::ValueObjectSP();
591
592 // we need to copy current_sp into a new object otherwise we will end up with
593 // all items named _Myval
594 DataExtractor data;
596 current_sp->GetData(data, error);
597 if (error.Fail())
598 return lldb::ValueObjectSP();
599
600 StreamString name;
601 name.Printf("[%" PRIu64 "]", (uint64_t)idx);
602 return CreateValueObjectFromData(name.GetString(), data,
603 m_backend.GetExecutionContextRef(),
604 m_element_type);
605}
606
607lldb::ChildCacheState MsvcStlListFrontEnd::Update() {
608 AbstractListFrontEnd::Update();
609 m_tail = nullptr;
610 m_head = nullptr;
611
612 ValueObjectSP last =
613 m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myhead"});
614 if (!last)
616 ValueObjectSP first = last->GetChildMemberWithName("_Next");
617 if (!first)
619
620 m_head = first.get();
621 m_tail = last.get();
622
623 // With PDB, we can't get the element type from the template arguments
624 if (!m_element_type && m_head)
625 m_element_type = GetMsvcStlElementTypeFromHead(*m_head);
626
628}
629
631 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
632 return (valobj_sp ? new LibCxxListFrontEnd(valobj_sp) : nullptr);
633}
634
635SyntheticChildrenFrontEnd *
637 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
638 return valobj_sp ? new LibCxxForwardListFrontEnd(*valobj_sp) : nullptr;
639}
640
642 if (auto valobj_sp = valobj.GetNonSyntheticValue())
643 return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr;
644
645 return false;
646}
647
650 lldb::ValueObjectSP valobj_sp) {
651 return (valobj_sp ? new MsvcStlListFrontEnd(valobj_sp) : nullptr);
652}
653
657 return valobj_sp ? new MsvcStlForwardListFrontEnd(*valobj_sp) : nullptr;
658}
static llvm::raw_ostream & error(Stream &strm)
static std::optional< size_t > CalculateNumChildren(CompilerType container_elem_type, uint64_t num_elements, CompilerType element_type)
Calculates the number of elements stored in a container (with element type 'container_elem_type') as ...
Generic representation of a type in a programming language.
CompilerType GetTypeTemplateArgument(size_t idx, bool expand_pack=false) const
size_t GetNumTemplateArguments(bool expand_pack=false) const
Return the number of template arguments the type has.
CompilerType GetNonReferenceType() const
If this type is a reference to a type (L value or R value reference), return a new type with the refe...
bool IsReferenceType(CompilerType *pointee_type=nullptr, bool *is_rvalue=nullptr) const
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.
bool Fail() const
Test for error condition.
Definition Status.cpp:294
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success=nullptr)
virtual lldb::ValueObjectSP GetChildMemberWithName(llvm::StringRef name, bool can_create=true)
lldb::ValueObjectSP GetChildAtNamePath(llvm::ArrayRef< llvm::StringRef > names)
virtual lldb::ValueObjectSP GetNonSyntheticValue()
#define LLDB_INVALID_ADDRESS
#define UINT32_MAX
std::optional< size_t > ExtractIndexFromString(const char *item_name)
SyntheticChildrenFrontEnd * MsvcStlListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp)
SyntheticChildrenFrontEnd * MsvcStlForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp)
lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair)
Definition LibCxx.cpp:75
SyntheticChildrenFrontEnd * LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
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
bool IsMsvcStlList(ValueObject &valobj)
SyntheticChildrenFrontEnd * LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
A class that represents a running process on the host machine.
bool operator!=(const Address &lhs, const Address &rhs)
Definition Address.cpp:1017
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::Process > ProcessSP
uint64_t addr_t
Definition lldb-types.h:80
bool LLDB_API operator==(const SBAddress &lhs, const SBAddress &rhs)
Definition SBAddress.cpp:60