369 lines
14 KiB
Python
369 lines
14 KiB
Python
# 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()
|