Home | History | Annotate | Download | only in synthetic
      1 import lldb
      2 import lldb.formatters.Logger
      3 
      4 # libcxx STL formatters for LLDB
      5 # These formatters are based upon the implementation of libc++ that
      6 # ships with current releases of OS X - They will not work for other implementations
      7 # of the standard C++ library - and they are bound to use the libc++-specific namespace
      8 
      9 # the std::string summary is just an example for your convenience
     10 # the actual summary that LLDB uses is C++ code inside the debugger's own core
     11 
     12 # this could probably be made more efficient but since it only reads a handful of bytes at a time
     13 # we probably don't need to worry too much about this for the time being
     14 def make_string(F,L):
     15 	strval = ''
     16 	G = F.GetData().uint8
     17 	for X in range(L):
     18 		V = G[X]
     19 		if V == 0:
     20 			break
     21 		strval = strval + chr(V % 256)
     22 	return '"' + strval + '"'
     23 
     24 # if we ever care about big-endian, these two functions might need to change
     25 def is_short_string(value):
     26 	return True if (value & 1) == 0 else False
     27 def extract_short_size(value):
     28 	return ((value >> 1) % 256)
     29 
     30 # some of the members of libc++ std::string are anonymous or have internal names that convey
     31 # no external significance - we access them by index since this saves a name lookup that would add
     32 # no information for readers of the code, but when possible try to use meaningful variable names
     33 def stdstring_SummaryProvider(valobj,dict):
     34 	logger = lldb.formatters.Logger.Logger()
     35 	r = valobj.GetChildAtIndex(0)
     36 	B = r.GetChildAtIndex(0)
     37 	first = B.GetChildAtIndex(0)
     38 	D = first.GetChildAtIndex(0)
     39 	l = D.GetChildAtIndex(0)
     40 	s = D.GetChildAtIndex(1)
     41 	D20 = s.GetChildAtIndex(0)
     42 	size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)
     43 	if is_short_string(size_mode):
     44 		size = extract_short_size(size_mode)
     45 		return make_string(s.GetChildAtIndex(1),size)
     46 	else:
     47 		data_ptr = l.GetChildAtIndex(2)
     48 		size_vo = l.GetChildAtIndex(1)
     49 		size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
     50 		if size <= 1 or size == None: # should never be the case
     51 			return '""'
     52 		try:
     53 			data = data_ptr.GetPointeeData(0,size)
     54 		except:
     55 			return '""'
     56 		error = lldb.SBError()
     57 		strval = data.GetString(error,0)
     58 		if error.Fail():
     59 			return '<error:' + error.GetCString() + '>'
     60 		else:
     61 			return '"' + strval + '"'
     62 
     63 class stdvector_SynthProvider:
     64 
     65 	def __init__(self, valobj, dict):
     66 		logger = lldb.formatters.Logger.Logger()
     67 		self.valobj = valobj;
     68 
     69 	def num_children(self):
     70 		logger = lldb.formatters.Logger.Logger()
     71 		try:
     72 			start_val = self.start.GetValueAsUnsigned(0)
     73 			finish_val = self.finish.GetValueAsUnsigned(0)
     74 			# Before a vector has been constructed, it will contain bad values
     75 			# so we really need to be careful about the length we return since
     76 			# unitialized data can cause us to return a huge number. We need
     77 			# to also check for any of the start, finish or end of storage values
     78 			# being zero (NULL). If any are, then this vector has not been 
     79 			# initialized yet and we should return zero
     80 
     81 			# Make sure nothing is NULL
     82 			if start_val == 0 or finish_val == 0:
     83 				return 0
     84 			# Make sure start is less than finish
     85 			if start_val >= finish_val:
     86 				return 0
     87 
     88 			num_children = (finish_val-start_val)
     89 			if (num_children % self.data_size) != 0:
     90 				return 0
     91 			else:
     92 				num_children = num_children/self.data_size
     93 			return num_children
     94 		except:
     95 			return 0;
     96 
     97 	def get_child_index(self,name):
     98 		logger = lldb.formatters.Logger.Logger()
     99 		try:
    100 			return int(name.lstrip('[').rstrip(']'))
    101 		except:
    102 			return -1
    103 
    104 	def get_child_at_index(self,index):
    105 		logger = lldb.formatters.Logger.Logger()
    106 		logger >> "Retrieving child " + str(index)
    107 		if index < 0:
    108 			return None;
    109 		if index >= self.num_children():
    110 			return None;
    111 		try:
    112 			offset = index * self.data_size
    113 			return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type)
    114 		except:
    115 			return None
    116 
    117 	def update(self):
    118 		logger = lldb.formatters.Logger.Logger()
    119 		try:
    120 			self.start = self.valobj.GetChildMemberWithName('__begin_')
    121 			self.finish = self.valobj.GetChildMemberWithName('__end_')
    122 			# the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
    123 			# if this ends up not being correct, we can use the APIs to get at template arguments
    124 			data_type_finder = self.valobj.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_')
    125 			self.data_type = data_type_finder.GetType().GetPointeeType()
    126 			self.data_size = self.data_type.GetByteSize()
    127 		except:
    128 			pass
    129 
    130 	def has_children(self):
    131 		return True
    132 
    133 # Just an example: the actual summary is produced by a summary string: size=${svar%#}
    134 def stdvector_SummaryProvider(valobj,dict):
    135 	prov = stdvector_SynthProvider(valobj,None)
    136 	return 'size=' + str(prov.num_children())
    137 
    138 class stdlist_entry:
    139 
    140 	def __init__(self,entry):
    141 		logger = lldb.formatters.Logger.Logger()
    142 		self.entry = entry
    143 
    144 	def _next_impl(self):
    145 		logger = lldb.formatters.Logger.Logger()
    146 		return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
    147 
    148 	def _prev_impl(self):
    149 		logger = lldb.formatters.Logger.Logger()
    150 		return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
    151 
    152 	def _value_impl(self):
    153 		logger = lldb.formatters.Logger.Logger()
    154 		return self.entry.GetValueAsUnsigned(0)
    155 
    156 	def _isnull_impl(self):
    157 		logger = lldb.formatters.Logger.Logger()
    158 		return self._value_impl() == 0
    159 
    160 	def _sbvalue_impl(self):
    161 		logger = lldb.formatters.Logger.Logger()
    162 		return self.entry
    163 
    164 	next = property(_next_impl,None)
    165 	value = property(_value_impl,None)
    166 	is_null = property(_isnull_impl,None)
    167 	sbvalue = property(_sbvalue_impl,None)
    168 
    169 class stdlist_iterator:
    170 
    171 	def increment_node(self,node):
    172 		logger = lldb.formatters.Logger.Logger()
    173 		if node.is_null:
    174 			return None
    175 		return node.next
    176 
    177 	def __init__(self,node):
    178 		logger = lldb.formatters.Logger.Logger()
    179 		self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
    180 
    181 	def value(self):
    182 		logger = lldb.formatters.Logger.Logger()
    183 		return self.node.sbvalue # and return the SBValue back on exit
    184 
    185 	def next(self):
    186 		logger = lldb.formatters.Logger.Logger()
    187 		node = self.increment_node(self.node)
    188 		if node != None and node.sbvalue.IsValid() and not(node.is_null):
    189 			self.node = node
    190 			return self.value()
    191 		else:
    192 			return None
    193 
    194 	def advance(self,N):
    195 		logger = lldb.formatters.Logger.Logger()
    196 		if N < 0:
    197 			return None
    198 		if N == 0:
    199 			return self.value()
    200 		if N == 1:
    201 			return self.next()
    202 		while N > 0:
    203 			self.next()
    204 			N = N - 1
    205 		return self.value()
    206 
    207 
    208 class stdlist_SynthProvider:
    209 	def __init__(self, valobj, dict):
    210 		logger = lldb.formatters.Logger.Logger()
    211 		self.valobj = valobj
    212 		self.count = None
    213 
    214 	def next_node(self,node):
    215 		logger = lldb.formatters.Logger.Logger()
    216 		return node.GetChildMemberWithName('__next_')
    217 
    218 	def value(self,node):
    219 		logger = lldb.formatters.Logger.Logger()
    220 		return node.GetValueAsUnsigned()
    221 
    222 	# Floyd's cyle-finding algorithm
    223 	# try to detect if this list has a loop
    224 	def has_loop(self):
    225 		global _list_uses_loop_detector
    226 		logger = lldb.formatters.Logger.Logger()
    227 		if _list_uses_loop_detector == False:
    228 			logger >> "Asked not to use loop detection"
    229 			return False
    230 		slow = stdlist_entry(self.head)
    231 		fast1 = stdlist_entry(self.head)
    232 		fast2 = stdlist_entry(self.head)
    233 		while slow.next.value != self.node_address:
    234 			slow_value = slow.value
    235 			fast1 = fast2.next
    236 			fast2 = fast1.next
    237 			if fast1.value == slow_value or fast2.value == slow_value:
    238 				return True
    239 			slow = slow.next
    240 		return False
    241 
    242 	def num_children(self):
    243 		global _list_capping_size
    244 		logger = lldb.formatters.Logger.Logger()
    245 		if self.count == None:
    246 			self.count = self.num_children_impl()
    247 			if self.count > _list_capping_size:
    248 				self.count = _list_capping_size
    249 		return self.count
    250 
    251 	def num_children_impl(self):
    252 		global _list_capping_size
    253 		logger = lldb.formatters.Logger.Logger()
    254 		try:
    255 			next_val = self.head.GetValueAsUnsigned(0)
    256 			prev_val = self.tail.GetValueAsUnsigned(0)
    257 			# After a std::list has been initialized, both next and prev will be non-NULL
    258 			if next_val == 0 or prev_val == 0:
    259 				return 0
    260 			if next_val == self.node_address:
    261 				return 0
    262 			if next_val == prev_val:
    263 				return 1
    264 			if self.has_loop():
    265 				return 0
    266 			size = 2
    267 			current = stdlist_entry(self.head)
    268 			while current.next.value != self.node_address:
    269 				size = size + 1
    270 				current = current.next
    271 				if size > _list_capping_size:
    272 					return _list_capping_size
    273 			return (size - 1)
    274 		except:
    275 			return 0;
    276 
    277 	def get_child_index(self,name):
    278 		logger = lldb.formatters.Logger.Logger()
    279 		try:
    280 			return int(name.lstrip('[').rstrip(']'))
    281 		except:
    282 			return -1
    283 
    284 	def get_child_at_index(self,index):
    285 		logger = lldb.formatters.Logger.Logger()
    286 		logger >> "Fetching child " + str(index)
    287 		if index < 0:
    288 			return None;
    289 		if index >= self.num_children():
    290 			return None;
    291 		try:
    292 			current = stdlist_iterator(self.head)
    293 			current = current.advance(index)
    294 			# we do not return __value_ because then all our children would be named __value_
    295 			# we need to make a copy of __value__ with the right name - unfortunate
    296 			obj = current.GetChildMemberWithName('__value_')
    297 			obj_data = obj.GetData()
    298 			return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
    299 		except:
    300 			return None
    301 
    302 	def extract_type(self):
    303 		logger = lldb.formatters.Logger.Logger()
    304 		list_type = self.valobj.GetType().GetUnqualifiedType()
    305 		if list_type.IsReferenceType():
    306 			list_type = list_type.GetDereferencedType()
    307 		if list_type.GetNumberOfTemplateArguments() > 0:
    308 			data_type = list_type.GetTemplateArgumentType(0)
    309 		else:
    310 			data_type = None
    311 		return data_type
    312 
    313 	def update(self):
    314 		logger = lldb.formatters.Logger.Logger()
    315 		self.count = None
    316 		try:
    317 			impl = self.valobj.GetChildMemberWithName('__end_')
    318 			self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
    319 			self.head = impl.GetChildMemberWithName('__next_')
    320 			self.tail = impl.GetChildMemberWithName('__prev_')
    321 			self.data_type = self.extract_type()
    322 			self.data_size = self.data_type.GetByteSize()
    323 		except:
    324 			pass
    325 
    326 	def has_children(self):
    327 		return True
    328 
    329 
    330 # Just an example: the actual summary is produced by a summary string: size=${svar%#}
    331 def stdlist_SummaryProvider(valobj,dict):
    332 	prov = stdlist_SynthProvider(valobj,None)
    333 	return 'size=' + str(prov.num_children())
    334 
    335 # a tree node - this class makes the syntax in the actual iterator nicer to read and maintain
    336 class stdmap_iterator_node:
    337 	def _left_impl(self):
    338 		logger = lldb.formatters.Logger.Logger()
    339 		return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
    340 
    341 	def _right_impl(self):
    342 		logger = lldb.formatters.Logger.Logger()
    343 		return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
    344 
    345 	def _parent_impl(self):
    346 		logger = lldb.formatters.Logger.Logger()
    347 		return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
    348 
    349 	def _value_impl(self):
    350 		logger = lldb.formatters.Logger.Logger()
    351 		return self.node.GetValueAsUnsigned(0)
    352 
    353 	def _sbvalue_impl(self):
    354 		logger = lldb.formatters.Logger.Logger()
    355 		return self.node
    356 
    357 	def _null_impl(self):
    358 		logger = lldb.formatters.Logger.Logger()
    359 		return self.value == 0
    360 
    361 	def __init__(self,node):
    362 		logger = lldb.formatters.Logger.Logger()
    363 		self.node = node
    364 
    365 	left = property(_left_impl,None)
    366 	right = property(_right_impl,None)
    367 	parent = property(_parent_impl,None)
    368 	value = property(_value_impl,None)
    369 	is_null = property(_null_impl,None)
    370 	sbvalue = property(_sbvalue_impl,None)
    371 
    372 # a Python implementation of the tree iterator used by libc++
    373 class stdmap_iterator:
    374 
    375 	def tree_min(self,x):
    376 		logger = lldb.formatters.Logger.Logger()
    377 		steps = 0
    378 		if x.is_null:
    379 			return None
    380 		while (not x.left.is_null):
    381 			x = x.left
    382 			steps += 1
    383 			if steps > self.max_count:
    384 				logger >> "Returning None - we overflowed"
    385 				return None
    386 		return x
    387 
    388 	def tree_max(self,x):
    389 		logger = lldb.formatters.Logger.Logger()
    390 		if x.is_null:
    391 			return None
    392 		while (not x.right.is_null):
    393 			x =  x.right
    394 		return x
    395 
    396 	def tree_is_left_child(self,x):
    397 		logger = lldb.formatters.Logger.Logger()
    398 		if x.is_null:
    399 			return None
    400 		return True if x.value == x.parent.left.value else False
    401 
    402 	def increment_node(self,node):
    403 		logger = lldb.formatters.Logger.Logger()
    404 		if node.is_null:
    405 			return None
    406 		if not node.right.is_null:
    407 			return self.tree_min(node.right)
    408 		steps = 0
    409 		while (not self.tree_is_left_child(node)):
    410 			steps += 1
    411 			if steps > self.max_count:
    412 				logger >> "Returning None - we overflowed"
    413 				return None
    414 			node = node.parent
    415 		return node.parent
    416 
    417 	def __init__(self,node,max_count=0):
    418 		logger = lldb.formatters.Logger.Logger()
    419 		self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry
    420 		self.max_count = max_count
    421 
    422 	def value(self):
    423 		logger = lldb.formatters.Logger.Logger()
    424 		return self.node.sbvalue # and return the SBValue back on exit
    425 
    426 	def next(self):
    427 		logger = lldb.formatters.Logger.Logger()
    428 		node = self.increment_node(self.node)
    429 		if node != None and node.sbvalue.IsValid() and not(node.is_null):
    430 			self.node = node
    431 			return self.value()
    432 		else:
    433 			return None
    434 
    435 	def advance(self,N):
    436 		logger = lldb.formatters.Logger.Logger()
    437 		if N < 0:
    438 			return None
    439 		if N == 0:
    440 			return self.value()
    441 		if N == 1:
    442 			return self.next()
    443 		while N > 0:
    444 			if self.next() == None:
    445 				return None
    446 			N = N - 1
    447 		return self.value()
    448 
    449 class stdmap_SynthProvider:
    450 
    451 	def __init__(self, valobj, dict):
    452 		logger = lldb.formatters.Logger.Logger()
    453 		self.valobj = valobj;
    454 		self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
    455 		self.count = None
    456 
    457 	def update(self):
    458 		logger = lldb.formatters.Logger.Logger()
    459 		self.count = None
    460 		try:
    461 			# 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
    462 			# if this gets set to True, then we will merrily return None for any child from that moment on
    463 			self.garbage = False
    464 			self.tree = self.valobj.GetChildMemberWithName('__tree_')
    465 			self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
    466 			# this data is either lazily-calculated, or cannot be inferred at this moment
    467 			# we still need to mark it as None, meaning "please set me ASAP"
    468 			self.data_type = None
    469 			self.data_size = None
    470 			self.skip_size = None
    471 		except:
    472 			pass
    473 
    474 	def num_children(self):
    475 		global _map_capping_size
    476 		logger = lldb.formatters.Logger.Logger()
    477 		if self.count == None:
    478 			self.count = self.num_children_impl()
    479 			if self.count > _map_capping_size:
    480 				self.count = _map_capping_size
    481 		return self.count
    482 
    483 	def num_children_impl(self):
    484 		logger = lldb.formatters.Logger.Logger()
    485 		try:
    486 			return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
    487 		except:
    488 			return 0;
    489 
    490 	def has_children(self):
    491 		return True
    492 
    493 	def get_data_type(self):
    494 		logger = lldb.formatters.Logger.Logger()
    495 		if self.data_type == None or self.data_size == None:
    496 			if self.num_children() == 0:
    497 				return False
    498 			deref = self.root_node.Dereference()
    499 			if not(deref.IsValid()):
    500 				return False
    501 			value = deref.GetChildMemberWithName('__value_')
    502 			if not(value.IsValid()):
    503 				return False
    504 			self.data_type = value.GetType()
    505 			self.data_size = self.data_type.GetByteSize()
    506 			self.skip_size = None
    507 			return True
    508 		else:
    509 			return True
    510 
    511 	def get_value_offset(self,node):
    512 		logger = lldb.formatters.Logger.Logger()
    513 		if self.skip_size == None:
    514 			node_type = node.GetType()
    515 			fields_count = node_type.GetNumberOfFields()
    516 			for i in range(fields_count):
    517 				field = node_type.GetFieldAtIndex(i)
    518 				if field.GetName() == '__value_':
    519 					self.skip_size = field.GetOffsetInBytes()
    520 					break
    521 		return (self.skip_size != None)
    522 
    523 	def get_child_index(self,name):
    524 		logger = lldb.formatters.Logger.Logger()
    525 		try:
    526 			return int(name.lstrip('[').rstrip(']'))
    527 		except:
    528 			return -1
    529 
    530 	def get_child_at_index(self,index):
    531 		logger = lldb.formatters.Logger.Logger()
    532 		logger >> "Retrieving child " + str(index)
    533 		if index < 0:
    534 			return None
    535 		if index >= self.num_children():
    536 			return None;
    537 		if self.garbage:
    538 			logger >> "Returning None since this tree is garbage"
    539 			return None
    540 		try:
    541 			iterator = stdmap_iterator(self.root_node,max_count=self.num_children())
    542 			# the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
    543 			# out of which we can grab the information we need - every other node has a less informative
    544 			# type which omits all value information and only contains housekeeping information for the RB tree
    545 			# hence, we need to know if we are at a node != 0, so that we can still get at the data
    546 			need_to_skip = (index > 0)
    547 			current = iterator.advance(index)
    548 			if current == None:
    549 				logger >> "Tree is garbage - returning None"
    550 				self.garbage = True
    551 				return None
    552 			if self.get_data_type():
    553 				if not(need_to_skip):
    554 					current = current.Dereference()
    555 					obj = current.GetChildMemberWithName('__value_')
    556 					obj_data = obj.GetData()
    557 					self.get_value_offset(current) # make sure we have a valid offset for the next items
    558 					# we do not return __value_ because then we would end up with a child named
    559 					# __value_ instead of [0]
    560 					return self.valobj.CreateValueFromData('[' + str(index) + ']',obj_data,self.data_type)
    561 				else:
    562 					# FIXME we need to have accessed item 0 before accessing any other item!
    563 					if self.skip_size == None:
    564 						logger >> "You asked for item > 0 before asking for item == 0, I will fetch 0 now then retry"
    565 						if self.get_child_at_index(0):
    566 							return self.get_child_at_index(index)
    567 						else:
    568 							logger >> "item == 0 could not be found. sorry, nothing can be done here."
    569 							return None
    570 					return current.CreateChildAtOffset('[' + str(index) + ']',self.skip_size,self.data_type)
    571 			else:
    572 				logger >> "Unable to infer data-type - returning None (should mark tree as garbage here?)"
    573 				return None
    574 		except Exception as err:
    575 			logger >> "Hit an exception: " + str(err)
    576 			return None
    577 
    578 # Just an example: the actual summary is produced by a summary string: size=${svar%#}
    579 def stdmap_SummaryProvider(valobj,dict):
    580 	prov = stdmap_SynthProvider(valobj,None)
    581 	return 'size=' + str(prov.num_children())
    582 
    583 class stddeque_SynthProvider:
    584     def __init__(self, valobj, d):
    585         logger = lldb.formatters.Logger.Logger()
    586         logger.write("init")
    587         self.valobj = valobj
    588         self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
    589         self.count = None
    590         try:
    591             self.find_block_size()
    592         except:
    593             self.block_size = -1
    594             self.element_size = -1
    595         logger.write("block_size=%d, element_size=%d" % (self.block_size, self.element_size))
    596 
    597     def find_block_size(self):
    598         # in order to use the deque we must have the block size, or else
    599         # it's impossible to know what memory addresses are valid
    600         self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
    601         self.element_size = self.element_type.GetByteSize()
    602         # The code says this, but there must be a better way:
    603         # template <class _Tp, class _Allocator>
    604         # class __deque_base {
    605         #    static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16;
    606         # }
    607         if self.element_size < 256:
    608             self.block_size =  4096 / self.element_size
    609         else:
    610             self.block_size = 16
    611 
    612     def num_children(self):
    613         global _deque_capping_size
    614         logger = lldb.formatters.Logger.Logger()
    615         if self.count is None:
    616             return 0
    617         return min(self.count, _deque_capping_size)
    618 
    619     def has_children(self):
    620         return True
    621 
    622     def get_child_index(self,name):
    623         logger = lldb.formatters.Logger.Logger()
    624         try:
    625             return int(name.lstrip('[').rstrip(']'))
    626         except:
    627             return -1
    628 
    629     def get_child_at_index(self,index):
    630         logger = lldb.formatters.Logger.Logger()
    631         logger.write("Fetching child " + str(index))
    632         if index < 0 or self.count is None:
    633                 return None;
    634         if index >= self.num_children():
    635                 return None;
    636         try:
    637             i, j = divmod(self.start+index, self.block_size)
    638             return self.first.CreateValueFromExpression('[' + str(index) + ']',
    639                                                         '*(*(%s + %d) + %d)' % (self.first.get_expr_path(), i, j))
    640         except:
    641             return None
    642 
    643     def update(self):
    644         logger = lldb.formatters.Logger.Logger()
    645         try:
    646             # A deque is effectively a two-dim array, with fixed width.
    647             # 'map' contains pointers to the rows of this array. The
    648             # full memory area allocated by the deque is delimited
    649             # by 'first' and 'end_cap'. However, only a subset of this
    650             # memory contains valid data since a deque may have some slack
    651             # at the front and back in order to have O(1) insertion at
    652             # both ends. The rows in active use are delimited by
    653             # 'begin' and 'end'.
    654             #
    655             # To find the elements that are actually constructed, the 'start'
    656             # variable tells which element in this NxM array is the 0th
    657             # one, and the 'size' element gives the number of elements
    658             # in the deque.
    659             count = self.valobj.GetChildMemberWithName('__size_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
    660             # give up now if we cant access memory reliably
    661             if self.block_size < 0:
    662                 logger.write("block_size < 0")
    663                 return
    664             map_ = self.valobj.GetChildMemberWithName('__map_')
    665             start = self.valobj.GetChildMemberWithName('__start_').GetValueAsUnsigned(0)
    666             first = map_.GetChildMemberWithName('__first_')
    667             map_first = first.GetValueAsUnsigned(0)
    668             map_begin = map_.GetChildMemberWithName('__begin_').GetValueAsUnsigned(0)
    669             map_end   = map_.GetChildMemberWithName('__end_').GetValueAsUnsigned(0)
    670             map_endcap= map_.GetChildMemberWithName('__end_cap_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
    671             # check consistency
    672             if not map_first <= map_begin <= map_end <= map_endcap:
    673                 logger.write("map pointers are not monotonic")
    674                 return
    675             total_rows, junk = divmod(map_endcap - map_first, self.pointer_size)
    676             if junk:
    677                 logger.write("endcap-first doesnt align correctly")
    678                 return
    679             active_rows, junk = divmod(map_end - map_begin, self.pointer_size)
    680             if junk:
    681                 logger.write("end-begin doesnt align correctly")
    682                 return
    683             start_row, junk = divmod(map_begin - map_first, self.pointer_size)
    684             if junk:
    685                 logger.write("begin-first doesnt align correctly")
    686                 return
    687             if not start_row*self.block_size <= start < (start_row+1)*self.block_size:
    688                 logger.write("0th element must be in the 'begin' row")
    689                 return
    690             end_row = start_row + active_rows
    691             if not count:
    692                 if active_rows:
    693                     logger.write("empty deque but begin!=end")
    694                     return                            
    695             elif not (end_row-1)*self.block_size <= start+count < end_row*self.block_size:
    696                 logger.write("nth element must be before the 'end' row")
    697                 return
    698             logger.write("update success: count=%r, start=%r, first=%r" % (count,start,first))
    699             # if consistent, save all we really need:
    700             self.count = count
    701             self.start = start
    702             self.first = first
    703         except:
    704             self.count = None
    705             self.start = None
    706             self.map_first = None
    707             self.map_begin = None
    708 
    709 class stdsharedptr_SynthProvider:
    710     def __init__(self, valobj, d):
    711         logger = lldb.formatters.Logger.Logger()
    712         logger.write("init")
    713         self.valobj = valobj
    714         #self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType()
    715         self.ptr = None
    716         self.cntrl = None
    717         process = valobj.GetProcess()
    718         self.endianness = process.GetByteOrder()
    719         self.pointer_size = process.GetAddressByteSize()
    720         self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
    721 
    722     def num_children(self):
    723         return 1
    724 
    725     def has_children(self):
    726         return True
    727 
    728     def get_child_index(self,name):
    729         if name=="__ptr_":
    730             return 0
    731         if name=="count":
    732             return 1
    733         if name=="weak_count":
    734             return 2
    735         return -1
    736 
    737     def get_child_at_index(self,index):
    738         if index == 0:
    739             return self.ptr
    740         if index == 1:
    741             if self.cntrl == None:
    742                 count = 0
    743             else:
    744                 count = 1 + self.cntrl.GetChildMemberWithName('__shared_owners_').GetValueAsSigned()
    745             return self.valobj.CreateValueFromData("count",
    746                                                    lldb.SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [count]),
    747                                                    self.count_type)
    748         if index == 2:
    749             if self.cntrl == None:
    750                 count = 0
    751             else:
    752                 count = 1 + self.cntrl.GetChildMemberWithName('__shared_weak_owners_').GetValueAsSigned()
    753             return self.valobj.CreateValueFromData("weak_count",
    754                                                    lldb.SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [count]),
    755                                                    self.count_type)
    756         return None
    757 
    758     def update(self):
    759         logger = lldb.formatters.Logger.Logger()
    760         self.ptr = self.valobj.GetChildMemberWithName('__ptr_')#.Cast(self.element_ptr_type)
    761         cntrl = self.valobj.GetChildMemberWithName('__cntrl_')
    762         if cntrl.GetValueAsUnsigned(0):
    763             self.cntrl = cntrl.Dereference()
    764         else:
    765             self.cntrl = None
    766 
    767 # we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
    768 # talking with libc++ developer: "std::__1::class_name is set in stone until we decide to change the ABI. That shouldn't happen within a 5 year time frame"
    769 def __lldb_init_module(debugger,dict):
    770 	debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx')
    771 	debugger.HandleCommand('type summary add -F libcxx.stdstring_SummaryProvider "std::__1::basic_string<char, class std::__1::char_traits<char>, class std::__1::allocator<char> >" -w libcxx')
    772 	debugger.HandleCommand('type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx')
    773 	debugger.HandleCommand('type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx')
    774 	debugger.HandleCommand('type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx')
    775 	debugger.HandleCommand('type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx')
    776 	debugger.HandleCommand('type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx')
    777 	debugger.HandleCommand('type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
    778 	debugger.HandleCommand("type category enable libcxx")
    779 	debugger.HandleCommand('type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx')
    780 	debugger.HandleCommand('type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx')
    781 	# turns out the structs look the same, so weak_ptr can be handled the same!
    782 	debugger.HandleCommand('type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx')
    783 
    784 _map_capping_size = 255
    785 _list_capping_size = 255
    786 _list_uses_loop_detector = True
    787 _deque_capping_size = 255
    788