/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- LibCxxList.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 | | |
11 | | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
12 | | #include "lldb/Core/ValueObject.h" |
13 | | #include "lldb/Core/ValueObjectConstResult.h" |
14 | | #include "lldb/DataFormatters/FormattersHelpers.h" |
15 | | #include "lldb/Target/Target.h" |
16 | | #include "lldb/Utility/DataBufferHeap.h" |
17 | | #include "lldb/Utility/Endian.h" |
18 | | #include "lldb/Utility/Status.h" |
19 | | #include "lldb/Utility/Stream.h" |
20 | | |
21 | | using namespace lldb; |
22 | | using namespace lldb_private; |
23 | | using namespace lldb_private::formatters; |
24 | | |
25 | | namespace { |
26 | | |
27 | | class ListEntry { |
28 | | public: |
29 | 1.82k | ListEntry() = default; |
30 | 8.18k | ListEntry(ValueObjectSP entry_sp) : m_entry_sp(std::move(entry_sp)) {} |
31 | | ListEntry(ValueObject *entry) |
32 | 1.76k | : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()0 ) {} |
33 | | |
34 | 8.18k | ListEntry next() { |
35 | 8.18k | if (!m_entry_sp) |
36 | 0 | return ListEntry(); |
37 | 8.18k | return ListEntry(m_entry_sp->GetChildMemberWithName("__next_")); |
38 | 8.18k | } |
39 | | |
40 | 0 | ListEntry prev() { |
41 | 0 | if (!m_entry_sp) |
42 | 0 | return ListEntry(); |
43 | 0 | return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_")); |
44 | 0 | } |
45 | | |
46 | 8.17k | uint64_t value() const { |
47 | 8.17k | if (!m_entry_sp) |
48 | 0 | return 0; |
49 | 8.17k | return m_entry_sp->GetValueAsUnsigned(0); |
50 | 8.17k | } |
51 | | |
52 | 4.91k | bool null() { return (value() == 0); } |
53 | | |
54 | 4.91k | explicit operator bool() { return GetEntry() && !null()4.91k ; } |
55 | | |
56 | 6.61k | ValueObjectSP GetEntry() { return m_entry_sp; } |
57 | | |
58 | 256 | void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } |
59 | | |
60 | 1.63k | bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); } |
61 | | |
62 | 1.63k | bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); } |
63 | | |
64 | | private: |
65 | | ValueObjectSP m_entry_sp; |
66 | | }; |
67 | | |
68 | | class ListIterator { |
69 | | public: |
70 | 1.69k | ListIterator() = default; |
71 | 0 | ListIterator(ListEntry entry) : m_entry(std::move(entry)) {} |
72 | 0 | ListIterator(ValueObjectSP entry) : m_entry(std::move(entry)) {} |
73 | 1.69k | ListIterator(ValueObject *entry) : m_entry(entry) {} |
74 | | |
75 | 0 | ValueObjectSP value() { return m_entry.GetEntry(); } |
76 | | |
77 | 1.69k | ValueObjectSP advance(size_t count) { |
78 | 1.69k | if (count == 0) |
79 | 54 | return m_entry.GetEntry(); |
80 | 1.64k | if (count == 1) { |
81 | 1.64k | next(); |
82 | 1.64k | return m_entry.GetEntry(); |
83 | 1.64k | } |
84 | 0 | while (count > 0) { |
85 | 0 | next(); |
86 | 0 | count--; |
87 | 0 | if (m_entry.null()) |
88 | 0 | return lldb::ValueObjectSP(); |
89 | 0 | } |
90 | 0 | return m_entry.GetEntry(); |
91 | 0 | } |
92 | | |
93 | 0 | bool operator==(const ListIterator &rhs) const { |
94 | 0 | return (rhs.m_entry == m_entry); |
95 | 0 | } |
96 | | |
97 | | protected: |
98 | 1.64k | void next() { m_entry = m_entry.next(); } |
99 | | |
100 | 0 | void prev() { m_entry = m_entry.prev(); } |
101 | | |
102 | | private: |
103 | | ListEntry m_entry; |
104 | | }; |
105 | | |
106 | | class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { |
107 | | public: |
108 | 0 | size_t GetIndexOfChildWithName(ConstString name) override { |
109 | 0 | return ExtractIndexFromString(name.GetCString()); |
110 | 0 | } |
111 | 22 | bool MightHaveChildren() override { return true; } |
112 | | bool Update() override; |
113 | | |
114 | | protected: |
115 | | AbstractListFrontEnd(ValueObject &valobj) |
116 | 64 | : SyntheticChildrenFrontEnd(valobj) {} |
117 | | |
118 | | size_t m_count = 0; |
119 | | ValueObject *m_head = nullptr; |
120 | | |
121 | | static constexpr bool g_use_loop_detect = true; |
122 | | size_t m_loop_detected = 0; // The number of elements that have had loop |
123 | | // detection run over them. |
124 | | ListEntry m_slow_runner; // Used for loop detection |
125 | | ListEntry m_fast_runner; // Used for loop detection |
126 | | |
127 | | size_t m_list_capping_size = 0; |
128 | | CompilerType m_element_type; |
129 | | std::map<size_t, ListIterator> m_iterators; |
130 | | |
131 | | bool HasLoop(size_t count); |
132 | | ValueObjectSP GetItem(size_t idx); |
133 | | }; |
134 | | |
135 | | class ForwardListFrontEnd : public AbstractListFrontEnd { |
136 | | public: |
137 | | ForwardListFrontEnd(ValueObject &valobj); |
138 | | |
139 | | size_t CalculateNumChildren() override; |
140 | | ValueObjectSP GetChildAtIndex(size_t idx) override; |
141 | | bool Update() override; |
142 | | }; |
143 | | |
144 | | class ListFrontEnd : public AbstractListFrontEnd { |
145 | | public: |
146 | | ListFrontEnd(lldb::ValueObjectSP valobj_sp); |
147 | | |
148 | 8 | ~ListFrontEnd() override = default; |
149 | | |
150 | | size_t CalculateNumChildren() override; |
151 | | |
152 | | lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; |
153 | | |
154 | | bool Update() override; |
155 | | |
156 | | private: |
157 | | lldb::addr_t m_node_address = 0; |
158 | | ValueObject *m_tail = nullptr; |
159 | | }; |
160 | | |
161 | | } // end anonymous namespace |
162 | | |
163 | 128 | bool AbstractListFrontEnd::Update() { |
164 | 128 | m_loop_detected = 0; |
165 | 128 | m_count = UINT32_MAX; |
166 | 128 | m_head = nullptr; |
167 | 128 | m_list_capping_size = 0; |
168 | 128 | m_slow_runner.SetEntry(nullptr); |
169 | 128 | m_fast_runner.SetEntry(nullptr); |
170 | 128 | m_iterators.clear(); |
171 | | |
172 | 128 | if (m_backend.GetTargetSP()) |
173 | 128 | m_list_capping_size = |
174 | 128 | m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); |
175 | 128 | if (m_list_capping_size == 0) |
176 | 0 | m_list_capping_size = 255; |
177 | | |
178 | 128 | CompilerType list_type = m_backend.GetCompilerType(); |
179 | 128 | if (list_type.IsReferenceType()) |
180 | 16 | list_type = list_type.GetNonReferenceType(); |
181 | | |
182 | 128 | if (list_type.GetNumTemplateArguments() == 0) |
183 | 0 | return false; |
184 | 128 | m_element_type = list_type.GetTypeTemplateArgument(0); |
185 | | |
186 | 128 | return false; |
187 | 128 | } |
188 | | |
189 | 1.69k | bool AbstractListFrontEnd::HasLoop(size_t count) { |
190 | 1.69k | if (!g_use_loop_detect) |
191 | 0 | return false; |
192 | | // don't bother checking for a loop if we won't actually need to jump nodes |
193 | 1.69k | if (m_count < 2) |
194 | 18 | return false; |
195 | | |
196 | 1.68k | if (m_loop_detected == 0) { |
197 | | // This is the first time we are being run (after the last update). Set up |
198 | | // the loop invariant for the first element. |
199 | 36 | m_slow_runner = ListEntry(m_head).next(); |
200 | 36 | m_fast_runner = m_slow_runner.next(); |
201 | 36 | m_loop_detected = 1; |
202 | 36 | } |
203 | | |
204 | | // Loop invariant: |
205 | | // Loop detection has been run over the first m_loop_detected elements. If |
206 | | // m_slow_runner == m_fast_runner then the loop has been detected after |
207 | | // m_loop_detected elements. |
208 | 1.68k | const size_t steps_to_run = std::min(count, m_count); |
209 | 3.31k | while (m_loop_detected < steps_to_run && m_slow_runner1.64k && m_fast_runner1.64k && |
210 | 3.31k | m_slow_runner != m_fast_runner1.63k ) { |
211 | | |
212 | 1.63k | m_slow_runner = m_slow_runner.next(); |
213 | 1.63k | m_fast_runner = m_fast_runner.next().next(); |
214 | 1.63k | m_loop_detected++; |
215 | 1.63k | } |
216 | 1.68k | if (count <= m_loop_detected) |
217 | 1.66k | return false; // No loop in the first m_loop_detected elements. |
218 | 12 | if (!m_slow_runner || !m_fast_runner) |
219 | 12 | return false; // Reached the end of the list. Definitely no loops. |
220 | 0 | return m_slow_runner == m_fast_runner; |
221 | 12 | } |
222 | | |
223 | 1.69k | ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) { |
224 | 1.69k | size_t advance = idx; |
225 | 1.69k | ListIterator current(m_head); |
226 | 1.69k | if (idx > 0) { |
227 | 1.64k | auto cached_iterator = m_iterators.find(idx - 1); |
228 | 1.64k | if (cached_iterator != m_iterators.end()) { |
229 | 1.64k | current = cached_iterator->second; |
230 | 1.64k | advance = 1; |
231 | 1.64k | } |
232 | 1.64k | } |
233 | 1.69k | ValueObjectSP value_sp = current.advance(advance); |
234 | 1.69k | m_iterators[idx] = current; |
235 | 1.69k | return value_sp; |
236 | 1.69k | } |
237 | | |
238 | | ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj) |
239 | 28 | : AbstractListFrontEnd(valobj) { |
240 | 28 | Update(); |
241 | 28 | } |
242 | | |
243 | 1.60k | size_t ForwardListFrontEnd::CalculateNumChildren() { |
244 | 1.60k | if (m_count != UINT32_MAX) |
245 | 1.58k | return m_count; |
246 | | |
247 | 28 | ListEntry current(m_head); |
248 | 28 | m_count = 0; |
249 | 1.60k | while (current && m_count < m_list_capping_size1.58k ) { |
250 | 1.57k | ++m_count; |
251 | 1.57k | current = current.next(); |
252 | 1.57k | } |
253 | 28 | return m_count; |
254 | 1.60k | } |
255 | | |
256 | 1.58k | ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(size_t idx) { |
257 | 1.58k | if (idx >= CalculateNumChildren()) |
258 | 0 | return nullptr; |
259 | | |
260 | 1.58k | if (!m_head) |
261 | 0 | return nullptr; |
262 | | |
263 | 1.58k | if (HasLoop(idx + 1)) |
264 | 0 | return nullptr; |
265 | | |
266 | 1.58k | ValueObjectSP current_sp = GetItem(idx); |
267 | 1.58k | if (!current_sp) |
268 | 0 | return nullptr; |
269 | | |
270 | 1.58k | current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child |
271 | 1.58k | if (!current_sp) |
272 | 8 | return nullptr; |
273 | | |
274 | | // we need to copy current_sp into a new object otherwise we will end up with |
275 | | // all items named __value_ |
276 | 1.57k | DataExtractor data; |
277 | 1.57k | Status error; |
278 | 1.57k | current_sp->GetData(data, error); |
279 | 1.57k | if (error.Fail()) |
280 | 0 | return nullptr; |
281 | | |
282 | 1.57k | return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data, |
283 | 1.57k | m_backend.GetExecutionContextRef(), |
284 | 1.57k | m_element_type); |
285 | 1.57k | } |
286 | | |
287 | 56 | bool ForwardListFrontEnd::Update() { |
288 | 56 | AbstractListFrontEnd::Update(); |
289 | | |
290 | 56 | Status err; |
291 | 56 | ValueObjectSP backend_addr(m_backend.AddressOf(err)); |
292 | 56 | if (err.Fail() || !backend_addr) |
293 | 0 | return false; |
294 | | |
295 | 56 | ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_")); |
296 | 56 | if (!impl_sp) |
297 | 0 | return false; |
298 | 56 | impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); |
299 | 56 | if (!impl_sp) |
300 | 0 | return false; |
301 | 56 | m_head = impl_sp->GetChildMemberWithName("__next_").get(); |
302 | 56 | return false; |
303 | 56 | } |
304 | | |
305 | | ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) |
306 | 36 | : AbstractListFrontEnd(*valobj_sp) { |
307 | 36 | if (valobj_sp) |
308 | 36 | Update(); |
309 | 36 | } |
310 | | |
311 | 154 | size_t ListFrontEnd::CalculateNumChildren() { |
312 | 154 | if (m_count != UINT32_MAX) |
313 | 118 | return m_count; |
314 | 36 | if (!m_head || !m_tail || m_node_address == 0) |
315 | 0 | return 0; |
316 | 36 | ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_")); |
317 | 36 | if (size_alloc) { |
318 | 36 | ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc); |
319 | 36 | if (value) { |
320 | 36 | m_count = value->GetValueAsUnsigned(UINT32_MAX); |
321 | 36 | } |
322 | 36 | } |
323 | 36 | if (m_count != UINT32_MAX) { |
324 | 36 | return m_count; |
325 | 36 | } else { |
326 | 0 | uint64_t next_val = m_head->GetValueAsUnsigned(0); |
327 | 0 | uint64_t prev_val = m_tail->GetValueAsUnsigned(0); |
328 | 0 | if (next_val == 0 || prev_val == 0) |
329 | 0 | return 0; |
330 | 0 | if (next_val == m_node_address) |
331 | 0 | return 0; |
332 | 0 | if (next_val == prev_val) |
333 | 0 | return 1; |
334 | 0 | uint64_t size = 2; |
335 | 0 | ListEntry current(m_head); |
336 | 0 | while (current.next() && current.next().value() != m_node_address) { |
337 | 0 | size++; |
338 | 0 | current = current.next(); |
339 | 0 | if (size > m_list_capping_size) |
340 | 0 | break; |
341 | 0 | } |
342 | 0 | return m_count = (size - 1); |
343 | 0 | } |
344 | 36 | } |
345 | | |
346 | 118 | lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(size_t idx) { |
347 | 118 | static ConstString g_value("__value_"); |
348 | 118 | static ConstString g_next("__next_"); |
349 | | |
350 | 118 | if (idx >= CalculateNumChildren()) |
351 | 0 | return lldb::ValueObjectSP(); |
352 | | |
353 | 118 | if (!m_head || !m_tail || m_node_address == 0) |
354 | 0 | return lldb::ValueObjectSP(); |
355 | | |
356 | 118 | if (HasLoop(idx + 1)) |
357 | 0 | return lldb::ValueObjectSP(); |
358 | | |
359 | 118 | ValueObjectSP current_sp = GetItem(idx); |
360 | 118 | if (!current_sp) |
361 | 0 | return lldb::ValueObjectSP(); |
362 | | |
363 | 118 | current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child |
364 | 118 | if (!current_sp) |
365 | 0 | return lldb::ValueObjectSP(); |
366 | | |
367 | 118 | if (current_sp->GetName() == g_next) { |
368 | 118 | ProcessSP process_sp(current_sp->GetProcessSP()); |
369 | 118 | if (!process_sp) |
370 | 0 | return lldb::ValueObjectSP(); |
371 | | |
372 | | // if we grabbed the __next_ pointer, then the child is one pointer deep-er |
373 | 118 | lldb::addr_t addr = current_sp->GetParent()->GetPointerValue(); |
374 | 118 | addr = addr + 2 * process_sp->GetAddressByteSize(); |
375 | 118 | ExecutionContext exe_ctx(process_sp); |
376 | 118 | current_sp = |
377 | 118 | CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type); |
378 | 118 | if (!current_sp) |
379 | 0 | return lldb::ValueObjectSP(); |
380 | 118 | } |
381 | | |
382 | | // we need to copy current_sp into a new object otherwise we will end up with |
383 | | // all items named __value_ |
384 | 118 | DataExtractor data; |
385 | 118 | Status error; |
386 | 118 | current_sp->GetData(data, error); |
387 | 118 | if (error.Fail()) |
388 | 0 | return lldb::ValueObjectSP(); |
389 | | |
390 | 118 | StreamString name; |
391 | 118 | name.Printf("[%" PRIu64 "]", (uint64_t)idx); |
392 | 118 | return CreateValueObjectFromData(name.GetString(), data, |
393 | 118 | m_backend.GetExecutionContextRef(), |
394 | 118 | m_element_type); |
395 | 118 | } |
396 | | |
397 | 72 | bool ListFrontEnd::Update() { |
398 | 72 | AbstractListFrontEnd::Update(); |
399 | 72 | m_tail = nullptr; |
400 | 72 | m_node_address = 0; |
401 | | |
402 | 72 | Status err; |
403 | 72 | ValueObjectSP backend_addr(m_backend.AddressOf(err)); |
404 | 72 | if (err.Fail() || !backend_addr) |
405 | 0 | return false; |
406 | 72 | m_node_address = backend_addr->GetValueAsUnsigned(0); |
407 | 72 | if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) |
408 | 0 | return false; |
409 | 72 | ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__end_")); |
410 | 72 | if (!impl_sp) |
411 | 0 | return false; |
412 | 72 | m_head = impl_sp->GetChildMemberWithName("__next_").get(); |
413 | 72 | m_tail = impl_sp->GetChildMemberWithName("__prev_").get(); |
414 | 72 | return false; |
415 | 72 | } |
416 | | |
417 | | SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator( |
418 | 36 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
419 | 36 | return (valobj_sp ? new ListFrontEnd(valobj_sp) : nullptr0 ); |
420 | 36 | } |
421 | | |
422 | | SyntheticChildrenFrontEnd * |
423 | | formatters::LibcxxStdForwardListSyntheticFrontEndCreator( |
424 | 28 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
425 | 28 | return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr0 ; |
426 | 28 | } |