forked from OSchip/llvm-project
Added formatters for libc++ (http://libcxx.llvm.org):
std::string has a summary provider std::vector std::list and std::map have both a summary and a synthetic children provider Given the usage of a custom namespace (std::__1::classname) for the implementation of libc++, we keep both libstdcpp and libc++ formatters enabled at the same time since that raises no conflicts and enabled for seamless transition between the two The formatters for libc++ reside in a libcxx category, and are loaded from libcxx.py (to be found in examples/synthetic) The formatters-stl test cases have been divided to be separate for libcxx and libstdcpp. This separation is necessary because (a) we need different compiler flags for libc++ than for libstdcpp (b) libc++ inlines a lot more than libstdcpp and some code changes were required to accommodate this difference llvm-svn: 152570
This commit is contained in:
parent
701a6b473d
commit
c7f873064b
|
@ -0,0 +1,481 @@
|
|||
import lldb
|
||||
|
||||
# libcxx STL formatters for LLDB
|
||||
# These formatters are based upon the implementation of libc++ that
|
||||
# ships with current releases of OS X - They will not work for other implementations
|
||||
# of the standard C++ library - and they are bound to use the libc++-specific namespace
|
||||
|
||||
# this could probably be made more efficient but since it only reads a handful of bytes at a time
|
||||
# we probably don't need to worry too much about this for the time being
|
||||
def make_string(F,L):
|
||||
strval = ''
|
||||
G = F.GetData().uint8
|
||||
for X in range(L):
|
||||
V = G[X]
|
||||
if V == 0:
|
||||
break
|
||||
strval = strval + chr(V % 256)
|
||||
return '"' + strval + '"'
|
||||
|
||||
# if we ever care about big-endian, these two functions might need to change
|
||||
def is_short_string(value):
|
||||
return True if (value & 1) == 0 else False
|
||||
def extract_short_size(value):
|
||||
return ((value >> 1) % 256)
|
||||
|
||||
# some of the members of libc++ std::string are anonymous or have internal names that convey
|
||||
# no external significance - we access them by index since this saves a name lookup that would add
|
||||
# no information for readers of the code, but when possible try to use meaningful variable names
|
||||
def stdstring_SummaryProvider(valobj,dict):
|
||||
r = valobj.GetChildAtIndex(0)
|
||||
B = r.GetChildAtIndex(0)
|
||||
first = B.GetChildAtIndex(0)
|
||||
D = first.GetChildAtIndex(0)
|
||||
l = D.GetChildAtIndex(0)
|
||||
s = D.GetChildAtIndex(1)
|
||||
D20 = s.GetChildAtIndex(0)
|
||||
size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)
|
||||
if is_short_string(size_mode):
|
||||
size = extract_short_size(size_mode)
|
||||
return make_string(s.GetChildAtIndex(1),size)
|
||||
else:
|
||||
data_ptr = l.GetChildAtIndex(2)
|
||||
size_vo = l.GetChildAtIndex(1)
|
||||
size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
|
||||
if size <= 1: # should never be the case
|
||||
return '""'
|
||||
data = data_ptr.GetPointeeData(0,size)
|
||||
error = lldb.SBError()
|
||||
strval = data.GetString(error,0)
|
||||
if error.Fail():
|
||||
return '<error:' + error.GetCString() + '>'
|
||||
else:
|
||||
return '"' + strval + '"'
|
||||
|
||||
class stdvector_SynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
self.valobj = valobj;
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
try:
|
||||
start_val = self.start.GetValueAsUnsigned(0)
|
||||
finish_val = self.finish.GetValueAsUnsigned(0)
|
||||
# Before a vector has been constructed, it will contain bad values
|
||||
# so we really need to be careful about the length we return since
|
||||
# unitialized data can cause us to return a huge number. We need
|
||||
# to also check for any of the start, finish or end of storage values
|
||||
# being zero (NULL). If any are, then this vector has not been
|
||||
# initialized yet and we should return zero
|
||||
|
||||
# Make sure nothing is NULL
|
||||
if start_val == 0 or finish_val == 0:
|
||||
return 0
|
||||
# Make sure start is less than finish
|
||||
if start_val >= finish_val:
|
||||
return 0
|
||||
|
||||
num_children = (finish_val-start_val)
|
||||
if (num_children % self.data_size) != 0:
|
||||
return 0
|
||||
else:
|
||||
num_children = num_children/self.data_size
|
||||
return num_children
|
||||
except:
|
||||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
if index < 0:
|
||||
return None;
|
||||
if index >= self.num_children():
|
||||
return None;
|
||||
try:
|
||||
offset = index * self.data_size
|
||||
return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type)
|
||||
except:
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
try:
|
||||
self.start = self.valobj.GetChildMemberWithName('__begin_')
|
||||
self.finish = self.valobj.GetChildMemberWithName('__end_')
|
||||
# the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
|
||||
# if this ends up not being correct, we can use the APIs to get at template arguments
|
||||
data_type_finder = self.valobj.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_')
|
||||
self.data_type = data_type_finder.GetType().GetPointeeType()
|
||||
self.data_size = self.data_type.GetByteSize()
|
||||
except:
|
||||
pass
|
||||
|
||||
def stdvector_SummaryProvider(valobj,dict):
|
||||
prov = stdvector_SynthProvider(valobj,None)
|
||||
return 'size=' + str(prov.num_children())
|
||||
|
||||
class stdlist_entry:
|
||||
|
||||
def __init__(self,entry):
|
||||
self.entry = entry
|
||||
|
||||
def _next_impl(self):
|
||||
return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
|
||||
|
||||
def _prev_impl(self):
|
||||
return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
|
||||
|
||||
def _value_impl(self):
|
||||
return self.entry.GetValueAsUnsigned(0)
|
||||
|
||||
def _isnull_impl(self):
|
||||
return self._value_impl() == 0
|
||||
|
||||
def _sbvalue_impl(self):
|
||||
return self.entry
|
||||
|
||||
next = property(_next_impl,None)
|
||||
value = property(_value_impl,None)
|
||||
is_null = property(_isnull_impl,None)
|
||||
sbvalue = property(_sbvalue_impl,None)
|
||||
|
||||
class stdlist_iterator:
|
||||
|
||||
def increment_node(self,node):
|
||||
if node.is_null:
|
||||
return None
|
||||
return node.next
|
||||
|
||||
def __init__(self,node):
|
||||
self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
|
||||
|
||||
def value(self):
|
||||
return self.node.sbvalue # and return the SBValue back on exit
|
||||
|
||||
def next(self):
|
||||
node = self.increment_node(self.node)
|
||||
if node != None and node.sbvalue.IsValid() and not(node.is_null):
|
||||
self.node = node
|
||||
return self.value()
|
||||
else:
|
||||
return None
|
||||
|
||||
def advance(self,N):
|
||||
if N < 0:
|
||||
return None
|
||||
if N == 0:
|
||||
return self.value()
|
||||
if N == 1:
|
||||
return self.next()
|
||||
while N > 0:
|
||||
self.next()
|
||||
N = N - 1
|
||||
return self.value()
|
||||
|
||||
|
||||
class stdlist_SynthProvider:
|
||||
def __init__(self, valobj, dict):
|
||||
self.valobj = valobj
|
||||
self.update()
|
||||
|
||||
def next_node(self,node):
|
||||
return node.GetChildMemberWithName('__next_')
|
||||
|
||||
def value(self,node):
|
||||
return node.GetValueAsUnsigned()
|
||||
|
||||
# Floyd's cyle-finding algorithm
|
||||
# try to detect if this list has a loop
|
||||
def has_loop(self):
|
||||
slow = stdlist_entry(self.head)
|
||||
fast1 = stdlist_entry(self.head)
|
||||
fast2 = stdlist_entry(self.head)
|
||||
while slow.next.value != self.node_address:
|
||||
slow_value = slow.value
|
||||
fast1 = fast2.next
|
||||
fast2 = fast1.next
|
||||
if fast1.value == slow_value or fast2.value == slow_value:
|
||||
return True
|
||||
slow = slow.next
|
||||
return False
|
||||
|
||||
def num_children(self):
|
||||
if self.count == None:
|
||||
self.count = self.num_children_impl()
|
||||
return self.count
|
||||
|
||||
def num_children_impl(self):
|
||||
try:
|
||||
next_val = self.head.GetValueAsUnsigned(0)
|
||||
prev_val = self.tail.GetValueAsUnsigned(0)
|
||||
# After a std::list has been initialized, both next and prev will be non-NULL
|
||||
if next_val == 0 or prev_val == 0:
|
||||
return 0
|
||||
if next_val == self.node_address:
|
||||
return 0
|
||||
if next_val == prev_val:
|
||||
return 1
|
||||
if self.has_loop():
|
||||
return 0
|
||||
size = 2
|
||||
current = stdlist_entry(self.head)
|
||||
while current.next.value != self.node_address:
|
||||
size = size + 1
|
||||
current = current.next
|
||||
return (size - 1)
|
||||
except:
|
||||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
if index < 0:
|
||||
return None;
|
||||
if index >= self.num_children():
|
||||
return None;
|
||||
try:
|
||||
current = stdlist_iterator(self.head)
|
||||
current = current.advance(index)
|
||||
# we do not return __value_ because then all our children would be named __value_
|
||||
# we need to make a copy of __value__ with the right name - unfortunate
|
||||
obj = current.GetChildMemberWithName('__value_')
|
||||
obj_data = obj.GetData()
|
||||
return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
|
||||
except:
|
||||
return None
|
||||
|
||||
def extract_type(self):
|
||||
list_type = self.valobj.GetType().GetUnqualifiedType()
|
||||
if list_type.GetNumberOfTemplateArguments() > 0:
|
||||
data_type = list_type.GetTemplateArgumentType(0)
|
||||
else:
|
||||
data_type = None
|
||||
return data_type
|
||||
|
||||
def update(self):
|
||||
try:
|
||||
impl = self.valobj.GetChildMemberWithName('__end_')
|
||||
self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
|
||||
self.head = impl.GetChildMemberWithName('__next_')
|
||||
self.tail = impl.GetChildMemberWithName('__prev_')
|
||||
self.data_type = self.extract_type()
|
||||
self.data_size = self.data_type.GetByteSize()
|
||||
self.count = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def stdlist_SummaryProvider(valobj,dict):
|
||||
prov = stdlist_SynthProvider(valobj,None)
|
||||
return 'size=' + str(prov.num_children())
|
||||
|
||||
# a tree node - this class makes the syntax in the actual iterator nicer to read and maintain
|
||||
class stdmap_iterator_node:
|
||||
def _left_impl(self):
|
||||
return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
|
||||
|
||||
def _right_impl(self):
|
||||
return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
|
||||
|
||||
def _parent_impl(self):
|
||||
return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
|
||||
|
||||
def _value_impl(self):
|
||||
return self.node.GetValueAsUnsigned(0)
|
||||
|
||||
def _sbvalue_impl(self):
|
||||
return self.node
|
||||
|
||||
def _null_impl(self):
|
||||
return self.value == 0
|
||||
|
||||
def __init__(self,node):
|
||||
self.node = node
|
||||
|
||||
left = property(_left_impl,None)
|
||||
right = property(_right_impl,None)
|
||||
parent = property(_parent_impl,None)
|
||||
value = property(_value_impl,None)
|
||||
is_null = property(_null_impl,None)
|
||||
sbvalue = property(_sbvalue_impl,None)
|
||||
|
||||
# a Python implementation of the tree iterator used by libc++
|
||||
class stdmap_iterator:
|
||||
|
||||
def tree_min(self,x):
|
||||
if x.is_null:
|
||||
return None
|
||||
while (not x.left.is_null):
|
||||
x = x.left
|
||||
return x
|
||||
|
||||
def tree_max(self,x):
|
||||
if x.is_null:
|
||||
return None
|
||||
while (not x.right.is_null):
|
||||
x = x.right
|
||||
return x
|
||||
|
||||
def tree_is_left_child(self,x):
|
||||
if x.is_null:
|
||||
return None
|
||||
return True if x.value == x.parent.left.value else False
|
||||
|
||||
def increment_node(self,node):
|
||||
if node.is_null:
|
||||
return None
|
||||
if not node.right.is_null:
|
||||
return self.tree_min(node.right)
|
||||
while (not self.tree_is_left_child(node)):
|
||||
node = node.parent
|
||||
return node.parent
|
||||
|
||||
def __init__(self,node):
|
||||
self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry
|
||||
|
||||
def value(self):
|
||||
return self.node.sbvalue # and return the SBValue back on exit
|
||||
|
||||
def next(self):
|
||||
node = self.increment_node(self.node)
|
||||
if node != None and node.sbvalue.IsValid() and not(node.is_null):
|
||||
self.node = node
|
||||
return self.value()
|
||||
else:
|
||||
return None
|
||||
|
||||
def advance(self,N):
|
||||
if N < 0:
|
||||
return None
|
||||
if N == 0:
|
||||
return self.value()
|
||||
if N == 1:
|
||||
return self.next()
|
||||
while N > 0:
|
||||
self.next()
|
||||
N = N - 1
|
||||
return self.value()
|
||||
|
||||
class stdmap_SynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
self.valobj = valobj;
|
||||
self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
try:
|
||||
self.tree = self.valobj.GetChildMemberWithName('__tree_')
|
||||
self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
|
||||
# this data is either lazily-calculated, or cannot be inferred at this moment
|
||||
# we still need to mark it as None, meaning "please set me ASAP"
|
||||
self.data_type = None
|
||||
self.data_size = None
|
||||
self.skip_size = None
|
||||
self.count = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def num_children(self):
|
||||
if self.count == None:
|
||||
self.count = self.num_children_impl()
|
||||
return self.count
|
||||
|
||||
def num_children_impl(self):
|
||||
try:
|
||||
return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
|
||||
except:
|
||||
return 0;
|
||||
|
||||
def get_data_type(self):
|
||||
if self.data_type == None or self.data_size == None:
|
||||
if self.num_children() == 0:
|
||||
return False
|
||||
deref = self.root_node.Dereference()
|
||||
if not(deref.IsValid()):
|
||||
return False
|
||||
value = deref.GetChildMemberWithName('__value_')
|
||||
if not(value.IsValid()):
|
||||
return False
|
||||
self.data_type = value.GetType()
|
||||
self.data_size = self.data_type.GetByteSize()
|
||||
self.skip_size = None
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_value_offset(self,node):
|
||||
if self.skip_size == None:
|
||||
node_type = node.GetType()
|
||||
fields_count = node_type.GetNumberOfFields()
|
||||
for i in range(fields_count):
|
||||
field = node_type.GetFieldAtIndex(i)
|
||||
if field.GetName() == '__value_':
|
||||
self.skip_size = field.GetOffsetInBytes()
|
||||
break
|
||||
return (self.skip_size != None)
|
||||
|
||||
def get_child_index(self,name):
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
if index < 0:
|
||||
return None
|
||||
if index >= self.num_children():
|
||||
return None;
|
||||
try:
|
||||
iterator = stdmap_iterator(self.root_node)
|
||||
# the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
|
||||
# out of which we can grab the information we need - every other node has a less informative
|
||||
# type which omits all value information and only contains housekeeping information for the RB tree
|
||||
# hence, we need to know if we are at a node != 0, so that we can still get at the data
|
||||
need_to_skip = (index > 0)
|
||||
current = iterator.advance(index)
|
||||
if self.get_data_type():
|
||||
if not(need_to_skip):
|
||||
current = current.Dereference()
|
||||
obj = current.GetChildMemberWithName('__value_')
|
||||
obj_data = obj.GetData()
|
||||
self.get_value_offset(current) # make sure we have a valid offset for the next items
|
||||
# we do not return __value_ because then we would end up with a child named
|
||||
# __value_ instead of [0]
|
||||
return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
|
||||
else:
|
||||
# FIXME we need to have accessed item 0 before accessing any other item!
|
||||
if self.skip_size == None:
|
||||
return None
|
||||
return current.CreateChildAtOffset('[' + str(index) + ']',self.skip_size,self.data_type)
|
||||
else:
|
||||
print "foo"
|
||||
return None
|
||||
except Exception as err:
|
||||
print err
|
||||
return None
|
||||
|
||||
def stdmap_SummaryProvider(valobj,dict):
|
||||
prov = stdmap_SynthProvider(valobj,None)
|
||||
return 'size=' + str(prov.num_children())
|
||||
|
||||
|
||||
# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
|
||||
# talking with libc++ developer: "std::__1::class_name is set in stone until we decide to change the ABI. That shouldn't happen within a 5 year time frame"
|
||||
def __lldb_init_module(debugger,dict):
|
||||
debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx')
|
||||
debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::basic_string<char, class std::__1::char_traits<char>, class std::__1::allocator<char> >" -w libcxx')
|
||||
debugger.HandleCommand('type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx')
|
||||
debugger.HandleCommand('type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx')
|
||||
debugger.HandleCommand('type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx')
|
||||
debugger.HandleCommand('type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx')
|
||||
debugger.HandleCommand('type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx')
|
||||
debugger.HandleCommand('type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
|
||||
debugger.HandleCommand("type category enable libcxx")
|
|
@ -670,6 +670,7 @@ private:
|
|||
ConstString m_default_category_name;
|
||||
ConstString m_system_category_name;
|
||||
ConstString m_gnu_cpp_category_name;
|
||||
ConstString m_libcxx_category_name;
|
||||
ConstString m_objc_category_name;
|
||||
ConstString m_corefoundation_category_name;
|
||||
ConstString m_coregraphics_category_name;
|
||||
|
@ -690,6 +691,9 @@ private:
|
|||
void
|
||||
LoadSTLFormatters();
|
||||
|
||||
void
|
||||
LoadLibcxxFormatters();
|
||||
|
||||
void
|
||||
LoadSystemFormatters();
|
||||
|
||||
|
|
|
@ -184,6 +184,20 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ -f "${SRC_ROOT}/examples/synthetic/libcxx.py" ]
|
||||
then
|
||||
if [ $Debug == 1 ]
|
||||
then
|
||||
echo "Copying libcxx.py to ${framework_python_dir}"
|
||||
fi
|
||||
cp "${SRC_ROOT}/examples/synthetic/libcxx.py" "${framework_python_dir}"
|
||||
else
|
||||
if [ $Debug == 1 ]
|
||||
then
|
||||
echo "Unable to find ${SRC_ROOT}/examples/synthetic/libcxx.py"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Copy the ObjC formatters over to the framework Python directory
|
||||
if [ -f "${SRC_ROOT}/examples/summaries/objc.py" ]
|
||||
then
|
||||
|
|
|
@ -580,6 +580,7 @@ FormatManager::FormatManager() :
|
|||
m_default_category_name(ConstString("default")),
|
||||
m_system_category_name(ConstString("system")),
|
||||
m_gnu_cpp_category_name(ConstString("gnu-libstdc++")),
|
||||
m_libcxx_category_name(ConstString("libcxx")),
|
||||
m_objc_category_name(ConstString("objc")),
|
||||
m_corefoundation_category_name(ConstString("CoreFoundation")),
|
||||
m_coregraphics_category_name(ConstString("CoreGraphics")),
|
||||
|
@ -590,6 +591,7 @@ FormatManager::FormatManager() :
|
|||
|
||||
LoadSystemFormatters();
|
||||
LoadSTLFormatters();
|
||||
LoadLibcxxFormatters();
|
||||
#ifndef LLDB_DISABLE_PYTHON
|
||||
LoadObjCFormatters();
|
||||
#endif
|
||||
|
@ -600,6 +602,7 @@ FormatManager::FormatManager() :
|
|||
//EnableCategory(m_coreservices_category_name,CategoryMap::Last);
|
||||
//EnableCategory(m_coregraphics_category_name,CategoryMap::Last);
|
||||
EnableCategory(m_gnu_cpp_category_name,CategoryMap::Last);
|
||||
EnableCategory(m_libcxx_category_name,CategoryMap::Last);
|
||||
//EnableCategory(m_vectortypes_category_name,CategoryMap::Last);
|
||||
EnableCategory(m_system_category_name,CategoryMap::Last);
|
||||
}
|
||||
|
@ -653,6 +656,57 @@ FormatManager::LoadSTLFormatters()
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
FormatManager::LoadLibcxxFormatters()
|
||||
{
|
||||
TypeSummaryImpl::Flags stl_summary_flags;
|
||||
stl_summary_flags.SetCascades(true)
|
||||
.SetSkipPointers(false)
|
||||
.SetSkipReferences(false)
|
||||
.SetDontShowChildren(true)
|
||||
.SetDontShowValue(true)
|
||||
.SetShowMembersOneLiner(false)
|
||||
.SetHideItemNames(false);
|
||||
|
||||
std::string code(" libcxx.stdstring_SummaryProvider(valobj,dict)");
|
||||
lldb::TypeSummaryImplSP std_string_summary_sp(new ScriptSummaryFormat(stl_summary_flags, "libcxx.stdstring_SummaryProvider",code.c_str()));
|
||||
|
||||
TypeCategoryImpl::SharedPointer libcxx_category_sp = GetCategory(m_libcxx_category_name);
|
||||
|
||||
libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::string"),
|
||||
std_string_summary_sp);
|
||||
libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string<char, class std::__1::char_traits<char>, class std::__1::allocator<char> >"),
|
||||
std_string_summary_sp);
|
||||
|
||||
|
||||
#ifndef LLDB_DISABLE_PYTHON
|
||||
|
||||
SyntheticChildren::Flags stl_synth_flags;
|
||||
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false);
|
||||
|
||||
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)vector<.+>$")),
|
||||
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||
"libcxx.stdvector_SynthProvider")));
|
||||
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)list<.+>$")),
|
||||
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||
"libcxx.stdlist_SynthProvider")));
|
||||
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)map<.+> >$")),
|
||||
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||
"libcxx.stdmap_SynthProvider")));
|
||||
|
||||
stl_summary_flags.SetDontShowChildren(false);
|
||||
code.assign(" libcxx.stdvector_SummaryProvider(valobj,dict)");
|
||||
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)vector<.+>$")),
|
||||
TypeSummaryImplSP(new ScriptSummaryFormat(stl_summary_flags, "libcxx.stdvector_SummaryProvider",code.c_str())));
|
||||
code.assign(" libcxx.stdlist_SummaryProvider(valobj,dict)");
|
||||
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)list<.+>$")),
|
||||
TypeSummaryImplSP(new ScriptSummaryFormat(stl_summary_flags, "libcxx.stdlist_SummaryProvider",code.c_str())));
|
||||
code.assign(" libcxx.stdmap_SummaryProvider(valobj,dict)");
|
||||
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)map<.+> >$")),
|
||||
TypeSummaryImplSP(new ScriptSummaryFormat(stl_summary_flags, "libcxx.stdmap_SummaryProvider",code.c_str())));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
FormatManager::LoadSystemFormatters()
|
||||
{
|
||||
|
|
|
@ -279,7 +279,7 @@ ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interprete
|
|||
int old_count = Debugger::TestDebuggerRefCount();
|
||||
|
||||
|
||||
run_string.Printf ("run_one_line (%s, 'import copy, os, re, sys, uuid, lldb, gnu_libstdcpp, objc')", m_dictionary_name.c_str());
|
||||
run_string.Printf ("run_one_line (%s, 'import copy, os, re, sys, uuid, lldb, gnu_libstdcpp, libcxx, objc')", m_dictionary_name.c_str());
|
||||
PyRun_SimpleString (run_string.GetData());
|
||||
|
||||
// WARNING: temporary code that loads Cocoa formatters - this should be done on a per-platform basis rather than loading the whole set
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
LEVEL = ../../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
||||
|
||||
CXXFLAGS += -stdlib=libc++ -O0
|
||||
LDFLAGS += -stdlib=libc++
|
|
@ -0,0 +1,175 @@
|
|||
"""
|
||||
Test lldb data formatter subsystem.
|
||||
"""
|
||||
|
||||
import os, time
|
||||
import unittest2
|
||||
import lldb
|
||||
from lldbtest import *
|
||||
|
||||
class LibcxxListDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "list")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDsym()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def test_with_dwarf_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDwarf()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
# Find the line number to break at.
|
||||
self.line = line_number('main.cpp', '// Set break point at this line.')
|
||||
self.line2 = line_number('main.cpp', '// Set second break point at this line.')
|
||||
|
||||
def data_formatter_commands(self):
|
||||
"""Test that that file and class static variables display correctly."""
|
||||
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
|
||||
|
||||
self.expect("breakpoint set -f main.cpp -l %d" % self.line,
|
||||
BREAKPOINT_CREATED,
|
||||
startstr = "Breakpoint created: 1: file ='main.cpp', line = %d" %
|
||||
self.line)
|
||||
self.expect("breakpoint set -f main.cpp -l %d" % self.line2,
|
||||
BREAKPOINT_CREATED,
|
||||
startstr = "Breakpoint created: 2: file ='main.cpp', line = %d" %
|
||||
self.line2)
|
||||
|
||||
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 filter clear', check=False)
|
||||
self.runCmd('type synth clear', check=False)
|
||||
self.runCmd("settings set target.max-children-count 256", check=False)
|
||||
|
||||
# Execute the cleanup function during test case tear down.
|
||||
self.addTearDownHook(cleanup)
|
||||
|
||||
self.runCmd("frame variable numbers_list -T")
|
||||
self.runCmd("type summary add std::int_list std::string_list int_list string_list --summary-string \"list has ${svar%#} items\" -e")
|
||||
self.runCmd("type format add -f hex int")
|
||||
|
||||
self.expect("frame variable numbers_list --raw", matching=False,
|
||||
substrs = ['list has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.expect("frame variable numbers_list",
|
||||
substrs = ['list has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.expect("p numbers_list",
|
||||
substrs = ['list has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect("frame variable numbers_list",
|
||||
substrs = ['list has 1 items',
|
||||
'[0] = ',
|
||||
'0x12345678'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable numbers_list",
|
||||
substrs = ['list has 4 items',
|
||||
'[0] = ',
|
||||
'0x12345678',
|
||||
'[1] =',
|
||||
'0x11223344',
|
||||
'[2] =',
|
||||
'0xbeeffeed',
|
||||
'[3] =',
|
||||
'0x00abba00'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable numbers_list",
|
||||
substrs = ['list has 6 items',
|
||||
'[0] = ',
|
||||
'0x12345678',
|
||||
'0x11223344',
|
||||
'0xbeeffeed',
|
||||
'0x00abba00',
|
||||
'[4] =',
|
||||
'0x0abcdef0',
|
||||
'[5] =',
|
||||
'0x0cab0cab'])
|
||||
|
||||
self.expect("p numbers_list",
|
||||
substrs = ['list has 6 items',
|
||||
'[0] = ',
|
||||
'0x12345678',
|
||||
'0x11223344',
|
||||
'0xbeeffeed',
|
||||
'0x00abba00',
|
||||
'[4] =',
|
||||
'0x0abcdef0',
|
||||
'[5] =',
|
||||
'0x0cab0cab'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable numbers_list[0]",
|
||||
substrs = ['0x12345678']);
|
||||
self.expect("frame variable numbers_list[1]",
|
||||
substrs = ['0x11223344']);
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect("frame variable numbers_list",
|
||||
substrs = ['list has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable numbers_list",
|
||||
substrs = ['list has 4 items',
|
||||
'[0] = ', '1',
|
||||
'[1] = ', '2',
|
||||
'[2] = ', '3',
|
||||
'[3] = ', '4'])
|
||||
|
||||
self.runCmd("type format delete int")
|
||||
|
||||
self.runCmd("c")
|
||||
|
||||
self.expect("frame variable text_list",
|
||||
substrs = ['list has 3 items',
|
||||
'[0]', 'goofy',
|
||||
'[1]', 'is',
|
||||
'[2]', 'smart'])
|
||||
|
||||
self.expect("p text_list",
|
||||
substrs = ['list has 3 items',
|
||||
'\"goofy\"',
|
||||
'\"is\"',
|
||||
'\"smart\"'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable text_list[0]",
|
||||
substrs = ['goofy']);
|
||||
self.expect("frame variable text_list[3]",
|
||||
substrs = ['!!!']);
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,35 @@
|
|||
#include <string>
|
||||
#define _LIBCPP_INLINE_VISIBILITY
|
||||
#include <list>
|
||||
|
||||
|
||||
typedef std::list<int> int_list;
|
||||
typedef std::list<std::string> string_list;
|
||||
|
||||
int main()
|
||||
{
|
||||
int_list numbers_list;
|
||||
|
||||
(numbers_list.push_back(0x12345678)); // Set break point at this line.
|
||||
(numbers_list.push_back(0x11223344));
|
||||
(numbers_list.push_back(0xBEEFFEED));
|
||||
(numbers_list.push_back(0x00ABBA00));
|
||||
(numbers_list.push_back(0x0ABCDEF0));
|
||||
(numbers_list.push_back(0x0CAB0CAB));
|
||||
|
||||
numbers_list.clear();
|
||||
|
||||
(numbers_list.push_back(1));
|
||||
(numbers_list.push_back(2));
|
||||
(numbers_list.push_back(3));
|
||||
(numbers_list.push_back(4));
|
||||
|
||||
string_list text_list;
|
||||
(text_list.push_back(std::string("goofy")));
|
||||
(text_list.push_back(std::string("is")));
|
||||
(text_list.push_back(std::string("smart")));
|
||||
|
||||
(text_list.push_back(std::string("!!!"))); // Set second break point at this line.
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
LEVEL = ../../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
||||
|
||||
CXXFLAGS += -stdlib=libc++ -O0
|
||||
LDFLAGS += -stdlib=libc++
|
|
@ -0,0 +1,332 @@
|
|||
"""
|
||||
Test lldb data formatter subsystem.
|
||||
"""
|
||||
|
||||
import os, time
|
||||
import unittest2
|
||||
import lldb
|
||||
from lldbtest import *
|
||||
|
||||
class LibcxxMapDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libstdcpp", "map")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDsym()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def test_with_dwarf_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDwarf()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
# Find the line number to break at.
|
||||
self.line = line_number('main.cpp', '// Set break point at this line.')
|
||||
|
||||
def data_formatter_commands(self):
|
||||
"""Test that that file and class static variables display correctly."""
|
||||
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
|
||||
|
||||
self.expect("breakpoint set -f main.cpp -l %d" % self.line,
|
||||
BREAKPOINT_CREATED,
|
||||
startstr = "Breakpoint created: 1: file ='main.cpp', line = %d" %
|
||||
self.line)
|
||||
|
||||
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 filter clear', check=False)
|
||||
self.runCmd('type synth clear', check=False)
|
||||
self.runCmd("settings set target.max-children-count 256", check=False)
|
||||
|
||||
# Execute the cleanup function during test case tear down.
|
||||
self.addTearDownHook(cleanup)
|
||||
|
||||
self.runCmd("frame variable ii -T")
|
||||
|
||||
self.runCmd("type summary add -x \"std::map<\" --summary-string \"map has ${svar%#} items\" -e")
|
||||
|
||||
self.expect('frame variable ii',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect('frame variable ii',
|
||||
substrs = ['map has 2 items',
|
||||
'[0] = {',
|
||||
'first = 0',
|
||||
'second = 0',
|
||||
'[1] = {',
|
||||
'first = 1',
|
||||
'second = 1'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect('frame variable ii',
|
||||
substrs = ['map has 4 items',
|
||||
'[2] = {',
|
||||
'first = 2',
|
||||
'second = 0',
|
||||
'[3] = {',
|
||||
'first = 3',
|
||||
'second = 1'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable ii",
|
||||
substrs = ['map has 9 items',
|
||||
'[5] = {',
|
||||
'first = 5',
|
||||
'second = 0',
|
||||
'[7] = {',
|
||||
'first = 7',
|
||||
'second = 1'])
|
||||
|
||||
self.expect("p ii",
|
||||
substrs = ['map has 9 items',
|
||||
'[5] = {',
|
||||
'first = 5',
|
||||
'second = 0',
|
||||
'[7] = {',
|
||||
'first = 7',
|
||||
'second = 1'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable ii[0]",
|
||||
substrs = ['first = 0',
|
||||
'second = 0']);
|
||||
self.expect("frame variable ii[3]",
|
||||
substrs = ['first =',
|
||||
'second =']);
|
||||
|
||||
self.expect("frame variable ii[8]", matching=True,
|
||||
substrs = ['1234567'])
|
||||
|
||||
# check that the expression parser does not make use of
|
||||
# synthetic children instead of running code
|
||||
# TOT clang has a fix for this, which makes the expression command here succeed
|
||||
# since this would make the test fail or succeed depending on clang version in use
|
||||
# this is safer commented for the time being
|
||||
#self.expect("expression ii[8]", matching=False, error=True,
|
||||
# substrs = ['1234567'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect('frame variable ii',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n")
|
||||
self.runCmd("frame variable si -T")
|
||||
|
||||
#self.runCmd("type summary add std::strint_map strint_map --summary-string \"map has ${svar%#} items\" -e")
|
||||
#self.runCmd("type synth add std::strint_map strint_map -l StdMapSynthProvider")
|
||||
|
||||
self.expect('frame variable si',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect('frame variable si',
|
||||
substrs = ['map has 1 items',
|
||||
'[0] = ',
|
||||
'first = \"zero\"',
|
||||
'second = 0'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable si",
|
||||
substrs = ['map has 5 items',
|
||||
'[0] = ',
|
||||
'first = \"zero\"',
|
||||
'second = 0',
|
||||
'[1] = ',
|
||||
'first = \"one\"',
|
||||
'second = 1',
|
||||
'[2] = ',
|
||||
'first = \"two\"',
|
||||
'second = 2',
|
||||
'[3] = ',
|
||||
'first = \"three\"',
|
||||
'second = 3',
|
||||
'[4] = ',
|
||||
'first = \"four\"',
|
||||
'second = 4'])
|
||||
|
||||
self.expect("p si",
|
||||
substrs = ['map has 5 items',
|
||||
'[0] = ',
|
||||
'first = \"zero\"',
|
||||
'second = 0',
|
||||
'[1] = ',
|
||||
'first = \"one\"',
|
||||
'second = 1',
|
||||
'[2] = ',
|
||||
'first = \"two\"',
|
||||
'second = 2',
|
||||
'[3] = ',
|
||||
'first = \"three\"',
|
||||
'second = 3',
|
||||
'[4] = ',
|
||||
'first = \"four\"',
|
||||
'second = 4'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable si[0]",
|
||||
substrs = ['first = ', 'four',
|
||||
'second = 4']);
|
||||
|
||||
# check that the expression parser does not make use of
|
||||
# synthetic children instead of running code
|
||||
# TOT clang has a fix for this, which makes the expression command here succeed
|
||||
# since this would make the test fail or succeed depending on clang version in use
|
||||
# this is safer commented for the time being
|
||||
#self.expect("expression si[0]", matching=False, error=True,
|
||||
# substrs = ['first = ', 'zero'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect('frame variable si',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n")
|
||||
self.runCmd("frame variable is -T")
|
||||
|
||||
#self.runCmd("type summary add std::intstr_map intstr_map --summary-string \"map has ${svar%#} items\" -e")
|
||||
#self.runCmd("type synth add std::intstr_map intstr_map -l StdMapSynthProvider")
|
||||
|
||||
self.expect('frame variable is',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable is",
|
||||
substrs = ['map has 4 items',
|
||||
'[0] = ',
|
||||
'second = \"goofy\"',
|
||||
'first = 85',
|
||||
'[1] = ',
|
||||
'second = \"is\"',
|
||||
'first = 1',
|
||||
'[2] = ',
|
||||
'second = \"smart\"',
|
||||
'first = 2',
|
||||
'[3] = ',
|
||||
'second = \"!!!\"',
|
||||
'first = 3'])
|
||||
|
||||
self.expect("p is",
|
||||
substrs = ['map has 4 items',
|
||||
'[0] = ',
|
||||
'second = \"goofy\"',
|
||||
'first = 85',
|
||||
'[1] = ',
|
||||
'second = \"is\"',
|
||||
'first = 1',
|
||||
'[2] = ',
|
||||
'second = \"smart\"',
|
||||
'first = 2',
|
||||
'[3] = ',
|
||||
'second = \"!!!\"',
|
||||
'first = 3'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable is[0]",
|
||||
substrs = ['first = ',
|
||||
'second =']);
|
||||
|
||||
# check that the expression parser does not make use of
|
||||
# synthetic children instead of running code
|
||||
# TOT clang has a fix for this, which makes the expression command here succeed
|
||||
# since this would make the test fail or succeed depending on clang version in use
|
||||
# this is safer commented for the time being
|
||||
#self.expect("expression is[0]", matching=False, error=True,
|
||||
# substrs = ['first = ', 'goofy'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect('frame variable is',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n")
|
||||
self.runCmd("frame variable ss -T")
|
||||
|
||||
self.expect('frame variable ss',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable ss",
|
||||
substrs = ['map has 4 items',
|
||||
'[0] = ',
|
||||
'second = \"hello\"',
|
||||
'first = \"ciao\"',
|
||||
'[1] = ',
|
||||
'second = \"house\"',
|
||||
'first = \"casa\"',
|
||||
'[2] = ',
|
||||
'second = \"cat\"',
|
||||
'first = \"gatto\"',
|
||||
'[3] = ',
|
||||
'second = \"..is always a Mac!\"',
|
||||
'first = \"a Mac..\"'])
|
||||
|
||||
self.expect("p ss",
|
||||
substrs = ['map has 4 items',
|
||||
'[0] = ',
|
||||
'second = \"hello\"',
|
||||
'first = \"ciao\"',
|
||||
'[1] = ',
|
||||
'second = \"house\"',
|
||||
'first = \"casa\"',
|
||||
'[2] = ',
|
||||
'second = \"cat\"',
|
||||
'first = \"gatto\"',
|
||||
'[3] = ',
|
||||
'second = \"..is always a Mac!\"',
|
||||
'first = \"a Mac..\"'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable ss[3]",
|
||||
substrs = ['gatto', 'cat']);
|
||||
|
||||
# check that the expression parser does not make use of
|
||||
# synthetic children instead of running code
|
||||
# TOT clang has a fix for this, which makes the expression command here succeed
|
||||
# since this would make the test fail or succeed depending on clang version in use
|
||||
# this is safer commented for the time being
|
||||
#self.expect("expression ss[3]", matching=False, error=True,
|
||||
# substrs = ['gatto'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect('frame variable ss',
|
||||
substrs = ['map has 0 items',
|
||||
'{}'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,8 @@
|
|||
LEVEL = ../../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
||||
|
||||
CXXFLAGS += -stdlib=libc++ -O0
|
||||
LDFLAGS += -stdlib=libc++
|
|
@ -0,0 +1,204 @@
|
|||
"""
|
||||
Test lldb data formatter subsystem.
|
||||
"""
|
||||
|
||||
import os, time
|
||||
import unittest2
|
||||
import lldb
|
||||
from lldbtest import *
|
||||
|
||||
class LibcxxVectorDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "vector")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDsym()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def test_with_dwarf_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDwarf()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
# Find the line number to break at.
|
||||
self.line = line_number('main.cpp', '// Set break point at this line.')
|
||||
self.line2 = line_number('main.cpp', '// Set second break point at this line.')
|
||||
|
||||
def data_formatter_commands(self):
|
||||
"""Test that that file and class static variables display correctly."""
|
||||
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
|
||||
|
||||
self.expect("breakpoint set -f main.cpp -l %d" % self.line,
|
||||
BREAKPOINT_CREATED,
|
||||
startstr = "Breakpoint created: 1: file ='main.cpp', line = %d" %
|
||||
self.line)
|
||||
self.expect("breakpoint set -f main.cpp -l %d" % self.line2,
|
||||
BREAKPOINT_CREATED,
|
||||
startstr = "Breakpoint created: 2: file ='main.cpp', line = %d" %
|
||||
self.line2)
|
||||
|
||||
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 filter clear', check=False)
|
||||
self.runCmd('type synth clear', check=False)
|
||||
self.runCmd("settings set target.max-children-count 256", check=False)
|
||||
|
||||
# Execute the cleanup function during test case tear down.
|
||||
self.addTearDownHook(cleanup)
|
||||
|
||||
# empty vectors (and storage pointers SHOULD BOTH BE NULL..)
|
||||
self.expect("frame variable numbers",
|
||||
substrs = ['numbers = size=0'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
# first value added
|
||||
self.expect("frame variable numbers",
|
||||
substrs = ['numbers = size=1',
|
||||
'[0] = 1',
|
||||
'}'])
|
||||
|
||||
# add some more data
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable numbers",
|
||||
substrs = ['numbers = size=4',
|
||||
'[0] = 1',
|
||||
'[1] = 12',
|
||||
'[2] = 123',
|
||||
'[3] = 1234',
|
||||
'}'])
|
||||
|
||||
self.expect("p numbers",
|
||||
substrs = ['$', 'size=4',
|
||||
'[0] = 1',
|
||||
'[1] = 12',
|
||||
'[2] = 123',
|
||||
'[3] = 1234',
|
||||
'}'])
|
||||
|
||||
|
||||
# check access to synthetic children
|
||||
self.runCmd("type summary add --summary-string \"item 0 is ${var[0]}\" std::int_vect int_vect")
|
||||
self.expect('frame variable numbers',
|
||||
substrs = ['item 0 is 1']);
|
||||
|
||||
self.runCmd("type summary add --summary-string \"item 0 is ${svar[0]}\" std::int_vect int_vect")
|
||||
self.expect('frame variable numbers',
|
||||
substrs = ['item 0 is 1']);
|
||||
# move on with synths
|
||||
self.runCmd("type summary delete std::int_vect")
|
||||
self.runCmd("type summary delete int_vect")
|
||||
|
||||
# add some more data
|
||||
self.runCmd("n");self.runCmd("n");self.runCmd("n");
|
||||
|
||||
self.expect("frame variable numbers",
|
||||
substrs = ['numbers = size=7',
|
||||
'[0] = 1',
|
||||
'[1] = 12',
|
||||
'[2] = 123',
|
||||
'[3] = 1234',
|
||||
'[4] = 12345',
|
||||
'[5] = 123456',
|
||||
'[6] = 1234567',
|
||||
'}'])
|
||||
|
||||
self.expect("p numbers",
|
||||
substrs = ['$', 'size=7',
|
||||
'[0] = 1',
|
||||
'[1] = 12',
|
||||
'[2] = 123',
|
||||
'[3] = 1234',
|
||||
'[4] = 12345',
|
||||
'[5] = 123456',
|
||||
'[6] = 1234567',
|
||||
'}'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable numbers[0]",
|
||||
substrs = ['1']);
|
||||
self.expect("frame variable numbers[1]",
|
||||
substrs = ['12']);
|
||||
self.expect("frame variable numbers[2]",
|
||||
substrs = ['123']);
|
||||
self.expect("frame variable numbers[3]",
|
||||
substrs = ['1234']);
|
||||
|
||||
# clear out the vector and see that we do the right thing once again
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect("frame variable numbers",
|
||||
substrs = ['numbers = size=0'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
# first value added
|
||||
self.expect("frame variable numbers",
|
||||
substrs = ['numbers = size=1',
|
||||
'[0] = 7',
|
||||
'}'])
|
||||
|
||||
# check if we can display strings
|
||||
self.runCmd("c")
|
||||
|
||||
self.expect("frame variable strings",
|
||||
substrs = ['goofy',
|
||||
'is',
|
||||
'smart'])
|
||||
|
||||
self.expect("p strings",
|
||||
substrs = ['goofy',
|
||||
'is',
|
||||
'smart'])
|
||||
|
||||
# test summaries based on synthetic children
|
||||
self.runCmd("type summary add std::string_vect string_vect --summary-string \"vector has ${svar%#} items\" -e")
|
||||
self.expect("frame variable strings",
|
||||
substrs = ['vector has 3 items',
|
||||
'goofy',
|
||||
'is',
|
||||
'smart'])
|
||||
|
||||
self.expect("p strings",
|
||||
substrs = ['vector has 3 items',
|
||||
'goofy',
|
||||
'is',
|
||||
'smart'])
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect("frame variable strings",
|
||||
substrs = ['vector has 4 items'])
|
||||
|
||||
# check access-by-index
|
||||
self.expect("frame variable strings[0]",
|
||||
substrs = ['goofy']);
|
||||
self.expect("frame variable strings[1]",
|
||||
substrs = ['is']);
|
||||
|
||||
self.runCmd("n")
|
||||
|
||||
self.expect("frame variable strings",
|
||||
substrs = ['vector has 0 items'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,32 @@
|
|||
#include <string>
|
||||
#define _LIBCPP_INLINE_VISIBILITY
|
||||
#include <vector>
|
||||
typedef std::vector<int> int_vect;
|
||||
typedef std::vector<std::string> string_vect;
|
||||
|
||||
int main()
|
||||
{
|
||||
int_vect numbers;
|
||||
(numbers.push_back(1)); // Set break point at this line.
|
||||
(numbers.push_back(12));
|
||||
(numbers.push_back(123));
|
||||
(numbers.push_back(1234));
|
||||
(numbers.push_back(12345));
|
||||
(numbers.push_back(123456));
|
||||
(numbers.push_back(1234567));
|
||||
|
||||
numbers.clear();
|
||||
|
||||
(numbers.push_back(7));
|
||||
|
||||
string_vect strings;
|
||||
(strings.push_back(std::string("goofy")));
|
||||
(strings.push_back(std::string("is")));
|
||||
(strings.push_back(std::string("smart")));
|
||||
|
||||
(strings.push_back(std::string("!!!"))); // Set second break point at this line.
|
||||
|
||||
strings.clear();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
LEVEL = ../../../../make
|
||||
LEVEL = ../../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
|
@ -9,7 +9,7 @@ from lldbtest import *
|
|||
|
||||
class StdListDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "list")
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libstdcpp", "list")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
|
@ -1,4 +1,4 @@
|
|||
LEVEL = ../../../../make
|
||||
LEVEL = ../../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
|
@ -9,7 +9,7 @@ from lldbtest import *
|
|||
|
||||
class StdMapDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "map")
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libstdcpp", "map")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
|
@ -0,0 +1,55 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#define intint_map std::map<int, int>
|
||||
#define strint_map std::map<std::string, int>
|
||||
#define intstr_map std::map<int, std::string>
|
||||
#define strstr_map std::map<std::string, std::string>
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
intint_map ii;
|
||||
|
||||
ii[0] = 0; // Set break point at this line.
|
||||
ii[1] = 1;
|
||||
ii[2] = 0;
|
||||
ii[3] = 1;
|
||||
ii[4] = 0;
|
||||
ii[5] = 1;
|
||||
ii[6] = 0;
|
||||
ii[7] = 1;
|
||||
ii[85] = 1234567;
|
||||
|
||||
ii.clear();
|
||||
|
||||
strint_map si;
|
||||
|
||||
si["zero"] = 0;
|
||||
si["one"] = 1;
|
||||
si["two"] = 2;
|
||||
si["three"] = 3;
|
||||
si["four"] = 4;
|
||||
|
||||
si.clear();
|
||||
|
||||
intstr_map is;
|
||||
|
||||
is[85] = "goofy";
|
||||
is[1] = "is";
|
||||
is[2] = "smart";
|
||||
is[3] = "!!!";
|
||||
|
||||
is.clear();
|
||||
|
||||
strstr_map ss;
|
||||
|
||||
ss["ciao"] = "hello";
|
||||
ss["casa"] = "house";
|
||||
ss["gatto"] = "cat";
|
||||
ss["a Mac.."] = "..is always a Mac!";
|
||||
|
||||
ss.clear();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
LEVEL = ../../../../make
|
||||
LEVEL = ../../../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
|
@ -9,7 +9,7 @@ from lldbtest import *
|
|||
|
||||
class StdVectorDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "vector")
|
||||
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libstdcpp", "vector")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
Loading…
Reference in New Issue