Commit | Line | Data |
---|---|---|
1e611234 | 1 | # Frame-filter commands. |
88b9d363 | 2 | # Copyright (C) 2013-2022 Free Software Foundation, Inc. |
1e611234 PM |
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 frame-filters.""" | |
18 | ||
562fc849 | 19 | import sys |
1e611234 PM |
20 | import gdb |
21 | import copy | |
22 | from gdb.FrameIterator import FrameIterator | |
23 | from gdb.FrameDecorator import FrameDecorator | |
24 | import gdb.frames | |
25 | import itertools | |
26 | ||
27 | # GDB Commands. | |
28 | class SetFilterPrefixCmd(gdb.Command): | |
29 | """Prefix command for 'set' frame-filter related operations.""" | |
30 | ||
31 | def __init__(self): | |
13123da8 SM |
32 | super(SetFilterPrefixCmd, self).__init__( |
33 | "set frame-filter", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True | |
34 | ) | |
35 | ||
1e611234 PM |
36 | |
37 | class ShowFilterPrefixCmd(gdb.Command): | |
38 | """Prefix command for 'show' frame-filter related operations.""" | |
13123da8 | 39 | |
1e611234 | 40 | def __init__(self): |
13123da8 SM |
41 | super(ShowFilterPrefixCmd, self).__init__( |
42 | "show frame-filter", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True | |
43 | ) | |
44 | ||
45 | ||
1e611234 PM |
46 | class InfoFrameFilter(gdb.Command): |
47 | """List all registered Python frame-filters. | |
48 | ||
13123da8 | 49 | Usage: info frame-filters""" |
1e611234 PM |
50 | |
51 | def __init__(self): | |
13123da8 SM |
52 | super(InfoFrameFilter, self).__init__("info frame-filter", gdb.COMMAND_DATA) |
53 | ||
1e611234 PM |
54 | @staticmethod |
55 | def enabled_string(state): | |
56 | """Return "Yes" if filter is enabled, otherwise "No".""" | |
57 | if state: | |
58 | return "Yes" | |
59 | else: | |
60 | return "No" | |
61 | ||
17621150 | 62 | def print_list(self, title, frame_filters, blank_line): |
13123da8 SM |
63 | sorted_frame_filters = sorted( |
64 | frame_filters.items(), | |
65 | key=lambda i: gdb.frames.get_priority(i[1]), | |
66 | reverse=True, | |
67 | ) | |
1e611234 PM |
68 | |
69 | if len(sorted_frame_filters) == 0: | |
17621150 TT |
70 | return 0 |
71 | ||
1e611234 | 72 | print(title) |
17621150 TT |
73 | print(" Priority Enabled Name") |
74 | for frame_filter in sorted_frame_filters: | |
75 | name = frame_filter[0] | |
76 | try: | |
13123da8 SM |
77 | priority = "{:<8}".format(str(gdb.frames.get_priority(frame_filter[1]))) |
78 | enabled = "{:<7}".format( | |
79 | self.enabled_string(gdb.frames.get_enabled(frame_filter[1])) | |
80 | ) | |
17621150 TT |
81 | print(" %s %s %s" % (priority, enabled, name)) |
82 | except Exception: | |
83 | e = sys.exc_info()[1] | |
13123da8 | 84 | print(" Error printing filter '" + name + "': " + str(e)) |
1e611234 PM |
85 | if blank_line: |
86 | print("") | |
17621150 | 87 | return 1 |
1e611234 PM |
88 | |
89 | def invoke(self, arg, from_tty): | |
17621150 | 90 | any_printed = self.print_list("global frame-filters:", gdb.frame_filters, True) |
1e611234 PM |
91 | |
92 | cp = gdb.current_progspace() | |
13123da8 SM |
93 | any_printed += self.print_list( |
94 | "progspace %s frame-filters:" % cp.filename, cp.frame_filters, True | |
95 | ) | |
1e611234 PM |
96 | |
97 | for objfile in gdb.objfiles(): | |
13123da8 SM |
98 | any_printed += self.print_list( |
99 | "objfile %s frame-filters:" % objfile.filename, | |
100 | objfile.frame_filters, | |
101 | False, | |
102 | ) | |
17621150 TT |
103 | |
104 | if any_printed == 0: | |
13123da8 SM |
105 | print("No frame filters.") |
106 | ||
1e611234 PM |
107 | |
108 | # Internal enable/disable functions. | |
109 | ||
13123da8 | 110 | |
1e611234 | 111 | def _enable_parse_arg(cmd_name, arg): |
13123da8 | 112 | """Internal worker function to take an argument from |
1e611234 PM |
113 | enable/disable and return a tuple of arguments. |
114 | ||
115 | Arguments: | |
116 | cmd_name: Name of the command invoking this function. | |
117 | args: The argument as a string. | |
118 | ||
119 | Returns: | |
120 | A tuple containing the dictionary, and the argument, or just | |
121 | the dictionary in the case of "all". | |
122 | """ | |
123 | ||
13123da8 | 124 | argv = gdb.string_to_argv(arg) |
1e611234 | 125 | argc = len(argv) |
2fb009bb TT |
126 | if argc == 0: |
127 | raise gdb.GdbError(cmd_name + " requires an argument") | |
128 | if argv[0] == "all": | |
129 | if argc > 1: | |
13123da8 SM |
130 | raise gdb.GdbError( |
131 | cmd_name + ": with 'all' " "you may not specify a filter." | |
132 | ) | |
2fb009bb | 133 | elif argc != 2: |
13123da8 | 134 | raise gdb.GdbError(cmd_name + " takes exactly two arguments.") |
1e611234 PM |
135 | |
136 | return argv | |
137 | ||
13123da8 | 138 | |
1e611234 PM |
139 | def _do_enable_frame_filter(command_tuple, flag): |
140 | """Worker for enabling/disabling frame_filters. | |
141 | ||
142 | Arguments: | |
143 | command_type: A tuple with the first element being the | |
144 | frame filter dictionary, and the second being | |
145 | the frame filter name. | |
146 | flag: True for Enable, False for Disable. | |
147 | """ | |
148 | ||
149 | list_op = command_tuple[0] | |
150 | op_list = gdb.frames.return_list(list_op) | |
151 | ||
152 | if list_op == "all": | |
153 | for item in op_list: | |
154 | gdb.frames.set_enabled(item, flag) | |
155 | else: | |
156 | frame_filter = command_tuple[1] | |
157 | try: | |
158 | ff = op_list[frame_filter] | |
159 | except KeyError: | |
803b47e5 | 160 | msg = "frame-filter '" + str(frame_filter) + "' not found." |
1e611234 PM |
161 | raise gdb.GdbError(msg) |
162 | ||
163 | gdb.frames.set_enabled(ff, flag) | |
164 | ||
13123da8 | 165 | |
1e611234 PM |
166 | def _complete_frame_filter_list(text, word, all_flag): |
167 | """Worker for frame filter dictionary name completion. | |
168 | ||
169 | Arguments: | |
170 | text: The full text of the command line. | |
171 | word: The most recent word of the command line. | |
172 | all_flag: Whether to include the word "all" in completion. | |
173 | ||
174 | Returns: | |
175 | A list of suggested frame filter dictionary name completions | |
176 | from text/word analysis. This list can be empty when there | |
177 | are no suggestions for completion. | |
13123da8 | 178 | """ |
1e611234 PM |
179 | if all_flag == True: |
180 | filter_locations = ["all", "global", "progspace"] | |
181 | else: | |
182 | filter_locations = ["global", "progspace"] | |
183 | for objfile in gdb.objfiles(): | |
184 | filter_locations.append(objfile.filename) | |
185 | ||
186 | # If the user just asked for completions with no completion | |
187 | # hints, just return all the frame filter dictionaries we know | |
188 | # about. | |
13123da8 | 189 | if text == "": |
1e611234 PM |
190 | return filter_locations |
191 | ||
192 | # Otherwise filter on what we know. | |
13123da8 | 193 | flist = filter(lambda x, y=text: x.startswith(y), filter_locations) |
1e611234 PM |
194 | |
195 | # If we only have one completion, complete it and return it. | |
196 | if len(flist) == 1: | |
13123da8 | 197 | flist[0] = flist[0][len(text) - len(word) :] |
1e611234 PM |
198 | |
199 | # Otherwise, return an empty list, or a list of frame filter | |
200 | # dictionaries that the previous filter operation returned. | |
201 | return flist | |
202 | ||
13123da8 | 203 | |
1e611234 PM |
204 | def _complete_frame_filter_name(word, printer_dict): |
205 | """Worker for frame filter name completion. | |
206 | ||
207 | Arguments: | |
208 | ||
209 | word: The most recent word of the command line. | |
210 | ||
211 | printer_dict: The frame filter dictionary to search for frame | |
212 | filter name completions. | |
213 | ||
214 | Returns: A list of suggested frame filter name completions | |
215 | from word analysis of the frame filter dictionary. This list | |
216 | can be empty when there are no suggestions for completion. | |
217 | """ | |
218 | ||
219 | printer_keys = printer_dict.keys() | |
13123da8 | 220 | if word == "": |
1e611234 PM |
221 | return printer_keys |
222 | ||
13123da8 | 223 | flist = filter(lambda x, y=word: x.startswith(y), printer_keys) |
1e611234 PM |
224 | return flist |
225 | ||
13123da8 | 226 | |
1e611234 | 227 | class EnableFrameFilter(gdb.Command): |
62b1765c | 228 | """GDB command to enable the specified frame-filter. |
1e611234 | 229 | |
13123da8 | 230 | Usage: enable frame-filter DICTIONARY [NAME] |
1e611234 | 231 | |
13123da8 SM |
232 | DICTIONARY is the name of the frame filter dictionary on which to |
233 | operate. If dictionary is set to "all", perform operations on all | |
234 | dictionaries. Named dictionaries are: "global" for the global | |
235 | frame filter dictionary, "progspace" for the program space's frame | |
236 | filter dictionary. If either all, or the two named dictionaries | |
237 | are not specified, the dictionary name is assumed to be the name | |
238 | of an "objfile" -- a shared library or an executable. | |
239 | ||
240 | NAME matches the name of the frame-filter to operate on.""" | |
1e611234 | 241 | |
1e611234 | 242 | def __init__(self): |
13123da8 SM |
243 | super(EnableFrameFilter, self).__init__("enable frame-filter", gdb.COMMAND_DATA) |
244 | ||
1e611234 PM |
245 | def complete(self, text, word): |
246 | """Completion function for both frame filter dictionary, and | |
247 | frame filter name.""" | |
248 | if text.count(" ") == 0: | |
249 | return _complete_frame_filter_list(text, word, True) | |
250 | else: | |
251 | printer_list = gdb.frames.return_list(text.split()[0].rstrip()) | |
252 | return _complete_frame_filter_name(word, printer_list) | |
253 | ||
254 | def invoke(self, arg, from_tty): | |
255 | command_tuple = _enable_parse_arg("enable frame-filter", arg) | |
256 | _do_enable_frame_filter(command_tuple, True) | |
257 | ||
258 | ||
259 | class DisableFrameFilter(gdb.Command): | |
260 | """GDB command to disable the specified frame-filter. | |
261 | ||
13123da8 | 262 | Usage: disable frame-filter DICTIONARY [NAME] |
1e611234 | 263 | |
13123da8 SM |
264 | DICTIONARY is the name of the frame filter dictionary on which to |
265 | operate. If dictionary is set to "all", perform operations on all | |
266 | dictionaries. Named dictionaries are: "global" for the global | |
267 | frame filter dictionary, "progspace" for the program space's frame | |
268 | filter dictionary. If either all, or the two named dictionaries | |
269 | are not specified, the dictionary name is assumed to be the name | |
270 | of an "objfile" -- a shared library or an executable. | |
271 | ||
272 | NAME matches the name of the frame-filter to operate on.""" | |
1e611234 | 273 | |
1e611234 | 274 | def __init__(self): |
13123da8 SM |
275 | super(DisableFrameFilter, self).__init__( |
276 | "disable frame-filter", gdb.COMMAND_DATA | |
277 | ) | |
1e611234 PM |
278 | |
279 | def complete(self, text, word): | |
280 | """Completion function for both frame filter dictionary, and | |
281 | frame filter name.""" | |
282 | if text.count(" ") == 0: | |
283 | return _complete_frame_filter_list(text, word, True) | |
284 | else: | |
285 | printer_list = gdb.frames.return_list(text.split()[0].rstrip()) | |
286 | return _complete_frame_filter_name(word, printer_list) | |
287 | ||
288 | def invoke(self, arg, from_tty): | |
289 | command_tuple = _enable_parse_arg("disable frame-filter", arg) | |
290 | _do_enable_frame_filter(command_tuple, False) | |
291 | ||
13123da8 | 292 | |
1e611234 PM |
293 | class SetFrameFilterPriority(gdb.Command): |
294 | """GDB command to set the priority of the specified frame-filter. | |
295 | ||
13123da8 | 296 | Usage: set frame-filter priority DICTIONARY NAME PRIORITY |
1e611234 | 297 | |
13123da8 SM |
298 | DICTIONARY is the name of the frame filter dictionary on which to |
299 | operate. Named dictionaries are: "global" for the global frame | |
300 | filter dictionary, "progspace" for the program space's framefilter | |
301 | dictionary. If either of these two are not specified, the | |
302 | dictionary name is assumed to be the name of an "objfile" -- a | |
303 | shared library or an executable. | |
1e611234 | 304 | |
13123da8 | 305 | NAME matches the name of the frame filter to operate on. |
1e611234 | 306 | |
13123da8 SM |
307 | PRIORITY is the an integer to assign the new priority to the frame |
308 | filter.""" | |
1e611234 PM |
309 | |
310 | def __init__(self): | |
13123da8 SM |
311 | super(SetFrameFilterPriority, self).__init__( |
312 | "set frame-filter " "priority", gdb.COMMAND_DATA | |
313 | ) | |
1e611234 PM |
314 | |
315 | def _parse_pri_arg(self, arg): | |
316 | """Internal worker to parse a priority from a tuple. | |
317 | ||
318 | Arguments: | |
319 | arg: Tuple which contains the arguments from the command. | |
320 | ||
321 | Returns: | |
322 | A tuple containing the dictionary, name and priority from | |
323 | the arguments. | |
324 | ||
325 | Raises: | |
326 | gdb.GdbError: An error parsing the arguments. | |
327 | """ | |
328 | ||
13123da8 | 329 | argv = gdb.string_to_argv(arg) |
1e611234 PM |
330 | argc = len(argv) |
331 | if argc != 3: | |
13123da8 | 332 | print("set frame-filter priority " "takes exactly three arguments.") |
1e611234 PM |
333 | return None |
334 | ||
335 | return argv | |
336 | ||
337 | def _set_filter_priority(self, command_tuple): | |
338 | """Internal worker for setting priority of frame-filters, by | |
339 | parsing a tuple and calling _set_priority with the parsed | |
340 | tuple. | |
341 | ||
342 | Arguments: | |
343 | command_tuple: Tuple which contains the arguments from the | |
344 | command. | |
345 | """ | |
346 | ||
347 | list_op = command_tuple[0] | |
348 | frame_filter = command_tuple[1] | |
8f28f522 PM |
349 | |
350 | # GDB returns arguments as a string, so convert priority to | |
351 | # a number. | |
352 | priority = int(command_tuple[2]) | |
1e611234 PM |
353 | |
354 | op_list = gdb.frames.return_list(list_op) | |
355 | ||
356 | try: | |
357 | ff = op_list[frame_filter] | |
358 | except KeyError: | |
803b47e5 | 359 | msg = "frame-filter '" + str(frame_filter) + "' not found." |
1e611234 PM |
360 | raise gdb.GdbError(msg) |
361 | ||
362 | gdb.frames.set_priority(ff, priority) | |
363 | ||
364 | def complete(self, text, word): | |
365 | """Completion function for both frame filter dictionary, and | |
366 | frame filter name.""" | |
367 | if text.count(" ") == 0: | |
368 | return _complete_frame_filter_list(text, word, False) | |
369 | else: | |
370 | printer_list = gdb.frames.return_list(text.split()[0].rstrip()) | |
371 | return _complete_frame_filter_name(word, printer_list) | |
372 | ||
373 | def invoke(self, arg, from_tty): | |
374 | command_tuple = self._parse_pri_arg(arg) | |
f9e59d06 | 375 | if command_tuple is not None: |
1e611234 PM |
376 | self._set_filter_priority(command_tuple) |
377 | ||
13123da8 | 378 | |
1e611234 PM |
379 | class ShowFrameFilterPriority(gdb.Command): |
380 | """GDB command to show the priority of the specified frame-filter. | |
381 | ||
13123da8 | 382 | Usage: show frame-filter priority DICTIONARY NAME |
1e611234 | 383 | |
13123da8 SM |
384 | DICTIONARY is the name of the frame filter dictionary on which to |
385 | operate. Named dictionaries are: "global" for the global frame | |
386 | filter dictionary, "progspace" for the program space's framefilter | |
387 | dictionary. If either of these two are not specified, the | |
388 | dictionary name is assumed to be the name of an "objfile" -- a | |
389 | shared library or an executable. | |
1e611234 | 390 | |
13123da8 | 391 | NAME matches the name of the frame-filter to operate on.""" |
1e611234 PM |
392 | |
393 | def __init__(self): | |
13123da8 SM |
394 | super(ShowFrameFilterPriority, self).__init__( |
395 | "show frame-filter " "priority", gdb.COMMAND_DATA | |
396 | ) | |
1e611234 PM |
397 | |
398 | def _parse_pri_arg(self, arg): | |
399 | """Internal worker to parse a dictionary and name from a | |
400 | tuple. | |
401 | ||
402 | Arguments: | |
403 | arg: Tuple which contains the arguments from the command. | |
404 | ||
405 | Returns: | |
406 | A tuple containing the dictionary, and frame filter name. | |
407 | ||
408 | Raises: | |
409 | gdb.GdbError: An error parsing the arguments. | |
410 | """ | |
411 | ||
13123da8 | 412 | argv = gdb.string_to_argv(arg) |
1e611234 PM |
413 | argc = len(argv) |
414 | if argc != 2: | |
13123da8 | 415 | print("show frame-filter priority " "takes exactly two arguments.") |
1e611234 PM |
416 | return None |
417 | ||
418 | return argv | |
419 | ||
420 | def get_filter_priority(self, frame_filters, name): | |
421 | """Worker for retrieving the priority of frame_filters. | |
422 | ||
423 | Arguments: | |
424 | frame_filters: Name of frame filter dictionary. | |
425 | name: object to select printers. | |
426 | ||
427 | Returns: | |
428 | The priority of the frame filter. | |
429 | ||
430 | Raises: | |
431 | gdb.GdbError: A frame filter cannot be found. | |
432 | """ | |
433 | ||
434 | op_list = gdb.frames.return_list(frame_filters) | |
435 | ||
436 | try: | |
437 | ff = op_list[name] | |
438 | except KeyError: | |
439 | msg = "frame-filter '" + str(name) + "' not found." | |
440 | raise gdb.GdbError(msg) | |
441 | ||
442 | return gdb.frames.get_priority(ff) | |
443 | ||
444 | def complete(self, text, word): | |
445 | """Completion function for both frame filter dictionary, and | |
446 | frame filter name.""" | |
447 | ||
448 | if text.count(" ") == 0: | |
449 | return _complete_frame_filter_list(text, word, False) | |
450 | else: | |
451 | printer_list = frame._return_list(text.split()[0].rstrip()) | |
452 | return _complete_frame_filter_name(word, printer_list) | |
453 | ||
454 | def invoke(self, arg, from_tty): | |
455 | command_tuple = self._parse_pri_arg(arg) | |
f9e59d06 | 456 | if command_tuple is None: |
1e611234 PM |
457 | return |
458 | filter_name = command_tuple[1] | |
459 | list_name = command_tuple[0] | |
460 | try: | |
13123da8 | 461 | priority = self.get_filter_priority(list_name, filter_name) |
562fc849 PM |
462 | except Exception: |
463 | e = sys.exc_info()[1] | |
13123da8 | 464 | print("Error printing filter priority for '" + name + "':" + str(e)) |
1e611234 | 465 | else: |
13123da8 SM |
466 | print( |
467 | "Priority of filter '" | |
468 | + filter_name | |
469 | + "' in list '" | |
470 | + list_name | |
471 | + "' is: " | |
472 | + str(priority) | |
473 | ) | |
474 | ||
1e611234 PM |
475 | |
476 | # Register commands | |
477 | SetFilterPrefixCmd() | |
478 | ShowFilterPrefixCmd() | |
479 | InfoFrameFilter() | |
480 | EnableFrameFilter() | |
481 | DisableFrameFilter() | |
482 | SetFrameFilterPriority() | |
483 | ShowFrameFilterPriority() |