blob: aca5c7f220fc9a91fc48778eed58b3d7341ade18 [file] [log] [blame]
Enrico Granataa5c03082013-05-30 23:36:47 +00001# 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