| # Pretty-printer commands. |
| # Copyright (C) 2010-2013 Free Software Foundation, Inc. |
| |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 3 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| |
| """GDB commands for working with pretty-printers.""" |
| |
| import copy |
| import gdb |
| import re |
| |
| |
| def parse_printer_regexps(arg): |
| """Internal utility to parse a pretty-printer command argv. |
| |
| Arguments: |
| arg: The arguments to the command. The format is: |
| [object-regexp [name-regexp]]. |
| Individual printers in a collection are named as |
| printer-name;subprinter-name. |
| |
| Returns: |
| The result is a 3-tuple of compiled regular expressions, except that |
| the resulting compiled subprinter regexp is None if not provided. |
| |
| Raises: |
| SyntaxError: an error processing ARG |
| """ |
| |
| argv = gdb.string_to_argv(arg); |
| argc = len(argv) |
| object_regexp = "" # match everything |
| name_regexp = "" # match everything |
| subname_regexp = None |
| if argc > 3: |
| raise SyntaxError("too many arguments") |
| if argc >= 1: |
| object_regexp = argv[0] |
| if argc >= 2: |
| name_subname = argv[1].split(";", 1) |
| name_regexp = name_subname[0] |
| if len(name_subname) == 2: |
| subname_regexp = name_subname[1] |
| # That re.compile raises SyntaxError was determined empirically. |
| # We catch it and reraise it to provide a slightly more useful |
| # error message for the user. |
| try: |
| object_re = re.compile(object_regexp) |
| except SyntaxError: |
| raise SyntaxError("invalid object regexp: %s" % object_regexp) |
| try: |
| name_re = re.compile (name_regexp) |
| except SyntaxError: |
| raise SyntaxError("invalid name regexp: %s" % name_regexp) |
| if subname_regexp is not None: |
| try: |
| subname_re = re.compile(subname_regexp) |
| except SyntaxError: |
| raise SyntaxError("invalid subname regexp: %s" % subname_regexp) |
| else: |
| subname_re = None |
| return(object_re, name_re, subname_re) |
| |
| |
| def printer_enabled_p(printer): |
| """Internal utility to see if printer (or subprinter) is enabled.""" |
| if hasattr(printer, "enabled"): |
| return printer.enabled |
| else: |
| return True |
| |
| |
| class InfoPrettyPrinter(gdb.Command): |
| """GDB command to list all registered pretty-printers. |
| |
| Usage: info pretty-printer [object-regexp [name-regexp]] |
| |
| OBJECT-REGEXP is a regular expression matching the objects to list. |
| Objects are "global", the program space's file, and the objfiles within |
| that program space. |
| |
| NAME-REGEXP matches the name of the pretty-printer. |
| Individual printers in a collection are named as |
| printer-name;subprinter-name. |
| """ |
| |
| def __init__ (self): |
| super(InfoPrettyPrinter, self).__init__("info pretty-printer", |
| gdb.COMMAND_DATA) |
| |
| @staticmethod |
| def enabled_string(printer): |
| """Return "" if PRINTER is enabled, otherwise " [disabled]".""" |
| if printer_enabled_p(printer): |
| return "" |
| else: |
| return " [disabled]" |
| |
| @staticmethod |
| def printer_name(printer): |
| """Return the printer's name.""" |
| if hasattr(printer, "name"): |
| return printer.name |
| if hasattr(printer, "__name__"): |
| return printer.__name__ |
| # This "shouldn't happen", but the public API allows for |
| # direct additions to the pretty-printer list, and we shouldn't |
| # crash because someone added a bogus printer. |
| # Plus we want to give the user a way to list unknown printers. |
| return "unknown" |
| |
| def list_pretty_printers(self, pretty_printers, name_re, subname_re): |
| """Print a list of pretty-printers.""" |
| # A potential enhancement is to provide an option to list printers in |
| # "lookup order" (i.e. unsorted). |
| sorted_pretty_printers = sorted (copy.copy(pretty_printers), |
| key = self.printer_name) |
| for printer in sorted_pretty_printers: |
| name = self.printer_name(printer) |
| enabled = self.enabled_string(printer) |
| if name_re.match(name): |
| print (" %s%s" % (name, enabled)) |
| if (hasattr(printer, "subprinters") and |
| printer.subprinters is not None): |
| sorted_subprinters = sorted (copy.copy(printer.subprinters), |
| key = self.printer_name) |
| for subprinter in sorted_subprinters: |
| if (not subname_re or |
| subname_re.match(subprinter.name)): |
| print (" %s%s" % |
| (subprinter.name, |
| self.enabled_string(subprinter))) |
| |
| def invoke1(self, title, printer_list, |
| obj_name_to_match, object_re, name_re, subname_re): |
| """Subroutine of invoke to simplify it.""" |
| if printer_list and object_re.match(obj_name_to_match): |
| print (title) |
| self.list_pretty_printers(printer_list, name_re, subname_re) |
| |
| def invoke(self, arg, from_tty): |
| """GDB calls this to perform the command.""" |
| (object_re, name_re, subname_re) = parse_printer_regexps(arg) |
| self.invoke1("global pretty-printers:", gdb.pretty_printers, |
| "global", object_re, name_re, subname_re) |
| cp = gdb.current_progspace() |
| self.invoke1("progspace %s pretty-printers:" % cp.filename, |
| cp.pretty_printers, "progspace", |
| object_re, name_re, subname_re) |
| for objfile in gdb.objfiles(): |
| self.invoke1(" objfile %s pretty-printers:" % objfile.filename, |
| objfile.pretty_printers, objfile.filename, |
| object_re, name_re, subname_re) |
| |
| |
| def count_enabled_printers(pretty_printers): |
| """Return a 2-tuple of number of enabled and total printers.""" |
| enabled = 0 |
| total = 0 |
| for printer in pretty_printers: |
| if (hasattr(printer, "subprinters") |
| and printer.subprinters is not None): |
| if printer_enabled_p(printer): |
| for subprinter in printer.subprinters: |
| if printer_enabled_p(subprinter): |
| enabled += 1 |
| total += len(printer.subprinters) |
| else: |
| if printer_enabled_p(printer): |
| enabled += 1 |
| total += 1 |
| return (enabled, total) |
| |
| |
| def count_all_enabled_printers(): |
| """Return a 2-tuble of the enabled state and total number of all printers. |
| This includes subprinters. |
| """ |
| enabled_count = 0 |
| total_count = 0 |
| (t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers) |
| enabled_count += t_enabled |
| total_count += t_total |
| (t_enabled, t_total) = count_enabled_printers(gdb.current_progspace().pretty_printers) |
| enabled_count += t_enabled |
| total_count += t_total |
| for objfile in gdb.objfiles(): |
| (t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers) |
| enabled_count += t_enabled |
| total_count += t_total |
| return (enabled_count, total_count) |
| |
| |
| def pluralize(text, n, suffix="s"): |
| """Return TEXT pluralized if N != 1.""" |
| if n != 1: |
| return "%s%s" % (text, suffix) |
| else: |
| return text |
| |
| |
| def show_pretty_printer_enabled_summary(): |
| """Print the number of printers enabled/disabled. |
| We count subprinters individually. |
| """ |
| (enabled_count, total_count) = count_all_enabled_printers() |
| print ("%d of %d printers enabled" % (enabled_count, total_count)) |
| |
| |
| def do_enable_pretty_printer_1 (pretty_printers, name_re, subname_re, flag): |
| """Worker for enabling/disabling pretty-printers. |
| |
| Arguments: |
| pretty_printers: list of pretty-printers |
| name_re: regular-expression object to select printers |
| subname_re: regular expression object to select subprinters or None |
| if all are affected |
| flag: True for Enable, False for Disable |
| |
| Returns: |
| The number of printers affected. |
| This is just for informational purposes for the user. |
| """ |
| total = 0 |
| for printer in pretty_printers: |
| if (hasattr(printer, "name") and name_re.match(printer.name) or |
| hasattr(printer, "__name__") and name_re.match(printer.__name__)): |
| if (hasattr(printer, "subprinters") and |
| printer.subprinters is not None): |
| if not subname_re: |
| # Only record printers that change state. |
| if printer_enabled_p(printer) != flag: |
| for subprinter in printer.subprinters: |
| if printer_enabled_p(subprinter): |
| total += 1 |
| # NOTE: We preserve individual subprinter settings. |
| printer.enabled = flag |
| else: |
| # NOTE: Whether this actually disables the subprinter |
| # depends on whether the printer's lookup function supports |
| # the "enable" API. We can only assume it does. |
| for subprinter in printer.subprinters: |
| if subname_re.match(subprinter.name): |
| # Only record printers that change state. |
| if (printer_enabled_p(printer) and |
| printer_enabled_p(subprinter) != flag): |
| total += 1 |
| subprinter.enabled = flag |
| else: |
| # This printer has no subprinters. |
| # If the user does "disable pretty-printer .* .* foo" |
| # should we disable printers that don't have subprinters? |
| # How do we apply "foo" in this context? Since there is no |
| # "foo" subprinter it feels like we should skip this printer. |
| # There's still the issue of how to handle |
| # "disable pretty-printer .* .* .*", and every other variation |
| # that can match everything. For now punt and only support |
| # "disable pretty-printer .* .*" (i.e. subname is elided) |
| # to disable everything. |
| if not subname_re: |
| # Only record printers that change state. |
| if printer_enabled_p(printer) != flag: |
| total += 1 |
| printer.enabled = flag |
| return total |
| |
| |
| def do_enable_pretty_printer (arg, flag): |
| """Internal worker for enabling/disabling pretty-printers.""" |
| (object_re, name_re, subname_re) = parse_printer_regexps(arg) |
| |
| total = 0 |
| if object_re.match("global"): |
| total += do_enable_pretty_printer_1(gdb.pretty_printers, |
| name_re, subname_re, flag) |
| cp = gdb.current_progspace() |
| if object_re.match("progspace"): |
| total += do_enable_pretty_printer_1(cp.pretty_printers, |
| name_re, subname_re, flag) |
| for objfile in gdb.objfiles(): |
| if object_re.match(objfile.filename): |
| total += do_enable_pretty_printer_1(objfile.pretty_printers, |
| name_re, subname_re, flag) |
| |
| if flag: |
| state = "enabled" |
| else: |
| state = "disabled" |
| print ("%d %s %s" % (total, pluralize("printer", total), state)) |
| |
| # Print the total list of printers currently enabled/disabled. |
| # This is to further assist the user in determining whether the result |
| # is expected. Since we use regexps to select it's useful. |
| show_pretty_printer_enabled_summary() |
| |
| |
| # Enable/Disable one or more pretty-printers. |
| # |
| # This is intended for use when a broken pretty-printer is shipped/installed |
| # and the user wants to disable that printer without disabling all the other |
| # printers. |
| # |
| # A useful addition would be -v (verbose) to show each printer affected. |
| |
| class EnablePrettyPrinter (gdb.Command): |
| """GDB command to enable the specified pretty-printer. |
| |
| Usage: enable pretty-printer [object-regexp [name-regexp]] |
| |
| OBJECT-REGEXP is a regular expression matching the objects to examine. |
| Objects are "global", the program space's file, and the objfiles within |
| that program space. |
| |
| NAME-REGEXP matches the name of the pretty-printer. |
| Individual printers in a collection are named as |
| printer-name;subprinter-name. |
| """ |
| |
| def __init__(self): |
| super(EnablePrettyPrinter, self).__init__("enable pretty-printer", |
| gdb.COMMAND_DATA) |
| |
| def invoke(self, arg, from_tty): |
| """GDB calls this to perform the command.""" |
| do_enable_pretty_printer(arg, True) |
| |
| |
| class DisablePrettyPrinter (gdb.Command): |
| """GDB command to disable the specified pretty-printer. |
| |
| Usage: disable pretty-printer [object-regexp [name-regexp]] |
| |
| OBJECT-REGEXP is a regular expression matching the objects to examine. |
| Objects are "global", the program space's file, and the objfiles within |
| that program space. |
| |
| NAME-REGEXP matches the name of the pretty-printer. |
| Individual printers in a collection are named as |
| printer-name;subprinter-name. |
| """ |
| |
| def __init__(self): |
| super(DisablePrettyPrinter, self).__init__("disable pretty-printer", |
| gdb.COMMAND_DATA) |
| |
| def invoke(self, arg, from_tty): |
| """GDB calls this to perform the command.""" |
| do_enable_pretty_printer(arg, False) |
| |
| |
| def register_pretty_printer_commands(): |
| """Call from a top level script to install the pretty-printer commands.""" |
| InfoPrettyPrinter() |
| EnablePrettyPrinter() |
| DisablePrettyPrinter() |
| |
| register_pretty_printer_commands() |