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