1from __future__ import division 2import re 3import lldb.formatters.Logger 4 5# C++ STL formatters for LLDB 6# These formatters are based upon the version of the GNU libstdc++ 7# as it ships with Mac OS X 10.6.8 thru 10.8.0 8# You are encouraged to look at the STL implementation for your platform 9# before relying on these formatters to do the right thing for your setup 10 11 12class StdListSynthProvider: 13 14 def __init__(self, valobj, dict): 15 logger = lldb.formatters.Logger.Logger() 16 self.valobj = valobj 17 self.count = None 18 logger >> "Providing synthetic children for a list named " + \ 19 str(valobj.GetName()) 20 21 def next_node(self, node): 22 logger = lldb.formatters.Logger.Logger() 23 return node.GetChildMemberWithName('_M_next') 24 25 def is_valid(self, node): 26 logger = lldb.formatters.Logger.Logger() 27 valid = self.value(self.next_node(node)) != self.node_address 28 if valid: 29 logger >> "%s is valid" % str(self.valobj.GetName()) 30 else: 31 logger >> "synthetic value is not valid" 32 return valid 33 34 def value(self, node): 35 logger = lldb.formatters.Logger.Logger() 36 value = node.GetValueAsUnsigned() 37 logger >> "synthetic value for {}: {}".format( 38 str(self.valobj.GetName()), value) 39 return value 40 41 # Floyd's cycle-finding algorithm 42 # try to detect if this list has a loop 43 def has_loop(self): 44 global _list_uses_loop_detector 45 logger = lldb.formatters.Logger.Logger() 46 if not _list_uses_loop_detector: 47 logger >> "Asked not to use loop detection" 48 return False 49 slow = self.next 50 fast1 = self.next 51 fast2 = self.next 52 while self.is_valid(slow): 53 slow_value = self.value(slow) 54 fast1 = self.next_node(fast2) 55 fast2 = self.next_node(fast1) 56 if self.value(fast1) == slow_value or self.value( 57 fast2) == slow_value: 58 return True 59 slow = self.next_node(slow) 60 return False 61 62 def num_children(self): 63 logger = lldb.formatters.Logger.Logger() 64 if self.count is None: 65 # libstdc++ 6.0.21 added dedicated count field. 66 count_child = self.node.GetChildMemberWithName('_M_data') 67 if count_child and count_child.IsValid(): 68 self.count = count_child.GetValueAsUnsigned(0) 69 if self.count is None: 70 self.count = self.num_children_impl() 71 return self.count 72 73 def num_children_impl(self): 74 logger = lldb.formatters.Logger.Logger() 75 try: 76 next_val = self.next.GetValueAsUnsigned(0) 77 prev_val = self.prev.GetValueAsUnsigned(0) 78 # After a std::list has been initialized, both next and prev will 79 # be non-NULL 80 if next_val == 0 or prev_val == 0: 81 return 0 82 if next_val == self.node_address: 83 return 0 84 if next_val == prev_val: 85 return 1 86 if self.has_loop(): 87 return 0 88 size = 2 89 current = self.next 90 while current.GetChildMemberWithName( 91 '_M_next').GetValueAsUnsigned(0) != self.node_address: 92 size = size + 1 93 current = current.GetChildMemberWithName('_M_next') 94 return (size - 1) 95 except: 96 return 0 97 98 def get_child_index(self, name): 99 logger = lldb.formatters.Logger.Logger() 100 try: 101 return int(name.lstrip('[').rstrip(']')) 102 except: 103 return -1 104 105 def get_child_at_index(self, index): 106 logger = lldb.formatters.Logger.Logger() 107 logger >> "Fetching child " + str(index) 108 if index < 0: 109 return None 110 if index >= self.num_children(): 111 return None 112 try: 113 offset = index 114 current = self.next 115 while offset > 0: 116 current = current.GetChildMemberWithName('_M_next') 117 offset = offset - 1 118 return current.CreateChildAtOffset( 119 '[' + str(index) + ']', 120 2 * current.GetType().GetByteSize(), 121 self.data_type) 122 except: 123 return None 124 125 def extract_type(self): 126 logger = lldb.formatters.Logger.Logger() 127 list_type = self.valobj.GetType().GetUnqualifiedType() 128 if list_type.IsReferenceType(): 129 list_type = list_type.GetDereferencedType() 130 if list_type.GetNumberOfTemplateArguments() > 0: 131 data_type = list_type.GetTemplateArgumentType(0) 132 else: 133 data_type = None 134 return data_type 135 136 def update(self): 137 logger = lldb.formatters.Logger.Logger() 138 # preemptively setting this to None - we might end up changing our mind 139 # later 140 self.count = None 141 try: 142 impl = self.valobj.GetChildMemberWithName('_M_impl') 143 self.node = impl.GetChildMemberWithName('_M_node') 144 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) 145 self.next = self.node.GetChildMemberWithName('_M_next') 146 self.prev = self.node.GetChildMemberWithName('_M_prev') 147 self.data_type = self.extract_type() 148 self.data_size = self.data_type.GetByteSize() 149 except: 150 pass 151 152 def has_children(self): 153 return True 154 155 156class StdVectorSynthProvider: 157 158 class StdVectorImplementation(object): 159 160 def __init__(self, valobj): 161 self.valobj = valobj 162 self.count = None 163 164 def num_children(self): 165 if self.count is None: 166 self.count = self.num_children_impl() 167 return self.count 168 169 def num_children_impl(self): 170 try: 171 start_val = self.start.GetValueAsUnsigned(0) 172 finish_val = self.finish.GetValueAsUnsigned(0) 173 end_val = self.end.GetValueAsUnsigned(0) 174 # Before a vector has been constructed, it will contain bad values 175 # so we really need to be careful about the length we return since 176 # uninitialized data can cause us to return a huge number. We need 177 # to also check for any of the start, finish or end of storage values 178 # being zero (NULL). If any are, then this vector has not been 179 # initialized yet and we should return zero 180 181 # Make sure nothing is NULL 182 if start_val == 0 or finish_val == 0 or end_val == 0: 183 return 0 184 # Make sure start is less than finish 185 if start_val >= finish_val: 186 return 0 187 # Make sure finish is less than or equal to end of storage 188 if finish_val > end_val: 189 return 0 190 191 # if we have a struct (or other data type that the compiler pads to native word size) 192 # this check might fail, unless the sizeof() we get is itself incremented to take the 193 # padding bytes into account - on current clang it looks like 194 # this is the case 195 num_children = (finish_val - start_val) 196 if (num_children % self.data_size) != 0: 197 return 0 198 else: 199 num_children = num_children // self.data_size 200 return num_children 201 except: 202 return 0 203 204 def get_child_at_index(self, index): 205 logger = lldb.formatters.Logger.Logger() 206 logger >> "Retrieving child " + str(index) 207 if index < 0: 208 return None 209 if index >= self.num_children(): 210 return None 211 try: 212 offset = index * self.data_size 213 return self.start.CreateChildAtOffset( 214 '[' + str(index) + ']', offset, self.data_type) 215 except: 216 return None 217 218 def update(self): 219 # preemptively setting this to None - we might end up changing our 220 # mind later 221 self.count = None 222 try: 223 impl = self.valobj.GetChildMemberWithName('_M_impl') 224 self.start = impl.GetChildMemberWithName('_M_start') 225 self.finish = impl.GetChildMemberWithName('_M_finish') 226 self.end = impl.GetChildMemberWithName('_M_end_of_storage') 227 self.data_type = self.start.GetType().GetPointeeType() 228 self.data_size = self.data_type.GetByteSize() 229 # if any of these objects is invalid, it means there is no 230 # point in trying to fetch anything 231 if self.start.IsValid() and self.finish.IsValid( 232 ) and self.end.IsValid() and self.data_type.IsValid(): 233 self.count = None 234 else: 235 self.count = 0 236 except: 237 pass 238 return True 239 240 class StdVBoolImplementation(object): 241 242 def __init__(self, valobj, bool_type): 243 self.valobj = valobj 244 self.bool_type = bool_type 245 self.valid = False 246 247 def num_children(self): 248 if self.valid: 249 start = self.start_p.GetValueAsUnsigned(0) 250 finish = self.finish_p.GetValueAsUnsigned(0) 251 offset = self.offset.GetValueAsUnsigned(0) 252 if finish >= start: 253 return (finish - start) * 8 + offset 254 return 0 255 256 def get_child_at_index(self, index): 257 if index >= self.num_children(): 258 return None 259 element_type = self.start_p.GetType().GetPointeeType() 260 element_bits = 8 * element_type.GetByteSize() 261 element_offset = (index // element_bits) * \ 262 element_type.GetByteSize() 263 bit_offset = index % element_bits 264 element = self.start_p.CreateChildAtOffset( 265 '[' + str(index) + ']', element_offset, element_type) 266 bit = element.GetValueAsUnsigned(0) & (1 << bit_offset) 267 if bit != 0: 268 value_expr = "(bool)true" 269 else: 270 value_expr = "(bool)false" 271 return self.valobj.CreateValueFromExpression( 272 "[%d]" % index, value_expr) 273 274 def update(self): 275 try: 276 m_impl = self.valobj.GetChildMemberWithName('_M_impl') 277 self.m_start = m_impl.GetChildMemberWithName('_M_start') 278 self.m_finish = m_impl.GetChildMemberWithName('_M_finish') 279 self.start_p = self.m_start.GetChildMemberWithName('_M_p') 280 self.finish_p = self.m_finish.GetChildMemberWithName('_M_p') 281 self.offset = self.m_finish.GetChildMemberWithName('_M_offset') 282 self.valid = True 283 except: 284 self.valid = False 285 return True 286 287 def __init__(self, valobj, dict): 288 logger = lldb.formatters.Logger.Logger() 289 first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0) 290 if str(first_template_arg_type.GetName()) == "bool": 291 self.impl = self.StdVBoolImplementation( 292 valobj, first_template_arg_type) 293 else: 294 self.impl = self.StdVectorImplementation(valobj) 295 logger >> "Providing synthetic children for a vector named " + \ 296 str(valobj.GetName()) 297 298 def num_children(self): 299 return self.impl.num_children() 300 301 def get_child_index(self, name): 302 try: 303 return int(name.lstrip('[').rstrip(']')) 304 except: 305 return -1 306 307 def get_child_at_index(self, index): 308 return self.impl.get_child_at_index(index) 309 310 def update(self): 311 return self.impl.update() 312 313 def has_children(self): 314 return True 315 316 317class StdMapSynthProvider: 318 319 def __init__(self, valobj, dict): 320 logger = lldb.formatters.Logger.Logger() 321 self.valobj = valobj 322 self.count = None 323 logger >> "Providing synthetic children for a map named " + \ 324 str(valobj.GetName()) 325 326 # we need this function as a temporary workaround for rdar://problem/10801549 327 # which prevents us from extracting the std::pair<K,V> SBType out of the template 328 # arguments for _Rep_Type _M_t in the map itself - because we have to make up the 329 # typename and then find it, we may hit the situation were std::string has multiple 330 # names but only one is actually referenced in the debug information. hence, we need 331 # to replace the longer versions of std::string with the shorter one in order to be able 332 # to find the type name 333 def fixup_class_name(self, class_name): 334 logger = lldb.formatters.Logger.Logger() 335 if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >': 336 return 'std::basic_string<char>', True 337 if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >': 338 return 'std::basic_string<char>', True 339 if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >': 340 return 'std::basic_string<char>', True 341 if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >': 342 return 'std::basic_string<char>', True 343 return class_name, False 344 345 def update(self): 346 logger = lldb.formatters.Logger.Logger() 347 # preemptively setting this to None - we might end up changing our mind 348 # later 349 self.count = None 350 try: 351 # we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree 352 # if this gets set to True, then we will merrily return None for 353 # any child from that moment on 354 self.garbage = False 355 self.Mt = self.valobj.GetChildMemberWithName('_M_t') 356 self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl') 357 self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header') 358 359 map_type = self.valobj.GetType() 360 if map_type.IsReferenceType(): 361 logger >> "Dereferencing type" 362 map_type = map_type.GetDereferencedType() 363 364 # Get the type of std::pair<key, value>. It is the first template 365 # argument type of the 4th template argument to std::map. 366 allocator_type = map_type.GetTemplateArgumentType(3) 367 self.data_type = allocator_type.GetTemplateArgumentType(0) 368 if not self.data_type: 369 # GCC does not emit DW_TAG_template_type_parameter for 370 # std::allocator<...>. For such a case, get the type of 371 # std::pair from a member of std::map. 372 rep_type = self.valobj.GetChildMemberWithName('_M_t').GetType() 373 self.data_type = rep_type.GetTypedefedType().GetTemplateArgumentType(1) 374 375 # from libstdc++ implementation of _M_root for rbtree 376 self.Mroot = self.Mheader.GetChildMemberWithName('_M_parent') 377 self.data_size = self.data_type.GetByteSize() 378 self.skip_size = self.Mheader.GetType().GetByteSize() 379 except: 380 pass 381 382 def num_children(self): 383 logger = lldb.formatters.Logger.Logger() 384 if self.count is None: 385 self.count = self.num_children_impl() 386 return self.count 387 388 def num_children_impl(self): 389 logger = lldb.formatters.Logger.Logger() 390 try: 391 root_ptr_val = self.node_ptr_value(self.Mroot) 392 if root_ptr_val == 0: 393 return 0 394 count = self.Mimpl.GetChildMemberWithName( 395 '_M_node_count').GetValueAsUnsigned(0) 396 logger >> "I have " + str(count) + " children available" 397 return count 398 except: 399 return 0 400 401 def get_child_index(self, name): 402 logger = lldb.formatters.Logger.Logger() 403 try: 404 return int(name.lstrip('[').rstrip(']')) 405 except: 406 return -1 407 408 def get_child_at_index(self, index): 409 logger = lldb.formatters.Logger.Logger() 410 logger >> "Being asked to fetch child[" + str(index) + "]" 411 if index < 0: 412 return None 413 if index >= self.num_children(): 414 return None 415 if self.garbage: 416 logger >> "Returning None since we are a garbage tree" 417 return None 418 try: 419 offset = index 420 current = self.left(self.Mheader) 421 while offset > 0: 422 current = self.increment_node(current) 423 offset = offset - 1 424 # skip all the base stuff and get at the data 425 return current.CreateChildAtOffset( 426 '[' + str(index) + ']', self.skip_size, self.data_type) 427 except: 428 return None 429 430 # utility functions 431 def node_ptr_value(self, node): 432 logger = lldb.formatters.Logger.Logger() 433 return node.GetValueAsUnsigned(0) 434 435 def right(self, node): 436 logger = lldb.formatters.Logger.Logger() 437 return node.GetChildMemberWithName("_M_right") 438 439 def left(self, node): 440 logger = lldb.formatters.Logger.Logger() 441 return node.GetChildMemberWithName("_M_left") 442 443 def parent(self, node): 444 logger = lldb.formatters.Logger.Logger() 445 return node.GetChildMemberWithName("_M_parent") 446 447 # from libstdc++ implementation of iterator for rbtree 448 def increment_node(self, node): 449 logger = lldb.formatters.Logger.Logger() 450 max_steps = self.num_children() 451 if self.node_ptr_value(self.right(node)) != 0: 452 x = self.right(node) 453 max_steps -= 1 454 while self.node_ptr_value(self.left(x)) != 0: 455 x = self.left(x) 456 max_steps -= 1 457 logger >> str(max_steps) + " more to go before giving up" 458 if max_steps <= 0: 459 self.garbage = True 460 return None 461 return x 462 else: 463 x = node 464 y = self.parent(x) 465 max_steps -= 1 466 while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))): 467 x = y 468 y = self.parent(y) 469 max_steps -= 1 470 logger >> str(max_steps) + " more to go before giving up" 471 if max_steps <= 0: 472 self.garbage = True 473 return None 474 if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y): 475 x = y 476 return x 477 478 def has_children(self): 479 return True 480 481_list_uses_loop_detector = True 482