diff options
Diffstat (limited to 'examples/summaries/cocoa/CFString.py')
-rw-r--r-- | examples/summaries/cocoa/CFString.py | 646 |
1 files changed, 336 insertions, 310 deletions
diff --git a/examples/summaries/cocoa/CFString.py b/examples/summaries/cocoa/CFString.py index 570fd8280e0c..109d918a9de2 100644 --- a/examples/summaries/cocoa/CFString.py +++ b/examples/summaries/cocoa/CFString.py @@ -11,315 +11,341 @@ import lldb import lldb.runtime.objc.objc_runtime import lldb.formatters.Logger -def CFString_SummaryProvider (valobj,dict): - logger = lldb.formatters.Logger.Logger() - provider = CFStringSynthProvider(valobj,dict); - if provider.invalid == False: - try: - summary = provider.get_child_at_index(provider.get_child_index("content")) - if type(summary) == lldb.SBValue: - summary = summary.GetSummary() - else: - summary = '"' + summary + '"' - except: - summary = None - if summary == None: - summary = '<variable is not NSString>' - return '@'+summary - return '' - -def CFAttributedString_SummaryProvider (valobj,dict): - logger = lldb.formatters.Logger.Logger() - offset = valobj.GetTarget().GetProcess().GetAddressByteSize() - pointee = valobj.GetValueAsUnsigned(0) - summary = '<variable is not NSAttributedString>' - if pointee != None and pointee != 0: - pointee = pointee + offset - child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType()) - child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf() - provider = CFStringSynthProvider(child,dict); - if provider.invalid == False: - try: - summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary(); - except: - summary = '<variable is not NSAttributedString>' - if summary == None: - summary = '<variable is not NSAttributedString>' - return '@'+summary - - -def __lldb_init_module(debugger,dict): - debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef") - debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString") + +def CFString_SummaryProvider(valobj, dict): + logger = lldb.formatters.Logger.Logger() + provider = CFStringSynthProvider(valobj, dict) + if not provider.invalid: + try: + summary = provider.get_child_at_index( + provider.get_child_index("content")) + if isinstance(summary, lldb.SBValue): + summary = summary.GetSummary() + else: + summary = '"' + summary + '"' + except: + summary = None + if summary is None: + summary = '<variable is not NSString>' + return '@' + summary + return '' + + +def CFAttributedString_SummaryProvider(valobj, dict): + logger = lldb.formatters.Logger.Logger() + offset = valobj.GetTarget().GetProcess().GetAddressByteSize() + pointee = valobj.GetValueAsUnsigned(0) + summary = '<variable is not NSAttributedString>' + if pointee is not None and pointee != 0: + pointee = pointee + offset + child_ptr = valobj.CreateValueFromAddress( + "string_ptr", pointee, valobj.GetType()) + child = child_ptr.CreateValueFromAddress( + "string_data", + child_ptr.GetValueAsUnsigned(), + valobj.GetType()).AddressOf() + provider = CFStringSynthProvider(child, dict) + if not provider.invalid: + try: + summary = provider.get_child_at_index( + provider.get_child_index("content")).GetSummary() + except: + summary = '<variable is not NSAttributedString>' + if summary is None: + summary = '<variable is not NSAttributedString>' + return '@' + summary + + +def __lldb_init_module(debugger, dict): + debugger.HandleCommand( + "type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef") + debugger.HandleCommand( + "type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString") + class CFStringSynthProvider: - def __init__(self,valobj,dict): - logger = lldb.formatters.Logger.Logger() - self.valobj = valobj; - self.update() - - # children other than "content" are for debugging only and must not be used in production code - def num_children(self): - logger = lldb.formatters.Logger.Logger() - if self.invalid: - return 0; - return 6; - - def read_unicode(self, pointer,max_len=2048): - logger = lldb.formatters.Logger.Logger() - process = self.valobj.GetTarget().GetProcess() - error = lldb.SBError() - pystr = u'' - # cannot do the read at once because the length value has - # a weird encoding. better play it safe here - while max_len > 0: - content = process.ReadMemory(pointer, 2, error) - new_bytes = bytearray(content) - b0 = new_bytes[0] - b1 = new_bytes[1] - pointer = pointer + 2 - if b0 == 0 and b1 == 0: - break - # rearrange bytes depending on endianness - # (do we really need this or is Cocoa going to - # use Windows-compatible little-endian even - # if the target is big endian?) - if self.is_little: - value = b1 * 256 + b0 - else: - value = b0 * 256 + b1 - pystr = pystr + unichr(value) - # read max_len unicode values, not max_len bytes - max_len = max_len - 1 - return pystr - - # handle the special case strings - # only use the custom code for the tested LP64 case - def handle_special(self): - logger = lldb.formatters.Logger.Logger() - if self.is_64_bit == False: - # for 32bit targets, use safe ObjC code - return self.handle_unicode_string_safe() - offset = 12 - pointer = self.valobj.GetValueAsUnsigned(0) + offset - pystr = self.read_unicode(pointer) - return self.valobj.CreateValueFromExpression("content", - "(char*)\"" + pystr.encode('utf-8') + "\"") - - # last resort call, use ObjC code to read; the final aim is to - # be able to strip this call away entirely and only do the read - # ourselves - def handle_unicode_string_safe(self): - return self.valobj.CreateValueFromExpression("content", - "(char*)\"" + self.valobj.GetObjectDescription() + "\""); - - def handle_unicode_string(self): - logger = lldb.formatters.Logger.Logger() - # step 1: find offset - if self.inline: - pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); - if self.explicit == False: - # untested, use the safe code path - return self.handle_unicode_string_safe(); - else: - # a full pointer is skipped here before getting to the live data - pointer = pointer + self.pointer_size - else: - pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base() - # read 8 bytes here and make an address out of them - try: - char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() - vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type); - pointer = vopointer.GetValueAsUnsigned(0) - except: - return self.valobj.CreateValueFromExpression("content", - '(char*)"@\"invalid NSString\""') - # step 2: read Unicode data at pointer - pystr = self.read_unicode(pointer) - # step 3: return it - return pystr.encode('utf-8') - - def handle_inline_explicit(self): - logger = lldb.formatters.Logger.Logger() - offset = 3*self.pointer_size - offset = offset + self.valobj.GetValueAsUnsigned(0) - return self.valobj.CreateValueFromExpression("content", - "(char*)(" + str(offset) + ")") - - def handle_mutable_string(self): - logger = lldb.formatters.Logger.Logger() - offset = 2 * self.pointer_size - data = self.valobj.CreateChildAtOffset("content", - offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); - data_value = data.GetValueAsUnsigned(0) - if self.explicit and self.unicode: - return self.read_unicode(data_value).encode('utf-8') - else: - data_value = data_value + 1 - return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")") - - def handle_UTF8_inline(self): - logger = lldb.formatters.Logger.Logger() - offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); - if self.explicit == False: - offset = offset + 1; - return self.valobj.CreateValueFromAddress("content", - offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf(); - - def handle_UTF8_not_inline(self): - logger = lldb.formatters.Logger.Logger() - offset = self.size_of_cfruntime_base(); - return self.valobj.CreateChildAtOffset("content", - offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); - - def get_child_at_index(self,index): - logger = lldb.formatters.Logger.Logger() - logger >> "Querying for child [" + str(index) + "]" - if index == 0: - return self.valobj.CreateValueFromExpression("mutable", - str(int(self.mutable))); - if index == 1: - return self.valobj.CreateValueFromExpression("inline", - str(int(self.inline))); - if index == 2: - return self.valobj.CreateValueFromExpression("explicit", - str(int(self.explicit))); - if index == 3: - return self.valobj.CreateValueFromExpression("unicode", - str(int(self.unicode))); - if index == 4: - return self.valobj.CreateValueFromExpression("special", - str(int(self.special))); - if index == 5: - # we are handling the several possible combinations of flags. - # for each known combination we have a function that knows how to - # go fetch the data from memory instead of running code. if a string is not - # correctly displayed, one should start by finding a combination of flags that - # makes it different from these known cases, and provide a new reader function - # if this is not possible, a new flag might have to be made up (like the "special" flag - # below, which is not a real flag in CFString), or alternatively one might need to use - # the ObjC runtime helper to detect the new class and deal with it accordingly - #print 'mutable = ' + str(self.mutable) - #print 'inline = ' + str(self.inline) - #print 'explicit = ' + str(self.explicit) - #print 'unicode = ' + str(self.unicode) - #print 'special = ' + str(self.special) - if self.mutable == True: - return self.handle_mutable_string() - elif self.inline == True and self.explicit == True and \ - self.unicode == False and self.special == False and \ - self.mutable == False: - return self.handle_inline_explicit() - elif self.unicode == True: - return self.handle_unicode_string(); - elif self.special == True: - return self.handle_special(); - elif self.inline == True: - return self.handle_UTF8_inline(); - else: - return self.handle_UTF8_not_inline(); - - def get_child_index(self,name): - logger = lldb.formatters.Logger.Logger() - logger >> "Querying for child ['" + str(name) + "']" - if name == "content": - return self.num_children() - 1; - if name == "mutable": - return 0; - if name == "inline": - return 1; - if name == "explicit": - return 2; - if name == "unicode": - return 3; - if name == "special": - return 4; - - # CFRuntimeBase is defined as having an additional - # 4 bytes (padding?) on LP64 architectures - # to get its size we add up sizeof(pointer)+4 - # and then add 4 more bytes if we are on a 64bit system - def size_of_cfruntime_base(self): - logger = lldb.formatters.Logger.Logger() - return self.pointer_size+4+(4 if self.is_64_bit else 0) - - # the info bits are part of the CFRuntimeBase structure - # to get at them we have to skip a uintptr_t and then get - # at the least-significant byte of a 4 byte array. If we are - # on big-endian this means going to byte 3, if we are on - # little endian (OSX & iOS), this means reading byte 0 - def offset_of_info_bits(self): - logger = lldb.formatters.Logger.Logger() - offset = self.pointer_size - if self.is_little == False: - offset = offset + 3; - return offset; - - def read_info_bits(self): - logger = lldb.formatters.Logger.Logger() - cfinfo = self.valobj.CreateChildAtOffset("cfinfo", - self.offset_of_info_bits(), - self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)); - cfinfo.SetFormat(11) - info = cfinfo.GetValue(); - if info != None: - self.invalid = False; - return int(info,0); - else: - self.invalid = True; - return None; - - # calculating internal flag bits of the CFString object - # this stuff is defined and discussed in CFString.c - def is_mutable(self): - logger = lldb.formatters.Logger.Logger() - return (self.info_bits & 1) == 1; - - def is_inline(self): - logger = lldb.formatters.Logger.Logger() - return (self.info_bits & 0x60) == 0; - - # this flag's name is ambiguous, it turns out - # we must skip a length byte to get at the data - # when this flag is False - def has_explicit_length(self): - logger = lldb.formatters.Logger.Logger() - return (self.info_bits & (1 | 4)) != 4; - - # probably a subclass of NSString. obtained this from [str pathExtension] - # here info_bits = 0 and Unicode data at the start of the padding word - # in the long run using the isa value might be safer as a way to identify this - # instead of reading the info_bits - def is_special_case(self): - logger = lldb.formatters.Logger.Logger() - return self.info_bits == 0; - - def is_unicode(self): - logger = lldb.formatters.Logger.Logger() - return (self.info_bits & 0x10) == 0x10; - - # preparing ourselves to read into memory - # by adjusting architecture-specific info - def adjust_for_architecture(self): - logger = lldb.formatters.Logger.Logger() - self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() - self.is_64_bit = self.pointer_size == 8 - self.is_little = self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle - - # reading info bits out of the CFString and computing - # useful values to get at the real data - def compute_flags(self): - logger = lldb.formatters.Logger.Logger() - self.info_bits = self.read_info_bits(); - if self.info_bits == None: - return; - self.mutable = self.is_mutable(); - self.inline = self.is_inline(); - self.explicit = self.has_explicit_length(); - self.unicode = self.is_unicode(); - self.special = self.is_special_case(); - - def update(self): - logger = lldb.formatters.Logger.Logger() - self.adjust_for_architecture(); - self.compute_flags(); + + def __init__(self, valobj, dict): + logger = lldb.formatters.Logger.Logger() + self.valobj = valobj + self.update() + + # children other than "content" are for debugging only and must not be + # used in production code + def num_children(self): + logger = lldb.formatters.Logger.Logger() + if self.invalid: + return 0 + return 6 + + def read_unicode(self, pointer, max_len=2048): + logger = lldb.formatters.Logger.Logger() + process = self.valobj.GetTarget().GetProcess() + error = lldb.SBError() + pystr = u'' + # cannot do the read at once because the length value has + # a weird encoding. better play it safe here + while max_len > 0: + content = process.ReadMemory(pointer, 2, error) + new_bytes = bytearray(content) + b0 = new_bytes[0] + b1 = new_bytes[1] + pointer = pointer + 2 + if b0 == 0 and b1 == 0: + break + # rearrange bytes depending on endianness + # (do we really need this or is Cocoa going to + # use Windows-compatible little-endian even + # if the target is big endian?) + if self.is_little: + value = b1 * 256 + b0 + else: + value = b0 * 256 + b1 + pystr = pystr + unichr(value) + # read max_len unicode values, not max_len bytes + max_len = max_len - 1 + return pystr + + # handle the special case strings + # only use the custom code for the tested LP64 case + def handle_special(self): + logger = lldb.formatters.Logger.Logger() + if not self.is_64_bit: + # for 32bit targets, use safe ObjC code + return self.handle_unicode_string_safe() + offset = 12 + pointer = self.valobj.GetValueAsUnsigned(0) + offset + pystr = self.read_unicode(pointer) + return self.valobj.CreateValueFromExpression( + "content", "(char*)\"" + pystr.encode('utf-8') + "\"") + + # last resort call, use ObjC code to read; the final aim is to + # be able to strip this call away entirely and only do the read + # ourselves + def handle_unicode_string_safe(self): + return self.valobj.CreateValueFromExpression( + "content", "(char*)\"" + self.valobj.GetObjectDescription() + "\"") + + def handle_unicode_string(self): + logger = lldb.formatters.Logger.Logger() + # step 1: find offset + if self.inline: + pointer = self.valobj.GetValueAsUnsigned( + 0) + self.size_of_cfruntime_base() + if not self.explicit: + # untested, use the safe code path + return self.handle_unicode_string_safe() + else: + # a full pointer is skipped here before getting to the live + # data + pointer = pointer + self.pointer_size + else: + pointer = self.valobj.GetValueAsUnsigned( + 0) + self.size_of_cfruntime_base() + # read 8 bytes here and make an address out of them + try: + char_type = self.valobj.GetType().GetBasicType( + lldb.eBasicTypeChar).GetPointerType() + vopointer = self.valobj.CreateValueFromAddress( + "dummy", pointer, char_type) + pointer = vopointer.GetValueAsUnsigned(0) + except: + return self.valobj.CreateValueFromExpression( + "content", '(char*)"@\"invalid NSString\""') + # step 2: read Unicode data at pointer + pystr = self.read_unicode(pointer) + # step 3: return it + return pystr.encode('utf-8') + + def handle_inline_explicit(self): + logger = lldb.formatters.Logger.Logger() + offset = 3 * self.pointer_size + offset = offset + self.valobj.GetValueAsUnsigned(0) + return self.valobj.CreateValueFromExpression( + "content", "(char*)(" + str(offset) + ")") + + def handle_mutable_string(self): + logger = lldb.formatters.Logger.Logger() + offset = 2 * self.pointer_size + data = self.valobj.CreateChildAtOffset( + "content", offset, self.valobj.GetType().GetBasicType( + lldb.eBasicTypeChar).GetPointerType()) + data_value = data.GetValueAsUnsigned(0) + if self.explicit and self.unicode: + return self.read_unicode(data_value).encode('utf-8') + else: + data_value = data_value + 1 + return self.valobj.CreateValueFromExpression( + "content", "(char*)(" + str(data_value) + ")") + + def handle_UTF8_inline(self): + logger = lldb.formatters.Logger.Logger() + offset = self.valobj.GetValueAsUnsigned( + 0) + self.size_of_cfruntime_base() + if not self.explicit: + offset = offset + 1 + return self.valobj.CreateValueFromAddress( + "content", offset, self.valobj.GetType().GetBasicType( + lldb.eBasicTypeChar)).AddressOf() + + def handle_UTF8_not_inline(self): + logger = lldb.formatters.Logger.Logger() + offset = self.size_of_cfruntime_base() + return self.valobj.CreateChildAtOffset( + "content", offset, self.valobj.GetType().GetBasicType( + lldb.eBasicTypeChar).GetPointerType()) + + def get_child_at_index(self, index): + logger = lldb.formatters.Logger.Logger() + logger >> "Querying for child [" + str(index) + "]" + if index == 0: + return self.valobj.CreateValueFromExpression( + "mutable", str(int(self.mutable))) + if index == 1: + return self.valobj.CreateValueFromExpression("inline", + str(int(self.inline))) + if index == 2: + return self.valobj.CreateValueFromExpression( + "explicit", str(int(self.explicit))) + if index == 3: + return self.valobj.CreateValueFromExpression( + "unicode", str(int(self.unicode))) + if index == 4: + return self.valobj.CreateValueFromExpression( + "special", str(int(self.special))) + if index == 5: + # we are handling the several possible combinations of flags. + # for each known combination we have a function that knows how to + # go fetch the data from memory instead of running code. if a string is not + # correctly displayed, one should start by finding a combination of flags that + # makes it different from these known cases, and provide a new reader function + # if this is not possible, a new flag might have to be made up (like the "special" flag + # below, which is not a real flag in CFString), or alternatively one might need to use + # the ObjC runtime helper to detect the new class and deal with it accordingly + # print 'mutable = ' + str(self.mutable) + # print 'inline = ' + str(self.inline) + # print 'explicit = ' + str(self.explicit) + # print 'unicode = ' + str(self.unicode) + # print 'special = ' + str(self.special) + if self.mutable: + return self.handle_mutable_string() + elif self.inline and self.explicit and \ + self.unicode == False and self.special == False and \ + self.mutable == False: + return self.handle_inline_explicit() + elif self.unicode: + return self.handle_unicode_string() + elif self.special: + return self.handle_special() + elif self.inline: + return self.handle_UTF8_inline() + else: + return self.handle_UTF8_not_inline() + + def get_child_index(self, name): + logger = lldb.formatters.Logger.Logger() + logger >> "Querying for child ['" + str(name) + "']" + if name == "content": + return self.num_children() - 1 + if name == "mutable": + return 0 + if name == "inline": + return 1 + if name == "explicit": + return 2 + if name == "unicode": + return 3 + if name == "special": + return 4 + + # CFRuntimeBase is defined as having an additional + # 4 bytes (padding?) on LP64 architectures + # to get its size we add up sizeof(pointer)+4 + # and then add 4 more bytes if we are on a 64bit system + def size_of_cfruntime_base(self): + logger = lldb.formatters.Logger.Logger() + return self.pointer_size + 4 + (4 if self.is_64_bit else 0) + + # the info bits are part of the CFRuntimeBase structure + # to get at them we have to skip a uintptr_t and then get + # at the least-significant byte of a 4 byte array. If we are + # on big-endian this means going to byte 3, if we are on + # little endian (OSX & iOS), this means reading byte 0 + def offset_of_info_bits(self): + logger = lldb.formatters.Logger.Logger() + offset = self.pointer_size + if not self.is_little: + offset = offset + 3 + return offset + + def read_info_bits(self): + logger = lldb.formatters.Logger.Logger() + cfinfo = self.valobj.CreateChildAtOffset( + "cfinfo", + self.offset_of_info_bits(), + self.valobj.GetType().GetBasicType( + lldb.eBasicTypeChar)) + cfinfo.SetFormat(11) + info = cfinfo.GetValue() + if info is not None: + self.invalid = False + return int(info, 0) + else: + self.invalid = True + return None + + # calculating internal flag bits of the CFString object + # this stuff is defined and discussed in CFString.c + def is_mutable(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & 1) == 1 + + def is_inline(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & 0x60) == 0 + + # this flag's name is ambiguous, it turns out + # we must skip a length byte to get at the data + # when this flag is False + def has_explicit_length(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & (1 | 4)) != 4 + + # probably a subclass of NSString. obtained this from [str pathExtension] + # here info_bits = 0 and Unicode data at the start of the padding word + # in the long run using the isa value might be safer as a way to identify this + # instead of reading the info_bits + def is_special_case(self): + logger = lldb.formatters.Logger.Logger() + return self.info_bits == 0 + + def is_unicode(self): + logger = lldb.formatters.Logger.Logger() + return (self.info_bits & 0x10) == 0x10 + + # preparing ourselves to read into memory + # by adjusting architecture-specific info + def adjust_for_architecture(self): + logger = lldb.formatters.Logger.Logger() + self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() + self.is_64_bit = self.pointer_size == 8 + self.is_little = self.valobj.GetTarget().GetProcess( + ).GetByteOrder() == lldb.eByteOrderLittle + + # reading info bits out of the CFString and computing + # useful values to get at the real data + def compute_flags(self): + logger = lldb.formatters.Logger.Logger() + self.info_bits = self.read_info_bits() + if self.info_bits is None: + return + self.mutable = self.is_mutable() + self.inline = self.is_inline() + self.explicit = self.has_explicit_length() + self.unicode = self.is_unicode() + self.special = self.is_special_case() + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.adjust_for_architecture() + self.compute_flags() |