This revision was automatically updated to reflect the committed changes.
Closed by commit rG8f5beb4c4b11: [lldb/Dataformatter] Add support for
CoreFoundation Dictionaries and Sets. (authored by mib).
Changed prior to commit:
https://reviews.llvm.org/D78396?vs=260012&id=260447#toc
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D78396/new/
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,13 +376,19 @@
[newMutableDictionary setObject:@"foo" forKey:@"bar19"];
[newMutableDictionary setObject:@"foo" forKey:@"bar20"];
- id cfKeys[2] = { @"foo", @"bar", @"baz", @"quux" };
- id cfValues[2] = { @"foo", @"bar", @"baz", @"quux" };
- NSDictionary *nsDictionary = CFBridgingRelease(CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 2, nil, nil));
- CFDictionaryRef cfDictionaryRef = CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 3, nil, nil);
-
- NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:@"hello world from foo" attributes:newDictionary];
- [attrString isEqual:nil];
+ 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];
+ [attrString isEqual:nil];
NSAttributedString* mutableAttrString = [[NSMutableAttributedString alloc] initWithString:@"hello world from foo" attributes:newDictionary];
[mutableAttrString isEqual:nil];
@@ -411,9 +417,11 @@
NSSet* nsset = [[NSSet alloc] initWithObjects:str1,str2,str3,nil];
NSSet *nsmutableset = [[NSMutableSet alloc] initWithObjects:str1,str2,str3,nil];
- [nsmutableset addObject:str4];
+ [nsmutableset addObject:str4];
+ NSSet *nscfSet =
+ CFBridgingRelease(CFSetCreate(nil, (void *)cfValues, 2, nil));
- CFDataRef data_ref = CFDataCreate(kCFAllocatorDefault, [immutableData bytes], 5);
+ CFDataRef data_ref = CFDataCreate(kCFAllocatorDefault, [immutableData bytes], 5);
CFMutableDataRef mutable_data_ref = CFDataCreateMutable(kCFAllocatorDefault, 8);
CFDataAppendBytes(mutable_data_ref, [mutableData 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
@@ -29,6 +29,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 = ',
@@ -40,6 +42,36 @@
])
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,36 @@
std::vector<SetItemDescriptor> m_children;
};
+class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ 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;
+};
+
template <typename D32, typename D64>
class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
@@ -245,21 +276,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 +306,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 +361,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 +386,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;
@@ -475,16 +520,18 @@
auto ptr_size = process_sp->GetAddressByteSize();
DataBufferHeap buffer(ptr_size, 0);
switch (ptr_size) {
- case 0: // architecture has no clue?? - fail
+ case 0: // architecture has no clue - fail
return lldb::ValueObjectSP();
case 4:
- *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
+ *reinterpret_cast<uint32_t *>(buffer.GetBytes()) =
+ static_cast<uint32_t>(set_item.item_ptr);
break;
case 8:
- *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
+ *reinterpret_cast<uint64_t *>(buffer.GetBytes()) =
+ static_cast<uint64_t>(set_item.item_ptr);
break;
default:
- assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
+ lldbassert(false && "pointer size is not 4 nor 8");
}
StreamString idx_name;
idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
@@ -501,6 +548,128 @@
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() {}
+
+size_t
+lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ const 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();
+
+ const 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;
+
+ // Iterate over inferior memory, reading value pointers by shifting the
+ // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
+ // fails, otherwise, continue until the number of tries matches the number
+ // of childen.
+ 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) {
+
+ DataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0));
+
+ switch (m_ptr_size) {
+ case 0: // architecture has no clue - fail
+ return lldb::ValueObjectSP();
+ case 4:
+ *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) =
+ static_cast<uint32_t>(set_item.item_ptr);
+ break;
+ case 8:
+ *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) =
+ static_cast<uint64_t>(set_item.item_ptr);
+ break;
+ default:
+ lldbassert(false && "pointer size is not 4 nor 8");
+ }
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ DataExtractor data(buffer_sp, 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"
@@ -140,6 +141,37 @@
std::vector<DictionaryItemDescriptor> m_children;
};
+class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ 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;
+};
+
class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
@@ -377,6 +409,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 +421,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 +439,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 +503,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 +523,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 +682,140 @@
return dict_item.valobj_sp;
}
+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() {}
+
+size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ const 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();
+
+ const 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;
+
+ // Iterate over inferior memory, reading key/value pointers by shifting each
+ // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read
+ // fails, otherwise, continue until the number of tries matches the number
+ // of childen.
+ 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));
+
+ switch (m_ptr_size) {
+ case 0: // architecture has no clue - fail
+ return lldb::ValueObjectSP();
+ case 4: {
+ uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes());
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } break;
+ case 8: {
+ uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes());
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } break;
+ default:
+ lldbassert(false && "pointer size is not 4 nor 8");
+ }
+
+ 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 +908,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 +958,7 @@
}
if (error.Fail())
return false;
- return false;
+ return true;
}
template <typename D32, typename D64>
@@ -795,9 +970,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 +1059,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,77 @@
+//===-- 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() = default;
+ ~CFBasicHash() = default;
+
+ 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;
+
+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];
+ };
+ template <typename T> bool UpdateFor(std::unique_ptr<__CFBasicHash<T>> &m_ht);
+
+ size_t GetPointerCount() const;
+
+private:
+ uint32_t m_ptr_size = UINT32_MAX;
+ lldb::ByteOrder m_byte_order = lldb::eByteOrderInvalid;
+ Address m_address = LLDB_INVALID_ADDRESS;
+ std::unique_ptr<__CFBasicHash<uint32_t>> m_ht_32 = nullptr;
+ std::unique_ptr<__CFBasicHash<uint64_t>> m_ht_64 = nullptr;
+ ExecutionContextRef m_exe_ctx_ref;
+ bool m_mutable = true;
+ bool m_multi = false;
+ 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,114 @@
+#include "CFBasicHash.h"
+
+#include "lldb/Utility/Endian.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool CFBasicHash::IsValid() const {
+ if (m_address != LLDB_INVALID_ADDRESS) {
+ if (m_ptr_size == 4 && m_ht_32)
+ return true;
+ else if (m_ptr_size == 8 && m_ht_64)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+bool CFBasicHash::Update(addr_t addr, ExecutionContextRef exe_ctx_rf) {
+ if (addr == LLDB_INVALID_ADDRESS || !addr)
+ 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();
+
+ if (m_ptr_size == 4)
+ return UpdateFor(m_ht_32);
+ else if (m_ptr_size == 8)
+ return UpdateFor(m_ht_64);
+ return false;
+
+ llvm_unreachable(
+ "Unsupported architecture. Only 32bits and 64bits supported.");
+}
+
+template <typename T>
+bool CFBasicHash::UpdateFor(std::unique_ptr<__CFBasicHash<T>> &m_ht) {
+ if (m_byte_order != endian::InlHostByteOrder())
+ return false;
+
+ Status error;
+ Target *target = m_exe_ctx_ref.GetTargetSP().get();
+ addr_t addr = m_address.GetLoadAddress(target);
+ size_t size = sizeof(typename __CFBasicHash<T>::RuntimeBase) +
+ sizeof(typename __CFBasicHash<T>::Bits);
+
+ m_ht = std::make_unique<__CFBasicHash<T>>();
+ m_exe_ctx_ref.GetProcessSP()->ReadMemory(addr, m_ht.get(),
+ size, error);
+ if (error.Fail())
+ return false;
+
+ m_mutable = !(m_ht->base.cfinfoa & (1 << 6));
+ m_multi = m_ht->bits.counts_offset;
+ m_type = static_cast<HashType>(m_ht->bits.keys_offset);
+ addr_t ptr_offset = addr + size;
+ size_t ptr_count = GetPointerCount();
+ size = ptr_count * sizeof(T);
+
+ m_exe_ctx_ref.GetProcessSP()->ReadMemory(ptr_offset, m_ht->pointers, size,
+ error);
+
+ if (error.Fail()) {
+ m_ht = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits