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:
Enrico Granata 2012-03-12 19:47:17 +00:00
parent 701a6b473d
commit c7f873064b
23 changed files with 1417 additions and 7 deletions

View File

@ -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")

View File

@ -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();

View File

@ -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

View File

@ -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()
{

View File

@ -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

View File

@ -0,0 +1,8 @@
LEVEL = ../../../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules
CXXFLAGS += -stdlib=libc++ -O0
LDFLAGS += -stdlib=libc++

View File

@ -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()

View File

@ -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;
}

View File

@ -0,0 +1,8 @@
LEVEL = ../../../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules
CXXFLAGS += -stdlib=libc++ -O0
LDFLAGS += -stdlib=libc++

View File

@ -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()

View File

@ -0,0 +1,8 @@
LEVEL = ../../../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules
CXXFLAGS += -stdlib=libc++ -O0
LDFLAGS += -stdlib=libc++

View File

@ -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()

View File

@ -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;
}

View File

@ -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):

View File

@ -1,4 +1,4 @@
LEVEL = ../../../../make
LEVEL = ../../../../../make
CXX_SOURCES := main.cpp

View File

@ -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):

View File

@ -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;
}

View File

@ -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):