blob: 570fd8280e0cce816c9977a81209670f1509ffb9 [file] [log] [blame]
Enrico Granata3f1052b2012-03-13 21:52:00 +00001"""
2LLDB AppKit formatters
3
4part of The LLVM Compiler Infrastructure
5This file is distributed under the University of Illinois Open Source
6License. See LICENSE.TXT for details.
7"""
Enrico Granata7a204352012-09-04 19:18:17 +00008# example synthetic children and summary provider for CFString (and related NSString class)
9# the real code is part of the LLDB core
Enrico Granataeb4a4792012-02-23 23:10:27 +000010import lldb
Enrico Granata28399ad2012-04-25 01:39:27 +000011import lldb.runtime.objc.objc_runtime
12import lldb.formatters.Logger
Enrico Granataeb4a4792012-02-23 23:10:27 +000013
14def CFString_SummaryProvider (valobj,dict):
Enrico Granata28399ad2012-04-25 01:39:27 +000015 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000016 provider = CFStringSynthProvider(valobj,dict);
17 if provider.invalid == False:
Enrico Granata3f1052b2012-03-13 21:52:00 +000018 try:
Enrico Granata86ea8d82012-03-29 01:34:34 +000019 summary = provider.get_child_at_index(provider.get_child_index("content"))
20 if type(summary) == lldb.SBValue:
21 summary = summary.GetSummary()
22 else:
23 summary = '"' + summary + '"'
Enrico Granata3f1052b2012-03-13 21:52:00 +000024 except:
25 summary = None
26 if summary == None:
27 summary = '<variable is not NSString>'
28 return '@'+summary
Enrico Granataeb4a4792012-02-23 23:10:27 +000029 return ''
30
31def CFAttributedString_SummaryProvider (valobj,dict):
Enrico Granata28399ad2012-04-25 01:39:27 +000032 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000033 offset = valobj.GetTarget().GetProcess().GetAddressByteSize()
34 pointee = valobj.GetValueAsUnsigned(0)
Enrico Granata3f1052b2012-03-13 21:52:00 +000035 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000036 if pointee != None and pointee != 0:
37 pointee = pointee + offset
38 child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType())
39 child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf()
40 provider = CFStringSynthProvider(child,dict);
41 if provider.invalid == False:
42 try:
43 summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary();
44 except:
Enrico Granata3f1052b2012-03-13 21:52:00 +000045 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000046 if summary == None:
Enrico Granata3f1052b2012-03-13 21:52:00 +000047 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000048 return '@'+summary
49
50
51def __lldb_init_module(debugger,dict):
52 debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef")
53 debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString")
54
55class CFStringSynthProvider:
56 def __init__(self,valobj,dict):
Enrico Granata28399ad2012-04-25 01:39:27 +000057 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000058 self.valobj = valobj;
59 self.update()
60
61 # children other than "content" are for debugging only and must not be used in production code
62 def num_children(self):
Enrico Granata28399ad2012-04-25 01:39:27 +000063 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000064 if self.invalid:
65 return 0;
66 return 6;
67
Enrico Granatabd5a7a92012-05-15 01:22:45 +000068 def read_unicode(self, pointer,max_len=2048):
Enrico Granata28399ad2012-04-25 01:39:27 +000069 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000070 process = self.valobj.GetTarget().GetProcess()
71 error = lldb.SBError()
72 pystr = u''
73 # cannot do the read at once because the length value has
74 # a weird encoding. better play it safe here
Enrico Granatabd5a7a92012-05-15 01:22:45 +000075 while max_len > 0:
Enrico Granataeb4a4792012-02-23 23:10:27 +000076 content = process.ReadMemory(pointer, 2, error)
77 new_bytes = bytearray(content)
78 b0 = new_bytes[0]
79 b1 = new_bytes[1]
80 pointer = pointer + 2
81 if b0 == 0 and b1 == 0:
82 break
83 # rearrange bytes depending on endianness
84 # (do we really need this or is Cocoa going to
85 # use Windows-compatible little-endian even
86 # if the target is big endian?)
87 if self.is_little:
88 value = b1 * 256 + b0
89 else:
90 value = b0 * 256 + b1
91 pystr = pystr + unichr(value)
Enrico Granatabd5a7a92012-05-15 01:22:45 +000092 # read max_len unicode values, not max_len bytes
93 max_len = max_len - 1
Enrico Granataeb4a4792012-02-23 23:10:27 +000094 return pystr
95
96 # handle the special case strings
97 # only use the custom code for the tested LP64 case
98 def handle_special(self):
Enrico Granata28399ad2012-04-25 01:39:27 +000099 logger = lldb.formatters.Logger.Logger()
Enrico Granata7bc0ec32012-02-29 03:28:49 +0000100 if self.is_64_bit == False:
Enrico Granataeb4a4792012-02-23 23:10:27 +0000101 # for 32bit targets, use safe ObjC code
102 return self.handle_unicode_string_safe()
103 offset = 12
104 pointer = self.valobj.GetValueAsUnsigned(0) + offset
105 pystr = self.read_unicode(pointer)
106 return self.valobj.CreateValueFromExpression("content",
107 "(char*)\"" + pystr.encode('utf-8') + "\"")
108
109 # last resort call, use ObjC code to read; the final aim is to
110 # be able to strip this call away entirely and only do the read
111 # ourselves
112 def handle_unicode_string_safe(self):
113 return self.valobj.CreateValueFromExpression("content",
114 "(char*)\"" + self.valobj.GetObjectDescription() + "\"");
115
116 def handle_unicode_string(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000117 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000118 # step 1: find offset
119 if self.inline:
120 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
121 if self.explicit == False:
122 # untested, use the safe code path
123 return self.handle_unicode_string_safe();
124 else:
Enrico Granata74ec8f92012-03-01 19:48:54 +0000125 # a full pointer is skipped here before getting to the live data
Enrico Granatacfdafa32012-03-05 19:56:33 +0000126 pointer = pointer + self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000127 else:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000128 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000129 # read 8 bytes here and make an address out of them
130 try:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000131 char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()
132 vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type);
133 pointer = vopointer.GetValueAsUnsigned(0)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000134 except:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000135 return self.valobj.CreateValueFromExpression("content",
Enrico Granataeb4a4792012-02-23 23:10:27 +0000136 '(char*)"@\"invalid NSString\""')
137 # step 2: read Unicode data at pointer
138 pystr = self.read_unicode(pointer)
139 # step 3: return it
Enrico Granata86ea8d82012-03-29 01:34:34 +0000140 return pystr.encode('utf-8')
Enrico Granataeb4a4792012-02-23 23:10:27 +0000141
142 def handle_inline_explicit(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000143 logger = lldb.formatters.Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000144 offset = 3*self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000145 offset = offset + self.valobj.GetValueAsUnsigned(0)
146 return self.valobj.CreateValueFromExpression("content",
147 "(char*)(" + str(offset) + ")")
148
149 def handle_mutable_string(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000150 logger = lldb.formatters.Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000151 offset = 2 * self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000152 data = self.valobj.CreateChildAtOffset("content",
153 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
154 data_value = data.GetValueAsUnsigned(0)
Enrico Granatabd5a7a92012-05-15 01:22:45 +0000155 if self.explicit and self.unicode:
156 return self.read_unicode(data_value).encode('utf-8')
157 else:
158 data_value = data_value + 1
159 return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")")
Enrico Granataeb4a4792012-02-23 23:10:27 +0000160
161 def handle_UTF8_inline(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000162 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000163 offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
164 if self.explicit == False:
165 offset = offset + 1;
166 return self.valobj.CreateValueFromAddress("content",
167 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf();
168
169 def handle_UTF8_not_inline(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000170 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000171 offset = self.size_of_cfruntime_base();
172 return self.valobj.CreateChildAtOffset("content",
173 offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
174
175 def get_child_at_index(self,index):
Enrico Granata28399ad2012-04-25 01:39:27 +0000176 logger = lldb.formatters.Logger.Logger()
Enrico Granata247bd412012-04-02 16:39:29 +0000177 logger >> "Querying for child [" + str(index) + "]"
Enrico Granataeb4a4792012-02-23 23:10:27 +0000178 if index == 0:
179 return self.valobj.CreateValueFromExpression("mutable",
180 str(int(self.mutable)));
181 if index == 1:
182 return self.valobj.CreateValueFromExpression("inline",
183 str(int(self.inline)));
184 if index == 2:
185 return self.valobj.CreateValueFromExpression("explicit",
186 str(int(self.explicit)));
187 if index == 3:
188 return self.valobj.CreateValueFromExpression("unicode",
189 str(int(self.unicode)));
190 if index == 4:
191 return self.valobj.CreateValueFromExpression("special",
192 str(int(self.special)));
193 if index == 5:
194 # we are handling the several possible combinations of flags.
195 # for each known combination we have a function that knows how to
196 # go fetch the data from memory instead of running code. if a string is not
197 # correctly displayed, one should start by finding a combination of flags that
198 # makes it different from these known cases, and provide a new reader function
199 # if this is not possible, a new flag might have to be made up (like the "special" flag
200 # below, which is not a real flag in CFString), or alternatively one might need to use
201 # the ObjC runtime helper to detect the new class and deal with it accordingly
Enrico Granata86ea8d82012-03-29 01:34:34 +0000202 #print 'mutable = ' + str(self.mutable)
203 #print 'inline = ' + str(self.inline)
204 #print 'explicit = ' + str(self.explicit)
205 #print 'unicode = ' + str(self.unicode)
206 #print 'special = ' + str(self.special)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000207 if self.mutable == True:
208 return self.handle_mutable_string()
209 elif self.inline == True and self.explicit == True and \
210 self.unicode == False and self.special == False and \
211 self.mutable == False:
212 return self.handle_inline_explicit()
213 elif self.unicode == True:
214 return self.handle_unicode_string();
215 elif self.special == True:
216 return self.handle_special();
217 elif self.inline == True:
218 return self.handle_UTF8_inline();
219 else:
220 return self.handle_UTF8_not_inline();
221
222 def get_child_index(self,name):
Enrico Granata28399ad2012-04-25 01:39:27 +0000223 logger = lldb.formatters.Logger.Logger()
Enrico Granata247bd412012-04-02 16:39:29 +0000224 logger >> "Querying for child ['" + str(name) + "']"
Enrico Granataeb4a4792012-02-23 23:10:27 +0000225 if name == "content":
226 return self.num_children() - 1;
227 if name == "mutable":
228 return 0;
229 if name == "inline":
230 return 1;
231 if name == "explicit":
232 return 2;
233 if name == "unicode":
234 return 3;
235 if name == "special":
236 return 4;
237
Enrico Granataeb4a4792012-02-23 23:10:27 +0000238 # CFRuntimeBase is defined as having an additional
239 # 4 bytes (padding?) on LP64 architectures
240 # to get its size we add up sizeof(pointer)+4
241 # and then add 4 more bytes if we are on a 64bit system
242 def size_of_cfruntime_base(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000243 logger = lldb.formatters.Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000244 return self.pointer_size+4+(4 if self.is_64_bit else 0)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000245
246 # the info bits are part of the CFRuntimeBase structure
247 # to get at them we have to skip a uintptr_t and then get
248 # at the least-significant byte of a 4 byte array. If we are
249 # on big-endian this means going to byte 3, if we are on
250 # little endian (OSX & iOS), this means reading byte 0
251 def offset_of_info_bits(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000252 logger = lldb.formatters.Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000253 offset = self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000254 if self.is_little == False:
255 offset = offset + 3;
256 return offset;
257
258 def read_info_bits(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000259 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000260 cfinfo = self.valobj.CreateChildAtOffset("cfinfo",
261 self.offset_of_info_bits(),
262 self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar));
263 cfinfo.SetFormat(11)
264 info = cfinfo.GetValue();
265 if info != None:
266 self.invalid = False;
267 return int(info,0);
268 else:
269 self.invalid = True;
270 return None;
271
272 # calculating internal flag bits of the CFString object
273 # this stuff is defined and discussed in CFString.c
274 def is_mutable(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000275 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000276 return (self.info_bits & 1) == 1;
277
278 def is_inline(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000279 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000280 return (self.info_bits & 0x60) == 0;
281
282 # this flag's name is ambiguous, it turns out
283 # we must skip a length byte to get at the data
284 # when this flag is False
285 def has_explicit_length(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000286 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000287 return (self.info_bits & (1 | 4)) != 4;
288
289 # probably a subclass of NSString. obtained this from [str pathExtension]
290 # here info_bits = 0 and Unicode data at the start of the padding word
291 # in the long run using the isa value might be safer as a way to identify this
292 # instead of reading the info_bits
293 def is_special_case(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000294 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000295 return self.info_bits == 0;
296
297 def is_unicode(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000298 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000299 return (self.info_bits & 0x10) == 0x10;
300
301 # preparing ourselves to read into memory
302 # by adjusting architecture-specific info
303 def adjust_for_architecture(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000304 logger = lldb.formatters.Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000305 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
306 self.is_64_bit = self.pointer_size == 8
307 self.is_little = self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle
Enrico Granataeb4a4792012-02-23 23:10:27 +0000308
309 # reading info bits out of the CFString and computing
310 # useful values to get at the real data
311 def compute_flags(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000312 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000313 self.info_bits = self.read_info_bits();
314 if self.info_bits == None:
315 return;
316 self.mutable = self.is_mutable();
317 self.inline = self.is_inline();
318 self.explicit = self.has_explicit_length();
319 self.unicode = self.is_unicode();
320 self.special = self.is_special_case();
321
322 def update(self):
Enrico Granata28399ad2012-04-25 01:39:27 +0000323 logger = lldb.formatters.Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000324 self.adjust_for_architecture();
Enrico Granata247bd412012-04-02 16:39:29 +0000325 self.compute_flags();