js/src/gdb/mozilla/prettyprinters.py

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 # mozilla/prettyprinters.py --- infrastructure for SpiderMonkey's auto-loaded pretty-printers.
     3 import gdb
     4 import re
     6 # Decorators for declaring pretty-printers.
     7 #
     8 # In each case, the decoratee should be a SpiderMonkey-style pretty-printer
     9 # factory, taking both a gdb.Value instance and a TypeCache instance as
    10 # arguments; see TypeCache, below.
    12 # Check that |fn| hasn't been registered as a pretty-printer under some
    13 # other name already. (The 'enabled' flags used by GDB's
    14 # 'enable/disable/info pretty-printer' commands are simply stored as
    15 # properties of the function objects themselves, so a single function
    16 # object can't carry the 'enabled' flags for two different printers.)
    17 def check_for_reused_pretty_printer(fn):
    18     if hasattr(fn, 'enabled'):
    19         raise RuntimeError, ("pretty-printer function %r registered more than once" % fn)
    21 # a dictionary mapping gdb.Type tags to pretty-printer functions.
    22 printers_by_tag = {}
    24 # A decorator: add the decoratee as a pretty-printer lookup function for types
    25 # named |type_name|.
    26 def pretty_printer(type_name):
    27     def add(fn):
    28         check_for_reused_pretty_printer(fn)
    29         add_to_subprinter_list(fn, type_name)
    30         printers_by_tag[type_name] = fn
    31         return fn
    32     return add
    34 # a dictionary mapping gdb.Type tags to pretty-printer functions for pointers to
    35 # that type.
    36 ptr_printers_by_tag = {}
    38 # A decorator: add the decoratee as a pretty-printer lookup function for
    39 # pointers to types named |type_name|.
    40 def ptr_pretty_printer(type_name):
    41     def add(fn):
    42         check_for_reused_pretty_printer(fn)
    43         add_to_subprinter_list(fn, "ptr-to-" + type_name)
    44         ptr_printers_by_tag[type_name] = fn
    45         return fn
    46     return add
    48 # a dictionary mapping gdb.Type tags to pretty-printer functions for
    49 # references to that type.
    50 ref_printers_by_tag = {}
    52 # A decorator: add the decoratee as a pretty-printer lookup function for
    53 # references to instances of types named |type_name|.
    54 def ref_pretty_printer(type_name):
    55     def add(fn):
    56         check_for_reused_pretty_printer(fn)
    57         add_to_subprinter_list(fn, "ref-to-" + type_name)
    58         ref_printers_by_tag[type_name] = fn
    59         return fn
    60     return add
    62 # a dictionary mapping the template name portion of gdb.Type tags to
    63 # pretty-printer functions for instantiations of that template.
    64 template_printers_by_tag = {}
    66 # A decorator: add the decoratee as a pretty-printer lookup function for
    67 # instantiations of templates named |template_name|.
    68 def template_pretty_printer(template_name):
    69     def add(fn):
    70         check_for_reused_pretty_printer(fn)
    71         add_to_subprinter_list(fn, 'instantiations-of-' + template_name)
    72         template_printers_by_tag[template_name] = fn
    73         return fn
    74     return add
    76 # A list of (REGEXP, PRINTER) pairs, such that if REGEXP (a RegexObject)
    77 # matches the result of converting a gdb.Value's type to a string, then
    78 # PRINTER is a pretty-printer lookup function that will probably like that
    79 # value.
    80 printers_by_regexp = []
    82 # A decorator: add the decoratee as a pretty-printer factory for types
    83 # that, when converted to a string, match |pattern|. Use |name| as the
    84 # pretty-printer's name, when listing, enabling and disabling.
    85 def pretty_printer_for_regexp(pattern, name):
    86     compiled = re.compile(pattern)
    87     def add(fn):
    88         check_for_reused_pretty_printer(fn)
    89         add_to_subprinter_list(fn, name)
    90         printers_by_regexp.append((compiled, fn))
    91         return fn
    92     return add
    94 # Forget all pretty-printer lookup functions defined in the module name
    95 # |module_name|, if any exist. Use this at the top of each pretty-printer
    96 # module like this:
    97 #
    98 #   clear_module_printers(__name__)
    99 def clear_module_printers(module_name):
   100     global printers_by_tag, ptr_printers_by_tag, ref_printers_by_tag
   101     global template_printers_by_tag, printers_by_regexp
   103     # Remove all pretty-printers defined in the module named |module_name|
   104     # from d.
   105     def clear_dictionary(d):
   106         # Walk the dictionary, building a list of keys whose entries we
   107         # should remove. (It's not safe to delete entries from a dictionary
   108         # while we're iterating over it.)
   109         to_delete = []
   110         for (k, v) in d.iteritems():
   111             if v.__module__ == module_name:
   112                 to_delete.append(k)
   113                 remove_from_subprinter_list(v)
   114         for k in to_delete:
   115             del d[k]
   117     clear_dictionary(printers_by_tag)
   118     clear_dictionary(ptr_printers_by_tag)
   119     clear_dictionary(ref_printers_by_tag)
   120     clear_dictionary(template_printers_by_tag)
   122     # Iterate over printers_by_regexp, deleting entries from the given module.
   123     new_list = []
   124     for p in printers_by_regexp:
   125         if p.__module__ == module_name:
   126             remove_from_subprinter_list(p)
   127         else:
   128             new_list.append(p)
   129     printers_by_regexp = new_list
   131 # Our subprinters array. The 'subprinters' attributes of all lookup
   132 # functions returned by lookup_for_objfile point to this array instance,
   133 # which we mutate as subprinters are added and removed.
   134 subprinters = []
   136 # Set up the 'name' and 'enabled' attributes on |subprinter|, and add it to our
   137 # list of all SpiderMonkey subprinters.
   138 def add_to_subprinter_list(subprinter, name):
   139     subprinter.name = name
   140     subprinter.enabled = True
   141     subprinters.append(subprinter)
   143 # Remove |subprinter| from our list of all SpiderMonkey subprinters.
   144 def remove_from_subprinter_list(subprinter):
   145     subprinters.remove(subprinter)
   147 # An exception class meaning, "This objfile has no SpiderMonkey in it."
   148 class NotSpiderMonkeyObjfileError(TypeError):
   149     pass
   151 # TypeCache: a cache for frequently used information about an objfile.
   152 #
   153 # When a new SpiderMonkey objfile is loaded, we construct an instance of
   154 # this class for it. Then, whenever we construct a pretty-printer for some
   155 # gdb.Value, we also pass, as a second argument, the TypeCache for the
   156 # objfile to which that value's type belongs.
   157 #
   158 # if objfile doesn't seem to have SpiderMonkey code in it, the constructor
   159 # raises NotSpiderMonkeyObjfileError.
   160 #
   161 # Pretty-printer modules may add attributes to this to hold their own
   162 # cached values. Such attributes should be named mod_NAME, where the module
   163 # is named mozilla.NAME; for example, mozilla.JSString should store its
   164 # metadata in the TypeCache's mod_JSString attribute.
   165 class TypeCache(object):
   166     def __init__(self, objfile):
   167         self.objfile = objfile
   169         # Unfortunately, the Python interface doesn't allow us to specify
   170         # the objfile in whose scope lookups should occur. But simply
   171         # knowing that we need to lookup the types afresh is probably
   172         # enough.
   173         self.void_t = gdb.lookup_type('void')
   174         self.void_ptr_t = self.void_t.pointer()
   175         try:
   176             self.JSString_ptr_t = gdb.lookup_type('JSString').pointer()
   177             self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer()
   178         except gdb.error:
   179             raise NotSpiderMonkeyObjfileError
   181         self.mod_JSString = None
   182         self.mod_JSObject = None
   183         self.mod_jsval = None
   185 # Yield a series of all the types that |t| implements, by following typedefs
   186 # and iterating over base classes. Specifically:
   187 # - |t| itself is the first value yielded.
   188 # - If we yield a typedef, we later yield its definition.
   189 # - If we yield a type with base classes, we later yield those base classes.
   190 # - If we yield a type with some base classes that are typedefs,
   191 #   we yield all the type's base classes before following the typedefs.
   192 #   (Actually, this never happens, because G++ doesn't preserve the typedefs in
   193 #   the DWARF.)
   194 #
   195 # This is a hokey attempt to order the implemented types by meaningfulness when
   196 # pretty-printed. Perhaps it is entirely misguided, and we should actually
   197 # collect all applicable pretty-printers, and then use some ordering on the
   198 # pretty-printers themselves.
   199 #
   200 # We may yield a type more than once (say, if it appears more than once in the
   201 # class hierarchy).
   202 def implemented_types(t):
   204     # Yield all types that follow |t|.
   205     def followers(t):
   206         if t.code == gdb.TYPE_CODE_TYPEDEF:
   207             yield t.target()
   208             for t2 in followers(t.target()): yield t2
   209         elif t.code == gdb.TYPE_CODE_STRUCT:
   210             base_classes = []
   211             for f in t.fields():
   212                 if f.is_base_class:
   213                     yield f.type
   214                     base_classes.append(f.type)
   215             for b in base_classes:
   216                 for t2 in followers(b): yield t2
   218     yield t
   219     for t2 in followers(t): yield t2
   221 template_regexp = re.compile("([\w_:]+)<")
   223 # Construct and return a pretty-printer lookup function for objfile, or
   224 # return None if the objfile doesn't contain SpiderMonkey code
   225 # (specifically, definitions for SpiderMonkey types).
   226 def lookup_for_objfile(objfile):
   227     # Create a type cache for this objfile.
   228     try:
   229         cache = TypeCache(objfile)
   230     except NotSpiderMonkeyObjfileError:
   231         if gdb.parameter("verbose"):
   232             gdb.write("objfile '%s' has no SpiderMonkey code; not registering pretty-printers\n"
   233                       % (objfile.filename,))
   234         return None
   236     # Return a pretty-printer for |value|, if we have one. This is the lookup
   237     # function object we place in each gdb.Objfile's pretty-printers list, so it
   238     # carries |name|, |enabled|, and |subprinters| attributes.
   239     def lookup(value):
   240         # If |table| has a pretty-printer for |tag|, apply it to |value|.
   241         def check_table(table, tag):
   242             if tag in table:
   243                 f = table[tag]
   244                 if f.enabled:
   245                     return f(value, cache)
   246             return None
   248         def check_table_by_type_name(table, t):
   249             if t.code == gdb.TYPE_CODE_TYPEDEF:
   250                 return check_table(table, str(t))
   251             elif t.code == gdb.TYPE_CODE_STRUCT and t.tag:
   252                 return check_table(table, t.tag)
   253             else:
   254                 return None
   256         for t in implemented_types(value.type):
   257             if t.code == gdb.TYPE_CODE_PTR:
   258                 for t2 in implemented_types(t.target()):
   259                     p = check_table_by_type_name(ptr_printers_by_tag, t2)
   260                     if p: return p
   261             elif t.code == gdb.TYPE_CODE_REF:
   262                 for t2 in implemented_types(t.target()):
   263                     p = check_table_by_type_name(ref_printers_by_tag, t2)
   264                     if p: return p
   265             else:
   266                 p = check_table_by_type_name(printers_by_tag, t)
   267                 if p: return p
   268                 if t.code == gdb.TYPE_CODE_STRUCT and t.tag:
   269                     m = template_regexp.match(t.tag)
   270                     if m:
   271                         p = check_table(template_printers_by_tag, m.group(1))
   272                         if p: return p
   274         # Failing that, look for a printer in printers_by_regexp. We have
   275         # to scan the whole list, so regexp printers should be used
   276         # sparingly.
   277         s = str(value.type)
   278         for (r, f) in printers_by_regexp:
   279             if f.enabled:
   280                 m = r.match(s)
   281                 if m:
   282                     p = f(value, cache)
   283                     if p: return p
   285         # No luck.
   286         return None
   288     # Give |lookup| the attributes expected of a pretty-printer with
   289     # subprinters, for enabling and disabling.
   290     lookup.name = "SpiderMonkey"
   291     lookup.enabled = True
   292     lookup.subprinters = subprinters
   294     return lookup
   296 # A base class for pretty-printers for pointer values that handles null
   297 # pointers, by declining to construct a pretty-printer for them at all.
   298 # Derived classes may simply assume that self.value is non-null.
   299 #
   300 # To help share code, this class can also be used with reference types.
   301 #
   302 # This class provides the following methods, which subclasses are free to
   303 # override:
   304 #
   305 # __init__(self, value, cache): Save value and cache as properties by those names
   306 #     on the instance.
   307 #
   308 # to_string(self): format the type's name and address, as GDB would, and then
   309 #     call a 'summary' method (which the subclass must define) to produce a
   310 #     description of the referent.
   311 #
   312 #     Note that pretty-printers returning a 'string' display hint must not use
   313 #     this default 'to_string' method, as GDB will take everything it returns,
   314 #     including the type name and address, as string contents.
   315 class Pointer(object):
   316     def __new__(cls, value, cache):
   317         # Don't try to provide pretty-printers for NULL pointers.
   318         if value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR and value == 0:
   319             return None
   320         return super(Pointer, cls).__new__(cls)
   322     def __init__(self, value, cache):
   323         self.value = value
   324         self.cache = cache
   326     def to_string(self):
   327         # See comment above.
   328         assert not hasattr(self, 'display_hint') or self.display_hint() != 'string'
   329         concrete_type = self.value.type.strip_typedefs()
   330         if concrete_type.code == gdb.TYPE_CODE_PTR:
   331             address = self.value.cast(self.cache.void_ptr_t)
   332         elif concrete_type.code == gdb.TYPE_CODE_REF:
   333             address = '@' + str(self.value.address.cast(self.cache.void_ptr_t))
   334         else:
   335             assert not "mozilla.prettyprinters.Pointer applied to bad value type"
   336         try:
   337             summary = self.summary()
   338         except gdb.MemoryError as r:
   339             summary = str(r)
   340         v = '(%s) %s %s' % (self.value.type, address, summary)
   341         return v
   343     def summary(self):
   344         raise NotImplementedError
   346 field_enum_value = None
   348 # Given |t|, a gdb.Type instance representing an enum type, return the
   349 # numeric value of the enum value named |name|.
   350 #
   351 # Pre-2012-4-18 versions of GDB store the value of an enum member on the
   352 # gdb.Field's 'bitpos' attribute; later versions store it on the 'enumval'
   353 # attribute. This function retrieves the value from either.
   354 def enum_value(t, name):
   355     global field_enum_value
   356     f = t[name]
   357     # Monkey-patching is a-okay in polyfills! Just because.
   358     if not field_enum_value:
   359         if hasattr(f, 'enumval'):
   360             field_enum_value = lambda f: f.enumval
   361         else:
   362             field_enum_value = lambda f: f.bitpos
   363     return field_enum_value(f)

mercurial