Part 1 of a series of fixes meant to improve reliability and increase ease of bug fixing for data formatter issues.

We are introducing a new Logger class on the Python side. This has the same purpose, but is unrelated, to the C++ logging facility
The Pythonic logging can be enabled by using the following scripting commands:
(lldb) script Logger._lldb_formatters_debug_level = {0,1,2,...}
0 = no logging
1 = do log
2 = flush after logging each line - slower but safer
3 or more = each time a Logger is constructed, log the function that has created it
more log levels may be added, each one being more log-active than the previous
by default, the log output will come out on your screen, to direct it to a file:
(lldb) script Logger._lldb_formatters_debug_filename = 'filename'
that will make the output go to the file - set to None to disable the file output and get screen logging back
Logging has been enabled for the C++ STL formatters and for Cocoa class NSData - more logging will follow


synthetic children providers for classes list and map (both libstdcpp and libcxx) now have internal capping for safety reasons
this will fix crashers where a malformed list or map would not ever meet our termination conditions

to set the cap to a different value:

(lldb) script {gnu_libstdcpp|libcxx}.{map|list}_capping_size = new_cap (by default, it is 255)

you can optionally disable the loop detection algorithm for lists

(lldb) script {gnu_libstdcpp|libcxx}.list_uses_loop_detector = False

llvm-svn: 153676
This commit is contained in:
Enrico Granata 2012-03-29 19:29:45 +00:00
parent 751aac610a
commit d50f18b1a0
6 changed files with 333 additions and 6 deletions

View File

@ -0,0 +1,122 @@
from __future__ import print_function
import sys
import os.path
import inspect
class NopLogger:
def __init__(self):
pass
def write(self,data):
pass
def flush(self):
pass
def close(self):
pass
class StdoutLogger:
def __init__(self):
pass
def write(self,data):
print(data)
def flush(self):
pass
def close(self):
pass
class FileLogger:
def __init__(self, name):
self.file = None
try:
name = os.path.abspath(name)
self.file = open(name,'a')
except:
try:
self.file = open('formatters.log','a')
except:
pass
def write(self,data):
if self.file != None:
print(data,file=self.file)
else:
print(data)
def flush(self):
if self.file != None:
self.file.flush()
def close(self):
if self.file != None:
self.file.close()
self.file = None
# to enable logging:
# define Logger._lldb_formatters_debug_level to any number greater than 0
# if you define it to any value greater than 1, the log will be automatically flushed after each write (slower but should make sure most of the stuff makes it to the log even if we crash)
# if you define it to any value greater than 2, the calling function's details will automatically be logged (even slower, but provides additional details)
# if you need the log to go to a file instead of on screen, define Logger._lldb_formatters_debug_filename to a valid filename
class Logger:
def __init__(self,autoflush=False,logcaller=False):
global _lldb_formatters_debug_level
global _lldb_formatters_debug_filename
self.autoflush = autoflush
want_log = False
try:
want_log = (_lldb_formatters_debug_level > 0)
except:
pass
if not (want_log):
self.impl = NopLogger()
return
want_file = False
try:
want_file = (_lldb_formatters_debug_filename != None and _lldb_formatters_debug_filename != '' and _lldb_formatters_debug_filename != 0)
except:
pass
if want_file:
self.impl = FileLogger(_lldb_formatters_debug_filename)
else:
self.impl = StdoutLogger()
try:
self.autoflush = (_lldb_formatters_debug_level > 1)
except:
self.autoflush = autoflush
want_caller_info = False
try:
want_caller_info = (_lldb_formatters_debug_level > 2)
except:
pass
if want_caller_info:
self._log_caller()
def _log_caller(self):
caller = inspect.stack()[2]
try:
if caller != None and len(caller) > 3:
self.write('Logging from function ' + str(caller))
else:
self.write('Caller info not available - Required caller logging not possible')
finally:
del caller # needed per Python docs to avoid keeping objects alive longer than we care
def write(self,data):
self.impl.write(data)
if self.autoflush:
self.flush()
def __rshift__(self,data):
self.write(data)
def flush(self):
self.impl.flush()
def close(self):
self.impl.close()

View File

