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