1# Unwinder commands.
2# Copyright 2015-2020 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
17import gdb
18import re
19
20
21def validate_regexp(exp, idstring):
22    try:
23        return re.compile(exp)
24    except SyntaxError:
25        raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp))
26
27
28def parse_unwinder_command_args(arg):
29    """Internal utility to parse unwinder command argv.
30
31    Arguments:
32        arg: The arguments to the command. The format is:
33             [locus-regexp [name-regexp]]
34
35    Returns:
36        A 2-tuple of compiled regular expressions.
37
38    Raises:
39        SyntaxError: an error processing ARG
40    """
41
42    argv = gdb.string_to_argv(arg)
43    argc = len(argv)
44    if argc > 2:
45        raise SyntaxError("Too many arguments.")
46    locus_regexp = ""
47    name_regexp = ""
48    if argc >= 1:
49        locus_regexp = argv[0]
50        if argc >= 2:
51            name_regexp = argv[1]
52    return (validate_regexp(locus_regexp, "locus"),
53            validate_regexp(name_regexp, "unwinder"))
54
55
56class InfoUnwinder(gdb.Command):
57    """GDB command to list unwinders.
58
59Usage: info unwinder [LOCUS-REGEXP [NAME-REGEXP]]
60
61LOCUS-REGEXP is a regular expression matching the location of the
62unwinder.  If it is omitted, all registered unwinders from all
63loci are listed.  A locus can be 'global', 'progspace' to list
64the unwinders from the current progspace, or a regular expression
65matching filenames of objfiles.
66
67NAME-REGEXP is a regular expression to filter unwinder names.  If
68this omitted for a specified locus, then all registered unwinders
69in the locus are listed."""
70
71    def __init__(self):
72        super(InfoUnwinder, self).__init__("info unwinder",
73                                            gdb.COMMAND_STACK)
74
75    def list_unwinders(self, title, unwinders, name_re):
76        """Lists the unwinders whose name matches regexp.
77
78        Arguments:
79            title: The line to print before the list.
80            unwinders: The list of the unwinders.
81            name_re: unwinder name filter.
82        """
83        if not unwinders:
84            return
85        print(title)
86        for unwinder in unwinders:
87            if name_re.match(unwinder.name):
88                print("  %s%s" % (unwinder.name,
89                                  "" if unwinder.enabled else " [disabled]"))
90
91    def invoke(self, arg, from_tty):
92        locus_re, name_re = parse_unwinder_command_args(arg)
93        if locus_re.match("global"):
94            self.list_unwinders("Global:", gdb.frame_unwinders,
95                                name_re)
96        if locus_re.match("progspace"):
97            cp = gdb.current_progspace()
98            self.list_unwinders("Progspace %s:" % cp.filename,
99                                cp.frame_unwinders, name_re)
100        for objfile in gdb.objfiles():
101            if locus_re.match(objfile.filename):
102                self.list_unwinders("Objfile %s:" % objfile.filename,
103                                    objfile.frame_unwinders, name_re)
104
105
106def do_enable_unwinder1(unwinders, name_re, flag):
107    """Enable/disable unwinders whose names match given regex.
108
109    Arguments:
110        unwinders: The list of unwinders.
111        name_re: Unwinder name filter.
112        flag: Enable/disable.
113
114    Returns:
115        The number of unwinders affected.
116    """
117    total = 0
118    for unwinder in unwinders:
119        if name_re.match(unwinder.name):
120            unwinder.enabled = flag
121            total += 1
122    return total
123
124
125def do_enable_unwinder(arg, flag):
126    """Enable/disable unwinder(s)."""
127    (locus_re, name_re) = parse_unwinder_command_args(arg)
128    total = 0
129    if locus_re.match("global"):
130        total += do_enable_unwinder1(gdb.frame_unwinders, name_re, flag)
131    if locus_re.match("progspace"):
132        total += do_enable_unwinder1(gdb.current_progspace().frame_unwinders,
133                                     name_re, flag)
134    for objfile in gdb.objfiles():
135        if locus_re.match(objfile.filename):
136            total += do_enable_unwinder1(objfile.frame_unwinders, name_re,
137                                         flag)
138    if total > 0:
139        gdb.invalidate_cached_frames()
140    print("%d unwinder%s %s" % (total, "" if total == 1 else "s",
141                                "enabled" if flag else "disabled"))
142
143
144class EnableUnwinder(gdb.Command):
145    """GDB command to enable unwinders.
146
147Usage: enable unwinder [LOCUS-REGEXP [NAME-REGEXP]]
148
149LOCUS-REGEXP is a regular expression specifying the unwinders to
150enable.  It can 'global', 'progspace', or the name of an objfile
151within that progspace.
152
153NAME_REGEXP is a regular expression to filter unwinder names.  If
154this omitted for a specified locus, then all registered unwinders
155in the locus are affected."""
156
157    def __init__(self):
158        super(EnableUnwinder, self).__init__("enable unwinder",
159                                             gdb.COMMAND_STACK)
160
161    def invoke(self, arg, from_tty):
162        """GDB calls this to perform the command."""
163        do_enable_unwinder(arg, True)
164
165
166class DisableUnwinder(gdb.Command):
167    """GDB command to disable the specified unwinder.
168
169Usage: disable unwinder [LOCUS-REGEXP [NAME-REGEXP]]
170
171LOCUS-REGEXP is a regular expression specifying the unwinders to
172disable.  It can 'global', 'progspace', or the name of an objfile
173within that progspace.
174
175NAME_REGEXP is a regular expression to filter unwinder names.  If
176this omitted for a specified locus, then all registered unwinders
177in the locus are affected."""
178
179    def __init__(self):
180        super(DisableUnwinder, self).__init__("disable unwinder",
181                                              gdb.COMMAND_STACK)
182
183    def invoke(self, arg, from_tty):
184        """GDB calls this to perform the command."""
185        do_enable_unwinder(arg, False)
186
187
188def register_unwinder_commands():
189    """Installs the unwinder commands."""
190    InfoUnwinder()
191    EnableUnwinder()
192    DisableUnwinder()
193
194
195register_unwinder_commands()
196