@ -10,6 +10,7 @@ import lldb
import ctypes import ctypes
import objc_runtime import objc_runtime
import metrics import metrics
import Logger
statistics = metrics.Metrics() statistics = metrics.Metrics()
statistics.add_metric('invalid_isa') statistics.add_metric('invalid_isa')
@ -25,6 +26,8 @@ class NSConcreteData_SummaryProvider:
pass pass
def __init__(self, valobj, params): def __init__(self, valobj, params):
logger = Logger.Logger()
logger >> "NSConcreteData_SummaryProvider __init__"
self.valobj = valobj; self.valobj = valobj;
self.sys_params = params self.sys_params = params
if not(self.sys_params.types_cache.NSUInteger): if not(self.sys_params.types_cache.NSUInteger):
@ -46,9 +49,13 @@ class NSConcreteData_SummaryProvider:
return 2 * self.sys_params.pointer_size return 2 * self.sys_params.pointer_size
def length(self): def length(self):
logger = Logger.Logger()
logger >> "NSConcreteData_SummaryProvider length"
size = self.valobj.CreateChildAtOffset("count", size = self.valobj.CreateChildAtOffset("count",
self.offset(), self.offset(),
self.sys_params.types_cache.NSUInteger) self.sys_params.types_cache.NSUInteger)
logger >> str(size)
logger >> str(size.GetValueAsUnsigned(0))
return size.GetValueAsUnsigned(0) return size.GetValueAsUnsigned(0)
@ -57,6 +64,8 @@ class NSDataUnknown_SummaryProvider:
pass pass
def __init__(self, valobj, params): def __init__(self, valobj, params):
logger = Logger.Logger()
logger >> "NSDataUnknown_SummaryProvider __init__"
self.valobj = valobj; self.valobj = valobj;
self.sys_params = params self.sys_params = params
self.update(); self.update();
@ -65,21 +74,31 @@ class NSDataUnknown_SummaryProvider:
self.adjust_for_architecture(); self.adjust_for_architecture();
def length(self): def length(self):
logger = Logger.Logger()
logger >> "NSDataUnknown_SummaryProvider length"
stream = lldb.SBStream() stream = lldb.SBStream()
self.valobj.GetExpressionPath(stream) self.valobj.GetExpressionPath(stream)
logger >> stream.GetData()
num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " length]"); num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " length]");
logger >> "still in after expression: " + str(num_children_vo)
if num_children_vo.IsValid(): if num_children_vo.IsValid():
logger >> "wow - expr output is valid: " + str(num_children_vo.GetValueAsUnsigned())
return num_children_vo.GetValueAsUnsigned(0) return num_children_vo.GetValueAsUnsigned(0)
logger >> "invalid expr output - too bad"
return '<variable is not NSData>' return '<variable is not NSData>'
def GetSummary_Impl(valobj): def GetSummary_Impl(valobj):
global statistics global statistics
logger = Logger.Logger()
logger >> "NSData GetSummary_Impl"
class_data,wrapper = objc_runtime.Utilities.prepare_class_detection(valobj,statistics) class_data,wrapper = objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
if wrapper: if wrapper:
logger >> "got a wrapper summary - using it"
return wrapper return wrapper
name_string = class_data.class_name() name_string = class_data.class_name()
logger >> "class name: " + name_string
if name_string == 'NSConcreteData' or \ if name_string == 'NSConcreteData' or \
name_string == 'NSConcreteMutableData' or \ name_string == 'NSConcreteMutableData' or \
name_string == '__NSCFData': name_string == '__NSCFData':
@ -91,12 +110,16 @@ def GetSummary_Impl(valobj):
return wrapper; return wrapper;
def NSData_SummaryProvider (valobj,dict): def NSData_SummaryProvider (valobj,dict):
logger = Logger.Logger()
logger >> "NSData_SummaryProvider"
provider = GetSummary_Impl(valobj); provider = GetSummary_Impl(valobj);
logger >> "found a summary provider, it is: " + str(provider)
if provider != None: if provider != None:
try: try:
summary = provider.length(); summary = provider.length();
except: except:
summary = None summary = None
logger >> "got a summary: it is " + str(summary)
if summary == None: if summary == None:
summary = '<variable is not NSData>' summary = '<variable is not NSData>'
elif isinstance(summary,basestring): elif isinstance(summary,basestring):
@ -110,7 +133,10 @@ def NSData_SummaryProvider (valobj,dict):
return 'Summary Unavailable' return 'Summary Unavailable'
def NSData_SummaryProvider2 (valobj,dict): def NSData_SummaryProvider2 (valobj,dict):
logger = Logger.Logger()
logger >> "NSData_SummaryProvider2"
provider = GetSummary_Impl(valobj); provider = GetSummary_Impl(valobj);
logger >> "found a summary provider, it is: " + str(provider)
if provider != None: if provider != None:
if isinstance(provider,objc_runtime.SpecialSituation_Description): if isinstance(provider,objc_runtime.SpecialSituation_Description):
return provider.message() return provider.message()
@ -118,6 +144,7 @@ def NSData_SummaryProvider2 (valobj,dict):
summary = provider.length(); summary = provider.length();
except: except:
summary = None summary = None
logger >> "got a summary: it is " + str(summary)
if summary == None: if summary == None:
summary = '<variable is not CFData>' summary = '<variable is not CFData>'
elif isinstance(summary,basestring): elif isinstance(summary,basestring):

