Ben Cheng | 7823f2a | 2014-04-08 14:53:42 -0700 | [diff] [blame] | 1 | # Pretty-printer utilities. |
Rong Xu | a7bb6f3 | 2014-11-03 13:21:25 -0800 | [diff] [blame] | 2 | # Copyright (C) 2010-2014 Free Software Foundation, Inc. |
Ben Cheng | 7823f2a | 2014-04-08 14:53:42 -0700 | [diff] [blame] | 3 | |
| 4 | # This program is free software; you can redistribute it and/or modify |
| 5 | # it under the terms of the GNU General Public License as published by |
| 6 | # the Free Software Foundation; either version 3 of the License, or |
| 7 | # (at your option) any later version. |
| 8 | # |
| 9 | # This program is distributed in the hope that it will be useful, |
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | # GNU General Public License for more details. |
| 13 | # |
| 14 | # You should have received a copy of the GNU General Public License |
| 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | |
| 17 | """Utilities for working with pretty-printers.""" |
| 18 | |
| 19 | import gdb |
| 20 | import gdb.types |
| 21 | import re |
| 22 | import sys |
| 23 | |
| 24 | if sys.version_info[0] > 2: |
| 25 | # Python 3 removed basestring and long |
| 26 | basestring = str |
| 27 | long = int |
| 28 | |
| 29 | class PrettyPrinter(object): |
| 30 | """A basic pretty-printer. |
| 31 | |
| 32 | Attributes: |
| 33 | name: A unique string among all printers for the context in which |
| 34 | it is defined (objfile, progspace, or global(gdb)), and should |
| 35 | meaningfully describe what can be pretty-printed. |
| 36 | E.g., "StringPiece" or "protobufs". |
| 37 | subprinters: An iterable object with each element having a `name' |
| 38 | attribute, and, potentially, "enabled" attribute. |
| 39 | Or this is None if there are no subprinters. |
| 40 | enabled: A boolean indicating if the printer is enabled. |
| 41 | |
| 42 | Subprinters are for situations where "one" pretty-printer is actually a |
| 43 | collection of several printers. E.g., The libstdc++ pretty-printer has |
| 44 | a pretty-printer for each of several different types, based on regexps. |
| 45 | """ |
| 46 | |
| 47 | # While one might want to push subprinters into the subclass, it's |
| 48 | # present here to formalize such support to simplify |
| 49 | # commands/pretty_printers.py. |
| 50 | |
| 51 | def __init__(self, name, subprinters=None): |
| 52 | self.name = name |
| 53 | self.subprinters = subprinters |
| 54 | self.enabled = True |
| 55 | |
| 56 | def __call__(self, val): |
| 57 | # The subclass must define this. |
| 58 | raise NotImplementedError("PrettyPrinter __call__") |
| 59 | |
| 60 | |
| 61 | class SubPrettyPrinter(object): |
| 62 | """Baseclass for sub-pretty-printers. |
| 63 | |
| 64 | Sub-pretty-printers needn't use this, but it formalizes what's needed. |
| 65 | |
| 66 | Attributes: |
| 67 | name: The name of the subprinter. |
| 68 | enabled: A boolean indicating if the subprinter is enabled. |
| 69 | """ |
| 70 | |
| 71 | def __init__(self, name): |
| 72 | self.name = name |
| 73 | self.enabled = True |
| 74 | |
| 75 | |
| 76 | def register_pretty_printer(obj, printer, replace=False): |
| 77 | """Register pretty-printer PRINTER with OBJ. |
| 78 | |
| 79 | The printer is added to the front of the search list, thus one can override |
| 80 | an existing printer if one needs to. Use a different name when overriding |
| 81 | an existing printer, otherwise an exception will be raised; multiple |
| 82 | printers with the same name are disallowed. |
| 83 | |
| 84 | Arguments: |
| 85 | obj: Either an objfile, progspace, or None (in which case the printer |
| 86 | is registered globally). |
| 87 | printer: Either a function of one argument (old way) or any object |
| 88 | which has attributes: name, enabled, __call__. |
| 89 | replace: If True replace any existing copy of the printer. |
| 90 | Otherwise if the printer already exists raise an exception. |
| 91 | |
| 92 | Returns: |
| 93 | Nothing. |
| 94 | |
| 95 | Raises: |
| 96 | TypeError: A problem with the type of the printer. |
| 97 | ValueError: The printer's name contains a semicolon ";". |
| 98 | RuntimeError: A printer with the same name is already registered. |
| 99 | |
| 100 | If the caller wants the printer to be listable and disableable, it must |
| 101 | follow the PrettyPrinter API. This applies to the old way (functions) too. |
| 102 | If printer is an object, __call__ is a method of two arguments: |
| 103 | self, and the value to be pretty-printed. See PrettyPrinter. |
| 104 | """ |
| 105 | |
| 106 | # Watch for both __name__ and name. |
| 107 | # Functions get the former for free, but we don't want to use an |
| 108 | # attribute named __foo__ for pretty-printers-as-objects. |
| 109 | # If printer has both, we use `name'. |
| 110 | if not hasattr(printer, "__name__") and not hasattr(printer, "name"): |
| 111 | raise TypeError("printer missing attribute: name") |
| 112 | if hasattr(printer, "name") and not hasattr(printer, "enabled"): |
| 113 | raise TypeError("printer missing attribute: enabled") |
| 114 | if not hasattr(printer, "__call__"): |
| 115 | raise TypeError("printer missing attribute: __call__") |
| 116 | |
| 117 | if obj is None: |
| 118 | if gdb.parameter("verbose"): |
| 119 | gdb.write("Registering global %s pretty-printer ...\n" % name) |
| 120 | obj = gdb |
| 121 | else: |
| 122 | if gdb.parameter("verbose"): |
| 123 | gdb.write("Registering %s pretty-printer for %s ...\n" % |
| 124 | (printer.name, obj.filename)) |
| 125 | |
| 126 | if hasattr(printer, "name"): |
| 127 | if not isinstance(printer.name, basestring): |
| 128 | raise TypeError("printer name is not a string") |
| 129 | # If printer provides a name, make sure it doesn't contain ";". |
| 130 | # Semicolon is used by the info/enable/disable pretty-printer commands |
| 131 | # to delimit subprinters. |
| 132 | if printer.name.find(";") >= 0: |
| 133 | raise ValueError("semicolon ';' in printer name") |
| 134 | # Also make sure the name is unique. |
| 135 | # Alas, we can't do the same for functions and __name__, they could |
| 136 | # all have a canonical name like "lookup_function". |
| 137 | # PERF: gdb records printers in a list, making this inefficient. |
| 138 | i = 0 |
| 139 | for p in obj.pretty_printers: |
| 140 | if hasattr(p, "name") and p.name == printer.name: |
| 141 | if replace: |
| 142 | del obj.pretty_printers[i] |
| 143 | break |
| 144 | else: |
| 145 | raise RuntimeError("pretty-printer already registered: %s" % |
| 146 | printer.name) |
| 147 | i = i + 1 |
| 148 | |
| 149 | obj.pretty_printers.insert(0, printer) |
| 150 | |
| 151 | |
| 152 | class RegexpCollectionPrettyPrinter(PrettyPrinter): |
| 153 | """Class for implementing a collection of regular-expression based pretty-printers. |
| 154 | |
| 155 | Intended usage: |
| 156 | |
| 157 | pretty_printer = RegexpCollectionPrettyPrinter("my_library") |
| 158 | pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer) |
| 159 | ... |
| 160 | pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter) |
| 161 | register_pretty_printer(obj, pretty_printer) |
| 162 | """ |
| 163 | |
| 164 | class RegexpSubprinter(SubPrettyPrinter): |
| 165 | def __init__(self, name, regexp, gen_printer): |
| 166 | super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name) |
| 167 | self.regexp = regexp |
| 168 | self.gen_printer = gen_printer |
| 169 | self.compiled_re = re.compile(regexp) |
| 170 | |
| 171 | def __init__(self, name): |
| 172 | super(RegexpCollectionPrettyPrinter, self).__init__(name, []) |
| 173 | |
| 174 | def add_printer(self, name, regexp, gen_printer): |
| 175 | """Add a printer to the list. |
| 176 | |
| 177 | The printer is added to the end of the list. |
| 178 | |
| 179 | Arguments: |
| 180 | name: The name of the subprinter. |
| 181 | regexp: The regular expression, as a string. |
| 182 | gen_printer: A function/method that given a value returns an |
| 183 | object to pretty-print it. |
| 184 | |
| 185 | Returns: |
| 186 | Nothing. |
| 187 | """ |
| 188 | |
| 189 | # NOTE: A previous version made the name of each printer the regexp. |
| 190 | # That makes it awkward to pass to the enable/disable commands (it's |
| 191 | # cumbersome to make a regexp of a regexp). So now the name is a |
| 192 | # separate parameter. |
| 193 | |
| 194 | self.subprinters.append(self.RegexpSubprinter(name, regexp, |
| 195 | gen_printer)) |
| 196 | |
| 197 | def __call__(self, val): |
| 198 | """Lookup the pretty-printer for the provided value.""" |
| 199 | |
| 200 | # Get the type name. |
| 201 | typename = gdb.types.get_basic_type(val.type).tag |
| 202 | if not typename: |
| 203 | return None |
| 204 | |
| 205 | # Iterate over table of type regexps to determine |
| 206 | # if a printer is registered for that type. |
| 207 | # Return an instantiation of the printer if found. |
| 208 | for printer in self.subprinters: |
| 209 | if printer.enabled and printer.compiled_re.search(typename): |
| 210 | return printer.gen_printer(val) |
| 211 | |
| 212 | # Cannot find a pretty printer. Return None. |
| 213 | return None |
| 214 | |
| 215 | # A helper class for printing enum types. This class is instantiated |
| 216 | # with a list of enumerators to print a particular Value. |
| 217 | class _EnumInstance: |
| 218 | def __init__(self, enumerators, val): |
| 219 | self.enumerators = enumerators |
| 220 | self.val = val |
| 221 | |
| 222 | def to_string(self): |
| 223 | flag_list = [] |
| 224 | v = long(self.val) |
| 225 | any_found = False |
| 226 | for (e_name, e_value) in self.enumerators: |
| 227 | if v & e_value != 0: |
| 228 | flag_list.append(e_name) |
| 229 | v = v & ~e_value |
| 230 | any_found = True |
| 231 | if not any_found or v != 0: |
| 232 | # Leftover value. |
| 233 | flag_list.append('<unknown: 0x%x>' % v) |
| 234 | return "0x%x [%s]" % (self.val, " | ".join(flag_list)) |
| 235 | |
| 236 | class FlagEnumerationPrinter(PrettyPrinter): |
| 237 | """A pretty-printer which can be used to print a flag-style enumeration. |
| 238 | A flag-style enumeration is one where the enumerators are or'd |
| 239 | together to create values. The new printer will print these |
| 240 | symbolically using '|' notation. The printer must be registered |
| 241 | manually. This printer is most useful when an enum is flag-like, |
| 242 | but has some overlap. GDB's built-in printing will not handle |
| 243 | this case, but this printer will attempt to.""" |
| 244 | |
| 245 | def __init__(self, enum_type): |
| 246 | super(FlagEnumerationPrinter, self).__init__(enum_type) |
| 247 | self.initialized = False |
| 248 | |
| 249 | def __call__(self, val): |
| 250 | if not self.initialized: |
| 251 | self.initialized = True |
| 252 | flags = gdb.lookup_type(self.name) |
| 253 | self.enumerators = [] |
| 254 | for field in flags.fields(): |
| 255 | self.enumerators.append((field.name, field.enumval)) |
| 256 | # Sorting the enumerators by value usually does the right |
| 257 | # thing. |
| 258 | self.enumerators.sort(key = lambda x: x.enumval) |
| 259 | |
| 260 | if self.enabled: |
| 261 | return _EnumInstance(self.enumerators, val) |
| 262 | else: |
| 263 | return None |