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 ConstString class_name) {
42 return class_name.GetStringRef().starts_with(m_prefix.GetStringRef());
43}
44
47
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(ConstString 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 ConstString class_name(descriptor->GetClassName());
418 llvm::StringRef class_name_ref(class_name.GetStringRef());
419
420 static constexpr llvm::StringLiteral g_DictionaryI("__NSDictionaryI");
421 static constexpr llvm::StringLiteral g_DictionaryM("__NSDictionaryM");
422 static constexpr llvm::StringLiteral g_DictionaryMLegacy(
423 "__NSDictionaryM_Legacy");
424 static constexpr llvm::StringLiteral g_DictionaryMImmutable(
425 "__NSDictionaryM_Immutable");
426 static constexpr llvm::StringLiteral g_DictionaryMFrozen(
427 "__NSFrozenDictionaryM");
428 static constexpr llvm::StringLiteral g_Dictionary1(
429 "__NSSingleEntryDictionaryI");
430 static constexpr llvm::StringLiteral g_Dictionary0("__NSDictionary0");
431 static constexpr llvm::StringLiteral g_DictionaryCF("__CFDictionary");
432 static constexpr llvm::StringLiteral g_DictionaryNSCF("__NSCFDictionary");
433 static constexpr llvm::StringLiteral g_DictionaryCFRef("CFDictionaryRef");
434 static constexpr llvm::StringLiteral g_ConstantDictionary(
435 "NSConstantDictionary");
436
437 if (class_name_ref.empty())
438 return false;
439
440 if (class_name_ref == g_DictionaryI ||
441 class_name_ref == g_DictionaryMImmutable) {
443 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
444 ptr_size, 0, error);
445 if (error.Fail())
446 return false;
447
448 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
449 } else if (class_name_ref == g_ConstantDictionary) {
451 value = process_sp->ReadUnsignedIntegerFromMemory(
452 valobj_addr + 2 * ptr_size, ptr_size, 0, error);
453 if (error.Fail())
454 return false;
455 } else if (class_name_ref == g_DictionaryM ||
456 class_name_ref == g_DictionaryMLegacy ||
457 class_name_ref == g_DictionaryMFrozen) {
458 AppleObjCRuntime *apple_runtime =
459 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
461 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
462 value =
463 Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr, error);
464 } else {
465 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
466 ptr_size, 0, error);
467 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
468 }
469 if (error.Fail())
470 return false;
471 } else if (class_name_ref == g_Dictionary1) {
472 value = 1;
473 } else if (class_name_ref == g_Dictionary0) {
474 value = 0;
475 } else if (class_name_ref == g_DictionaryCF ||
476 class_name_ref == g_DictionaryNSCF ||
477 class_name_ref == 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 llvm::StringRef class_name_ref(class_name.GetStringRef());
532
533 static constexpr llvm::StringLiteral g_DictionaryI("__NSDictionaryI");
534 static constexpr llvm::StringLiteral g_DictionaryM("__NSDictionaryM");
535 static constexpr llvm::StringLiteral g_Dictionary1(
536 "__NSSingleEntryDictionaryI");
537 static constexpr llvm::StringLiteral g_DictionaryMFrozen(
538 "__NSFrozenDictionaryM");
539 static constexpr llvm::StringLiteral g_DictionaryMLegacy(
540 "__NSDictionaryM_Legacy");
541 static constexpr llvm::StringLiteral g_DictionaryCF("__CFDictionary");
542 static constexpr llvm::StringLiteral g_DictionaryNSCF("__NSCFDictionary");
543 static constexpr llvm::StringLiteral g_DictionaryCFRef("CFDictionaryRef");
544 static constexpr llvm::StringLiteral g_ConstantDictionary(
545 "NSConstantDictionary");
546
547 if (class_name_ref.empty())
548 return nullptr;
549
550 if (class_name_ref == g_DictionaryI) {
551 return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
552 } else if (class_name_ref == g_ConstantDictionary) {
553 return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
554 } else if (class_name_ref == g_DictionaryM ||
555 class_name_ref == g_DictionaryMFrozen) {
556 if (runtime->GetFoundationVersion() >= 1437) {
558 } else if (runtime->GetFoundationVersion() >= 1428) {
560 } else {
561 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
562 }
563 } else if (class_name_ref == g_DictionaryMLegacy) {
564 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
565 } else if (class_name_ref == g_Dictionary1) {
566 return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
567 } else if (class_name_ref == g_DictionaryCF ||
568 class_name_ref == g_DictionaryNSCF ||
569 class_name_ref == g_DictionaryCFRef) {
570 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
571 } else {
573 for (auto &candidate : map) {
574 if (candidate.first && candidate.first->Match((class_name)))
575 return candidate.second(synth, valobj_sp);
576 }
577 }
578
579 return nullptr;
580}
581
585
593
594llvm::Expected<uint32_t> lldb_private::formatters::
596 if (!m_data_32 && !m_data_64)
597 return 0;
598 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
599}
600
603 m_children.clear();
604 delete m_data_32;
605 m_data_32 = nullptr;
606 delete m_data_64;
607 m_data_64 = nullptr;
608 m_ptr_size = 0;
609 ValueObjectSP valobj_sp = m_backend.GetSP();
610 if (!valobj_sp)
612 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
614 error.Clear();
615 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
616 if (!process_sp)
618 m_ptr_size = process_sp->GetAddressByteSize();
619 m_order = process_sp->GetByteOrder();
620 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
621 if (m_ptr_size == 4) {
623 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
624 error);
625 } else {
627 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
628 error);
629 }
630 if (error.Fail())
632 m_data_ptr = data_location + m_ptr_size;
634}
635
638 uint32_t idx) {
639 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
640
641 if (idx >= num_children)
642 return lldb::ValueObjectSP();
643
644 if (m_children.empty()) {
645 // do the scan phase
646 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
647
648 uint32_t tries = 0;
649 uint32_t test_idx = 0;
650
651 while (tries < num_children) {
652 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
653 val_at_idx = key_at_idx + m_ptr_size;
654 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
655 if (!process_sp)
656 return lldb::ValueObjectSP();
658 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
659 if (error.Fail())
660 return lldb::ValueObjectSP();
661 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
662 if (error.Fail())
663 return lldb::ValueObjectSP();
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
761 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
762
763 uint32_t tries = 0;
764 uint32_t test_idx = 0;
765
766 // Iterate over inferior memory, reading key/value pointers by shifting each
767 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
768 // fails, otherwise, continue until the number of tries matches the number
769 // of childen.
770 while (tries < num_children) {
771 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
772 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
773
774 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
775 if (error.Fail())
776 return lldb::ValueObjectSP();
777 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
778 if (error.Fail())
779 return lldb::ValueObjectSP();
780
781 test_idx++;
782
783 if (!key_at_idx || !val_at_idx)
784 continue;
785 tries++;
786
787 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
789
790 m_children.push_back(descriptor);
791 }
792 }
793
794 if (idx >= m_children.size()) // should never happen
795 return lldb::ValueObjectSP();
796
797 DictionaryItemDescriptor &dict_item = m_children[idx];
798 if (!dict_item.valobj_sp) {
799 if (!m_pair_type.IsValid()) {
800 TargetSP target_sp(m_backend.GetTargetSP());
801 if (!target_sp)
802 return ValueObjectSP();
803 m_pair_type = GetLLDBNSPairType(target_sp);
804 }
805 if (!m_pair_type.IsValid())
806 return ValueObjectSP();
807
808 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
809
810 switch (m_ptr_size) {
811 case 0: // architecture has no clue - fail
812 return lldb::ValueObjectSP();
813 case 4: {
814 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
815 *data_ptr = dict_item.key_ptr;
816 *(data_ptr + 1) = dict_item.val_ptr;
817 } break;
818 case 8: {
819 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
820 *data_ptr = dict_item.key_ptr;
821 *(data_ptr + 1) = dict_item.val_ptr;
822 } break;
823 default:
824 lldbassert(false && "pointer size is not 4 nor 8");
825 }
826
827 StreamString idx_name;
828 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
829 DataExtractor data(buffer_sp, m_order, m_ptr_size);
831 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
832 }
833 return dict_item.valobj_sp;
834}
835
839
844
847 ValueObjectSP valobj_sp = m_backend.GetSP();
848 if (!valobj_sp)
850 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
852 error.Clear();
853 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
854 if (!process_sp)
856 m_ptr_size = process_sp->GetAddressByteSize();
857 m_order = process_sp->GetByteOrder();
858 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
859 m_size = process_sp->ReadUnsignedIntegerFromMemory(
860 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
861 if (error.Fail())
863 m_keys_ptr =
864 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
865 if (error.Fail())
868 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
869
870 return error.Success() ? lldb::ChildCacheState::eReuse
872}
873
876 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
877
878 if (idx >= num_children)
879 return lldb::ValueObjectSP();
880
881 if (m_children.empty()) {
882 // do the scan phase
883 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
884 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
885 if (!process_sp)
886 return lldb::ValueObjectSP();
887
888 for (unsigned int child = 0; child < num_children; ++child) {
890 key_at_idx = process_sp->ReadPointerFromMemory(
891 m_keys_ptr + child * m_ptr_size, error);
892 if (error.Fail())
893 return lldb::ValueObjectSP();
894 val_at_idx = process_sp->ReadPointerFromMemory(
895 m_objects_ptr + child * m_ptr_size, error);
896 if (error.Fail())
897 return lldb::ValueObjectSP();
898 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
900 m_children.push_back(descriptor);
901 }
902 }
903
904 if (idx >= m_children.size()) // should never happen
905 return lldb::ValueObjectSP();
906
907 DictionaryItemDescriptor &dict_item = m_children[idx];
908 if (!dict_item.valobj_sp) {
909 if (!m_pair_type.IsValid()) {
910 TargetSP target_sp(m_backend.GetTargetSP());
911 if (!target_sp)
912 return ValueObjectSP();
913 m_pair_type = GetLLDBNSPairType(target_sp);
914 }
915 if (!m_pair_type.IsValid())
916 return ValueObjectSP();
917
918 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
919
920 if (m_ptr_size == 8) {
921 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
922 *data_ptr = dict_item.key_ptr;
923 *(data_ptr + 1) = dict_item.val_ptr;
924 } else {
925 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
926 *data_ptr = dict_item.key_ptr;
927 *(data_ptr + 1) = dict_item.val_ptr;
928 }
929
930 StreamString idx_name;
931 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
932 DataExtractor data(buffer_sp, m_order, m_ptr_size);
934 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
935 }
936 return dict_item.valobj_sp;
937}
938
942
943llvm::Expected<size_t> lldb_private::formatters::
945 static constexpr llvm::StringLiteral g_zero("[0]");
946 if (name == g_zero)
947 return 0;
948 return llvm::createStringErrorV("type has no child named '{0}'", name);
949}
950
955
961
964 uint32_t idx) {
965 if (idx != 0)
966 return lldb::ValueObjectSP();
967
968 if (m_pair.get())
969 return m_pair;
970
971 auto process_sp(m_backend.GetProcessSP());
972 if (!process_sp)
973 return nullptr;
974
975 auto ptr_size = process_sp->GetAddressByteSize();
976
977 lldb::addr_t key_ptr =
978 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
979 lldb::addr_t value_ptr = key_ptr + ptr_size;
980
982
983 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
984 if (error.Fail())
985 return nullptr;
986 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
987 if (error.Fail())
988 return nullptr;
989
990 auto pair_type =
991 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
992
993 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
994
995 if (ptr_size == 8) {
996 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
997 *data_ptr = key_at_idx;
998 *(data_ptr + 1) = value_at_idx;
999 } else {
1000 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1001 *data_ptr = key_at_idx;
1002 *(data_ptr + 1) = value_at_idx;
1003 }
1004
1005 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1007 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1008
1009 return m_pair;
1010}
1011
1012template <typename D32, typename D64>
1017
1018template <typename D32, typename D64>
1022 delete m_data_32;
1023 m_data_32 = nullptr;
1024 delete m_data_64;
1025 m_data_64 = nullptr;
1026}
1027
1028template <typename D32, typename D64>
1029llvm::Expected<uint32_t>
1032 if (!m_data_32 && !m_data_64)
1033 return 0;
1034 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1035}
1036
1037template <typename D32, typename D64>
1040 D64>::Update() {
1041 m_children.clear();
1042 ValueObjectSP valobj_sp = m_backend.GetSP();
1043 m_ptr_size = 0;
1044 delete m_data_32;
1045 m_data_32 = nullptr;
1046 delete m_data_64;
1047 m_data_64 = nullptr;
1048 if (!valobj_sp)
1050 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1051 Status error;
1052 error.Clear();
1053 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1054 if (!process_sp)
1056 m_ptr_size = process_sp->GetAddressByteSize();
1057 m_order = process_sp->GetByteOrder();
1058 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1059 if (m_ptr_size == 4) {
1060 m_data_32 = new D32();
1061 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1062 error);
1063 } else {
1064 m_data_64 = new D64();
1065 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1066 error);
1067 }
1068
1069 return error.Success() ? lldb::ChildCacheState::eReuse
1071}
1072
1073template <typename D32, typename D64>
1076 D32, D64>::GetChildAtIndex(uint32_t idx) {
1077 lldb::addr_t m_keys_ptr;
1078 lldb::addr_t m_values_ptr;
1079 if (m_data_32) {
1080 uint32_t size = m_data_32->GetSize();
1081 m_keys_ptr = m_data_32->_buffer;
1082 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1083 } else {
1084 uint32_t size = m_data_64->GetSize();
1085 m_keys_ptr = m_data_64->_buffer;
1086 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1087 }
1088
1089 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1090
1091 if (idx >= num_children)
1092 return lldb::ValueObjectSP();
1093
1094 if (m_children.empty()) {
1095 // do the scan phase
1096 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1097
1098 uint32_t tries = 0;
1099 uint32_t test_idx = 0;
1100
1101 while (tries < num_children) {
1102 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1103 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1104 ;
1105 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1106 if (!process_sp)
1107 return lldb::ValueObjectSP();
1108 Status error;
1109 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1110 if (error.Fail())
1111 return lldb::ValueObjectSP();
1112 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1113 if (error.Fail())
1114 return lldb::ValueObjectSP();
1115
1116 test_idx++;
1117
1118 if (!key_at_idx || !val_at_idx)
1119 continue;
1120 tries++;
1121
1122 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1124
1125 m_children.push_back(descriptor);
1126 }
1127 }
1128
1129 if (idx >= m_children.size()) // should never happen
1130 return lldb::ValueObjectSP();
1131
1132 DictionaryItemDescriptor &dict_item = m_children[idx];
1133 if (!dict_item.valobj_sp) {
1134 if (!m_pair_type.IsValid()) {
1135 TargetSP target_sp(m_backend.GetTargetSP());
1136 if (!target_sp)
1137 return ValueObjectSP();
1138 m_pair_type = GetLLDBNSPairType(target_sp);
1139 }
1140 if (!m_pair_type.IsValid())
1141 return ValueObjectSP();
1142
1143 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1144
1145 if (m_ptr_size == 8) {
1146 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1147 *data_ptr = dict_item.key_ptr;
1148 *(data_ptr + 1) = dict_item.val_ptr;
1149 } else {
1150 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1151 *data_ptr = dict_item.key_ptr;
1152 *(data_ptr + 1) = dict_item.val_ptr;
1153 }
1154
1155 StreamString idx_name;
1156 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1157 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1159 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1160 }
1161 return dict_item.valobj_sp;
1162}
1163
1167
1175
1178 if (!m_data_32 && !m_data_64)
1179 return 0;
1180 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1181}
1182
1185 m_children.clear();
1186 ValueObjectSP valobj_sp = m_backend.GetSP();
1187 m_ptr_size = 0;
1188 delete m_data_32;
1189 m_data_32 = nullptr;
1190 delete m_data_64;
1191 m_data_64 = nullptr;
1192 if (!valobj_sp)
1194 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1195 Status error;
1196 error.Clear();
1197 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1198 if (!process_sp)
1200 m_ptr_size = process_sp->GetAddressByteSize();
1201 m_order = process_sp->GetByteOrder();
1202 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1203 if (m_ptr_size == 4) {
1205 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1206 error);
1207 } else {
1209 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1210 error);
1211 }
1212
1213 return error.Success() ? lldb::ChildCacheState::eReuse
1215}
1216
1220 lldb::addr_t m_keys_ptr =
1221 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1222 lldb::addr_t m_values_ptr =
1223 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1224
1225 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1226
1227 if (idx >= num_children)
1228 return lldb::ValueObjectSP();
1229
1230 if (m_children.empty()) {
1231 // do the scan phase
1232 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1233
1234 uint32_t tries = 0;
1235 uint32_t test_idx = 0;
1236
1237 while (tries < num_children) {
1238 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1239 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1240 ;
1241 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1242 if (!process_sp)
1243 return lldb::ValueObjectSP();
1244 Status error;
1245 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1246 if (error.Fail())
1247 return lldb::ValueObjectSP();
1248 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1249 if (error.Fail())
1250 return lldb::ValueObjectSP();
1251
1252 test_idx++;
1253
1254 if (!key_at_idx || !val_at_idx)
1255 continue;
1256 tries++;
1257
1258 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1260
1261 m_children.push_back(descriptor);
1262 }
1263 }
1264
1265 if (idx >= m_children.size()) // should never happen
1266 return lldb::ValueObjectSP();
1267
1268 DictionaryItemDescriptor &dict_item = m_children[idx];
1269 if (!dict_item.valobj_sp) {
1270 if (!m_pair_type.IsValid()) {
1271 TargetSP target_sp(m_backend.GetTargetSP());
1272 if (!target_sp)
1273 return ValueObjectSP();
1274 m_pair_type = GetLLDBNSPairType(target_sp);
1275 }
1276 if (!m_pair_type.IsValid())
1277 return ValueObjectSP();
1278
1279 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1280
1281 if (m_ptr_size == 8) {
1282 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1283 *data_ptr = dict_item.key_ptr;
1284 *(data_ptr + 1) = dict_item.val_ptr;
1285 } else {
1286 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1287 *data_ptr = dict_item.key_ptr;
1288 *(data_ptr + 1) = dict_item.val_ptr;
1289 }
1290
1291 StreamString idx_name;
1292 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1293 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1295 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1296 }
1297 return dict_item.valobj_sp;
1298}
1299
1301 ValueObject &, Stream &, const TypeSummaryOptions &);
1302
1304 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 uniqued constant string class.
Definition ConstString.h:40
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
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:359
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:2038
uint32_t GetAddressByteSize() const
Definition Process.cpp:3930
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...
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
Determine the index of a named child.
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