View File

@ -1,4 +1,5 @@
import re import re
import 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++
@ -9,20 +10,29 @@ import re
class StdListSynthProvider: class StdListSynthProvider:
def __init__(self, valobj, dict): def __init__(self, valobj, dict):
logger = Logger.Logger()
self.valobj = valobj self.valobj = valobj
def next_node(self,node): def next_node(self,node):
logger = Logger.Logger()
return node.GetChildMemberWithName('_M_next') return node.GetChildMemberWithName('_M_next')
def is_valid(self,node): def is_valid(self,node):
logger = Logger.Logger()
return self.value(self.next_node(node)) != self.node_address return self.value(self.next_node(node)) != self.node_address
def value(self,node): def value(self,node):
logger = Logger.Logger()
return node.GetValueAsUnsigned() return node.GetValueAsUnsigned()
# Floyd's cyle-finding algorithm # Floyd's cyle-finding algorithm
# try to detect if this list has a loop # try to detect if this list has a loop
def has_loop(self): def has_loop(self):
global _list_uses_loop_detector
logger = Logger.Logger()
if _list_uses_loop_detector == False:
logger >> "Asked not to use loop detection"
return False
slow = self.next slow = self.next
fast1 = self.next fast1 = self.next
fast2 = self.next fast2 = self.next
@ -36,11 +46,17 @@ class StdListSynthProvider:
return False return False
def num_children(self): def num_children(self):
global _list_capping_size
logger = Logger.Logger()
if self.count == None: if self.count == None:
self.count = self.num_children_impl() self.count = self.num_children_impl()
if self.count > _list_capping_size:
self.count = _list_capping_size
return self.count return self.count
def num_children_impl(self): def num_children_impl(self):
logger = Logger.Logger()
global _list_capping_size
try: try:
next_val = self.next.GetValueAsUnsigned(0) next_val = self.next.GetValueAsUnsigned(0)
prev_val = self.prev.GetValueAsUnsigned(0) prev_val = self.prev.GetValueAsUnsigned(0)
@ -58,17 +74,21 @@ class StdListSynthProvider:
while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address: while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address:
size = size + 1 size = size + 1
current = current.GetChildMemberWithName('_M_next') current = current.GetChildMemberWithName('_M_next')
if size > _list_capping_size:
return _list_capping_size
return (size - 1) return (size - 1)
except: except:
return 0; return 0;
def get_child_index(self,name): def get_child_index(self,name):
logger = Logger.Logger()
try: try:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self,index): def get_child_at_index(self,index):
logger = Logger.Logger()
if index < 0: if index < 0:
return None; return None;
if index >= self.num_children(): if index >= self.num_children():
@ -84,6 +104,7 @@ class StdListSynthProvider:
return None return None
def extract_type(self): def extract_type(self):
logger = Logger.Logger()
list_type = self.valobj.GetType().GetUnqualifiedType() list_type = self.valobj.GetType().GetUnqualifiedType()
if list_type.IsReferenceType(): if list_type.IsReferenceType():
list_type = list_type.GetDereferencedType() list_type = list_type.GetDereferencedType()
@ -94,6 +115,7 @@ class StdListSynthProvider:
return data_type return data_type
def update(self): def update(self):
logger = Logger.Logger()
try: try:
impl = self.valobj.GetChildMemberWithName('_M_impl') impl = self.valobj.GetChildMemberWithName('_M_impl')
node = impl.GetChildMemberWithName('_M_node') node = impl.GetChildMemberWithName('_M_node')
@ -109,19 +131,23 @@ class StdListSynthProvider:
class StdVectorSynthProvider: class StdVectorSynthProvider:
def __init__(self, valobj, dict): def __init__(self, valobj, dict):
logger = Logger.Logger()
self.valobj = valobj; self.valobj = valobj;
def num_children(self): def num_children(self):
logger = Logger.Logger()
if self.count == None: if self.count == None:
self.count = self.num_children_impl() self.count = self.num_children_impl()
return self.count return self.count
def is_valid_pointer(ptr,process): def is_valid_pointer(ptr,process):
logger = Logger.Logger()
error = lldb.SBError() error = lldb.SBError()
process.ReadMemory(ptr,1,error) process.ReadMemory(ptr,1,error)
return False if error.Fail() else True return False if error.Fail() else True
def num_children_impl(self): def num_children_impl(self):
logger = Logger.Logger()
try: try:
start_val = self.start.GetValueAsUnsigned(0) start_val = self.start.GetValueAsUnsigned(0)
finish_val = self.finish.GetValueAsUnsigned(0) finish_val = self.finish.GetValueAsUnsigned(0)
@ -156,12 +182,14 @@ class StdVectorSynthProvider:
return 0; return 0;
def get_child_index(self,name): def get_child_index(self,name):
logger = Logger.Logger()
try: try:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self,index): def get_child_at_index(self,index):
logger = Logger.Logger()
if index < 0: if index < 0:
return None; return None;
if index >= self.num_children(): if index >= self.num_children():
@ -173,6 +201,7 @@ class StdVectorSynthProvider:
return None return None
def update(self): def update(self):
logger = Logger.Logger()
try: try:
impl = self.valobj.GetChildMemberWithName('_M_impl') impl = self.valobj.GetChildMemberWithName('_M_impl')
self.start = impl.GetChildMemberWithName('_M_start') self.start = impl.GetChildMemberWithName('_M_start')
@ -192,7 +221,9 @@ class StdVectorSynthProvider:
class StdMapSynthProvider: class StdMapSynthProvider:
def __init__(self, valobj, dict): def __init__(self, valobj, dict):
logger = Logger.Logger()
self.valobj = valobj; self.valobj = valobj;
logger >> "Providing synthetic children for a map named " + str(valobj.GetName())
# we need this function as a temporary workaround for rdar://problem/10801549 # we need this function as a temporary workaround for rdar://problem/10801549
# which prevents us from extracting the std::pair<K,V> SBType out of the template # which prevents us from extracting the std::pair<K,V> SBType out of the template
@ -202,6 +233,7 @@ class StdMapSynthProvider:
# to replace the longer versions of std::string with the shorter one in order to be able # to replace the longer versions of std::string with the shorter one in order to be able
# to find the type name # to find the type name
def fixup_class_name(self, class_name): def fixup_class_name(self, class_name):
logger = Logger.Logger()
if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >': if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >':
return 'std::basic_string<char>',True return 'std::basic_string<char>',True
if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >': if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >':
@ -213,7 +245,12 @@ class StdMapSynthProvider:
return class_name,False return class_name,False
def update(self): def update(self):
logger = Logger.Logger()
try: try:
self.count = None
# we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree
# if this gets set to True, then we will merrily return None for any child from that moment on
self.garbage = False
self.Mt = self.valobj.GetChildMemberWithName('_M_t') self.Mt = self.valobj.GetChildMemberWithName('_M_t')
self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl') self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl')
self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header') self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header')
@ -254,25 +291,43 @@ class StdMapSynthProvider:
pass pass
def num_children(self): def num_children(self):
global _map_capping_size
logger = Logger.Logger()
if self.count == None:
self.count = self.num_children_impl()
if self.count > _map_capping_size:
self.count = _map_capping_size
return self.count
def num_children_impl(self):
logger = Logger.Logger()
try: try:
root_ptr_val = self.node_ptr_value(self.Mroot) root_ptr_val = self.node_ptr_value(self.Mroot)
if root_ptr_val == 0: if root_ptr_val == 0:
return 0; return 0;
return self.Mimpl.GetChildMemberWithName('_M_node_count').GetValueAsUnsigned(0) count = self.Mimpl.GetChildMemberWithName('_M_node_count').GetValueAsUnsigned(0)
logger >> "I have " + str(count) + " children available"
return count
except: except:
return 0; return 0;
def get_child_index(self,name): def get_child_index(self,name):
logger = Logger.Logger()
try: try:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self,index): def get_child_at_index(self,index):
logger = Logger.Logger()
logger >> "Being asked to fetch child[" + str(index) + "]"
if index < 0: if index < 0:
return None return None
if index >= self.num_children(): if index >= self.num_children():
return None; return None;
if self.garbage:
logger >> "Returning None since we are a garbage tree"
return None
try: try:
offset = index offset = index
current = self.left(self.Mheader); current = self.left(self.Mheader);
@ -286,31 +341,53 @@ class StdMapSynthProvider:
# utility functions # utility functions
def node_ptr_value(self,node): def node_ptr_value(self,node):
logger = Logger.Logger()
return node.GetValueAsUnsigned(0) return node.GetValueAsUnsigned(0)
def right(self,node): def right(self,node):
logger = Logger.Logger()
return node.GetChildMemberWithName("_M_right"); return node.GetChildMemberWithName("_M_right");
def left(self,node): def left(self,node):
logger = Logger.Logger()
return node.GetChildMemberWithName("_M_left"); return node.GetChildMemberWithName("_M_left");
def parent(self,node): def parent(self,node):
logger = Logger.Logger()
return node.GetChildMemberWithName("_M_parent"); return node.GetChildMemberWithName("_M_parent");
# from libstdc++ implementation of iterator for rbtree # from libstdc++ implementation of iterator for rbtree
def increment_node(self,node): def increment_node(self,node):
logger = Logger.Logger()
max_steps = self.num_children()
if self.node_ptr_value(self.right(node)) != 0: if self.node_ptr_value(self.right(node)) != 0:
x = self.right(node); x = self.right(node);
max_steps -= 1
while self.node_ptr_value(self.left(x)) != 0: while self.node_ptr_value(self.left(x)) != 0:
x = self.left(x); x = self.left(x);
max_steps -= 1
logger >> str(max_steps) + " more to go before giving up"
if max_steps <= 0:
self.garbage = True
return None
return x; return x;
else: else:
x = node; x = node;
y = self.parent(x) y = self.parent(x)
max_steps -= 1
while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))): while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))):
x = y; x = y;
y = self.parent(y); y = self.parent(y);
max_steps -= 1
logger >> str(max_steps) + " more to go before giving up"
if max_steps <= 0:
self.garbage = True
return None
if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y): if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y):
x = y; x = y;
return x; return x;
_map_capping_size = 255
_list_capping_size = 255
_list_uses_loop_detector = True

