js/src/gdb/mozilla/prettyprinters.py

changeset 0
6474c204b198
     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)

mercurial