1 | //===-- NSSet.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 "NSSet.h" |
10 | #include "CFBasicHash.h" |
11 | |
12 | #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" |
13 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
14 | #include "lldb/DataFormatters/FormattersHelpers.h" |
15 | #include "lldb/Target/Language.h" |
16 | #include "lldb/Target/Target.h" |
17 | #include "lldb/Utility/DataBufferHeap.h" |
18 | #include "lldb/Utility/Endian.h" |
19 | #include "lldb/Utility/Status.h" |
20 | #include "lldb/Utility/Stream.h" |
21 | #include "lldb/ValueObject/ValueObject.h" |
22 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | using namespace lldb_private::formatters; |
27 | |
28 | std::map<ConstString, CXXFunctionSummaryFormat::Callback> & |
29 | NSSet_Additionals::GetAdditionalSummaries() { |
30 | static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; |
31 | return g_map; |
32 | } |
33 | |
34 | std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & |
35 | NSSet_Additionals::GetAdditionalSynthetics() { |
36 | static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> |
37 | g_map; |
38 | return g_map; |
39 | } |
40 | |
41 | namespace lldb_private { |
42 | namespace formatters { |
43 | class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
44 | public: |
45 | NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
46 | |
47 | ~NSSetISyntheticFrontEnd() override; |
48 | |
49 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
50 | |
51 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
52 | |
53 | lldb::ChildCacheState Update() override; |
54 | |
55 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; |
56 | |
57 | private: |
58 | struct DataDescriptor_32 { |
59 | uint32_t _used : 26; |
60 | uint32_t _szidx : 6; |
61 | }; |
62 | |
63 | struct DataDescriptor_64 { |
64 | uint64_t _used : 58; |
65 | uint32_t _szidx : 6; |
66 | }; |
67 | |
68 | struct SetItemDescriptor { |
69 | lldb::addr_t item_ptr; |
70 | lldb::ValueObjectSP valobj_sp; |
71 | }; |
72 | |
73 | ExecutionContextRef m_exe_ctx_ref; |
74 | uint8_t m_ptr_size = 8; |
75 | DataDescriptor_32 *m_data_32 = nullptr; |
76 | DataDescriptor_64 *m_data_64 = nullptr; |
77 | lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS; |
78 | std::vector<SetItemDescriptor> m_children; |
79 | }; |
80 | |
81 | class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
82 | public: |
83 | NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
84 | |
85 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
86 | |
87 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
88 | |
89 | lldb::ChildCacheState Update() override; |
90 | |
91 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; |
92 | |
93 | private: |
94 | struct SetItemDescriptor { |
95 | lldb::addr_t item_ptr; |
96 | lldb::ValueObjectSP valobj_sp; |
97 | }; |
98 | |
99 | ExecutionContextRef m_exe_ctx_ref; |
100 | uint8_t m_ptr_size = 8; |
101 | lldb::ByteOrder m_order = lldb::eByteOrderInvalid; |
102 | |
103 | CFBasicHash m_hashtable; |
104 | |
105 | CompilerType m_pair_type; |
106 | std::vector<SetItemDescriptor> m_children; |
107 | }; |
108 | |
109 | template <typename D32, typename D64> |
110 | class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
111 | public: |
112 | GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); |
113 | |
114 | ~GenericNSSetMSyntheticFrontEnd() override; |
115 | |
116 | llvm::Expected<uint32_t> CalculateNumChildren() override; |
117 | |
118 | lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
119 | |
120 | lldb::ChildCacheState Update() override; |
121 | |
122 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; |
123 | |
124 | private: |
125 | |
126 | struct SetItemDescriptor { |
127 | lldb::addr_t item_ptr; |
128 | lldb::ValueObjectSP valobj_sp; |
129 | }; |
130 | |
131 | ExecutionContextRef m_exe_ctx_ref; |
132 | uint8_t m_ptr_size = 8; |
133 | D32 *m_data_32; |
134 | D64 *m_data_64; |
135 | std::vector<SetItemDescriptor> m_children; |
136 | }; |
137 | |
138 | namespace Foundation1300 { |
139 | struct DataDescriptor_32 { |
140 | uint32_t _used : 26; |
141 | uint32_t _size; |
142 | uint32_t _mutations; |
143 | uint32_t _objs_addr; |
144 | }; |
145 | |
146 | struct DataDescriptor_64 { |
147 | uint64_t _used : 58; |
148 | uint64_t _size; |
149 | uint64_t _mutations; |
150 | uint64_t _objs_addr; |
151 | }; |
152 | |
153 | using NSSetMSyntheticFrontEnd = |
154 | GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
155 | } |
156 | |
157 | namespace Foundation1428 { |
158 | struct DataDescriptor_32 { |
159 | uint32_t _used : 26; |
160 | uint32_t _size; |
161 | uint32_t _objs_addr; |
162 | uint32_t _mutations; |
163 | }; |
164 | |
165 | struct DataDescriptor_64 { |
166 | uint64_t _used : 58; |
167 | uint64_t _size; |
168 | uint64_t _objs_addr; |
169 | uint64_t _mutations; |
170 | }; |
171 | |
172 | using NSSetMSyntheticFrontEnd = |
173 | GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
174 | } |
175 | |
176 | namespace Foundation1437 { |
177 | struct DataDescriptor_32 { |
178 | uint32_t _cow; |
179 | // __table storage |
180 | uint32_t _objs_addr; |
181 | uint32_t _muts; |
182 | uint32_t _used : 26; |
183 | uint32_t _szidx : 6; |
184 | }; |
185 | |
186 | struct DataDescriptor_64 { |
187 | uint64_t _cow; |
188 | // __Table storage |
189 | uint64_t _objs_addr; |
190 | uint32_t _muts; |
191 | uint32_t _used : 26; |
192 | uint32_t _szidx : 6; |
193 | }; |
194 | |
195 | using NSSetMSyntheticFrontEnd = |
196 | GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; |
197 | |
198 | template <typename DD> |
199 | uint64_t |
200 | __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, |
201 | Status &error) { |
202 | const lldb::addr_t start_of_descriptor = |
203 | valobj_addr + process.GetAddressByteSize(); |
204 | DD descriptor = DD(); |
205 | process.ReadMemory(vm_addr: start_of_descriptor, buf: &descriptor, size: sizeof(descriptor), |
206 | error); |
207 | if (error.Fail()) { |
208 | return 0; |
209 | } |
210 | return descriptor._used; |
211 | } |
212 | |
213 | uint64_t |
214 | __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, |
215 | Status &error) { |
216 | if (process.GetAddressByteSize() == 4) { |
217 | return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error); |
218 | } else { |
219 | return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error); |
220 | } |
221 | } |
222 | } // namespace Foundation1437 |
223 | } // namespace formatters |
224 | } // namespace lldb_private |
225 | |
226 | template <bool cf_style> |
227 | bool lldb_private::formatters::NSSetSummaryProvider( |
228 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
229 | static constexpr llvm::StringLiteral g_TypeHint("NSSet"); |
230 | |
231 | ProcessSP process_sp = valobj.GetProcessSP(); |
232 | if (!process_sp) |
233 | return false; |
234 | |
235 | ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(process&: *process_sp); |
236 | |
237 | if (!runtime) |
238 | return false; |
239 | |
240 | ObjCLanguageRuntime::ClassDescriptorSP descriptor( |
241 | runtime->GetClassDescriptor(in_value&: valobj)); |
242 | |
243 | if (!descriptor || !descriptor->IsValid()) |
244 | return false; |
245 | |
246 | uint32_t ptr_size = process_sp->GetAddressByteSize(); |
247 | bool is_64bit = (ptr_size == 8); |
248 | |
249 | lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(fail_value: 0); |
250 | |
251 | if (!valobj_addr) |
252 | return false; |
253 | |
254 | uint64_t value = 0; |
255 | |
256 | ConstString class_name(descriptor->GetClassName()); |
257 | |
258 | static const ConstString g_SetI("__NSSetI"); |
259 | static const ConstString g_OrderedSetI("__NSOrderedSetI"); |
260 | static const ConstString g_SetM("__NSSetM"); |
261 | static const ConstString g_SetCF("__NSCFSet"); |
262 | static const ConstString g_SetCFRef("CFSetRef"); |
263 | |
264 | if (class_name.IsEmpty()) |
265 | return false; |
266 | |
267 | if (class_name == g_SetI || class_name == g_OrderedSetI) { |
268 | Status error; |
269 | value = process_sp->ReadUnsignedIntegerFromMemory(load_addr: valobj_addr + ptr_size, |
270 | byte_size: ptr_size, fail_value: 0, error); |
271 | if (error.Fail()) |
272 | return false; |
273 | value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); |
274 | } else if (class_name == g_SetM) { |
275 | AppleObjCRuntime *apple_runtime = |
276 | llvm::dyn_cast_or_null<AppleObjCRuntime>(Val: runtime); |
277 | Status error; |
278 | if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { |
279 | value = Foundation1437::__NSSetMSize(process&: *process_sp, valobj_addr, error); |
280 | } else { |
281 | value = process_sp->ReadUnsignedIntegerFromMemory(load_addr: valobj_addr + ptr_size, |
282 | byte_size: ptr_size, fail_value: 0, error); |
283 | value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); |
284 | } |
285 | if (error.Fail()) |
286 | return false; |
287 | } else if (class_name == g_SetCF || class_name == g_SetCFRef) { |
288 | ExecutionContext exe_ctx(process_sp); |
289 | CFBasicHash cfbh; |
290 | if (!cfbh.Update(addr: valobj_addr, exe_ctx_rf: exe_ctx)) |
291 | return false; |
292 | value = cfbh.GetCount(); |
293 | } else { |
294 | auto &map(NSSet_Additionals::GetAdditionalSummaries()); |
295 | auto iter = map.find(x: class_name), end = map.end(); |
296 | if (iter != end) |
297 | return iter->second(valobj, stream, options); |
298 | else |
299 | return false; |
300 | } |
301 | |
302 | llvm::StringRef prefix, suffix; |
303 | if (Language *language = Language::FindPlugin(language: options.GetLanguage())) |
304 | std::tie(args&: prefix, args&: suffix) = language->GetFormatterPrefixSuffix(type_hint: g_TypeHint); |
305 | |
306 | stream << prefix; |
307 | stream.Printf(format: "%"PRIu64 " %s%s", value, "element", value == 1 ? "": "s"); |
308 | stream << suffix; |
309 | return true; |
310 | } |
311 | |
312 | SyntheticChildrenFrontEnd * |
313 | lldb_private::formatters::NSSetSyntheticFrontEndCreator( |
314 | CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { |
315 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
316 | if (!process_sp) |
317 | return nullptr; |
318 | ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(process&: *process_sp); |
319 | if (!runtime) |
320 | return nullptr; |
321 | |
322 | CompilerType valobj_type(valobj_sp->GetCompilerType()); |
323 | Flags flags(valobj_type.GetTypeInfo()); |
324 | |
325 | if (flags.IsClear(bit: eTypeIsPointer)) { |
326 | Status error; |
327 | valobj_sp = valobj_sp->AddressOf(error); |
328 | if (error.Fail() || !valobj_sp) |
329 | return nullptr; |
330 | } |
331 | |
332 | ObjCLanguageRuntime::ClassDescriptorSP descriptor( |
333 | runtime->GetClassDescriptor(in_value&: *valobj_sp)); |
334 | |
335 | if (!descriptor || !descriptor->IsValid()) |
336 | return nullptr; |
337 | |
338 | ConstString class_name = descriptor->GetClassName(); |
339 | |
340 | static const ConstString g_SetI("__NSSetI"); |
341 | static const ConstString g_OrderedSetI("__NSOrderedSetI"); |
342 | static const ConstString g_SetM("__NSSetM"); |
343 | static const ConstString g_SetCF("__NSCFSet"); |
344 | static const ConstString g_SetCFRef("CFSetRef"); |
345 | |
346 | if (class_name.IsEmpty()) |
347 | return nullptr; |
348 | |
349 | if (class_name == g_SetI || class_name == g_OrderedSetI) { |
350 | return (new NSSetISyntheticFrontEnd(valobj_sp)); |
351 | } else if (class_name == g_SetM) { |
352 | AppleObjCRuntime *apple_runtime = |
353 | llvm::dyn_cast_or_null<AppleObjCRuntime>(Val: runtime); |
354 | if (apple_runtime) { |
355 | if (apple_runtime->GetFoundationVersion() >= 1437) |
356 | return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp)); |
357 | else if (apple_runtime->GetFoundationVersion() >= 1428) |
358 | return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp)); |
359 | else |
360 | return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); |
361 | } else { |
362 | return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); |
363 | } |
364 | } else if (class_name == g_SetCF || class_name == g_SetCFRef) { |
365 | return (new NSCFSetSyntheticFrontEnd(valobj_sp)); |
366 | } else { |
367 | auto &map(NSSet_Additionals::GetAdditionalSynthetics()); |
368 | auto iter = map.find(x: class_name), end = map.end(); |
369 | if (iter != end) |
370 | return iter->second(synth, valobj_sp); |
371 | return nullptr; |
372 | } |
373 | } |
374 | |
375 | lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd( |
376 | lldb::ValueObjectSP valobj_sp) |
377 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref() { |
378 | if (valobj_sp) |
379 | Update(); |
380 | } |
381 | |
382 | lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { |
383 | delete m_data_32; |
384 | m_data_32 = nullptr; |
385 | delete m_data_64; |
386 | m_data_64 = nullptr; |
387 | } |
388 | |
389 | llvm::Expected<size_t> |
390 | lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( |
391 | ConstString name) { |
392 | auto optional_idx = ExtractIndexFromString(item_name: name.AsCString()); |
393 | if (!optional_idx) { |
394 | return llvm::createStringError(Fmt: "Type has no child named '%s'", |
395 | Vals: name.AsCString()); |
396 | } |
397 | uint32_t idx = *optional_idx; |
398 | if (idx >= CalculateNumChildrenIgnoringErrors()) |
399 | return llvm::createStringError(Fmt: "Type has no child named '%s'", |
400 | Vals: name.AsCString()); |
401 | return idx; |
402 | } |
403 | |
404 | llvm::Expected<uint32_t> |
405 | lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { |
406 | if (!m_data_32 && !m_data_64) |
407 | return 0; |
408 | return (m_data_32 ? m_data_32->_used : m_data_64->_used); |
409 | } |
410 | |
411 | lldb::ChildCacheState |
412 | lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { |
413 | m_children.clear(); |
414 | delete m_data_32; |
415 | m_data_32 = nullptr; |
416 | delete m_data_64; |
417 | m_data_64 = nullptr; |
418 | m_ptr_size = 0; |
419 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
420 | if (!valobj_sp) |
421 | return lldb::ChildCacheState::eRefetch; |
422 | if (!valobj_sp) |
423 | return lldb::ChildCacheState::eRefetch; |
424 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
425 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
426 | if (!process_sp) |
427 | return lldb::ChildCacheState::eRefetch; |
428 | m_ptr_size = process_sp->GetAddressByteSize(); |
429 | uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size; |
430 | Status error; |
431 | if (m_ptr_size == 4) { |
432 | m_data_32 = new DataDescriptor_32(); |
433 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(DataDescriptor_32), |
434 | error); |
435 | } else { |
436 | m_data_64 = new DataDescriptor_64(); |
437 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(DataDescriptor_64), |
438 | error); |
439 | } |
440 | if (error.Fail()) |
441 | return lldb::ChildCacheState::eRefetch; |
442 | m_data_ptr = data_location + m_ptr_size; |
443 | return lldb::ChildCacheState::eReuse; |
444 | } |
445 | |
446 | lldb::ValueObjectSP |
447 | lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex( |
448 | uint32_t idx) { |
449 | uint32_t num_children = CalculateNumChildrenIgnoringErrors(); |
450 | |
451 | if (idx >= num_children) |
452 | return lldb::ValueObjectSP(); |
453 | |
454 | ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); |
455 | if (!process_sp) |
456 | return lldb::ValueObjectSP(); |
457 | |
458 | if (m_children.empty()) { |
459 | // do the scan phase |
460 | lldb::addr_t obj_at_idx = 0; |
461 | |
462 | uint32_t tries = 0; |
463 | uint32_t test_idx = 0; |
464 | |
465 | while (tries < num_children) { |
466 | obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); |
467 | if (!process_sp) |
468 | return lldb::ValueObjectSP(); |
469 | Status error; |
470 | obj_at_idx = process_sp->ReadPointerFromMemory(vm_addr: obj_at_idx, error); |
471 | if (error.Fail()) |
472 | return lldb::ValueObjectSP(); |
473 | |
474 | test_idx++; |
475 | |
476 | if (!obj_at_idx) |
477 | continue; |
478 | tries++; |
479 | |
480 | SetItemDescriptor descriptor = {.item_ptr: obj_at_idx, .valobj_sp: lldb::ValueObjectSP()}; |
481 | |
482 | m_children.push_back(x: descriptor); |
483 | } |
484 | } |
485 | |
486 | if (idx >= m_children.size()) // should never happen |
487 | return lldb::ValueObjectSP(); |
488 | |
489 | SetItemDescriptor &set_item = m_children[idx]; |
490 | if (!set_item.valobj_sp) { |
491 | auto ptr_size = process_sp->GetAddressByteSize(); |
492 | DataBufferHeap buffer(ptr_size, 0); |
493 | switch (ptr_size) { |
494 | case 0: // architecture has no clue - fail |
495 | return lldb::ValueObjectSP(); |
496 | case 4: |
497 | *reinterpret_cast<uint32_t *>(buffer.GetBytes()) = |
498 | static_cast<uint32_t>(set_item.item_ptr); |
499 | break; |
500 | case 8: |
501 | *reinterpret_cast<uint64_t *>(buffer.GetBytes()) = |
502 | static_cast<uint64_t>(set_item.item_ptr); |
503 | break; |
504 | default: |
505 | lldbassert(false && "pointer size is not 4 nor 8"); |
506 | } |
507 | StreamString idx_name; |
508 | idx_name.Printf(format: "[%"PRIu64 "]", (uint64_t)idx); |
509 | |
510 | DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), |
511 | process_sp->GetByteOrder(), |
512 | process_sp->GetAddressByteSize()); |
513 | |
514 | set_item.valobj_sp = CreateValueObjectFromData( |
515 | name: idx_name.GetString(), data, exe_ctx: m_exe_ctx_ref, |
516 | type: m_backend.GetCompilerType().GetBasicTypeFromAST( |
517 | basic_type: lldb::eBasicTypeObjCID)); |
518 | } |
519 | return set_item.valobj_sp; |
520 | } |
521 | |
522 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( |
523 | lldb::ValueObjectSP valobj_sp) |
524 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), |
525 | m_pair_type() {} |
526 | |
527 | llvm::Expected<size_t> |
528 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( |
529 | ConstString name) { |
530 | auto optional_idx = ExtractIndexFromString(item_name: name.AsCString()); |
531 | if (!optional_idx) { |
532 | return llvm::createStringError(Fmt: "Type has no child named '%s'", |
533 | Vals: name.AsCString()); |
534 | } |
535 | uint32_t idx = *optional_idx; |
536 | if (idx >= CalculateNumChildrenIgnoringErrors()) |
537 | return llvm::createStringError(Fmt: "Type has no child named '%s'", |
538 | Vals: name.AsCString()); |
539 | return idx; |
540 | } |
541 | |
542 | llvm::Expected<uint32_t> |
543 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { |
544 | if (!m_hashtable.IsValid()) |
545 | return 0; |
546 | return m_hashtable.GetCount(); |
547 | } |
548 | |
549 | lldb::ChildCacheState |
550 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { |
551 | m_children.clear(); |
552 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
553 | m_ptr_size = 0; |
554 | if (!valobj_sp) |
555 | return lldb::ChildCacheState::eRefetch; |
556 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
557 | |
558 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
559 | if (!process_sp) |
560 | return lldb::ChildCacheState::eRefetch; |
561 | m_ptr_size = process_sp->GetAddressByteSize(); |
562 | m_order = process_sp->GetByteOrder(); |
563 | return m_hashtable.Update(addr: valobj_sp->GetValueAsUnsigned(fail_value: 0), exe_ctx_rf: m_exe_ctx_ref) |
564 | ? lldb::ChildCacheState::eReuse |
565 | : lldb::ChildCacheState::eRefetch; |
566 | } |
567 | |
568 | lldb::ValueObjectSP |
569 | lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( |
570 | uint32_t idx) { |
571 | lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); |
572 | |
573 | const uint32_t num_children = CalculateNumChildrenIgnoringErrors(); |
574 | |
575 | if (idx >= num_children) |
576 | return lldb::ValueObjectSP(); |
577 | |
578 | if (m_children.empty()) { |
579 | ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); |
580 | if (!process_sp) |
581 | return lldb::ValueObjectSP(); |
582 | |
583 | Status error; |
584 | lldb::addr_t val_at_idx = 0; |
585 | |
586 | uint32_t tries = 0; |
587 | uint32_t test_idx = 0; |
588 | |
589 | // Iterate over inferior memory, reading value pointers by shifting the |
590 | // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read |
591 | // fails, otherwise, continue until the number of tries matches the number |
592 | // of childen. |
593 | while (tries < num_children) { |
594 | val_at_idx = m_values_ptr + (test_idx * m_ptr_size); |
595 | |
596 | val_at_idx = process_sp->ReadPointerFromMemory(vm_addr: val_at_idx, error); |
597 | if (error.Fail()) |
598 | return lldb::ValueObjectSP(); |
599 | |
600 | test_idx++; |
601 | |
602 | if (!val_at_idx) |
603 | continue; |
604 | tries++; |
605 | |
606 | SetItemDescriptor descriptor = {.item_ptr: val_at_idx, .valobj_sp: lldb::ValueObjectSP()}; |
607 | |
608 | m_children.push_back(x: descriptor); |
609 | } |
610 | } |
611 | |
612 | if (idx >= m_children.size()) // should never happen |
613 | return lldb::ValueObjectSP(); |
614 | |
615 | SetItemDescriptor &set_item = m_children[idx]; |
616 | if (!set_item.valobj_sp) { |
617 | |
618 | WritableDataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0)); |
619 | |
620 | switch (m_ptr_size) { |
621 | case 0: // architecture has no clue - fail |
622 | return lldb::ValueObjectSP(); |
623 | case 4: |
624 | *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) = |
625 | static_cast<uint32_t>(set_item.item_ptr); |
626 | break; |
627 | case 8: |
628 | *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) = |
629 | static_cast<uint64_t>(set_item.item_ptr); |
630 | break; |
631 | default: |
632 | lldbassert(false && "pointer size is not 4 nor 8"); |
633 | } |
634 | StreamString idx_name; |
635 | idx_name.Printf(format: "[%"PRIu64 "]", (uint64_t)idx); |
636 | |
637 | DataExtractor data(buffer_sp, m_order, m_ptr_size); |
638 | |
639 | set_item.valobj_sp = CreateValueObjectFromData( |
640 | name: idx_name.GetString(), data, exe_ctx: m_exe_ctx_ref, |
641 | type: m_backend.GetCompilerType().GetBasicTypeFromAST( |
642 | basic_type: lldb::eBasicTypeObjCID)); |
643 | } |
644 | |
645 | return set_item.valobj_sp; |
646 | } |
647 | |
648 | template <typename D32, typename D64> |
649 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< |
650 | D32, D64>::GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
651 | : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), |
652 | m_data_32(nullptr), m_data_64(nullptr) { |
653 | if (valobj_sp) |
654 | Update(); |
655 | } |
656 | |
657 | template <typename D32, typename D64> |
658 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>:: |
659 | GenericNSSetMSyntheticFrontEnd::~GenericNSSetMSyntheticFrontEnd() { |
660 | delete m_data_32; |
661 | m_data_32 = nullptr; |
662 | delete m_data_64; |
663 | m_data_64 = nullptr; |
664 | } |
665 | |
666 | template <typename D32, typename D64> |
667 | llvm::Expected<size_t> lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< |
668 | D32, D64>::GetIndexOfChildWithName(ConstString name) { |
669 | auto optional_idx = ExtractIndexFromString(item_name: name.AsCString()); |
670 | if (!optional_idx) { |
671 | return llvm::createStringError(Fmt: "Type has no child named '%s'", |
672 | Vals: name.AsCString()); |
673 | } |
674 | uint32_t idx = *optional_idx; |
675 | if (idx >= CalculateNumChildrenIgnoringErrors()) |
676 | return llvm::createStringError(Fmt: "Type has no child named '%s'", |
677 | Vals: name.AsCString()); |
678 | return idx; |
679 | } |
680 | |
681 | template <typename D32, typename D64> |
682 | llvm::Expected<uint32_t> |
683 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< |
684 | D32, D64>::CalculateNumChildren() { |
685 | if (!m_data_32 && !m_data_64) |
686 | return 0; |
687 | return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used); |
688 | } |
689 | |
690 | template <typename D32, typename D64> |
691 | lldb::ChildCacheState |
692 | lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { |
693 | m_children.clear(); |
694 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
695 | m_ptr_size = 0; |
696 | delete m_data_32; |
697 | m_data_32 = nullptr; |
698 | delete m_data_64; |
699 | m_data_64 = nullptr; |
700 | if (!valobj_sp) |
701 | return lldb::ChildCacheState::eRefetch; |
702 | if (!valobj_sp) |
703 | return lldb::ChildCacheState::eRefetch; |
704 | m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); |
705 | lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
706 | if (!process_sp) |
707 | return lldb::ChildCacheState::eRefetch; |
708 | m_ptr_size = process_sp->GetAddressByteSize(); |
709 | uint64_t data_location = valobj_sp->GetValueAsUnsigned(fail_value: 0) + m_ptr_size; |
710 | Status error; |
711 | if (m_ptr_size == 4) { |
712 | m_data_32 = new D32(); |
713 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_32, size: sizeof(D32), |
714 | error); |
715 | } else { |
716 | m_data_64 = new D64(); |
717 | process_sp->ReadMemory(vm_addr: data_location, buf: m_data_64, size: sizeof(D64), |
718 | error); |
719 | } |
720 | return error.Success() ? lldb::ChildCacheState::eReuse |
721 | : lldb::ChildCacheState::eRefetch; |
722 | } |
723 | |
724 | template <typename D32, typename D64> |
725 | lldb::ValueObjectSP |
726 | lldb_private::formatters:: |
727 | GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(uint32_t idx) { |
728 | lldb::addr_t m_objs_addr = |
729 | (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); |
730 | |
731 | uint32_t num_children = CalculateNumChildrenIgnoringErrors(); |
732 | |
733 | if (idx >= num_children) |
734 | return lldb::ValueObjectSP(); |
735 | |
736 | ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); |
737 | if (!process_sp) |
738 | return lldb::ValueObjectSP(); |
739 | |
740 | if (m_children.empty()) { |
741 | // do the scan phase |
742 | lldb::addr_t obj_at_idx = 0; |
743 | |
744 | uint32_t tries = 0; |
745 | uint32_t test_idx = 0; |
746 | |
747 | while (tries < num_children) { |
748 | obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); |
749 | if (!process_sp) |
750 | return lldb::ValueObjectSP(); |
751 | Status error; |
752 | obj_at_idx = process_sp->ReadPointerFromMemory(vm_addr: obj_at_idx, error); |
753 | if (error.Fail()) |
754 | return lldb::ValueObjectSP(); |
755 | |
756 | test_idx++; |
757 | |
758 | if (!obj_at_idx) |
759 | continue; |
760 | tries++; |
761 | |
762 | SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; |
763 | |
764 | m_children.push_back(descriptor); |
765 | } |
766 | } |
767 | |
768 | if (idx >= m_children.size()) // should never happen |
769 | return lldb::ValueObjectSP(); |
770 | |
771 | SetItemDescriptor &set_item = m_children[idx]; |
772 | if (!set_item.valobj_sp) { |
773 | auto ptr_size = process_sp->GetAddressByteSize(); |
774 | DataBufferHeap buffer(ptr_size, 0); |
775 | switch (ptr_size) { |
776 | case 0: // architecture has no clue?? - fail |
777 | return lldb::ValueObjectSP(); |
778 | case 4: |
779 | *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; |
780 | break; |
781 | case 8: |
782 | *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; |
783 | break; |
784 | default: |
785 | assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); |
786 | } |
787 | StreamString idx_name; |
788 | idx_name.Printf(format: "[%"PRIu64 "]", (uint64_t)idx); |
789 | |
790 | DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), |
791 | process_sp->GetByteOrder(), |
792 | process_sp->GetAddressByteSize()); |
793 | |
794 | set_item.valobj_sp = CreateValueObjectFromData( |
795 | name: idx_name.GetString(), data, exe_ctx: m_exe_ctx_ref, |
796 | type: m_backend.GetCompilerType().GetBasicTypeFromAST( |
797 | lldb::eBasicTypeObjCID)); |
798 | } |
799 | return set_item.valobj_sp; |
800 | } |
801 | |
802 | template bool lldb_private::formatters::NSSetSummaryProvider<true>( |
803 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); |
804 | |
805 | template bool lldb_private::formatters::NSSetSummaryProvider<false>( |
806 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); |
807 |
Definitions
- GetAdditionalSummaries
- GetAdditionalSynthetics
- NSSetISyntheticFrontEnd
- DataDescriptor_32
- DataDescriptor_64
- SetItemDescriptor
- NSCFSetSyntheticFrontEnd
- SetItemDescriptor
- GenericNSSetMSyntheticFrontEnd
- SetItemDescriptor
- DataDescriptor_32
- DataDescriptor_64
- DataDescriptor_32
- DataDescriptor_64
- DataDescriptor_32
- DataDescriptor_64
- __NSSetMSize_Impl
- __NSSetMSize
- NSSetSummaryProvider
- NSSetSyntheticFrontEndCreator
- NSSetISyntheticFrontEnd
- ~NSSetISyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- NSCFSetSyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
- GetChildAtIndex
- GenericNSSetMSyntheticFrontEnd
- ~GenericNSSetMSyntheticFrontEnd
- GetIndexOfChildWithName
- CalculateNumChildren
- Update
Improve your Profiling and Debugging skills
Find out more