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
22#include "lldb/Target/Target.h"
24#include "lldb/Utility/Endian.h"
25#include "lldb/Utility/Status.h"
26#include "lldb/Utility/Stream.h"
29
30using namespace lldb;
31using namespace lldb_private;
32using namespace lldb_private::formatters;
33
37
39 ConstString class_name) {
40 return class_name.GetStringRef().starts_with(m_prefix.GetStringRef());
41}
42
45
50
57
65
67 CompilerType compiler_type;
68 TypeSystemClangSP scratch_ts_sp =
70
71 if (!scratch_ts_sp)
72 return compiler_type;
73
74 static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair");
75
76 compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(
77 scratch_ts_sp->getASTContext(), g_lldb_autogen_nspair);
78
79 if (!compiler_type) {
80 compiler_type = scratch_ts_sp->CreateRecordType(
81 nullptr, OptionalClangModuleID(), g_lldb_autogen_nspair,
82 llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC);
83
84 if (compiler_type) {
86 CompilerType id_compiler_type =
87 scratch_ts_sp->GetBasicType(eBasicTypeObjCID);
88 TypeSystemClang::AddFieldToRecordType(compiler_type, "key",
89 id_compiler_type, 0);
90 TypeSystemClang::AddFieldToRecordType(compiler_type, "value",
91 id_compiler_type, 0);
93 }
94 }
95 return compiler_type;
96}
97
98namespace lldb_private {
99namespace formatters {
138
166
193
195public:
197
199
200 llvm::Expected<uint32_t> CalculateNumChildren() override;
201
202 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
203
204 lldb::ChildCacheState Update() override;
205
206 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
207
208private:
210};
211
212template <typename D32, typename D64>
240
241namespace Foundation1100 {
243 public:
245
247
248 llvm::Expected<uint32_t> CalculateNumChildren() override;
249
250 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
251
252 lldb::ChildCacheState Update() override;
253
254 private:
256 uint32_t _used : 26;
257 uint32_t _kvo : 1;
258 uint32_t _size;
259 uint32_t _mutations;
260 uint32_t _objs_addr;
261 uint32_t _keys_addr;
262 };
263
265 uint64_t _used : 58;
266 uint32_t _kvo : 1;
267 uint64_t _size;
268 uint64_t _mutations;
269 uint64_t _objs_addr;
270 uint64_t _keys_addr;
271 };
272
278
280 uint8_t m_ptr_size = 8;
285 std::vector<DictionaryItemDescriptor> m_children;
286 };
287}
288
289namespace Foundation1428 {
290 namespace {
291 struct DataDescriptor_32 {
292 uint32_t _used : 26;
293 uint32_t _kvo : 1;
294 uint32_t _size;
295 uint32_t _buffer;
296 uint64_t GetSize() { return _size; }
297 };
298
299 struct DataDescriptor_64 {
300 uint64_t _used : 58;
301 uint32_t _kvo : 1;
302 uint64_t _size;
303 uint64_t _buffer;
304 uint64_t GetSize() { return _size; }
305 };
306 }
307
310}
311
312namespace Foundation1437 {
313 static const uint64_t NSDictionaryCapacities[] = {
314 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
315 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
316 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
317 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
318 111638519, 180634607, 292272623, 472907251
319 };
320
321 static const size_t NSDictionaryNumSizeBuckets =
322 sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
323
324 namespace {
325 struct DataDescriptor_32 {
326 uint32_t _buffer;
327 uint32_t _muts;
328 uint32_t _used : 25;
329 uint32_t _kvo : 1;
330 uint32_t _szidx : 6;
331
332 uint64_t GetSize() {
333 return (_szidx) >= NSDictionaryNumSizeBuckets ?
334 0 : NSDictionaryCapacities[_szidx];
335 }
336 };
337
338 struct DataDescriptor_64 {
339 uint64_t _buffer;
340 uint32_t _muts;
341 uint32_t _used : 25;
342 uint32_t _kvo : 1;
343 uint32_t _szidx : 6;
344
345 uint64_t GetSize() {
346 return (_szidx) >= NSDictionaryNumSizeBuckets ?
347 0 : NSDictionaryCapacities[_szidx];
348 }
349 };
350 } // namespace
351
354
355 template <typename DD>
356 uint64_t
358 lldb::addr_t valobj_addr, Status &error) {
359 const lldb::addr_t start_of_descriptor =
360 valobj_addr + process.GetAddressByteSize();
361 DD descriptor = DD();
362 process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
363 error);
364 if (error.Fail()) {
365 return 0;
366 }
367 return descriptor._used;
368 }
369
370 uint64_t
372 Status &error) {
373 if (process.GetAddressByteSize() == 4) {
374 return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
375 error);
376 } else {
377 return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
378 error);
379 }
380 }
381
382}
383} // namespace formatters
384} // namespace lldb_private
385
386template <bool name_entries>
388 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
389 static constexpr llvm::StringLiteral g_TypeHint("NSDictionary");
390 ProcessSP process_sp = valobj.GetProcessSP();
391 if (!process_sp)
392 return false;
393
394 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
395
396 if (!runtime)
397 return false;
398
400 runtime->GetNonKVOClassDescriptor(valobj));
401
402 if (!descriptor || !descriptor->IsValid())
403 return false;
404
405 uint32_t ptr_size = process_sp->GetAddressByteSize();
406 bool is_64bit = (ptr_size == 8);
407
408 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
409
410 if (!valobj_addr)
411 return false;
412
413 uint64_t value = 0;
414
415 ConstString class_name(descriptor->GetClassName());
416
417 static const ConstString g_DictionaryI("__NSDictionaryI");
418 static const ConstString g_DictionaryM("__NSDictionaryM");
419 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
420 static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
421 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
422 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
423 static const ConstString g_Dictionary0("__NSDictionary0");
424 static const ConstString g_DictionaryCF("__CFDictionary");
425 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
426 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
427 static const ConstString g_ConstantDictionary("NSConstantDictionary");
428
429 if (class_name.IsEmpty())
430 return false;
431
432 if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
434 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
435 ptr_size, 0, error);
436 if (error.Fail())
437 return false;
438
439 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
440 } else if (class_name == g_ConstantDictionary) {
442 value = process_sp->ReadUnsignedIntegerFromMemory(
443 valobj_addr + 2 * ptr_size, ptr_size, 0, error);
444 if (error.Fail())
445 return false;
446 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
447 class_name == g_DictionaryMFrozen) {
448 AppleObjCRuntime *apple_runtime =
449 llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
451 if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
452 value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
453 error);
454 } else {
455 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
456 ptr_size, 0, error);
457 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
458 }
459 if (error.Fail())
460 return false;
461 } else if (class_name == g_Dictionary1) {
462 value = 1;
463 } else if (class_name == g_Dictionary0) {
464 value = 0;
465 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
466 class_name == g_DictionaryCFRef) {
467 ExecutionContext exe_ctx(process_sp);
468 CFBasicHash cfbh;
469 if (!cfbh.Update(valobj_addr, exe_ctx))
470 return false;
471 value = cfbh.GetCount();
472 } else {
474 for (auto &candidate : map) {
475 if (candidate.first && candidate.first->Match(class_name))
476 return candidate.second(valobj, stream, options);
477 }
478 return false;
479 }
480
481 llvm::StringRef prefix, suffix;
482 if (Language *language = Language::FindPlugin(options.GetLanguage()))
483 std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
484
485 stream << prefix;
486 stream.Printf("%" PRIu64 " %s%s", value, "key/value pair",
487 value == 1 ? "" : "s");
488 stream << suffix;
489 return true;
490}
491
494 CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
495 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
496 if (!process_sp)
497 return nullptr;
498 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
499 ObjCLanguageRuntime::Get(*process_sp));
500 if (!runtime)
501 return nullptr;
502
503 CompilerType valobj_type(valobj_sp->GetCompilerType());
504 Flags flags(valobj_type.GetTypeInfo());
505
506 if (flags.IsClear(eTypeIsPointer)) {
508 valobj_sp = valobj_sp->AddressOf(error);
509 if (error.Fail() || !valobj_sp)
510 return nullptr;
511 }
512
514 runtime->GetClassDescriptor(*valobj_sp));
515
516 if (!descriptor || !descriptor->IsValid())
517 return nullptr;
518
519 ConstString class_name(descriptor->GetClassName());
520
521 static const ConstString g_DictionaryI("__NSDictionaryI");
522 static const ConstString g_DictionaryM("__NSDictionaryM");
523 static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
524 static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
525 static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM");
526 static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
527 static const ConstString g_Dictionary0("__NSDictionary0");
528 static const ConstString g_DictionaryCF("__CFDictionary");
529 static const ConstString g_DictionaryNSCF("__NSCFDictionary");
530 static const ConstString g_DictionaryCFRef("CFDictionaryRef");
531 static const ConstString g_ConstantDictionary("NSConstantDictionary");
532
533 if (class_name.IsEmpty())
534 return nullptr;
535
536 if (class_name == g_DictionaryI) {
537 return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
538 } else if (class_name == g_ConstantDictionary) {
539 return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
540 } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
541 if (runtime->GetFoundationVersion() >= 1437) {
543 } else if (runtime->GetFoundationVersion() >= 1428) {
545 } else {
546 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
547 }
548 } else if (class_name == g_DictionaryMLegacy) {
549 return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
550 } else if (class_name == g_Dictionary1) {
551 return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
552 } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
553 class_name == g_DictionaryCFRef) {
554 return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
555 } else {
557 for (auto &candidate : map) {
558 if (candidate.first && candidate.first->Match((class_name)))
559 return candidate.second(synth, valobj_sp);
560 }
561 }
562
563 return nullptr;
564}
565
569
577
578llvm::Expected<uint32_t> lldb_private::formatters::
580 if (!m_data_32 && !m_data_64)
581 return 0;
582 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
583}
584
587 m_children.clear();
588 delete m_data_32;
589 m_data_32 = nullptr;
590 delete m_data_64;
591 m_data_64 = nullptr;
592 m_ptr_size = 0;
593 ValueObjectSP valobj_sp = m_backend.GetSP();
594 if (!valobj_sp)
596 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
598 error.Clear();
599 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
600 if (!process_sp)
602 m_ptr_size = process_sp->GetAddressByteSize();
603 m_order = process_sp->GetByteOrder();
604 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
605 if (m_ptr_size == 4) {
607 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
608 error);
609 } else {
611 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
612 error);
613 }
614 if (error.Fail())
616 m_data_ptr = data_location + m_ptr_size;
618}
619
622 uint32_t idx) {
623 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
624
625 if (idx >= num_children)
626 return lldb::ValueObjectSP();
627
628 if (m_children.empty()) {
629 // do the scan phase
630 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
631
632 uint32_t tries = 0;
633 uint32_t test_idx = 0;
634
635 while (tries < num_children) {
636 key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
637 val_at_idx = key_at_idx + m_ptr_size;
638 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
639 if (!process_sp)
640 return lldb::ValueObjectSP();
642 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
643 if (error.Fail())
644 return lldb::ValueObjectSP();
645 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
646 if (error.Fail())
647 return lldb::ValueObjectSP();
648
649 test_idx++;
650
651 if (!key_at_idx || !val_at_idx)
652 continue;
653 tries++;
654
655 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
657
658 m_children.push_back(descriptor);
659 }
660 }
661
662 if (idx >= m_children.size()) // should never happen
663 return lldb::ValueObjectSP();
664
665 DictionaryItemDescriptor &dict_item = m_children[idx];
666 if (!dict_item.valobj_sp) {
667 if (!m_pair_type.IsValid()) {
668 TargetSP target_sp(m_backend.GetTargetSP());
669 if (!target_sp)
670 return ValueObjectSP();
671 m_pair_type = GetLLDBNSPairType(target_sp);
672 }
673 if (!m_pair_type.IsValid())
674 return ValueObjectSP();
675
676 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
677
678 if (m_ptr_size == 8) {
679 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
680 *data_ptr = dict_item.key_ptr;
681 *(data_ptr + 1) = dict_item.val_ptr;
682 } else {
683 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
684 *data_ptr = dict_item.key_ptr;
685 *(data_ptr + 1) = dict_item.val_ptr;
686 }
687
688 StreamString idx_name;
689 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
690 DataExtractor data(buffer_sp, m_order, m_ptr_size);
691 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
693 }
694 return dict_item.valobj_sp;
695}
696
701
702llvm::Expected<uint32_t> lldb_private::formatters::
704 if (!m_hashtable.IsValid())
705 return 0;
706 return m_hashtable.GetCount();
707}
708
711 m_children.clear();
712 ValueObjectSP valobj_sp = m_backend.GetSP();
713 m_ptr_size = 0;
714 if (!valobj_sp)
716 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
717
718 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
719 if (!process_sp)
721 m_ptr_size = process_sp->GetAddressByteSize();
722 m_order = process_sp->GetByteOrder();
723 return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref)
726}
727
730 uint32_t idx) {
731 lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer();
732 lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer();
733
734 const uint32_t num_children = CalculateNumChildrenIgnoringErrors();
735
736 if (idx >= num_children)
737 return lldb::ValueObjectSP();
738
739 if (m_children.empty()) {
740 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
741 if (!process_sp)
742 return lldb::ValueObjectSP();
743
745 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
746
747 uint32_t tries = 0;
748 uint32_t test_idx = 0;
749
750 // Iterate over inferior memory, reading key/value pointers by shifting each
751 // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
752 // fails, otherwise, continue until the number of tries matches the number
753 // of childen.
754 while (tries < num_children) {
755 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
756 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
757
758 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
759 if (error.Fail())
760 return lldb::ValueObjectSP();
761 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
762 if (error.Fail())
763 return lldb::ValueObjectSP();
764
765 test_idx++;
766
767 if (!key_at_idx || !val_at_idx)
768 continue;
769 tries++;
770
771 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
773
774 m_children.push_back(descriptor);
775 }
776 }
777
778 if (idx >= m_children.size()) // should never happen
779 return lldb::ValueObjectSP();
780
781 DictionaryItemDescriptor &dict_item = m_children[idx];
782 if (!dict_item.valobj_sp) {
783 if (!m_pair_type.IsValid()) {
784 TargetSP target_sp(m_backend.GetTargetSP());
785 if (!target_sp)
786 return ValueObjectSP();
787 m_pair_type = GetLLDBNSPairType(target_sp);
788 }
789 if (!m_pair_type.IsValid())
790 return ValueObjectSP();
791
792 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
793
794 switch (m_ptr_size) {
795 case 0: // architecture has no clue - fail
796 return lldb::ValueObjectSP();
797 case 4: {
798 uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
799 *data_ptr = dict_item.key_ptr;
800 *(data_ptr + 1) = dict_item.val_ptr;
801 } break;
802 case 8: {
803 uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
804 *data_ptr = dict_item.key_ptr;
805 *(data_ptr + 1) = dict_item.val_ptr;
806 } break;
807 default:
808 lldbassert(false && "pointer size is not 4 nor 8");
809 }
810
811 StreamString idx_name;
812 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
813 DataExtractor data(buffer_sp, m_order, m_ptr_size);
814 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
816 }
817 return dict_item.valobj_sp;
818}
819
823
828
831 ValueObjectSP valobj_sp = m_backend.GetSP();
832 if (!valobj_sp)
834 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
836 error.Clear();
837 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
838 if (!process_sp)
840 m_ptr_size = process_sp->GetAddressByteSize();
841 m_order = process_sp->GetByteOrder();
842 uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
843 m_size = process_sp->ReadUnsignedIntegerFromMemory(
844 valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
845 if (error.Fail())
847 m_keys_ptr =
848 process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
849 if (error.Fail())
852 process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
853
854 return error.Success() ? lldb::ChildCacheState::eReuse
856}
857
860 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
861
862 if (idx >= num_children)
863 return lldb::ValueObjectSP();
864
865 if (m_children.empty()) {
866 // do the scan phase
867 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
868 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
869 if (!process_sp)
870 return lldb::ValueObjectSP();
871
872 for (unsigned int child = 0; child < num_children; ++child) {
874 key_at_idx = process_sp->ReadPointerFromMemory(
875 m_keys_ptr + child * m_ptr_size, error);
876 if (error.Fail())
877 return lldb::ValueObjectSP();
878 val_at_idx = process_sp->ReadPointerFromMemory(
879 m_objects_ptr + child * m_ptr_size, error);
880 if (error.Fail())
881 return lldb::ValueObjectSP();
882 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
884 m_children.push_back(descriptor);
885 }
886 }
887
888 if (idx >= m_children.size()) // should never happen
889 return lldb::ValueObjectSP();
890
891 DictionaryItemDescriptor &dict_item = m_children[idx];
892 if (!dict_item.valobj_sp) {
893 if (!m_pair_type.IsValid()) {
894 TargetSP target_sp(m_backend.GetTargetSP());
895 if (!target_sp)
896 return ValueObjectSP();
897 m_pair_type = GetLLDBNSPairType(target_sp);
898 }
899 if (!m_pair_type.IsValid())
900 return ValueObjectSP();
901
902 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
903
904 if (m_ptr_size == 8) {
905 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
906 *data_ptr = dict_item.key_ptr;
907 *(data_ptr + 1) = dict_item.val_ptr;
908 } else {
909 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
910 *data_ptr = dict_item.key_ptr;
911 *(data_ptr + 1) = dict_item.val_ptr;
912 }
913
914 StreamString idx_name;
915 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
916 DataExtractor data(buffer_sp, m_order, m_ptr_size);
917 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
919 }
920 return dict_item.valobj_sp;
921}
922
926
927llvm::Expected<size_t> lldb_private::formatters::
929 static const ConstString g_zero("[0]");
930 if (name == g_zero)
931 return 0;
932 return llvm::createStringError("Type has no child named '%s'",
933 name.AsCString());
934}
935
940
946
949 uint32_t idx) {
950 if (idx != 0)
951 return lldb::ValueObjectSP();
952
953 if (m_pair.get())
954 return m_pair;
955
956 auto process_sp(m_backend.GetProcessSP());
957 if (!process_sp)
958 return nullptr;
959
960 auto ptr_size = process_sp->GetAddressByteSize();
961
962 lldb::addr_t key_ptr =
963 m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
964 lldb::addr_t value_ptr = key_ptr + ptr_size;
965
967
968 lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
969 if (error.Fail())
970 return nullptr;
971 lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
972 if (error.Fail())
973 return nullptr;
974
975 auto pair_type =
976 GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
977
978 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
979
980 if (ptr_size == 8) {
981 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
982 *data_ptr = key_at_idx;
983 *(data_ptr + 1) = value_at_idx;
984 } else {
985 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
986 *data_ptr = key_at_idx;
987 *(data_ptr + 1) = value_at_idx;
988 }
989
990 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
992 "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
993
994 return m_pair;
995}
996
997template <typename D32, typename D64>
1002
1003template <typename D32, typename D64>
1007 delete m_data_32;
1008 m_data_32 = nullptr;
1009 delete m_data_64;
1010 m_data_64 = nullptr;
1011}
1012
1013template <typename D32, typename D64>
1014llvm::Expected<uint32_t>
1017 if (!m_data_32 && !m_data_64)
1018 return 0;
1019 return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used);
1020}
1021
1022template <typename D32, typename D64>
1025 D64>::Update() {
1026 m_children.clear();
1027 ValueObjectSP valobj_sp = m_backend.GetSP();
1028 m_ptr_size = 0;
1029 delete m_data_32;
1030 m_data_32 = nullptr;
1031 delete m_data_64;
1032 m_data_64 = nullptr;
1033 if (!valobj_sp)
1035 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1036 Status error;
1037 error.Clear();
1038 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1039 if (!process_sp)
1041 m_ptr_size = process_sp->GetAddressByteSize();
1042 m_order = process_sp->GetByteOrder();
1043 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1044 if (m_ptr_size == 4) {
1045 m_data_32 = new D32();
1046 process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
1047 error);
1048 } else {
1049 m_data_64 = new D64();
1050 process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
1051 error);
1052 }
1053
1054 return error.Success() ? lldb::ChildCacheState::eReuse
1056}
1057
1058template <typename D32, typename D64>
1061 D32, D64>::GetChildAtIndex(uint32_t idx) {
1062 lldb::addr_t m_keys_ptr;
1063 lldb::addr_t m_values_ptr;
1064 if (m_data_32) {
1065 uint32_t size = m_data_32->GetSize();
1066 m_keys_ptr = m_data_32->_buffer;
1067 m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
1068 } else {
1069 uint32_t size = m_data_64->GetSize();
1070 m_keys_ptr = m_data_64->_buffer;
1071 m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
1072 }
1073
1074 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1075
1076 if (idx >= num_children)
1077 return lldb::ValueObjectSP();
1078
1079 if (m_children.empty()) {
1080 // do the scan phase
1081 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1082
1083 uint32_t tries = 0;
1084 uint32_t test_idx = 0;
1085
1086 while (tries < num_children) {
1087 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1088 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1089 ;
1090 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1091 if (!process_sp)
1092 return lldb::ValueObjectSP();
1093 Status error;
1094 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1095 if (error.Fail())
1096 return lldb::ValueObjectSP();
1097 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1098 if (error.Fail())
1099 return lldb::ValueObjectSP();
1100
1101 test_idx++;
1102
1103 if (!key_at_idx || !val_at_idx)
1104 continue;
1105 tries++;
1106
1107 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1109
1110 m_children.push_back(descriptor);
1111 }
1112 }
1113
1114 if (idx >= m_children.size()) // should never happen
1115 return lldb::ValueObjectSP();
1116
1117 DictionaryItemDescriptor &dict_item = m_children[idx];
1118 if (!dict_item.valobj_sp) {
1119 if (!m_pair_type.IsValid()) {
1120 TargetSP target_sp(m_backend.GetTargetSP());
1121 if (!target_sp)
1122 return ValueObjectSP();
1123 m_pair_type = GetLLDBNSPairType(target_sp);
1124 }
1125 if (!m_pair_type.IsValid())
1126 return ValueObjectSP();
1127
1128 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1129
1130 if (m_ptr_size == 8) {
1131 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1132 *data_ptr = dict_item.key_ptr;
1133 *(data_ptr + 1) = dict_item.val_ptr;
1134 } else {
1135 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1136 *data_ptr = dict_item.key_ptr;
1137 *(data_ptr + 1) = dict_item.val_ptr;
1138 }
1139
1140 StreamString idx_name;
1141 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1142 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1143 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1145 }
1146 return dict_item.valobj_sp;
1147}
1148
1152
1160
1163 if (!m_data_32 && !m_data_64)
1164 return 0;
1165 return (m_data_32 ? m_data_32->_used : m_data_64->_used);
1166}
1167
1170 m_children.clear();
1171 ValueObjectSP valobj_sp = m_backend.GetSP();
1172 m_ptr_size = 0;
1173 delete m_data_32;
1174 m_data_32 = nullptr;
1175 delete m_data_64;
1176 m_data_64 = nullptr;
1177 if (!valobj_sp)
1179 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
1180 Status error;
1181 error.Clear();
1182 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1183 if (!process_sp)
1185 m_ptr_size = process_sp->GetAddressByteSize();
1186 m_order = process_sp->GetByteOrder();
1187 uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
1188 if (m_ptr_size == 4) {
1190 process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
1191 error);
1192 } else {
1194 process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
1195 error);
1196 }
1197
1198 return error.Success() ? lldb::ChildCacheState::eReuse
1200}
1201
1205 lldb::addr_t m_keys_ptr =
1206 (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
1207 lldb::addr_t m_values_ptr =
1208 (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
1209
1210 uint32_t num_children = CalculateNumChildrenIgnoringErrors();
1211
1212 if (idx >= num_children)
1213 return lldb::ValueObjectSP();
1214
1215 if (m_children.empty()) {
1216 // do the scan phase
1217 lldb::addr_t key_at_idx = 0, val_at_idx = 0;
1218
1219 uint32_t tries = 0;
1220 uint32_t test_idx = 0;
1221
1222 while (tries < num_children) {
1223 key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
1224 val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
1225 ;
1226 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
1227 if (!process_sp)
1228 return lldb::ValueObjectSP();
1229 Status error;
1230 key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
1231 if (error.Fail())
1232 return lldb::ValueObjectSP();
1233 val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
1234 if (error.Fail())
1235 return lldb::ValueObjectSP();
1236
1237 test_idx++;
1238
1239 if (!key_at_idx || !val_at_idx)
1240 continue;
1241 tries++;
1242
1243 DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1245
1246 m_children.push_back(descriptor);
1247 }
1248 }
1249
1250 if (idx >= m_children.size()) // should never happen
1251 return lldb::ValueObjectSP();
1252
1253 DictionaryItemDescriptor &dict_item = m_children[idx];
1254 if (!dict_item.valobj_sp) {
1255 if (!m_pair_type.IsValid()) {
1256 TargetSP target_sp(m_backend.GetTargetSP());
1257 if (!target_sp)
1258 return ValueObjectSP();
1259 m_pair_type = GetLLDBNSPairType(target_sp);
1260 }
1261 if (!m_pair_type.IsValid())
1262 return ValueObjectSP();
1263
1264 WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1265
1266 if (m_ptr_size == 8) {
1267 uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1268 *data_ptr = dict_item.key_ptr;
1269 *(data_ptr + 1) = dict_item.val_ptr;
1270 } else {
1271 uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1272 *data_ptr = dict_item.key_ptr;
1273 *(data_ptr + 1) = dict_item.val_ptr;
1274 }
1275
1276 StreamString idx_name;
1277 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1278 DataExtractor data(buffer_sp, m_order, m_ptr_size);
1279 dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1281 }
1282 return dict_item.valobj_sp;
1283}
1284
1286 ValueObject &, Stream &, const TypeSummaryOptions &);
1287
1289 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
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
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:354
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:1907
uint32_t GetAddressByteSize() const
Definition Process.cpp:3725
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)
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