View File

@ -1,4 +1,5 @@
import lldb import lldb
import Logger
# libcxx STL formatters for LLDB # libcxx STL formatters for LLDB
# These formatters are based upon the implementation of libc++ that # These formatters are based upon the implementation of libc++ that
@ -27,6 +28,7 @@ def extract_short_size(value):
# no external significance - we access them by index since this saves a name lookup that would add # 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 # no information for readers of the code, but when possible try to use meaningful variable names
def stdstring_SummaryProvider(valobj,dict): def stdstring_SummaryProvider(valobj,dict):
logger = Logger.Logger()
r = valobj.GetChildAtIndex(0) r = valobj.GetChildAtIndex(0)
B = r.GetChildAtIndex(0) B = r.GetChildAtIndex(0)
first = B.GetChildAtIndex(0) first = B.GetChildAtIndex(0)
@ -55,9 +57,11 @@ def stdstring_SummaryProvider(valobj,dict):
class stdvector_SynthProvider: class stdvector_SynthProvider:
def __init__(self, valobj, dict): def __init__(self, valobj, dict):
logger = Logger.Logger()
self.valobj = valobj; self.valobj = valobj;
def num_children(self): def num_children(self):
logger = Logger.Logger()
try: try:
start_val = self.start.GetValueAsUnsigned(0) start_val = self.start.GetValueAsUnsigned(0)
finish_val = self.finish.GetValueAsUnsigned(0) finish_val = self.finish.GetValueAsUnsigned(0)
@ -85,12 +89,14 @@ class stdvector_SynthProvider:
return 0; return 0;
def get_child_index(self,name): def get_child_index(self,name):
logger = Logger.Logger()
try: try:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self,index): def get_child_at_index(self,index):
logger = Logger.Logger()
if index < 0: if index < 0:
return None; return None;
if index >= self.num_children(): if index >= self.num_children():
@ -102,6 +108,7 @@ class stdvector_SynthProvider:
return None return None
def update(self): def update(self):
logger = Logger.Logger()
try: try:
self.start = self.valobj.GetChildMemberWithName('__begin_') self.start = self.valobj.GetChildMemberWithName('__begin_')
self.finish = self.valobj.GetChildMemberWithName('__end_') self.finish = self.valobj.GetChildMemberWithName('__end_')
@ -121,21 +128,27 @@ def stdvector_SummaryProvider(valobj,dict):
class stdlist_entry: class stdlist_entry:
def __init__(self,entry): def __init__(self,entry):
logger = Logger.Logger()
self.entry = entry self.entry = entry
def _next_impl(self): def _next_impl(self):
logger = Logger.Logger()
return stdlist_entry(self.entry.GetChildMemberWithName('__next_')) return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
def _prev_impl(self): def _prev_impl(self):
logger = Logger.Logger()
return stdlist_entry(self.entry.GetChildMemberWithName('__prev_')) return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
def _value_impl(self): def _value_impl(self):
logger = Logger.Logger()
return self.entry.GetValueAsUnsigned(0) return self.entry.GetValueAsUnsigned(0)
def _isnull_impl(self): def _isnull_impl(self):
logger = Logger.Logger()
return self._value_impl() == 0 return self._value_impl() == 0
def _sbvalue_impl(self): def _sbvalue_impl(self):
logger = Logger.Logger()
return self.entry return self.entry
next = property(_next_impl,None) next = property(_next_impl,None)
@ -146,17 +159,21 @@ class stdlist_entry:
class stdlist_iterator: class stdlist_iterator:
def increment_node(self,node): def increment_node(self,node):
logger = Logger.Logger()
if node.is_null: if node.is_null:
return None return None
return node.next return node.next
def __init__(self,node): def __init__(self,node):
logger = Logger.Logger()
self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
def value(self): def value(self):
logger = Logger.Logger()
return self.node.sbvalue # and return the SBValue back on exit return self.node.sbvalue # and return the SBValue back on exit
def next(self): def next(self):
logger = Logger.Logger()
node = self.increment_node(self.node) node = self.increment_node(self.node)
if node != None and node.sbvalue.IsValid() and not(node.is_null): if node != None and node.sbvalue.IsValid() and not(node.is_null):
self.node = node self.node = node
@ -165,6 +182,7 @@ class stdlist_iterator:
return None return None
def advance(self,N): def advance(self,N):
logger = Logger.Logger()
if N < 0: if N < 0:
return None return None
if N == 0: if N == 0:
@ -179,17 +197,25 @@ class stdlist_iterator:
class stdlist_SynthProvider: class stdlist_SynthProvider:
def __init__(self, valobj, dict): def __init__(self, valobj, dict):
logger = Logger.Logger()
self.valobj = valobj self.valobj = valobj
def next_node(self,node): def next_node(self,node):
logger = Logger.Logger()
return node.GetChildMemberWithName('__next_') return node.GetChildMemberWithName('__next_')
def value(self,node): def value(self,node):
logger = Logger.Logger()
return node.GetValueAsUnsigned() return node.GetValueAsUnsigned()
# Floyd's cyle-finding algorithm # Floyd's cyle-finding algorithm
# try to detect if this list has a loop # try to detect if this list has a loop
def has_loop(self): def has_loop(self):
global _list_uses_loop_detector
logger = Logger.Logger()
if _list_uses_loop_detector == False:
logger >> "Asked not to use loop detection"
return False
slow = stdlist_entry(self.head) slow = stdlist_entry(self.head)
fast1 = stdlist_entry(self.head) fast1 = stdlist_entry(self.head)
fast2 = stdlist_entry(self.head) fast2 = stdlist_entry(self.head)
@ -203,11 +229,17 @@ class stdlist_SynthProvider:
return False return False
def num_children(self): def num_children(self):
global _list_capping_size
logger = Logger.Logger()
if self.count == None: if self.count == None:
self.count = self.num_children_impl() self.count = self.num_children_impl()
if self.count > _list_capping_size:
self.count = _list_capping_size
return self.count return self.count
def num_children_impl(self): def num_children_impl(self):
global _list_capping_size
logger = Logger.Logger()
try: try:
next_val = self.head.GetValueAsUnsigned(0) next_val = self.head.GetValueAsUnsigned(0)
prev_val = self.tail.GetValueAsUnsigned(0) prev_val = self.tail.GetValueAsUnsigned(0)
@ -225,17 +257,21 @@ class stdlist_SynthProvider:
while current.next.value != self.node_address: while current.next.value != self.node_address:
size = size + 1 size = size + 1
current = current.next current = current.next
if size > _list_capping_size:
return _list_capping_size
return (size - 1) return (size - 1)
except: except:
return 0; return 0;
def get_child_index(self,name): def get_child_index(self,name):
logger = Logger.Logger()
try: try:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self,index): def get_child_at_index(self,index):
logger = Logger.Logger()
if index < 0: if index < 0:
return None; return None;
if index >= self.num_children(): if index >= self.num_children():
@ -252,6 +288,7 @@ class stdlist_SynthProvider:
return None return None
def extract_type(self): def extract_type(self):
logger = Logger.Logger()
list_type = self.valobj.GetType().GetUnqualifiedType() list_type = self.valobj.GetType().GetUnqualifiedType()
if list_type.IsReferenceType(): if list_type.IsReferenceType():
list_type = list_type.GetDereferencedType() list_type = list_type.GetDereferencedType()
@ -262,6 +299,7 @@ class stdlist_SynthProvider:
return data_type return data_type
def update(self): def update(self):
logger = Logger.Logger()
try: try:
impl = self.valobj.GetChildMemberWithName('__end_') impl = self.valobj.GetChildMemberWithName('__end_')
self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
@ -281,24 +319,31 @@ def stdlist_SummaryProvider(valobj,dict):
# a tree node - this class makes the syntax in the actual iterator nicer to read and maintain # a tree node - this class makes the syntax in the actual iterator nicer to read and maintain
class stdmap_iterator_node: class stdmap_iterator_node:
def _left_impl(self): def _left_impl(self):
logger = Logger.Logger()
return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_")) return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
def _right_impl(self): def _right_impl(self):
logger = Logger.Logger()
return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_")) return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
def _parent_impl(self): def _parent_impl(self):
logger = Logger.Logger()
return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_")) return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
def _value_impl(self): def _value_impl(self):
logger = Logger.Logger()
return self.node.GetValueAsUnsigned(0) return self.node.GetValueAsUnsigned(0)
def _sbvalue_impl(self): def _sbvalue_impl(self):
logger = Logger.Logger()
return self.node return self.node
def _null_impl(self): def _null_impl(self):
logger = Logger.Logger()
return self.value == 0 return self.value == 0
def __init__(self,node): def __init__(self,node):
logger = Logger.Logger()
self.node = node self.node = node
left = property(_left_impl,None) left = property(_left_impl,None)
@ -312,13 +357,20 @@ class stdmap_iterator_node:
class stdmap_iterator: class stdmap_iterator:
def tree_min(self,x): def tree_min(self,x):
logger = Logger.Logger()
steps = 0
if x.is_null: if x.is_null:
return None return None
while (not x.left.is_null): while (not x.left.is_null):
x = x.left x = x.left
steps += 1
if steps > self.max_count:
logger >> "Returning None - we overflowed"
return None
return x return x
def tree_max(self,x): def tree_max(self,x):
logger = Logger.Logger()
if x.is_null: if x.is_null:
return None return None
while (not x.right.is_null): while (not x.right.is_null):
@ -326,26 +378,37 @@ class stdmap_iterator:
return x return x
def tree_is_left_child(self,x): def tree_is_left_child(self,x):
logger = Logger.Logger()
if x.is_null: if x.is_null:
return None return None
return True if x.value == x.parent.left.value else False return True if x.value == x.parent.left.value else False
def increment_node(self,node): def increment_node(self,node):
logger = Logger.Logger()
if node.is_null: if node.is_null:
return None return None
if not node.right.is_null: if not node.right.is_null:
return self.tree_min(node.right) return self.tree_min(node.right)
steps = 0
while (not self.tree_is_left_child(node)): while (not self.tree_is_left_child(node)):
steps += 1
if steps > self.max_count:
logger >> "Returning None - we overflowed"
return None
node = node.parent node = node.parent
return node.parent return node.parent
def __init__(self,node): def __init__(self,node,max_count=0):
logger = Logger.Logger()
self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry
self.max_count = max_count
def value(self): def value(self):
logger = Logger.Logger()
return self.node.sbvalue # and return the SBValue back on exit return self.node.sbvalue # and return the SBValue back on exit
def next(self): def next(self):
logger = Logger.Logger()
node = self.increment_node(self.node) node = self.increment_node(self.node)
if node != None and node.sbvalue.IsValid() and not(node.is_null): if node != None and node.sbvalue.IsValid() and not(node.is_null):
self.node = node self.node = node
@ -354,6 +417,7 @@ class stdmap_iterator:
return None return None
def advance(self,N): def advance(self,N):
logger = Logger.Logger()
if N < 0: if N < 0:
return None return None
if N == 0: if N == 0:
@ -361,18 +425,24 @@ class stdmap_iterator:
if N == 1: if N == 1:
return self.next() return self.next()
while N > 0: while N > 0:
self.next() if self.next() == None:
return None
N = N - 1 N = N - 1
return self.value() return self.value()
class stdmap_SynthProvider: class stdmap_SynthProvider:
def __init__(self, valobj, dict): def __init__(self, valobj, dict):
logger = Logger.Logger()
self.valobj = valobj; self.valobj = valobj;
self.pointer_size = self.valobj.GetProcess().GetAddressByteSize() self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
def update(self): def update(self):
logger = Logger.Logger()
try: try:
# we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree
# if this gets set to True, then we will merrily return None for any child from that moment on
self.garbage = False
self.tree = self.valobj.GetChildMemberWithName('__tree_') self.tree = self.valobj.GetChildMemberWithName('__tree_')
self.root_node = self.tree.GetChildMemberWithName('__begin_node_') self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
# this data is either lazily-calculated, or cannot be inferred at this moment # this data is either lazily-calculated, or cannot be inferred at this moment
@ -385,17 +455,23 @@ class stdmap_SynthProvider:
pass pass
def num_children(self): def num_children(self):
global _map_capping_size
logger = Logger.Logger()
if self.count == None: if self.count == None:
self.count = self.num_children_impl() self.count = self.num_children_impl()
if self.count > _map_capping_size:
self.count = _map_capping_size
return self.count return self.count
def num_children_impl(self): def num_children_impl(self):
logger = Logger.Logger()
try: try:
return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned() return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
except: except:
return 0; return 0;
def get_data_type(self): def get_data_type(self):
logger = Logger.Logger()
if self.data_type == None or self.data_size == None: if self.data_type == None or self.data_size == None:
if self.num_children() == 0: if self.num_children() == 0:
return False return False
@ -413,6 +489,7 @@ class stdmap_SynthProvider:
return True return True
def get_value_offset(self,node): def get_value_offset(self,node):
logger = Logger.Logger()
if self.skip_size == None: if self.skip_size == None:
node_type = node.GetType() node_type = node.GetType()
fields_count = node_type.GetNumberOfFields() fields_count = node_type.GetNumberOfFields()
@ -424,24 +501,31 @@ class stdmap_SynthProvider:
return (self.skip_size != None) return (self.skip_size != None)
def get_child_index(self,name): def get_child_index(self,name):
logger = Logger.Logger()
try: try:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self,index): def get_child_at_index(self,index):
logger = Logger.Logger()
if index < 0: if index < 0:
return None return None
if index >= self.num_children(): if index >= self.num_children():
return None; return None;
if self.garbage:
return None
try: try:
iterator = stdmap_iterator(self.root_node) iterator = stdmap_iterator(self.root_node,max_count=self.num_children())
# the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type # 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 # 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 # 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 # 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) need_to_skip = (index > 0)
current = iterator.advance(index) current = iterator.advance(index)
if current == None:
self.garbage = True
return None
if self.get_data_type(): if self.get_data_type():
if not(need_to_skip): if not(need_to_skip):
current = current.Dereference() current = current.Dereference()
@ -481,3 +565,7 @@ 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")
_map_capping_size = 255
_list_capping_size = 255
_list_uses_loop_detector = True

