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 6import lldb 7 8def 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 36def 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 = "\ 62struct $__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\ 78struct $__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 166def __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