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