<rdar://problem/12529957>

Synthetic children provider for NSSet

llvm-svn: 175468
This commit is contained in:
Enrico Granata 2013-02-18 23:16:23 +00:00
parent 3a8002f61d
commit 9f02e0912d
6 changed files with 601 additions and 8 deletions

View File

@ -371,6 +371,131 @@ namespace lldb_private {
SyntheticChildrenFrontEnd* NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP);
class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd
{
private:
struct DataDescriptor_32
{
uint32_t _used : 26;
uint32_t _szidx : 6;
};
struct DataDescriptor_64
{
uint64_t _used : 58;
uint32_t _szidx : 6;
};
struct SetItemDescriptor
{
lldb::addr_t item_ptr;
lldb::ValueObjectSP valobj_sp;
};
public:
NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp);
virtual size_t
CalculateNumChildren ();
virtual lldb::ValueObjectSP
GetChildAtIndex (size_t idx);
virtual bool
Update();
virtual bool
MightHaveChildren ();
virtual size_t
GetIndexOfChildWithName (const ConstString &name);
virtual
~NSSetISyntheticFrontEnd ();
private:
ExecutionContextRef m_exe_ctx_ref;
uint8_t m_ptr_size;
DataDescriptor_32 *m_data_32;
DataDescriptor_64 *m_data_64;
lldb::addr_t m_data_ptr;
std::vector<SetItemDescriptor> m_children;
};
class NSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd
{
private:
struct DataDescriptor_32
{
uint32_t _used : 26;
uint32_t _size;
uint32_t _mutations;
uint32_t _objs_addr;
};
struct DataDescriptor_64
{
uint64_t _used : 58;
uint64_t _size;
uint64_t _mutations;
uint64_t _objs_addr;
};
struct SetItemDescriptor
{
lldb::addr_t item_ptr;
lldb::ValueObjectSP valobj_sp;
};
public:
NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp);
virtual size_t
CalculateNumChildren ();
virtual lldb::ValueObjectSP
GetChildAtIndex (size_t idx);
virtual bool
Update();
virtual bool
MightHaveChildren ();
virtual size_t
GetIndexOfChildWithName (const ConstString &name);
virtual
~NSSetMSyntheticFrontEnd ();
private:
ExecutionContextRef m_exe_ctx_ref;
uint8_t m_ptr_size;
DataDescriptor_32 *m_data_32;
DataDescriptor_64 *m_data_64;
std::vector<SetItemDescriptor> m_children;
};
class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd
{
public:
NSSetCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp);
virtual size_t
CalculateNumChildren ();
virtual lldb::ValueObjectSP
GetChildAtIndex (size_t idx);
virtual bool
Update();
virtual bool
MightHaveChildren ();
virtual size_t
GetIndexOfChildWithName (const ConstString &name);
virtual
~NSSetCodeRunningSyntheticFrontEnd ();
};
SyntheticChildrenFrontEnd* NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP);
class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd
{
public:

View File

@ -868,6 +868,15 @@ FormatManager::LoadObjCFormatters()
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider<true>, "NSDictionary summary provider", ConstString("CFDictionaryRef"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider<true>, "NSDictionary summary provider", ConstString("CFMutableDictionaryRef"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "NSSet summary", ConstString("NSSet"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<true>, "CFSetRef summary", ConstString("CFSetRef"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<true>, "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "__NSSetI summary", ConstString("__NSSetI"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "__NSSetM summary", ConstString("__NSSetM"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "NSSet summary", ConstString("NSMutableSet"), appkit_flags);
// AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}", ConstString("$_lldb_typegen_nspair"), appkit_flags);
appkit_flags.SetDontShowChildren(true);
@ -885,6 +894,11 @@ FormatManager::LoadObjCFormatters()
AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFDictionaryRef"), ScriptedSyntheticChildren::Flags());
AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFMutableDictionaryRef"), ScriptedSyntheticChildren::Flags());
AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("NSSet"), ScriptedSyntheticChildren::Flags());
AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("__NSSetI"), ScriptedSyntheticChildren::Flags());
AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("__NSSetM"), ScriptedSyntheticChildren::Flags());
AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("NSMutableSet"), ScriptedSyntheticChildren::Flags());
AddScriptSummary(appkit_category_sp, "lldb.formatters.objc.CFBag.CFBag_SummaryProvider", ConstString("CFBagRef"), appkit_flags);
AddScriptSummary(appkit_category_sp, "lldb.formatters.objc.CFBag.CFBag_SummaryProvider", ConstString("__CFBag"), appkit_flags);
AddScriptSummary(appkit_category_sp, "lldb.formatters.objc.CFBag.CFBag_SummaryProvider", ConstString("const struct __CFBag"), appkit_flags);
@ -934,14 +948,6 @@ FormatManager::LoadObjCFormatters()
AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSHost summary provider", ConstString("NSHost"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSTask summary provider", ConstString("NSTask"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSValue summary provider", ConstString("NSValue"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "NSSet summary", ConstString("NSSet"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<true>, "CFSetRef summary", ConstString("CFSetRef"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<true>, "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "__NSSetI summary", ConstString("__NSSetI"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "__NSSetM summary", ConstString("__NSSetM"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>, "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("NSURL"), appkit_flags);
AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("CFURLRef"), appkit_flags);

View File

@ -111,6 +111,336 @@ lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& str
return true;
}
SyntheticChildrenFrontEnd* lldb_private::formatters::NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
{
lldb::ProcessSP process_sp (valobj_sp->GetProcessSP());
if (!process_sp)
return NULL;
ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
if (!runtime)
return NULL;
if (!valobj_sp->IsPointerType())
{
Error error;
valobj_sp = valobj_sp->AddressOf(error);
if (error.Fail() || !valobj_sp)
return NULL;
}
ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get()));
if (!descriptor.get() || !descriptor->IsValid())
return NULL;
const char* class_name = descriptor->GetClassName().GetCString();
if (!class_name || !*class_name)
return NULL;
if (!strcmp(class_name,"__NSSetI"))
{
return (new NSSetISyntheticFrontEnd(valobj_sp));
}
else if (!strcmp(class_name,"__NSSetM"))
{
return (new NSSetMSyntheticFrontEnd(valobj_sp));
}
else
{
return /*(new NSSetCodeRunningSyntheticFrontEnd(valobj_sp))*/ NULL;
}
}
lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
SyntheticChildrenFrontEnd(*valobj_sp.get()),
m_exe_ctx_ref(),
m_ptr_size(8),
m_data_32(NULL),
m_data_64(NULL)
{
if (valobj_sp)
Update();
}
lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd ()
{
delete m_data_32;
m_data_32 = NULL;
delete m_data_64;
m_data_64 = NULL;
}
size_t
lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName (const 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::NSSetISyntheticFrontEnd::CalculateNumChildren ()
{
if (!m_data_32 && !m_data_64)
return 0;
return (m_data_32 ? m_data_32->_used : m_data_64->_used);
}
bool
lldb_private::formatters::NSSetISyntheticFrontEnd::Update()
{
m_children.clear();
delete m_data_32;
m_data_32 = NULL;
delete m_data_64;
m_data_64 = NULL;
m_ptr_size = 0;
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return false;
if (valobj_sp->IsDynamic())
valobj_sp = valobj_sp->GetStaticValue();
if (!valobj_sp)
return false;
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
Error error;
if (valobj_sp->IsPointerType())
{
valobj_sp = valobj_sp->Dereference(error);
if (error.Fail() || !valobj_sp)
return false;
}
error.Clear();
lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
if (!process_sp)
return false;
m_ptr_size = process_sp->GetAddressByteSize();
uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
if (m_ptr_size == 4)
{
m_data_32 = new DataDescriptor_32();
process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
}
else
{
m_data_64 = new DataDescriptor_64();
process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
}
if (error.Fail())
return false;
m_data_ptr = data_location + m_ptr_size;
return false;
}
bool
lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren ()
{
return true;
}
lldb::ValueObjectSP
lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex (size_t idx)
{
uint32_t num_children = CalculateNumChildren();
if (idx >= num_children)
return lldb::ValueObjectSP();
if (m_children.empty())
{
// do the scan phase
lldb::addr_t obj_at_idx = 0;
uint32_t tries = 0;
uint32_t test_idx = 0;
while(tries < num_children)
{
obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
if (!process_sp)
return lldb::ValueObjectSP();
Error error;
obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
if (error.Fail())
return lldb::ValueObjectSP();
test_idx++;
if (!obj_at_idx)
continue;
tries++;
SetItemDescriptor descriptor = {obj_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)
{
// make the new ValueObject
StreamString expr;
expr.Printf("(id)%" PRIu64,set_item.item_ptr);
StreamString idx_name;
idx_name.Printf("[%zu]",idx);
set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref);
}
return set_item.valobj_sp;
}
lldb_private::formatters::NSSetMSyntheticFrontEnd::NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
SyntheticChildrenFrontEnd(*valobj_sp.get()),
m_exe_ctx_ref(),
m_ptr_size(8),
m_data_32(NULL),
m_data_64(NULL)
{
if (valobj_sp)
Update ();
}
lldb_private::formatters::NSSetMSyntheticFrontEnd::~NSSetMSyntheticFrontEnd ()
{
delete m_data_32;
m_data_32 = NULL;
delete m_data_64;
m_data_64 = NULL;
}
size_t
lldb_private::formatters::NSSetMSyntheticFrontEnd::GetIndexOfChildWithName (const 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::NSSetMSyntheticFrontEnd::CalculateNumChildren ()
{
if (!m_data_32 && !m_data_64)
return 0;
return (m_data_32 ? m_data_32->_used : m_data_64->_used);
}
bool
lldb_private::formatters::NSSetMSyntheticFrontEnd::Update()
{
m_children.clear();
ValueObjectSP valobj_sp = m_backend.GetSP();
m_ptr_size = 0;
delete m_data_32;
m_data_32 = NULL;
delete m_data_64;
m_data_64 = NULL;
if (!valobj_sp)
return false;
if (valobj_sp->IsDynamic())
valobj_sp = valobj_sp->GetStaticValue();
if (!valobj_sp)
return false;
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
Error error;
if (valobj_sp->IsPointerType())
{
valobj_sp = valobj_sp->Dereference(error);
if (error.Fail() || !valobj_sp)
return false;
}
error.Clear();
lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
if (!process_sp)
return false;
m_ptr_size = process_sp->GetAddressByteSize();
uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
if (m_ptr_size == 4)
{
m_data_32 = new DataDescriptor_32();
process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
}
else
{
m_data_64 = new DataDescriptor_64();
process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
}
if (error.Fail())
return false;
return false;
}
bool
lldb_private::formatters::NSSetMSyntheticFrontEnd::MightHaveChildren ()
{
return true;
}
lldb::ValueObjectSP
lldb_private::formatters::NSSetMSyntheticFrontEnd::GetChildAtIndex (size_t idx)
{
lldb::addr_t m_objs_addr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
uint32_t num_children = CalculateNumChildren();
if (idx >= num_children)
return lldb::ValueObjectSP();
if (m_children.empty())
{
// do the scan phase
lldb::addr_t obj_at_idx = 0;
uint32_t tries = 0;
uint32_t test_idx = 0;
while(tries < num_children)
{
obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
if (!process_sp)
return lldb::ValueObjectSP();
Error error;
obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
if (error.Fail())
return lldb::ValueObjectSP();
test_idx++;
if (!obj_at_idx)
continue;
tries++;
SetItemDescriptor descriptor = {obj_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)
{
// make the new ValueObject
StreamString expr;
expr.Printf("(id)%" PRIu64,set_item.item_ptr);
StreamString idx_name;
idx_name.Printf("[%zu]",idx);
set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref);
}
return set_item.valobj_sp;
}
template bool
lldb_private::formatters::NSSetSummaryProvider<true> (ValueObject& valobj, Stream& stream);

View File

@ -0,0 +1,9 @@
LEVEL = ../../../make
OBJC_SOURCES := main.m
CFLAGS_EXTRAS += -w
include $(LEVEL)/Makefile.rules
LDFLAGS += -framework Foundation

View File

@ -0,0 +1,89 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
import datetime
import lldbutil
class DataFormatterRdar12529957TestCase(TestBase):
mydir = os.path.join("functionalities", "data-formatter", "rdar-12529957")
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_rdar12529957_with_dsym_and_run_command(self):
"""Test that NSSet reports its synthetic children properly."""
self.buildDsym()
self.rdar12529957_tester()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dwarf_test
def test_rdar12529957_with_dwarf_and_run_command(self):
"""Test that NSSet reports its synthetic children properly."""
self.buildDwarf()
self.rdar12529957_tester()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break at.
self.line = line_number('main.m', '// Set break point at this line.')
def rdar12529957_tester(self):
"""Test that NSSet reports its synthetic children properly."""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True)
self.runCmd("run", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
self.runCmd('type format clear', check=False)
self.runCmd('type summary clear', check=False)
self.runCmd('type synth clear', check=False)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
# Now check that we are displaying Cocoa classes correctly
self.expect('frame variable set',
substrs = ['4 objects'])
self.expect('frame variable mutable',
substrs = ['9 objects'])
self.expect('frame variable set --ptr-depth 1 -d run -T',
substrs = ['4 objects','[0]','[1]','[2]','[3]','hello','world','(int)1','(int)2'])
self.expect('frame variable mutable --ptr-depth 1 -d run -T',
substrs = ['9 objects','(int)5','@"3 objects"','@"www.apple.com"','(int)3','@"world"','(int)4'])
self.runCmd("next")
self.expect('frame variable mutable',
substrs = ['0 objects'])
self.runCmd("next")
self.expect('frame variable mutable',
substrs = ['4 objects'])
self.expect('frame variable mutable --ptr-depth 1 -d run -T',
substrs = ['4 objects','[0]','[1]','[2]','[3]','hello','world','(int)1','(int)2'])
self.runCmd("next")
self.expect('frame variable mutable',
substrs = ['4 objects'])
self.expect('frame variable mutable --ptr-depth 1 -d run -T',
substrs = ['4 objects','[0]','[1]','[2]','[3]','hello','world','(int)1','(int)2'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,34 @@
//===-- main.m ------------------------------------------------*- ObjC -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSSet* set = [NSSet setWithArray:@[@1,@"hello",@2,@"world"]];
NSMutableSet* mutable = [NSMutableSet setWithCapacity:5];
[mutable addObject:@1];
[mutable addObject:@2];
[mutable addObject:@3];
[mutable addObject:@4];
[mutable addObject:@5];
[mutable addObject:[NSURL URLWithString:@"www.apple.com"]];
[mutable addObject:@[@1,@2,@3]];
[mutable unionSet:set];
[mutable removeAllObjects]; // Set break point at this line.
[mutable unionSet:set];
[mutable addObject:@1];
[pool drain];
return 0;
}