1061da546Spatrick# This implements the "diagnose-nsstring" command, usually installed in the debug session like 2061da546Spatrick# command script import lldb.diagnose 3061da546Spatrick# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the 4061da546Spatrick# decisions it did and providing some useful context information that can 5061da546Spatrick# be used for improving the formatter 6061da546Spatrick 7061da546Spatrickimport lldb 8061da546Spatrick 9061da546Spatrick 10061da546Spatrickdef read_memory(process, location, size): 11061da546Spatrick data = "" 12061da546Spatrick error = lldb.SBError() 13061da546Spatrick for x in range(0, size - 1): 14061da546Spatrick byte = process.ReadUnsignedFromMemory(x + location, 1, error) 15061da546Spatrick if error.fail: 16061da546Spatrick data = data + "err%s" % "" if x == size - 2 else ":" 17061da546Spatrick else: 18061da546Spatrick try: 19061da546Spatrick data = data + "0x%x" % byte 20061da546Spatrick if byte == 0: 21061da546Spatrick data = data + "(\\0)" 22061da546Spatrick elif byte == 0xa: 23061da546Spatrick data = data + "(\\a)" 24061da546Spatrick elif byte == 0xb: 25061da546Spatrick data = data + "(\\b)" 26061da546Spatrick elif byte == 0xc: 27061da546Spatrick data = data + "(\\c)" 28061da546Spatrick elif byte == '\n': 29061da546Spatrick data = data + "(\\n)" 30061da546Spatrick else: 31061da546Spatrick data = data + "(%s)" % chr(byte) 32061da546Spatrick if x < size - 2: 33061da546Spatrick data = data + ":" 34061da546Spatrick except Exception as e: 35061da546Spatrick print(e) 36061da546Spatrick return data 37061da546Spatrick 38061da546Spatrick 39061da546Spatrickdef diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict): 40061da546Spatrick """ 41061da546Spatrick A command to diagnose the LLDB NSString data formatter 42061da546Spatrick invoke as 43061da546Spatrick (lldb) diagnose-nsstring <expr returning NSString> 44061da546Spatrick e.g. 45061da546Spatrick (lldb) diagnose-nsstring @"Hello world" 46061da546Spatrick """ 47061da546Spatrick target = debugger.GetSelectedTarget() 48061da546Spatrick process = target.GetProcess() 49061da546Spatrick thread = process.GetSelectedThread() 50061da546Spatrick frame = thread.GetSelectedFrame() 51061da546Spatrick if not target.IsValid() or not process.IsValid(): 52061da546Spatrick return "unable to get target/process - cannot proceed" 53061da546Spatrick options = lldb.SBExpressionOptions() 54061da546Spatrick options.SetFetchDynamicValue() 55061da546Spatrick error = lldb.SBError() 56061da546Spatrick if frame.IsValid(): 57061da546Spatrick nsstring = frame.EvaluateExpression(command, options) 58061da546Spatrick else: 59061da546Spatrick nsstring = target.EvaluateExpression(command, options) 60061da546Spatrick print(str(nsstring), file=result) 61061da546Spatrick nsstring_address = nsstring.GetValueAsUnsigned(0) 62061da546Spatrick if nsstring_address == 0: 63061da546Spatrick return "unable to obtain the string - cannot proceed" 64061da546Spatrick expression = "\ 65061da546Spatrickstruct $__lldb__notInlineMutable {\ 66061da546Spatrick char* buffer;\ 67061da546Spatrick signed long length;\ 68061da546Spatrick signed long capacity;\ 69061da546Spatrick unsigned int hasGap:1;\ 70061da546Spatrick unsigned int isFixedCapacity:1;\ 71061da546Spatrick unsigned int isExternalMutable:1;\ 72061da546Spatrick unsigned int capacityProvidedExternally:1;\n\ 73061da546Spatrick#if __LP64__\n\ 74061da546Spatrick unsigned long desiredCapacity:60;\n\ 75061da546Spatrick#else\n\ 76061da546Spatrick unsigned long desiredCapacity:28;\n\ 77061da546Spatrick#endif\n\ 78061da546Spatrick void* contentsAllocator;\ 79061da546Spatrick};\ 80061da546Spatrick\ 81061da546Spatrickstruct $__lldb__CFString {\ 82061da546Spatrick void* _cfisa;\ 83061da546Spatrick uint8_t _cfinfo[4];\ 84061da546Spatrick uint32_t _rc;\ 85061da546Spatrick union {\ 86061da546Spatrick struct __inline1 {\ 87061da546Spatrick signed long length;\ 88061da546Spatrick } inline1;\ 89061da546Spatrick struct __notInlineImmutable1 {\ 90061da546Spatrick char* buffer;\ 91061da546Spatrick signed long length;\ 92061da546Spatrick void* contentsDeallocator;\ 93061da546Spatrick } notInlineImmutable1;\ 94061da546Spatrick struct __notInlineImmutable2 {\ 95061da546Spatrick char* buffer;\ 96061da546Spatrick void* contentsDeallocator;\ 97061da546Spatrick } notInlineImmutable2;\ 98061da546Spatrick struct $__lldb__notInlineMutable notInlineMutable;\ 99061da546Spatrick } variants;\ 100061da546Spatrick};\ 101061da546Spatrick" 102061da546Spatrick 103061da546Spatrick expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address 104061da546Spatrick # print expression 105061da546Spatrick dumped = target.EvaluateExpression(expression, options) 106061da546Spatrick print(str(dumped), file=result) 107061da546Spatrick 108061da546Spatrick little_endian = (target.byte_order == lldb.eByteOrderLittle) 109061da546Spatrick ptr_size = target.addr_size 110061da546Spatrick 111061da546Spatrick info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex( 112061da546Spatrick 0 if little_endian else 3).GetValueAsUnsigned(0) 113061da546Spatrick is_mutable = (info_bits & 1) == 1 114061da546Spatrick is_inline = (info_bits & 0x60) == 0 115061da546Spatrick has_explicit_length = (info_bits & (1 | 4)) != 4 116061da546Spatrick is_unicode = (info_bits & 0x10) == 0x10 117061da546Spatrick is_special = ( 118061da546Spatrick nsstring.GetDynamicValue( 119061da546Spatrick lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2") 120061da546Spatrick has_null = (info_bits & 8) == 8 121061da546Spatrick 122061da546Spatrick print("\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \ 123061da546Spatrick (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"), file=result) 124061da546Spatrick 125061da546Spatrick explicit_length_offset = 0 126061da546Spatrick if not has_null and has_explicit_length and not is_special: 127061da546Spatrick explicit_length_offset = 2 * ptr_size 128061da546Spatrick if is_mutable and not is_inline: 129061da546Spatrick explicit_length_offset = explicit_length_offset + ptr_size 130061da546Spatrick elif is_inline: 131061da546Spatrick pass 132061da546Spatrick elif not is_inline and not is_mutable: 133061da546Spatrick explicit_length_offset = explicit_length_offset + ptr_size 134061da546Spatrick else: 135061da546Spatrick explicit_length_offset = 0 136061da546Spatrick 137061da546Spatrick if explicit_length_offset == 0: 138061da546Spatrick print("There is no explicit length marker - skipping this step\n", file=result) 139061da546Spatrick else: 140061da546Spatrick explicit_length_offset = nsstring_address + explicit_length_offset 141061da546Spatrick explicit_length = process.ReadUnsignedFromMemory( 142061da546Spatrick explicit_length_offset, 4, error) 143061da546Spatrick print("Explicit length location is at 0x%x - read value is %d\n" % ( 144061da546Spatrick explicit_length_offset, explicit_length), file=result) 145061da546Spatrick 146061da546Spatrick if is_mutable: 147061da546Spatrick location = 2 * ptr_size + nsstring_address 148061da546Spatrick location = process.ReadPointerFromMemory(location, error) 149061da546Spatrick elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable: 150061da546Spatrick location = 3 * ptr_size + nsstring_address 151061da546Spatrick elif is_unicode: 152061da546Spatrick location = 2 * ptr_size + nsstring_address 153061da546Spatrick if is_inline: 154061da546Spatrick if not has_explicit_length: 155061da546Spatrick print("Unicode & Inline & !Explicit is a new combo - no formula for it", file=result) 156061da546Spatrick else: 157061da546Spatrick location += ptr_size 158061da546Spatrick else: 159061da546Spatrick location = process.ReadPointerFromMemory(location, error) 160061da546Spatrick elif is_special: 161061da546Spatrick location = nsstring_address + ptr_size + 4 162061da546Spatrick elif is_inline: 163061da546Spatrick location = 2 * ptr_size + nsstring_address 164061da546Spatrick if not has_explicit_length: 165061da546Spatrick location += 1 166061da546Spatrick else: 167061da546Spatrick location = 2 * ptr_size + nsstring_address 168061da546Spatrick location = process.ReadPointerFromMemory(location, error) 169061da546Spatrick print("Expected data location: 0x%x\n" % (location), file=result) 170061da546Spatrick print("1K of data around location: %s\n" % read_memory( 171061da546Spatrick process, location, 1024), file=result) 172061da546Spatrick print("5K of data around string pointer: %s\n" % read_memory( 173061da546Spatrick process, nsstring_address, 1024 * 5), file=result) 174061da546Spatrick 175061da546Spatrick 176061da546Spatrickdef __lldb_init_module(debugger, internal_dict): 177061da546Spatrick debugger.HandleCommand( 178*f6aab3d8Srobert "command script add -o -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" % 179061da546Spatrick __name__) 180061da546Spatrick print('The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.') 181061da546Spatrick 182061da546Spatrick__lldb_init_module(lldb.debugger, None) 183061da546Spatrick__lldb_init_module = None 184