diff options
author | Phil Muldoon <pmuldoon@redhat.com> | 2013-05-10 10:26:03 +0000 |
---|---|---|
committer | Phil Muldoon <pmuldoon@redhat.com> | 2013-05-10 10:26:03 +0000 |
commit | 1e611234ee3f4a1d2434f3fe7530cab87c936e0d (patch) | |
tree | 450923cf7ab19bcdc64f96fe4d7d6cd78d6e8fcd /gdb/python/lib | |
parent | 3ecb7338119e24eee389cd1b697231d643f87500 (diff) | |
download | gdb-1e611234ee3f4a1d2434f3fe7530cab87c936e0d.zip gdb-1e611234ee3f4a1d2434f3fe7530cab87c936e0d.tar.gz gdb-1e611234ee3f4a1d2434f3fe7530cab87c936e0d.tar.bz2 |
2013-05-10 Phil Muldoon <pmuldoon@redhat.com>
* stack.c (backtrace_command_1): Add "no-filters", and Python frame
filter logic.
(backtrace_command): Add "no-filters" option parsing.
(_initialize_stack): Alter help to reflect "no-filters" option.
* Makefile.in (SUBDIR_PYTHON_OBS): Add py-framefilter.o
(SUBDIR_PYTHON_SRCS): Add py-framefilter.c
(py-frame.o): Add target
* data-directory/Makefile.in (PYTHON_DIR): Add Python frame
filter files.
* python/python.h: Add new frame filter constants, and flag enum.
(apply_frame_filter): Add definition.
* python/python.c (apply_frame_filter): New non-Python
enabled function.
* python/py-utils.c (py_xdecref): New function.
(make_cleanup_py_xdecref): Ditto.
* python/py-objfile.c: Declare frame_filters dictionary.
(objfpy_dealloc): Add frame_filters dealloc.
(objfpy_new): Initialize frame_filters attribute.
(objfile_to_objfile_object): Ditto.
(objfpy_get_frame_filters): New function.
(objfpy_set_frame_filters): New function.
* python/py-progspace.c: Declare frame_filters dictionary.
(pspy_dealloc): Add frame_filters dealloc.
(pspy_new): Initialize frame_filters attribute.
(pspacee_to_pspace_object): Ditto.
(pspy_get_frame_filters): New function.
(pspy_set_frame_filters): New function.
* python/py-framefilter.c: New file.
* python/lib/gdb/command/frame_filters.py: New file.
* python/lib/gdb/frames.py: New file.
* python/lib/gdb/__init__.py: Initialize global frame_filters
dictionary
* python/lib/gdb/FrameDecorator.py: New file.
* python/lib/gdb/FrameIterator.py: New file.
* mi/mi-cmds.c (mi_cmds): Add frame filters command.
* mi/mi-cmds.h: Declare.
* mi/mi-cmd-stack.c (mi_cmd_stack_list_frames): Add
--no-frame-filter logic, and Python frame filter logic.
(stack_enable_frame_filters): New function.
(parse_no_frame_option): Ditto.
(mi_cmd_stack_list_frames): Add --no-frame-filter and Python frame
filter logic.
(mi_cmd_stack_list_locals): Ditto.
(mi_cmd_stack_list_args): Ditto.
(mi_cmd_stack_list_variables): Ditto.
* NEWS: Add frame filter note.
2013-05-10 Phil Muldoon <pmuldoon@redhat.com>
* gdb.python/py-framefilter.py: New File.
* gdb.python/py-framefilter-mi.exp: Ditto.
* gdb.python/py-framefilter.c: Ditto.
* gdb.python/py-framefilter-mi.exp: Ditto.
* gdb.python/py-framefilter-mi.c: Ditto,
* gdb.python/py-framefilter-gdb.py.in: Ditto.
2013-05-10 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Backtrace): Add "no-filter" argument.
(Python API): Add Frame Filters API, Frame Wrapper API,
Writing a Frame Filter/Wrapper, Managing Management of Frame
Filters chapter entries.
(Frame Filters API): New Node.
(Frame Wrapper API): New Node.
(Writing a Frame Filter): New Node.
(Managing Frame Filters): New Node.
(Progspaces In Python): Add note about frame_filters attribute.
(Objfiles in Python): Ditto.
(GDB/MI Stack Manipulation): Add -enable-frame-filters command,
@anchors and --no-frame-filters option to -stack-list-variables,
-stack-list-frames, -stack-list-locals and -stack-list-arguments
commands.
Diffstat (limited to 'gdb/python/lib')
-rw-r--r-- | gdb/python/lib/gdb/FrameDecorator.py | 285 | ||||
-rw-r--r-- | gdb/python/lib/gdb/FrameIterator.py | 45 | ||||
-rw-r--r-- | gdb/python/lib/gdb/__init__.py | 2 | ||||
-rw-r--r-- | gdb/python/lib/gdb/command/frame_filters.py | 461 | ||||
-rw-r--r-- | gdb/python/lib/gdb/frames.py | 229 |
5 files changed, 1022 insertions, 0 deletions
diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py new file mode 100644 index 0000000..cacab4d --- /dev/null +++ b/gdb/python/lib/gdb/FrameDecorator.py @@ -0,0 +1,285 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import gdb + +class FrameDecorator(object): + """Basic implementation of a Frame Decorator""" + + """ This base frame decorator decorates a frame or another frame + decorator, and provides convenience methods. If this object is + wrapping a frame decorator, defer to that wrapped object's method + if it has one. This allows for frame decorators that have + sub-classed FrameDecorator object, but also wrap other frame + decorators on the same frame to correctly execute. + + E.g + + If the result of frame filters running means we have one gdb.Frame + wrapped by multiple frame decorators, all sub-classed from + FrameDecorator, the resulting hierarchy will be: + + Decorator1 + -- (wraps) Decorator2 + -- (wraps) FrameDecorator + -- (wraps) gdb.Frame + + In this case we have two frame decorators, both of which are + sub-classed from FrameDecorator. If Decorator1 just overrides the + 'function' method, then all of the other methods are carried out + by the super-class FrameDecorator. But Decorator2 may have + overriden other methods, so FrameDecorator will look at the + 'base' parameter and defer to that class's methods. And so on, + down the chain.""" + + # 'base' can refer to a gdb.Frame or another frame decorator. In + # the latter case, the child class will have called the super + # method and _base will be an object conforming to the Frame Filter + # class. + def __init__(self, base): + self._base = base + + @staticmethod + def _is_limited_frame(frame): + """Internal utility to determine if the frame is special or + limited.""" + sal = frame.find_sal() + + if (not sal.symtab or not sal.symtab.filename + or frame.type() == gdb.DUMMY_FRAME + or frame.type() == gdb.SIGTRAMP_FRAME): + + return True + + return False + + def elided(self): + """Return any elided frames that this class might be + wrapping, or None.""" + if hasattr(self._base, "elided"): + return self._base.elided() + + return None + + def function(self): + """ Return the name of the frame's function or an address of + the function of the frame. First determine if this is a + special frame. If not, try to determine filename from GDB's + frame internal function API. Finally, if a name cannot be + determined return the address. If this function returns an + address, GDB will attempt to determine the function name from + its internal minimal symbols store (for example, for inferiors + without debug-info).""" + + # Both gdb.Frame, and FrameDecorator have a method called + # "function", so determine which object this is. + if not isinstance(self._base, gdb.Frame): + if hasattr(self._base, "function"): + # If it is not a gdb.Frame, and there is already a + # "function" method, use that. + return self._base.function() + + frame = self.inferior_frame() + + if frame.type() == gdb.DUMMY_FRAME: + return "<function called from gdb>" + elif frame.type() == gdb.SIGTRAMP_FRAME: + return "<signal handler called>" + + func = frame.function() + + # If we cannot determine the function name, return the + # address. If GDB detects an integer value from this function + # it will attempt to find the function name from minimal + # symbols via its own internal functions. + if func == None: + pc = frame.pc() + return pc + + return str(func) + + def address(self): + """ Return the address of the frame's pc""" + + if hasattr(self._base, "address"): + return self._base.address() + + frame = self.inferior_frame() + return frame.pc() + + def filename(self): + """ Return the filename associated with this frame, detecting + and returning the appropriate library name is this is a shared + library.""" + + if hasattr(self._base, "filename"): + return self._base.filename() + + frame = self.inferior_frame() + sal = frame.find_sal() + if not sal.symtab or not sal.symtab.filename: + pc = frame.pc() + return gdb.solib_name(pc) + else: + return sal.symtab.filename + + def frame_args(self): + """ Return an iterable of frame arguments for this frame, if + any. The iterable object contains objects conforming with the + Symbol/Value interface. If there are no frame arguments, or + if this frame is deemed to be a special case, return None.""" + + if hasattr(self._base, "frame_args"): + return self._base.frame_args() + + frame = self.inferior_frame() + if self._is_limited_frame(frame): + return None + + args = FrameVars(frame) + return args.fetch_frame_args() + + def frame_locals(self): + """ Return an iterable of local variables for this frame, if + any. The iterable object contains objects conforming with the + Symbol/Value interface. If there are no frame locals, or if + this frame is deemed to be a special case, return None.""" + + if hasattr(self._base, "frame_locals"): + return self._base.frame_locals() + + frame = self.inferior_frame() + if self._is_limited_frame(frame): + return None + + args = FrameVars(frame) + return args.fetch_frame_locals() + + def line(self): + """ Return line number information associated with the frame's + pc. If symbol table/line information does not exist, or if + this frame is deemed to be a special case, return None""" + + if hasattr(self._base, "line"): + return self._base.line() + + frame = self.inferior_frame() + if self._is_limited_frame(frame): + return None + + sal = frame.find_sal() + if (sal): + return sal.line + else: + return None + + def inferior_frame(self): + """ Return the gdb.Frame underpinning this frame decorator.""" + + # If 'base' is a frame decorator, we want to call its inferior + # frame method. If '_base' is a gdb.Frame, just return that. + if hasattr(self._base, "inferior_frame"): + return self._base.inferior_frame() + return self._base + +class SymValueWrapper(object): + """A container class conforming to the Symbol/Value interface + which holds frame locals or frame arguments.""" + def __init__(self, symbol, value): + self.sym = symbol + self.val = value + + def value(self): + """ Return the value associated with this symbol, or None""" + return self.val + + def symbol(self): + """ Return the symbol, or Python text, associated with this + symbol, or None""" + return self.sym + +class FrameVars(object): + + """Utility class to fetch and store frame local variables, or + frame arguments.""" + + def __init__(self, frame): + self.frame = frame + self.symbol_class = { + gdb.SYMBOL_LOC_STATIC: True, + gdb.SYMBOL_LOC_REGISTER: True, + gdb.SYMBOL_LOC_ARG: True, + gdb.SYMBOL_LOC_REF_ARG: True, + gdb.SYMBOL_LOC_LOCAL: True, + gdb.SYMBOL_LOC_REGPARM_ADDR: True, + gdb.SYMBOL_LOC_COMPUTED: True + } + + def fetch_b(self, sym): + """ Local utility method to determine if according to Symbol + type whether it should be included in the iterator. Not all + symbols are fetched, and only symbols that return + True from this method should be fetched.""" + + # SYM may be a string instead of a symbol in the case of + # synthetic local arguments or locals. If that is the case, + # always fetch. + if isinstance(sym, basestring): + return True + + sym_type = sym.addr_class + + return self.symbol_class.get(sym_type, False) + + def fetch_frame_locals(self): + """Public utility method to fetch frame local variables for + the stored frame. Frame arguments are not fetched. If there + are no frame local variables, return an empty list.""" + lvars = [] + + block = self.frame.block() + + while block != None: + if block.is_global or block.is_static: + break + for sym in block: + if sym.is_argument: + continue; + if self.fetch_b(sym): + lvars.append(SymValueWrapper(sym, None)) + + block = block.superblock + + return lvars + + def fetch_frame_args(self): + """Public utility method to fetch frame arguments for the + stored frame. Frame arguments are the only type fetched. If + there are no frame argument variables, return an empty list.""" + + args = [] + block = self.frame.block() + while block != None: + if block.function != None: + break + block = block.superblock + + if block != None: + for sym in block: + if not sym.is_argument: + continue; + args.append(SymValueWrapper(sym, None)) + + return args diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py new file mode 100644 index 0000000..b3af94b --- /dev/null +++ b/gdb/python/lib/gdb/FrameIterator.py @@ -0,0 +1,45 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import gdb +import itertools + +class FrameIterator(object): + """A gdb.Frame iterator. Iterates over gdb.Frames or objects that + conform to that interface.""" + + def __init__(self, frame_obj): + """Initialize a FrameIterator. + + Arguments: + frame_obj the starting frame.""" + + super(FrameIterator, self).__init__() + self.frame = frame_obj + + def __iter__(self): + return self + + def next(self): + """next implementation. + + Returns: + The next oldest frame.""" + + result = self.frame + if result is None: + raise StopIteration + self.frame = result.older() + return result diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 6311583..61f5b5e 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -67,6 +67,8 @@ pretty_printers = [] # Initial type printers. type_printers = [] +# Initial frame filters. +frame_filters = {} # Convenience variable to GDB's python directory PYTHONDIR = os.path.dirname(os.path.dirname(__file__)) diff --git a/gdb/python/lib/gdb/command/frame_filters.py b/gdb/python/lib/gdb/command/frame_filters.py new file mode 100644 index 0000000..1b73059 --- /dev/null +++ b/gdb/python/lib/gdb/command/frame_filters.py @@ -0,0 +1,461 @@ +# Frame-filter commands. +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""GDB commands for working with frame-filters.""" + +import gdb +import copy +from gdb.FrameIterator import FrameIterator +from gdb.FrameDecorator import FrameDecorator +import gdb.frames +import itertools + +# GDB Commands. +class SetFilterPrefixCmd(gdb.Command): + """Prefix command for 'set' frame-filter related operations.""" + + def __init__(self): + super(SetFilterPrefixCmd, self).__init__("set frame-filter", + gdb.COMMAND_OBSCURE, + gdb.COMPLETE_NONE, True) + +class ShowFilterPrefixCmd(gdb.Command): + """Prefix command for 'show' frame-filter related operations.""" + def __init__(self): + super(ShowFilterPrefixCmd, self).__init__("show frame-filter", + gdb.COMMAND_OBSCURE, + gdb.COMPLETE_NONE, True) +class InfoFrameFilter(gdb.Command): + """List all registered Python frame-filters. + + Usage: info frame-filters + """ + + def __init__(self): + super(InfoFrameFilter, self).__init__("info frame-filter", + gdb.COMMAND_DATA) + @staticmethod + def enabled_string(state): + """Return "Yes" if filter is enabled, otherwise "No".""" + if state: + return "Yes" + else: + return "No" + + def list_frame_filters(self, frame_filters): + """ Internal worker function to list and print frame filters + in a dictionary. + + Arguments: + frame_filters: The name of the dictionary, as + specified by GDB user commands. + """ + + sorted_frame_filters = sorted(frame_filters.items(), + key=lambda i: gdb.frames.get_priority(i[1]), + reverse=True) + + if len(sorted_frame_filters) == 0: + print(" No frame filters registered.") + else: + print(" Priority Enabled Name") + for frame_filter in sorted_frame_filters: + name = frame_filter[0] + try: + priority = '{:<8}'.format( + str(gdb.frames.get_priority(frame_filter[1]))) + enabled = '{:<7}'.format( + self.enabled_string(gdb.frames.get_enabled(frame_filter[1]))) + except Exception as e: + print(" Error printing filter '"+name+"': "+str(e)) + else: + print(" %s %s %s" % (priority, enabled, name)) + + def print_list(self, title, filter_list, blank_line): + print(title) + self.list_frame_filters(filter_list) + if blank_line: + print("") + + def invoke(self, arg, from_tty): + self.print_list("global frame-filters:", gdb.frame_filters, True) + + cp = gdb.current_progspace() + self.print_list("progspace %s frame-filters:" % cp.filename, + cp.frame_filters, True) + + for objfile in gdb.objfiles(): + self.print_list("objfile %s frame-filters:" % objfile.filename, + objfile.frame_filters, False) + +# Internal enable/disable functions. + +def _enable_parse_arg(cmd_name, arg): + """ Internal worker function to take an argument from + enable/disable and return a tuple of arguments. + + Arguments: + cmd_name: Name of the command invoking this function. + args: The argument as a string. + + Returns: + A tuple containing the dictionary, and the argument, or just + the dictionary in the case of "all". + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argv[0] == "all" and argc > 1: + raise gdb.GdbError(cmd_name + ": with 'all' " \ + "you may not specify a filter.") + else: + if argv[0] != "all" and argc != 2: + raise gdb.GdbError(cmd_name + " takes exactly two arguments.") + + return argv + +def _do_enable_frame_filter(command_tuple, flag): + """Worker for enabling/disabling frame_filters. + + Arguments: + command_type: A tuple with the first element being the + frame filter dictionary, and the second being + the frame filter name. + flag: True for Enable, False for Disable. + """ + + list_op = command_tuple[0] + op_list = gdb.frames.return_list(list_op) + + if list_op == "all": + for item in op_list: + gdb.frames.set_enabled(item, flag) + else: + frame_filter = command_tuple[1] + try: + ff = op_list[frame_filter] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + gdb.frames.set_enabled(ff, flag) + +def _complete_frame_filter_list(text, word, all_flag): + """Worker for frame filter dictionary name completion. + + Arguments: + text: The full text of the command line. + word: The most recent word of the command line. + all_flag: Whether to include the word "all" in completion. + + Returns: + A list of suggested frame filter dictionary name completions + from text/word analysis. This list can be empty when there + are no suggestions for completion. + """ + if all_flag == True: + filter_locations = ["all", "global", "progspace"] + else: + filter_locations = ["global", "progspace"] + for objfile in gdb.objfiles(): + filter_locations.append(objfile.filename) + + # If the user just asked for completions with no completion + # hints, just return all the frame filter dictionaries we know + # about. + if (text == ""): + return filter_locations + + # Otherwise filter on what we know. + flist = filter(lambda x,y=text:x.startswith(y), filter_locations) + + # If we only have one completion, complete it and return it. + if len(flist) == 1: + flist[0] = flist[0][len(text)-len(word):] + + # Otherwise, return an empty list, or a list of frame filter + # dictionaries that the previous filter operation returned. + return flist + +def _complete_frame_filter_name(word, printer_dict): + """Worker for frame filter name completion. + + Arguments: + + word: The most recent word of the command line. + + printer_dict: The frame filter dictionary to search for frame + filter name completions. + + Returns: A list of suggested frame filter name completions + from word analysis of the frame filter dictionary. This list + can be empty when there are no suggestions for completion. + """ + + printer_keys = printer_dict.keys() + if (word == ""): + return printer_keys + + flist = filter(lambda x,y=word:x.startswith(y), printer_keys) + return flist + +class EnableFrameFilter(gdb.Command): + """GDB command to disable the specified frame-filter. + + Usage: enable frame-filter enable DICTIONARY [NAME] + + DICTIONARY is the name of the frame filter dictionary on which to + operate. If dictionary is set to "all", perform operations on all + dictionaries. Named dictionaries are: "global" for the global + frame filter dictionary, "progspace" for the program space's frame + filter dictionary. If either all, or the two named dictionaries + are not specified, the dictionary name is assumed to be the name + of the object-file name. + + NAME matches the name of the frame-filter to operate on. If + DICTIONARY is "all", NAME is ignored. + """ + def __init__(self): + super(EnableFrameFilter, self).__init__("enable frame-filter", + gdb.COMMAND_DATA) + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, True) + else: + printer_list = gdb.frames.return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = _enable_parse_arg("enable frame-filter", arg) + _do_enable_frame_filter(command_tuple, True) + + +class DisableFrameFilter(gdb.Command): + """GDB command to disable the specified frame-filter. + + Usage: disable frame-filter disable DICTIONARY [NAME] + + DICTIONARY is the name of the frame filter dictionary on which to + operate. If dictionary is set to "all", perform operations on all + dictionaries. Named dictionaries are: "global" for the global + frame filter dictionary, "progspace" for the program space's frame + filter dictionary. If either all, or the two named dictionaries + are not specified, the dictionary name is assumed to be the name + of the object-file name. + + NAME matches the name of the frame-filter to operate on. If + DICTIONARY is "all", NAME is ignored. + """ + def __init__(self): + super(DisableFrameFilter, self).__init__("disable frame-filter", + gdb.COMMAND_DATA) + + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, True) + else: + printer_list = gdb.frames.return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = _enable_parse_arg("disable frame-filter", arg) + _do_enable_frame_filter(command_tuple, False) + +class SetFrameFilterPriority(gdb.Command): + """GDB command to set the priority of the specified frame-filter. + + Usage: set frame-filter priority DICTIONARY NAME PRIORITY + + DICTIONARY is the name of the frame filter dictionary on which to + operate. Named dictionaries are: "global" for the global frame + filter dictionary, "progspace" for the program space's framefilter + dictionary. If either of these two are not specified, the + dictionary name is assumed to be the name of the object-file name. + + NAME matches the name of the frame filter to operate on. + + PRIORITY is the an integer to assign the new priority to the frame + filter. + """ + + def __init__(self): + super(SetFrameFilterPriority, self).__init__("set frame-filter " \ + "priority", + gdb.COMMAND_DATA) + + def _parse_pri_arg(self, arg): + """Internal worker to parse a priority from a tuple. + + Arguments: + arg: Tuple which contains the arguments from the command. + + Returns: + A tuple containing the dictionary, name and priority from + the arguments. + + Raises: + gdb.GdbError: An error parsing the arguments. + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argc != 3: + print("set frame-filter priority " \ + "takes exactly three arguments.") + return None + + return argv + + def _set_filter_priority(self, command_tuple): + """Internal worker for setting priority of frame-filters, by + parsing a tuple and calling _set_priority with the parsed + tuple. + + Arguments: + command_tuple: Tuple which contains the arguments from the + command. + """ + + list_op = command_tuple[0] + frame_filter = command_tuple[1] + priority = command_tuple[2] + + op_list = gdb.frames.return_list(list_op) + + try: + ff = op_list[frame_filter] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + gdb.frames.set_priority(ff, priority) + + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, False) + else: + printer_list = gdb.frames.return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = self._parse_pri_arg(arg) + if command_tuple != None: + self._set_filter_priority(command_tuple) + +class ShowFrameFilterPriority(gdb.Command): + """GDB command to show the priority of the specified frame-filter. + + Usage: show frame-filter priority DICTIONARY NAME + + DICTIONARY is the name of the frame filter dictionary on which to + operate. Named dictionaries are: "global" for the global frame + filter dictionary, "progspace" for the program space's framefilter + dictionary. If either of these two are not specified, the + dictionary name is assumed to be the name of the object-file name. + + NAME matches the name of the frame-filter to operate on. + """ + + def __init__(self): + super(ShowFrameFilterPriority, self).__init__("show frame-filter " \ + "priority", + gdb.COMMAND_DATA) + + def _parse_pri_arg(self, arg): + """Internal worker to parse a dictionary and name from a + tuple. + + Arguments: + arg: Tuple which contains the arguments from the command. + + Returns: + A tuple containing the dictionary, and frame filter name. + + Raises: + gdb.GdbError: An error parsing the arguments. + """ + + argv = gdb.string_to_argv(arg); + argc = len(argv) + if argc != 2: + print("show frame-filter priority " \ + "takes exactly two arguments.") + return None + + return argv + + def get_filter_priority(self, frame_filters, name): + """Worker for retrieving the priority of frame_filters. + + Arguments: + frame_filters: Name of frame filter dictionary. + name: object to select printers. + + Returns: + The priority of the frame filter. + + Raises: + gdb.GdbError: A frame filter cannot be found. + """ + + op_list = gdb.frames.return_list(frame_filters) + + try: + ff = op_list[name] + except KeyError: + msg = "frame-filter '" + str(name) + "' not found." + raise gdb.GdbError(msg) + + return gdb.frames.get_priority(ff) + + def complete(self, text, word): + """Completion function for both frame filter dictionary, and + frame filter name.""" + + if text.count(" ") == 0: + return _complete_frame_filter_list(text, word, False) + else: + printer_list = frame._return_list(text.split()[0].rstrip()) + return _complete_frame_filter_name(word, printer_list) + + def invoke(self, arg, from_tty): + command_tuple = self._parse_pri_arg(arg) + if command_tuple == None: + return + filter_name = command_tuple[1] + list_name = command_tuple[0] + try: + priority = self.get_filter_priority(list_name, filter_name); + except Exception as e: + print("Error printing filter priority for '"+name+"':"+str(e)) + else: + print("Priority of filter '" + filter_name + "' in list '" \ + + list_name + "' is: " + str(priority)) + +# Register commands +SetFilterPrefixCmd() +ShowFilterPrefixCmd() +InfoFrameFilter() +EnableFrameFilter() +DisableFrameFilter() +SetFrameFilterPriority() +ShowFrameFilterPriority() diff --git a/gdb/python/lib/gdb/frames.py b/gdb/python/lib/gdb/frames.py new file mode 100644 index 0000000..10dce8e --- /dev/null +++ b/gdb/python/lib/gdb/frames.py @@ -0,0 +1,229 @@ +# Frame-filter commands. +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""Internal functions for working with frame-filters.""" + +import gdb +from gdb.FrameIterator import FrameIterator +from gdb.FrameDecorator import FrameDecorator +import itertools +import collections + +def get_priority(filter_item): + """ Internal worker function to return the frame-filter's priority + from a frame filter object. This is a fail free function as it is + used in sorting and filtering. If a badly implemented frame + filter does not implement the priority attribute, return zero + (otherwise sorting/filtering will fail and prevent other frame + filters from executing). + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The priority of the frame filter from the "priority" + attribute, or zero. + """ + # Do not fail here, as the sort will fail. If a filter has not + # (incorrectly) set a priority, set it to zero. + return getattr(filter_item, "priority", 0) + +def set_priority(filter_item, priority): + """ Internal worker function to set the frame-filter's priority. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + priority: The priority to assign as an integer. + """ + + filter_item.priority = priority + +def get_enabled(filter_item): + """ Internal worker function to return a filter's enabled state + from a frame filter object. This is a fail free function as it is + used in sorting and filtering. If a badly implemented frame + filter does not implement the enabled attribute, return False + (otherwise sorting/filtering will fail and prevent other frame + filters from executing). + + Arguments: + filter_item: An object conforming to the frame filter + interface. + + Returns: + The enabled state of the frame filter from the "enabled" + attribute, or False. + """ + + # If the filter class is badly implemented when called from the + # Python filter command, do not cease filter operations, just set + # enabled to False. + return getattr(filter_item, "enabled", False) + +def set_enabled(filter_item, state): + """ Internal Worker function to set the frame-filter's enabled + state. + + Arguments: + filter_item: An object conforming to the frame filter + interface. + state: True or False, depending on desired state. + """ + + filter_item.enabled = state + +def return_list(name): + """ Internal Worker function to return the frame filter + dictionary, depending on the name supplied as an argument. If the + name is not "all", "global" or "progspace", it is assumed to name + an object-file. + + Arguments: + name: The name of the list, as specified by GDB user commands. + + Returns: + A dictionary object for a single specified dictionary, or a + list containing all the items for "all" + + Raises: + gdb.GdbError: A dictionary of that name cannot be found. + """ + + # If all dictionaries are wanted in the case of "all" we + # cannot return a combined dictionary as keys() may clash in + # between different dictionaries. As we just want all the frame + # filters to enable/disable them all, just return the combined + # items() as a list. + if name == "all": + all_dicts = gdb.frame_filters.values() + all_dicts = all_dicts + gdb.current_progspace().frame_filters.values() + for objfile in gdb.objfiles(): + all_dicts = all_dicts + objfile.frame_filters.values() + return all_dicts + + if name == "global": + return gdb.frame_filters + else: + if name == "progspace": + cp = gdb.current_progspace() + return cp.frame_filters + else: + for objfile in gdb.objfiles(): + if name == objfile.filename: + return objfile.frame_filters + + msg = "Cannot find frame-filter dictionary for '" + name + "'" + raise gdb.GdbError(msg) + +def _sort_list(): + """ Internal Worker function to merge all known frame-filter + lists, prune any filters with the state set to "disabled", and + sort the list on the frame-filter's "priority" attribute. + + Returns: + sorted_list: A sorted, pruned list of frame filters to + execute. + """ + + all_filters = [] + for objfile in gdb.objfiles(): + all_filters = all_filters + objfile.frame_filters.values() + cp = gdb.current_progspace() + + all_filters = all_filters + cp.frame_filters.values() + all_filters = all_filters + gdb.frame_filters.values() + + sorted_frame_filters = sorted(all_filters, key = get_priority, + reverse = True) + + sorted_frame_filters = filter(get_enabled, + sorted_frame_filters) + + return sorted_frame_filters + +def execute_frame_filters(frame, frame_low, frame_high): + """ Internal function called from GDB that will execute the chain + of frame filters. Each filter is executed in priority order. + After the execution completes, slice the iterator to frame_low - + frame_high range. + + Arguments: + frame: The initial frame. + + frame_low: The low range of the slice. If this is a negative + integer then it indicates a backward slice (ie bt -4) which + counts backward from the last frame in the backtrace. + + frame_high: The high range of the slice. If this is -1 then + it indicates all frames until the end of the stack from + frame_low. + + Returns: + frame_iterator: The sliced iterator after all frame + filters have had a change to execute, or None if no frame + filters are registered. + """ + + # Get a sorted list of frame filters. + sorted_list = _sort_list() + + # Check to see if there are any frame-filters. If not, just + # return None and let default backtrace printing occur. + if len(sorted_list) == 0: + return None + + frame_iterator = FrameIterator(frame) + + # Apply a basic frame decorator to all gdb.Frames. This unifies the + # interface. + frame_iterator = itertools.imap(FrameDecorator, frame_iterator) + + for ff in sorted_list: + frame_iterator = ff.filter(frame_iterator) + + # Slicing + + # Is this a slice from the end of the backtrace, ie bt -2? + if frame_low < 0: + count = 0 + slice_length = abs(frame_low) + # We cannot use MAXLEN argument for deque as it is 2.6 onwards + # and some GDB versions might be < 2.6. + sliced = collections.deque() + + for frame_item in frame_iterator: + if count >= slice_length: + sliced.popleft(); + count = count + 1 + sliced.append(frame_item) + + return iter(sliced) + + # -1 for frame_high means until the end of the backtrace. Set to + # None if that is the case, to indicate to itertools.islice to + # slice to the end of the iterator. + if frame_high == -1: + frame_high = None + else: + # As frames start from 0, add one to frame_high so islice + # correctly finds the end + frame_high = frame_high + 1; + + sliced = itertools.islice(frame_iterator, frame_low, frame_high) + + return sliced |