1 //===-- LibCxxList.cpp ------------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 // Other libraries and framework includes
16 #include "lldb/Core/DataBufferHeap.h"
17 #include "lldb/Core/Error.h"
18 #include "lldb/Core/Stream.h"
19 #include "lldb/Core/ValueObject.h"
20 #include "lldb/Core/ValueObjectConstResult.h"
21 #include "lldb/DataFormatters/FormattersHelpers.h"
22 #include "lldb/Host/Endian.h"
23 #include "lldb/Symbol/ClangASTContext.h"
24 #include "lldb/Target/Target.h"
27 using namespace lldb_private;
28 using namespace lldb_private::formatters;
34 ListEntry() = default;
35 ListEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
36 ListEntry(const ListEntry &rhs) = default;
37 ListEntry(ValueObject *entry)
38 : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
41 static ConstString g_next("__next_");
45 return ListEntry(m_entry_sp->GetChildMemberWithName(g_next, true));
49 static ConstString g_prev("__prev_");
53 return ListEntry(m_entry_sp->GetChildMemberWithName(g_prev, true));
56 uint64_t value() const {
59 return m_entry_sp->GetValueAsUnsigned(0);
62 bool null() { return (value() == 0); }
64 explicit operator bool() { return GetEntry() && !null(); }
66 ValueObjectSP GetEntry() { return m_entry_sp; }
68 void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
70 bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); }
72 bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); }
75 ValueObjectSP m_entry_sp;
80 ListIterator() = default;
81 ListIterator(ListEntry entry) : m_entry(entry) {}
82 ListIterator(ValueObjectSP entry) : m_entry(entry) {}
83 ListIterator(const ListIterator &rhs) = default;
84 ListIterator(ValueObject *entry) : m_entry(entry) {}
86 ValueObjectSP value() { return m_entry.GetEntry(); }
88 ValueObjectSP advance(size_t count) {
90 return m_entry.GetEntry();
93 return m_entry.GetEntry();
99 return lldb::ValueObjectSP();
101 return m_entry.GetEntry();
104 bool operator==(const ListIterator &rhs) const {
105 return (rhs.m_entry == m_entry);
109 void next() { m_entry = m_entry.next(); }
111 void prev() { m_entry = m_entry.prev(); }
117 } // end anonymous namespace
119 namespace lldb_private {
120 namespace formatters {
121 class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
123 LibcxxStdListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
125 ~LibcxxStdListSyntheticFrontEnd() override = default;
127 size_t CalculateNumChildren() override;
129 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
131 bool Update() override;
133 bool MightHaveChildren() override;
135 size_t GetIndexOfChildWithName(const ConstString &name) override;
138 bool HasLoop(size_t count);
140 size_t m_list_capping_size;
141 static const bool g_use_loop_detect = true;
143 size_t m_loop_detected; // The number of elements that have had loop detection
145 ListEntry m_slow_runner; // Used for loop detection
146 ListEntry m_fast_runner; // Used for loop detection
148 lldb::addr_t m_node_address;
151 CompilerType m_element_type;
153 std::map<size_t, ListIterator> m_iterators;
155 } // namespace formatters
156 } // namespace lldb_private
158 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::
159 LibcxxStdListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
160 : SyntheticChildrenFrontEnd(*valobj_sp), m_list_capping_size(0),
161 m_loop_detected(0), m_node_address(), m_head(nullptr), m_tail(nullptr),
162 m_element_type(), m_count(UINT32_MAX), m_iterators() {
167 bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop(
169 if (!g_use_loop_detect)
171 // don't bother checking for a loop if we won't actually need to jump nodes
175 if (m_loop_detected == 0) {
176 // This is the first time we are being run (after the last update). Set up
178 // invariant for the first element.
179 m_slow_runner = ListEntry(m_head).next();
180 m_fast_runner = m_slow_runner.next();
185 // Loop detection has been run over the first m_loop_detected elements. If
187 // m_fast_runner then the loop has been detected after m_loop_detected
189 const size_t steps_to_run = std::min(count, m_count);
190 while (m_loop_detected < steps_to_run && m_slow_runner && m_fast_runner &&
191 m_slow_runner != m_fast_runner) {
193 m_slow_runner = m_slow_runner.next();
194 m_fast_runner = m_fast_runner.next().next();
197 if (count <= m_loop_detected)
198 return false; // No loop in the first m_loop_detected elements.
199 if (!m_slow_runner || !m_fast_runner)
200 return false; // Reached the end of the list. Definitely no loops.
201 return m_slow_runner == m_fast_runner;
204 size_t lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::
205 CalculateNumChildren() {
206 if (m_count != UINT32_MAX)
208 if (!m_head || !m_tail || m_node_address == 0)
210 ValueObjectSP size_alloc(
211 m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true));
214 size_alloc->GetChildMemberWithName(ConstString("__first_"), true));
216 m_count = first->GetValueAsUnsigned(UINT32_MAX);
219 if (m_count != UINT32_MAX) {
222 uint64_t next_val = m_head->GetValueAsUnsigned(0);
223 uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
224 if (next_val == 0 || prev_val == 0)
226 if (next_val == m_node_address)
228 if (next_val == prev_val)
231 ListEntry current(m_head);
232 while (current.next() && current.next().value() != m_node_address) {
234 current = current.next();
235 if (size > m_list_capping_size)
238 return m_count = (size - 1);
243 lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex(
245 static ConstString g_value("__value_");
246 static ConstString g_next("__next_");
248 if (idx >= CalculateNumChildren())
249 return lldb::ValueObjectSP();
251 if (!m_head || !m_tail || m_node_address == 0)
252 return lldb::ValueObjectSP();
254 if (HasLoop(idx + 1))
255 return lldb::ValueObjectSP();
257 size_t actual_advance = idx;
259 ListIterator current(m_head);
261 auto cached_iterator = m_iterators.find(idx - 1);
262 if (cached_iterator != m_iterators.end()) {
263 current = cached_iterator->second;
268 ValueObjectSP current_sp(current.advance(actual_advance));
270 return lldb::ValueObjectSP();
272 m_iterators[idx] = current;
274 current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child
276 return lldb::ValueObjectSP();
278 if (current_sp->GetName() == g_next) {
279 ProcessSP process_sp(current_sp->GetProcessSP());
283 // if we grabbed the __next_ pointer, then the child is one pointer deep-er
284 lldb::addr_t addr = current_sp->GetParent()->GetPointerValue();
285 addr = addr + 2 * process_sp->GetAddressByteSize();
286 ExecutionContext exe_ctx(process_sp);
288 CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type);
291 // we need to copy current_sp into a new object otherwise we will end up with
292 // all items named __value_
295 current_sp->GetData(data, error);
297 return lldb::ValueObjectSP();
300 name.Printf("[%" PRIu64 "]", (uint64_t)idx);
301 return CreateValueObjectFromData(name.GetString(), data,
302 m_backend.GetExecutionContextRef(),
306 bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() {
308 m_head = m_tail = nullptr;
310 m_count = UINT32_MAX;
312 m_slow_runner.SetEntry(nullptr);
313 m_fast_runner.SetEntry(nullptr);
316 ValueObjectSP backend_addr(m_backend.AddressOf(err));
317 m_list_capping_size = 0;
318 if (m_backend.GetTargetSP())
319 m_list_capping_size =
320 m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
321 if (m_list_capping_size == 0)
322 m_list_capping_size = 255;
323 if (err.Fail() || !backend_addr)
325 m_node_address = backend_addr->GetValueAsUnsigned(0);
326 if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
328 ValueObjectSP impl_sp(
329 m_backend.GetChildMemberWithName(ConstString("__end_"), true));
332 CompilerType list_type = m_backend.GetCompilerType();
333 if (list_type.IsReferenceType())
334 list_type = list_type.GetNonReferenceType();
336 if (list_type.GetNumTemplateArguments() == 0)
338 lldb::TemplateArgumentKind kind;
339 m_element_type = list_type.GetTemplateArgument(0, kind);
340 m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
341 m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get();
345 bool lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::
346 MightHaveChildren() {
350 size_t lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::
351 GetIndexOfChildWithName(const ConstString &name) {
352 return ExtractIndexFromString(name.GetCString());
355 SyntheticChildrenFrontEnd *
356 lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator(
357 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
358 return (valobj_sp ? new LibcxxStdListSyntheticFrontEnd(valobj_sp) : nullptr);