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
19#include "llvm/Support/ErrorExtras.h"
20
24#include "lldb/Target/Target.h"
26#include "lldb/Utility/Endian.h"
27#include "lldb/Utility/Status.h"
28#include "lldb/Utility/Stream.h"
31
32using namespace lldb;
33using namespace lldb_private;
34using namespace lldb_private::formatters;
35
39
41 llvm::StringRef class_name) {
42 return class_name.starts_with(m_prefix);
43}
44
47
49 llvm::StringRef class_name) {
50 return (class_name == m_name);
51}
52
59
67
69 CompilerType compiler_type;
70 TypeSystemClangSP scratch_ts_sp =
72
73 if (!scratch_ts_sp)
74 return compiler_type;
75
76 static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair");
77
78 compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(
79 scratch_ts_sp->getASTContext(), g_lldb_autogen_nspair);
80
81 if (!compiler_type) {
82 compiler_type = scratch_ts_sp->CreateRecordType(
83 nullptr, OptionalClangModuleID(), g_lldb_autogen_nspair,
84 llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC);
85
86 if (compiler_type) {
88 CompilerType id_compiler_type =
89 scratch_ts_sp->GetBasicType(eBasicTypeObjCID);
90 TypeSystemClang::AddFieldToRecordType(compiler_type, "key",
91 id_compiler_type, 0);
92 TypeSystemClang::AddFieldToRecordType(compiler_type, "value",
93 id_compiler_type, 0);
95 }
96 }
97 return compiler_type;
98}
99
100namespace lldb_private {
101namespace formatters {
140
168
195
197public:
199
201
202 llvm::Expected<uint32_t> CalculateNumChildren() override;
203
204 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
205
206 lldb::ChildCacheState Update() override;
207
208 llvm::Expected<size_t> GetIndexOfChildWithName(llvm::StringRef name) override;
209
210private:
212};
213
214template <typename D32, typename D64>
242
243namespace Foundation1100 {
245 public:
247
249
250 llvm::Expected<uint32_t> CalculateNumChildren() override;
251
252 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
253
254 lldb::ChildCacheState Update() override;
255
256 private:
258 uint32_t _used : 26;
259 uint32_t _kvo : 1;
260 uint32_t _size;
261 uint32_t _mutations;
262 uint32_t _objs_addr;
263 uint32_t _keys_addr;
264 };
265
267 uint64_t _used : 58;
268 uint32_t _kvo : 1;
269 uint64_t _size;
270 uint64_t _mutations;
271 uint64_t _objs_addr;
272 uint64_t _keys_addr;
273 };
274
280
282 uint8_t m_ptr_size = 8;
287 std::vector<DictionaryItemDescriptor> m_children;
288 };
289}
290
291namespace Foundation1428 {
292 namespace {
293 struct DataDescriptor_32 {
294 uint32_t _used : 26;
295 uint32_t _kvo : 1;
296 uint32_t _size;
297 uint32_t _buffer;
298 uint64_t GetSize() { return _size; }
299 };
300
301 struct DataDescriptor_64 {
302 uint64_t _used : 58;
303 uint32_t _kvo : 1;
304 uint64_t _size;
305 uint64_t _buffer;
306 uint64_t GetSize() { return _size; }
307 };
308 }
309
312}
313
314namespace Foundation1437 {
315 static const uint64_t NSDictionaryCapacities[] = {
316 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
317 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
318 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
319 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
320 111638519, 180634607, 292272623, 472907251
321 };
322
323 static const size_t NSDictionaryNumSizeBuckets =
324 sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
325
326 namespace {
327 struct DataDescriptor_32 {
328 uint32_t _buffer;
329 uint32_t _muts;
330 uint32_t _used : 25;
331 uint32_t _kvo : 1;
332 uint32_t _szidx : 6;
333
334 uint64_t GetSize() {
335 return (_szidx) >= NSDictionaryNumSizeBuckets ?
336 0 : NSDictionaryCapacities[_szidx];
337 }
338 };
339
340 struct DataDescriptor_64 {
341 uint64_t _buffer;
342 uint32_t _muts;
343 uint32_t _used : 25;
344 uint32_t _kvo : 1;
345 uint32_t _szidx : 6;
346
347 uint64_t GetSize() {
348 return (_szidx) >= NSDictionaryNumSizeBuckets ?
349 0 : NSDictionaryCapacities[_szidx];
350 }
351 };
352 } // namespace
353
356
357 template <typename DD>
358 uint64_t
360 lldb::addr_t valobj_addr, Status &error) {
361 const lldb::addr_t start_of_descriptor =
362 valobj_addr + process.GetAddressByteSize();
363 DD descriptor = DD();
364 process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
365 error);
366 if (error.Fail()) {
367 return 0;
368 }
369 return descriptor._used;
370 }
371
372 uint64_t
374 Status &error) {
375 if (process.GetAddressByteSize() == 4) {
376 return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
377 error);
378 } else {
379 return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
380 error);
381 }
382 }
383
384}
385} // namespace formatters
386} // namespace lldb_private
387
388template <bool name_entries>
390 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
391 static constexpr llvm::StringLiteral g_TypeHint("NSDictionary");
392 ProcessSP process_sp = valobj.GetProcessSP();
393 if (!process_sp)
394 return false;
395
396 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
397
398 if (!runtime)
399 return false;
400
402 runtime->GetNonKVOClassDescriptor(valobj));
403
404 if (!descriptor || !descriptor->IsValid())
405 return false;
406
407 uint32_t ptr_size = process_sp->GetAddressByteSize();
408 bool is_64bit = (ptr_size == 8);
409
410 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
411
412 if (!valobj_addr)
413 return false;
414
415 uint64_t value = 0;
416
417 llvm::StringRef class_name(descriptor->GetClassName());
418
419 static constexpr llvm::StringLiteral g_DictionaryI("__NSDictionaryI");
420 static constexpr llvm::StringLiteral g_DictionaryM("__NSDictionaryM");
421 static constexpr llvm::StringLiteral g_DictionaryMLegacy(
422 "__NSDictionaryM_Legacy");
423 static constexpr llvm::StringLiteral g_DictionaryMImmutable(
424 "__NSDictionaryM_Immutable");
425 static constexpr llvm::StringLiteral g_DictionaryMFrozen(
426 "__NSFrozenDictionaryM");
427 static constexpr llvm::StringLiteral g_Dictionary1(
428 "__NSSingleEntryDictionaryI");
429 static constexpr llvm::StringLiteral g_Dictionary0("__NSDictionary0");
430 static constexpr llvm::StringLiteral g_DictionaryCF("__CFDictionary");
431 static constexpr llvm::StringLiteral g_DictionaryNSCF("__NSCFDictionary");
432 static constexpr llvm::StringLiteral g_DictionaryCFRef("CFDictionaryRef");
433 static constexpr llvm::StringLiteral g_ConstantDictionary(
434 "NSConstantDictionary");
435
436 if (class_name.empty())
437 return false;
438
439 if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
441 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
442 ptr_size, 0, error);
443 if (error.Fail())
444 return false;
445
446 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
447 } else if (class_name == g_ConstantDictionary) {
449 value = process_sp->ReadUnsignedIntegerFromMemory(
450 valobj_addr + 2 * ptr_size, ptr_size, 0, error);
451 if (error.Fail())
452 return false;
453 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
454 class_name == g_DictionaryMFrozen) {
455 AppleObjCRuntime *apple_runtime =
456 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
458 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
459 value =
460 Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr, error);
461 } else {
462 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
463 ptr_size, 0, error);
464 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
465 }
466 if (error.Fail())
467 return false;
468 } else if (class_name == g_Dictionary1) {
469 value = 1;
470 } else if (class_name == g_Dictionary0) {
471 value = 0;
472 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
473 class_name == g_DictionaryCFRef) {
474 ExecutionContext exe_ctx(process_sp);
475 CFBasicHash cfbh;
476 if (!cfbh.Update(valobj_addr, exe_ctx))
477 return false;
478 value = cfbh.GetCount();
479 } else {
481 for (auto &candidate : map) {
482 if (candidate.first && candidate.first->Match(class_name))
483 return candidate.second(valobj, stream, options);
484 }
485 return false;
486 }
487
488 llvm::StringRef prefix, suffix;
489 if (Language *language = Language::FindPlugin(options.GetLanguage()))
490 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
491
492 stream << prefix;
493 stream.Printf("%" PRIu64 " %s%s", value, "key/value pair",
494 value == 1 ? "" : "s");
495 stream << suffix;
496 return true;
497}
498
501 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
502 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
503 if (!process_sp)
504 return nullptr;
505 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
506 ObjCLanguageRuntime::Get(*process_sp));
507 if (!runtime)
508 return nullptr;
509
510 CompilerType valobj_type(valobj_sp->GetCompilerType());
511 Flags flags(valobj_type.GetTypeInfo());
512
513 if (flags.IsClear(eTypeIsPointer)) {
515 valobj_sp = valobj_sp->AddressOf(error);
516 if (error.Fail() || !valobj_sp)
517 return nullptr;
518 }
519
521 runtime->GetClassDescriptor(*valobj_sp));
522
523 if (!descriptor || !descriptor->IsValid())
524 return nullptr;
525
526 llvm::StringRef class_name(descriptor->GetClassName());
527
528 static constexpr llvm::StringLiteral g_DictionaryI("__NSDictionaryI");
529 static constexpr llvm::StringLiteral g_DictionaryM("__NSDictionaryM");
530 static constexpr llvm::StringLiteral g_Dictionary1(
531 "__NSSingleEntryDictionaryI");
532 static constexpr llvm::StringLiteral g_DictionaryMFrozen(
533 "__NSFrozenDictionaryM");
534 static constexpr llvm::StringLiteral g_DictionaryMLegacy(
535 "__NSDictionaryM_Legacy");
536 static constexpr llvm::StringLiteral g_DictionaryCF("__CFDictionary");
537 static constexpr llvm::StringLiteral g_DictionaryNSCF("__NSCFDictionary");
538 static constexpr llvm::StringLiteral g_DictionaryCFRef("CFDictionaryRef");
539 static constexpr llvm::StringLiteral g_ConstantDictionary(
540 "NSConstantDictionary");
541
542 if (class_name.empty())
543 return nullptr;
544
545 if (class_name == g_DictionaryI) {
546 return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
547 } else if (class_name == g_ConstantDictionary) {
548 return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
549 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
550 if (runtime->GetFoundationVersion() >= 1437) {
552 } else if (runtime->GetFoundationVersion() >= 1428) {
554 } else {
555 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
556 }
557 } else if (class_name == g_DictionaryMLegacy) {
558 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
559 } else if (class_name == g_Dictionary1) {
560 return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
561 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
562 class_name == g_DictionaryCFRef) {
563 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
564 } else {
566 for (auto &candidate : map) {
567 if (candidate.first && candidate.first->Match((class_name)))
568 return candidate.second(synth, valobj_sp);
569 }
570 }
571
572 return nullptr;
573}
574
578
586
587llvm::Expected<uint32_t> lldb_private::formatters::
589 if (!m_data_32 && !m_data_64)
590 return 0;
591 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
592}
593
596 m_children.clear();
597 delete m_data_32;
598 m_data_32 = nullptr;
599 delete m_data_64;
600 m_data_64 = nullptr;
601 m_ptr_size = 0;
602 ValueObjectSP valobj_sp = m_backend.GetSP();
603 if (!valobj_sp)
605 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
607 error.Clear();
608 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
609 if (!process_sp)
611 m_ptr_size = process_sp->GetAddressByteSize();
612 m_order = process_sp->GetByteOrder();
613 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
614 if (m_ptr_size == 4) {
616 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
617 error);
618 } else {
620 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
621 error);
622 }
623 if (error.Fail())
625 m_data_ptr = data_location + m_ptr_size;
627}
628
631 uint32_t idx) {
632 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
633
634 if (idx >= num_children)
635 return lldb::ValueObjectSP();
636
637 if (m_children.empty()) {
638 // do the scan phase
639 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
640
641 uint32_t tries = 0;
642 uint32_t test_idx = 0;
643
644 while (tries < num_children) {
645 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
646 val_at_idx = key_at_idx + m_ptr_size;
647 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
648 if (!process_sp)
649 return lldb::ValueObjectSP();
650 llvm::Expected<lldb::addr_t> key =
651 process_sp->ReadPointerFromMemory(key_at_idx);
652 if (!key) {
653 llvm::consumeError(key.takeError());
654 return lldb::ValueObjectSP();
655 }
656 key_at_idx = *key;
657 llvm::Expected<lldb::addr_t> val =
658 process_sp->ReadPointerFromMemory(val_at_idx);
659 if (!val) {
660 llvm::consumeError(val.takeError());
661 return lldb::ValueObjectSP();
662 }
663 val_at_idx = *val;
664
665 test_idx++;
666
667 if (!key_at_idx || !val_at_idx)
668 continue;
669 tries++;
670
671 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
673
674 m_children.push_back(descriptor);
675 }
676 }
677
678 if (idx >= m_children.size()) // should never happen
679 return lldb::ValueObjectSP();
680
681 DictionaryItemDescriptor &dict_item = m_children[idx];
682 if (!dict_item.valobj_sp) {
683 if (!m_pair_type.IsValid()) {
684 TargetSP target_sp(m_backend.GetTargetSP());
685 if (!target_sp)
686 return ValueObjectSP();
687 m_pair_type = GetLLDBNSPairType(target_sp);
688 }
689 if (!m_pair_type.IsValid())
690 return ValueObjectSP();
691
692 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
693
694 if (m_ptr_size == 8) {
695 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
696 *data_ptr = dict_item.key_ptr;
697 *(data_ptr + 1) = dict_item.val_ptr;
698 } else {
699 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
700 *data_ptr = dict_item.key_ptr;
701 *(data_ptr + 1) = dict_item.val_ptr;
702 }
703
704 StreamString idx_name;
705 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
706 DataExtractor data(buffer_sp, m_order, m_ptr_size);
708 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
709 }
710 return dict_item.valobj_sp;
711}
712
717
718llvm::Expected<uint32_t> lldb_private::formatters::
720 if (!m_hashtable.IsValid())
721 return 0;
722 return m_hashtable.GetCount();
723}
724
727 m_children.clear();
728 ValueObjectSP valobj_sp = m_backend.GetSP();
729 m_ptr_size = 0;
730 if (!valobj_sp)
732 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
733
734 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
735 if (!process_sp)
737 m_ptr_size = process_sp->GetAddressByteSize();
738 m_order = process_sp->GetByteOrder();
739 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref)
742}
743
746 uint32_t idx) {
747 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
748 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
749
750 const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
751
752 if (idx >= num_children)
753 return lldb::ValueObjectSP();
754
755 if (m_children.empty()) {
756 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
757 if (!process_sp)
758 return lldb::ValueObjectSP();
759
760 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
761
762 uint32_t tries = 0;
763 uint32_t test_idx = 0;
764
765 // Iterate over inferior memory, reading key/value pointers by shifting each
766 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
767 // fails, otherwise, continue until the number of tries matches the number
768 // of childen.
769 while (tries < num_children) {
770 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
771 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
772
773 llvm::Expected<lldb::addr_t> key =
774 process_sp->ReadPointerFromMemory(key_at_idx);
775 if (!key) {
776 llvm::consumeError(key.takeError());
777 return lldb::ValueObjectSP();
778 }
779 key_at_idx = *key;
780 llvm::Expected<lldb::addr_t> val =
781 process_sp->ReadPointerFromMemory(val_at_idx);
782 if (!val) {
783 llvm::consumeError(val.takeError());
784 return lldb::ValueObjectSP();
785 }
786 val_at_idx = *val;
787
788 test_idx++;
789
790 if (!key_at_idx || !val_at_idx)
791 continue;
792 tries++;
793
794 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
796
797 m_children.push_back(descriptor);
798 }
799 }
800
801 if (idx >= m_children.size()) // should never happen
802 return lldb::ValueObjectSP();
803
804 DictionaryItemDescriptor &dict_item = m_children[idx];
805 if (!dict_item.valobj_sp) {
806 if (!m_pair_type.IsValid()) {
807 TargetSP target_sp(m_backend.GetTargetSP());
808 if (!target_sp)
809 return ValueObjectSP();
810 m_pair_type = GetLLDBNSPairType(target_sp);
811 }
812 if (!m_pair_type.IsValid())
813 return ValueObjectSP();
814
815 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
816
817 switch (m_ptr_size) {
818 case 0: // architecture has no clue - fail
819 return lldb::ValueObjectSP();
820 case 4: {
821 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
822 *data_ptr = dict_item.key_ptr;
823 *(data_ptr + 1) = dict_item.val_ptr;
824 } break;
825 case 8: {
826 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
827 *data_ptr = dict_item.key_ptr;
828 *(data_ptr + 1) = dict_item.val_ptr;
829 } break;
830 default:
831 lldbassert(false && "pointer size is not 4 nor 8");
832 }
833
834 StreamString idx_name;
835 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
836 DataExtractor data(buffer_sp, m_order, m_ptr_size);
838 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
839 }
840 return dict_item.valobj_sp;
841}
842
846
851
854 ValueObjectSP valobj_sp = m_backend.GetSP();
855 if (!valobj_sp)
857 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
859 error.Clear();
860 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
861 if (!process_sp)
863 m_ptr_size = process_sp->GetAddressByteSize();
864 m_order = process_sp->GetByteOrder();
865 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
866 m_size = process_sp->ReadUnsignedIntegerFromMemory(
867 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
868 if (error.Fail())
870 llvm::Expected<lldb::addr_t> keys_ptr =
871 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size);
872 if (!keys_ptr) {
873 llvm::consumeError(keys_ptr.takeError());
875 }
876 m_keys_ptr = *keys_ptr;
877 llvm::Expected<lldb::addr_t> objects_ptr =
878 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size);
879 if (!objects_ptr) {
880 llvm::consumeError(objects_ptr.takeError());
882 }
883 m_objects_ptr = *objects_ptr;
884
886}
887
890 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
891
892 if (idx >= num_children)
893 return lldb::ValueObjectSP();
894
895 if (m_children.empty()) {
896 // do the scan phase
897 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
898 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
899 if (!process_sp)
900 return lldb::ValueObjectSP();
901
902 for (unsigned int child = 0; child < num_children; ++child) {
903 llvm::Expected<lldb::addr_t> key =
904 process_sp->ReadPointerFromMemory(m_keys_ptr + child * m_ptr_size);
905 if (!key) {
906 llvm::consumeError(key.takeError());
907 return lldb::ValueObjectSP();
908 }
909 key_at_idx = *key;
910 llvm::Expected<lldb::addr_t> val =
911 process_sp->ReadPointerFromMemory(m_objects_ptr + child * m_ptr_size);
912 if (!val) {
913 llvm::consumeError(val.takeError());
914 return lldb::ValueObjectSP();
915 }
916 val_at_idx = *val;
917 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
919 m_children.push_back(descriptor);
920 }
921 }
922
923 if (idx >= m_children.size()) // should never happen
924 return lldb::ValueObjectSP();
925
926 DictionaryItemDescriptor &dict_item = m_children[idx];
927 if (!dict_item.valobj_sp) {
928 if (!m_pair_type.IsValid()) {
929 TargetSP target_sp(m_backend.GetTargetSP());
930 if (!target_sp)
931 return ValueObjectSP();
932 m_pair_type = GetLLDBNSPairType(target_sp);
933 }
934 if (!m_pair_type.IsValid())
935 return ValueObjectSP();
936
937 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
938
939 if (m_ptr_size == 8) {
940 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
941 *data_ptr = dict_item.key_ptr;
942 *(data_ptr + 1) = dict_item.val_ptr;
943 } else {
944 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
945 *data_ptr = dict_item.key_ptr;
946 *(data_ptr + 1) = dict_item.val_ptr;
947 }
948
949 StreamString idx_name;
950 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
951 DataExtractor data(buffer_sp, m_order, m_ptr_size);
953 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
954 }
955 return dict_item.valobj_sp;
956}
957
961
962llvm::Expected<size_t>
964 GetIndexOfChildWithName(llvm::StringRef name) {
965 static constexpr llvm::StringLiteral g_zero("[0]");
966 if (name == g_zero)
967 return 0;
968 return llvm::createStringErrorV("type has no child named '{0}'", name);
969}
970
975
981
984 uint32_t idx) {
985 if (idx != 0)
986 return lldb::ValueObjectSP();
987
988 if (m_pair.get())
989 return m_pair;
990
991 auto process_sp(m_backend.GetProcessSP());
992 if (!process_sp)
993 return nullptr;
994
995 auto ptr_size = process_sp->GetAddressByteSize();
996
997 lldb::addr_t key_ptr =
998 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
999 lldb::addr_t value_ptr = key_ptr + ptr_size;
1000
1001 llvm::Expected<lldb::addr_t> value_at_idx =
1002 process_sp->ReadPointerFromMemory(key_ptr);
1003 if (!value_at_idx) {
1004 llvm::consumeError(value_at_idx.takeError());
1005 return nullptr;
1006 }
1007 llvm::Expected<lldb::addr_t> key_at_idx =
1008 process_sp->ReadPointerFromMemory(value_ptr);
1009 if (!key_at_idx) {
1010 llvm::consumeError(key_at_idx.takeError());
1011 return nullptr;
1012 }
1013
1014 auto pair_type =
1015 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1016
1017 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1018
1019 if (ptr_size == 8) {
1020 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1021 *data_ptr = *key_at_idx;
1022 *(data_ptr + 1) = *value_at_idx;
1023 } else {
1024 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1025 *data_ptr = *key_at_idx;
1026 *(data_ptr + 1) = *value_at_idx;
1027 }
1028
1029 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1031 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1032
1033 return m_pair;
1034}
1035
1036template <typename D32, typename D64>
1041
1042template <typename D32, typename D64>
1046 delete m_data_32;
1047 m_data_32 = nullptr;
1048 delete m_data_64;
1049 m_data_64 = nullptr;
1050}
1051
1052template <typename D32, typename D64>
1053llvm::Expected<uint32_t>
1056 if (!m_data_32 && !m_data_64)
1057 return 0;
1058 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1059}
1060
1061template <typename D32, typename D64>
1064 D64>::Update() {
1065 m_children.clear();
1066 ValueObjectSP valobj_sp = m_backend.GetSP();
1067 m_ptr_size = 0;
1068 delete m_data_32;
1069 m_data_32 = nullptr;
1070 delete m_data_64;
1071 m_data_64 = nullptr;
1072 if (!valobj_sp)
1074 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1075 Status error;
1076 error.Clear();
1077 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1078 if (!process_sp)
1080 m_ptr_size = process_sp->GetAddressByteSize();
1081 m_order = process_sp->GetByteOrder();
1082 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1083 if (m_ptr_size == 4) {
1084 m_data_32 = new D32();
1085 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1086 error);
1087 } else {
1088 m_data_64 = new D64();
1089 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1090 error);
1091 }
1092
1093 return error.Success() ? lldb::ChildCacheState::eReuse
1095}
1096
1097template <typename D32, typename D64>
1100 D32, D64>::GetChildAtIndex(uint32_t idx) {
1101 lldb::addr_t m_keys_ptr;
1102 lldb::addr_t m_values_ptr;
1103 if (m_data_32) {
1104 uint32_t size = m_data_32->GetSize();
1105 m_keys_ptr = m_data_32->_buffer;
1106 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1107 } else {
1108 uint32_t size = m_data_64->GetSize();
1109 m_keys_ptr = m_data_64->_buffer;
1110 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1111 }
1112
1113 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1114
1115 if (idx >= num_children)
1116 return lldb::ValueObjectSP();
1117
1118 if (m_children.empty()) {
1119 // do the scan phase
1120 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1121
1122 uint32_t tries = 0;
1123 uint32_t test_idx = 0;
1124
1125 while (tries < num_children) {
1126 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1127 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1128 ;
1129 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1130 if (!process_sp)
1131 return lldb::ValueObjectSP();
1132 llvm::Expected<lldb::addr_t> key =
1133 process_sp->ReadPointerFromMemory(key_at_idx);
1134 if (!key) {
1135 llvm::consumeError(key.takeError());
1136 return lldb::ValueObjectSP();
1137 }
1138 key_at_idx = *key;
1139 llvm::Expected<lldb::addr_t> val =
1140 process_sp->ReadPointerFromMemory(val_at_idx);
1141 if (!val) {
1142 llvm::consumeError(val.takeError());
1143 return lldb::ValueObjectSP();
1144 }
1145 val_at_idx = *val;
1146
1147 test_idx++;
1148
1149 if (!key_at_idx || !val_at_idx)
1150 continue;
1151 tries++;
1152
1153 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1155
1156 m_children.push_back(descriptor);
1157 }
1158 }
1159
1160 if (idx >= m_children.size()) // should never happen
1161 return lldb::ValueObjectSP();
1162
1163 DictionaryItemDescriptor &dict_item = m_children[idx];
1164 if (!dict_item.valobj_sp) {
1165 if (!m_pair_type.IsValid()) {
1166 TargetSP target_sp(m_backend.GetTargetSP());
1167 if (!target_sp)
1168 return ValueObjectSP();
1169 m_pair_type = GetLLDBNSPairType(target_sp);
1170 }
1171 if (!m_pair_type.IsValid())
1172 return ValueObjectSP();
1173
1174 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1175
1176 if (m_ptr_size == 8) {
1177 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1178 *data_ptr = dict_item.key_ptr;
1179 *(data_ptr + 1) = dict_item.val_ptr;
1180 } else {
1181 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1182 *data_ptr = dict_item.key_ptr;
1183 *(data_ptr + 1) = dict_item.val_ptr;
1184 }
1185
1186 StreamString idx_name;
1187 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1188 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1190 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1191 }
1192 return dict_item.valobj_sp;
1193}
1194
1198
1206
1209 if (!m_data_32 && !m_data_64)
1210 return 0;
1211 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1212}
1213
1216 m_children.clear();
1217 ValueObjectSP valobj_sp = m_backend.GetSP();
1218 m_ptr_size = 0;
1219 delete m_data_32;
1220 m_data_32 = nullptr;
1221 delete m_data_64;
1222 m_data_64 = nullptr;
1223 if (!valobj_sp)
1225 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1226 Status error;
1227 error.Clear();
1228 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1229 if (!process_sp)
1231 m_ptr_size = process_sp->GetAddressByteSize();
1232 m_order = process_sp->GetByteOrder();
1233 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1234 if (m_ptr_size == 4) {
1236 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1237 error);
1238 } else {
1240 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1241 error);
1242 }
1243
1244 return error.Success() ? lldb::ChildCacheState::eReuse
1246}
1247
1251 lldb::addr_t m_keys_ptr =
1252 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1253 lldb::addr_t m_values_ptr =
1254 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1255
1256 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1257
1258 if (idx >= num_children)
1259 return lldb::ValueObjectSP();
1260
1261 if (m_children.empty()) {
1262 // do the scan phase
1263 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1264
1265 uint32_t tries = 0;
1266 uint32_t test_idx = 0;
1267
1268 while (tries < num_children) {
1269 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1270 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1271 ;
1272 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1273 if (!process_sp)
1274 return lldb::ValueObjectSP();
1275 llvm::Expected<lldb::addr_t> key =
1276 process_sp->ReadPointerFromMemory(key_at_idx);
1277 if (!key) {
1278 llvm::consumeError(key.takeError());
1279 return lldb::ValueObjectSP();
1280 }
1281 key_at_idx = *key;
1282 llvm::Expected<lldb::addr_t> val =
1283 process_sp->ReadPointerFromMemory(val_at_idx);
1284 if (!val) {
1285 llvm::consumeError(val.takeError());
1286 return lldb::ValueObjectSP();
1287 }
1288 val_at_idx = *val;
1289
1290 test_idx++;
1291
1292 if (!key_at_idx || !val_at_idx)
1293 continue;
1294 tries++;
1295
1296 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1298
1299 m_children.push_back(descriptor);
1300 }
1301 }
1302
1303 if (idx >= m_children.size()) // should never happen
1304 return lldb::ValueObjectSP();
1305
1306 DictionaryItemDescriptor &dict_item = m_children[idx];
1307 if (!dict_item.valobj_sp) {
1308 if (!m_pair_type.IsValid()) {
1309 TargetSP target_sp(m_backend.GetTargetSP());
1310 if (!target_sp)
1311 return ValueObjectSP();
1312 m_pair_type = GetLLDBNSPairType(target_sp);
1313 }
1314 if (!m_pair_type.IsValid())
1315 return ValueObjectSP();
1316
1317 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1318
1319 if (m_ptr_size == 8) {
1320 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1321 *data_ptr = dict_item.key_ptr;
1322 *(data_ptr + 1) = dict_item.val_ptr;
1323 } else {
1324 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1325 *data_ptr = dict_item.key_ptr;
1326 *(data_ptr + 1) = dict_item.val_ptr;
1327 }
1328
1329 StreamString idx_name;
1330 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1331 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1333 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1334 }
1335 return dict_item.valobj_sp;
1336}
1337
1339 ValueObject &, Stream &, const TypeSummaryOptions &);
1340
1342 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.
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
static Language * FindPlugin(lldb::LanguageType language)
Definition Language.cpp:89
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:369
size_t ReadMemory(const ProcessAddress &process_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition Process.cpp:2114
uint32_t GetAddressByteSize() const
Definition Process.cpp:3973
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)
SyntheticChildrenFrontEnd(ValueObject &backend)
lldb::ValueObjectSP CreateChildValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type)
lldb::LanguageType GetLanguage() const
static bool CompleteTagDeclarationDefinition(const CompilerType &type)
static bool StartTagDeclarationDefinition(const CompilerType &type)
static clang::FieldDecl * AddFieldToRecordType(const CompilerType &type, llvm::StringRef name, const CompilerType &field_type, uint32_t bitfield_bit_size)
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...
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
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
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) 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(llvm::StringRef name) override
Determine the index of a named child.
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
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
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)
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