gdb: implement missing debug handler hook for Python
[binutils-gdb.git] / gdb / python / lib / gdb / command / missing_debug.py
1 # Missing debug related commands.
2 #
3 # Copyright 2023 Free Software Foundation, Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 import gdb
19 import re
20
21
22 def validate_regexp(exp, idstring):
23 """Compile exp into a compiler regular expression object.
24
25 Arguments:
26 exp: The string to compile into a re.Pattern object.
27 idstring: A string, what exp is a regexp for.
28
29 Returns:
30 A re.Pattern object representing exp.
31
32 Raises:
33 SyntaxError: If exp is an invalid regexp.
34 """
35 try:
36 return re.compile(exp)
37 except SyntaxError:
38 raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp))
39
40
41 def parse_missing_debug_command_args(arg):
42 """Internal utility to parse missing debug handler command argv.
43
44 Arguments:
45 arg: The arguments to the command. The format is:
46 [locus-regexp [name-regexp]]
47
48 Returns:
49 A 2-tuple of compiled regular expressions.
50
51 Raises:
52 SyntaxError: an error processing ARG
53 """
54 argv = gdb.string_to_argv(arg)
55 argc = len(argv)
56 if argc > 2:
57 raise SyntaxError("Too many arguments.")
58 locus_regexp = ""
59 name_regexp = ""
60 if argc >= 1:
61 locus_regexp = argv[0]
62 if argc >= 2:
63 name_regexp = argv[1]
64 return (
65 validate_regexp(locus_regexp, "locus"),
66 validate_regexp(name_regexp, "handler"),
67 )
68
69
70 class InfoMissingDebugHanders(gdb.Command):
71 """GDB command to list missing debug handlers.
72
73 Usage: info missing-debug-handlers [LOCUS-REGEXP [NAME-REGEXP]]
74
75 LOCUS-REGEXP is a regular expression matching the location of the
76 handler. If it is omitted, all registered handlers from all
77 loci are listed. A locus can be 'global', 'progspace' to list
78 the handlers from the current progspace, or a regular expression
79 matching filenames of progspaces.
80
81 NAME-REGEXP is a regular expression to filter missing debug
82 handler names. If this omitted for a specified locus, then all
83 registered handlers in the locus are listed.
84 """
85
86 def __init__(self):
87 super().__init__("info missing-debug-handlers", gdb.COMMAND_FILES)
88
89 def list_handlers(self, title, handlers, name_re):
90 """Lists the missing debug handlers whose name matches regexp.
91
92 Arguments:
93 title: The line to print before the list.
94 handlers: The list of the missing debug handlers.
95 name_re: handler name filter.
96 """
97 if not handlers:
98 return
99 print(title)
100 for handler in handlers:
101 if name_re.match(handler.name):
102 print(
103 " %s%s" % (handler.name, "" if handler.enabled else " [disabled]")
104 )
105
106 def invoke(self, arg, from_tty):
107 locus_re, name_re = parse_missing_debug_command_args(arg)
108
109 if locus_re.match("progspace") and locus_re.pattern != "":
110 cp = gdb.current_progspace()
111 self.list_handlers(
112 "Progspace %s:" % cp.filename, cp.missing_debug_handlers, name_re
113 )
114
115 for progspace in gdb.progspaces():
116 filename = progspace.filename or ""
117 if locus_re.match(filename):
118 if filename == "":
119 if progspace == gdb.current_progspace():
120 msg = "Current Progspace:"
121 else:
122 msg = "Progspace <no-file>:"
123 else:
124 msg = "Progspace %s:" % filename
125 self.list_handlers(
126 msg,
127 progspace.missing_debug_handlers,
128 name_re,
129 )
130
131 # Print global handlers last, as these are invoked last.
132 if locus_re.match("global"):
133 self.list_handlers("Global:", gdb.missing_debug_handlers, name_re)
134
135
136 def do_enable_handler1(handlers, name_re, flag):
137 """Enable/disable missing debug handlers whose names match given regex.
138
139 Arguments:
140 handlers: The list of missing debug handlers.
141 name_re: Handler name filter.
142 flag: A boolean indicating if we should enable or disable.
143
144 Returns:
145 The number of handlers affected.
146 """
147 total = 0
148 for handler in handlers:
149 if name_re.match(handler.name) and handler.enabled != flag:
150 handler.enabled = flag
151 total += 1
152 return total
153
154
155 def do_enable_handler(arg, flag):
156 """Enable or disable missing debug handlers."""
157 (locus_re, name_re) = parse_missing_debug_command_args(arg)
158 total = 0
159 if locus_re.match("global"):
160 total += do_enable_handler1(gdb.missing_debug_handlers, name_re, flag)
161 if locus_re.match("progspace") and locus_re.pattern != "":
162 total += do_enable_handler1(
163 gdb.current_progspace().missing_debug_handlers, name_re, flag
164 )
165 for progspace in gdb.progspaces():
166 filename = progspace.filename or ""
167 if locus_re.match(filename):
168 total += do_enable_handler1(progspace.missing_debug_handlers, name_re, flag)
169 print(
170 "%d missing debug handler%s %s"
171 % (total, "" if total == 1 else "s", "enabled" if flag else "disabled")
172 )
173
174
175 class EnableMissingDebugHandler(gdb.Command):
176 """GDB command to enable missing debug handlers.
177
178 Usage: enable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
179
180 LOCUS-REGEXP is a regular expression specifying the handlers to
181 enable. It can be 'global', 'progspace' for the current
182 progspace, or the filename for a file associated with a progspace.
183
184 NAME_REGEXP is a regular expression to filter handler names. If
185 this omitted for a specified locus, then all registered handlers
186 in the locus are affected.
187 """
188
189 def __init__(self):
190 super().__init__("enable missing-debug-handler", gdb.COMMAND_FILES)
191
192 def invoke(self, arg, from_tty):
193 """GDB calls this to perform the command."""
194 do_enable_handler(arg, True)
195
196
197 class DisableMissingDebugHandler(gdb.Command):
198 """GDB command to disable missing debug handlers.
199
200 Usage: disable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
201
202 LOCUS-REGEXP is a regular expression specifying the handlers to
203 enable. It can be 'global', 'progspace' for the current
204 progspace, or the filename for a file associated with a progspace.
205
206 NAME_REGEXP is a regular expression to filter handler names. If
207 this omitted for a specified locus, then all registered handlers
208 in the locus are affected.
209 """
210
211 def __init__(self):
212 super().__init__("disable missing-debug-handler", gdb.COMMAND_FILES)
213
214 def invoke(self, arg, from_tty):
215 """GDB calls this to perform the command."""
216 do_enable_handler(arg, False)
217
218
219 def register_missing_debug_handler_commands():
220 """Installs the missing debug handler commands."""
221 InfoMissingDebugHanders()
222 EnableMissingDebugHandler()
223 DisableMissingDebugHandler()
224
225
226 register_missing_debug_handler_commands()