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
419 static const ConstString g_DictionaryI("__NSDictionaryI");
420 static const ConstString g_DictionaryM("__NSDictionaryM");
421 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
422 static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
423 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
424 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
425 static const ConstString g_Dictionary0("__NSDictionary0");
426 static const ConstString g_DictionaryCF("__CFDictionary");
427 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
428 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
429 static const ConstString g_ConstantDictionary("NSConstantDictionary");
430
431 if (class_name.IsEmpty())
432 return false;
433
434 if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
436 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
437 ptr_size, 0, error);
438 if (error.Fail())
439 return false;
440
441 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
442 } else if (class_name == g_ConstantDictionary) {
444 value = process_sp->ReadUnsignedIntegerFromMemory(
445 valobj_addr + 2 * ptr_size, ptr_size, 0, error);
446 if (error.Fail())
447 return false;
448 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
449 class_name == g_DictionaryMFrozen) {
450 AppleObjCRuntime *apple_runtime =
451 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
453 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
454 value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
455 error);
456 } else {
457 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
458 ptr_size, 0, error);
459 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
460 }
461 if (error.Fail())
462 return false;
463 } else if (class_name == g_Dictionary1) {
464 value = 1;
465 } else if (class_name == g_Dictionary0) {
466 value = 0;
467 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
468 class_name == g_DictionaryCFRef) {
469 ExecutionContext exe_ctx(process_sp);
470 CFBasicHash cfbh;
471 if (!cfbh.Update(valobj_addr, exe_ctx))
472 return false;
473 value = cfbh.GetCount();
474 } else {
476 for (auto &candidate : map) {
477 if (candidate.first && candidate.first->Match(class_name))
478 return candidate.second(valobj, stream, options);
479 }
480 return false;
481 }
482
483 llvm::StringRef prefix, suffix;
484 if (Language *language = Language::FindPlugin(options.GetLanguage()))
485 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
486
487 stream << prefix;
488 stream.Printf("%" PRIu64 " %s%s", value, "key/value pair",
489 value == 1 ? "" : "s");
490 stream << suffix;
491 return true;
492}
493
496 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
497 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
498 if (!process_sp)
499 return nullptr;
500 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
501 ObjCLanguageRuntime::Get(*process_sp));
502 if (!runtime)
503 return nullptr;
504
505 CompilerType valobj_type(valobj_sp->GetCompilerType());
506 Flags flags(valobj_type.GetTypeInfo());
507
508 if (flags.IsClear(eTypeIsPointer)) {
510 valobj_sp = valobj_sp->AddressOf(error);
511 if (error.Fail() || !valobj_sp)
512 return nullptr;
513 }
514
516 runtime->GetClassDescriptor(*valobj_sp));
517
518 if (!descriptor || !descriptor->IsValid())
519 return nullptr;
520
521 ConstString class_name(descriptor->GetClassName());
522
523 static const ConstString g_DictionaryI("__NSDictionaryI");
524 static const ConstString g_DictionaryM("__NSDictionaryM");
525 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
526 static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
527 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
528 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
529 static const ConstString g_Dictionary0("__NSDictionary0");
530 static const ConstString g_DictionaryCF("__CFDictionary");
531 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
532 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
533 static const ConstString g_ConstantDictionary("NSConstantDictionary");
534
535 if (class_name.IsEmpty())
536 return nullptr;
537
538 if (class_name == g_DictionaryI) {
539 return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
540 } else if (class_name == g_ConstantDictionary) {
541 return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
542 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
543 if (runtime->GetFoundationVersion() >= 1437) {
545 } else if (runtime->GetFoundationVersion() >= 1428) {
547 } else {
548 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
549 }
550 } else if (class_name == g_DictionaryMLegacy) {
551 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
552 } else if (class_name == g_Dictionary1) {
553 return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
554 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
555 class_name == g_DictionaryCFRef) {
556 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
557 } else {
559 for (auto &candidate : map) {
560 if (candidate.first && candidate.first->Match((class_name)))
561 return candidate.second(synth, valobj_sp);
562 }
563 }
564
565 return nullptr;
566}
567
571
579
580llvm::Expected<uint32_t> lldb_private::formatters::
582 if (!m_data_32 && !m_data_64)
583 return 0;
584 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
585}
586
589 m_children.clear();
590 delete m_data_32;
591 m_data_32 = nullptr;
592 delete m_data_64;
593 m_data_64 = nullptr;
594 m_ptr_size = 0;
595 ValueObjectSP valobj_sp = m_backend.GetSP();
596 if (!valobj_sp)
598 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
600 error.Clear();
601 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
602 if (!process_sp)
604 m_ptr_size = process_sp->GetAddressByteSize();
605 m_order = process_sp->GetByteOrder();
606 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
607 if (m_ptr_size == 4) {
609 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
610 error);
611 } else {
613 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
614 error);
615 }
616 if (error.Fail())
618 m_data_ptr = data_location + m_ptr_size;
620}
621
624 uint32_t idx) {
625 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
626
627 if (idx >= num_children)
628 return lldb::ValueObjectSP();
629
630 if (m_children.empty()) {
631 // do the scan phase
632 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
633
634 uint32_t tries = 0;
635 uint32_t test_idx = 0;
636
637 while (tries < num_children) {
638 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
639 val_at_idx = key_at_idx + m_ptr_size;
640 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
641 if (!process_sp)
642 return lldb::ValueObjectSP();
644 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
645 if (error.Fail())
646 return lldb::ValueObjectSP();
647 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
648 if (error.Fail())
649 return lldb::ValueObjectSP();
650
651 test_idx++;
652
653 if (!key_at_idx || !val_at_idx)
654 continue;
655 tries++;
656
657 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
659
660 m_children.push_back(descriptor);
661 }
662 }
663
664 if (idx >= m_children.size()) // should never happen
665 return lldb::ValueObjectSP();
666
667 DictionaryItemDescriptor &dict_item = m_children[idx];
668 if (!dict_item.valobj_sp) {
669 if (!m_pair_type.IsValid()) {
670 TargetSP target_sp(m_backend.GetTargetSP());
671 if (!target_sp)
672 return ValueObjectSP();
673 m_pair_type = GetLLDBNSPairType(target_sp);
674 }
675 if (!m_pair_type.IsValid())
676 return ValueObjectSP();
677
678 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
679
680 if (m_ptr_size == 8) {
681 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
682 *data_ptr = dict_item.key_ptr;
683 *(data_ptr + 1) = dict_item.val_ptr;
684 } else {
685 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
686 *data_ptr = dict_item.key_ptr;
687 *(data_ptr + 1) = dict_item.val_ptr;
688 }
689
690 StreamString idx_name;
691 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
692 DataExtractor data(buffer_sp, m_order, m_ptr_size);
693 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
695 }
696 return dict_item.valobj_sp;
697}
698
703
704llvm::Expected<uint32_t> lldb_private::formatters::
706 if (!m_hashtable.IsValid())
707 return 0;
708 return m_hashtable.GetCount();
709}
710
713 m_children.clear();
714 ValueObjectSP valobj_sp = m_backend.GetSP();
715 m_ptr_size = 0;
716 if (!valobj_sp)
718 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
719
720 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
721 if (!process_sp)
723 m_ptr_size = process_sp->GetAddressByteSize();
724 m_order = process_sp->GetByteOrder();
725 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref)
728}
729
732 uint32_t idx) {
733 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
734 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
735
736 const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
737
738 if (idx >= num_children)
739 return lldb::ValueObjectSP();
740
741 if (m_children.empty()) {
742 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
743 if (!process_sp)
744 return lldb::ValueObjectSP();
745
747 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
748
749 uint32_t tries = 0;
750 uint32_t test_idx = 0;
751
752 // Iterate over inferior memory, reading key/value pointers by shifting each
753 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
754 // fails, otherwise, continue until the number of tries matches the number
755 // of childen.
756 while (tries < num_children) {
757 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
758 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
759
760 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
761 if (error.Fail())
762 return lldb::ValueObjectSP();
763 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
764 if (error.Fail())
765 return lldb::ValueObjectSP();
766
767 test_idx++;
768
769 if (!key_at_idx || !val_at_idx)
770 continue;
771 tries++;
772
773 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
775
776 m_children.push_back(descriptor);
777 }
778 }
779
780 if (idx >= m_children.size()) // should never happen
781 return lldb::ValueObjectSP();
782
783 DictionaryItemDescriptor &dict_item = m_children[idx];
784 if (!dict_item.valobj_sp) {
785 if (!m_pair_type.IsValid()) {
786 TargetSP target_sp(m_backend.GetTargetSP());
787 if (!target_sp)
788 return ValueObjectSP();
789 m_pair_type = GetLLDBNSPairType(target_sp);
790 }
791 if (!m_pair_type.IsValid())
792 return ValueObjectSP();
793
794 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
795
796 switch (m_ptr_size) {
797 case 0: // architecture has no clue - fail
798 return lldb::ValueObjectSP();
799 case 4: {
800 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
801 *data_ptr = dict_item.key_ptr;
802 *(data_ptr + 1) = dict_item.val_ptr;
803 } break;
804 case 8: {
805 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
806 *data_ptr = dict_item.key_ptr;
807 *(data_ptr + 1) = dict_item.val_ptr;
808 } break;
809 default:
810 lldbassert(false && "pointer size is not 4 nor 8");
811 }
812
813 StreamString idx_name;
814 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
815 DataExtractor data(buffer_sp, m_order, m_ptr_size);
816 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
818 }
819 return dict_item.valobj_sp;
820}
821
825
830
833 ValueObjectSP valobj_sp = m_backend.GetSP();
834 if (!valobj_sp)
836 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
838 error.Clear();
839 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
840 if (!process_sp)
842 m_ptr_size = process_sp->GetAddressByteSize();
843 m_order = process_sp->GetByteOrder();
844 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
845 m_size = process_sp->ReadUnsignedIntegerFromMemory(
846 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
847 if (error.Fail())
849 m_keys_ptr =
850 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
851 if (error.Fail())
854 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
855
856 return error.Success() ? lldb::ChildCacheState::eReuse
858}
859
862 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
863
864 if (idx >= num_children)
865 return lldb::ValueObjectSP();
866
867 if (m_children.empty()) {
868 // do the scan phase
869 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
870 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
871 if (!process_sp)
872 return lldb::ValueObjectSP();
873
874 for (unsigned int child = 0; child < num_children; ++child) {
876 key_at_idx = process_sp->ReadPointerFromMemory(
877 m_keys_ptr + child * m_ptr_size, error);
878 if (error.Fail())
879 return lldb::ValueObjectSP();
880 val_at_idx = process_sp->ReadPointerFromMemory(
881 m_objects_ptr + child * m_ptr_size, error);
882 if (error.Fail())
883 return lldb::ValueObjectSP();
884 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
886 m_children.push_back(descriptor);
887 }
888 }
889
890 if (idx >= m_children.size()) // should never happen
891 return lldb::ValueObjectSP();
892
893 DictionaryItemDescriptor &dict_item = m_children[idx];
894 if (!dict_item.valobj_sp) {
895 if (!m_pair_type.IsValid()) {
896 TargetSP target_sp(m_backend.GetTargetSP());
897 if (!target_sp)
898 return ValueObjectSP();
899 m_pair_type = GetLLDBNSPairType(target_sp);
900 }
901 if (!m_pair_type.IsValid())
902 return ValueObjectSP();
903
904 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
905
906 if (m_ptr_size == 8) {
907 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
908 *data_ptr = dict_item.key_ptr;
909 *(data_ptr + 1) = dict_item.val_ptr;
910 } else {
911 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
912 *data_ptr = dict_item.key_ptr;
913 *(data_ptr + 1) = dict_item.val_ptr;
914 }
915
916 StreamString idx_name;
917 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
918 DataExtractor data(buffer_sp, m_order, m_ptr_size);
919 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
921 }
922 return dict_item.valobj_sp;
923}
924
928
929llvm::Expected<size_t> lldb_private::formatters::
931 static const ConstString g_zero("[0]");
932 if (name == g_zero)
933 return 0;
934 return llvm::createStringErrorV("type has no child named '{0}'", name);
935}
936
941
947
950 uint32_t idx) {
951 if (idx != 0)
952 return lldb::ValueObjectSP();
953
954 if (m_pair.get())
955 return m_pair;
956
957 auto process_sp(m_backend.GetProcessSP());
958 if (!process_sp)
959 return nullptr;
960
961 auto ptr_size = process_sp->GetAddressByteSize();
962
963 lldb::addr_t key_ptr =
964 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
965 lldb::addr_t value_ptr = key_ptr + ptr_size;
966
968
969 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
970 if (error.Fail())
971 return nullptr;
972 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
973 if (error.Fail())
974 return nullptr;
975
976 auto pair_type =
977 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
978
979 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
980
981 if (ptr_size == 8) {
982 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
983 *data_ptr = key_at_idx;
984 *(data_ptr + 1) = value_at_idx;
985 } else {
986 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
987 *data_ptr = key_at_idx;
988 *(data_ptr + 1) = value_at_idx;
989 }
990
991 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
993 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
994
995 return m_pair;
996}
997
998template <typename D32, typename D64>
1003
1004template <typename D32, typename D64>
1008 delete m_data_32;
1009 m_data_32 = nullptr;
1010 delete m_data_64;
1011 m_data_64 = nullptr;
1012}
1013
1014template <typename D32, typename D64>
1015llvm::Expected<uint32_t>
1018 if (!m_data_32 && !m_data_64)
1019 return 0;
1020 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1021}
1022
1023template <typename D32, typename D64>
1026 D64>::Update() {
1027 m_children.clear();
1028 ValueObjectSP valobj_sp = m_backend.GetSP();
1029 m_ptr_size = 0;
1030 delete m_data_32;
1031 m_data_32 = nullptr;
1032 delete m_data_64;
1033 m_data_64 = nullptr;
1034 if (!valobj_sp)
1036 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1037 Status error;
1038 error.Clear();
1039 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1040 if (!process_sp)
1042 m_ptr_size = process_sp->GetAddressByteSize();
1043 m_order = process_sp->GetByteOrder();
1044 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1045 if (m_ptr_size == 4) {
1046 m_data_32 = new D32();
1047 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1048 error);
1049 } else {
1050 m_data_64 = new D64();
1051 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1052 error);
1053 }
1054
1055 return error.Success() ? lldb::ChildCacheState::eReuse
1057}
1058
1059template <typename D32, typename D64>
1062 D32, D64>::GetChildAtIndex(uint32_t idx) {
1063 lldb::addr_t m_keys_ptr;
1064 lldb::addr_t m_values_ptr;
1065 if (m_data_32) {
1066 uint32_t size = m_data_32->GetSize();
1067 m_keys_ptr = m_data_32->_buffer;
1068 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1069 } else {
1070 uint32_t size = m_data_64->GetSize();
1071 m_keys_ptr = m_data_64->_buffer;
1072 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1073 }
1074
1075 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1076
1077 if (idx >= num_children)
1078 return lldb::ValueObjectSP();
1079
1080 if (m_children.empty()) {
1081 // do the scan phase
1082 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1083
1084 uint32_t tries = 0;
1085 uint32_t test_idx = 0;
1086
1087 while (tries < num_children) {
1088 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1089 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1090 ;
1091 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1092 if (!process_sp)
1093 return lldb::ValueObjectSP();
1094 Status error;
1095 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1096 if (error.Fail())
1097 return lldb::ValueObjectSP();
1098 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1099 if (error.Fail())
1100 return lldb::ValueObjectSP();
1101
1102 test_idx++;
1103
1104 if (!key_at_idx || !val_at_idx)
1105 continue;
1106 tries++;
1107
1108 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1110
1111 m_children.push_back(descriptor);
1112 }
1113 }
1114
1115 if (idx >= m_children.size()) // should never happen
1116 return lldb::ValueObjectSP();
1117
1118 DictionaryItemDescriptor &dict_item = m_children[idx];
1119 if (!dict_item.valobj_sp) {
1120 if (!m_pair_type.IsValid()) {
1121 TargetSP target_sp(m_backend.GetTargetSP());
1122 if (!target_sp)
1123 return ValueObjectSP();
1124 m_pair_type = GetLLDBNSPairType(target_sp);
1125 }
1126 if (!m_pair_type.IsValid())
1127 return ValueObjectSP();
1128
1129 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1130
1131 if (m_ptr_size == 8) {
1132 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1133 *data_ptr = dict_item.key_ptr;
1134 *(data_ptr + 1) = dict_item.val_ptr;
1135 } else {
1136 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1137 *data_ptr = dict_item.key_ptr;
1138 *(data_ptr + 1) = dict_item.val_ptr;
1139 }
1140
1141 StreamString idx_name;
1142 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1143 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1144 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1146 }
1147 return dict_item.valobj_sp;
1148}
1149
1153
1161
1164 if (!m_data_32 && !m_data_64)
1165 return 0;
1166 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1167}
1168
1171 m_children.clear();
1172 ValueObjectSP valobj_sp = m_backend.GetSP();
1173 m_ptr_size = 0;
1174 delete m_data_32;
1175 m_data_32 = nullptr;
1176 delete m_data_64;
1177 m_data_64 = nullptr;
1178 if (!valobj_sp)
1180 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1181 Status error;
1182 error.Clear();
1183 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1184 if (!process_sp)
1186 m_ptr_size = process_sp->GetAddressByteSize();
1187 m_order = process_sp->GetByteOrder();
1188 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1189 if (m_ptr_size == 4) {
1191 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1192 error);
1193 } else {
1195 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1196 error);
1197 }
1198
1199 return error.Success() ? lldb::ChildCacheState::eReuse
1201}
1202
1206 lldb::addr_t m_keys_ptr =
1207 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1208 lldb::addr_t m_values_ptr =
1209 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1210
1211 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1212
1213 if (idx >= num_children)
1214 return lldb::ValueObjectSP();
1215
1216 if (m_children.empty()) {
1217 // do the scan phase
1218 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1219
1220 uint32_t tries = 0;
1221 uint32_t test_idx = 0;
1222
1223 while (tries < num_children) {
1224 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1225 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1226 ;
1227 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1228 if (!process_sp)
1229 return lldb::ValueObjectSP();
1230 Status error;
1231 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1232 if (error.Fail())
1233 return lldb::ValueObjectSP();
1234 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1235 if (error.Fail())
1236 return lldb::ValueObjectSP();
1237
1238 test_idx++;
1239
1240 if (!key_at_idx || !val_at_idx)
1241 continue;
1242 tries++;
1243
1244 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1246
1247 m_children.push_back(descriptor);
1248 }
1249 }
1250
1251 if (idx >= m_children.size()) // should never happen
1252 return lldb::ValueObjectSP();
1253
1254 DictionaryItemDescriptor &dict_item = m_children[idx];
1255 if (!dict_item.valobj_sp) {
1256 if (!m_pair_type.IsValid()) {
1257 TargetSP target_sp(m_backend.GetTargetSP());
1258 if (!target_sp)
1259 return ValueObjectSP();
1260 m_pair_type = GetLLDBNSPairType(target_sp);
1261 }
1262 if (!m_pair_type.IsValid())
1263 return ValueObjectSP();
1264
1265 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1266
1267 if (m_ptr_size == 8) {
1268 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1269 *data_ptr = dict_item.key_ptr;
1270 *(data_ptr + 1) = dict_item.val_ptr;
1271 } else {
1272 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1273 *data_ptr = dict_item.key_ptr;
1274 *(data_ptr + 1) = dict_item.val_ptr;
1275 }
1276
1277 StreamString idx_name;
1278 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1279 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1280 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1282 }
1283 return dict_item.valobj_sp;
1284}
1285
1287 ValueObject &, Stream &, const TypeSummaryOptions &);
1288
1290 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
bool IsEmpty() const
Test for empty string.
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
A subclass of DataBuffer that stores a data buffer on the heap.
An data extractor class.
Execution context objects refer to objects in the execution of the program that is being debugged.
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
A class to manage flags.
Definition Flags.h:22
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:355
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:1911
uint32_t GetAddressByteSize() const
Definition Process.cpp:3774
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:132
uint32_t CalculateNumChildrenIgnoringErrors(uint32_t max=UINT32_MAX)
lldb::ValueObjectSP CreateValueObjectFromData(llvm::StringRef name, const DataExtractor &data, const ExecutionContext &exe_ctx, CompilerType type)
SyntheticChildrenFrontEnd(ValueObject &backend)
lldb::LanguageType GetLanguage() const
static 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