LLDB mainline
NSDictionary.cpp
Go to the documentation of this file.
1//===-- NSDictionary.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 <mutex>
10
11#include "clang/AST/DeclCXX.h"
12
13#include "CFBasicHash.h"
14#include "NSDictionary.h"
15
18
22#include "lldb/Target/Target.h"
24#include "lldb/Utility/Endian.h"
25#include "lldb/Utility/Status.h"
26#include "lldb/Utility/Stream.h"
29
30using namespace lldb;
31using namespace lldb_private;
32using namespace lldb_private::formatters;
33
37
39 ConstString class_name) {
40 return class_name.GetStringRef().starts_with(m_prefix.GetStringRef());
41}
42
45
50
57
65
67 CompilerType compiler_type;
68 TypeSystemClangSP scratch_ts_sp =
70
71 if (!scratch_ts_sp)
72 return compiler_type;
73
74 static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair");
75
76 compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(
77 scratch_ts_sp->getASTContext(), g_lldb_autogen_nspair);
78
79 if (!compiler_type) {
80 compiler_type = scratch_ts_sp->CreateRecordType(
82 g_lldb_autogen_nspair, llvm::to_underlying(clang::TagTypeKind::Struct),
84
85 if (compiler_type) {
87 CompilerType id_compiler_type =
88 scratch_ts_sp->GetBasicType(eBasicTypeObjCID);
90 compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
92 compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
94 }
95 }
96 return compiler_type;
97}
98
99namespace lldb_private {
100namespace formatters {
102public:
104
106
107 llvm::Expected<uint32_t> CalculateNumChildren() override;
108
109 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
110
111 lldb::ChildCacheState Update() override;
112
113 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
114
115private:
117 uint32_t _used : 26;
118 uint32_t _szidx : 6;
119 };
120
122 uint64_t _used : 58;
123 uint32_t _szidx : 6;
124 };
125
131
133 uint8_t m_ptr_size = 8;
139 std::vector<DictionaryItemDescriptor> m_children;
140};
141
171
200
202public:
204
206
207 llvm::Expected<uint32_t> CalculateNumChildren() override;
208
209 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
210
211 lldb::ChildCacheState Update() override;
212
213 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
214
215private:
217};
218
219template <typename D32, typename D64>
249
250namespace Foundation1100 {
252 public:
254
256
257 llvm::Expected<uint32_t> CalculateNumChildren() override;
258
259 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
260
261 lldb::ChildCacheState Update() override;
262
263 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
264
265 private:
267 uint32_t _used : 26;
268 uint32_t _kvo : 1;
269 uint32_t _size;
270 uint32_t _mutations;
271 uint32_t _objs_addr;
272 uint32_t _keys_addr;
273 };
274
276 uint64_t _used : 58;
277 uint32_t _kvo : 1;
278 uint64_t _size;
279 uint64_t _mutations;
280 uint64_t _objs_addr;
281 uint64_t _keys_addr;
282 };
283
289
291 uint8_t m_ptr_size = 8;
296 std::vector<DictionaryItemDescriptor> m_children;
297 };
298}
299
300namespace Foundation1428 {
301 namespace {
302 struct DataDescriptor_32 {
303 uint32_t _used : 26;
304 uint32_t _kvo : 1;
305 uint32_t _size;
306 uint32_t _buffer;
307 uint64_t GetSize() { return _size; }
308 };
309
310 struct DataDescriptor_64 {
311 uint64_t _used : 58;
312 uint32_t _kvo : 1;
313 uint64_t _size;
314 uint64_t _buffer;
315 uint64_t GetSize() { return _size; }
316 };
317 }
318
321}
322
323namespace Foundation1437 {
324 static const uint64_t NSDictionaryCapacities[] = {
325 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
326 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
327 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
328 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
329 111638519, 180634607, 292272623, 472907251
330 };
331
332 static const size_t NSDictionaryNumSizeBuckets =
333 sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
334
335 namespace {
336 struct DataDescriptor_32 {
337 uint32_t _buffer;
338 uint32_t _muts;
339 uint32_t _used : 25;
340 uint32_t _kvo : 1;
341 uint32_t _szidx : 6;
342
343 uint64_t GetSize() {
344 return (_szidx) >= NSDictionaryNumSizeBuckets ?
345 0 : NSDictionaryCapacities[_szidx];
346 }
347 };
348
349 struct DataDescriptor_64 {
350 uint64_t _buffer;
351 uint32_t _muts;
352 uint32_t _used : 25;
353 uint32_t _kvo : 1;
354 uint32_t _szidx : 6;
355
356 uint64_t GetSize() {
357 return (_szidx) >= NSDictionaryNumSizeBuckets ?
358 0 : NSDictionaryCapacities[_szidx];
359 }
360 };
361 } // namespace
362
365
366 template <typename DD>
367 uint64_t
369 lldb::addr_t valobj_addr, Status &error) {
370 const lldb::addr_t start_of_descriptor =
371 valobj_addr + process.GetAddressByteSize();
372 DD descriptor = DD();
373 process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
374 error);
375 if (error.Fail()) {
376 return 0;
377 }
378 return descriptor._used;
379 }
380
381 uint64_t
383 Status &error) {
384 if (process.GetAddressByteSize() == 4) {
385 return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
386 error);
387 } else {
388 return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
389 error);
390 }
391 }
392
393}
394} // namespace formatters
395} // namespace lldb_private
396
397template <bool name_entries>
399 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
400 static constexpr llvm::StringLiteral g_TypeHint("NSDictionary");
401 ProcessSP process_sp = valobj.GetProcessSP();
402 if (!process_sp)
403 return false;
404
405 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
406
407 if (!runtime)
408 return false;
409
411 runtime->GetNonKVOClassDescriptor(valobj));
412
413 if (!descriptor || !descriptor->IsValid())
414 return false;
415
416 uint32_t ptr_size = process_sp->GetAddressByteSize();
417 bool is_64bit = (ptr_size == 8);
418
419 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
420
421 if (!valobj_addr)
422 return false;
423
424 uint64_t value = 0;
425
426 ConstString class_name(descriptor->GetClassName());
427
428 static const ConstString g_DictionaryI("__NSDictionaryI");
429 static const ConstString g_DictionaryM("__NSDictionaryM");
430 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
431 static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
432 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
433 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
434 static const ConstString g_Dictionary0("__NSDictionary0");
435 static const ConstString g_DictionaryCF("__CFDictionary");
436 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
437 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
438 static const ConstString g_ConstantDictionary("NSConstantDictionary");
439
440 if (class_name.IsEmpty())
441 return false;
442
443 if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
445 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
446 ptr_size, 0, error);
447 if (error.Fail())
448 return false;
449
450 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
451 } else if (class_name == g_ConstantDictionary) {
453 value = process_sp->ReadUnsignedIntegerFromMemory(
454 valobj_addr + 2 * ptr_size, ptr_size, 0, error);
455 if (error.Fail())
456 return false;
457 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
458 class_name == g_DictionaryMFrozen) {
459 AppleObjCRuntime *apple_runtime =
460 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
462 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
463 value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
464 error);
465 } else {
466 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
467 ptr_size, 0, error);
468 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
469 }
470 if (error.Fail())
471 return false;
472 } else if (class_name == g_Dictionary1) {
473 value = 1;
474 } else if (class_name == g_Dictionary0) {
475 value = 0;
476 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
477 class_name == g_DictionaryCFRef) {
478 ExecutionContext exe_ctx(process_sp);
479 CFBasicHash cfbh;
480 if (!cfbh.Update(valobj_addr, exe_ctx))
481 return false;
482 value = cfbh.GetCount();
483 } else {
485 for (auto &candidate : map) {
486 if (candidate.first && candidate.first->Match(class_name))
487 return candidate.second(valobj, stream, options);
488 }
489 return false;
490 }
491
492 llvm::StringRef prefix, suffix;
493 if (Language *language = Language::FindPlugin(options.GetLanguage()))
494 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
495
496 stream << prefix;
497 stream.Printf("%" PRIu64 " %s%s", value, "key/value pair",
498 value == 1 ? "" : "s");
499 stream << suffix;
500 return true;
501}
502
505 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
506 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
507 if (!process_sp)
508 return nullptr;
509 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
510 ObjCLanguageRuntime::Get(*process_sp));
511 if (!runtime)
512 return nullptr;
513
514 CompilerType valobj_type(valobj_sp->GetCompilerType());
515 Flags flags(valobj_type.GetTypeInfo());
516
517 if (flags.IsClear(eTypeIsPointer)) {
519 valobj_sp = valobj_sp->AddressOf(error);
520 if (error.Fail() || !valobj_sp)
521 return nullptr;
522 }
523
525 runtime->GetClassDescriptor(*valobj_sp));
526
527 if (!descriptor || !descriptor->IsValid())
528 return nullptr;
529
530 ConstString class_name(descriptor->GetClassName());
531
532 static const ConstString g_DictionaryI("__NSDictionaryI");
533 static const ConstString g_DictionaryM("__NSDictionaryM");
534 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
535 static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
536 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
537 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
538 static const ConstString g_Dictionary0("__NSDictionary0");
539 static const ConstString g_DictionaryCF("__CFDictionary");
540 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
541 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
542 static const ConstString g_ConstantDictionary("NSConstantDictionary");
543
544 if (class_name.IsEmpty())
545 return nullptr;
546
547 if (class_name == g_DictionaryI) {
548 return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
549 } else if (class_name == g_ConstantDictionary) {
550 return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
551 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
552 if (runtime->GetFoundationVersion() >= 1437) {
554 } else if (runtime->GetFoundationVersion() >= 1428) {
556 } else {
558 }
559 } else if (class_name == g_DictionaryMLegacy) {
561 } else if (class_name == g_Dictionary1) {
562 return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
563 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
564 class_name == g_DictionaryCFRef) {
565 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
566 } else {
568 for (auto &candidate : map) {
569 if (candidate.first && candidate.first->Match((class_name)))
570 return candidate.second(synth, valobj_sp);
571 }
572 }
573
574 return nullptr;
575}
576
580
588
589llvm::Expected<size_t> lldb_private::formatters::
591 auto optional_idx = ExtractIndexFromString(name.AsCString());
592 if (!optional_idx) {
593 return llvm::createStringError("Type has no child named '%s'",
594 name.AsCString());
595 }
596 uint32_t idx = *optional_idx;
598 return llvm::createStringError("Type has no child named '%s'",
599 name.AsCString());
600 return idx;
601}
602
603llvm::Expected<uint32_t> lldb_private::formatters::
605 if (!m_data_32 && !m_data_64)
606 return 0;
607 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
608}
609
612 m_children.clear();
613 delete m_data_32;
614 m_data_32 = nullptr;
615 delete m_data_64;
616 m_data_64 = nullptr;
617 m_ptr_size = 0;
618 ValueObjectSP valobj_sp = m_backend.GetSP();
619 if (!valobj_sp)
621 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
623 error.Clear();
624 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
625 if (!process_sp)
627 m_ptr_size = process_sp->GetAddressByteSize();
628 m_order = process_sp->GetByteOrder();
629 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
630 if (m_ptr_size == 4) {
632 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
633 error);
634 } else {
636 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
637 error);
638 }
639 if (error.Fail())
641 m_data_ptr = data_location + m_ptr_size;
643}
644
647 uint32_t idx) {
648 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
649
650 if (idx >= num_children)
651 return lldb::ValueObjectSP();
652
653 if (m_children.empty()) {
654 // do the scan phase
655 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
656
657 uint32_t tries = 0;
658 uint32_t test_idx = 0;
659
660 while (tries < num_children) {
661 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
662 val_at_idx = key_at_idx + m_ptr_size;
663 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
664 if (!process_sp)
665 return lldb::ValueObjectSP();
667 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
668 if (error.Fail())
669 return lldb::ValueObjectSP();
670 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
671 if (error.Fail())
672 return lldb::ValueObjectSP();
673
674 test_idx++;
675
676 if (!key_at_idx || !val_at_idx)
677 continue;
678 tries++;
679
680 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
682
683 m_children.push_back(descriptor);
684 }
685 }
686
687 if (idx >= m_children.size()) // should never happen
688 return lldb::ValueObjectSP();
689
690 DictionaryItemDescriptor &dict_item = m_children[idx];
691 if (!dict_item.valobj_sp) {
692 if (!m_pair_type.IsValid()) {
693 TargetSP target_sp(m_backend.GetTargetSP());
694 if (!target_sp)
695 return ValueObjectSP();
696 m_pair_type = GetLLDBNSPairType(target_sp);
697 }
698 if (!m_pair_type.IsValid())
699 return ValueObjectSP();
700
701 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
702
703 if (m_ptr_size == 8) {
704 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
705 *data_ptr = dict_item.key_ptr;
706 *(data_ptr + 1) = dict_item.val_ptr;
707 } else {
708 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
709 *data_ptr = dict_item.key_ptr;
710 *(data_ptr + 1) = dict_item.val_ptr;
711 }
712
713 StreamString idx_name;
714 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
715 DataExtractor data(buffer_sp, m_order, m_ptr_size);
716 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
718 }
719 return dict_item.valobj_sp;
720}
721
726
727llvm::Expected<size_t> lldb_private::formatters::
729 auto optional_idx = ExtractIndexFromString(name.AsCString());
730 if (!optional_idx) {
731 return llvm::createStringError("Type has no child named '%s'",
732 name.AsCString());
733 }
734 uint32_t idx = *optional_idx;
736 return llvm::createStringError("Type has no child named '%s'",
737 name.AsCString());
738 return idx;
739}
740
741llvm::Expected<uint32_t> lldb_private::formatters::
743 if (!m_hashtable.IsValid())
744 return 0;
745 return m_hashtable.GetCount();
746}
747
750 m_children.clear();
751 ValueObjectSP valobj_sp = m_backend.GetSP();
752 m_ptr_size = 0;
753 if (!valobj_sp)
755 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
756
757 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
758 if (!process_sp)
760 m_ptr_size = process_sp->GetAddressByteSize();
761 m_order = process_sp->GetByteOrder();
762 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref)
765}
766
769 uint32_t idx) {
770 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
771 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
772
773 const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
774
775 if (idx >= num_children)
776 return lldb::ValueObjectSP();
777
778 if (m_children.empty()) {
779 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
780 if (!process_sp)
781 return lldb::ValueObjectSP();
782
784 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
785
786 uint32_t tries = 0;
787 uint32_t test_idx = 0;
788
789 // Iterate over inferior memory, reading key/value pointers by shifting each
790 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
791 // fails, otherwise, continue until the number of tries matches the number
792 // of childen.
793 while (tries < num_children) {
794 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
795 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
796
797 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
798 if (error.Fail())
799 return lldb::ValueObjectSP();
800 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
801 if (error.Fail())
802 return lldb::ValueObjectSP();
803
804 test_idx++;
805
806 if (!key_at_idx || !val_at_idx)
807 continue;
808 tries++;
809
810 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
812
813 m_children.push_back(descriptor);
814 }
815 }
816
817 if (idx >= m_children.size()) // should never happen
818 return lldb::ValueObjectSP();
819
820 DictionaryItemDescriptor &dict_item = m_children[idx];
821 if (!dict_item.valobj_sp) {
822 if (!m_pair_type.IsValid()) {
823 TargetSP target_sp(m_backend.GetTargetSP());
824 if (!target_sp)
825 return ValueObjectSP();
826 m_pair_type = GetLLDBNSPairType(target_sp);
827 }
828 if (!m_pair_type.IsValid())
829 return ValueObjectSP();
830
831 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
832
833 switch (m_ptr_size) {
834 case 0: // architecture has no clue - fail
835 return lldb::ValueObjectSP();
836 case 4: {
837 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
838 *data_ptr = dict_item.key_ptr;
839 *(data_ptr + 1) = dict_item.val_ptr;
840 } break;
841 case 8: {
842 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
843 *data_ptr = dict_item.key_ptr;
844 *(data_ptr + 1) = dict_item.val_ptr;
845 } break;
846 default:
847 lldbassert(false && "pointer size is not 4 nor 8");
848 }
849
850 StreamString idx_name;
851 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
852 DataExtractor data(buffer_sp, m_order, m_ptr_size);
853 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
855 }
856 return dict_item.valobj_sp;
857}
858
862
863llvm::Expected<size_t>
866 auto optional_idx = ExtractIndexFromString(name.AsCString());
867 if (!optional_idx) {
868 return llvm::createStringError("Type has no child named '%s'",
869 name.AsCString());
870 }
871 uint32_t idx = *optional_idx;
873 return llvm::createStringError("Type has no child named '%s'",
874 name.AsCString());
875 return idx;
876}
877
882
885 ValueObjectSP valobj_sp = m_backend.GetSP();
886 if (!valobj_sp)
888 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
890 error.Clear();
891 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
892 if (!process_sp)
894 m_ptr_size = process_sp->GetAddressByteSize();
895 m_order = process_sp->GetByteOrder();
896 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
897 m_size = process_sp->ReadUnsignedIntegerFromMemory(
898 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
899 if (error.Fail())
901 m_keys_ptr =
902 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
903 if (error.Fail())
906 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
907
908 return error.Success() ? lldb::ChildCacheState::eReuse
910}
911
914 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
915
916 if (idx >= num_children)
917 return lldb::ValueObjectSP();
918
919 if (m_children.empty()) {
920 // do the scan phase
921 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
922 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
923 if (!process_sp)
924 return lldb::ValueObjectSP();
925
926 for (unsigned int child = 0; child < num_children; ++child) {
928 key_at_idx = process_sp->ReadPointerFromMemory(
929 m_keys_ptr + child * m_ptr_size, error);
930 if (error.Fail())
931 return lldb::ValueObjectSP();
932 val_at_idx = process_sp->ReadPointerFromMemory(
933 m_objects_ptr + child * m_ptr_size, error);
934 if (error.Fail())
935 return lldb::ValueObjectSP();
936 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
938 m_children.push_back(descriptor);
939 }
940 }
941
942 if (idx >= m_children.size()) // should never happen
943 return lldb::ValueObjectSP();
944
945 DictionaryItemDescriptor &dict_item = m_children[idx];
946 if (!dict_item.valobj_sp) {
947 if (!m_pair_type.IsValid()) {
948 TargetSP target_sp(m_backend.GetTargetSP());
949 if (!target_sp)
950 return ValueObjectSP();
951 m_pair_type = GetLLDBNSPairType(target_sp);
952 }
953 if (!m_pair_type.IsValid())
954 return ValueObjectSP();
955
956 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
957
958 if (m_ptr_size == 8) {
959 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
960 *data_ptr = dict_item.key_ptr;
961 *(data_ptr + 1) = dict_item.val_ptr;
962 } else {
963 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
964 *data_ptr = dict_item.key_ptr;
965 *(data_ptr + 1) = dict_item.val_ptr;
966 }
967
968 StreamString idx_name;
969 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
970 DataExtractor data(buffer_sp, m_order, m_ptr_size);
971 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
973 }
974 return dict_item.valobj_sp;
975}
976
980
981llvm::Expected<size_t> lldb_private::formatters::
983 static const ConstString g_zero("[0]");
984 if (name == g_zero)
985 return 0;
986 return llvm::createStringError("Type has no child named '%s'",
987 name.AsCString());
988}
989
994
1000
1003 uint32_t idx) {
1004 if (idx != 0)
1005 return lldb::ValueObjectSP();
1006
1007 if (m_pair.get())
1008 return m_pair;
1009
1010 auto process_sp(m_backend.GetProcessSP());
1011 if (!process_sp)
1012 return nullptr;
1013
1014 auto ptr_size = process_sp->GetAddressByteSize();
1015
1016 lldb::addr_t key_ptr =
1017 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1018 lldb::addr_t value_ptr = key_ptr + ptr_size;
1019
1020 Status error;
1021
1022 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
1023 if (error.Fail())
1024 return nullptr;
1025 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
1026 if (error.Fail())
1027 return nullptr;
1028
1029 auto pair_type =
1030 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1031
1032 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1033
1034 if (ptr_size == 8) {
1035 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1036 *data_ptr = key_at_idx;
1037 *(data_ptr + 1) = value_at_idx;
1038 } else {
1039 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1040 *data_ptr = key_at_idx;
1041 *(data_ptr + 1) = value_at_idx;
1042 }
1043
1044 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1046 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1047
1048 return m_pair;
1049}
1050
1051template <typename D32, typename D64>
1056
1057template <typename D32, typename D64>
1061 delete m_data_32;
1062 m_data_32 = nullptr;
1063 delete m_data_64;
1064 m_data_64 = nullptr;
1065}
1066
1067template <typename D32, typename D64>
1068llvm::Expected<size_t>
1071 auto optional_idx = ExtractIndexFromString(name.AsCString());
1072 if (!optional_idx) {
1073 return llvm::createStringError("Type has no child named '%s'",
1074 name.AsCString());
1075 }
1076 uint32_t idx = *optional_idx;
1078 return llvm::createStringError("Type has no child named '%s'",
1079 name.AsCString());
1080 return idx;
1081}
1082
1083template <typename D32, typename D64>
1084llvm::Expected<uint32_t>
1087 if (!m_data_32 && !m_data_64)
1088 return 0;
1089 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1090}
1091
1092template <typename D32, typename D64>
1095 D64>::Update() {
1096 m_children.clear();
1097 ValueObjectSP valobj_sp = m_backend.GetSP();
1098 m_ptr_size = 0;
1099 delete m_data_32;
1100 m_data_32 = nullptr;
1101 delete m_data_64;
1102 m_data_64 = nullptr;
1103 if (!valobj_sp)
1105 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1106 Status error;
1107 error.Clear();
1108 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1109 if (!process_sp)
1111 m_ptr_size = process_sp->GetAddressByteSize();
1112 m_order = process_sp->GetByteOrder();
1113 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1114 if (m_ptr_size == 4) {
1115 m_data_32 = new D32();
1116 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1117 error);
1118 } else {
1119 m_data_64 = new D64();
1120 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1121 error);
1122 }
1123
1124 return error.Success() ? lldb::ChildCacheState::eReuse
1126}
1127
1128template <typename D32, typename D64>
1131 D32, D64>::GetChildAtIndex(uint32_t idx) {
1132 lldb::addr_t m_keys_ptr;
1133 lldb::addr_t m_values_ptr;
1134 if (m_data_32) {
1135 uint32_t size = m_data_32->GetSize();
1136 m_keys_ptr = m_data_32->_buffer;
1137 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1138 } else {
1139 uint32_t size = m_data_64->GetSize();
1140 m_keys_ptr = m_data_64->_buffer;
1141 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1142 }
1143
1144 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1145
1146 if (idx >= num_children)
1147 return lldb::ValueObjectSP();
1148
1149 if (m_children.empty()) {
1150 // do the scan phase
1151 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1152
1153 uint32_t tries = 0;
1154 uint32_t test_idx = 0;
1155
1156 while (tries < num_children) {
1157 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1158 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1159 ;
1160 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1161 if (!process_sp)
1162 return lldb::ValueObjectSP();
1163 Status error;
1164 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1165 if (error.Fail())
1166 return lldb::ValueObjectSP();
1167 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1168 if (error.Fail())
1169 return lldb::ValueObjectSP();
1170
1171 test_idx++;
1172
1173 if (!key_at_idx || !val_at_idx)
1174 continue;
1175 tries++;
1176
1177 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1179
1180 m_children.push_back(descriptor);
1181 }
1182 }
1183
1184 if (idx >= m_children.size()) // should never happen
1185 return lldb::ValueObjectSP();
1186
1187 DictionaryItemDescriptor &dict_item = m_children[idx];
1188 if (!dict_item.valobj_sp) {
1189 if (!m_pair_type.IsValid()) {
1190 TargetSP target_sp(m_backend.GetTargetSP());
1191 if (!target_sp)
1192 return ValueObjectSP();
1193 m_pair_type = GetLLDBNSPairType(target_sp);
1194 }
1195 if (!m_pair_type.IsValid())
1196 return ValueObjectSP();
1197
1198 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1199
1200 if (m_ptr_size == 8) {
1201 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1202 *data_ptr = dict_item.key_ptr;
1203 *(data_ptr + 1) = dict_item.val_ptr;
1204 } else {
1205 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1206 *data_ptr = dict_item.key_ptr;
1207 *(data_ptr + 1) = dict_item.val_ptr;
1208 }
1209
1210 StreamString idx_name;
1211 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1212 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1213 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1215 }
1216 return dict_item.valobj_sp;
1217}
1218
1222
1230
1233 auto optional_idx = ExtractIndexFromString(name.AsCString());
1234 if (!optional_idx) {
1235 return llvm::createStringError("Type has no child named '%s'",
1236 name.AsCString());
1237 }
1238 uint32_t idx = *optional_idx;
1240 return llvm::createStringError("Type has no child named '%s'",
1241 name.AsCString());
1242 return idx;
1243}
1244
1247 if (!m_data_32 && !m_data_64)
1248 return 0;
1249 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1250}
1251
1254 m_children.clear();
1255 ValueObjectSP valobj_sp = m_backend.GetSP();
1256 m_ptr_size = 0;
1257 delete m_data_32;
1258 m_data_32 = nullptr;
1259 delete m_data_64;
1260 m_data_64 = nullptr;
1261 if (!valobj_sp)
1263 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1264 Status error;
1265 error.Clear();
1266 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1267 if (!process_sp)
1269 m_ptr_size = process_sp->GetAddressByteSize();
1270 m_order = process_sp->GetByteOrder();
1271 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1272 if (m_ptr_size == 4) {
1274 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1275 error);
1276 } else {
1278 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1279 error);
1280 }
1281
1282 return error.Success() ? lldb::ChildCacheState::eReuse
1284}
1285
1289 lldb::addr_t m_keys_ptr =
1290 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1291 lldb::addr_t m_values_ptr =
1292 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1293
1294 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1295
1296 if (idx >= num_children)
1297 return lldb::ValueObjectSP();
1298
1299 if (m_children.empty()) {
1300 // do the scan phase
1301 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1302
1303 uint32_t tries = 0;
1304 uint32_t test_idx = 0;
1305
1306 while (tries < num_children) {
1307 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1308 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1309 ;
1310 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1311 if (!process_sp)
1312 return lldb::ValueObjectSP();
1313 Status error;
1314 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1315 if (error.Fail())
1316 return lldb::ValueObjectSP();
1317 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1318 if (error.Fail())
1319 return lldb::ValueObjectSP();
1320
1321 test_idx++;
1322
1323 if (!key_at_idx || !val_at_idx)
1324 continue;
1325 tries++;
1326
1327 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1329
1330 m_children.push_back(descriptor);
1331 }
1332 }
1333
1334 if (idx >= m_children.size()) // should never happen
1335 return lldb::ValueObjectSP();
1336
1337 DictionaryItemDescriptor &dict_item = m_children[idx];
1338 if (!dict_item.valobj_sp) {
1339 if (!m_pair_type.IsValid()) {
1340 TargetSP target_sp(m_backend.GetTargetSP());
1341 if (!target_sp)
1342 return ValueObjectSP();
1343 m_pair_type = GetLLDBNSPairType(target_sp);
1344 }
1345 if (!m_pair_type.IsValid())
1346 return ValueObjectSP();
1347
1348 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1349
1350 if (m_ptr_size == 8) {
1351 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1352 *data_ptr = dict_item.key_ptr;
1353 *(data_ptr + 1) = dict_item.val_ptr;
1354 } else {
1355 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1356 *data_ptr = dict_item.key_ptr;
1357 *(data_ptr + 1) = dict_item.val_ptr;
1358 }
1359
1360 StreamString idx_name;
1361 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1362 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1363 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1365 }
1366 return dict_item.valobj_sp;
1367}
1368
1370 ValueObject &, Stream &, const TypeSummaryOptions &);
1371
1373 ValueObject &, Stream &, const TypeSummaryOptions &);
static llvm::raw_ostream & error(Stream &strm)
#define lldbassert(x)
Definition LLDBAssert.h:16
static CompilerType GetLLDBNSPairType(TargetSP target_sp)
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 ...
bool Update(lldb::addr_t addr, ExecutionContextRef exe_ctx_rf)
std::function< SyntheticChildrenFrontEnd *(CXXSyntheticChildren *, lldb::ValueObjectSP)> CreateFrontEndCallback
Generic representation of a type in a programming language.
uint32_t GetTypeInfo(CompilerType *pointee_or_element_compiler_type=nullptr) const
A uniqued constant string class.
Definition ConstString.h:40
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
bool IsEmpty() const
Test for empty string.
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
A subclass of DataBuffer that stores a data buffer on the heap.
An data extractor class.
Execution context objects refer to objects in the execution of the program that is being debugged.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
A class to manage flags.
Definition Flags.h:22
bool IsClear(ValueType bit) const
Test a single flag bit to see if it is clear (zero).
Definition Flags.h:111
static Language * FindPlugin(lldb::LanguageType language)
Definition Language.cpp:84
std::shared_ptr< ClassDescriptor > ClassDescriptorSP
ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value)
static ObjCLanguageRuntime * Get(Process &process)
virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value)
A plug-in interface definition class for debugging a process.
Definition Process.h:357
virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition Process.cpp:1930
uint32_t GetAddressByteSize() const
Definition Process.cpp:3620
static lldb::TypeSystemClangSP GetForTarget(Target &target, std::optional< IsolatedASTKind > ast_kind=DefaultAST, bool create_on_demand=true)
Returns the scratch TypeSystemClang for the given target.
An error handling class.
Definition Status.h:118
llvm::StringRef GetString() const
A stream class that can stream formatted output to a file.
Definition Stream.h:28
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
uint32_t CalculateNumChildrenIgnoringErrors(uint32_t max=UINT32_MAX)
lldb::ValueObjectSP CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type)
SyntheticChildrenFrontEnd(ValueObject &backend)
lldb::LanguageType GetLanguage() const
static clang::FieldDecl * AddFieldToRecordType(const CompilerType &type, llvm::StringRef name, const CompilerType &field_type, lldb::AccessType access, uint32_t bitfield_bit_size)
static bool CompleteTagDeclarationDefinition(const CompilerType &type)
static bool StartTagDeclarationDefinition(const CompilerType &type)
lldb::ProcessSP GetProcessSP() const
virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success=nullptr)
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
std::vector< DictionaryItemDescriptor > m_children
llvm::Expected< uint32_t > CalculateNumChildren() override
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
llvm::Expected< uint32_t > CalculateNumChildren() override
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
std::vector< DictionaryItemDescriptor > m_children
NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
llvm::Expected< uint32_t > CalculateNumChildren() override
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
static AdditionalFormatters< CXXFunctionSummaryFormat::Callback > & GetAdditionalSummaries()
static AdditionalFormatters< CXXSyntheticChildren::CreateFrontEndCallback > & GetAdditionalSynthetics()
std::vector< AdditionalFormatter< FormatterType > > AdditionalFormatters
#define LLDB_INVALID_ADDRESS
GenericNSDictionaryMSyntheticFrontEnd< DataDescriptor_32, DataDescriptor_64 > NSDictionaryMSyntheticFrontEnd
static const uint64_t NSDictionaryCapacities[]
uint64_t __NSDictionaryMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, Status &error)
GenericNSDictionaryMSyntheticFrontEnd< DataDescriptor_32, DataDescriptor_64 > NSDictionaryMSyntheticFrontEnd
uint64_t __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, Status &error)
std::optional< size_t > ExtractIndexFromString(const char *item_name)
template bool NSDictionarySummaryProvider< false >(ValueObject &, Stream &, const TypeSummaryOptions &)
template bool NSDictionarySummaryProvider< true >(ValueObject &, Stream &, const TypeSummaryOptions &)
bool NSDictionarySummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
SyntheticChildrenFrontEnd * NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
A class that represents a running process on the host machine.
ChildCacheState
Specifies if children need to be re-computed after a call to SyntheticChildrenFrontEnd::Update.
@ eRefetch
Children need to be recomputed dynamically.
@ eReuse
Children did not change and don't need to be recomputed; re-use what we computed the last time we cal...
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
@ eLanguageTypeC
Non-standardized C, such as K&R.
std::shared_ptr< lldb_private::Process > ProcessSP
ByteOrder
Byte ordering definitions.
std::shared_ptr< lldb_private::TypeSystemClang > TypeSystemClangSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP
std::function< bool(ValueObject &, Stream &, const TypeSummaryOptions &)> Callback