1# Pretty-printer commands.
2# Copyright (C) 2010-2012 Free Software Foundation, Inc.
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
19import copy
20import gdb
21import re
22
23
24def 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
76def 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
84class 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 = copy.copy(pretty_printers)
128        sorted_pretty_printers.sort(lambda x, y:
129                                        cmp(self.printer_name(x),
130                                            self.printer_name(y)))
131        for printer in sorted_pretty_printers:
132            name = self.printer_name(printer)
133            enabled = self.enabled_string(printer)
134            if name_re.match(name):
135                print "  %s%s" % (name, enabled)
136                if (hasattr(printer, "subprinters") and
137                    printer.subprinters is not None):
138                    sorted_subprinters = copy.copy(printer.subprinters)
139                    sorted_subprinters.sort(lambda x, y:
140                                                cmp(self.printer_name(x),
141                                                    self.printer_name(y)))
142                    for subprinter in sorted_subprinters:
143                        if (not subname_re or
144                            subname_re.match(subprinter.name)):
145                            print ("    %s%s" %
146                                   (subprinter.name,
147                                    self.enabled_string(subprinter)))
148
149    def invoke1(self, title, printer_list,
150                obj_name_to_match, object_re, name_re, subname_re):
151        """Subroutine of invoke to simplify it."""
152        if printer_list and object_re.match(obj_name_to_match):
153            print title
154            self.list_pretty_printers(printer_list, name_re, subname_re)
155
156    def invoke(self, arg, from_tty):
157        """GDB calls this to perform the command."""
158        (object_re, name_re, subname_re) = parse_printer_regexps(arg)
159        self.invoke1("global pretty-printers:", gdb.pretty_printers,
160                     "global", object_re, name_re, subname_re)
161        cp = gdb.current_progspace()
162        self.invoke1("progspace %s pretty-printers:" % cp.filename,
163                     cp.pretty_printers, "progspace",
164                     object_re, name_re, subname_re)
165        for objfile in gdb.objfiles():
166            self.invoke1("  objfile %s pretty-printers:" % objfile.filename,
167                         objfile.pretty_printers, objfile.filename,
168                         object_re, name_re, subname_re)
169
170
171def count_enabled_printers(pretty_printers):
172    """Return a 2-tuple of number of enabled and total printers."""
173    enabled = 0
174    total = 0
175    for printer in pretty_printers:
176        if (hasattr(printer, "subprinters")
177            and printer.subprinters is not None):
178            if printer_enabled_p(printer):
179                for subprinter in printer.subprinters:
180                    if printer_enabled_p(subprinter):
181                        enabled += 1
182            total += len(printer.subprinters)
183        else:
184            if printer_enabled_p(printer):
185                enabled += 1
186            total += 1
187    return (enabled, total)
188
189
190def count_all_enabled_printers():
191    """Return a 2-tuble of the enabled state and total number of all printers.
192    This includes subprinters.
193    """
194    enabled_count = 0
195    total_count = 0
196    (t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers)
197    enabled_count += t_enabled
198    total_count += t_total
199    (t_enabled, t_total) = count_enabled_printers(gdb.current_progspace().pretty_printers)
200    enabled_count += t_enabled
201    total_count += t_total
202    for objfile in gdb.objfiles():
203        (t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers)
204        enabled_count += t_enabled
205        total_count += t_total
206    return (enabled_count, total_count)
207
208
209def pluralize(text, n, suffix="s"):
210    """Return TEXT pluralized if N != 1."""
211    if n != 1:
212        return "%s%s" % (text, suffix)
213    else:
214        return text
215
216
217def show_pretty_printer_enabled_summary():
218    """Print the number of printers enabled/disabled.
219    We count subprinters individually.
220    """
221    (enabled_count, total_count) = count_all_enabled_printers()
222    print "%d of %d printers enabled" % (enabled_count, total_count)
223
224
225def do_enable_pretty_printer_1 (pretty_printers, name_re, subname_re, flag):
226    """Worker for enabling/disabling pretty-printers.
227
228    Arguments:
229        pretty_printers: list of pretty-printers
230        name_re: regular-expression object to select printers
231        subname_re: regular expression object to select subprinters or None
232                    if all are affected
233        flag: True for Enable, False for Disable
234
235    Returns:
236        The number of printers affected.
237        This is just for informational purposes for the user.
238    """
239    total = 0
240    for printer in pretty_printers:
241        if (hasattr(printer, "name") and name_re.match(printer.name) or
242            hasattr(printer, "__name__") and name_re.match(printer.__name__)):
243            if (hasattr(printer, "subprinters") and
244                printer.subprinters is not None):
245                if not subname_re:
246                    # Only record printers that change state.
247                    if printer_enabled_p(printer) != flag:
248                        for subprinter in printer.subprinters:
249                            if printer_enabled_p(subprinter):
250                                total += 1
251                    # NOTE: We preserve individual subprinter settings.
252                    printer.enabled = flag
253                else:
254                    # NOTE: Whether this actually disables the subprinter
255                    # depends on whether the printer's lookup function supports
256                    # the "enable" API.  We can only assume it does.
257                    for subprinter in printer.subprinters:
258                        if subname_re.match(subprinter.name):
259                            # Only record printers that change state.
260                           if (printer_enabled_p(printer) and
261                               printer_enabled_p(subprinter) != flag):
262                               total += 1
263                           subprinter.enabled = flag
264            else:
265                # This printer has no subprinters.
266                # If the user does "disable pretty-printer .* .* foo"
267                # should we disable printers that don't have subprinters?
268                # How do we apply "foo" in this context?  Since there is no
269                # "foo" subprinter it feels like we should skip this printer.
270                # There's still the issue of how to handle
271                # "disable pretty-printer .* .* .*", and every other variation
272                # that can match everything.  For now punt and only support
273                # "disable pretty-printer .* .*" (i.e. subname is elided)
274                # to disable everything.
275                if not subname_re:
276                    # Only record printers that change state.
277                    if printer_enabled_p(printer) != flag:
278                        total += 1
279                    printer.enabled = flag
280    return total
281
282
283def do_enable_pretty_printer (arg, flag):
284    """Internal worker for enabling/disabling pretty-printers."""
285    (object_re, name_re, subname_re) = parse_printer_regexps(arg)
286
287    total = 0
288    if object_re.match("global"):
289        total += do_enable_pretty_printer_1(gdb.pretty_printers,
290                                            name_re, subname_re, flag)
291    cp = gdb.current_progspace()
292    if object_re.match("progspace"):
293        total += do_enable_pretty_printer_1(cp.pretty_printers,
294                                            name_re, subname_re, flag)
295    for objfile in gdb.objfiles():
296        if object_re.match(objfile.filename):
297            total += do_enable_pretty_printer_1(objfile.pretty_printers,
298                                                name_re, subname_re, flag)
299
300    if flag:
301        state = "enabled"
302    else:
303        state = "disabled"
304    print "%d %s %s" % (total, pluralize("printer", total), state)
305
306    # Print the total list of printers currently enabled/disabled.
307    # This is to further assist the user in determining whether the result
308    # is expected.  Since we use regexps to select it's useful.
309    show_pretty_printer_enabled_summary()
310
311
312# Enable/Disable one or more pretty-printers.
313#
314# This is intended for use when a broken pretty-printer is shipped/installed
315# and the user wants to disable that printer without disabling all the other
316# printers.
317#
318# A useful addition would be -v (verbose) to show each printer affected.
319
320class EnablePrettyPrinter (gdb.Command):
321    """GDB command to enable the specified pretty-printer.
322
323    Usage: enable pretty-printer [object-regexp [name-regexp]]
324
325    OBJECT-REGEXP is a regular expression matching the objects to examine.
326    Objects are "global", the program space's file, and the objfiles within
327    that program space.
328
329    NAME-REGEXP matches the name of the pretty-printer.
330    Individual printers in a collection are named as
331    printer-name;subprinter-name.
332    """
333
334    def __init__(self):
335        super(EnablePrettyPrinter, self).__init__("enable pretty-printer",
336                                                   gdb.COMMAND_DATA)
337
338    def invoke(self, arg, from_tty):
339        """GDB calls this to perform the command."""
340        do_enable_pretty_printer(arg, True)
341
342
343class DisablePrettyPrinter (gdb.Command):
344    """GDB command to disable the specified pretty-printer.
345
346    Usage: disable pretty-printer [object-regexp [name-regexp]]
347
348    OBJECT-REGEXP is a regular expression matching the objects to examine.
349    Objects are "global", the program space's file, and the objfiles within
350    that program space.
351
352    NAME-REGEXP matches the name of the pretty-printer.
353    Individual printers in a collection are named as
354    printer-name;subprinter-name.
355    """
356
357    def __init__(self):
358        super(DisablePrettyPrinter, self).__init__("disable pretty-printer",
359                                                   gdb.COMMAND_DATA)
360
361    def invoke(self, arg, from_tty):
362        """GDB calls this to perform the command."""
363        do_enable_pretty_printer(arg, False)
364
365
366def register_pretty_printer_commands():
367    """Call from a top level script to install the pretty-printer commands."""
368    InfoPrettyPrinter()
369    EnablePrettyPrinter()
370    DisablePrettyPrinter()
371
372register_pretty_printer_commands()
373