Data formatters for libc++ deque and shared/weak ptrs - a contribution by Jared Grubb
llvm-svn: 162680
This commit is contained in:
parent
e94695cfb9
commit
114bb19d19
|
@ -3,7 +3,7 @@ import lldb.formatters.Logger
|
||||||
|
|
||||||
# C++ STL formatters for LLDB
|
# C++ STL formatters for LLDB
|
||||||
# These formatters are based upon the version of the GNU libstdc++
|
# These formatters are based upon the version of the GNU libstdc++
|
||||||
# as it ships with Mac OS X 10.6.8 thru 10.7.3
|
# as it ships with Mac OS X 10.6.8 thru 10.8.0
|
||||||
# You are encouraged to look at the STL implementation for your platform
|
# You are encouraged to look at the STL implementation for your platform
|
||||||
# before relying on these formatters to do the right thing for your setup
|
# before relying on these formatters to do the right thing for your setup
|
||||||
|
|
||||||
|
|
|
@ -44,9 +44,12 @@ def stdstring_SummaryProvider(valobj,dict):
|
||||||
data_ptr = l.GetChildAtIndex(2)
|
data_ptr = l.GetChildAtIndex(2)
|
||||||
size_vo = l.GetChildAtIndex(1)
|
size_vo = l.GetChildAtIndex(1)
|
||||||
size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
|
size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
|
||||||
if size <= 1: # should never be the case
|
if size <= 1 or size == None: # should never be the case
|
||||||
return '""'
|
return '""'
|
||||||
|
try:
|
||||||
data = data_ptr.GetPointeeData(0,size)
|
data = data_ptr.GetPointeeData(0,size)
|
||||||
|
except:
|
||||||
|
return '""'
|
||||||
error = lldb.SBError()
|
error = lldb.SBError()
|
||||||
strval = data.GetString(error,0)
|
strval = data.GetString(error,0)
|
||||||
if error.Fail():
|
if error.Fail():
|
||||||
|
@ -558,6 +561,183 @@ def stdmap_SummaryProvider(valobj,dict):
|
||||||
prov = stdmap_SynthProvider(valobj,None)
|
prov = stdmap_SynthProvider(valobj,None)
|
||||||
return 'size=' + str(prov.num_children())
|
return 'size=' + str(prov.num_children())
|
||||||
|
|
||||||
|
class stddeque_SynthProvider:
|
||||||
|
def __init__(self, valobj, d):
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
logger.write("init")
|
||||||
|
self.valobj = valobj
|
||||||
|
self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
|
||||||
|
self.count = None
|
||||||
|
try:
|
||||||
|
self.find_block_size()
|
||||||
|
except:
|
||||||
|
self.block_size = -1
|
||||||
|
self.element_size = -1
|
||||||
|
logger.write("block_size=%d, element_size=%d" % (self.block_size, self.element_size))
|
||||||
|
|
||||||
|
def find_block_size(self):
|
||||||
|
# in order to use the deque we must have the block size, or else
|
||||||
|
# it's impossible to know what memory addresses are valid
|
||||||
|
self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
|
||||||
|
self.element_size = self.element_type.GetByteSize()
|
||||||
|
# The code says this, but there must be a better way:
|
||||||
|
# template <class _Tp, class _Allocator>
|
||||||
|
# class __deque_base {
|
||||||
|
# static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16;
|
||||||
|
# }
|
||||||
|
if self.element_size < 256:
|
||||||
|
self.block_size = 4096 / self.element_size
|
||||||
|
else:
|
||||||
|
self.block_size = 16
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
global _deque_capping_size
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
if self.count is None:
|
||||||
|
return 0
|
||||||
|
return min(self.count, _deque_capping_size)
|
||||||
|
|
||||||
|
def get_child_index(self,name):
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
try:
|
||||||
|
return int(name.lstrip('[').rstrip(']'))
|
||||||
|
except:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def get_child_at_index(self,index):
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
logger.write("Fetching child " + str(index))
|
||||||
|
if index < 0 or self.count is None:
|
||||||
|
return None;
|
||||||
|
if index >= self.num_children():
|
||||||
|
return None;
|
||||||
|
try:
|
||||||
|
i, j = divmod(self.start+index, self.block_size)
|
||||||
|
return self.first.CreateValueFromExpression('[' + str(index) + ']',
|
||||||
|
'*(*(%s + %d) + %d)' % (self.first.get_expr_path(), i, j))
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
try:
|
||||||
|
# A deque is effectively a two-dim array, with fixed width.
|
||||||
|
# 'map' contains pointers to the rows of this array. The
|
||||||
|
# full memory area allocated by the deque is delimited
|
||||||
|
# by 'first' and 'end_cap'. However, only a subset of this
|
||||||
|
# memory contains valid data since a deque may have some slack
|
||||||
|
# at the front and back in order to have O(1) insertion at
|
||||||
|
# both ends. The rows in active use are delimited by
|
||||||
|
# 'begin' and 'end'.
|
||||||
|
#
|
||||||
|
# To find the elements that are actually constructed, the 'start'
|
||||||
|
# variable tells which element in this NxM array is the 0th
|
||||||
|
# one, and the 'size' element gives the number of elements
|
||||||
|
# in the deque.
|
||||||
|
count = self.valobj.GetChildMemberWithName('__size_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
|
||||||
|
# give up now if we cant access memory reliably
|
||||||
|
if self.block_size < 0:
|
||||||
|
logger.write("block_size < 0")
|
||||||
|
return
|
||||||
|
map_ = self.valobj.GetChildMemberWithName('__map_')
|
||||||
|
start = self.valobj.GetChildMemberWithName('__start_').GetValueAsUnsigned(0)
|
||||||
|
first = map_.GetChildMemberWithName('__first_')
|
||||||
|
map_first = first.GetValueAsUnsigned(0)
|
||||||
|
map_begin = map_.GetChildMemberWithName('__begin_').GetValueAsUnsigned(0)
|
||||||
|
map_end = map_.GetChildMemberWithName('__end_').GetValueAsUnsigned(0)
|
||||||
|
map_endcap= map_.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
|
||||||
|
# check consistency
|
||||||
|
if not map_first <= map_begin <= map_end <= map_endcap:
|
||||||
|
logger.write("map pointers are not monotonic")
|
||||||
|
return
|
||||||
|
total_rows, junk = divmod(map_endcap - map_first, self.pointer_size)
|
||||||
|
if junk:
|
||||||
|
logger.write("endcap-first doesnt align correctly")
|
||||||
|
return
|
||||||
|
active_rows, junk = divmod(map_end - map_begin, self.pointer_size)
|
||||||
|
if junk:
|
||||||
|
logger.write("end-begin doesnt align correctly")
|
||||||
|
return
|
||||||
|
start_row, junk = divmod(map_begin - map_first, self.pointer_size)
|
||||||
|
if junk:
|
||||||
|
logger.write("begin-first doesnt align correctly")
|
||||||
|
return
|
||||||
|
if not start_row*self.block_size <= start < (start_row+1)*self.block_size:
|
||||||
|
logger.write("0th element must be in the 'begin' row")
|
||||||
|
return
|
||||||
|
end_row = start_row + active_rows
|
||||||
|
if not count:
|
||||||
|
if active_rows:
|
||||||
|
logger.write("empty deque but begin!=end")
|
||||||
|
return
|
||||||
|
elif not (end_row-1)*self.block_size <= start+count < end_row*self.block_size:
|
||||||
|
logger.write("nth element must be before the 'end' row")
|
||||||
|
return
|
||||||
|
logger.write("update success: count=%r, start=%r, first=%r" % (count,start,first))
|
||||||
|
# if consistent, save all we really need:
|
||||||
|
self.count = count
|
||||||
|
self.start = start
|
||||||
|
self.first = first
|
||||||
|
except:
|
||||||
|
self.count = None
|
||||||
|
self.start = None
|
||||||
|
self.map_first = None
|
||||||
|
self.map_begin = None
|
||||||
|
|
||||||
|
class stdsharedptr_SynthProvider:
|
||||||
|
def __init__(self, valobj, d):
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
logger.write("init")
|
||||||
|
self.valobj = valobj
|
||||||
|
#self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType()
|
||||||
|
self.ptr = None
|
||||||
|
self.cntrl = None
|
||||||
|
process = valobj.GetProcess()
|
||||||
|
self.endianness = process.GetByteOrder()
|
||||||
|
self.pointer_size = process.GetAddressByteSize()
|
||||||
|
self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def get_child_index(self,name):
|
||||||
|
if name=="__ptr_":
|
||||||
|
return 0
|
||||||
|
if name=="count":
|
||||||
|
return 1
|
||||||
|
if name=="weak_count":
|
||||||
|
return 2
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def get_child_at_index(self,index):
|
||||||
|
if index == 0:
|
||||||
|
return self.ptr
|
||||||
|
if index == 1:
|
||||||
|
if self.cntrl == None:
|
||||||
|
count = 0
|
||||||
|
else:
|
||||||
|
count = 1 + self.cntrl.GetChildMemberWithName('__shared_owners_').GetValueAsSigned()
|
||||||
|
return self.valobj.CreateValueFromData("count",
|
||||||
|
lldb.SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [count]),
|
||||||
|
self.count_type)
|
||||||
|
if index == 2:
|
||||||
|
if self.cntrl == None:
|
||||||
|
count = 0
|
||||||
|
else:
|
||||||
|
count = 1 + self.cntrl.GetChildMemberWithName('__shared_weak_owners_').GetValueAsSigned()
|
||||||
|
return self.valobj.CreateValueFromData("weak_count",
|
||||||
|
lldb.SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [count]),
|
||||||
|
self.count_type)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
logger = lldb.formatters.Logger.Logger()
|
||||||
|
self.ptr = self.valobj.GetChildMemberWithName('__ptr_')#.Cast(self.element_ptr_type)
|
||||||
|
cntrl = self.valobj.GetChildMemberWithName('__cntrl_')
|
||||||
|
if cntrl.GetValueAsUnsigned(0):
|
||||||
|
self.cntrl = cntrl.Dereference()
|
||||||
|
else:
|
||||||
|
self.cntrl = None
|
||||||
|
|
||||||
# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
|
# 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"
|
# 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"
|
||||||
|
@ -571,7 +751,12 @@ def __lldb_init_module(debugger,dict):
|
||||||
debugger.HandleCommand('type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -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 summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
|
||||||
debugger.HandleCommand("type category enable libcxx")
|
debugger.HandleCommand("type category enable libcxx")
|
||||||
|
debugger.HandleCommand('type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx')
|
||||||
|
debugger.HandleCommand('type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx')
|
||||||
|
# turns out the structs look the same, so weak_ptr can be handled the same!
|
||||||
|
debugger.HandleCommand('type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx')
|
||||||
|
|
||||||
_map_capping_size = 255
|
_map_capping_size = 255
|
||||||
_list_capping_size = 255
|
_list_capping_size = 255
|
||||||
_list_uses_loop_detector = True
|
_list_uses_loop_detector = True
|
||||||
|
_deque_capping_size = 255
|
||||||
|
|
|
@ -796,14 +796,30 @@ FormatManager::LoadLibcxxFormatters()
|
||||||
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::map<.+> >(( )?&)?$")),
|
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::map<.+> >(( )?&)?$")),
|
||||||
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||||
"lldb.formatters.cpp.libcxx.stdmap_SynthProvider")));
|
"lldb.formatters.cpp.libcxx.stdmap_SynthProvider")));
|
||||||
|
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")),
|
||||||
|
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||||
|
"lldb.formatters.cpp.libcxx.stddeque_SynthProvider")));
|
||||||
|
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)shared_ptr<.+>(( )?&)?$")),
|
||||||
|
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||||
|
"lldb.formatters.cpp.libcxx.stdsharedptr_SynthProvider")));
|
||||||
|
libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)weak_ptr<.+>(( )?&)?$")),
|
||||||
|
SyntheticChildrenSP(new TypeSyntheticImpl(stl_synth_flags,
|
||||||
|
"lldb.formatters.cpp.libcxx.stdsharedptr_SynthProvider")));
|
||||||
|
|
||||||
stl_summary_flags.SetDontShowChildren(false);
|
stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(true);
|
||||||
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::vector<.+>(( )?&)?")),
|
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::vector<.+>(( )?&)?$")),
|
||||||
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||||
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::list<.+>(( )?&)?$")),
|
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::list<.+>(( )?&)?$")),
|
||||||
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||||
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::map<.+> >(( )?&)?$")),
|
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::map<.+> >(( )?&)?$")),
|
||||||
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||||
|
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::deque<.+>(( )?&)?$")),
|
||||||
|
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||||
|
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::shared_ptr<.+>(( )?&)?$")),
|
||||||
|
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})")));
|
||||||
|
libcxx_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::__1::weak_ptr<.+>(( )?&)?$")),
|
||||||
|
TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})")));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue