forked from OSchip/llvm-project
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:
parent
751aac610a
commit
d50f18b1a0
|
@ -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()
|
||||
|
|
@ -10,6 +10,7 @@ import lldb
|
|||
import ctypes
|
||||
import objc_runtime
|
||||
import metrics
|
||||
import Logger
|
||||
|
||||
statistics = metrics.Metrics()
|
||||
statistics.add_metric('invalid_isa')
|
||||
|
@ -25,6 +26,8 @@ class NSConcreteData_SummaryProvider:
|
|||
pass
|
||||
|
||||
def __init__(self, valobj, params):
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSConcreteData_SummaryProvider __init__"
|
||||
self.valobj = valobj;
|
||||
self.sys_params = params
|
||||
if not(self.sys_params.types_cache.NSUInteger):
|
||||
|
@ -46,9 +49,13 @@ class NSConcreteData_SummaryProvider:
|
|||
return 2 * self.sys_params.pointer_size
|
||||
|
||||
def length(self):
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSConcreteData_SummaryProvider length"
|
||||
size = self.valobj.CreateChildAtOffset("count",
|
||||
self.offset(),
|
||||
self.sys_params.types_cache.NSUInteger)
|
||||
logger >> str(size)
|
||||
logger >> str(size.GetValueAsUnsigned(0))
|
||||
return size.GetValueAsUnsigned(0)
|
||||
|
||||
|
||||
|
@ -57,6 +64,8 @@ class NSDataUnknown_SummaryProvider:
|
|||
pass
|
||||
|
||||
def __init__(self, valobj, params):
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSDataUnknown_SummaryProvider __init__"
|
||||
self.valobj = valobj;
|
||||
self.sys_params = params
|
||||
self.update();
|
||||
|
@ -65,21 +74,31 @@ class NSDataUnknown_SummaryProvider:
|
|||
self.adjust_for_architecture();
|
||||
|
||||
def length(self):
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSDataUnknown_SummaryProvider length"
|
||||
stream = lldb.SBStream()
|
||||
self.valobj.GetExpressionPath(stream)
|
||||
logger >> stream.GetData()
|
||||
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():
|
||||
logger >> "wow - expr output is valid: " + str(num_children_vo.GetValueAsUnsigned())
|
||||
return num_children_vo.GetValueAsUnsigned(0)
|
||||
logger >> "invalid expr output - too bad"
|
||||
return '<variable is not NSData>'
|
||||
|
||||
|
||||
def GetSummary_Impl(valobj):
|
||||
global statistics
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSData GetSummary_Impl"
|
||||
class_data,wrapper = objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
|
||||
if wrapper:
|
||||
logger >> "got a wrapper summary - using it"
|
||||
return wrapper
|
||||
|
||||
name_string = class_data.class_name()
|
||||
logger >> "class name: " + name_string
|
||||
if name_string == 'NSConcreteData' or \
|
||||
name_string == 'NSConcreteMutableData' or \
|
||||
name_string == '__NSCFData':
|
||||
|
@ -91,12 +110,16 @@ def GetSummary_Impl(valobj):
|
|||
return wrapper;
|
||||
|
||||
def NSData_SummaryProvider (valobj,dict):
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSData_SummaryProvider"
|
||||
provider = GetSummary_Impl(valobj);
|
||||
logger >> "found a summary provider, it is: " + str(provider)
|
||||
if provider != None:
|
||||
try:
|
||||
summary = provider.length();
|
||||
except:
|
||||
summary = None
|
||||
logger >> "got a summary: it is " + str(summary)
|
||||
if summary == None:
|
||||
summary = '<variable is not NSData>'
|
||||
elif isinstance(summary,basestring):
|
||||
|
@ -110,7 +133,10 @@ def NSData_SummaryProvider (valobj,dict):
|
|||
return 'Summary Unavailable'
|
||||
|
||||
def NSData_SummaryProvider2 (valobj,dict):
|
||||
logger = Logger.Logger()
|
||||
logger >> "NSData_SummaryProvider2"
|
||||
provider = GetSummary_Impl(valobj);
|
||||
logger >> "found a summary provider, it is: " + str(provider)
|
||||
if provider != None:
|
||||
if isinstance(provider,objc_runtime.SpecialSituation_Description):
|
||||
return provider.message()
|
||||
|
@ -118,6 +144,7 @@ def NSData_SummaryProvider2 (valobj,dict):
|
|||
summary = provider.length();
|
||||
except:
|
||||
summary = None
|
||||
logger >> "got a summary: it is " + str(summary)
|
||||
if summary == None:
|
||||
summary = '<variable is not CFData>'
|
||||
elif isinstance(summary,basestring):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import re
|
||||
import Logger
|
||||
|
||||
# C++ STL formatters for LLDB
|
||||
# These formatters are based upon the version of the GNU libstdc++
|
||||
|
@ -9,20 +10,29 @@ import re
|
|||
class StdListSynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
logger = Logger.Logger()
|
||||
self.valobj = valobj
|
||||
|
||||
def next_node(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetChildMemberWithName('_M_next')
|
||||
|
||||
def is_valid(self,node):
|
||||
logger = Logger.Logger()
|
||||
return self.value(self.next_node(node)) != self.node_address
|
||||
|
||||
def value(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetValueAsUnsigned()
|
||||
|
||||
# Floyd's cyle-finding algorithm
|
||||
# try to detect if this list has a loop
|
||||
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
|
||||
fast1 = self.next
|
||||
fast2 = self.next
|
||||
|
@ -36,11 +46,17 @@ class StdListSynthProvider:
|
|||
return False
|
||||
|
||||
def num_children(self):
|
||||
global _list_capping_size
|
||||
logger = Logger.Logger()
|
||||
if self.count == None:
|
||||
self.count = self.num_children_impl()
|
||||
if self.count > _list_capping_size:
|
||||
self.count = _list_capping_size
|
||||
return self.count
|
||||
|
||||
def num_children_impl(self):
|
||||
logger = Logger.Logger()
|
||||
global _list_capping_size
|
||||
try:
|
||||
next_val = self.next.GetValueAsUnsigned(0)
|
||||
prev_val = self.prev.GetValueAsUnsigned(0)
|
||||
|
@ -58,17 +74,21 @@ class StdListSynthProvider:
|
|||
while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address:
|
||||
size = size + 1
|
||||
current = current.GetChildMemberWithName('_M_next')
|
||||
if size > _list_capping_size:
|
||||
return _list_capping_size
|
||||
return (size - 1)
|
||||
except:
|
||||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
logger = Logger.Logger()
|
||||
if index < 0:
|
||||
return None;
|
||||
if index >= self.num_children():
|
||||
|
@ -84,6 +104,7 @@ class StdListSynthProvider:
|
|||
return None
|
||||
|
||||
def extract_type(self):
|
||||
logger = Logger.Logger()
|
||||
list_type = self.valobj.GetType().GetUnqualifiedType()
|
||||
if list_type.IsReferenceType():
|
||||
list_type = list_type.GetDereferencedType()
|
||||
|
@ -94,6 +115,7 @@ class StdListSynthProvider:
|
|||
return data_type
|
||||
|
||||
def update(self):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
impl = self.valobj.GetChildMemberWithName('_M_impl')
|
||||
node = impl.GetChildMemberWithName('_M_node')
|
||||
|
@ -109,19 +131,23 @@ class StdListSynthProvider:
|
|||
class StdVectorSynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
logger = Logger.Logger()
|
||||
self.valobj = valobj;
|
||||
|
||||
def num_children(self):
|
||||
logger = Logger.Logger()
|
||||
if self.count == None:
|
||||
self.count = self.num_children_impl()
|
||||
return self.count
|
||||
|
||||
def is_valid_pointer(ptr,process):
|
||||
logger = Logger.Logger()
|
||||
error = lldb.SBError()
|
||||
process.ReadMemory(ptr,1,error)
|
||||
return False if error.Fail() else True
|
||||
|
||||
def num_children_impl(self):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
start_val = self.start.GetValueAsUnsigned(0)
|
||||
finish_val = self.finish.GetValueAsUnsigned(0)
|
||||
|
@ -156,12 +182,14 @@ class StdVectorSynthProvider:
|
|||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
logger = Logger.Logger()
|
||||
if index < 0:
|
||||
return None;
|
||||
if index >= self.num_children():
|
||||
|
@ -173,6 +201,7 @@ class StdVectorSynthProvider:
|
|||
return None
|
||||
|
||||
def update(self):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
impl = self.valobj.GetChildMemberWithName('_M_impl')
|
||||
self.start = impl.GetChildMemberWithName('_M_start')
|
||||
|
@ -192,7 +221,9 @@ class StdVectorSynthProvider:
|
|||
class StdMapSynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
logger = Logger.Logger()
|
||||
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
|
||||
# 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 find the type 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> >':
|
||||
return 'std::basic_string<char>',True
|
||||
if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >':
|
||||
|
@ -213,7 +245,12 @@ class StdMapSynthProvider:
|
|||
return class_name,False
|
||||
|
||||
def update(self):
|
||||
logger = Logger.Logger()
|
||||
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.Mimpl = self.Mt.GetChildMemberWithName('_M_impl')
|
||||
self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header')
|
||||
|
@ -254,25 +291,43 @@ class StdMapSynthProvider:
|
|||
pass
|
||||
|
||||
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:
|
||||
root_ptr_val = self.node_ptr_value(self.Mroot)
|
||||
if root_ptr_val == 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:
|
||||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
logger = Logger.Logger()
|
||||
logger >> "Being asked to fetch child[" + str(index) + "]"
|
||||
if index < 0:
|
||||
return None
|
||||
if index >= self.num_children():
|
||||
return None;
|
||||
if self.garbage:
|
||||
logger >> "Returning None since we are a garbage tree"
|
||||
return None
|
||||
try:
|
||||
offset = index
|
||||
current = self.left(self.Mheader);
|
||||
|
@ -286,31 +341,53 @@ class StdMapSynthProvider:
|
|||
|
||||
# utility functions
|
||||
def node_ptr_value(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetValueAsUnsigned(0)
|
||||
|
||||
def right(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetChildMemberWithName("_M_right");
|
||||
|
||||
def left(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetChildMemberWithName("_M_left");
|
||||
|
||||
def parent(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetChildMemberWithName("_M_parent");
|
||||
|
||||
# from libstdc++ implementation of iterator for rbtree
|
||||
def increment_node(self,node):
|
||||
logger = Logger.Logger()
|
||||
max_steps = self.num_children()
|
||||
if self.node_ptr_value(self.right(node)) != 0:
|
||||
x = self.right(node);
|
||||
max_steps -= 1
|
||||
while self.node_ptr_value(self.left(x)) != 0:
|
||||
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;
|
||||
else:
|
||||
x = node;
|
||||
y = self.parent(x)
|
||||
max_steps -= 1
|
||||
while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))):
|
||||
x = 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):
|
||||
x = y;
|
||||
return x;
|
||||
|
||||
|
||||
_map_capping_size = 255
|
||||
_list_capping_size = 255
|
||||
_list_uses_loop_detector = True
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import lldb
|
||||
import Logger
|
||||
|
||||
# libcxx STL formatters for LLDB
|
||||
# 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 information for readers of the code, but when possible try to use meaningful variable names
|
||||
def stdstring_SummaryProvider(valobj,dict):
|
||||
logger = Logger.Logger()
|
||||
r = valobj.GetChildAtIndex(0)
|
||||
B = r.GetChildAtIndex(0)
|
||||
first = B.GetChildAtIndex(0)
|
||||
|
@ -55,9 +57,11 @@ def stdstring_SummaryProvider(valobj,dict):
|
|||
class stdvector_SynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
logger = Logger.Logger()
|
||||
self.valobj = valobj;
|
||||
|
||||
def num_children(self):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
start_val = self.start.GetValueAsUnsigned(0)
|
||||
finish_val = self.finish.GetValueAsUnsigned(0)
|
||||
|
@ -85,12 +89,14 @@ class stdvector_SynthProvider:
|
|||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
logger = Logger.Logger()
|
||||
if index < 0:
|
||||
return None;
|
||||
if index >= self.num_children():
|
||||
|
@ -102,6 +108,7 @@ class stdvector_SynthProvider:
|
|||
return None
|
||||
|
||||
def update(self):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
self.start = self.valobj.GetChildMemberWithName('__begin_')
|
||||
self.finish = self.valobj.GetChildMemberWithName('__end_')
|
||||
|
@ -121,21 +128,27 @@ def stdvector_SummaryProvider(valobj,dict):
|
|||
class stdlist_entry:
|
||||
|
||||
def __init__(self,entry):
|
||||
logger = Logger.Logger()
|
||||
self.entry = entry
|
||||
|
||||
def _next_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
|
||||
|
||||
def _prev_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
|
||||
|
||||
def _value_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return self.entry.GetValueAsUnsigned(0)
|
||||
|
||||
def _isnull_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return self._value_impl() == 0
|
||||
|
||||
def _sbvalue_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return self.entry
|
||||
|
||||
next = property(_next_impl,None)
|
||||
|
@ -146,17 +159,21 @@ class stdlist_entry:
|
|||
class stdlist_iterator:
|
||||
|
||||
def increment_node(self,node):
|
||||
logger = Logger.Logger()
|
||||
if node.is_null:
|
||||
return None
|
||||
return node.next
|
||||
|
||||
def __init__(self,node):
|
||||
logger = Logger.Logger()
|
||||
self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
|
||||
|
||||
def value(self):
|
||||
logger = Logger.Logger()
|
||||
return self.node.sbvalue # and return the SBValue back on exit
|
||||
|
||||
def next(self):
|
||||
logger = Logger.Logger()
|
||||
node = self.increment_node(self.node)
|
||||
if node != None and node.sbvalue.IsValid() and not(node.is_null):
|
||||
self.node = node
|
||||
|
@ -165,6 +182,7 @@ class stdlist_iterator:
|
|||
return None
|
||||
|
||||
def advance(self,N):
|
||||
logger = Logger.Logger()
|
||||
if N < 0:
|
||||
return None
|
||||
if N == 0:
|
||||
|
@ -179,17 +197,25 @@ class stdlist_iterator:
|
|||
|
||||
class stdlist_SynthProvider:
|
||||
def __init__(self, valobj, dict):
|
||||
logger = Logger.Logger()
|
||||
self.valobj = valobj
|
||||
|
||||
def next_node(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetChildMemberWithName('__next_')
|
||||
|
||||
def value(self,node):
|
||||
logger = Logger.Logger()
|
||||
return node.GetValueAsUnsigned()
|
||||
|
||||
# Floyd's cyle-finding algorithm
|
||||
# try to detect if this list has a loop
|
||||
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)
|
||||
fast1 = stdlist_entry(self.head)
|
||||
fast2 = stdlist_entry(self.head)
|
||||
|
@ -203,11 +229,17 @@ class stdlist_SynthProvider:
|
|||
return False
|
||||
|
||||
def num_children(self):
|
||||
global _list_capping_size
|
||||
logger = Logger.Logger()
|
||||
if self.count == None:
|
||||
self.count = self.num_children_impl()
|
||||
if self.count > _list_capping_size:
|
||||
self.count = _list_capping_size
|
||||
return self.count
|
||||
|
||||
def num_children_impl(self):
|
||||
global _list_capping_size
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
next_val = self.head.GetValueAsUnsigned(0)
|
||||
prev_val = self.tail.GetValueAsUnsigned(0)
|
||||
|
@ -225,17 +257,21 @@ class stdlist_SynthProvider:
|
|||
while current.next.value != self.node_address:
|
||||
size = size + 1
|
||||
current = current.next
|
||||
if size > _list_capping_size:
|
||||
return _list_capping_size
|
||||
return (size - 1)
|
||||
except:
|
||||
return 0;
|
||||
|
||||
def get_child_index(self,name):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
logger = Logger.Logger()
|
||||
if index < 0:
|
||||
return None;
|
||||
if index >= self.num_children():
|
||||
|
@ -252,6 +288,7 @@ class stdlist_SynthProvider:
|
|||
return None
|
||||
|
||||
def extract_type(self):
|
||||
logger = Logger.Logger()
|
||||
list_type = self.valobj.GetType().GetUnqualifiedType()
|
||||
if list_type.IsReferenceType():
|
||||
list_type = list_type.GetDereferencedType()
|
||||
|
@ -262,6 +299,7 @@ class stdlist_SynthProvider:
|
|||
return data_type
|
||||
|
||||
def update(self):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
impl = self.valobj.GetChildMemberWithName('__end_')
|
||||
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
|
||||
class stdmap_iterator_node:
|
||||
def _left_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
|
||||
|
||||
def _right_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
|
||||
|
||||
def _parent_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
|
||||
|
||||
def _value_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return self.node.GetValueAsUnsigned(0)
|
||||
|
||||
def _sbvalue_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return self.node
|
||||
|
||||
def _null_impl(self):
|
||||
logger = Logger.Logger()
|
||||
return self.value == 0
|
||||
|
||||
def __init__(self,node):
|
||||
logger = Logger.Logger()
|
||||
self.node = node
|
||||
|
||||
left = property(_left_impl,None)
|
||||
|
@ -312,13 +357,20 @@ class stdmap_iterator_node:
|
|||
class stdmap_iterator:
|
||||
|
||||
def tree_min(self,x):
|
||||
logger = Logger.Logger()
|
||||
steps = 0
|
||||
if x.is_null:
|
||||
return None
|
||||
while (not x.left.is_null):
|
||||
x = x.left
|
||||
steps += 1
|
||||
if steps > self.max_count:
|
||||
logger >> "Returning None - we overflowed"
|
||||
return None
|
||||
return x
|
||||
|
||||
def tree_max(self,x):
|
||||
logger = Logger.Logger()
|
||||
if x.is_null:
|
||||
return None
|
||||
while (not x.right.is_null):
|
||||
|
@ -326,26 +378,37 @@ class stdmap_iterator:
|
|||
return x
|
||||
|
||||
def tree_is_left_child(self,x):
|
||||
logger = Logger.Logger()
|
||||
if x.is_null:
|
||||
return None
|
||||
return True if x.value == x.parent.left.value else False
|
||||
|
||||
def increment_node(self,node):
|
||||
logger = Logger.Logger()
|
||||
if node.is_null:
|
||||
return None
|
||||
if not node.right.is_null:
|
||||
return self.tree_min(node.right)
|
||||
steps = 0
|
||||
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
|
||||
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.max_count = max_count
|
||||
|
||||
def value(self):
|
||||
logger = Logger.Logger()
|
||||
return self.node.sbvalue # and return the SBValue back on exit
|
||||
|
||||
def next(self):
|
||||
logger = Logger.Logger()
|
||||
node = self.increment_node(self.node)
|
||||
if node != None and node.sbvalue.IsValid() and not(node.is_null):
|
||||
self.node = node
|
||||
|
@ -354,6 +417,7 @@ class stdmap_iterator:
|
|||
return None
|
||||
|
||||
def advance(self,N):
|
||||
logger = Logger.Logger()
|
||||
if N < 0:
|
||||
return None
|
||||
if N == 0:
|
||||
|
@ -361,18 +425,24 @@ class stdmap_iterator:
|
|||
if N == 1:
|
||||
return self.next()
|
||||
while N > 0:
|
||||
self.next()
|
||||
if self.next() == None:
|
||||
return None
|
||||
N = N - 1
|
||||
return self.value()
|
||||
|
||||
class stdmap_SynthProvider:
|
||||
|
||||
def __init__(self, valobj, dict):
|
||||
logger = Logger.Logger()
|
||||
self.valobj = valobj;
|
||||
self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
|
||||
|
||||
def update(self):
|
||||
logger = Logger.Logger()
|
||||
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.root_node = self.tree.GetChildMemberWithName('__begin_node_')
|
||||
# this data is either lazily-calculated, or cannot be inferred at this moment
|
||||
|
@ -385,17 +455,23 @@ class stdmap_SynthProvider:
|
|||
pass
|
||||
|
||||
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:
|
||||
return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
|
||||
except:
|
||||
return 0;
|
||||
|
||||
def get_data_type(self):
|
||||
logger = Logger.Logger()
|
||||
if self.data_type == None or self.data_size == None:
|
||||
if self.num_children() == 0:
|
||||
return False
|
||||
|
@ -413,6 +489,7 @@ class stdmap_SynthProvider:
|
|||
return True
|
||||
|
||||
def get_value_offset(self,node):
|
||||
logger = Logger.Logger()
|
||||
if self.skip_size == None:
|
||||
node_type = node.GetType()
|
||||
fields_count = node_type.GetNumberOfFields()
|
||||
|
@ -424,24 +501,31 @@ class stdmap_SynthProvider:
|
|||
return (self.skip_size != None)
|
||||
|
||||
def get_child_index(self,name):
|
||||
logger = Logger.Logger()
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self,index):
|
||||
logger = Logger.Logger()
|
||||
if index < 0:
|
||||
return None
|
||||
if index >= self.num_children():
|
||||
return None;
|
||||
if self.garbage:
|
||||
return None
|
||||
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
|
||||
# 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 current == None:
|
||||
self.garbage = True
|
||||
return None
|
||||
if self.get_data_type():
|
||||
if not(need_to_skip):
|
||||
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 summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
|
||||
debugger.HandleCommand("type category enable libcxx")
|
||||
|
||||
_map_capping_size = 255
|
||||
_list_capping_size = 255
|
||||
_list_uses_loop_detector = True
|
||||
|
|
|
@ -508,6 +508,20 @@ else
|
|||
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" ]
|
||||
then
|
||||
if [ $Debug == 1 ]
|
||||
|
|
|
@ -296,8 +296,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, libcxx, objc')", m_dictionary_name.c_str());
|
||||
run_string.Printf ("run_one_line (%s, 'import copy, os, re, sys, uuid, lldb, gnu_libstdcpp, libcxx, objc, Logger')", 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
|
||||
|
|
Loading…
Reference in New Issue