Ben Cheng | 09ebc13 | 2013-10-18 00:02:49 -0700 | [diff] [blame] | 1 | # Pretty-printer commands. |
Ben Cheng | 7334f0a | 2014-04-30 14:28:17 -0700 | [diff] [blame] | 2 | # Copyright (C) 2010-2014 Free Software Foundation, Inc. |
Ben Cheng | 09ebc13 | 2013-10-18 00:02:49 -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 | """GDB commands for working with pretty-printers.""" |
| 18 | |
| 19 | import copy |
| 20 | import gdb |
| 21 | import re |
| 22 | |
| 23 | |
| 24 | def parse_printer_regexps(arg): |
| 25 | """Internal utility to parse a pretty-printer command argv. |
| 26 | |
| 27 | Arguments: |
| 28 | arg: The arguments to the command. The format is: |
| 29 | [object-regexp [name-regexp]]. |
| 30 | Individual printers in a collection are named as |
| 31 | printer-name;subprinter-name. |
| 32 | |
| 33 | Returns: |
| 34 | The result is a 3-tuple of compiled regular expressions, except that |
| 35 | the resulting compiled subprinter regexp is None if not provided. |
| 36 | |
| 37 | Raises: |
| 38 | SyntaxError: an error processing ARG |
| 39 | """ |
| 40 | |
| 41 | argv = gdb.string_to_argv(arg); |
| 42 | argc = len(argv) |
| 43 | object_regexp = "" # match everything |
| 44 | name_regexp = "" # match everything |
| 45 | subname_regexp = None |
| 46 | if argc > 3: |
| 47 | raise SyntaxError("too many arguments") |
| 48 | if argc >= 1: |
| 49 | object_regexp = argv[0] |
| 50 | if argc >= 2: |
| 51 | name_subname = argv[1].split(";", 1) |
| 52 | name_regexp = name_subname[0] |
| 53 | if len(name_subname) == 2: |
| 54 | subname_regexp = name_subname[1] |
| 55 | # That re.compile raises SyntaxError was determined empirically. |
| 56 | # We catch it and reraise it to provide a slightly more useful |
| 57 | # error message for the user. |
| 58 | try: |
| 59 | object_re = re.compile(object_regexp) |
| 60 | except SyntaxError: |
| 61 | raise SyntaxError("invalid object regexp: %s" % object_regexp) |
| 62 | try: |
| 63 | name_re = re.compile (name_regexp) |
| 64 | except SyntaxError: |
| 65 | raise SyntaxError("invalid name regexp: %s" % name_regexp) |
| 66 | if subname_regexp is not None: |
| 67 | try: |
| 68 | subname_re = re.compile(subname_regexp) |
| 69 | except SyntaxError: |
| 70 | raise SyntaxError("invalid subname regexp: %s" % subname_regexp) |
| 71 | else: |
| 72 | subname_re = None |
| 73 | return(object_re, name_re, subname_re) |
| 74 | |
| 75 | |
| 76 | def printer_enabled_p(printer): |
| 77 | """Internal utility to see if printer (or subprinter) is enabled.""" |
| 78 | if hasattr(printer, "enabled"): |
| 79 | return printer.enabled |
| 80 | else: |
| 81 | return True |
| 82 | |
| 83 | |
| 84 | class InfoPrettyPrinter(gdb.Command): |
| 85 | """GDB command to list all registered pretty-printers. |
| 86 | |
| 87 | Usage: info pretty-printer [object-regexp [name-regexp]] |
| 88 | |
| 89 | OBJECT-REGEXP is a regular expression matching the objects to list. |
| 90 | Objects are "global", the program space's file, and the objfiles within |
| 91 | that program space. |
| 92 | |
| 93 | NAME-REGEXP matches the name of the pretty-printer. |
| 94 | Individual printers in a collection are named as |
| 95 | printer-name;subprinter-name. |
| 96 | """ |
| 97 | |
| 98 | def __init__ (self): |
| 99 | super(InfoPrettyPrinter, self).__init__("info pretty-printer", |
| 100 | gdb.COMMAND_DATA) |
| 101 | |
| 102 | @staticmethod |
| 103 | def enabled_string(printer): |
| 104 | """Return "" if PRINTER is enabled, otherwise " [disabled]".""" |
| 105 | if printer_enabled_p(printer): |
| 106 | return "" |
| 107 | else: |
| 108 | return " [disabled]" |
| 109 | |
| 110 | @staticmethod |
| 111 | def printer_name(printer): |
| 112 | """Return the printer's name.""" |
| 113 | if hasattr(printer, "name"): |
| 114 | return printer.name |
| 115 | if hasattr(printer, "__name__"): |
| 116 | return printer.__name__ |
| 117 | # This "shouldn't happen", but the public API allows for |
| 118 | # direct additions to the pretty-printer list, and we shouldn't |
| 119 | # crash because someone added a bogus printer. |
| 120 | # Plus we want to give the user a way to list unknown printers. |
| 121 | return "unknown" |
| 122 | |
| 123 | def list_pretty_printers(self, pretty_printers, name_re, subname_re): |
| 124 | """Print a list of pretty-printers.""" |
| 125 | # A potential enhancement is to provide an option to list printers in |
| 126 | # "lookup order" (i.e. unsorted). |
| 127 | sorted_pretty_printers = sorted (copy.copy(pretty_printers), |
| 128 | key = self.printer_name) |
| 129 | for printer in sorted_pretty_printers: |
| 130 | name = self.printer_name(printer) |
| 131 | enabled = self.enabled_string(printer) |
| 132 | if name_re.match(name): |
| 133 | print (" %s%s" % (name, enabled)) |
| 134 | if (hasattr(printer, "subprinters") and |
| 135 | printer.subprinters is not None): |
| 136 | sorted_subprinters = sorted (copy.copy(printer.subprinters), |
| 137 | key = self.printer_name) |
| 138 | for subprinter in sorted_subprinters: |
| 139 | if (not subname_re or |
| 140 | subname_re.match(subprinter.name)): |
| 141 | print (" %s%s" % |
| 142 | (subprinter.name, |
| 143 | self.enabled_string(subprinter))) |
| 144 | |
| 145 | def invoke1(self, title, printer_list, |
| 146 | obj_name_to_match, object_re, name_re, subname_re): |
| 147 | """Subroutine of invoke to simplify it.""" |
| 148 | if printer_list and object_re.match(obj_name_to_match): |
| 149 | print (title) |
| 150 | self.list_pretty_printers(printer_list, name_re, subname_re) |
| 151 | |
| 152 | def invoke(self, arg, from_tty): |
| 153 | """GDB calls this to perform the command.""" |
| 154 | (object_re, name_re, subname_re) = parse_printer_regexps(arg) |
| 155 | self.invoke1("global pretty-printers:", gdb.pretty_printers, |
| 156 | "global", object_re, name_re, subname_re) |
| 157 | cp = gdb.current_progspace() |
| 158 | self.invoke1("progspace %s pretty-printers:" % cp.filename, |
| 159 | cp.pretty_printers, "progspace", |
| 160 | object_re, name_re, subname_re) |
| 161 | for objfile in gdb.objfiles(): |
| 162 | self.invoke1(" objfile %s pretty-printers:" % objfile.filename, |
| 163 | objfile.pretty_printers, objfile.filename, |
| 164 | object_re, name_re, subname_re) |
| 165 | |
| 166 | |
| 167 | def count_enabled_printers(pretty_printers): |
| 168 | """Return a 2-tuple of number of enabled and total printers.""" |
| 169 | enabled = 0 |
| 170 | total = 0 |
| 171 | for printer in pretty_printers: |
| 172 | if (hasattr(printer, "subprinters") |
| 173 | and printer.subprinters is not None): |
| 174 | if printer_enabled_p(printer): |
| 175 | for subprinter in printer.subprinters: |
| 176 | if printer_enabled_p(subprinter): |
| 177 | enabled += 1 |
| 178 | total += len(printer.subprinters) |
| 179 | else: |
| 180 | if printer_enabled_p(printer): |
| 181 | enabled += 1 |
| 182 | total += 1 |
| 183 | return (enabled, total) |
| 184 | |
| 185 | |
| 186 | def count_all_enabled_printers(): |
| 187 | """Return a 2-tuble of the enabled state and total number of all printers. |
| 188 | This includes subprinters. |
| 189 | """ |
| 190 | enabled_count = 0 |
| 191 | total_count = 0 |
| 192 | (t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers) |
| 193 | enabled_count += t_enabled |
| 194 | total_count += t_total |
| 195 | (t_enabled, t_total) = count_enabled_printers(gdb.current_progspace().pretty_printers) |
| 196 | enabled_count += t_enabled |
| 197 | total_count += t_total |
| 198 | for objfile in gdb.objfiles(): |
| 199 | (t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers) |
| 200 | enabled_count += t_enabled |
| 201 | total_count += t_total |
| 202 | return (enabled_count, total_count) |
| 203 | |
| 204 | |
| 205 | def pluralize(text, n, suffix="s"): |
| 206 | """Return TEXT pluralized if N != 1.""" |
| 207 | if n != 1: |
| 208 | return "%s%s" % (text, suffix) |
| 209 | else: |
| 210 | return text |
| 211 | |
| 212 | |
| 213 | def show_pretty_printer_enabled_summary(): |
| 214 | """Print the number of printers enabled/disabled. |
| 215 | We count subprinters individually. |
| 216 | """ |
| 217 | (enabled_count, total_count) = count_all_enabled_printers() |
| 218 | print ("%d of %d printers enabled" % (enabled_count, total_count)) |
| 219 | |
| 220 | |
| 221 | def do_enable_pretty_printer_1 (pretty_printers, name_re, subname_re, flag): |
| 222 | """Worker for enabling/disabling pretty-printers. |
| 223 | |
| 224 | Arguments: |
| 225 | pretty_printers: list of pretty-printers |
| 226 | name_re: regular-expression object to select printers |
| 227 | subname_re: regular expression object to select subprinters or None |
| 228 | if all are affected |
| 229 | flag: True for Enable, False for Disable |
| 230 | |
| 231 | Returns: |
| 232 | The number of printers affected. |
| 233 | This is just for informational purposes for the user. |
| 234 | """ |
| 235 | total = 0 |
| 236 | for printer in pretty_printers: |
| 237 | if (hasattr(printer, "name") and name_re.match(printer.name) or |
| 238 | hasattr(printer, "__name__") and name_re.match(printer.__name__)): |
| 239 | if (hasattr(printer, "subprinters") and |
| 240 | printer.subprinters is not None): |
| 241 | if not subname_re: |
| 242 | # Only record printers that change state. |
| 243 | if printer_enabled_p(printer) != flag: |
| 244 | for subprinter in printer.subprinters: |
| 245 | if printer_enabled_p(subprinter): |
| 246 | total += 1 |
| 247 | # NOTE: We preserve individual subprinter settings. |
| 248 | printer.enabled = flag |
| 249 | else: |
| 250 | # NOTE: Whether this actually disables the subprinter |
| 251 | # depends on whether the printer's lookup function supports |
| 252 | # the "enable" API. We can only assume it does. |
| 253 | for subprinter in printer.subprinters: |
| 254 | if subname_re.match(subprinter.name): |
| 255 | # Only record printers that change state. |
| 256 | if (printer_enabled_p(printer) and |
| 257 | printer_enabled_p(subprinter) != flag): |
| 258 | total += 1 |
| 259 | subprinter.enabled = flag |
| 260 | else: |
| 261 | # This printer has no subprinters. |
| 262 | # If the user does "disable pretty-printer .* .* foo" |
| 263 | # should we disable printers that don't have subprinters? |
| 264 | # How do we apply "foo" in this context? Since there is no |
| 265 | # "foo" subprinter it feels like we should skip this printer. |
| 266 | # There's still the issue of how to handle |
| 267 | # "disable pretty-printer .* .* .*", and every other variation |
| 268 | # that can match everything. For now punt and only support |
| 269 | # "disable pretty-printer .* .*" (i.e. subname is elided) |
| 270 | # to disable everything. |
| 271 | if not subname_re: |
| 272 | # Only record printers that change state. |
| 273 | if printer_enabled_p(printer) != flag: |
| 274 | total += 1 |
| 275 | printer.enabled = flag |
| 276 | return total |
| 277 | |
| 278 | |
| 279 | def do_enable_pretty_printer (arg, flag): |
| 280 | """Internal worker for enabling/disabling pretty-printers.""" |
| 281 | (object_re, name_re, subname_re) = parse_printer_regexps(arg) |
| 282 | |
| 283 | total = 0 |
| 284 | if object_re.match("global"): |
| 285 | total += do_enable_pretty_printer_1(gdb.pretty_printers, |
| 286 | name_re, subname_re, flag) |
| 287 | cp = gdb.current_progspace() |
| 288 | if object_re.match("progspace"): |
| 289 | total += do_enable_pretty_printer_1(cp.pretty_printers, |
| 290 | name_re, subname_re, flag) |
| 291 | for objfile in gdb.objfiles(): |
| 292 | if object_re.match(objfile.filename): |
| 293 | total += do_enable_pretty_printer_1(objfile.pretty_printers, |
| 294 | name_re, subname_re, flag) |
| 295 | |
| 296 | if flag: |
| 297 | state = "enabled" |
| 298 | else: |
| 299 | state = "disabled" |
| 300 | print ("%d %s %s" % (total, pluralize("printer", total), state)) |
| 301 | |
| 302 | # Print the total list of printers currently enabled/disabled. |
| 303 | # This is to further assist the user in determining whether the result |
| 304 | # is expected. Since we use regexps to select it's useful. |
| 305 | show_pretty_printer_enabled_summary() |
| 306 | |
| 307 | |
| 308 | # Enable/Disable one or more pretty-printers. |
| 309 | # |
| 310 | # This is intended for use when a broken pretty-printer is shipped/installed |
| 311 | # and the user wants to disable that printer without disabling all the other |
| 312 | # printers. |
| 313 | # |
| 314 | # A useful addition would be -v (verbose) to show each printer affected. |
| 315 | |
| 316 | class EnablePrettyPrinter (gdb.Command): |
| 317 | """GDB command to enable the specified pretty-printer. |
| 318 | |
| 319 | Usage: enable pretty-printer [object-regexp [name-regexp]] |
| 320 | |
| 321 | OBJECT-REGEXP is a regular expression matching the objects to examine. |
| 322 | Objects are "global", the program space's file, and the objfiles within |
| 323 | that program space. |
| 324 | |
| 325 | NAME-REGEXP matches the name of the pretty-printer. |
| 326 | Individual printers in a collection are named as |
| 327 | printer-name;subprinter-name. |
| 328 | """ |
| 329 | |
| 330 | def __init__(self): |
| 331 | super(EnablePrettyPrinter, self).__init__("enable pretty-printer", |
| 332 | gdb.COMMAND_DATA) |
| 333 | |
| 334 | def invoke(self, arg, from_tty): |
| 335 | """GDB calls this to perform the command.""" |
| 336 | do_enable_pretty_printer(arg, True) |
| 337 | |
| 338 | |
| 339 | class DisablePrettyPrinter (gdb.Command): |
| 340 | """GDB command to disable the specified pretty-printer. |
| 341 | |
| 342 | Usage: disable pretty-printer [object-regexp [name-regexp]] |
| 343 | |
| 344 | OBJECT-REGEXP is a regular expression matching the objects to examine. |
| 345 | Objects are "global", the program space's file, and the objfiles within |
| 346 | that program space. |
| 347 | |
| 348 | NAME-REGEXP matches the name of the pretty-printer. |
| 349 | Individual printers in a collection are named as |
| 350 | printer-name;subprinter-name. |
| 351 | """ |
| 352 | |
| 353 | def __init__(self): |
| 354 | super(DisablePrettyPrinter, self).__init__("disable pretty-printer", |
| 355 | gdb.COMMAND_DATA) |
| 356 | |
| 357 | def invoke(self, arg, from_tty): |
| 358 | """GDB calls this to perform the command.""" |
| 359 | do_enable_pretty_printer(arg, False) |
| 360 | |
| 361 | |
| 362 | def register_pretty_printer_commands(): |
| 363 | """Call from a top level script to install the pretty-printer commands.""" |
| 364 | InfoPrettyPrinter() |
| 365 | EnablePrettyPrinter() |
| 366 | DisablePrettyPrinter() |
| 367 | |
| 368 | register_pretty_printer_commands() |