[patch][python] 1 of 5 - Frame filter Python C code changes.

Phil Muldoon
2012-11-30T14:31:35Z

This email and patch covers the python/ changes for Python Frame Filters.

2012-11-30 Phil Muldoon <pmuldoon@redhat.com>

* 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/__init__.py: Initialize global frame_filters
dictionary
* python/lib/gdb/FrameWrapper.py: New file.
* python/lib/gdb/FrameIterator.py: New file.
* python/lib/gdb/BaseFrameWrapper.py: New file.
--

diff --git a/gdb/python/lib/gdb/BaseFrameWrapper.py b/gdb/python/lib/gdb/BaseFrameWrapper.py
new file mode 100644
index 0000000..c96a7df
--- /dev/null
+++ b/gdb/python/lib/gdb/BaseFrameWrapper.py
@@ -0,0 +1,293 @@
+# Copyright (C) 2012 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
+from gdb.FrameWrapper import FrameWrapper
+
+class BaseFrameWrapper(FrameWrapper):
+ """Basic implementation of a Frame Wrapper"""
+
+ """ This base frame wrapper wraps a frame or another frame
+ wrapper, and provides convenience methods. If this object is
+ wrapping a frame wrapper, defer to that wrapped object's method if
+ it has one. This allows for frame wrappers that have sub-classed
+ BaseFrameWrapper, but also wrap other frame wrappers 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 wrappers, all sub-classed from
+ BaseFrameWrapper:
+
+ Wrapper1(Wrapper2(BaseFrameWrapper(gdb.Frame)))
+
+ In this case we have two frame wrappers, both of which are
+ sub-classed from BaseFrameWrapper. If Wrapper1 just overrides the
+ 'function' method, then all of the other methods are carried out
+ by the super-class BaseFrameWrapper. But Wrapper2 may have
+ overriden other methods, so BaseFrameWrapper 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 filter. 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):
+ super(BaseFrameWrapper, self).__init__(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 == gdb.DUMMY_FRAME
+ or frame == 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, first determining
+ if it 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 not isinstance(self.base, gdb.Frame):
+ if hasattr(self.base, "function"):
+ 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()
+ sal = frame.find_sal()
+ pc = frame.pc()
+
+ if func == None:
+ unknown = format(" 0x%08x in" % pc)
+ return unknown
+
+ 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 iterator of frame arguments for this frame, if
+ any. The iterator 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 iterator of local variables for this frame, if
+ any. The iterator 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 wrapper."""
+
+ # If 'base' is a frame wrapper, 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 BaseSymValueWrapper():
+ """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():
+
+ """Utility class to fetch and store frame local variables, or
+ frame arguments."""
+
+ def __init__(self,frame):
+ self.frame = frame
+
+ @staticmethod
+ def fetch_b(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 {
+ 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
+ }.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 not frame local variables, return None."""
+ lvars = []
+ try:
+ block = self.frame.block()
+ except:
+ return None
+
+ for sym in block:
+ if sym.is_argument:
+ continue;
+ if self.fetch_b(sym):
+ lvars.append(BaseSymValueWrapper(sym, None))
+
+ if len(lvars) == 0:
+ return None
+
+ return iter(lvars)
+
+ def fetch_frame_args(self):
+ """Public utility method to fetch frame argument for the
+ stored frame. Frame arguments are the only type fetched. If
+ there are no frame arguments variables, return None."""
+
+ args = []
+ try:
+ block = self.frame.block()
+ except:
+ return None
+
+ for sym in block:
+ if not sym.is_argument:
+ continue;
+ args.append(BaseSymValueWrapper(sym,None))
+
+ if len(args) == 0:
+ return None
+
+ return iter(args)
+
+ def get_value(self, sym, block):
+ """Public utility method to fetch a value from a symbol."""
+ if len(sym.linkage_name):
+ nsym, is_field_of_this = gdb.lookup_symbol(sym.linkage_name, block)
+ if nsym != None:
+ if nsym.addr_class != gdb.SYMBOL_LOC_REGISTER:
+ sym = nsym
+
+ try:
+ val = sym.value(self.frame)
+
+ except RuntimeError, text:
+ val = text
+ if val == None:
+ val = "???"
+
+ return val
diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py
new file mode 100644
index 0000000..ddc9087
--- /dev/null
+++ b/gdb/python/lib/gdb/FrameIterator.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2012 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 __getitem__(self,index):
+ """__getitem__ implementation.
+
+ Arguments:
+ index: A specific index to fetch."""
+
+ return next(itertools.islice(self.frame,index,index+1))
+
+ 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/FrameWrapper.py b/gdb/python/lib/gdb/FrameWrapper.py
new file mode 100644
index 0000000..4db9a43
--- /dev/null
+++ b/gdb/python/lib/gdb/FrameWrapper.py
@@ -0,0 +1,155 @@
+# Copyright (C) 2012 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 FrameWrapper (object):
+ """Interface for a Frame Wrapper."""
+
+ """ A frame wrapper wraps a frame and provides additional and
+ convenience methods. """
+ def __init__(self, frame):
+ super(FrameWrapper, self).__init__()
+ self.frame = frame
+
+ def elided (self):
+ """ The elided method groups frames together in a
+ hierarchical system. An example would be an interpreter call
+ that occurs over many frames but might be better represented
+ as a group of frames distinct from the other frames.
+
+ Arguments: None
+
+ Returns: The elided function must return an iterator that
+ contains the frames that are being elided, or None.
+ Elided frames are indented from normal frames in a
+ backtrace, to show affinity with the frame that
+ elided them. Note that it is the frame filter's task
+ to filter out the elided frames from the source
+ iterator, and also to provide the iterator of elided
+ frames in this function. If this function returns a
+ None, no frames will be elided.
+ """
+
+ pass
+
+ def function (self):
+ """ The name of the function in the frame.
+
+ Arguments: None.
+
+ Returns: A string describing the function. If this function
+ returns None, no data will be displayed for this
+ field at printing.
+ """
+ pass
+
+ def address (self):
+ """ The address of the frame.
+
+ Arguments: None.
+
+ Returns: A numeric integer type of sufficient size to describe
+ the address of the frame, or None. If this function
+ returns a None, no data will be displayed for this
+ field at printing.
+ """
+
+ pass
+
+ def filename (self):
+ """ The filename associated with the function of this frame.
+
+ Arguments: None.
+
+ Returns: A string containing the filename, and optionally, the
+ path to the filename of the frame, or None. If this
+ function returns a None, no data will be displayed
+ for this field at printing.
+ """
+
+ pass
+
+ def line (self):
+ """ The line number associated with the current position
+ within the function addressed by this frame.
+
+ Arguments: None.
+
+ Returns: An integer type representing the line number, or
+ None. If this function returns a None, no data will
+ be displayed for this field at printing.
+ """
+
+ pass
+
+ def frame_args (self):
+ """ The arguments of the function in this frame.
+
+ Arguments: None.
+
+ Returns: An iterator that conforms to the Python iterator
+ protocol, or None. If this method returns a None,
+ instead of an iterator, then no data will be printed
+ for frame arguments. If this method returns an
+ iterator, it must contain objects that implement two
+ methods, described here.
+
+ The object must implement an argument method which
+ takes no parameters and must return a gdb.Symbol or a
+ Python string. It must also implement a value method
+ which takes no parameters and which must return a
+ gdb.Value, a Python value, or None. If the value
+ method returns a None, and the argument method
+ returns a gdb.Symbol, GDB will look-up and print the
+ value of the gdb.Symbol automatically. If the
+ argument method contains a string, then the value
+ method must not return a None.
+ """
+ pass
+
+ def frame_locals (self):
+ """ The local variables of the function in this frame.
+
+ Arguments: None.
+
+ Returns: An iterator that conforms to the Python iterator
+ protocol, or None. If this method returns a None,
+ instead of an iterator, then no data will be printed
+ for frame locals. If this method returns an
+ iterator, it must contain objects that implement two
+ methods, described here.
+
+ The object must implement an argument method which
+ takes no parameters and must return a gdb.Symbol or a
+ Python string. It must also implement a value method
+ which takes no parameters and which must return a
+ gdb.Value, a Python value, or None. If the value
+ method returns a None, and the argument method
+ returns a gdb.Symbol, GDB will look-up and print the
+ value of the gdb.Symbol automatically. If the
+ argument method contains a string, then the value
+ method must not return a None.
+ """
+ pass
+
+ def frame (self):
+ """ The gdb.Frame that this wrapper is wrapping.
+
+ Arguments: None.
+
+ Returns: The gdb.Frame that this wrapper is wrapping.
+ """
+ pass
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 0671526..a1c661d 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -72,6 +72,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..fc115b0
--- /dev/null
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -0,0 +1,621 @@
+# Frame-filter commands.
+# Copyright (C) 2012 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.BaseFrameWrapper import BaseFrameWrapper
+import itertools
+
+def _parse_arg(cmd_name, arg):
+ """ Internal worker function to take an argument 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.
+ """
+
+ argv = gdb.string_to_argv(arg);
+ argc = len(argv)
+ if argc != 2:
+ raise gdb.GdbError(cmd_name + " takes exactly two arguments.")
+
+ object_list = argv[0]
+ argument = argv[1]
+
+ return(object_list, argument)
+
+def _get_sort_priority(filter_item):
+ """ Internal worker function to return the frame-filter's priority
+ from a frame filter tuple object.
+
+ Arguments:
+ filter_item: A tuple, with the first element being the name,
+ and the second being the frame-filter object.
+
+ Returns:
+ The priority of the frame filter from the "priority"
+ attribute.
+ """
+ # Do not fail here, as the sort will fail. If a filter has not
+ # (incorrectly) set a priority, set it to zero.
+ if hasattr(filter_item[1], "priority"):
+ return filter_item[1].priority
+ else:
+ return 0
+
+def _get_priority(filter_item):
+ """ Internal worker function to return the frame-filter's priority.
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+
+ Returns:
+ The priority of the frame filter from the "priority"
+ attribute.
+
+ Raises:
+ gdb.GdbError: When the priority attribute has not been
+ implemented.
+ """
+ if hasattr(filter_item, "priority"):
+ return filter_item.priority
+ else:
+ raise gdb.GdbError("Cannot find class attribute 'priority'")
+
+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.
+
+ Raises:
+ gdb.GdbError: When the priority attribute has not been
+ implemented.
+ """
+
+ if hasattr(filter_item, "priority"):
+ filter_item.priority = priority
+ else:
+ raise gdb.GdbError("Cannot find class attribute 'priority'")
+
+def _get_enabled(filter_item):
+ """ Internal worker function to return the frame-filter's enabled
+ state.
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+
+ Returns:
+ The enabled state of the frame filter from the "enabled"
+ attribute.
+
+ Raises:
+ gdb.GdbError: When the enabled attribute has not been
+ implemented.
+ """
+
+ if hasattr(filter_item, "enabled"):
+ return filter_item.enabled
+ else:
+ raise gdb.GdbError("Cannot find class attribute 'enabled'")
+
+def _get_filter_enabled(filter_item):
+ """ Internal Worker function to return the frame-filter's enabled
+ state for filter operations.
+
+ Arguments:
+ filter_item: A tuple, with the first element being the name,
+ and the second being the frame-filter object.
+ Returns:
+ The enabled state of the frame filter from the "enabled"
+ attribute.
+
+ """
+ # If the filter class is badly implemented, do not cease filter
+ # operations, just set enabled to False.
+ try:
+ enabled = _get_enabled(filter_item[1])
+ except gdb.GdbError as e:
+ enabled = False
+
+ return enabled
+
+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.
+
+ Raises:
+ gdb.GdbError: When the enabled attribute has not been
+ implemented.
+ """
+
+ if hasattr(filter_item, "enabled"):
+ filter_item.enabled = state
+ else:
+ raise gdb.GdbError("Cannot find class attribute 'enabled'")
+
+def _get_name(frame_filter):
+ """ Internal Worker function to return the name of the
+ frame-filter.
+
+ Arguments:
+ filter_item: An object conforming to the frame filter
+ interface.
+
+ Returns:
+ The name of the frame filter from the "name" attribute.
+
+ Raises:
+ gdb.GdbError: When the name attribute has not been
+ implemented.
+ """
+ if hasattr(frame_filter, "name"):
+ return frame_filter.name
+ raise gdb.GdbError("Cannot find class attribute 'name'")
+
+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 "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.
+
+ Raises:
+ gdb.GdbError: A dictionary of that name cannot be found.
+ """
+
+ 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.items()
+ cp = gdb.current_progspace()
+
+ all_filters = all_filters + cp.frame_filters.items()
+ all_filters = all_filters + gdb.frame_filters.items()
+
+ sorted_frame_filters = copy.copy(all_filters)
+ sorted_frame_filters.sort(key = _get_sort_priority,
+ reverse = True)
+ sorted_frame_filters = filter(_get_filter_enabled,
+ sorted_frame_filters)
+
+ return sorted_frame_filters
+
+def invoke(frame):
+ """ Public internal function that will execute the chain of frame
+ filters. Each filter is executed in priority order.
+
+ Arguments:
+ frame: The initial frame.
+
+ Returns:
+ frame_iterator: The 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 base filter to all gdb.Frames. This unifies the
+ # interface.
+ frame_iterator = itertools.imap(BaseFrameWrapper, frame_iterator)
+
+ for ff in sorted_list:
+ frame_iterator = ff[1].filter(frame_iterator)
+
+ return frame_iterator
+
+# GDB Commands.
+class SetFilterPrefixCmd(gdb.Command):
+ def __init__(self):
+ super(SetFilterPrefixCmd, self).__init__("set python frame-filter",
+ gdb.COMMAND_DATA,
+ gdb.COMPLETE_COMMAND, True)
+class ShowFilterPrefixCmd(gdb.Command):
+ def __init__(self):
+ super(ShowFilterPrefixCmd, self).__init__("show python frame-filter",
+ gdb.COMMAND_DATA,
+ gdb.COMPLETE_COMMAND, True)
+class InfoFrameFilter(gdb.Command):
+ """GDB command to list all registered frame-filters.
+
+ Usage: info frame-filters
+ """
+ @staticmethod
+ def enabled_string(state):
+ """Return "Yes" if filter is enabled, otherwise "No"."""
+ if state:
+ return "Yes"
+ else:
+ return "No"
+
+ def __init__(self):
+ super(InfoFrameFilter, self).__init__("info frame-filter",
+ gdb.COMMAND_DATA)
+
+ 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 = frame_filters.items()
+ sorted_frame_filters.sort(key = _get_sort_priority,
+ reverse = True)
+
+ print " Priority Enabled Name"
+ print " ======== ======= ===="
+ if len(sorted_frame_filters) == 0:
+ print " No frame filters registered."
+ else:
+ for frame_filter in sorted_frame_filters:
+ name = frame_filter[0]
+ try:
+ priority = '{:<8}'.format(
+ str(_get_priority(frame_filter[1])))
+ enabled = '{:<7}'.format(
+ self.enabled_string(_get_enabled(frame_filter[1])))
+ except Exception as e:
+ print " Error printing filter '"+name+"': ",e
+ else:
+ print " %s %s %s" % (priority, enabled, name)
+
+ def print_list(self, title, filter_list):
+ """Print a list."""
+ if filter_list:
+ print title
+ self.list_frame_filters(filter_list)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ self.print_list("global frame-filters:", gdb.frame_filters)
+
+ cp = gdb.current_progspace()
+ self.print_list("progspace %s frame-filters:" % cp.filename,
+ cp.frame_filters)
+
+ for objfile in gdb.objfiles():
+ self.print_list("objfile %s frame-filters:" % objfile.filename,
+ objfile.frame_filters)
+
+# Internal enable/disable functions.
+
+def do_enable_frame_filter_1(frame_filters, name, flag):
+ """Internal worker for enabling/disabling frame_filters.
+
+ Arguments:
+ frame_filters: Dictionary that this frame filter is contained
+ within.
+ name: Name of the frame filter.
+ flag: True for Enable, False for Disable.
+
+ Raises:
+ gdb.GdbError: A frame filter cannot be found.
+ """
+
+ try:
+ ff = frame_filters[name]
+ except KeyError:
+ msg = "frame-filter '" + str(name) + "' not found."
+ raise gdb.GdbError(msg)
+
+ _set_enabled(ff, flag)
+
+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]
+ frame_filter = command_tuple[1]
+
+ op_list = _return_list(list_op)
+ do_enable_frame_filter_1(op_list, frame_filter, flag)
+
+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. 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(EnableFrameFilter, self).__init__("enable frame-filter",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ command_tuple = _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. 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(DisableFrameFilter, self).__init__("disable frame-filter",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ command_tuple = _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 python 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 new priority to set the frame filter.
+ """
+
+ def __init__(self):
+ super(SetFrameFilterPriority, self).__init__("set python " \
+ "frame-filter priority",
+ gdb.COMMAND_DATA,
+ gdb.COMPLETE_COMMAND)
+ 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:
+ raise gdb.GdbError("set python frame-filter priority " \
+ "takes exactly three arguments.")
+
+ object_list = argv[0]
+ name = argv[1]
+ priority = argv[2]
+ return(object_list, name, priority)
+
+ def _set_filter_priority_1(self, frame_filters, name, priority):
+ """Internal worker for setting priority of frame_filters.
+
+ Arguments:
+ frame_filters: The frame_filter dictionary.
+ name: The name of the filter.
+ priority: Priority of filter.
+
+ Raises:
+ gdb.GdbError: An error finding the frame filter.
+ """
+ try:
+ ff = frame_filters[name]
+ except KeyError:
+ msg = "frame-filter '" + str(name) + "' not found."
+ raise gdb.GdbError(msg)
+
+ _set_priority(ff, priority)
+
+ def _set_filter_priority(self,command_tuple):
+ """Internal worker for setting priority of frame-filters, by
+ parsing a tuple and calling _set_filter_priority_1 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 = _return_list(list_op)
+
+ self._set_filter_priority_1(op_list, frame_filter, priority)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+
+ command_tuple = self._parse_pri_arg(arg)
+ try:
+ self._set_filter_priority(command_tuple)
+ except gdb.GdbError as e:
+ # Print the error, instead of raising it.
+ gdb.write(e.message+"\n")
+
+class ShowFrameFilterPriority(gdb.Command):
+ """GDB command to show the priority of the specified frame-filter.
+
+ Usage: show python frame-filter priority list name
+
+ LIST is the name of the frame-filter list to operate. Named lists
+ are: "global" for the global frame-filter list, "progspace" for
+ the program space's file frame-filter list. If either of these
+ two are not specified, the list 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 python " \
+ "frame-filter priority",
+ gdb.COMMAND_DATA,
+ gdb.COMPLETE_COMMAND)
+ 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:
+ raise gdb.GdbError("show python frame-filter priority " \
+ "takes exactly two arguments.")
+
+ object_list = argv[0]
+ name = argv[1]
+ return (object_list, name)
+
+ 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 = _return_list(frame_filters)
+
+ try:
+ ff = op_list[name]
+ except KeyError:
+ msg = "frame-filter '" + str(name) + "' not found."
+ raise gdb.GdbError(msg)
+
+ return _get_priority(ff)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ try:
+ command_tuple = self._parse_pri_arg(arg)
+ except gdb.GdbError as e:
+ # Print the error instead of raising it.
+ gdb.write(e.message+"\n")
+ 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+"':",e
+ else:
+ print "Priority of filter '" + filter_name + "' in list '" \
+ + list_name + "' is: " + str(priority)
+
+def register_frame_filter_commands():
+ """Call from a top level script to install the frame-filter commands."""
+ InfoFrameFilter()
+ SetFilterPrefixCmd()
+ ShowFilterPrefixCmd()
+ EnableFrameFilter()
+ DisableFrameFilter()
+ SetFrameFilterPriority()
+ ShowFrameFilterPriority()
+
+register_frame_filter_commands()
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
new file mode 100644
index 0000000..ad0c44a
--- /dev/null
+++ b/gdb/python/py-framefilter.c
@@ -0,0 +1,1324 @@
+/* Python frame filters
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include "defs.h"
+#include "objfiles.h"
+#include "symtab.h"
+#include "language.h"
+#include "exceptions.h"
+#include "arch-utils.h"
+#include "python.h"
+#include "ui-out.h"
+#include "valprint.h"
+#include "annotate.h"
+#include "hashtab.h"
+#include "mi/mi-cmds.h"
+#include "demangle.h"
+#include "mi/mi-cmds.h"
+#include "python-internal.h"
+
+
+/* Helper function to extract a symbol, name and language definition
+ from a Python object that conforms to the "Symbol Value" interface.
+ OBJ is the Python object to extract the values from. **NAME is a
+ pass-through argument where the name of the symbol will be written.
+ **NAME is allocated in this function, but the caller is responsible
+ for clean up. **SYM is a pass-through argument where the symbol
+ will be written. In the case of the API returning a string, this
+ will be set to NULL. **LANGUAGE is also a pass-through argument
+ denoting the language attributed to the Symbol. In the case of
+ **SYM being NULL, this will be set to the current language.
+ Returns 0 on error with the appropriate Python exception set, and 1
+ on success. */
+
+static int
+extract_sym (PyObject *obj, char **name, struct symbol **sym,
+ const struct language_defn **language)
+{
+ if (PyObject_HasAttrString (obj, "symbol"))
+ {
+ PyObject *result = PyObject_CallMethod (obj, "symbol", NULL);
+
+ if (! result)
+ return 0;
+
+ /* For 'symbol' callback, the function can return a symbol or a
+ string. */
+ if (PyString_Check (result))
+ {
+ *name = python_string_to_host_string (result);
+ Py_DECREF (result);
+
+ if (! *name)
+ return 0;
+ *language = current_language;
+ *sym = NULL;
+ }
+ else
+ {
+ /* This type checks 'result' during the conversion so we
+ just call it unconditionally and check the return. */
+ *sym = symbol_object_to_symbol (result);
+
+ Py_DECREF (result);
+
+ if (! *sym)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Unexpected value. Expecting a " \
+ "gdb.Symbol or a Python string."));
+ return 0;
+ }
+
+ /* Duplicate the symbol name, so the caller has consistency
+ in garbage collection. */
+ *name = xstrdup (SYMBOL_PRINT_NAME (*sym));
+
+ if (language_mode == language_mode_auto)
+ *language = language_def (SYMBOL_LANGUAGE (*sym));
+ else
+ *language = current_language;
+ }
+ }
+ else
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Mandatory function 'symbol' not " \
+ "implemented."));
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Helper function to extract a value from an object that conforms to
+ the "Symbol Value" interface. OBJ is the Python object to extract
+ the value from. **VALUE is a pass-through argument where the value
+ will be written. If the object does not have the value attribute,
+ or provides the Python None for a value, **VALUE will be set to
+ NULL and this function will return as successful. Returns 0 on
+ error with the appropriate Python exception set, and 1 on
+ success. */
+
+static int
+extract_value (PyObject *obj, struct value **value)
+{
+ if (PyObject_HasAttrString (obj, "value"))
+ {
+ PyObject *vresult = PyObject_CallMethod (obj, "value", NULL);
+
+ if (! vresult)
+ return 0;
+
+ /* The Python code has returned 'None' for a value, so we set
+ value to NULL. This flags that GDB should read the
+ value. */
+ if (vresult == Py_None)
+ {
+ Py_DECREF (vresult);
+ *value = NULL;
+ return 1;
+ }
+ else
+ {
+ *value = convert_value_from_python (vresult);
+ Py_DECREF (vresult);
+
+ if (*value == NULL)
+ return 0;
+
+ return 1;
+ }
+ }
+ else
+ *value = NULL;
+
+ return 1;
+}
+
+static int
+mi_should_print (struct symbol *sym, const char *type)
+{
+ int print_me = 0;
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ default:
+ case LOC_UNDEF: /* catches errors */
+ case LOC_CONST: /* constant */
+ case LOC_TYPEDEF: /* local typedef */
+ case LOC_LABEL: /* local label */
+ case LOC_BLOCK: /* local function */
+ case LOC_CONST_BYTES: /* loc. byte seq. */
+ case LOC_UNRESOLVED: /* unresolved static */
+ case LOC_OPTIMIZED_OUT: /* optimized out */
+ print_me = 0;
+ break;
+
+ case LOC_ARG: /* argument */
+ case LOC_REF_ARG: /* reference arg */
+ case LOC_REGPARM_ADDR: /* indirect register arg */
+ case LOC_LOCAL: /* stack local */
+ case LOC_STATIC: /* static */
+ case LOC_REGISTER: /* register */
+ case LOC_COMPUTED: /* computed location */
+ if (strcmp (type, "all"))
+ print_me = 1;
+ else if (strcmp (type, "locals"))
+ print_me = !SYMBOL_IS_ARGUMENT (sym);
+ else
+ print_me = SYMBOL_IS_ARGUMENT (sym);
+ }
+ return print_me;
+}
+
+/* Helper function which outputs a type name to a "type" field in a
+ stream. OUT is the ui-out structure the type name will be output
+ too, and VAL is the value that the type will be extracted from.
+ Returns 0 on error, with any GDB exceptions converted to a Python
+ exception. */
+
+static int
+py_print_type (struct ui_out *out, struct value *val)
+{
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct type *type;
+ struct ui_file *stb;
+ struct cleanup *cleanup;
+
+ stb = mem_fileopen ();
+ cleanup = make_cleanup_ui_file_delete (stb);
+ type = check_typedef (value_type (val));
+ type_print (value_type (val), "", stb, -1);
+ ui_out_field_stream (out, "type", stb);
+ do_cleanups (cleanup);
+ }
+ if (except.reason > 0)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ except.message);
+ return 0;
+ }
+ return 1;
+}
+
+/* Helper function which outputs a value name to value field in a
+ stream. OUT is the ui-out structure the value will be output too,
+ VAL is the value that will be printed, OPTS contains the value
+ printing options, MI_PRINT_TYPE is the value delimiter for MI
+ output and LANGUAGE is the language_defn that the value will be
+ printed with. If the output is detected to be non-MI,
+ MI_PRINT_TYPE is ignored. Returns 0 on error, with any GDB
+ exceptions converted to a Python exception. */
+
+static int
+py_print_value (struct ui_out *out, struct value *val,
+ struct value_print_options opts,
+ int mi_print_type,
+ const struct language_defn *language)
+{
+ int should_print = 0;
+
+ /* MI disallows different value types against different options the
+ client passes, so test type against option. For CLI print all
+ values. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ struct type *type;
+
+ type = check_typedef (value_type (val));
+ if (mi_print_type == PRINT_ALL_VALUES)
+ should_print = 1;
+ else if (mi_print_type == PRINT_SIMPLE_VALUES
+ && TYPE_CODE (type) != TYPE_CODE_ARRAY
+ && TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ should_print = 1;
+ }
+ else
+ should_print = 1;
+
+ if (should_print)
+ {
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct ui_file *stb;
+ struct cleanup *cleanup;
+
+ stb = mem_fileopen ();
+ cleanup = make_cleanup_ui_file_delete (stb);
+ common_val_print (val, stb, 0, &opts, language);
+ ui_out_field_stream (out, "value", stb);
+ do_cleanups (cleanup);
+ }
+ if (except.reason > 0)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ except.message);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Helper function to call a Python method and extract an iterator
+ from the result, error checking for Python exception and returns
+ that are not iterators. FILTER is the Python object to call, and
+ FUNC is the name of the method. Returns a PyObject, or NULL on
+ error with the appropriate exception set. This function can return
+ an iterator, or None. */
+
+static PyObject *
+get_py_iter_from_func (PyObject *filter, char *func)
+{
+ if (PyObject_HasAttrString (filter, func))
+ {
+ PyObject *result = PyObject_CallMethod (filter, func, NULL);
+
+ if (result)
+ {
+ if (result != Py_None)
+ {
+ if (! PyIter_Check (result))
+ {
+ PyErr_Format (PyExc_RuntimeError,
+ _(" %s function must " \
+ "return an iterator."), func);
+ Py_DECREF (result);
+ return NULL;
+ }
+ else
+ {
+ PyObject *iterator = PyObject_GetIter (result);
+
+ Py_DECREF (result);
+
+ if (! iterator)
+ return NULL;
+ else
+ return iterator;
+ }
+ }
+ }
+ else
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/* Helper function to output a single frame argument and value to an
+ output stream. This function will account for entry values if the
+ FV parameter is populated, the frame argument has entry values
+ associated with them, and the appropriate "set entry-value"
+ options are set. Will output in CLI or MI like format depending
+ on the type of output stream detected. OUT is the output stream,
+ SYM_NAME is the name of the symbol. If SYM_NAME is populated then
+ it must have an accompanying value in the parameter FV. FA is a
+ frame argument structure. If FA is populated, both SYM_NAME and
+ FV are ignored. OPTS contains the value printing options,
+ MI_PRINT_TYPE is an enumerator to the value types that will be
+ printed if the output is MI. PRINT_MI_ARGS indicates whether to
+ output the ARGS="1" field in MI output. */
+static int
+py_print_single_arg (struct ui_out *out,
+ char *sym_name,
+ struct frame_arg *fa,
+ struct value *fv,
+ struct value_print_options opts,
+ int mi_print_type,
+ int print_mi_args_flag,
+ const char *cli_print_frame_args_type,
+ const struct language_defn *language)
+{
+ struct value *val;
+ struct cleanup *inner_cleanup =
+ make_cleanup (null_cleanup, NULL);
+
+ if (fa)
+ {
+ language = language_def (SYMBOL_LANGUAGE (fa->sym));
+ val = fa->val;
+ }
+ else
+ val = fv;
+
+ /* MI has varying rules for tuples, but generally if there is only
+ one element in each item in the list, do not start a tuple. */
+ if (print_mi_args_flag || mi_print_type != PRINT_NO_VALUES)
+ {
+ inner_cleanup =
+ make_cleanup_ui_out_tuple_begin_end (out,
+ NULL);
+ }
+
+ annotate_arg_begin ();
+
+ /* If frame argument is populated, check for entry-values and the
+ entry value options. */
+ if (fa)
+ {
+ struct ui_file *stb;
+
+ stb = mem_fileopen ();
+
+ fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym),
+ SYMBOL_LANGUAGE (fa->sym),
+ DMGL_PARAMS | DMGL_ANSI);
+ if (fa->entry_kind == print_entry_values_compact)
+ {
+ fputs_filtered ("=", stb);
+
+ fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym),
+ SYMBOL_LANGUAGE (fa->sym),
+ DMGL_PARAMS | DMGL_ANSI);
+ }
+ if (fa->entry_kind == print_entry_values_only
+ || fa->entry_kind == print_entry_values_compact)
+ {
+ fputs_filtered ("@entry", stb);
+ }
+ ui_out_field_stream (out, "name", stb);
+ ui_file_delete (stb);
+ }
+ else
+ /* Otherwise, just output the name. */
+ ui_out_field_string (out, "name", sym_name);
+
+ annotate_arg_name_end ();
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, "=");
+
+ if (print_mi_args_flag)
+ ui_out_field_int (out, "arg", 1);
+
+ /* For MI print the type. */
+ if (ui_out_is_mi_like_p (out)
+ && mi_print_type == PRINT_SIMPLE_VALUES)
+ {
+ if (! py_print_type (out, val))
+ goto error;
+ }
+
+ annotate_arg_value (value_type (val));
+
+ /* If the output is to the CLI, and the user option set print
+ frame-arguments is set to none, just output "...". */
+ if (! ui_out_is_mi_like_p (out)
+ && ! strcmp (cli_print_frame_args_type, "none"))
+
+ {
+ ui_out_field_string (out, "value", "...");
+ }
+ else
+ {
+ /* If CLI, and the first if condition above not true always
+ print values. For MI do not print values if the enumerator
+ is PRINT_NO_VALUES. */
+ if (! ui_out_is_mi_like_p (out)
+ || (ui_out_is_mi_like_p (out)
+ && mi_print_type != PRINT_NO_VALUES))
+ {
+ if (! py_print_value (out, val, opts, mi_print_type, language))
+ goto error;
+ }
+ }
+
+ do_cleanups (inner_cleanup);
+
+ return 1;
+
+ error:
+ do_cleanups (inner_cleanup);
+ return 0;
+}
+
+/* Helper function to loop over frame arguments provided by the
+ "frame_arguments" Python API. Elements in the iterator must
+ conform to the "Symbol Value" interface. ITER is the Python
+ iterator object, OUT is the output stream, MI_PRINT_TYPE is an
+ enumerator to the value types that will be printed if the output is
+ MI, PRINT_MI_ARGS indicates whether to output the ARGS="1" field in
+ MI output, CLI_PRINT_FRAME_ARGS_TYPE is an enumerator of the user
+ set option for frame argument printing output, and FRAME is the
+ backing frame. If (all) the frame argument values are provided via
+ the "value" API call, FRAME is not needed. */
+
+static int
+enumerate_args (PyObject *iter,
+ struct ui_out *out,
+ int mi_print_type,
+ int print_mi_args_flag,
+ const char *cli_print_frame_args_type,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+
+ get_user_print_options (&opts);
+
+ if (! ui_out_is_mi_like_p (out))
+ {
+ /* True in "summary" mode, false otherwise. */
+ opts.summary = !strcmp (cli_print_frame_args_type, "scalars");
+ }
+
+ opts.deref_ref = 1;
+
+
+ annotate_frame_args ();
+
+ /* Collect the first argument outside of the loop, so output of
+ commas in the argument output is correct. At the end of the
+ loop block collect another item from the iterator, and, if it is
+ not null emit a comma. */
+ item = PyIter_Next (iter);
+ if (! item && PyErr_Occurred ())
+ goto error;
+
+ while (item)
+ {
+ const struct language_defn *language;
+ char *sym_name;
+ struct symbol *sym;
+ struct value *val;
+ int success = 0;
+ volatile struct gdb_exception except;
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (! success)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (! success)
+ {
+ xfree (sym_name);
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ item = NULL;
+
+ if (sym && ui_out_is_mi_like_p (out) && ! mi_should_print (sym, "args"))
+ continue;
+
+ /* If the object did not provide a value, read it using
+ read_frame_args and account for entry values, if any. */
+ if (! val)
+ {
+ struct frame_arg arg, entryarg;
+
+ /* If there is no value, and also no symbol, set error and
+ exit. */
+ if (! sym)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("No symbol or value provided."));
+ xfree (sym_name);
+ goto error;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ read_frame_arg (sym, frame, &arg, &entryarg);
+ }
+ if (except.reason > 0)
+ {
+ xfree (sym_name);
+ PyErr_SetString (PyExc_RuntimeError,
+ except.message);
+ goto error;
+ }
+
+ /* The object has not provided a value, so this is a frame
+ argument to be read by GDB. In this case we have to
+ account for entry-values. */
+
+ if (arg.entry_kind != print_entry_values_only)
+ py_print_single_arg (out, NULL, &arg, NULL, opts,
+ mi_print_type,
+ print_mi_args_flag,
+ cli_print_frame_args_type, NULL);
+
+ if (entryarg.entry_kind != print_entry_values_no)
+ {
+ if (arg.entry_kind != print_entry_values_only)
+ {
+ ui_out_text (out, ", ");
+ ui_out_wrap_hint (out, " ");
+ }
+
+ py_print_single_arg (out, NULL, &entryarg, NULL, opts,
+ mi_print_type,
+ print_mi_args_flag,
+ cli_print_frame_args_type, NULL);
+ }
+
+ xfree (arg.error);
+ xfree (entryarg.error);
+ }
+ else
+ {
+ /* If the object has provided a value, we just print that. */
+ if (val)
+ py_print_single_arg (out, sym_name, NULL, val, opts,
+ mi_print_type,
+ print_mi_args_flag,
+ cli_print_frame_args_type,
+ language);
+ }
+
+ xfree (sym_name);
+
+ /* Collect the next item from the iterator. If
+ this is the last item, do not print the
+ comma. */
+ item = PyIter_Next (iter);
+ if (item)
+ ui_out_text (out, ", ");
+ else
+ if (PyErr_Occurred ())
+ goto error;
+
+ annotate_arg_end ();
+ }
+
+ return 1;
+
+ error:
+ return 0;
+}
+
+
+/* Helper function to loop over variables provided by the
+ "frame_locals" Python API. Elements in the iterator must conform
+ to the "Symbol Value" interface. ITER is the Python iterator
+ object, OUT is the output stream, MI_PRINT_TYPE is an enumerator to
+ the value types that will be printed if the output is MI,
+ PRINT_MI_ARGS_FLAG indicates whether to output the ARGS field in MI
+ output, and FRAME is the backing frame. If (all) of the variables
+ values are provided via the "value" API call, FRAME is not
+ needed. */
+
+static int
+enumerate_locals (PyObject *iter,
+ struct ui_out *out,
+ int mi_print_type,
+ int indent,
+ int print_mi_args_flag,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+
+ get_user_print_options (&opts);
+ opts.deref_ref = 1;
+
+ while ((item = PyIter_Next (iter)))
+ {
+ const struct language_defn *language;
+ char *sym_name;
+ struct value *val;
+ int success = 0;
+ struct symbol *sym;
+ volatile struct gdb_exception except;
+ struct cleanup *inner_cleanup =
+ make_cleanup (null_cleanup, NULL);
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (! success)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (! success)
+ {
+ xfree (sym_name);
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+
+ if (sym && ui_out_is_mi_like_p (out) && ! mi_should_print (sym, "locals"))
+ continue;
+
+ /* If the object did not provide a value, read it. */
+ if (! val)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ val = read_var_value (sym, frame);
+ }
+ if (except.reason > 0)
+ {
+ xfree (sym_name);
+ PyErr_SetString (PyExc_RuntimeError,
+ except.message);
+ goto error;
+ }
+ }
+
+ /* With PRINT_NO_VALUES, MI does not emit a tuple normally as
+ each output contains only one field. The exception is
+ -stack-list-variables, which always provides a tuple. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ if (print_mi_args_flag || mi_print_type != PRINT_NO_VALUES)
+ {
+ inner_cleanup =
+ make_cleanup_ui_out_tuple_begin_end (out,
+ NULL);
+ }
+ }
+ else
+ /* If the output is not MI we indent locals. */
+ ui_out_spaces (out, (8 + (indent * 2)));
+
+ ui_out_field_string (out, "name", sym_name);
+ xfree (sym_name);
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " = ");
+
+ if (ui_out_is_mi_like_p (out)
+ && mi_print_type == PRINT_SIMPLE_VALUES)
+ {
+ if (! py_print_type (out, val))
+ goto error;
+ }
+
+ if (! ui_out_is_mi_like_p (out)
+ || (ui_out_is_mi_like_p (out)
+ && mi_print_type != PRINT_NO_VALUES))
+ {
+ py_print_value (out, val, opts, mi_print_type, language);
+ }
+
+ ui_out_text (out, "\n");
+ do_cleanups (inner_cleanup);
+ }
+
+ if (! item && PyErr_Occurred())
+ goto error;
+
+ done:
+ return 1;
+
+ error:
+ return 0;
+}
+
+/* Helper function for -stack-list-variables. */
+
+static int
+py_mi_print_variables (PyObject *filter, struct ui_out *out,
+ int mi_print_type,
+ const char *cli_print_frame_args_type,
+ struct frame_info *frame)
+{
+ struct cleanup *old_chain;
+ PyObject *args_iter;
+ PyObject *locals_iter;
+
+ args_iter = get_py_iter_from_func (filter, "frame_args");
+ old_chain = make_cleanup_py_xdecref (args_iter);
+ if (! args_iter)
+ goto error;
+
+ locals_iter = get_py_iter_from_func (filter, "frame_locals");
+ if (! locals_iter)
+ goto error;
+
+ make_cleanup_py_decref (locals_iter);
+ make_cleanup_ui_out_list_begin_end (out, "variables");
+
+ if (args_iter != Py_None)
+ if (! enumerate_args (args_iter, out, mi_print_type,
+ 1, cli_print_frame_args_type, frame))
+ goto error;
+
+ if (locals_iter != Py_None)
+ if (! enumerate_locals (locals_iter, out, mi_print_type, 1, 1,
+ frame))
+
+ goto error;
+
+ do_cleanups (old_chain);
+ return 1;
+
+ error:
+ do_cleanups (old_chain);
+ return 0;
+}
+
+/* Helper function for printing locals. This function largely just
+ creates the wrapping tuple, and calls enumerate_locals. */
+
+static int
+py_print_locals (PyObject *filter,
+ struct ui_out *out,
+ int mi_print_type,
+ int indent,
+ struct frame_info *frame)
+{
+ PyObject *locals_iter = get_py_iter_from_func (filter,
+ "frame_locals");
+ struct cleanup *old_chain = make_cleanup_py_xdecref (locals_iter);
+
+ if (! locals_iter)
+ goto locals_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "locals");
+ if (locals_iter != Py_None)
+ if (! enumerate_locals (locals_iter, out, mi_print_type,
+ indent, 0, frame))
+ goto locals_error;
+
+ do_cleanups (old_chain);
+ return 1;
+
+ locals_error:
+ do_cleanups (old_chain);
+ return 0;
+}
+
+/* Helper function for printing frame arguments. This function
+ largely just creates the wrapping tuple, and calls
+ enumerate_args. */
+
+static int
+py_print_args (PyObject *filter,
+ struct ui_out *out,
+ int mi_print_type,
+ const char *cli_print_frame_args_type,
+ struct frame_info *frame)
+{
+ PyObject *args_iter = get_py_iter_from_func (filter, "frame_args");
+ struct cleanup *old_chain = make_cleanup_py_xdecref (args_iter);
+
+ if (! args_iter)
+ goto args_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "args");
+ annotate_frame_args ();
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " (");
+
+ if (args_iter != Py_None)
+ if (! enumerate_args (args_iter, out, mi_print_type,
+ 0, cli_print_frame_args_type,
+ frame))
+ goto args_error;
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, ")");
+
+ do_cleanups (old_chain);
+ return 1;
+
+ args_error:
+ do_cleanups (old_chain);
+ return 0;
+}
+
+/* Hash function for the printed frame hash. */
+
+static hashval_t
+hash_printed_frame_entry (const void *data)
+{
+ const struct frame_info *frame = data;
+
+ return htab_hash_pointer (frame);
+}
+
+/* Equality function for the printed hash. */
+
+static int
+eq_printed_frame_entry (const void *a, const void *b)
+{
+ const struct frame_info *ea = a;
+ const struct frame_info *eb = b;
+
+ return ea == eb;
+}
+
+
+/* Print a single frame to the designated output stream, detecting
+ whether the output is MI or console, and formatting the output
+ according to the conventions of that protocol. FILTER is the
+ frame-filter associated with this frame. PRINT_LEVEL is a flag
+ and indicates whether to print the frame level. PRINT_FRAME_INFO
+ is a flag that indicates to print the frame information
+ (everything other than arguments or locals). PRINT_ARGS is a flag
+ that indicates whether to print frame arguments, and PRINT_LOCALS
+ is a flag that indicates whether to print frame local variables.
+ MI_PRINT_TYPE is an enumerator to the value types that will be
+ printed if the output is MI. CLI_PRINT_FRAMES_ARGS_TYPE is an
+ enumerator indicating what type of frame arguments to print. OUT
+ is the output stream to print too, INDENT is the level of
+ indention for this frame (in the case of elided frames), and
+ LEVELS_PRINTED is a hash-table containing all the frames level
+ that have already been printed. If a frame level has been
+ printed, do not print it again (in the case of elided frames). */
+
+static int
+py_print_frame (PyObject *filter, int print_level, int print_frame_info,
+ int print_args, int print_locals, int mi_print_type,
+ const char *cli_print_frame_args_type,
+ struct ui_out *out, int indent, htab_t levels_printed)
+{
+ int has_addr = 0;
+ CORE_ADDR address = 0;
+ struct gdbarch *gdbarch = NULL;
+ struct frame_info *frame = NULL;
+ struct cleanup *cleanup_stack = make_cleanup (null_cleanup, NULL);
+ struct value_print_options opts;
+ PyObject *elided;
+
+ get_user_print_options (&opts);
+
+ /* Get the underlying frame. This is needed to determine GDB
+ architecture, and also, in the cases of frame variables/arguments to
+ read them if they returned filter object requires us to do so. */
+ if (PyObject_HasAttrString (filter, "inferior_frame"))
+ {
+ PyObject *result = PyObject_CallMethod (filter, "inferior_frame", NULL);
+
+ if (! result)
+ goto error;
+ frame = frame_object_to_frame_info (result);
+ if (! frame)
+ {
+ Py_DECREF (result);
+ goto error;
+ }
+
+ gdbarch = get_frame_arch (frame);
+ Py_DECREF (result);
+ }
+ else
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("'inferior_frame' API must be implemented."));
+ goto error;
+ }
+
+ /* stack-list-variables. */
+ if (print_locals && print_args && ! print_frame_info)
+ {
+ if (! py_mi_print_variables (filter, out,
+ mi_print_type,
+ cli_print_frame_args_type,
+ frame))
+ goto error;
+ else
+ return PY_BT_COMPLETED;
+ }
+
+ /* -stack-list-locals does not require a
+ wrapping frame attribute. */
+ if (print_frame_info || (print_args && ! print_locals))
+ make_cleanup_ui_out_tuple_begin_end (out, "frame");
+
+ if (print_frame_info)
+ {
+ /* Elided frames are also printed with this function (recursively)
+ and are printed with indention. */
+ if (indent > 0)
+ ui_out_spaces (out, indent);
+
+ /* The address is required for frame annotations, and also for
+ address printing. */
+ if (PyObject_HasAttrString (filter, "address"))
+ {
+ PyObject *paddr = PyObject_CallMethod (filter, "address", NULL);
+ if (paddr)
+ {
+ if (paddr != Py_None)
+ {
+ address = PyLong_AsLong (paddr);
+ has_addr = 1;
+ }
+ Py_DECREF (paddr);
+ }
+ else
+ goto error;
+ }
+
+ }
+
+ /* Print frame level. MI does not require the level if
+ locals/variables only are being printed. */
+ if ((print_frame_info || print_args) && print_level)
+ {
+ struct frame_info **slot;
+ int level;
+
+ slot = (struct frame_info **) htab_find_slot (levels_printed,
+ frame, INSERT);
+ level = frame_relative_level (frame);
+
+ /* Check if this frame has already been printed (there are cases
+ where elided synthetic dummy-frames have to 'borrow' the frame
+ architecture from the eliding frame. If that is the case, do
+ not print 'level', but print spaces. */
+ if (*slot != NULL && (*slot) == frame)
+ {
+ char buffer[10];
+ sprintf (buffer, "%d", level);
+ ui_out_spaces (out, strlen (buffer) + 2);
+ }
+ else
+ {
+ *slot = frame;
+ annotate_frame_begin (print_level ? level : 0,
+ gdbarch, address);
+ ui_out_text (out, "#");
+ ui_out_field_fmt_int (out, 2, ui_left, "level",
+ level);
+ }
+ }
+
+ if (print_frame_info)
+ {
+ /* Print address to the address field. If an address is not provided,
+ print nothing. */
+ if (opts.addressprint && has_addr)
+ {
+ annotate_frame_address ();
+ ui_out_field_core_addr (out, "addr", gdbarch, address);
+ annotate_frame_address_end ();
+ ui_out_text (out, " in ");
+ }
+
+ /* Print frame function. */
+ if (PyObject_HasAttrString (filter, "function"))
+ {
+ PyObject *result = PyObject_CallMethod (filter, "function", NULL);
+
+ if (result)
+ {
+ if (result != Py_None)
+ {
+ char *func = NULL;
+ char *dup = PyString_AsString (result);
+
+ if (! dup)
+ {
+ Py_DECREF (result);
+ goto error;
+ }
+
+ func = xstrdup (dup);
+ annotate_frame_function_name ();
+ ui_out_field_string (out, "func", func);
+ xfree (func);
+
+ }
+ Py_DECREF (result);
+ }
+ else
+ goto error;
+ }
+ }
+
+
+ /* Frame arguments. Check the result, and error if something went
+ wrong. */
+ if (print_args)
+ {
+ if (! py_print_args (filter, out, mi_print_type,
+ cli_print_frame_args_type, frame))
+ goto error;
+ }
+
+ /* File name/source/line number information. */
+ if (print_frame_info)
+ {
+ annotate_frame_source_begin ();
+
+ if (PyObject_HasAttrString (filter, "filename"))
+ {
+ PyObject *result = PyObject_CallMethod (filter, "filename",
+ NULL);
+ if (result)
+ {
+ if (result != Py_None)
+ {
+ char *filename = NULL;
+ char *dup = PyString_AsString (result);
+
+ if (! dup)
+ {
+ Py_DECREF (result);
+ goto error;
+ }
+
+ filename = xstrdup (dup);
+ ui_out_wrap_hint (out, " ");
+ ui_out_text (out, " at ");
+ annotate_frame_source_file ();
+ ui_out_field_string (out, "file", filename);
+ annotate_frame_source_file_end ();
+ xfree (filename);
+ }
+ Py_DECREF (result);
+ }
+ else
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "line"))
+ {
+ PyObject *result = PyObject_CallMethod (filter, "line", NULL);
+ int line;
+
+ if (result)
+ {
+ if (result != Py_None)
+ {
+ line = PyLong_AsLong (result);
+ ui_out_text (out, ":");
+ annotate_frame_source_line ();
+ ui_out_field_int (out, "line", line);
+ }
+ Py_DECREF (result);
+ }
+ else
+ goto error;
+ }
+ }
+
+ /* For MI we need to deal with the "children" list population of
+ elided frames, so if MI output detected do not send newline. */
+ if (! ui_out_is_mi_like_p (out))
+ {
+ annotate_frame_end ();
+ ui_out_text (out, "\n");
+ }
+
+ if (print_locals)
+ {
+ if (! py_print_locals (filter, out, mi_print_type, indent,
+ frame))
+ goto error;
+ }
+
+ /* Finally recursively print elided frames, if any. */
+ elided = get_py_iter_from_func (filter, "elided");
+ if (! elided)
+ goto error;
+
+ if (elided != Py_None)
+ {
+ PyObject *item;
+
+ make_cleanup_py_decref (elided);
+ make_cleanup_ui_out_list_begin_end (out, "children");
+
+ if (! ui_out_is_mi_like_p (out))
+ indent = indent + 4;
+
+ while ((item = PyIter_Next (elided)))
+ {
+ int success = py_print_frame (item, print_level,
+ print_frame_info,
+ print_args,
+ print_locals,
+ mi_print_type,
+ cli_print_frame_args_type,
+ out, indent,
+ levels_printed);
+
+ if (success == 0 && PyErr_Occurred ())
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ }
+ }
+
+ /* In MI now we can signal the end. */
+ if (ui_out_is_mi_like_p (out))
+ ui_out_text (out, "\n");
+
+ do_cleanups (cleanup_stack);
+ return PY_BT_COMPLETED;
+
+ error:
+ do_cleanups (cleanup_stack);
+ return PY_BT_ERROR;
+}
+
+/* Helper function to initiate frame filter invocation at starting
+ frame FRAME. */
+static PyObject *
+bootstrap_python_frame_filters (struct frame_info *frame)
+{
+
+ PyObject *module, *sort_func, *iterable, *frame_obj;
+
+ frame_obj = frame_info_to_frame_object (frame);
+ if (! frame_obj)
+ return NULL;
+
+ module = PyImport_ImportModule ("gdb.command.frame_filters");
+ if (! module)
+ {
+ Py_DECREF (frame_obj);
+ return NULL;
+ }
+
+ sort_func = PyObject_GetAttrString (module, "invoke");
+ if (! sort_func)
+ {
+ Py_DECREF (frame_obj);
+ Py_DECREF (module);
+ return NULL;
+ }
+
+ iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj, NULL);
+
+ Py_DECREF (module);
+ Py_DECREF (sort_func);
+ Py_DECREF (frame_obj);
+
+ if (! iterable)
+ return NULL;
+
+ return iterable;
+}
+
+/* Public and dispatch function for frame filters. This is the only
+ publicly exported function in this file. FRAME is the source
+ frame to start frame-filter invocation. FLAGS is an integer
+ holding the flags for printing. The following elements of the
+ FRAME_FILTER_FLAGS enum denotes makeup of FLAGS: PRINT_LEVEL is a
+ flag indicating whether to print the frame's relative level in the
+ output. PRINT_FRAME_INFO is a flag that indicates whether this
+ function should print the frame information, PRINT_ARGS is a flag
+ that indicates whether to print frame arguments, and PRINT_LOCALS,
+ likewise, with frame local variables. MI_PRINT_ARGS_TYPE is an
+ enum from MI that indicates which values types to print. This
+ parameter is ignored if the output is detected to be CLI.
+ CLI_PRINT_FRAME_ARGS_TYPE likewise is a an element of what value
+ types to print from CLI. OUT is the output stream to print, and
+ COUNT is a delimiter (required for MI slices). */
+
+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum print_values mi_print_args_type,
+ const char *cli_print_frame_args_type,
+ struct ui_out *out, int count)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct cleanup *cleanups;
+ int result = 0;
+ int print_result = 0;
+ int success = 0;
+ PyObject *iterable;
+ int print_level, print_frame_info, print_args, print_locals;
+
+ /* Extract print settings from FLAGS. */
+ print_level = (flags & PRINT_LEVEL) ? 1 : 0;
+ print_frame_info = (flags & PRINT_FRAME_INFO) ? 1 : 0;
+ print_args = (flags & PRINT_ARGS) ? 1 : 0;
+ print_locals = (flags & PRINT_LOCALS) ? 1 : 0;
+
+ cleanups = ensure_python_env (gdbarch, current_language);
+
+ iterable = bootstrap_python_frame_filters (frame);
+
+ if (!iterable)
+ goto done;
+
+ /* If iterable is None, then there are not any frame filters
+ registered. If this is the case, defer to default GDB printing
+ routines in MI and CLI. */
+
+ make_cleanup_py_decref (iterable);
+ if (iterable == Py_None)
+ {
+ do_cleanups (cleanups);
+ return PY_BT_NO_FILTERS;
+ }
+
+ /* Is it an iterator */
+ if PyIter_Check (iterable)
+ {
+ PyObject *iterator = PyObject_GetIter (iterable);
+ PyObject *item;
+ htab_t levels_printed;
+
+ if (! iterator)
+ goto done;
+
+ make_cleanup_py_decref (iterator);
+ levels_printed = htab_create (20,
+ hash_printed_frame_entry,
+ eq_printed_frame_entry,
+ NULL);
+
+ while ((item = PyIter_Next (iterator)) && count--)
+ {
+ success = py_print_frame (item, print_level,
+ print_frame_info, print_args,
+ print_locals,
+ mi_print_args_type,
+ cli_print_frame_args_type,
+ out, 0, levels_printed);
+
+ /* Do not exit on error printing the frame, continue with
+ other frames. */
+ if (success == PY_BT_ERROR && PyErr_Occurred ())
+ gdbpy_print_stack ();
+
+ Py_DECREF (item);
+ }
+
+ htab_delete (levels_printed);
+ }
+ else
+ {
+ Py_DECREF (iterable);
+ error (_("Frame filter must support iteration protocol."));
+ }
+
+ done:
+ if (PyErr_Occurred ())
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return success;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 5d2398f..228b71b 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -33,6 +33,8 @@ typedef struct
/* The pretty-printer list of functions. */
PyObject *printers;

+ /* The frame filter list of functions. */
+ PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
} objfile_object;
@@ -61,6 +63,7 @@ objfpy_dealloc (PyObject *o)
objfile_object *self = (objfile_object *) o;

Py_XDECREF (self->printers);
+ Py_XDECREF (self->frame_filters);
Py_XDECREF (self->type_printers);
self->ob_type->tp_free ((PyObject *) self);
}
@@ -81,9 +84,19 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}

+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self->printers);
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
+ Py_DECREF (self->printers);
+ Py_DECREF (self->frame_filters);
Py_DECREF (self);
return NULL;
}
@@ -129,6 +142,44 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}

+PyObject *
+objfpy_get_frame_filters (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+static int
+objfpy_set_frame_filters (PyObject *o, PyObject *filters, void *ignore)
+{
+ PyObject *tmp;
+ objfile_object *self = (objfile_object *) o;
+
+ if (! filters)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("Cannot delete the frame filters attribute."));
+ return -1;
+ }
+
+ if (! PyDict_Check (filters))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("The frame_filters attribute must be a dictionary."));
+ return -1;
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+ tmp = self->frame_filters;
+ Py_INCREF (filters);
+ self->frame_filters = filters;
+ Py_XDECREF (tmp);
+
+ return 0;
+}
+
/* Get the 'type_printers' attribute. */

static PyObject *
@@ -225,9 +276,19 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}

+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object->printers);
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
+ Py_DECREF (object->printers);
+ Py_DECREF (object->frame_filters);
Py_DECREF (object);
return NULL;
}
@@ -270,6 +331,8 @@ static PyGetSetDef objfile_getset[] =
"The objfile's filename, or None.", NULL },
{ "pretty_printers", objfpy_get_printers, objfpy_set_printers,
"Pretty printers.", NULL },
+ { "frame_filters", objfpy_get_frame_filters,
+ objfpy_set_frame_filters, "Frame Filters.", NULL },
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
"Type printers.", NULL },
{ NULL }
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index c1b1cac..9a26f39 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -35,6 +35,8 @@ typedef struct
/* The pretty-printer list of functions. */
PyObject *printers;

+ /* The frame filter list of functions. */
+ PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
} pspace_object;
@@ -69,6 +71,7 @@ pspy_dealloc (PyObject *self)
pspace_object *ps_self = (pspace_object *) self;

Py_XDECREF (ps_self->printers);
+ Py_XDECREF (ps_self->frame_filters);
Py_XDECREF (ps_self->type_printers);
self->ob_type->tp_free (self);
}
@@ -89,9 +92,19 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}

+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self->printers);
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
+ Py_DECREF (self->printers);
+ Py_DECREF (self->frame_filters);
Py_DECREF (self);
return NULL;
}
@@ -137,6 +150,44 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}

+PyObject *
+pspy_get_frame_filters (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+static int
+pspy_set_frame_filters (PyObject *o, PyObject *frame, void *ignore)
+{
+ PyObject *tmp;
+ pspace_object *self = (pspace_object *) o;
+
+ if (! frame)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "cannot delete the frame filter attribute");
+ return -1;
+ }
+
+ if (! PyDict_Check (frame))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "the frame filter attribute must be a dictionary");
+ return -1;
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+ tmp = self->frame_filters;
+ Py_INCREF (frame);
+ self->frame_filters = frame;
+ Py_XDECREF (tmp);
+
+ return 0;
+}
+
/* Get the 'type_printers' attribute. */

static PyObject *
@@ -221,13 +272,24 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}

+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object->printers);
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
+ Py_DECREF (object->printers);
+ Py_DECREF (object->frame_filters);
Py_DECREF (object);
return NULL;
}

+
set_program_space_data (pspace, pspy_pspace_data_key, object);
}
}
@@ -257,6 +319,8 @@ static PyGetSetDef pspace_getset[] =
"The progspace's main filename, or None.", NULL },
{ "pretty_printers", pspy_get_printers, pspy_set_printers,
"Pretty printers.", NULL },
+ { "frame_filters", pspy_get_frame_filters, pspy_set_frame_filters,
+ "Frame filters.", NULL },
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
"Type printers.", NULL },
{ NULL }
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index d5d0e431..f8c2e26 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -48,6 +48,33 @@ make_cleanup_py_decref (PyObject *py)
return make_cleanup (py_decref, (void *) py);
}

+/* This is a cleanup function which decrements the refcount on a
+ Python object. This function accounts appropriately for NULL
+ references. */
+
+static void
+py_xdecref (void *p)
+{
+ PyObject *py = p;
+
+ /* Note that we need the extra braces in this 'if' to avoid a
+ warning from gcc. */
+ if (py)
+ {
+ Py_XDECREF (py);
+ }
+}
+
+/* Return a new cleanup which will decrement the Python object's
+ refcount when run. Account for and operate on NULL references
+ correctly. */
+
+struct cleanup *
+make_cleanup_py_xdecref (PyObject *py)
+{
+ return make_cleanup (py_xdecref, (void *) py);
+}
+
/* Converts a Python 8-bit string to a unicode string object. Assumes the
8-bit string is in the host charset. If an error occurs during conversion,
returns NULL with a python exception set.
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index ee04860..0ebb661 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -215,9 +215,11 @@ PyObject *frame_info_to_frame_object (struct frame_info *frame);

PyObject *pspace_to_pspace_object (struct program_space *);
PyObject *pspy_get_printers (PyObject *, void *);
+PyObject *pspy_get_frame_filters (PyObject *, void *);

PyObject *objfile_to_objfile_object (struct objfile *);
PyObject *objfpy_get_printers (PyObject *, void *);
+PyObject *objfpy_get_frame_filters (PyObject *, void *);

thread_object *create_thread_object (struct thread_info *tp);
thread_object *find_thread_object (ptid_t ptid);
@@ -264,6 +266,7 @@ void gdbpy_initialize_thread_event (void);
void gdbpy_initialize_new_objfile_event (void);

struct cleanup *make_cleanup_py_decref (PyObject *py);
+struct cleanup *make_cleanup_py_xdecref (PyObject *py);

struct cleanup *ensure_python_env (struct gdbarch *gdbarch,
const struct language_defn *language);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 5f6df60..e7693bd 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1374,6 +1374,14 @@ free_type_printers (void *arg)
{
}

+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum print_values mi_print_args_type,
+ const char *cli_print_frame_args_type,
+ struct ui_out *out, int count)
+{
+ return PY_BT_NO_FILTERS;
+}
+
#endif /* HAVE_PYTHON */

\f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 72872b0..e90f78c 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -21,6 +21,7 @@
#define GDB_PYTHON_H

#include "value.h"
+#include "mi/mi-cmds.h"

struct breakpoint_object;

@@ -28,6 +29,28 @@ struct breakpoint_object;
E.g. When the program loads libfoo.so, look for libfoo-gdb.py. */
#define GDBPY_AUTO_FILE_NAME "-gdb.py"

+/* Python frame-filter status returns constants. */
+static const int PY_BT_ERROR = 0;
+static const int PY_BT_COMPLETED = 1;
+static const int PY_BT_NO_FILTERS = 2;
+
+/* Flags to pass to apply_frame_filter. */
+
+enum frame_filter_flags
+ {
+ /* Set this flag if frame level is to be printed. */
+ PRINT_LEVEL = 1,
+
+ /* Set this flag if frame information is to be printed. */
+ PRINT_FRAME_INFO = 2,
+
+ /* Set this flag if frame arguments are to be printed. */
+ PRINT_ARGS = 4,
+
+ /* Set this flag if frame locals are to be printed. */
+ PRINT_LOCALS = 8,
+ };
+
extern void finish_python_initialization (void);

void eval_python_from_control_command (struct command_line *);
@@ -41,6 +64,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
const struct value_print_options *options,
const struct language_defn *language);

+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum print_values mi_print_args_type,
+ const char *cli_print_args_type,
+ struct ui_out *out, int count);
+
void preserve_python_values (struct objfile *objfile, htab_t copied_types);

void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);