Home | History | Annotate | Download | only in python
      1 # This implements the "diagnose-nsstring" command, usually installed in the debug session like
      2 #   command script import lldb.diagnose
      3 # it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the
      4 # decisions it did and  providing some useful context information that can be used for improving the formatter
      5 
      6 import lldb
      7 
      8 def read_memory(process,location,size):
      9 	data = ""
     10 	error = lldb.SBError()
     11 	for x in range(0,size-1):
     12 		byte = process.ReadUnsignedFromMemory(x+location,1,error)
     13 		if error.fail:
     14 			data = data + "err%s" % "" if x == size-2 else ":"
     15 		else:
     16 			try:
     17 				data = data + "0x%x" % byte
     18 				if byte == 0:
     19 					data = data + "(\\0)"
     20 				elif byte == 0xa:
     21 					data = data + "(\\a)"
     22 				elif byte == 0xb:
     23 					data = data + "(\\b)"
     24 				elif byte == 0xc:
     25 					data = data + "(\\c)"
     26 				elif byte == '\n':
     27 					data = data + "(\\n)"
     28 				else:
     29 					data = data + "(%s)" % chr(byte)
     30 				if x < size-2:
     31 					data = data + ":"
     32 			except Exception as e:
     33 				print e
     34 	return data
     35 
     36 def diagnose_nsstring_Command_Impl(debugger,command,result,internal_dict):
     37 	"""
     38 	A command to diagnose the LLDB NSString data formatter
     39 	invoke as
     40 	(lldb) diagnose-nsstring <expr returning NSString>
     41 	e.g.
     42 	(lldb) diagnose-nsstring @"Hello world"
     43 	"""
     44 	target = debugger.GetSelectedTarget()
     45 	process = target.GetProcess()
     46 	thread = process.GetSelectedThread()
     47 	frame = thread.GetSelectedFrame()
     48 	if not target.IsValid() or not process.IsValid():
     49 		return "unable to get target/process - cannot proceed"
     50 	options = lldb.SBExpressionOptions()
     51 	options.SetFetchDynamicValue()
     52 	error = lldb.SBError()
     53 	if frame.IsValid():
     54 		nsstring = frame.EvaluateExpression(command,options)
     55 	else:
     56 		nsstring = target.EvaluateExpression(command,options)
     57 	print >>result,str(nsstring)
     58 	nsstring_address = nsstring.GetValueAsUnsigned(0)
     59 	if nsstring_address == 0:
     60 		return "unable to obtain the string - cannot proceed"
     61 	expression = "\
     62 struct $__lldb__notInlineMutable {\
     63     char* buffer;\
     64     signed long length;\
     65     signed long capacity;\
     66     unsigned int hasGap:1;\
     67     unsigned int isFixedCapacity:1;\
     68     unsigned int isExternalMutable:1;\
     69     unsigned int capacityProvidedExternally:1;\n\
     70 #if __LP64__\n\
     71     unsigned long desiredCapacity:60;\n\
     72 #else\n\
     73     unsigned long desiredCapacity:28;\n\
     74 #endif\n\
     75     void* contentsAllocator;\
     76 };\
     77 \
     78 struct $__lldb__CFString {\
     79     void* _cfisa;\
     80     uint8_t _cfinfo[4];\
     81     uint32_t _rc;\
     82     union {\
     83         struct __inline1 {\
     84             signed long length;\
     85         } inline1;\
     86         struct __notInlineImmutable1 {\
     87             char* buffer;\
     88             signed long length;\
     89             void* contentsDeallocator;\
     90         } notInlineImmutable1;\
     91         struct __notInlineImmutable2 {\
     92             char* buffer;\
     93             void* contentsDeallocator;\
     94         } notInlineImmutable2;\
     95         struct $__lldb__notInlineMutable notInlineMutable;\
     96     } variants;\
     97 };\
     98 "
     99 
    100 	expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address
    101 	# print expression
    102 	dumped = target.EvaluateExpression(expression,options)
    103 	print >>result, str(dumped)
    104 	
    105 	little_endian = (target.byte_order == lldb.eByteOrderLittle)
    106 	ptr_size = target.addr_size
    107 	
    108 	info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(0 if little_endian else 3).GetValueAsUnsigned(0)
    109 	is_mutable = (info_bits & 1) == 1
    110 	is_inline = (info_bits & 0x60) == 0
    111 	has_explicit_length = (info_bits & (1 | 4)) != 4
    112 	is_unicode = (info_bits & 0x10) == 0x10
    113 	is_special = (nsstring.GetDynamicValue(lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2")
    114 	has_null = (info_bits & 8) == 8
    115     
    116 	print >>result,"\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \
    117 		(info_bits, "yes" if is_mutable else "no","yes" if is_inline else "no","yes" if has_explicit_length else "no","yes" if is_unicode else "no","yes" if is_special else "no","yes" if has_null else "no")
    118 
    119 
    120 	explicit_length_offset = 0
    121 	if not has_null and has_explicit_length and not is_special:
    122 		explicit_length_offset = 2*ptr_size
    123 		if is_mutable and not is_inline:
    124 			explicit_length_offset = explicit_length_offset + ptr_size
    125 		elif is_inline:
    126 			pass
    127 		elif not is_inline and not is_mutable:
    128 			explicit_length_offset = explicit_length_offset + ptr_size
    129 		else:
    130 			explicit_length_offset = 0
    131 
    132 	if explicit_length_offset == 0:
    133 		print >>result,"There is no explicit length marker - skipping this step\n"
    134 	else:
    135 		explicit_length_offset = nsstring_address + explicit_length_offset
    136 		explicit_length = process.ReadUnsignedFromMemory(explicit_length_offset, 4, error)
    137 		print >>result,"Explicit length location is at 0x%x - read value is %d\n" % (explicit_length_offset,explicit_length)
    138 
    139 	if is_mutable:
    140 		location = 2 * ptr_size + nsstring_address
    141 		location = process.ReadPointerFromMemory(location,error)
    142 	elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable:
    143 		location = 3 * ptr_size + nsstring_address
    144 	elif is_unicode:
    145 		location = 2 * ptr_size + nsstring_address
    146 		if is_inline:
    147 			if not has_explicit_length:
    148 				print >>result,"Unicode & Inline & !Explicit is a new combo - no formula for it"
    149 			else:
    150 				location += ptr_size
    151 		else:
    152 			location = process.ReadPointerFromMemory(location,error)
    153 	elif is_special:
    154 		location = nsstring_address + ptr_size + 4
    155 	elif is_inline:
    156 		location = 2 * ptr_size + nsstring_address
    157 		if not has_explicit_length:
    158 			location += 1
    159 	else:
    160 		location = 2 * ptr_size + nsstring_address
    161 		location = process.ReadPointerFromMemory(location,error)
    162 	print >>result,"Expected data location: 0x%x\n" % (location)
    163 	print >>result,"1K of data around location: %s\n" % read_memory(process,location,1024)
    164 	print >>result,"5K of data around string pointer: %s\n" % read_memory(process,nsstring_address,1024*5)
    165 
    166 def __lldb_init_module(debugger, internal_dict):
    167 	debugger.HandleCommand("command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" % __name__)
    168 	print 'The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.'
    169 
    170 __lldb_init_module(lldb.debugger,None)
    171 __lldb_init_module = None