/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
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 | | class MapEntry { |
26 | | public: |
27 | 344 | MapEntry() = default; |
28 | 672 | explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} |
29 | | explicit MapEntry(ValueObject *entry) |
30 | 344 | : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()0 ) {} |
31 | | |
32 | 326 | ValueObjectSP left() const { |
33 | 326 | static ConstString g_left("__left_"); |
34 | 326 | if (!m_entry_sp) |
35 | 0 | return m_entry_sp; |
36 | 326 | return m_entry_sp->GetSyntheticChildAtOffset( |
37 | 326 | 0, m_entry_sp->GetCompilerType(), true); |
38 | 326 | } |
39 | | |
40 | 268 | ValueObjectSP right() const { |
41 | 268 | static ConstString g_right("__right_"); |
42 | 268 | if (!m_entry_sp) |
43 | 0 | return m_entry_sp; |
44 | 268 | return m_entry_sp->GetSyntheticChildAtOffset( |
45 | 268 | m_entry_sp->GetProcessSP()->GetAddressByteSize(), |
46 | 268 | m_entry_sp->GetCompilerType(), true); |
47 | 268 | } |
48 | | |
49 | 272 | ValueObjectSP parent() const { |
50 | 272 | static ConstString g_parent("__parent_"); |
51 | 272 | if (!m_entry_sp) |
52 | 0 | return m_entry_sp; |
53 | 272 | return m_entry_sp->GetSyntheticChildAtOffset( |
54 | 272 | 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(), |
55 | 272 | m_entry_sp->GetCompilerType(), true); |
56 | 272 | } |
57 | | |
58 | 1.55k | uint64_t value() const { |
59 | 1.55k | if (!m_entry_sp) |
60 | 0 | return 0; |
61 | 1.55k | return m_entry_sp->GetValueAsUnsigned(0); |
62 | 1.55k | } |
63 | | |
64 | 58 | bool error() const { |
65 | 58 | if (!m_entry_sp) |
66 | 0 | return true; |
67 | 58 | return m_entry_sp->GetError().Fail(); |
68 | 58 | } |
69 | | |
70 | 1.28k | bool null() const { return (value() == 0); } |
71 | | |
72 | 344 | ValueObjectSP GetEntry() const { return m_entry_sp; } |
73 | | |
74 | 194 | void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } |
75 | | |
76 | 0 | bool operator==(const MapEntry &rhs) const { |
77 | 0 | return (rhs.m_entry_sp.get() == m_entry_sp.get()); |
78 | 0 | } |
79 | | |
80 | | private: |
81 | | ValueObjectSP m_entry_sp; |
82 | | }; |
83 | | |
84 | | class MapIterator { |
85 | | public: |
86 | 344 | MapIterator() = default; |
87 | | MapIterator(MapEntry entry, size_t depth = 0) |
88 | 0 | : m_entry(std::move(entry)), m_max_depth(depth), m_error(false) {} |
89 | | MapIterator(ValueObjectSP entry, size_t depth = 0) |
90 | 0 | : m_entry(std::move(entry)), m_max_depth(depth), m_error(false) {} |
91 | | MapIterator(const MapIterator &rhs) |
92 | 0 | : m_entry(rhs.m_entry), m_max_depth(rhs.m_max_depth), m_error(false) {} |
93 | | MapIterator(ValueObject *entry, size_t depth = 0) |
94 | 344 | : m_entry(entry), m_max_depth(depth), m_error(false) {} |
95 | | |
96 | 598 | MapIterator &operator=(const MapIterator &) = default; |
97 | | |
98 | 0 | ValueObjectSP value() { return m_entry.GetEntry(); } |
99 | | |
100 | 344 | ValueObjectSP advance(size_t count) { |
101 | 344 | ValueObjectSP fail; |
102 | 344 | if (m_error) |
103 | 0 | return fail; |
104 | 344 | size_t steps = 0; |
105 | 612 | while (count > 0) { |
106 | 268 | next(); |
107 | 268 | count--, steps++; |
108 | 268 | if (m_error || m_entry.null() || (steps > m_max_depth)) |
109 | 0 | return fail; |
110 | 268 | } |
111 | 344 | return m_entry.GetEntry(); |
112 | 344 | } |
113 | | |
114 | | protected: |
115 | 268 | void next() { |
116 | 268 | if (m_entry.null()) |
117 | 0 | return; |
118 | 268 | MapEntry right(m_entry.right()); |
119 | 268 | if (!right.null()) { |
120 | 150 | m_entry = tree_min(std::move(right)); |
121 | 150 | return; |
122 | 150 | } |
123 | 118 | size_t steps = 0; |
124 | 136 | while (!is_left_child(m_entry)) { |
125 | 18 | if (m_entry.error()) { |
126 | 0 | m_error = true; |
127 | 0 | return; |
128 | 0 | } |
129 | 18 | m_entry.SetEntry(m_entry.parent()); |
130 | 18 | steps++; |
131 | 18 | if (steps > m_max_depth) { |
132 | 0 | m_entry = MapEntry(); |
133 | 0 | return; |
134 | 0 | } |
135 | 18 | } |
136 | 118 | m_entry = MapEntry(m_entry.parent()); |
137 | 118 | } |
138 | | |
139 | | private: |
140 | 150 | MapEntry tree_min(MapEntry x) { |
141 | 150 | if (x.null()) |
142 | 0 | return MapEntry(); |
143 | 150 | MapEntry left(x.left()); |
144 | 150 | size_t steps = 0; |
145 | 190 | while (!left.null()) { |
146 | 40 | if (left.error()) { |
147 | 0 | m_error = true; |
148 | 0 | return MapEntry(); |
149 | 0 | } |
150 | 40 | x = left; |
151 | 40 | left.SetEntry(x.left()); |
152 | 40 | steps++; |
153 | 40 | if (steps > m_max_depth) |
154 | 0 | return MapEntry(); |
155 | 40 | } |
156 | 150 | return x; |
157 | 150 | } |
158 | | |
159 | 136 | bool is_left_child(const MapEntry &x) { |
160 | 136 | if (x.null()) |
161 | 0 | return false; |
162 | 136 | MapEntry rhs(x.parent()); |
163 | 136 | rhs.SetEntry(rhs.left()); |
164 | 136 | return x.value() == rhs.value(); |
165 | 136 | } |
166 | | |
167 | | MapEntry m_entry; |
168 | | size_t m_max_depth = 0; |
169 | | bool m_error = false; |
170 | | }; |
171 | | |
172 | | namespace lldb_private { |
173 | | namespace formatters { |
174 | | class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
175 | | public: |
176 | | LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
177 | | |
178 | 62 | ~LibcxxStdMapSyntheticFrontEnd() override = default; |
179 | | |
180 | | size_t CalculateNumChildren() override; |
181 | | |
182 | | lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; |
183 | | |
184 | | bool Update() override; |
185 | | |
186 | | bool MightHaveChildren() override; |
187 | | |
188 | | size_t GetIndexOfChildWithName(ConstString name) override; |
189 | | |
190 | | private: |
191 | | bool GetDataType(); |
192 | | |
193 | | void GetValueOffset(const lldb::ValueObjectSP &node); |
194 | | |
195 | | ValueObject *m_tree = nullptr; |
196 | | ValueObject *m_root_node = nullptr; |
197 | | CompilerType m_element_type; |
198 | | uint32_t m_skip_size = UINT32_MAX; |
199 | | size_t m_count = UINT32_MAX; |
200 | | std::map<size_t, MapIterator> m_iterators; |
201 | | }; |
202 | | } // namespace formatters |
203 | | } // namespace lldb_private |
204 | | |
205 | | lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: |
206 | | LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
207 | 146 | : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_iterators() { |
208 | 146 | if (valobj_sp) |
209 | 146 | Update(); |
210 | 146 | } |
211 | | |
212 | | size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: |
213 | 830 | CalculateNumChildren() { |
214 | 830 | if (m_count != UINT32_MAX) |
215 | 686 | return m_count; |
216 | 144 | if (m_tree == nullptr) |
217 | 0 | return 0; |
218 | 144 | ValueObjectSP m_item(m_tree->GetChildMemberWithName("__pair3_")); |
219 | 144 | if (!m_item) |
220 | 0 | return 0; |
221 | | |
222 | 144 | switch (m_item->GetCompilerType().GetNumDirectBaseClasses()) { |
223 | 0 | case 1: |
224 | | // Assume a pre llvm r300140 __compressed_pair implementation: |
225 | 0 | m_item = m_item->GetChildMemberWithName("__first_"); |
226 | 0 | break; |
227 | 144 | case 2: { |
228 | | // Assume a post llvm r300140 __compressed_pair implementation: |
229 | 144 | ValueObjectSP first_elem_parent = m_item->GetChildAtIndex(0); |
230 | 144 | m_item = first_elem_parent->GetChildMemberWithName("__value_"); |
231 | 144 | break; |
232 | 0 | } |
233 | 0 | default: |
234 | 0 | return false; |
235 | 144 | } |
236 | | |
237 | 144 | if (!m_item) |
238 | 0 | return 0; |
239 | 144 | m_count = m_item->GetValueAsUnsigned(0); |
240 | 144 | return m_count; |
241 | 144 | } |
242 | | |
243 | 344 | bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() { |
244 | 344 | if (m_element_type.IsValid()) |
245 | 260 | return true; |
246 | 84 | m_element_type.Clear(); |
247 | 84 | ValueObjectSP deref; |
248 | 84 | Status error; |
249 | 84 | deref = m_root_node->Dereference(error); |
250 | 84 | if (!deref || error.Fail()) |
251 | 0 | return false; |
252 | 84 | deref = deref->GetChildMemberWithName("__value_"); |
253 | 84 | if (deref) { |
254 | 0 | m_element_type = deref->GetCompilerType(); |
255 | 0 | return true; |
256 | 0 | } |
257 | 84 | deref = m_backend.GetChildAtNamePath({"__tree_", "__pair3_"}); |
258 | 84 | if (!deref) |
259 | 0 | return false; |
260 | 84 | m_element_type = deref->GetCompilerType() |
261 | 84 | .GetTypeTemplateArgument(1) |
262 | 84 | .GetTypeTemplateArgument(1); |
263 | 84 | if (m_element_type) { |
264 | 50 | std::string name; |
265 | 50 | uint64_t bit_offset_ptr; |
266 | 50 | uint32_t bitfield_bit_size_ptr; |
267 | 50 | bool is_bitfield_ptr; |
268 | 50 | m_element_type = m_element_type.GetFieldAtIndex( |
269 | 50 | 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); |
270 | 50 | m_element_type = m_element_type.GetTypedefedType(); |
271 | 50 | return m_element_type.IsValid(); |
272 | 50 | } else { |
273 | 34 | m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); |
274 | 34 | return m_element_type.IsValid(); |
275 | 34 | } |
276 | 84 | } |
277 | | |
278 | | void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset( |
279 | 84 | const lldb::ValueObjectSP &node) { |
280 | 84 | if (m_skip_size != UINT32_MAX) |
281 | 0 | return; |
282 | 84 | if (!node) |
283 | 0 | return; |
284 | 84 | CompilerType node_type(node->GetCompilerType()); |
285 | 84 | uint64_t bit_offset; |
286 | 84 | if (node_type.GetIndexOfFieldWithName("__value_", nullptr, &bit_offset) != |
287 | 84 | UINT32_MAX) { |
288 | 0 | m_skip_size = bit_offset / 8u; |
289 | 84 | } else { |
290 | 84 | auto ast_ctx = node_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>(); |
291 | 84 | if (!ast_ctx) |
292 | 0 | return; |
293 | 84 | CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( |
294 | 84 | llvm::StringRef(), |
295 | 84 | {{"ptr0", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, |
296 | 84 | {"ptr1", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, |
297 | 84 | {"ptr2", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, |
298 | 84 | {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, |
299 | 84 | {"payload", (m_element_type.GetCompleteType(), m_element_type)}}); |
300 | 84 | std::string child_name; |
301 | 84 | uint32_t child_byte_size; |
302 | 84 | int32_t child_byte_offset = 0; |
303 | 84 | uint32_t child_bitfield_bit_size; |
304 | 84 | uint32_t child_bitfield_bit_offset; |
305 | 84 | bool child_is_base_class; |
306 | 84 | bool child_is_deref_of_parent; |
307 | 84 | uint64_t language_flags; |
308 | 84 | if (tree_node_type |
309 | 84 | .GetChildCompilerTypeAtIndex( |
310 | 84 | nullptr, 4, true, true, true, child_name, child_byte_size, |
311 | 84 | child_byte_offset, child_bitfield_bit_size, |
312 | 84 | child_bitfield_bit_offset, child_is_base_class, |
313 | 84 | child_is_deref_of_parent, nullptr, language_flags) |
314 | 84 | .IsValid()) |
315 | 84 | m_skip_size = (uint32_t)child_byte_offset; |
316 | 84 | } |
317 | 84 | } |
318 | | |
319 | | lldb::ValueObjectSP |
320 | | lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex( |
321 | 344 | size_t idx) { |
322 | 344 | static ConstString g_cc_("__cc_"), g_cc("__cc"); |
323 | 344 | static ConstString g_nc("__nc"); |
324 | | |
325 | 344 | if (idx >= CalculateNumChildren()) |
326 | 0 | return lldb::ValueObjectSP(); |
327 | 344 | if (m_tree == nullptr || m_root_node == nullptr) |
328 | 0 | return lldb::ValueObjectSP(); |
329 | | |
330 | 344 | MapIterator iterator(m_root_node, CalculateNumChildren()); |
331 | | |
332 | 344 | const bool need_to_skip = (idx > 0); |
333 | 344 | size_t actual_advancde = idx; |
334 | 344 | if (need_to_skip) { |
335 | 260 | auto cached_iterator = m_iterators.find(idx - 1); |
336 | 260 | if (cached_iterator != m_iterators.end()) { |
337 | 254 | iterator = cached_iterator->second; |
338 | 254 | actual_advancde = 1; |
339 | 254 | } |
340 | 260 | } |
341 | | |
342 | 344 | ValueObjectSP iterated_sp(iterator.advance(actual_advancde)); |
343 | 344 | if (!iterated_sp) { |
344 | | // this tree is garbage - stop |
345 | 0 | m_tree = |
346 | 0 | nullptr; // this will stop all future searches until an Update() happens |
347 | 0 | return iterated_sp; |
348 | 0 | } |
349 | 344 | if (GetDataType()) { |
350 | 344 | if (!need_to_skip) { |
351 | 84 | Status error; |
352 | 84 | iterated_sp = iterated_sp->Dereference(error); |
353 | 84 | if (!iterated_sp || error.Fail()) { |
354 | 0 | m_tree = nullptr; |
355 | 0 | return lldb::ValueObjectSP(); |
356 | 0 | } |
357 | 84 | GetValueOffset(iterated_sp); |
358 | 84 | auto child_sp = iterated_sp->GetChildMemberWithName("__value_"); |
359 | 84 | if (child_sp) |
360 | 0 | iterated_sp = child_sp; |
361 | 84 | else |
362 | 84 | iterated_sp = iterated_sp->GetSyntheticChildAtOffset( |
363 | 84 | m_skip_size, m_element_type, true); |
364 | 84 | if (!iterated_sp) { |
365 | 0 | m_tree = nullptr; |
366 | 0 | return lldb::ValueObjectSP(); |
367 | 0 | } |
368 | 260 | } else { |
369 | | // because of the way our debug info is made, we need to read item 0 |
370 | | // first so that we can cache information used to generate other elements |
371 | 260 | if (m_skip_size == UINT32_MAX) |
372 | 4 | GetChildAtIndex(0); |
373 | 260 | if (m_skip_size == UINT32_MAX) { |
374 | 0 | m_tree = nullptr; |
375 | 0 | return lldb::ValueObjectSP(); |
376 | 0 | } |
377 | 260 | iterated_sp = iterated_sp->GetSyntheticChildAtOffset( |
378 | 260 | m_skip_size, m_element_type, true); |
379 | 260 | if (!iterated_sp) { |
380 | 0 | m_tree = nullptr; |
381 | 0 | return lldb::ValueObjectSP(); |
382 | 0 | } |
383 | 260 | } |
384 | 344 | } else { |
385 | 0 | m_tree = nullptr; |
386 | 0 | return lldb::ValueObjectSP(); |
387 | 0 | } |
388 | | // at this point we have a valid |
389 | | // we need to copy current_sp into a new object otherwise we will end up with |
390 | | // all items named __value_ |
391 | 344 | StreamString name; |
392 | 344 | name.Printf("[%" PRIu64 "]", (uint64_t)idx); |
393 | 344 | auto potential_child_sp = iterated_sp->Clone(ConstString(name.GetString())); |
394 | 344 | if (potential_child_sp) { |
395 | 344 | switch (potential_child_sp->GetNumChildren()) { |
396 | 60 | case 1: { |
397 | 60 | auto child0_sp = potential_child_sp->GetChildAtIndex(0); |
398 | 60 | if (child0_sp && |
399 | 60 | (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc)) |
400 | 0 | potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); |
401 | 60 | break; |
402 | 0 | } |
403 | 190 | case 2: { |
404 | 190 | auto child0_sp = potential_child_sp->GetChildAtIndex(0); |
405 | 190 | auto child1_sp = potential_child_sp->GetChildAtIndex(1); |
406 | 190 | if (child0_sp && |
407 | 190 | (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc) && |
408 | 190 | child1_sp0 && child1_sp->GetName() == g_nc0 ) |
409 | 0 | potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); |
410 | 190 | break; |
411 | 0 | } |
412 | 344 | } |
413 | 344 | } |
414 | 344 | m_iterators[idx] = iterator; |
415 | 344 | return potential_child_sp; |
416 | 344 | } |
417 | | |
418 | 290 | bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { |
419 | 290 | m_count = UINT32_MAX; |
420 | 290 | m_tree = m_root_node = nullptr; |
421 | 290 | m_iterators.clear(); |
422 | 290 | m_tree = m_backend.GetChildMemberWithName("__tree_").get(); |
423 | 290 | if (!m_tree) |
424 | 2 | return false; |
425 | 288 | m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get(); |
426 | 288 | return false; |
427 | 290 | } |
428 | | |
429 | | bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: |
430 | 68 | MightHaveChildren() { |
431 | 68 | return true; |
432 | 68 | } |
433 | | |
434 | | size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: |
435 | 2 | GetIndexOfChildWithName(ConstString name) { |
436 | 2 | return ExtractIndexFromString(name.GetCString()); |
437 | 2 | } |
438 | | |
439 | | SyntheticChildrenFrontEnd * |
440 | | lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator( |
441 | 146 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
442 | 146 | return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr0 ); |
443 | 146 | } |