]>
Commit | Line | Data |
---|---|---|
7b51bc51 | 1 | # Pretty-printer commands. |
d01e8234 | 2 | # Copyright (C) 2010-2025 Free Software Foundation, Inc. |
7b51bc51 DE |
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 | ||
19 | import copy | |
7b51bc51 DE |
20 | import re |
21 | ||
c2cf30e7 TT |
22 | import gdb |
23 | ||
7b51bc51 DE |
24 | |
25 | def parse_printer_regexps(arg): | |
26 | """Internal utility to parse a pretty-printer command argv. | |
27 | ||
28 | Arguments: | |
29 | arg: The arguments to the command. The format is: | |
30 | [object-regexp [name-regexp]]. | |
31 | Individual printers in a collection are named as | |
4e04c971 | 32 | printer-name;subprinter-name. |
7b51bc51 DE |
33 | |
34 | Returns: | |
35 | The result is a 3-tuple of compiled regular expressions, except that | |
36 | the resulting compiled subprinter regexp is None if not provided. | |
37 | ||
38 | Raises: | |
39 | SyntaxError: an error processing ARG | |
40 | """ | |
41 | ||
13123da8 | 42 | argv = gdb.string_to_argv(arg) |
7b51bc51 DE |
43 | argc = len(argv) |
44 | object_regexp = "" # match everything | |
45 | name_regexp = "" # match everything | |
46 | subname_regexp = None | |
47 | if argc > 3: | |
48 | raise SyntaxError("too many arguments") | |
49 | if argc >= 1: | |
50 | object_regexp = argv[0] | |
51 | if argc >= 2: | |
4e04c971 | 52 | name_subname = argv[1].split(";", 1) |
7b51bc51 DE |
53 | name_regexp = name_subname[0] |
54 | if len(name_subname) == 2: | |
55 | subname_regexp = name_subname[1] | |
56 | # That re.compile raises SyntaxError was determined empirically. | |
57 | # We catch it and reraise it to provide a slightly more useful | |
58 | # error message for the user. | |
59 | try: | |
60 | object_re = re.compile(object_regexp) | |
61 | except SyntaxError: | |
62 | raise SyntaxError("invalid object regexp: %s" % object_regexp) | |
63 | try: | |
13123da8 | 64 | name_re = re.compile(name_regexp) |
7b51bc51 DE |
65 | except SyntaxError: |
66 | raise SyntaxError("invalid name regexp: %s" % name_regexp) | |
67 | if subname_regexp is not None: | |
68 | try: | |
69 | subname_re = re.compile(subname_regexp) | |
70 | except SyntaxError: | |
71 | raise SyntaxError("invalid subname regexp: %s" % subname_regexp) | |
72 | else: | |
73 | subname_re = None | |
13123da8 | 74 | return (object_re, name_re, subname_re) |
7b51bc51 DE |
75 | |
76 | ||
77 | def printer_enabled_p(printer): | |
78 | """Internal utility to see if printer (or subprinter) is enabled.""" | |
79 | if hasattr(printer, "enabled"): | |
80 | return printer.enabled | |
81 | else: | |
82 | return True | |
83 | ||
84 | ||
85 | class InfoPrettyPrinter(gdb.Command): | |
86 | """GDB command to list all registered pretty-printers. | |
87 | ||
13123da8 | 88 | Usage: info pretty-printer [OBJECT-REGEXP [NAME-REGEXP]] |
7b51bc51 | 89 | |
13123da8 SM |
90 | OBJECT-REGEXP is a regular expression matching the objects to list. |
91 | Objects are "global", the program space's file, and the objfiles within | |
92 | that program space. | |
7b51bc51 | 93 | |
13123da8 SM |
94 | NAME-REGEXP matches the name of the pretty-printer. |
95 | Individual printers in a collection are named as | |
96 | printer-name;subprinter-name.""" | |
7b51bc51 | 97 | |
13123da8 SM |
98 | def __init__(self): |
99 | super(InfoPrettyPrinter, self).__init__("info pretty-printer", gdb.COMMAND_DATA) | |
7b51bc51 DE |
100 | |
101 | @staticmethod | |
102 | def enabled_string(printer): | |
103 | """Return "" if PRINTER is enabled, otherwise " [disabled]".""" | |
104 | if printer_enabled_p(printer): | |
105 | return "" | |
106 | else: | |
107 | return " [disabled]" | |
108 | ||
109 | @staticmethod | |
110 | def printer_name(printer): | |
111 | """Return the printer's name.""" | |
112 | if hasattr(printer, "name"): | |
113 | return printer.name | |
114 | if hasattr(printer, "__name__"): | |
115 | return printer.__name__ | |
116 | # This "shouldn't happen", but the public API allows for | |
117 | # direct additions to the pretty-printer list, and we shouldn't | |
118 | # crash because someone added a bogus printer. | |
119 | # Plus we want to give the user a way to list unknown printers. | |
120 | return "unknown" | |
121 | ||
122 | def list_pretty_printers(self, pretty_printers, name_re, subname_re): | |
123 | """Print a list of pretty-printers.""" | |
124 | # A potential enhancement is to provide an option to list printers in | |
125 | # "lookup order" (i.e. unsorted). | |
13123da8 SM |
126 | sorted_pretty_printers = sorted( |
127 | copy.copy(pretty_printers), key=self.printer_name | |
128 | ) | |
7b51bc51 DE |
129 | for printer in sorted_pretty_printers: |
130 | name = self.printer_name(printer) | |
131 | enabled = self.enabled_string(printer) | |
132 | if name_re.match(name): | |
13123da8 SM |
133 | print(" %s%s" % (name, enabled)) |
134 | if hasattr(printer, "subprinters") and printer.subprinters is not None: | |
135 | sorted_subprinters = sorted( | |
136 | copy.copy(printer.subprinters), key=self.printer_name | |
137 | ) | |
7b51bc51 | 138 | for subprinter in sorted_subprinters: |
13123da8 SM |
139 | if not subname_re or subname_re.match(subprinter.name): |
140 | print( | |
141 | " %s%s" | |
142 | % (subprinter.name, self.enabled_string(subprinter)) | |
143 | ) | |
144 | ||
145 | def invoke1( | |
146 | self, title, printer_list, obj_name_to_match, object_re, name_re, subname_re | |
147 | ): | |
75c8c9d7 | 148 | """Subroutine of invoke to simplify it.""" |
7b51bc51 | 149 | if printer_list and object_re.match(obj_name_to_match): |
13123da8 | 150 | print(title) |
7b51bc51 DE |
151 | self.list_pretty_printers(printer_list, name_re, subname_re) |
152 | ||
153 | def invoke(self, arg, from_tty): | |
154 | """GDB calls this to perform the command.""" | |
155 | (object_re, name_re, subname_re) = parse_printer_regexps(arg) | |
13123da8 SM |
156 | self.invoke1( |
157 | "global pretty-printers:", | |
158 | gdb.pretty_printers, | |
159 | "global", | |
160 | object_re, | |
161 | name_re, | |
162 | subname_re, | |
163 | ) | |
7b51bc51 | 164 | cp = gdb.current_progspace() |
13123da8 SM |
165 | self.invoke1( |
166 | "progspace %s pretty-printers:" % cp.filename, | |
167 | cp.pretty_printers, | |
168 | "progspace", | |
169 | object_re, | |
170 | name_re, | |
171 | subname_re, | |
172 | ) | |
7b51bc51 | 173 | for objfile in gdb.objfiles(): |
13123da8 SM |
174 | self.invoke1( |
175 | "objfile %s pretty-printers:" % objfile.filename, | |
176 | objfile.pretty_printers, | |
177 | objfile.filename, | |
178 | object_re, | |
179 | name_re, | |
180 | subname_re, | |
181 | ) | |
7b51bc51 DE |
182 | |
183 | ||
184 | def count_enabled_printers(pretty_printers): | |
185 | """Return a 2-tuple of number of enabled and total printers.""" | |
186 | enabled = 0 | |
187 | total = 0 | |
188 | for printer in pretty_printers: | |
13123da8 | 189 | if hasattr(printer, "subprinters") and printer.subprinters is not None: |
7b51bc51 DE |
190 | if printer_enabled_p(printer): |
191 | for subprinter in printer.subprinters: | |
192 | if printer_enabled_p(subprinter): | |
193 | enabled += 1 | |
194 | total += len(printer.subprinters) | |
195 | else: | |
196 | if printer_enabled_p(printer): | |
197 | enabled += 1 | |
198 | total += 1 | |
199 | return (enabled, total) | |
200 | ||
201 | ||
202 | def count_all_enabled_printers(): | |
203 | """Return a 2-tuble of the enabled state and total number of all printers. | |
204 | This includes subprinters. | |
205 | """ | |
206 | enabled_count = 0 | |
207 | total_count = 0 | |
208 | (t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers) | |
209 | enabled_count += t_enabled | |
210 | total_count += t_total | |
13123da8 SM |
211 | (t_enabled, t_total) = count_enabled_printers( |
212 | gdb.current_progspace().pretty_printers | |
213 | ) | |
7b51bc51 DE |
214 | enabled_count += t_enabled |
215 | total_count += t_total | |
216 | for objfile in gdb.objfiles(): | |
217 | (t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers) | |
218 | enabled_count += t_enabled | |
219 | total_count += t_total | |
220 | return (enabled_count, total_count) | |
221 | ||
222 | ||
223 | def pluralize(text, n, suffix="s"): | |
224 | """Return TEXT pluralized if N != 1.""" | |
225 | if n != 1: | |
226 | return "%s%s" % (text, suffix) | |
227 | else: | |
228 | return text | |
229 | ||
230 | ||
231 | def show_pretty_printer_enabled_summary(): | |
232 | """Print the number of printers enabled/disabled. | |
233 | We count subprinters individually. | |
234 | """ | |
235 | (enabled_count, total_count) = count_all_enabled_printers() | |
13123da8 | 236 | print("%d of %d printers enabled" % (enabled_count, total_count)) |
7b51bc51 DE |
237 | |
238 | ||
13123da8 | 239 | def do_enable_pretty_printer_1(pretty_printers, name_re, subname_re, flag): |
7b51bc51 DE |
240 | """Worker for enabling/disabling pretty-printers. |
241 | ||
242 | Arguments: | |
243 | pretty_printers: list of pretty-printers | |
244 | name_re: regular-expression object to select printers | |
245 | subname_re: regular expression object to select subprinters or None | |
246 | if all are affected | |
247 | flag: True for Enable, False for Disable | |
248 | ||
249 | Returns: | |
250 | The number of printers affected. | |
251 | This is just for informational purposes for the user. | |
252 | """ | |
253 | total = 0 | |
254 | for printer in pretty_printers: | |
13123da8 SM |
255 | if ( |
256 | hasattr(printer, "name") | |
257 | and name_re.match(printer.name) | |
258 | or hasattr(printer, "__name__") | |
259 | and name_re.match(printer.__name__) | |
260 | ): | |
261 | if hasattr(printer, "subprinters") and printer.subprinters is not None: | |
7b51bc51 DE |
262 | if not subname_re: |
263 | # Only record printers that change state. | |
264 | if printer_enabled_p(printer) != flag: | |
265 | for subprinter in printer.subprinters: | |
266 | if printer_enabled_p(subprinter): | |
267 | total += 1 | |
268 | # NOTE: We preserve individual subprinter settings. | |
269 | printer.enabled = flag | |
270 | else: | |
271 | # NOTE: Whether this actually disables the subprinter | |
272 | # depends on whether the printer's lookup function supports | |
273 | # the "enable" API. We can only assume it does. | |
274 | for subprinter in printer.subprinters: | |
275 | if subname_re.match(subprinter.name): | |
276 | # Only record printers that change state. | |
13123da8 SM |
277 | if ( |
278 | printer_enabled_p(printer) | |
279 | and printer_enabled_p(subprinter) != flag | |
280 | ): | |
281 | total += 1 | |
282 | subprinter.enabled = flag | |
7b51bc51 DE |
283 | else: |
284 | # This printer has no subprinters. | |
285 | # If the user does "disable pretty-printer .* .* foo" | |
286 | # should we disable printers that don't have subprinters? | |
287 | # How do we apply "foo" in this context? Since there is no | |
288 | # "foo" subprinter it feels like we should skip this printer. | |
289 | # There's still the issue of how to handle | |
290 | # "disable pretty-printer .* .* .*", and every other variation | |
291 | # that can match everything. For now punt and only support | |
292 | # "disable pretty-printer .* .*" (i.e. subname is elided) | |
293 | # to disable everything. | |
294 | if not subname_re: | |
295 | # Only record printers that change state. | |
296 | if printer_enabled_p(printer) != flag: | |
297 | total += 1 | |
298 | printer.enabled = flag | |
299 | return total | |
300 | ||
301 | ||
13123da8 | 302 | def do_enable_pretty_printer(arg, flag): |
7b51bc51 DE |
303 | """Internal worker for enabling/disabling pretty-printers.""" |
304 | (object_re, name_re, subname_re) = parse_printer_regexps(arg) | |
305 | ||
306 | total = 0 | |
307 | if object_re.match("global"): | |
13123da8 SM |
308 | total += do_enable_pretty_printer_1( |
309 | gdb.pretty_printers, name_re, subname_re, flag | |
310 | ) | |
7b51bc51 DE |
311 | cp = gdb.current_progspace() |
312 | if object_re.match("progspace"): | |
13123da8 SM |
313 | total += do_enable_pretty_printer_1( |
314 | cp.pretty_printers, name_re, subname_re, flag | |
315 | ) | |
7b51bc51 DE |
316 | for objfile in gdb.objfiles(): |
317 | if object_re.match(objfile.filename): | |
13123da8 SM |
318 | total += do_enable_pretty_printer_1( |
319 | objfile.pretty_printers, name_re, subname_re, flag | |
320 | ) | |
7b51bc51 DE |
321 | |
322 | if flag: | |
323 | state = "enabled" | |
324 | else: | |
325 | state = "disabled" | |
13123da8 | 326 | print("%d %s %s" % (total, pluralize("printer", total), state)) |
7b51bc51 DE |
327 | |
328 | # Print the total list of printers currently enabled/disabled. | |
329 | # This is to further assist the user in determining whether the result | |
330 | # is expected. Since we use regexps to select it's useful. | |
331 | show_pretty_printer_enabled_summary() | |
332 | ||
333 | ||
334 | # Enable/Disable one or more pretty-printers. | |
335 | # | |
336 | # This is intended for use when a broken pretty-printer is shipped/installed | |
337 | # and the user wants to disable that printer without disabling all the other | |
338 | # printers. | |
339 | # | |
340 | # A useful addition would be -v (verbose) to show each printer affected. | |
341 | ||
13123da8 SM |
342 | |
343 | class EnablePrettyPrinter(gdb.Command): | |
7b51bc51 DE |
344 | """GDB command to enable the specified pretty-printer. |
345 | ||
13123da8 | 346 | Usage: enable pretty-printer [OBJECT-REGEXP [NAME-REGEXP]] |
7b51bc51 | 347 | |
13123da8 SM |
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. | |
7b51bc51 | 351 | |
13123da8 SM |
352 | NAME-REGEXP matches the name of the pretty-printer. |
353 | Individual printers in a collection are named as | |
354 | printer-name;subprinter-name.""" | |
7b51bc51 DE |
355 | |
356 | def __init__(self): | |
13123da8 SM |
357 | super(EnablePrettyPrinter, self).__init__( |
358 | "enable pretty-printer", gdb.COMMAND_DATA | |
359 | ) | |
7b51bc51 DE |
360 | |
361 | def invoke(self, arg, from_tty): | |
362 | """GDB calls this to perform the command.""" | |
363 | do_enable_pretty_printer(arg, True) | |
364 | ||
365 | ||
13123da8 | 366 | class DisablePrettyPrinter(gdb.Command): |
7b51bc51 DE |
367 | """GDB command to disable the specified pretty-printer. |
368 | ||
13123da8 | 369 | Usage: disable pretty-printer [OBJECT-REGEXP [NAME-REGEXP]] |
7b51bc51 | 370 | |
13123da8 SM |
371 | OBJECT-REGEXP is a regular expression matching the objects to examine. |
372 | Objects are "global", the program space's file, and the objfiles within | |
373 | that program space. | |
7b51bc51 | 374 | |
13123da8 SM |
375 | NAME-REGEXP matches the name of the pretty-printer. |
376 | Individual printers in a collection are named as | |
377 | printer-name;subprinter-name.""" | |
7b51bc51 DE |
378 | |
379 | def __init__(self): | |
13123da8 SM |
380 | super(DisablePrettyPrinter, self).__init__( |
381 | "disable pretty-printer", gdb.COMMAND_DATA | |
382 | ) | |
7b51bc51 DE |
383 | |
384 | def invoke(self, arg, from_tty): | |
385 | """GDB calls this to perform the command.""" | |
386 | do_enable_pretty_printer(arg, False) | |
387 | ||
388 | ||
389 | def register_pretty_printer_commands(): | |
390 | """Call from a top level script to install the pretty-printer commands.""" | |
391 | InfoPrettyPrinter() | |
392 | EnablePrettyPrinter() | |
393 | DisablePrettyPrinter() | |
5e239b84 | 394 | |
13123da8 | 395 | |
5e239b84 | 396 | register_pretty_printer_commands() |