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(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 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>
971 GetIndexOfChildWithName(llvm::StringRef name) {
972 static constexpr llvm::StringLiteral g_zero("[0]");
973 if (name == g_zero)
974 return 0;
975 return llvm::createStringErrorV("type has no child named '{0}'", name);
976}
977
982
988
991 uint32_t idx) {
992 if (idx != 0)
993 return lldb::ValueObjectSP();
994
995 if (m_pair.get())
996 return m_pair;
997
998 auto process_sp(m_backend.GetProcessSP());
999 if (!process_sp)
1000 return nullptr;
1001
1002 auto ptr_size = process_sp->GetAddressByteSize();
1003
1004 lldb::addr_t key_ptr =
1005 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
1006 lldb::addr_t value_ptr = key_ptr + ptr_size;
1007
1008 llvm::Expected<lldb::addr_t> value_at_idx =
1009 process_sp->ReadPointerFromMemory(key_ptr);
1010 if (!value_at_idx) {
1011 llvm::consumeError(value_at_idx.takeError());
1012 return nullptr;
1013 }
1014 llvm::Expected<lldb::addr_t> key_at_idx =
1015 process_sp->ReadPointerFromMemory(value_ptr);
1016 if (!key_at_idx) {
1017 llvm::consumeError(key_at_idx.takeError());
1018 return nullptr;
1019 }
1020
1021 auto pair_type =
1022 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
1023
1024 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
1025
1026 if (ptr_size == 8) {
1027 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1028 *data_ptr = *key_at_idx;
1029 *(data_ptr + 1) = *value_at_idx;
1030 } else {
1031 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1032 *data_ptr = *key_at_idx;
1033 *(data_ptr + 1) = *value_at_idx;
1034 }
1035
1036 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
1038 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
1039
1040 return m_pair;
1041}
1042
1043template <typename D32, typename D64>
1048
1049template <typename D32, typename D64>
1053 delete m_data_32;
1054 m_data_32 = nullptr;
1055 delete m_data_64;
1056 m_data_64 = nullptr;
1057}
1058
1059template <typename D32, typename D64>
1060llvm::Expected<uint32_t>
1063 if (!m_data_32 && !m_data_64)
1064 return 0;
1065 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1066}
1067
1068template <typename D32, typename D64>
1071 D64>::Update() {
1072 m_children.clear();
1073 ValueObjectSP valobj_sp = m_backend.GetSP();
1074 m_ptr_size = 0;
1075 delete m_data_32;
1076 m_data_32 = nullptr;
1077 delete m_data_64;
1078 m_data_64 = nullptr;
1079 if (!valobj_sp)
1081 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1082 Status error;
1083 error.Clear();
1084 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1085 if (!process_sp)
1087 m_ptr_size = process_sp->GetAddressByteSize();
1088 m_order = process_sp->GetByteOrder();
1089 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1090 if (m_ptr_size == 4) {
1091 m_data_32 = new D32();
1092 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1093 error);
1094 } else {
1095 m_data_64 = new D64();
1096 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1097 error);
1098 }
1099
1100 return error.Success() ? lldb::ChildCacheState::eReuse
1102}
1103
1104template <typename D32, typename D64>
1107 D32, D64>::GetChildAtIndex(uint32_t idx) {
1108 lldb::addr_t m_keys_ptr;
1109 lldb::addr_t m_values_ptr;
1110 if (m_data_32) {
1111 uint32_t size = m_data_32->GetSize();
1112 m_keys_ptr = m_data_32->_buffer;
1113 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1114 } else {
1115 uint32_t size = m_data_64->GetSize();
1116 m_keys_ptr = m_data_64->_buffer;
1117 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1118 }
1119
1120 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1121
1122 if (idx >= num_children)
1123 return lldb::ValueObjectSP();
1124
1125 if (m_children.empty()) {
1126 // do the scan phase
1127 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1128
1129 uint32_t tries = 0;
1130 uint32_t test_idx = 0;
1131
1132 while (tries < num_children) {
1133 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1134 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1135 ;
1136 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1137 if (!process_sp)
1138 return lldb::ValueObjectSP();
1139 llvm::Expected<lldb::addr_t> key =
1140 process_sp->ReadPointerFromMemory(key_at_idx);
1141 if (!key) {
1142 llvm::consumeError(key.takeError());
1143 return lldb::ValueObjectSP();
1144 }
1145 key_at_idx = *key;
1146 llvm::Expected<lldb::addr_t> val =
1147 process_sp->ReadPointerFromMemory(val_at_idx);
1148 if (!val) {
1149 llvm::consumeError(val.takeError());
1150 return lldb::ValueObjectSP();
1151 }
1152 val_at_idx = *val;
1153
1154 test_idx++;
1155
1156 if (!key_at_idx || !val_at_idx)
1157 continue;
1158 tries++;
1159
1160 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1162
1163 m_children.push_back(descriptor);
1164 }
1165 }
1166
1167 if (idx >= m_children.size()) // should never happen
1168 return lldb::ValueObjectSP();
1169
1170 DictionaryItemDescriptor &dict_item = m_children[idx];
1171 if (!dict_item.valobj_sp) {
1172 if (!m_pair_type.IsValid()) {
1173 TargetSP target_sp(m_backend.GetTargetSP());
1174 if (!target_sp)
1175 return ValueObjectSP();
1176 m_pair_type = GetLLDBNSPairType(target_sp);
1177 }
1178 if (!m_pair_type.IsValid())
1179 return ValueObjectSP();
1180
1181 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1182
1183 if (m_ptr_size == 8) {
1184 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1185 *data_ptr = dict_item.key_ptr;
1186 *(data_ptr + 1) = dict_item.val_ptr;
1187 } else {
1188 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1189 *data_ptr = dict_item.key_ptr;
1190 *(data_ptr + 1) = dict_item.val_ptr;
1191 }
1192
1193 StreamString idx_name;
1194 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1195 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1197 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1198 }
1199 return dict_item.valobj_sp;
1200}
1201
1205
1213
1216 if (!m_data_32 && !m_data_64)
1217 return 0;
1218 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1219}
1220
1223 m_children.clear();
1224 ValueObjectSP valobj_sp = m_backend.GetSP();
1225 m_ptr_size = 0;
1226 delete m_data_32;
1227 m_data_32 = nullptr;
1228 delete m_data_64;
1229 m_data_64 = nullptr;
1230 if (!valobj_sp)
1232 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1233 Status error;
1234 error.Clear();
1235 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1236 if (!process_sp)
1238 m_ptr_size = process_sp->GetAddressByteSize();
1239 m_order = process_sp->GetByteOrder();
1240 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1241 if (m_ptr_size == 4) {
1243 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1244 error);
1245 } else {
1247 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1248 error);
1249 }
1250
1251 return error.Success() ? lldb::ChildCacheState::eReuse
1253}
1254
1258 lldb::addr_t m_keys_ptr =
1259 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1260 lldb::addr_t m_values_ptr =
1261 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1262
1263 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1264
1265 if (idx >= num_children)
1266 return lldb::ValueObjectSP();
1267
1268 if (m_children.empty()) {
1269 // do the scan phase
1270 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1271
1272 uint32_t tries = 0;
1273 uint32_t test_idx = 0;
1274
1275 while (tries < num_children) {
1276 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1277 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1278 ;
1279 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1280 if (!process_sp)
1281 return lldb::ValueObjectSP();
1282 llvm::Expected<lldb::addr_t> key =
1283 process_sp->ReadPointerFromMemory(key_at_idx);
1284 if (!key) {
1285 llvm::consumeError(key.takeError());
1286 return lldb::ValueObjectSP();
1287 }
1288 key_at_idx = *key;
1289 llvm::Expected<lldb::addr_t> val =
1290 process_sp->ReadPointerFromMemory(val_at_idx);
1291 if (!val) {
1292 llvm::consumeError(val.takeError());
1293 return lldb::ValueObjectSP();
1294 }
1295 val_at_idx = *val;
1296
1297 test_idx++;
1298
1299 if (!key_at_idx || !val_at_idx)
1300 continue;
1301 tries++;
1302
1303 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1305
1306 m_children.push_back(descriptor);
1307 }
1308 }
1309
1310 if (idx >= m_children.size()) // should never happen
1311 return lldb::ValueObjectSP();
1312
1313 DictionaryItemDescriptor &dict_item = m_children[idx];
1314 if (!dict_item.valobj_sp) {
1315 if (!m_pair_type.IsValid()) {
1316 TargetSP target_sp(m_backend.GetTargetSP());
1317 if (!target_sp)
1318 return ValueObjectSP();
1319 m_pair_type = GetLLDBNSPairType(target_sp);
1320 }
1321 if (!m_pair_type.IsValid())
1322 return ValueObjectSP();
1323
1324 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1325
1326 if (m_ptr_size == 8) {
1327 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1328 *data_ptr = dict_item.key_ptr;
1329 *(data_ptr + 1) = dict_item.val_ptr;
1330 } else {
1331 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1332 *data_ptr = dict_item.key_ptr;
1333 *(data_ptr + 1) = dict_item.val_ptr;
1334 }
1335
1336 StreamString idx_name;
1337 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1338 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1340 idx_name.GetString(), data, m_exe_ctx_ref, m_pair_type);
1341 }
1342 return dict_item.valobj_sp;
1343}
1344
1346 ValueObject &, Stream &, const TypeSummaryOptions &);
1347
1349 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:86
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
virtual 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:3964
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