# Assorted Pretty printers for wesnoth data structures in gdb import gdb import re import itertools import wesnoth_type_tools import importlib importlib.reload(wesnoth_type_tools) from wesnoth_type_tools import strip_all_type, dereference_if_possible class RecursionManager(object): """Keeps track of the levels of recursion and whether expansion should happen or not """ default = 2 curr = 0 # @classmethod # def __init__(cls, val): # cls.default = val # cls.curr=0; @classmethod def get_level(cls): return cls.default @classmethod def set_level(cls, val): cls.curr = 0 if val >= 0: cls.default = val return cls.default @classmethod def reset(cls): cls.curr = 0 return cls.default @classmethod def should_display(cls): return cls.curr <= cls.default @classmethod def inc(cls): cls.curr += 1 return cls.should_display() @classmethod def dec(cls): if cls.curr > 0: cls.curr -= 1 return cls.should_display() #Printer for n_interned::t_interned class T_InternedPrinter(object): """Print a t_interned_token""" def __init__(self, val): self.val = val def to_string(self): #Returns either a string or an other convertible to string return self.val['iter_']['first']['_M_dataplus']['_M_p'] def display_hint(self): #one of 'string' 'array' 'map' return 'string' #Printer for n_token::t_token class T_TokenPrinter(object): """Print a t_token""" def __init__(self, val): self.val = val def to_string(self): # Get the type. type = self.val.type # Get the type name. type = strip_all_type(self.val) #Return the underlying base class baseclass = type.fields()[0].type return self.val.cast(baseclass) def display_hint(self): #one of 'string' 'array' 'map' return 'string' class TstringPrinter(object): """Print a t_string""" def __init__(self, val): self.val = val def to_string(self): # Get the type name. type = strip_all_type(self.val) #Dereference pointers lval = dereference_if_possible(self.val) #Return the underlying base class baseclass = type.fields()[0].type shared = lval.cast(baseclass)['val_'] if shared == 0: return 'NULL' #Print the untranslated string or an error try: ret = shared.dereference()['val']['value_']['iter_']['first'] except RuntimeError as e: return "wesnoth_gdb error invalid tstring" return ret def display_hint(self): #one of 'string' 'array' 'map' return 'string' class AttributeValuePrinter(object): """Print an attribute_value""" def __init__(self, val): self.val = val def to_string(self): return 'attribute_value' def children(self): # Get the type. self.type = strip_all_type(self.val) try: #Dereference pointers lval = dereference_if_possible(self.val) attr = lval attr_type = attr['type_'] class Atype: EMPTY = 0 BOOL = 1 INT = 2 DOUBLE = 3 TSTRING = 4 TOKEN = 5 if attr_type == Atype.EMPTY: yield "EMPTY", "" elif attr_type == Atype.BOOL: yield 'bool', 'true' if attr['bool_value_'] else 'false' elif attr_type == Atype.INT: yield 'int', 'int ' + ('%s' % attr['int_value_']) elif attr_type == Atype.DOUBLE: yield 'double', 'double ' + ('%s' % attr['double_value_']) elif attr_type == Atype.TOKEN: yield 'token', 'token ' + ('%s' % attr['token_value_']) elif attr_type == Atype.TSTRING: yield 't_string', 't_string ' + ('%s' % attr['t_string_value_']) else: yield 'unknown', "unknown type code = " + ('%d' % attr_type) except RuntimeError as e: yield 'error', "wesnoth_gdb error invalid %s" % self.val.type def display_hint(self): #one of 'string' 'array' 'map' return 'string' #This will be brittle as it depends on the underlying boost implementation, but its better than nothing #With much help from http://stackoverflow.com/questions/2804641/pretty-printing-boostunordered-map-on-gdb class BoostUnorderedMapPrinter(object): """ Print a boost unordered map""" class _iterator: def __init__(self, val): self.buckets = val['table_']['buckets_'] if self.buckets != 0: t_key = val.type.template_argument(0) t_val = val.type.template_argument(1) self.bucket_count = val['table_']['bucket_count_'] self.current_bucket = 0 pair = "std::pair<%s const, %s>" % (t_key, t_val) self.pair_pointer = gdb.lookup_type(pair).pointer() self.base_pointer = gdb.lookup_type("boost::unordered_detail::value_base< %s >" % pair).pointer() self.node_pointer = gdb.lookup_type("boost::unordered_detail::hash_node, boost::unordered_detail::ungrouped>" % pair).pointer() self.node = self.buckets[self.current_bucket]['next_'] def __iter__(self): return self def __next__(self): if self.buckets == 0 or not RecursionManager.should_display(): raise StopIteration while not self.node: self.current_bucket += 1 if self.current_bucket >= self.bucket_count: raise StopIteration self.node = self.buckets[self.current_bucket]['next_'] iterator = self.node.cast(self.node_pointer).cast(self.base_pointer).cast(self.pair_pointer).dereference() self.node = self.node['next_'] return ('%s' % iterator['first']), iterator['second'] def __init__(self, val): self.val = val self.buckets = val['table_']['buckets_'] self.descended = False def __del__(self): if self.descended: RecursionManager.dec() def children(self): self.descended = True RecursionManager.inc() return self._iterator(self.val) def to_string(self): ret = "boost::unordered_map" if self.buckets == 0: ret += " EMPTY" return ret def display_hint(self): return 'string' class BoostUnorderedMapIteratorPrinter(object): def __init__(self, val): pair = val.type.template_argument(0).template_argument(0) t_key = pair.template_argument(0) t_val = pair.template_argument(1) self.node = val['base_']['node_'] self.pair_pointer = pair.pointer() self.base_pointer = gdb.lookup_type("boost::unordered_detail::value_base< %s >" % pair).pointer() self.node_pointer = gdb.lookup_type("boost::unordered_detail::hash_node, boost::unordered_detail::ungrouped>" % pair).pointer() self.bucket_pointer = gdb.lookup_type("boost::unordered_detail::hash_bucket >" % pair).pointer() def to_string(self): if not self.node: return 'NULL' iterator = self.node.cast(self.node_pointer).cast(self.base_pointer).cast(self.pair_pointer).dereference() return iterator['second'] def display_hint(self): return 'string' class ConfigPrinter(object): """Print a config""" def __init__(self, val): self.val = val # Get the type name. self.type = strip_all_type(self.val) #Dereference pointers self.lval = dereference_if_possible(self.val) def to_string(self): return "config" def children(self): if RecursionManager.should_display(): #yield "invalid", self.val['invalid'] yield "values", self.lval['values_'] yield "children", self.lval['children_'] RecursionManager.inc() try: yield "ordered_children", self.lval['ordered_children'] except RuntimeError as e: RecursionManager.dec() else: RecursionManager.dec() else: pass def display_hint(self): #one of 'string' 'array' 'map' return 'string' # register the pretty-printers def add_printers(pretty_printers_dict): pretty_printers_dict[re.compile('^n_interned::t_interned_token.*$')] = T_InternedPrinter pretty_printers_dict[re.compile('^n_token::t_token$')] = T_TokenPrinter pretty_printers_dict[re.compile('^t_string$')] = TstringPrinter pretty_printers_dict[re.compile('^config::attribute_value$')] = AttributeValuePrinter pretty_printers_dict[re.compile('^boost::unordered_map.*$')] = BoostUnorderedMapPrinter pretty_printers_dict[re.compile('^boost::unordered_detail::hash_iterator\