You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			352 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| LLDB AppKit formatters
 | |
| 
 | |
| part of The LLVM Compiler Infrastructure
 | |
| This file is distributed under the University of Illinois Open Source
 | |
| License. See LICENSE.TXT for details.
 | |
| """
 | |
| # example synthetic children and summary provider for CFString (and related NSString class)
 | |
| # the real code is part of the LLDB core
 | |
| 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 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 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()
 |