View File

@ -508,6 +508,20 @@ else
fi fi
fi fi
if [ -f "${SRC_ROOT}/examples/summaries/cocoa/Logger.py" ]
then
if [ $Debug == 1 ]
then
echo "Copying Logger.py to ${framework_python_dir}"
fi
cp "${SRC_ROOT}/examples/summaries/cocoa/Logger.py" "${framework_python_dir}"
else
if [ $Debug == 1 ]
then
echo "Unable to find ${SRC_ROOT}/examples/summaries/cocoa/Logger.py"
fi
fi
if [ -f "${SRC_ROOT}/examples/summaries/cocoa/objc_lldb.py" ] if [ -f "${SRC_ROOT}/examples/summaries/cocoa/objc_lldb.py" ]
then then
if [ $Debug == 1 ] if [ $Debug == 1 ]

View File

@ -296,8 +296,7 @@ ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interprete
int old_count = Debugger::TestDebuggerRefCount(); int old_count = Debugger::TestDebuggerRefCount();
run_string.Printf ("run_one_line (%s, 'import copy, os, re, sys, uuid, lldb, gnu_libstdcpp, libcxx, objc, Logger')", 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()); 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 // WARNING: temporary code that loads Cocoa formatters - this should be done on a per-platform basis rather than loading the whole set