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();
657 llvm::Expected<lldb::addr_t> key =
658 process_sp->ReadPointerFromMemory(key_at_idx);
659 if (!key) {
660 llvm::consumeError(key.takeError());
661 return lldb::ValueObjectSP();
662 }
663 key_at_idx = *key;
664 llvm::Expected<lldb::addr_t> val =
665 process_sp->ReadPointerFromMemory(val_at_idx);
666 if (!val) {
667 llvm::consumeError(val.takeError());
668 return lldb::ValueObjectSP();
669 }
670 val_at_idx = *val;
671
672 test_idx++;
673
674 if (!key_at_idx || !val_at_idx)
675 continue;
676 tries++;
677
678 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
680
681 m_children.push_back(descriptor);
682 }
683 }
684
685 if (idx >= m_children.size()) // should never happen
686 return lldb::ValueObjectSP();
687
688 DictionaryItemDescriptor &dict_item = m_children[idx];
689 if (!dict_item.valobj_sp) {
690 if (!m_pair_type.IsValid()) {
691 TargetSP target_sp(m_backend.GetTargetSP());
692 if (!target_sp)
693 return ValueObjectSP();
694 m_pair_type = GetLLDBNSPairType(target_sp);
695 }
696 if (!m_pair_type.IsValid())
697 return ValueObjectSP();
698
699 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
700
701 if (m_ptr_size == 8) {
702 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
703 *data_ptr = dict_item.key_ptr;
704 *(data_ptr + 1) = dict_item.val_ptr;
705 } else {
706 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
707 *data_ptr = dict_item.key_ptr;
708 *(data_ptr + 1) = dict_item.val_ptr;
709 }
710
711 StreamString idx_name;
712 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
713 DataExtractor data(buffer_sp, m_order, m_ptr_size);
715 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
716 }
717 return dict_item.valobj_sp;
718}
719
724
725llvm::Expected<uint32_t> lldb_private::formatters::
727 if (!m_hashtable.IsValid())
728 return 0;
729 return m_hashtable.GetCount();
730}
731
734 m_children.clear();
735 ValueObjectSP valobj_sp = m_backend.GetSP();
736 m_ptr_size = 0;
737 if (!valobj_sp)
739 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
740
741 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
742 if (!process_sp)
744 m_ptr_size = process_sp->GetAddressByteSize();
745 m_order = process_sp->GetByteOrder();
746 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref)
749}
750
753 uint32_t idx) {
754 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
755 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
756
757 const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
758
759 if (idx >= num_children)
760 return lldb::ValueObjectSP();
761
762 if (m_children.empty()) {
763 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
764 if (!process_sp)
765 return lldb::ValueObjectSP();
766
767 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
768
769 uint32_t tries = 0;
770 uint32_t test_idx = 0;
771
772 // Iterate over inferior memory, reading key/value pointers by shifting each
773 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
774 // fails, otherwise, continue until the number of tries matches the number
775 // of childen.
776 while (tries < num_children) {
777 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
778 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
779
780 llvm::Expected<lldb::addr_t> key =
781 process_sp->ReadPointerFromMemory(key_at_idx);
782 if (!key) {
783 llvm::consumeError(key.takeError());
784 return lldb::ValueObjectSP();
785 }
786 key_at_idx = *key;
787 llvm::Expected<lldb::addr_t> val =
788 process_sp->ReadPointerFromMemory(val_at_idx);
789 if (!val) {
790 llvm::consumeError(val.takeError());
791 return lldb::ValueObjectSP();
792 }
793 val_at_idx = *val;
794
795 test_idx++;
796
797 if (!key_at_idx || !val_at_idx)
798 continue;
799 tries++;
800
801 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
803
804 m_children.push_back(descriptor);
805 }
806 }
807
808 if (idx >= m_children.size()) // should never happen
809 return lldb::ValueObjectSP();
810
811 DictionaryItemDescriptor &dict_item = m_children[idx];
812 if (!dict_item.valobj_sp) {
813 if (!m_pair_type.IsValid()) {
814 TargetSP target_sp(m_backend.GetTargetSP());
815 if (!target_sp)
816 return ValueObjectSP();
817 m_pair_type = GetLLDBNSPairType(target_sp);
818 }
819 if (!m_pair_type.IsValid())
820 return ValueObjectSP();
821
822 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
823
824 switch (m_ptr_size) {
825 case 0: // architecture has no clue - fail
826 return lldb::ValueObjectSP();
827 case 4: {
828 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
829 *data_ptr = dict_item.key_ptr;
830 *(data_ptr + 1) = dict_item.val_ptr;
831 } break;
832 case 8: {
833 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
834 *data_ptr = dict_item.key_ptr;
835 *(data_ptr + 1) = dict_item.val_ptr;
836 } break;
837 default:
838 lldbassert(false && "pointer size is not 4 nor 8");
839 }
840
841 StreamString idx_name;
842 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
843 DataExtractor data(buffer_sp, m_order, m_ptr_size);
845 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
846 }
847 return dict_item.valobj_sp;
848}
849
853
858
861 ValueObjectSP valobj_sp = m_backend.GetSP();
862 if (!valobj_sp)
864 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
866 error.Clear();
867 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
868 if (!process_sp)
870 m_ptr_size = process_sp->GetAddressByteSize();
871 m_order = process_sp->GetByteOrder();
872 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
873 m_size = process_sp->ReadUnsignedIntegerFromMemory(
874 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
875 if (error.Fail())
877 llvm::Expected<lldb::addr_t> keys_ptr =
878 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size);
879 if (!keys_ptr) {
880 llvm::consumeError(keys_ptr.takeError());
882 }
883 m_keys_ptr = *keys_ptr;
884 llvm::Expected<lldb::addr_t> objects_ptr =
885 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size);
886 if (!objects_ptr) {
887 llvm::consumeError(objects_ptr.takeError());
889 }
890 m_objects_ptr = *objects_ptr;
891
893}
894
897 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
898
899 if (idx >= num_children)
900 return lldb::ValueObjectSP();
901
902 if (m_children.empty()) {
903 // do the scan phase
904 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
905 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
906 if (!process_sp)
907 return lldb::ValueObjectSP();
908
909 for (unsigned int child = 0; child < num_children; ++child) {
910 llvm::Expected<lldb::addr_t> key =
911 process_sp->ReadPointerFromMemory(m_keys_ptr + child * m_ptr_size);
912 if (!key) {
913 llvm::consumeError(key.takeError());
914 return lldb::ValueObjectSP();
915 }
916 key_at_idx = *key;
917 llvm::Expected<lldb::addr_t> val =
918 process_sp->ReadPointerFromMemory(m_objects_ptr + child * m_ptr_size);
919 if (!val) {
920 llvm::consumeError(val.takeError());
921 return lldb::ValueObjectSP();
922 }
923 val_at_idx = *val;
924 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
926 m_children.push_back(descriptor);
927 }
928 }
929
930 if (idx >= m_children.size()) // should never happen
931 return lldb::ValueObjectSP();
932
933 DictionaryItemDescriptor &dict_item = m_children[idx];
934 if (!dict_item.valobj_sp) {
935 if (!m_pair_type.IsValid()) {
936 TargetSP target_sp(m_backend.GetTargetSP());
937 if (!target_sp)
938 return ValueObjectSP();
939 m_pair_type = GetLLDBNSPairType(target_sp);
940 }
941 if (!m_pair_type.IsValid())
942 return ValueObjectSP();
943
944 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
945
946 if (m_ptr_size == 8) {
947 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
948 *data_ptr = dict_item.key_ptr;
949 *(data_ptr + 1) = dict_item.val_ptr;
950 } else {
951 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
952 *data_ptr = dict_item.key_ptr;
953 *(data_ptr + 1) = dict_item.val_ptr;
954 }
955
956 StreamString idx_name;
957 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
958 DataExtractor data(buffer_sp, m_order, m_ptr_size);
960 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
961 }
962 return dict_item.valobj_sp;
963}
964
968
969llvm::Expected<size_t> lldb_private::formatters::
971 static constexpr llvm::StringLiteral g_zero("[0]");
972 if (name == g_zero)
973 return 0;
974 return llvm::createStringErrorV("type has no child named '{0}'", name);
975}
976
981
987
990 uint32_t idx) {
991 if (idx != 0)
992 return lldb::ValueObjectSP();
993
994 if (m_pair.get())
995 return m_pair;
996
997 auto process_sp(m_backend.GetProcessSP());
998 if (!process_sp)
999 return nullptr;
1000
1001 auto ptr_size = process_sp->GetAddressByteSize();
1002
1003 lldb::addr_t key_ptr =
1004 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1005 lldb::addr_t value_ptr = key_ptr + ptr_size;
1006
1007 llvm::Expected<lldb::addr_t> value_at_idx =
1008 process_sp->ReadPointerFromMemory(key_ptr);
1009 if (!value_at_idx) {
1010 llvm::consumeError(value_at_idx.takeError());
1011 return nullptr;
1012 }
1013 llvm::Expected<lldb::addr_t> key_at_idx =
1014 process_sp->ReadPointerFromMemory(value_ptr);
1015 if (!key_at_idx) {
1016 llvm::consumeError(key_at_idx.takeError());
1017 return nullptr;
1018 }
1019
1020 auto pair_type =
1021 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1022
1023 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1024
1025 if (ptr_size == 8) {
1026 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1027 *data_ptr = *key_at_idx;
1028 *(data_ptr + 1) = *value_at_idx;
1029 } else {
1030 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1031 *data_ptr = *key_at_idx;
1032 *(data_ptr + 1) = *value_at_idx;
1033 }
1034
1035 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1037 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1038
1039 return m_pair;
1040}
1041
1042template <typename D32, typename D64>
1047
1048template <typename D32, typename D64>
1052 delete m_data_32;
1053 m_data_32 = nullptr;
1054 delete m_data_64;
1055 m_data_64 = nullptr;
1056}
1057
1058template <typename D32, typename D64>
1059llvm::Expected<uint32_t>
1062 if (!m_data_32 && !m_data_64)
1063 return 0;
1064 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1065}
1066
1067template <typename D32, typename D64>
1070 D64>::Update() {
1071 m_children.clear();
1072 ValueObjectSP valobj_sp = m_backend.GetSP();
1073 m_ptr_size = 0;
1074 delete m_data_32;
1075 m_data_32 = nullptr;
1076 delete m_data_64;
1077 m_data_64 = nullptr;
1078 if (!valobj_sp)
1080 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1081 Status error;
1082 error.Clear();
1083 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1084 if (!process_sp)
1086 m_ptr_size = process_sp->GetAddressByteSize();
1087 m_order = process_sp->GetByteOrder();
1088 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1089 if (m_ptr_size == 4) {
1090 m_data_32 = new D32();
1091 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1092 error);
1093 } else {
1094 m_data_64 = new D64();
1095 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1096 error);
1097 }
1098
1099 return error.Success() ? lldb::ChildCacheState::eReuse
1101}
1102
1103template <typename D32, typename D64>
1106 D32, D64>::GetChildAtIndex(uint32_t idx) {
1107 lldb::addr_t m_keys_ptr;
1108 lldb::addr_t m_values_ptr;
1109 if (m_data_32) {
1110 uint32_t size = m_data_32->GetSize();
1111 m_keys_ptr = m_data_32->_buffer;
1112 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1113 } else {
1114 uint32_t size = m_data_64->GetSize();
1115 m_keys_ptr = m_data_64->_buffer;
1116 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1117 }
1118
1119 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1120
1121 if (idx >= num_children)
1122 return lldb::ValueObjectSP();
1123
1124 if (m_children.empty()) {
1125 // do the scan phase
1126 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1127
1128 uint32_t tries = 0;
1129 uint32_t test_idx = 0;
1130
1131 while (tries < num_children) {
1132 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1133 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1134 ;
1135 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1136 if (!process_sp)
1137 return lldb::ValueObjectSP();
1138 llvm::Expected<lldb::addr_t> key =
1139 process_sp->ReadPointerFromMemory(key_at_idx);
1140 if (!key) {
1141 llvm::consumeError(key.takeError());
1142 return lldb::ValueObjectSP();
1143 }
1144 key_at_idx = *key;
1145 llvm::Expected<lldb::addr_t> val =
1146 process_sp->ReadPointerFromMemory(val_at_idx);
1147 if (!val) {
1148 llvm::consumeError(val.takeError());
1149 return lldb::ValueObjectSP();
1150 }
1151 val_at_idx = *val;
1152
1153 test_idx++;
1154
1155 if (!key_at_idx || !val_at_idx)
1156 continue;
1157 tries++;
1158
1159 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1161
1162 m_children.push_back(descriptor);
1163 }
1164 }
1165
1166 if (idx >= m_children.size()) // should never happen
1167 return lldb::ValueObjectSP();
1168
1169 DictionaryItemDescriptor &dict_item = m_children[idx];
1170 if (!dict_item.valobj_sp) {
1171 if (!m_pair_type.IsValid()) {
1172 TargetSP target_sp(m_backend.GetTargetSP());
1173 if (!target_sp)
1174 return ValueObjectSP();
1175 m_pair_type = GetLLDBNSPairType(target_sp);
1176 }
1177 if (!m_pair_type.IsValid())
1178 return ValueObjectSP();
1179
1180 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1181
1182 if (m_ptr_size == 8) {
1183 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1184 *data_ptr = dict_item.key_ptr;
1185 *(data_ptr + 1) = dict_item.val_ptr;
1186 } else {
1187 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1188 *data_ptr = dict_item.key_ptr;
1189 *(data_ptr + 1) = dict_item.val_ptr;
1190 }
1191
1192 StreamString idx_name;
1193 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1194 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1196 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1197 }
1198 return dict_item.valobj_sp;
1199}
1200
1204
1212
1215 if (!m_data_32 && !m_data_64)
1216 return 0;
1217 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1218}
1219
1222 m_children.clear();
1223 ValueObjectSP valobj_sp = m_backend.GetSP();
1224 m_ptr_size = 0;
1225 delete m_data_32;
1226 m_data_32 = nullptr;
1227 delete m_data_64;
1228 m_data_64 = nullptr;
1229 if (!valobj_sp)
1231 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1232 Status error;
1233 error.Clear();
1234 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1235 if (!process_sp)
1237 m_ptr_size = process_sp->GetAddressByteSize();
1238 m_order = process_sp->GetByteOrder();
1239 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1240 if (m_ptr_size == 4) {
1242 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1243 error);
1244 } else {
1246 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1247 error);
1248 }
1249
1250 return error.Success() ? lldb::ChildCacheState::eReuse
1252}
1253
1257 lldb::addr_t m_keys_ptr =
1258 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1259 lldb::addr_t m_values_ptr =
1260 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1261
1262 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1263
1264 if (idx >= num_children)
1265 return lldb::ValueObjectSP();
1266
1267 if (m_children.empty()) {
1268 // do the scan phase
1269 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1270
1271 uint32_t tries = 0;
1272 uint32_t test_idx = 0;
1273
1274 while (tries < num_children) {
1275 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1276 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1277 ;
1278 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1279 if (!process_sp)
1280 return lldb::ValueObjectSP();
1281 llvm::Expected<lldb::addr_t> key =
1282 process_sp->ReadPointerFromMemory(key_at_idx);
1283 if (!key) {
1284 llvm::consumeError(key.takeError());
1285 return lldb::ValueObjectSP();
1286 }
1287 key_at_idx = *key;
1288 llvm::Expected<lldb::addr_t> val =
1289 process_sp->ReadPointerFromMemory(val_at_idx);
1290 if (!val) {
1291 llvm::consumeError(val.takeError());
1292 return lldb::ValueObjectSP();
1293 }
1294 val_at_idx = *val;
1295
1296 test_idx++;
1297
1298 if (!key_at_idx || !val_at_idx)
1299 continue;
1300 tries++;
1301
1302 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1304
1305 m_children.push_back(descriptor);
1306 }
1307 }
1308
1309 if (idx >= m_children.size()) // should never happen
1310 return lldb::ValueObjectSP();
1311
1312 DictionaryItemDescriptor &dict_item = m_children[idx];
1313 if (!dict_item.valobj_sp) {
1314 if (!m_pair_type.IsValid()) {
1315 TargetSP target_sp(m_backend.GetTargetSP());
1316 if (!target_sp)
1317 return ValueObjectSP();
1318 m_pair_type = GetLLDBNSPairType(target_sp);
1319 }
1320 if (!m_pair_type.IsValid())
1321 return ValueObjectSP();
1322
1323 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1324
1325 if (m_ptr_size == 8) {
1326 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1327 *data_ptr = dict_item.key_ptr;
1328 *(data_ptr + 1) = dict_item.val_ptr;
1329 } else {
1330 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1331 *data_ptr = dict_item.key_ptr;
1332 *(data_ptr + 1) = dict_item.val_ptr;
1333 }
1334
1335 StreamString idx_name;
1336 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1337 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1339 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1340 }
1341 return dict_item.valobj_sp;
1342}
1343
1345 ValueObject &, Stream &, const TypeSummaryOptions &);
1346
1348 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:367
virtual size_t ReadMemory(const ProcessAddress &process_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition Process.cpp:2081
uint32_t GetAddressByteSize() const
Definition Process.cpp:3977
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