mib created this revision. mib added reviewers: friss, davide, jingham. mib added a project: LLDB. Herald added subscribers: lldb-commits, mgorny. mib edited the summary of this revision. mib updated this revision to Diff 258402. mib added a comment.
Reformat patch. [lldb/Dataformatter] Add support for CoreFoundation Dictionaries and Sets This patch improves data formatting for CoreFoundation containers: CFDictionary and CFSet. These data formatters make the containers and their children appear in Xcode's variables view (and on the command line) without having to expand the data structure. Previous implementation (D48450 <https://reviews.llvm.org/D48450>) only supported showing the container's element count. (lldb) frame var dict (__NSCFDictionary *) dict = 0x00000001004062b0 2 key/value pairs (lldb) frame var set (__NSCFSet *) set = 0x0000000100406330 2 elements Now the variable can be dereferenced to dispaly the container's elements: (lldb) frame var *dict (__NSCFDictionary) *dict = { [0] = { key = 0x0000000100004050 @"123" value = 0x0000000100004090 @"456" } [1] = { key = 0x0000000100004030 @"abc" value = 0x0000000100004070 @"def" } } (lldb) frame var *set (__NSCFSet) *set = { [0] = 0x0000000100004050 @"123" [1] = 0x0000000100004030 @"abc" } rdar://39882287 Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D78396 Files: lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp lldb/source/Plugins/Language/ObjC/CFBasicHash.h lldb/source/Plugins/Language/ObjC/CMakeLists.txt lldb/source/Plugins/Language/ObjC/NSDictionary.cpp lldb/source/Plugins/Language/ObjC/NSDictionary.h lldb/source/Plugins/Language/ObjC/NSSet.cpp lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m
Index: lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m =================================================================== --- lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m +++ lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m @@ -376,9 +376,10 @@ [newMutableDictionary setObject:@"foo" forKey:@"bar19"]; [newMutableDictionary setObject:@"foo" forKey:@"bar20"]; - id cfKeys[2] = { @"foo", @"bar", @"baz", @"quux" }; - id cfValues[2] = { @"foo", @"bar", @"baz", @"quux" }; + id cfKeys[4] = { @"foo", @"bar", @"baz", @"quux" }; + id cfValues[4] = { @"foo", @"bar", @"baz", @"quux" }; NSDictionary *nsDictionary = CFBridgingRelease(CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 2, nil, nil)); + NSDictionary *nscfDictionary = CFBridgingRelease(CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 4, nil, nil)); CFDictionaryRef cfDictionaryRef = CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 3, nil, nil); NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:@"hello world from foo" attributes:newDictionary]; @@ -412,6 +413,7 @@ NSSet* nsset = [[NSSet alloc] initWithObjects:str1,str2,str3,nil]; NSSet *nsmutableset = [[NSMutableSet alloc] initWithObjects:str1,str2,str3,nil]; [nsmutableset addObject:str4]; + NSSet* nscfSet = CFBridgingRelease(CFSetCreate(nil, (void *)cfValues, 2, nil)); CFDataRef data_ref = CFDataCreate(kCFAllocatorDefault, [immutableData bytes], 5); Index: lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py =================================================================== --- lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py +++ lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py @@ -20,6 +20,7 @@ self.appkit_tester_impl(self.nscontainers_data_formatter_commands) def nscontainers_data_formatter_commands(self): + self.expect( 'frame variable newArray nsDictionary newDictionary nscfDictionary cfDictionaryRef newMutableDictionary cfarray_ref mutable_array_ref', substrs=[ @@ -29,6 +30,8 @@ ' 2 key/value pairs', '(NSDictionary *) newDictionary = ', ' 12 key/value pairs', + '(NSDictionary *) nscfDictionary = ', + ' 4 key/value pairs', '(CFDictionaryRef) cfDictionaryRef = ', ' 3 key/value pairs', '(NSDictionary *) newMutableDictionary = ', @@ -39,6 +42,36 @@ ' @"11 elements"', ]) + self.expect( + 'frame variable -d run-target *nscfDictionary', + patterns=[ + '\(__NSCFDictionary\) \*nscfDictionary =', + 'key = 0x.* @"foo"', + 'value = 0x.* @"foo"', + 'key = 0x.* @"bar"', + 'value = 0x.* @"bar"', + 'key = 0x.* @"baz"', + 'value = 0x.* @"baz"', + 'key = 0x.* @"quux"', + 'value = 0x.* @"quux"', + ]) + + + self.expect( + 'frame var nscfSet', + substrs=[ + '(NSSet *) nscfSet = ', + '2 elements', + ]) + + self.expect( + 'frame variable -d run-target *nscfSet', + patterns=[ + '\(__NSCFSet\) \*nscfSet =', + '\[0\] = 0x.* @".*"', + '\[1\] = 0x.* @".*"', + ]) + self.expect( 'frame variable iset1 iset2 imset', substrs=['4 indexes', '512 indexes', '10 indexes']) Index: lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp =================================================================== --- lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp +++ lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp @@ -606,6 +606,11 @@ lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetM synthetic children", ConstString("__NSSetM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__NSCFSet synthetic children", ConstString("__NSCFSet"), + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSMutableSet synthetic children", ConstString("NSMutableSet"), Index: lldb/source/Plugins/Language/ObjC/NSSet.cpp =================================================================== --- lldb/source/Plugins/Language/ObjC/NSSet.cpp +++ lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "NSSet.h" +#include "CFBasicHash.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" @@ -79,6 +80,54 @@ std::vector<SetItemDescriptor> m_children; }; +class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSCFSetSyntheticFrontEnd() override; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct SetItemDescriptor { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + + CFBasicHash m_hashtable; + + CompilerType m_pair_type; + std::vector<SetItemDescriptor> m_children; + +private: + // Prime numbers. Values above 100 have been adjusted up so that the + // malloced block size will be just below a multiple of 512; values + // above 1200 have been adjusted up to just below a multiple of 4096. + constexpr static const uintptr_t NSDictionaryCapacities[] = { + 0, 3, 6, 11, 19, 32, 52, + 85, 118, 155, 237, 390, 672, 1065, + 1732, 2795, 4543, 7391, 12019, 19302, 31324, + 50629, 81956, 132580, 214215, 346784, 561026, 907847, + 1468567, 2376414, 3844982, 6221390, 10066379, 16287773, 26354132, + 42641916, 68996399, 111638327, 180634415, 292272755, + }; + + static const size_t NSDictionaryNumSizeBuckets = + sizeof(NSDictionaryCapacities) / sizeof(uint64_t); +}; + template <typename D32, typename D64> class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: @@ -245,21 +294,24 @@ uint64_t value = 0; - ConstString class_name_cs = descriptor->GetClassName(); - const char *class_name = class_name_cs.GetCString(); + ConstString class_name(descriptor->GetClassName()); - if (!class_name || !*class_name) + static const ConstString g_SetI("__NSSetI"); + static const ConstString g_OrderedSetI("__NSOrderedSetI"); + static const ConstString g_SetM("__NSSetM"); + static const ConstString g_SetCF("__NSCFSet"); + + if (class_name.IsEmpty()) return false; - if (!strcmp(class_name, "__NSSetI") || - !strcmp(class_name, "__NSOrderedSetI")) { + if (class_name == g_SetI || class_name == g_OrderedSetI) { Status error; value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); if (error.Fail()) return false; value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); - } else if (!strcmp(class_name, "__NSSetM")) { + } else if (class_name == g_SetM) { AppleObjCRuntime *apple_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); Status error; @@ -272,9 +324,15 @@ } if (error.Fail()) return false; + } else if (class_name == g_SetCF) { + ExecutionContext exe_ctx(process_sp); + CFBasicHash cfbh; + if (!cfbh.Update(valobj_addr, exe_ctx)) + return false; + value = cfbh.GetCount(); } else { auto &map(NSSet_Additionals::GetAdditionalSummaries()); - auto iter = map.find(class_name_cs), end = map.end(); + auto iter = map.find(class_name), end = map.end(); if (iter != end) return iter->second(valobj, stream, options); else @@ -321,16 +379,19 @@ if (!descriptor || !descriptor->IsValid()) return nullptr; - ConstString class_name_cs = descriptor->GetClassName(); - const char *class_name = class_name_cs.GetCString(); + ConstString class_name = descriptor->GetClassName(); - if (!class_name || !*class_name) + static const ConstString g_SetI("__NSSetI"); + static const ConstString g_OrderedSetI("__NSOrderedSetI"); + static const ConstString g_SetM("__NSSetM"); + static const ConstString g_SetCF("__NSCFSet"); + + if (class_name.IsEmpty()) return nullptr; - if (!strcmp(class_name, "__NSSetI") || - !strcmp(class_name, "__NSOrderedSetI")) { + if (class_name == g_SetI || class_name == g_OrderedSetI) { return (new NSSetISyntheticFrontEnd(valobj_sp)); - } else if (!strcmp(class_name, "__NSSetM")) { + } else if (class_name == g_SetM) { AppleObjCRuntime *apple_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); if (apple_runtime) { @@ -343,9 +404,11 @@ } else { return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); } + } else if (class_name == g_SetCF) { + return (new NSCFSetSyntheticFrontEnd(valobj_sp)); } else { auto &map(NSSet_Additionals::GetAdditionalSynthetics()); - auto iter = map.find(class_name_cs), end = map.end(); + auto iter = map.find(class_name), end = map.end(); if (iter != end) return iter->second(synth, valobj_sp); return nullptr; @@ -501,6 +564,124 @@ return set_item.valobj_sp; } +lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), + m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {} + +lldb_private::formatters::NSCFSetSyntheticFrontEnd:: + ~NSCFSetSyntheticFrontEnd() {} + +size_t +lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t +lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { + if (!m_hashtable.IsValid()) + return 0; + return m_hashtable.GetCount(); +} + +bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref); +} + +bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( + size_t idx) { + lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); + + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + Status error; + lldb::addr_t val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!val_at_idx) + continue; + tries++; + + SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + SetItemDescriptor &set_item = m_children[idx]; + if (!set_item.valobj_sp) { + auto ptr_size = m_ptr_size; + DataBufferHeap buffer(ptr_size, 0); + switch (ptr_size) { + case 0: // architecture has no clue?? - fail + return lldb::ValueObjectSP(); + case 4: + *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; + break; + case 8: + *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; + break; + default: + assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); + } + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + + DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), m_order, + m_ptr_size); + + set_item.valobj_sp = CreateValueObjectFromData( + idx_name.GetString(), data, m_exe_ctx_ref, + m_backend.GetCompilerType().GetBasicTypeFromAST( + lldb::eBasicTypeObjCID)); + } + return set_item.valobj_sp; +} + template <typename D32, typename D64> lldb_private::formatters:: GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd( Index: lldb/source/Plugins/Language/ObjC/NSDictionary.h =================================================================== --- lldb/source/Plugins/Language/ObjC/NSDictionary.h +++ lldb/source/Plugins/Language/ObjC/NSDictionary.h @@ -1,5 +1,4 @@ -//===-- NSDictionary.h ---------------------------------------------------*- C++ -//-*-===// +//===-- NSDictionary.h ------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. Index: lldb/source/Plugins/Language/ObjC/NSDictionary.cpp =================================================================== --- lldb/source/Plugins/Language/ObjC/NSDictionary.cpp +++ lldb/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -10,6 +10,7 @@ #include "clang/AST/DeclCXX.h" +#include "CFBasicHash.h" #include "NSDictionary.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" @@ -97,6 +98,7 @@ namespace lldb_private { namespace formatters { +#pragma mark NSDictionaryI class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); @@ -140,6 +142,57 @@ std::vector<DictionaryItemDescriptor> m_children; }; +#pragma mark NSCFDictionary +class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSCFDictionarySyntheticFrontEnd() override; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + + CFBasicHash m_hashtable; + + CompilerType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; + +private: + // Prime numbers. Values above 100 have been adjusted up so that the + // malloced block size will be just below a multiple of 512; values + // above 1200 have been adjusted up to just below a multiple of 4096. + constexpr static const uintptr_t NSDictionaryCapacities[] = { + 0, 3, 6, 11, 19, 32, 52, + 85, 118, 155, 237, 390, 672, 1065, + 1732, 2795, 4543, 7391, 12019, 19302, 31324, + 50629, 81956, 132580, 214215, 346784, 561026, 907847, + 1468567, 2376414, 3844982, 6221390, 10066379, 16287773, 26354132, + 42641916, 68996399, 111638327, 180634415, 292272755, + }; + + static const size_t NSDictionaryNumSizeBuckets = + sizeof(NSDictionaryCapacities) / sizeof(uint64_t); +}; + +#pragma mark NSDictionary1 class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); @@ -160,6 +213,7 @@ ValueObjectSP m_pair; }; +#pragma mark GenericNSDictionaryM template <typename D32, typename D64> class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: @@ -377,6 +431,7 @@ static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); static const ConstString g_Dictionary0("__NSDictionary0"); static const ConstString g_DictionaryCF("__NSCFDictionary"); + static const ConstString g_DictionaryCFRef("CFDictionaryRef"); if (class_name.IsEmpty()) return false; @@ -388,8 +443,7 @@ if (error.Fail()) return false; value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); - } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy || - class_name == g_DictionaryCF) { + } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy) { AppleObjCRuntime *apple_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); Status error; @@ -407,8 +461,13 @@ value = 1; } else if (class_name == g_Dictionary0) { value = 0; - } - else { + } else if (class_name == g_DictionaryCF || class_name == g_DictionaryCFRef) { + ExecutionContext exe_ctx(process_sp); + CFBasicHash cfbh; + if (!cfbh.Update(valobj_addr, exe_ctx)) + return false; + value = cfbh.GetCount(); + } else { auto &map(NSDictionary_Additionals::GetAdditionalSummaries()); for (auto &candidate : map) { if (candidate.first && candidate.first->Match(class_name)) @@ -466,6 +525,8 @@ static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable"); static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); static const ConstString g_Dictionary0("__NSDictionary0"); + static const ConstString g_DictionaryCF("__NSCFDictionary"); + static const ConstString g_DictionaryCFRef("CFDictionaryRef"); if (class_name.IsEmpty()) return nullptr; @@ -484,6 +545,8 @@ return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp)); } else if (class_name == g_Dictionary1) { return (new NSDictionary1SyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_DictionaryCF || class_name == g_DictionaryCFRef) { + return (new NSCFDictionarySyntheticFrontEnd(valobj_sp)); } else { auto &map(NSDictionary_Additionals::GetAdditionalSynthetics()); for (auto &candidate : map) { @@ -641,6 +704,133 @@ return dict_item.valobj_sp; } +#pragma mark NSCFDictionary +lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8), + m_order(lldb::eByteOrderInvalid), m_hashtable(), m_pair_type() {} + +lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + ~NSCFDictionarySyntheticFrontEnd() {} + +size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; +} + +size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + CalculateNumChildren() { + if (!m_hashtable.IsValid()) + return 0; + return m_hashtable.GetCount(); +} + +bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return false; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref); +} + +bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex( + size_t idx) { + lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer(); + lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); + + uint32_t num_children = CalculateNumChildren(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + Status error; + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + if (m_ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {} @@ -733,8 +923,8 @@ } template <typename D32, typename D64> -size_t -lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: GetIndexOfChildWithName(ConstString name) { +size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< + D32, D64>::GetIndexOfChildWithName(ConstString name) { const char *item_name = name.GetCString(); uint32_t idx = ExtractIndexFromString(item_name); if (idx < UINT32_MAX && idx >= CalculateNumChildren()) @@ -783,7 +973,7 @@ } if (error.Fail()) return false; - return false; + return true; } template <typename D32, typename D64> @@ -795,9 +985,8 @@ template <typename D32, typename D64> lldb::ValueObjectSP -lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: - GetChildAtIndex( - size_t idx) { +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< + D32, D64>::GetChildAtIndex(size_t idx) { lldb::addr_t m_keys_ptr; lldb::addr_t m_values_ptr; if (m_data_32) { @@ -885,7 +1074,6 @@ return dict_item.valobj_sp; } - lldb_private::formatters::Foundation1100:: NSDictionaryMSyntheticFrontEnd:: NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) Index: lldb/source/Plugins/Language/ObjC/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Language/ObjC/CMakeLists.txt +++ lldb/source/Plugins/Language/ObjC/CMakeLists.txt @@ -11,6 +11,7 @@ add_lldb_library(lldbPluginObjCLanguage PLUGIN ObjCLanguage.cpp CF.cpp + CFBasicHash.cpp Cocoa.cpp CoreMedia.cpp NSArray.cpp Index: lldb/source/Plugins/Language/ObjC/CFBasicHash.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Language/ObjC/CFBasicHash.h @@ -0,0 +1,126 @@ +//===-- CFBasicHash.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CFBASICHASH_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CFBASICHASH_H + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +namespace lldb_private { + +class CFBasicHash { +public: + enum class HashType { set = 0, dict }; + + CFBasicHash(); + ~CFBasicHash(); + + bool Update(lldb::addr_t addr, ExecutionContextRef exe_ctx_rf); + + bool IsValid() const; + + bool IsMutable() const { return m_mutable; }; + bool IsMultiVariant() const { return m_multi; } + HashType GetType() const { return m_type; } + + size_t GetCount() const; + lldb::addr_t GetKeyPointer() const; + lldb::addr_t GetValuePointer() const; + + constexpr static const uintptr_t __CFBasicHashTableSizes[64] = {0, + 3, + 7, + 13, + 23, + 41, + 71, + 127, + 191, + 251, + 383, + 631, + 1087, + 1723, + 2803, + 4523, + 7351, + 11959, + 19447, + 31231, + 50683, + 81919, + 132607, + 214519, + 346607, + 561109, + 907759, + 1468927, + 2376191, + 3845119, + 6221311, + 10066421, + 16287743, + 26354171, + 42641881, + 68996069, + 111638519, + 180634607, + 292272623, + 472907251, + 765180413UL, + 1238087663UL, + 2003267557UL, + 3241355263UL, + 5244622819UL}; + +private: + template <typename T> struct __CFBasicHash { + struct RuntimeBase { + T cfisa; + T cfinfoa; + } base; + + struct Bits { + uint16_t __reserved0; + uint16_t __reserved1 : 2; + uint16_t keys_offset : 1; + uint16_t counts_offset : 2; + uint16_t counts_width : 2; + uint16_t __reserved2 : 9; + uint32_t used_buckets; // number of used buckets + uint64_t deleted : 16; // number of elements deleted + uint64_t num_buckets_idx : 8; // index to number of buckets + uint64_t __reserved3 : 40; + uint64_t __reserved4; + } bits; + + T pointers[3]; + }; + + size_t GetPointerCount() const; + +private: + uint32_t m_ptr_size; + lldb::ByteOrder m_byte_order; + + Address m_address; + + __CFBasicHash<uint32_t> *m_ht_32; + __CFBasicHash<uint64_t> *m_ht_64; + + ExecutionContextRef m_exe_ctx_ref; + + bool m_mutable; + bool m_multi; + HashType m_type; +}; + +}; // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CFBASICHASH_H Index: lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp @@ -0,0 +1,182 @@ +#include "CFBasicHash.h" + +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb; +using namespace lldb_private; + +CFBasicHash::CFBasicHash() + : m_ptr_size(UINT32_MAX), m_byte_order(eByteOrderInvalid), + m_address(LLDB_INVALID_ADDRESS) {} + +CFBasicHash::~CFBasicHash() { m_address = LLDB_INVALID_ADDRESS; } + +bool CFBasicHash::IsValid() const { + if (m_address != LLDB_INVALID_ADDRESS) + if (m_ptr_size == 4) + return m_ht_32; + return m_ht_64; + return false; +} + +bool CFBasicHash::Update(addr_t addr, ExecutionContextRef exe_ctx_rf) { + if (addr == LLDB_INVALID_ADDRESS) + return false; + + m_address = addr; + m_exe_ctx_ref = exe_ctx_rf; + m_ptr_size = + m_exe_ctx_ref.GetTargetSP()->GetArchitecture().GetAddressByteSize(); + m_byte_order = m_exe_ctx_ref.GetTargetSP()->GetArchitecture().GetByteOrder(); + Status error; + size_t size = 0; + + if (m_ptr_size == 4) { + + size = sizeof(__CFBasicHash<uint32_t>::RuntimeBase); + size += sizeof(__CFBasicHash<uint32_t>::Bits); + + DataBufferHeap buffer(size, 0); + m_exe_ctx_ref.GetProcessSP()->ReadMemory(addr, buffer.GetBytes(), size, + error); + if (error.Fail()) + return false; + + DataExtractor data_extractor = DataExtractor( + buffer.GetBytes(), buffer.GetByteSize(), m_byte_order, m_ptr_size); + + m_ht_32 = new __CFBasicHash<uint32_t>(); + size_t copied = data_extractor.CopyData(0, size, m_ht_32); + + if (copied != size) { + delete m_ht_32; + m_ht_32 = nullptr; + return false; + } + + m_mutable = !(m_ht_32->base.cfinfoa & (1 << 6)); + m_multi = m_ht_32->bits.counts_offset; + m_type = static_cast<HashType>(m_ht_32->bits.keys_offset); + addr_t ptr_offset = addr + size; + size_t ptr_count = GetPointerCount(); + size = ptr_count * sizeof(uint32_t); + buffer = DataBufferHeap(size, 0); + m_exe_ctx_ref.GetProcessSP()->ReadMemory(ptr_offset, buffer.GetBytes(), + size, error); + + if (error.Fail()) { + delete m_ht_32; + m_ht_32 = nullptr; + return false; + } + + data_extractor = DataExtractor(buffer.GetBytes(), buffer.GetByteSize(), + m_byte_order, m_ptr_size); + + copied = data_extractor.CopyData(0, size, m_ht_32->pointers); + + if (copied != size) { + delete m_ht_32; + m_ht_32 = nullptr; + return false; + } + + return true; + + } else if (m_ptr_size == 8) { + + size = sizeof(__CFBasicHash<uint64_t>::RuntimeBase); + size += sizeof(__CFBasicHash<uint64_t>::Bits); + + DataBufferHeap buffer(size, 0); + m_exe_ctx_ref.GetProcessSP()->ReadMemory(addr, buffer.GetBytes(), size, + error); + if (error.Fail()) + return false; + + DataExtractor data_extractor = DataExtractor( + buffer.GetBytes(), buffer.GetByteSize(), m_byte_order, m_ptr_size); + + m_ht_64 = new __CFBasicHash<uint64_t>(); + size_t copied = data_extractor.CopyData(0, size, m_ht_64); + + if (copied != size) { + delete m_ht_64; + m_ht_64 = nullptr; + return false; + } + + m_mutable = !(m_ht_64->base.cfinfoa & (1 << 6)); + m_multi = m_ht_64->bits.counts_offset; + m_type = static_cast<HashType>(m_ht_64->bits.keys_offset); + addr_t ptr_offset = addr + size; + size_t ptr_count = GetPointerCount(); + size = ptr_count * sizeof(uint64_t); + buffer = DataBufferHeap(size, 0); + m_exe_ctx_ref.GetProcessSP()->ReadMemory(ptr_offset, buffer.GetBytes(), + size, error); + + if (error.Fail()) { + delete m_ht_64; + m_ht_64 = nullptr; + return false; + } + + data_extractor = DataExtractor(buffer.GetBytes(), buffer.GetByteSize(), + m_byte_order, m_ptr_size); + + copied = data_extractor.CopyData(0, size, m_ht_64->pointers); + + if (copied != size) { + delete m_ht_64; + m_ht_64 = nullptr; + return false; + } + + return true; + } + + // Unsupported architecure + return false; +} + +size_t CFBasicHash::GetCount() const { + if (!IsValid()) + return 0; + + if (!m_multi) + return (m_ptr_size == 4) ? m_ht_32->bits.used_buckets + : m_ht_64->bits.used_buckets; + + // FIXME: Add support for multi + return 0; +} + +size_t CFBasicHash::GetPointerCount() const { + if (!IsValid()) + return 0; + + if (m_multi) + return 3; // Bits::counts_offset; + return (m_type == HashType::dict) + 1; +} + +addr_t CFBasicHash::GetKeyPointer() const { + if (!IsValid()) + return LLDB_INVALID_ADDRESS; + + if (m_ptr_size == 4) + return m_ht_32->pointers[m_ht_32->bits.keys_offset]; + + return m_ht_64->pointers[m_ht_64->bits.keys_offset]; +} + +addr_t CFBasicHash::GetValuePointer() const { + if (!IsValid()) + return LLDB_INVALID_ADDRESS; + + if (m_ptr_size == 4) + return m_ht_32->pointers[0]; + + return m_ht_64->pointers[0]; +}
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits