1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/gdb/mozilla/prettyprinters.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,363 @@ 1.4 +# mozilla/prettyprinters.py --- infrastructure for SpiderMonkey's auto-loaded pretty-printers. 1.5 + 1.6 +import gdb 1.7 +import re 1.8 + 1.9 +# Decorators for declaring pretty-printers. 1.10 +# 1.11 +# In each case, the decoratee should be a SpiderMonkey-style pretty-printer 1.12 +# factory, taking both a gdb.Value instance and a TypeCache instance as 1.13 +# arguments; see TypeCache, below. 1.14 + 1.15 +# Check that |fn| hasn't been registered as a pretty-printer under some 1.16 +# other name already. (The 'enabled' flags used by GDB's 1.17 +# 'enable/disable/info pretty-printer' commands are simply stored as 1.18 +# properties of the function objects themselves, so a single function 1.19 +# object can't carry the 'enabled' flags for two different printers.) 1.20 +def check_for_reused_pretty_printer(fn): 1.21 + if hasattr(fn, 'enabled'): 1.22 + raise RuntimeError, ("pretty-printer function %r registered more than once" % fn) 1.23 + 1.24 +# a dictionary mapping gdb.Type tags to pretty-printer functions. 1.25 +printers_by_tag = {} 1.26 + 1.27 +# A decorator: add the decoratee as a pretty-printer lookup function for types 1.28 +# named |type_name|. 1.29 +def pretty_printer(type_name): 1.30 + def add(fn): 1.31 + check_for_reused_pretty_printer(fn) 1.32 + add_to_subprinter_list(fn, type_name) 1.33 + printers_by_tag[type_name] = fn 1.34 + return fn 1.35 + return add 1.36 + 1.37 +# a dictionary mapping gdb.Type tags to pretty-printer functions for pointers to 1.38 +# that type. 1.39 +ptr_printers_by_tag = {} 1.40 + 1.41 +# A decorator: add the decoratee as a pretty-printer lookup function for 1.42 +# pointers to types named |type_name|. 1.43 +def ptr_pretty_printer(type_name): 1.44 + def add(fn): 1.45 + check_for_reused_pretty_printer(fn) 1.46 + add_to_subprinter_list(fn, "ptr-to-" + type_name) 1.47 + ptr_printers_by_tag[type_name] = fn 1.48 + return fn 1.49 + return add 1.50 + 1.51 +# a dictionary mapping gdb.Type tags to pretty-printer functions for 1.52 +# references to that type. 1.53 +ref_printers_by_tag = {} 1.54 + 1.55 +# A decorator: add the decoratee as a pretty-printer lookup function for 1.56 +# references to instances of types named |type_name|. 1.57 +def ref_pretty_printer(type_name): 1.58 + def add(fn): 1.59 + check_for_reused_pretty_printer(fn) 1.60 + add_to_subprinter_list(fn, "ref-to-" + type_name) 1.61 + ref_printers_by_tag[type_name] = fn 1.62 + return fn 1.63 + return add 1.64 + 1.65 +# a dictionary mapping the template name portion of gdb.Type tags to 1.66 +# pretty-printer functions for instantiations of that template. 1.67 +template_printers_by_tag = {} 1.68 + 1.69 +# A decorator: add the decoratee as a pretty-printer lookup function for 1.70 +# instantiations of templates named |template_name|. 1.71 +def template_pretty_printer(template_name): 1.72 + def add(fn): 1.73 + check_for_reused_pretty_printer(fn) 1.74 + add_to_subprinter_list(fn, 'instantiations-of-' + template_name) 1.75 + template_printers_by_tag[template_name] = fn 1.76 + return fn 1.77 + return add 1.78 + 1.79 +# A list of (REGEXP, PRINTER) pairs, such that if REGEXP (a RegexObject) 1.80 +# matches the result of converting a gdb.Value's type to a string, then 1.81 +# PRINTER is a pretty-printer lookup function that will probably like that 1.82 +# value. 1.83 +printers_by_regexp = [] 1.84 + 1.85 +# A decorator: add the decoratee as a pretty-printer factory for types 1.86 +# that, when converted to a string, match |pattern|. Use |name| as the 1.87 +# pretty-printer's name, when listing, enabling and disabling. 1.88 +def pretty_printer_for_regexp(pattern, name): 1.89 + compiled = re.compile(pattern) 1.90 + def add(fn): 1.91 + check_for_reused_pretty_printer(fn) 1.92 + add_to_subprinter_list(fn, name) 1.93 + printers_by_regexp.append((compiled, fn)) 1.94 + return fn 1.95 + return add 1.96 + 1.97 +# Forget all pretty-printer lookup functions defined in the module name 1.98 +# |module_name|, if any exist. Use this at the top of each pretty-printer 1.99 +# module like this: 1.100 +# 1.101 +# clear_module_printers(__name__) 1.102 +def clear_module_printers(module_name): 1.103 + global printers_by_tag, ptr_printers_by_tag, ref_printers_by_tag 1.104 + global template_printers_by_tag, printers_by_regexp 1.105 + 1.106 + # Remove all pretty-printers defined in the module named |module_name| 1.107 + # from d. 1.108 + def clear_dictionary(d): 1.109 + # Walk the dictionary, building a list of keys whose entries we 1.110 + # should remove. (It's not safe to delete entries from a dictionary 1.111 + # while we're iterating over it.) 1.112 + to_delete = [] 1.113 + for (k, v) in d.iteritems(): 1.114 + if v.__module__ == module_name: 1.115 + to_delete.append(k) 1.116 + remove_from_subprinter_list(v) 1.117 + for k in to_delete: 1.118 + del d[k] 1.119 + 1.120 + clear_dictionary(printers_by_tag) 1.121 + clear_dictionary(ptr_printers_by_tag) 1.122 + clear_dictionary(ref_printers_by_tag) 1.123 + clear_dictionary(template_printers_by_tag) 1.124 + 1.125 + # Iterate over printers_by_regexp, deleting entries from the given module. 1.126 + new_list = [] 1.127 + for p in printers_by_regexp: 1.128 + if p.__module__ == module_name: 1.129 + remove_from_subprinter_list(p) 1.130 + else: 1.131 + new_list.append(p) 1.132 + printers_by_regexp = new_list 1.133 + 1.134 +# Our subprinters array. The 'subprinters' attributes of all lookup 1.135 +# functions returned by lookup_for_objfile point to this array instance, 1.136 +# which we mutate as subprinters are added and removed. 1.137 +subprinters = [] 1.138 + 1.139 +# Set up the 'name' and 'enabled' attributes on |subprinter|, and add it to our 1.140 +# list of all SpiderMonkey subprinters. 1.141 +def add_to_subprinter_list(subprinter, name): 1.142 + subprinter.name = name 1.143 + subprinter.enabled = True 1.144 + subprinters.append(subprinter) 1.145 + 1.146 +# Remove |subprinter| from our list of all SpiderMonkey subprinters. 1.147 +def remove_from_subprinter_list(subprinter): 1.148 + subprinters.remove(subprinter) 1.149 + 1.150 +# An exception class meaning, "This objfile has no SpiderMonkey in it." 1.151 +class NotSpiderMonkeyObjfileError(TypeError): 1.152 + pass 1.153 + 1.154 +# TypeCache: a cache for frequently used information about an objfile. 1.155 +# 1.156 +# When a new SpiderMonkey objfile is loaded, we construct an instance of 1.157 +# this class for it. Then, whenever we construct a pretty-printer for some 1.158 +# gdb.Value, we also pass, as a second argument, the TypeCache for the 1.159 +# objfile to which that value's type belongs. 1.160 +# 1.161 +# if objfile doesn't seem to have SpiderMonkey code in it, the constructor 1.162 +# raises NotSpiderMonkeyObjfileError. 1.163 +# 1.164 +# Pretty-printer modules may add attributes to this to hold their own 1.165 +# cached values. Such attributes should be named mod_NAME, where the module 1.166 +# is named mozilla.NAME; for example, mozilla.JSString should store its 1.167 +# metadata in the TypeCache's mod_JSString attribute. 1.168 +class TypeCache(object): 1.169 + def __init__(self, objfile): 1.170 + self.objfile = objfile 1.171 + 1.172 + # Unfortunately, the Python interface doesn't allow us to specify 1.173 + # the objfile in whose scope lookups should occur. But simply 1.174 + # knowing that we need to lookup the types afresh is probably 1.175 + # enough. 1.176 + self.void_t = gdb.lookup_type('void') 1.177 + self.void_ptr_t = self.void_t.pointer() 1.178 + try: 1.179 + self.JSString_ptr_t = gdb.lookup_type('JSString').pointer() 1.180 + self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer() 1.181 + except gdb.error: 1.182 + raise NotSpiderMonkeyObjfileError 1.183 + 1.184 + self.mod_JSString = None 1.185 + self.mod_JSObject = None 1.186 + self.mod_jsval = None 1.187 + 1.188 +# Yield a series of all the types that |t| implements, by following typedefs 1.189 +# and iterating over base classes. Specifically: 1.190 +# - |t| itself is the first value yielded. 1.191 +# - If we yield a typedef, we later yield its definition. 1.192 +# - If we yield a type with base classes, we later yield those base classes. 1.193 +# - If we yield a type with some base classes that are typedefs, 1.194 +# we yield all the type's base classes before following the typedefs. 1.195 +# (Actually, this never happens, because G++ doesn't preserve the typedefs in 1.196 +# the DWARF.) 1.197 +# 1.198 +# This is a hokey attempt to order the implemented types by meaningfulness when 1.199 +# pretty-printed. Perhaps it is entirely misguided, and we should actually 1.200 +# collect all applicable pretty-printers, and then use some ordering on the 1.201 +# pretty-printers themselves. 1.202 +# 1.203 +# We may yield a type more than once (say, if it appears more than once in the 1.204 +# class hierarchy). 1.205 +def implemented_types(t): 1.206 + 1.207 + # Yield all types that follow |t|. 1.208 + def followers(t): 1.209 + if t.code == gdb.TYPE_CODE_TYPEDEF: 1.210 + yield t.target() 1.211 + for t2 in followers(t.target()): yield t2 1.212 + elif t.code == gdb.TYPE_CODE_STRUCT: 1.213 + base_classes = [] 1.214 + for f in t.fields(): 1.215 + if f.is_base_class: 1.216 + yield f.type 1.217 + base_classes.append(f.type) 1.218 + for b in base_classes: 1.219 + for t2 in followers(b): yield t2 1.220 + 1.221 + yield t 1.222 + for t2 in followers(t): yield t2 1.223 + 1.224 +template_regexp = re.compile("([\w_:]+)<") 1.225 + 1.226 +# Construct and return a pretty-printer lookup function for objfile, or 1.227 +# return None if the objfile doesn't contain SpiderMonkey code 1.228 +# (specifically, definitions for SpiderMonkey types). 1.229 +def lookup_for_objfile(objfile): 1.230 + # Create a type cache for this objfile. 1.231 + try: 1.232 + cache = TypeCache(objfile) 1.233 + except NotSpiderMonkeyObjfileError: 1.234 + if gdb.parameter("verbose"): 1.235 + gdb.write("objfile '%s' has no SpiderMonkey code; not registering pretty-printers\n" 1.236 + % (objfile.filename,)) 1.237 + return None 1.238 + 1.239 + # Return a pretty-printer for |value|, if we have one. This is the lookup 1.240 + # function object we place in each gdb.Objfile's pretty-printers list, so it 1.241 + # carries |name|, |enabled|, and |subprinters| attributes. 1.242 + def lookup(value): 1.243 + # If |table| has a pretty-printer for |tag|, apply it to |value|. 1.244 + def check_table(table, tag): 1.245 + if tag in table: 1.246 + f = table[tag] 1.247 + if f.enabled: 1.248 + return f(value, cache) 1.249 + return None 1.250 + 1.251 + def check_table_by_type_name(table, t): 1.252 + if t.code == gdb.TYPE_CODE_TYPEDEF: 1.253 + return check_table(table, str(t)) 1.254 + elif t.code == gdb.TYPE_CODE_STRUCT and t.tag: 1.255 + return check_table(table, t.tag) 1.256 + else: 1.257 + return None 1.258 + 1.259 + for t in implemented_types(value.type): 1.260 + if t.code == gdb.TYPE_CODE_PTR: 1.261 + for t2 in implemented_types(t.target()): 1.262 + p = check_table_by_type_name(ptr_printers_by_tag, t2) 1.263 + if p: return p 1.264 + elif t.code == gdb.TYPE_CODE_REF: 1.265 + for t2 in implemented_types(t.target()): 1.266 + p = check_table_by_type_name(ref_printers_by_tag, t2) 1.267 + if p: return p 1.268 + else: 1.269 + p = check_table_by_type_name(printers_by_tag, t) 1.270 + if p: return p 1.271 + if t.code == gdb.TYPE_CODE_STRUCT and t.tag: 1.272 + m = template_regexp.match(t.tag) 1.273 + if m: 1.274 + p = check_table(template_printers_by_tag, m.group(1)) 1.275 + if p: return p 1.276 + 1.277 + # Failing that, look for a printer in printers_by_regexp. We have 1.278 + # to scan the whole list, so regexp printers should be used 1.279 + # sparingly. 1.280 + s = str(value.type) 1.281 + for (r, f) in printers_by_regexp: 1.282 + if f.enabled: 1.283 + m = r.match(s) 1.284 + if m: 1.285 + p = f(value, cache) 1.286 + if p: return p 1.287 + 1.288 + # No luck. 1.289 + return None 1.290 + 1.291 + # Give |lookup| the attributes expected of a pretty-printer with 1.292 + # subprinters, for enabling and disabling. 1.293 + lookup.name = "SpiderMonkey" 1.294 + lookup.enabled = True 1.295 + lookup.subprinters = subprinters 1.296 + 1.297 + return lookup 1.298 + 1.299 +# A base class for pretty-printers for pointer values that handles null 1.300 +# pointers, by declining to construct a pretty-printer for them at all. 1.301 +# Derived classes may simply assume that self.value is non-null. 1.302 +# 1.303 +# To help share code, this class can also be used with reference types. 1.304 +# 1.305 +# This class provides the following methods, which subclasses are free to 1.306 +# override: 1.307 +# 1.308 +# __init__(self, value, cache): Save value and cache as properties by those names 1.309 +# on the instance. 1.310 +# 1.311 +# to_string(self): format the type's name and address, as GDB would, and then 1.312 +# call a 'summary' method (which the subclass must define) to produce a 1.313 +# description of the referent. 1.314 +# 1.315 +# Note that pretty-printers returning a 'string' display hint must not use 1.316 +# this default 'to_string' method, as GDB will take everything it returns, 1.317 +# including the type name and address, as string contents. 1.318 +class Pointer(object): 1.319 + def __new__(cls, value, cache): 1.320 + # Don't try to provide pretty-printers for NULL pointers. 1.321 + if value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR and value == 0: 1.322 + return None 1.323 + return super(Pointer, cls).__new__(cls) 1.324 + 1.325 + def __init__(self, value, cache): 1.326 + self.value = value 1.327 + self.cache = cache 1.328 + 1.329 + def to_string(self): 1.330 + # See comment above. 1.331 + assert not hasattr(self, 'display_hint') or self.display_hint() != 'string' 1.332 + concrete_type = self.value.type.strip_typedefs() 1.333 + if concrete_type.code == gdb.TYPE_CODE_PTR: 1.334 + address = self.value.cast(self.cache.void_ptr_t) 1.335 + elif concrete_type.code == gdb.TYPE_CODE_REF: 1.336 + address = '@' + str(self.value.address.cast(self.cache.void_ptr_t)) 1.337 + else: 1.338 + assert not "mozilla.prettyprinters.Pointer applied to bad value type" 1.339 + try: 1.340 + summary = self.summary() 1.341 + except gdb.MemoryError as r: 1.342 + summary = str(r) 1.343 + v = '(%s) %s %s' % (self.value.type, address, summary) 1.344 + return v 1.345 + 1.346 + def summary(self): 1.347 + raise NotImplementedError 1.348 + 1.349 +field_enum_value = None 1.350 + 1.351 +# Given |t|, a gdb.Type instance representing an enum type, return the 1.352 +# numeric value of the enum value named |name|. 1.353 +# 1.354 +# Pre-2012-4-18 versions of GDB store the value of an enum member on the 1.355 +# gdb.Field's 'bitpos' attribute; later versions store it on the 'enumval' 1.356 +# attribute. This function retrieves the value from either. 1.357 +def enum_value(t, name): 1.358 + global field_enum_value 1.359 + f = t[name] 1.360 + # Monkey-patching is a-okay in polyfills! Just because. 1.361 + if not field_enum_value: 1.362 + if hasattr(f, 'enumval'): 1.363 + field_enum_value = lambda f: f.enumval 1.364 + else: 1.365 + field_enum_value = lambda f: f.bitpos 1.366 + return field_enum_value(f)