* [patch][python] 1 of 5 - Frame filter Python C code changes.
@ 2012-11-30 14:31 Phil Muldoon
2012-12-05 17:03 ` Tom Tromey
0 siblings, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2012-11-30 14:31 UTC (permalink / raw)
To: gdb-patches
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);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2012-11-30 14:31 [patch][python] 1 of 5 - Frame filter Python C code changes Phil Muldoon
@ 2012-12-05 17:03 ` Tom Tromey
2012-12-07 13:41 ` Phil Muldoon
0 siblings, 1 reply; 16+ messages in thread
From: Tom Tromey @ 2012-12-05 17:03 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Hi. Lots of notes on this one.
Phil> diff --git a/gdb/python/lib/gdb/BaseFrameWrapper.py b/gdb/python/lib/gdb/BaseFrameWrapper.py
Phil> + If the result of frame filters running means we have one gdb.Frame
Phil> + wrapped by multiple frame wrappers, all sub-classed from
Phil> + BaseFrameWrapper:
Phil> +
Phil> + Wrapper1(Wrapper2(BaseFrameWrapper(gdb.Frame)))
I think this notation is a bit weird.
Phil> + @staticmethod
Phil> + def is_limited_frame(frame):
Probably should start with an underscore.
Phil> + def __init__(self, base):
Phil> + super(BaseFrameWrapper, self).__init__(base)
Phil> + self.base = base
Perhaps a leading underscore for 'base'.
However -- the superclass sets 'self.frame'. If that is considered API,
then you don't need 'base' at all. And if it isn't considered API, it
should be renamed.
Phil> +class BaseSymValueWrapper():
Probably should be BaseSymValueWrapper(object) for Python 3.
Same for FrameVars.
Phil> + """A container class conforming to the Symbol/Value interface
Phil> + which holds frame locals or frame arguments."""
Phil> + def __init__(self, symbol, value):
Phil> + self.sym = symbol
Phil> + self.val = value
As far as I can tell this is always instantiated with value == None.
That seems wrong.
Phil> + def fetch_frame_locals(self):
Phil> + """Public utility method to fetch frame local variables for
Phil> + the stored frame. Frame arguments are not fetched. If there
Phil> + are not frame local variables, return None."""
Typo: should be "there are no".
[ fetch_frame_locals ]
Phil> + if len(lvars) == 0:
Phil> + return None
It is curious that this is needed.
Will whatever code eventually handles display of this do something
different for an empty list?
Phil> + def fetch_frame_args(self):
Phil> + """Public utility method to fetch frame argument for the
Phil> + stored frame. Frame arguments are the only type fetched. If
Phil> + there are no frame arguments variables, return None."""
I think "frame argument" - not plural.
Phil> + def get_value(self, sym, block):
Phil> + """Public utility method to fetch a value from a symbol."""
What calls this?
Phil> +class FrameIterator(object):
[...]
Phil> + def next (self):
Phil> + """__next__ implementation.
I think just "next", not "__next__".
Phil> + self.frame = result.older ()
No space before parens in Python.
It is hard to remember.
Phil> +class FrameWrapper (object):
Phil> + """Interface for a Frame Wrapper."""
If this is just for documentation, how about we just put it all in the
docs and delete the class?
Then we can rename "BaseFrameWrapper". I find the BaseFrameWrapper /
FrameWrapper distinction a bit confusing.
Narrowly speaking, this class is misnamed anyhow, since it doesn't
actually wrap anything.
Phil> +def _parse_arg(cmd_name, arg):
Phil> + """ Internal worker function to take an argument and return a
Phil> + tuple of arguments.
...
Phil> + object_list = argv[0]
Phil> + argument = argv[1]
Phil> +
Phil> + return(object_list, argument)
It seems like you can just return argv here.
Phil> +def _get_priority(filter_item):
Phil> + """ Internal worker function to return the frame-filter's priority.
It seems strange to have both _get_sort_priority and _get_priority.
Similarly elsewhere.
Phil> +def _set_priority(filter_item, priority):
Phil> + """ Internal worker function to set the frame-filter's priority.
Phil> +
Phil> + Arguments:
Phil> + filter_item: An object conforming to the frame filter
Phil> + interface.
Phil> + priority: The priority to assign as an integer.
Phil> +
Phil> + Raises:
Phil> + gdb.GdbError: When the priority attribute has not been
Phil> + implemented.
Phil> + """
Phil> +
Phil> + if hasattr(filter_item, "priority"):
Phil> + filter_item.priority = priority
Phil> + else:
Phil> + raise gdb.GdbError("Cannot find class attribute 'priority'")
Why check for priority before setting it? It seems simpler to just set
it. Similarly elsewhere.
Phil> + if hasattr(filter_item, "enabled"):
Phil> + return filter_item.enabled
Phil> + else:
Phil> + raise gdb.GdbError("Cannot find class attribute 'enabled'")
What is wrong with just fetching it and letting Python throw the
exception?
Phil> +def _return_list(name):
Phil> + """ Internal Worker function to return the frame filter
Phil> + dictionary, depending on the name supplied as an argument. If the
Phil> + name is not "global" or "progspace", it is assumed to name an
Phil> + object-file.
Phil> +
Phil> + Arguments:
Phil> + name: The name of the list, as specified by GDB user commands.
Phil> +
Phil> + Returns:
Phil> + A dictionary object.
Phil> +
Phil> + Raises:
Phil> + gdb.GdbError: A dictionary of that name cannot be found.
Phil> + """
Phil> +
Phil> + if name == "global":
Extra space.
Phil> + sorted_frame_filters = copy.copy(all_filters)
Phil> + sorted_frame_filters.sort(key = _get_sort_priority,
Phil> + reverse = True)
I think for Python 3 we recently switched to using 'sorted' instead of
the in-place sort.
Hmm, that patch doesn't seem to have gone in.
Phil> + for ff in sorted_list:
Phil> + frame_iterator = ff[1].filter(frame_iterator)
I don't understand the "[1]" here.
If the 'invoke' function is meant to be used from outside the new
commands, it should probably not be in gdb.command.
Phil> +# GDB Commands.
Phil> +class SetFilterPrefixCmd(gdb.Command):
I think this probably needs some help text.
Phil> +class ShowFilterPrefixCmd(gdb.Command):
Likewise.
Phil> +class InfoFrameFilter(gdb.Command):
Phil> + """GDB command to list all registered frame-filters.
Starting the text with "GDB command" is redundant.
Phil> + def __init__(self):
This should be the first method.
Phil> + def print_list(self, title, filter_list):
Phil> + """Print a list."""
Just remove this text.
It isn't useful.
Phil> + def invoke(self, arg, from_tty):
Phil> + """GDB calls this to perform the command."""
Likewise.
Phil> +# Internal enable/disable functions.
Phil> +
Phil> +def do_enable_frame_filter_1(frame_filters, name, flag):
This just has a single caller.
I don't think it needs to be separate.
Phil> + DICTIONARY is the name of the frame filter dictionary on which to
Phil> + operate. Named dictionaries are: "global" for the global frame
Phil> + filter dictionary, "progspace" for the program space's framefilter
Phil> + dictionary. If either of these two are not specified, the
Phil> + dictionary name is assumed to be the name of the object-file name.
It would be nice if these commands had completion methods that did the
right thing.
Phil> + object_list = argv[0]
Phil> + name = argv[1]
Phil> + priority = argv[2]
Phil> + return(object_list, name, priority)
I think you can just return argv here.
Phil> + def _set_filter_priority_1(self, frame_filters, name, priority):
I don't think this function is needed.
Phil> + object_list = argv[0]
Phil> + name = argv[1]
Phil> + return (object_list, name)
Just return argv.
I probably missed some instances of this pattern.
Phil> +def register_frame_filter_commands():
Phil> + """Call from a top level script to install the frame-filter commands."""
Phil> + InfoFrameFilter()
Phil> + SetFilterPrefixCmd()
Phil> + ShowFilterPrefixCmd()
Phil> + EnableFrameFilter()
Phil> + DisableFrameFilter()
Phil> + SetFrameFilterPriority()
Phil> + ShowFrameFilterPriority()
Phil> +
Phil> +register_frame_filter_commands()
No need to create a function just to call it once.
Phil> + OBJ is the Python object to extract the values from. **NAME is a
s/**NAME/NAME
Similarly elsewhere.
Phil> + pass-through argument where the name of the symbol will be written.
Phil> + **NAME is allocated in this function, but the caller is responsible
You probably mean *NAME here.
**NAME is a single character.
Phil> +static int
Phil> +extract_sym (PyObject *obj, char **name, struct symbol **sym,
Phil> + const struct language_defn **language)
[...]
Phil> + *language = current_language;
Why current_language and not python_language?
Phil> + PyErr_SetString (PyExc_RuntimeError,
Phil> + _("Unexpected value. Expecting a " \
Don't need a backslash here.
Phil> + PyErr_SetString (PyExc_RuntimeError,
Phil> + _("Mandatory function 'symbol' not " \
Phil> + "implemented."));
On the whole it seems simpler to just try to call the method and let
Python handle the failure, since you have to handle that case anyway.
Phil> +static int
Phil> +mi_should_print (struct symbol *sym, const char *type)
This is only ever called with constant strings for the second argument.
It is better to use an enum in this situation.
Phil> +static int
Phil> +py_print_type (struct ui_out *out, struct value *val)
[...]
Phil> + if (except.reason > 0)
Phil> + {
Phil> + PyErr_SetString (PyExc_RuntimeError,
Phil> + except.message);
Use gdbpy_convert_exception here.
This should be fixed at each TRY_CATCH.
Phil> +/* Helper function which outputs a value name to value field in a
This reads strangely.
Phil> + stream. OUT is the ui-out structure the value will be output too,
s/too/to/
Phil> +static int
Phil> +py_print_value (struct ui_out *out, struct value *val,
Phil> + struct value_print_options opts,
Phil> + int mi_print_type,
Why is mi_print_type 'int' and not enum print_values?
Phil> + /* MI disallows different value types against different options the
Phil> + client passes, so test type against option. For CLI print all
Phil> + values. */
This comment reads strangely.
Phil> + if (ui_out_is_mi_like_p (out))
Phil> + {
Phil> + struct type *type;
Phil> +
Phil> + type = check_typedef (value_type (val));
Phil> + if (mi_print_type == PRINT_ALL_VALUES)
Phil> + should_print = 1;
Phil> + else if (mi_print_type == PRINT_SIMPLE_VALUES
Phil> + && TYPE_CODE (type) != TYPE_CODE_ARRAY
Phil> + && TYPE_CODE (type) != TYPE_CODE_STRUCT
Phil> + && TYPE_CODE (type) != TYPE_CODE_UNION)
Phil> + should_print = 1;
Phil> + }
Rather than making this all conditional, you could make the API simpler
to understand by having the CLI always pass PRINT_ALL_VALUES.
Phil> +/* Helper function to call a Python method and extract an iterator
Phil> + from the result, error checking for Python exception and returns
Phil> + that are not iterators. FILTER is the Python object to call, and
Phil> + FUNC is the name of the method. Returns a PyObject, or NULL on
Phil> + error with the appropriate exception set. This function can return
Phil> + an iterator, or None. */
Phil> +
Phil> +static PyObject *
Phil> +get_py_iter_from_func (PyObject *filter, char *func)
Why not const char *?
Phil> + if (! PyIter_Check (result))
Phil> + {
Phil> + PyErr_Format (PyExc_RuntimeError,
Phil> + _(" %s function must " \
Phil> + "return an iterator."), func);
Phil> + Py_DECREF (result);
Phil> + return NULL;
Phil> + }
Phil> + else
Phil> + {
Phil> + PyObject *iterator = PyObject_GetIter (result);
I think it doesn't really make sense to both do PyIter_Check and then
call PyObject_GetIter.
Generally these patches specify the API to work on iterators. But it
seems to me that it is more flexible to work on iterables instead.
That way code could return an iterator, or an array or tuple, depending
on what is convenient to that code -- more Pythonic IMO.
This would require fixing up the docs in various places.
In this spot at least it would mean not calling PyIter_Check.
Phil> + if (! iterator)
Phil> + return NULL;
Phil> + else
Phil> + return iterator;
This can be just 'return iterator'
Phil> +/* Helper function to output a single frame argument and value to an
Phil> + output stream. This function will account for entry values if the
Phil> + FV parameter is populated, the frame argument has entry values
Phil> + associated with them, and the appropriate "set entry-value"
Phil> + options are set. Will output in CLI or MI like format depending
Phil> + on the type of output stream detected. OUT is the output stream,
Phil> + SYM_NAME is the name of the symbol. If SYM_NAME is populated then
Phil> + it must have an accompanying value in the parameter FV. FA is a
Phil> + frame argument structure. If FA is populated, both SYM_NAME and
Phil> + FV are ignored. OPTS contains the value printing options,
Phil> + MI_PRINT_TYPE is an enumerator to the value types that will be
Phil> + printed if the output is MI. PRINT_MI_ARGS indicates whether to
Phil> + output the ARGS="1" field in MI output. */
Phil> +static int
Phil> +py_print_single_arg (struct ui_out *out,
Missing blank line.
Phil> + char *sym_name,
Why not const char *?
Phil> + struct value_print_options opts,
Why by-value and not 'const struct value_print_options *opts'?
I missed it earlier but the same applies to py_print_value.
Phil> + int mi_print_type,
Phil> + int print_mi_args_flag,
Phil> + const char *cli_print_frame_args_type,
cli_print_frame_args_type isn't documented in the function comment.
The comment refers to print_mi_args, not print_mi_args_flag.
This seems like a lot of duplication to me.
Why not use a single enum for all cases? That would be a lot simpler.
It is fine to introduce a new enum just for this code.
This applies to many of the functions in this file.
Also these should have the proper enum type and not be ints.
Phil> + struct cleanup *inner_cleanup =
Phil> + make_cleanup (null_cleanup, NULL);
Phil> +
Phil> + if (fa)
Phil> + {
Phil> + language = language_def (SYMBOL_LANGUAGE (fa->sym));
Phil> + val = fa->val;
Phil> + }
Phil> + else
Phil> + val = fv;
Phil> +
Phil> + /* MI has varying rules for tuples, but generally if there is only
Phil> + one element in each item in the list, do not start a tuple. */
Phil> + if (print_mi_args_flag || mi_print_type != PRINT_NO_VALUES)
Phil> + {
Phil> + inner_cleanup =
Phil> + make_cleanup_ui_out_tuple_begin_end (out,
Phil> + NULL);
It isn't good to assign to the same cleanup variable like this.
You can just ignore the return value from this call though.
I think the name 'inner_cleanup' is odd here, considering that there is
no outer scope.
Phil> + /* If frame argument is populated, check for entry-values and the
Phil> + entry value options. */
Phil> + if (fa)
Phil> + {
Phil> + struct ui_file *stb;
Phil> +
Phil> + stb = mem_fileopen ();
Phil> +
Phil> + fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym),
Phil> + SYMBOL_LANGUAGE (fa->sym),
Phil> + DMGL_PARAMS | DMGL_ANSI);
Phil> + if (fa->entry_kind == print_entry_values_compact)
Phil> + {
Phil> + fputs_filtered ("=", stb);
Phil> +
Phil> + fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym),
Phil> + SYMBOL_LANGUAGE (fa->sym),
Phil> + DMGL_PARAMS | DMGL_ANSI);
Phil> + }
Phil> + if (fa->entry_kind == print_entry_values_only
Phil> + || fa->entry_kind == print_entry_values_compact)
Phil> + {
Phil> + fputs_filtered ("@entry", stb);
Phil> + }
Phil> + ui_out_field_stream (out, "name", stb);
Phil> + ui_file_delete (stb);
Phil> + }
Phil> + else
Phil> + /* Otherwise, just output the name. */
Phil> + ui_out_field_string (out, "name", sym_name);
If similar code appears elsewhere in gdb, how about refactoring so it
can be shared?
This seems a bit questionable given that it calls mem_fileopen but never
makes a cleanup for it.
Phil> + /* For MI print the type. */
Phil> + if (ui_out_is_mi_like_p (out)
Phil> + && mi_print_type == PRINT_SIMPLE_VALUES)
It seems weird to only do this for PRINT_SIMPLE_VALUES.
Phil> + else
Phil> + if (PyErr_Occurred ())
Merge into one line.
Phil> +static int
Phil> +enumerate_locals (PyObject *iter,
Phil> + struct ui_out *out,
Phil> + int mi_print_type,
Phil> + int indent,
Phil> + int print_mi_args_flag,
Phil> + struct frame_info *frame)
This seems to share a lot of code with enumerate_args.
Can't they be merged?
These functions call a bunch of ui_out functions outside of TRY_CATCH.
I think that isn't ok -- CLI backtraces are filtered, meaning that it is
possible for the user to type "q" at the page break, and this results in
a gdb exception being thrown.
Phil> + make_cleanup_ui_out_list_begin_end (out, "locals");
Phil> + if (locals_iter != Py_None)
Phil> + if (! enumerate_locals (locals_iter, out, mi_print_type,
Phil> + indent, 0, frame))
Phil> + goto locals_error;
In cases like this, does it really make sense to emit the "locals" field
even when it is empty? Is this what MI ordinarily does?
Phil> +static hashval_t
Phil> +hash_printed_frame_entry (const void *data)
Phil> +{
Phil> + const struct frame_info *frame = data;
Phil> +
Phil> + return htab_hash_pointer (frame);
Phil> +}
Phil> +
Phil> +/* Equality function for the printed hash. */
Phil> +
Phil> +static int
Phil> +eq_printed_frame_entry (const void *a, const void *b)
Phil> +{
Phil> + const struct frame_info *ea = a;
Phil> + const struct frame_info *eb = b;
Phil> +
Phil> + return ea == eb;
You don't need these, you can use htab_hash_pointer and htab_eq_pointer
instead.
Phil> + gdbarch = get_frame_arch (frame);
In general you can't call a gdb function outside of TRY_CATCH.
I think it is reasonably dangerous to deviate from this rule.
Phil> + Py_DECREF (result);
Phil> + }
Phil> + else
Phil> + {
Phil> + PyErr_SetString (PyExc_RuntimeError,
Phil> + _("'inferior_frame' API must be implemented."));
Phil> + goto error;
Phil> + }
This is another situation where you should just unconditionally call the
method. You only need to check for the method if the code should adapt
to its absence; if it is required, just call it and let Python report a
failure if it is not there.
Phil> + level = frame_relative_level (frame);
Unprotected call.
Phil> + char buffer[10];
Phil> + sprintf (buffer, "%d", level);
Phil> + ui_out_spaces (out, strlen (buffer) + 2);
Blank line between declarations and code.
Also we're trying to avoid sprintf now; use xnsprintf.
Phil> + ui_out_field_fmt_int (out, 2, ui_left, "level",
Phil> + level);
I wonder though if the above should use ui_out_field_skip instead of
ui_out_spaces. Or perhaps both if just the former doesn't result in the
correct output.
Phil> + func = xstrdup (dup);
Phil> + annotate_frame_function_name ();
Phil> + ui_out_field_string (out, "func", func);
Phil> + xfree (func);
Phil> +
Phil> + }
Extra blank line.
Also I think the name 'dup' here is misnomer.
It isn't actually a dup of anything.
And, there doesn't seem to be a reason to duplicate 'dup' just to print
it and then free it. Why not just print it directly?
This occurs elsewhere as well.
Phil> + /* For MI we need to deal with the "children" list population of
Phil> + elided frames, so if MI output detected do not send newline. */
Phil> + if (! ui_out_is_mi_like_p (out))
Phil> + {
Phil> + annotate_frame_end ();
Phil> + ui_out_text (out, "\n");
ui_out_text is a no-op for MI. It is usually cleaner to simply always
call it. It doesn't need a comment, either.
I think the same applies to annotations.
Occurs in other spots too.
Phil> + int success = py_print_frame (item, print_level,
Phil> + print_frame_info,
Phil> + print_args,
Phil> + print_locals,
Phil> + mi_print_type,
Phil> + cli_print_frame_args_type,
Phil> + out, indent,
Phil> + levels_printed);
Phil> +
Phil> + if (success == 0 && PyErr_Occurred ())
Phil> + {
Phil> + Py_DECREF (item);
Phil> + goto error;
Phil> + }
Can you have success == 0 but no error?
Why not return 1 in that case?
The py_print_frame doc comment doesn't explain its return values.
Oh, I see it returns a PY_BT_ constant. But then a check against 0 is
confusing, it should check against the constants.
Phil> +/* Helper function to initiate frame filter invocation at starting
Phil> + frame FRAME. */
Phil> +static PyObject *
Missing blank line.
Phil> +bootstrap_python_frame_filters (struct frame_info *frame)
Phil> +{
Phil> +
Phil> + PyObject *module, *sort_func, *iterable, *frame_obj;
Extra blank line.
Phil> + module = PyImport_ImportModule ("gdb.command.frame_filters");
Yeah, definitely should be a different module.
I think 'invoke' should be renamed, too; and documented.
It is nice for things like this if there is an obvious way from Python
to accomplish the same thing that gdb does internally. We didn't think
of this for value pretty printers (though you can still do it), but I'd
like us to consider it for new additions.
Phil> +/* Public and dispatch function for frame filters. This is the only
Not sure about that first sentence.
Phil> + publicly exported function in this file. FRAME is the source
Phil> + frame to start frame-filter invocation. FLAGS is an integer
Phil> + holding the flags for printing. The following elements of the
Phil> + FRAME_FILTER_FLAGS enum denotes makeup of FLAGS: PRINT_LEVEL is a
Phil> + flag indicating whether to print the frame's relative level in the
Phil> + output. PRINT_FRAME_INFO is a flag that indicates whether this
Phil> + function should print the frame information, PRINT_ARGS is a flag
Phil> + that indicates whether to print frame arguments, and PRINT_LOCALS,
Phil> + likewise, with frame local variables. MI_PRINT_ARGS_TYPE is an
Phil> + enum from MI that indicates which values types to print. This
Phil> + parameter is ignored if the output is detected to be CLI.
Phil> + CLI_PRINT_FRAME_ARGS_TYPE likewise is a an element of what value
Phil> + types to print from CLI. OUT is the output stream to print, and
Phil> + COUNT is a delimiter (required for MI slices). */
I think COUNT is just a count.
Phil> + int success = 0;
I think this is probably initialized improperly.
Phil> + /* If iterable is None, then there are not any frame filters
Extra space before "If". This throws off the indentation of the rest of
the comment.
Phil> + make_cleanup_py_decref (iterable);
Phil> + if (iterable == Py_None)
Phil> + {
Phil> + do_cleanups (cleanups);
Phil> + return PY_BT_NO_FILTERS;
Better I think to set 'success' and goto done.
Phil> + /* Is it an iterator */
End with a period & two spaces.
Phil> + if PyIter_Check (iterable)
I think it really should be an iterable, not an iterator.
So, remove the 'if' and call PyObject_GetIter unconditionally.
Phil> + levels_printed = htab_create (20,
Phil> + hash_printed_frame_entry,
Phil> + eq_printed_frame_entry,
Phil> + NULL);
You're creating cleanups for everything except this.
It doesn't really matter, since this code is not exposed to gdb
exceptions, but I wonder why. Simpler to make it all uniform.
Phil> + while ((item = PyIter_Next (iterator)) && count--)
If 'count-- == 0' then you will leak 'item'.
Phil> + success = py_print_frame (item, print_level,
Extra space after the '='.
Phil> + /* Do not exit on error printing the frame, continue with
Phil> + other frames. */
Phil> + if (success == PY_BT_ERROR && PyErr_Occurred ())
Checking PyErr_Occurred makes me think that the return results could use
a cleanup. I think PY_BT_ERROR should mean that an error definitely
occurred, so a second check isn't needed.
Phil> + gdbpy_print_stack ();
It appears this function will never return PY_BT_ERROR if frame printing
fails.
Phil> + else
Phil> + {
Phil> + Py_DECREF (iterable);
Phil> + error (_("Frame filter must support iteration protocol."));
Why an exception here but not for anything else?
Phil> + self->frame_filters = PyDict_New ();
Phil> + if (!self->frame_filters)
Phil> + {
Phil> + Py_DECREF (self->printers);
I think this will double-free, due to the destructor.
Phil> + Py_DECREF (self->printers);
Phil> + Py_DECREF (self->frame_filters);
Likewise.
Phil> +PyObject *
Phil> +objfpy_get_frame_filters (PyObject *o, void *ignore)
Needs a comment.
Phil> +static int
Phil> +objfpy_set_frame_filters (PyObject *o, PyObject *filters, void *ignore)
Likewise.
Phil> + object->frame_filters = PyDict_New ();
Phil> + if (!object->frame_filters)
Phil> + {
Phil> + Py_DECREF (object->printers);
Double free.
Phil> + Py_DECREF (object->printers);
Phil> + Py_DECREF (object->frame_filters);
Likewise.
Phil> + self->frame_filters = PyDict_New ();
Phil> + if (!self->frame_filters)
Phil> + {
Phil> + Py_DECREF (self->printers);
Likewise.
Phil> + Py_DECREF (self->printers);
Phil> + Py_DECREF (self->frame_filters);
Likewise.
Phil> +PyObject *
Phil> +pspy_get_frame_filters (PyObject *o, void *ignore)
Needs a comment.
Phil> +static int
Phil> +pspy_set_frame_filters (PyObject *o, PyObject *frame, void *ignore)
Likewise.
Phil> + object->frame_filters = PyDict_New ();
Phil> + if (!object->frame_filters)
Phil> + {
Phil> + Py_DECREF (object->printers);
Double free.
Phil> + Py_DECREF (object->printers);
Phil> + Py_DECREF (object->frame_filters);
Likewise.
Phil> +
Phil> set_program_space_data (pspace, pspy_pspace_data_key, object);
Extra blank line.
Phil> +/* This is a cleanup function which decrements the refcount on a
Phil> + Python object. This function accounts appropriately for NULL
Phil> + references. */
Phil> +
Phil> +static void
Phil> +py_xdecref (void *p)
Phil> +{
Phil> + PyObject *py = p;
Phil> +
Phil> + /* Note that we need the extra braces in this 'if' to avoid a
Phil> + warning from gcc. */
Phil> + if (py)
Phil> + {
Phil> + Py_XDECREF (py);
You don't need the 'if'. Py_XDECREF has one already.
Phil> + return make_cleanup (py_xdecref, (void *) py);
You don't need the cast.
Phil> +/* Python frame-filter status returns constants. */
Phil> +static const int PY_BT_ERROR = 0;
Phil> +static const int PY_BT_COMPLETED = 1;
Phil> +static const int PY_BT_NO_FILTERS = 2;
This should be an enum and then various 'int' types in the code changed
as well.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2012-12-05 17:03 ` Tom Tromey
@ 2012-12-07 13:41 ` Phil Muldoon
2012-12-07 19:03 ` Tom Tromey
0 siblings, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2012-12-07 13:41 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 12/05/2012 05:03 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
>
> Hi. Lots of notes on this one.
Thanks for the review. I will fix the issues you highlighted and
submit a patch in the new year. I will be out of the office until the
first week of January, so I will not be able to reply in the next two
weeks.
> Phil> diff --git a/gdb/python/lib/gdb/BaseFrameWrapper.py b/gdb/python/lib/gdb/BaseFrameWrapper.py
>
> Phil> + If the result of frame filters running means we have one gdb.Frame
> Phil> + wrapped by multiple frame wrappers, all sub-classed from
> Phil> + BaseFrameWrapper:
> Phil> +
> Phil> + Wrapper1(Wrapper2(BaseFrameWrapper(gdb.Frame)))
>
> I think this notation is a bit weird.
I was just trying to show a case where you have frame wrappers,
wrapping other frame wrappers, etc, until you get to BaseFrameWrapper
which wraps a gdb.Frame. What notation should I use to illustrate
this?
> Phil> + """A container class conforming to the Symbol/Value interface
> Phil> + which holds frame locals or frame arguments."""
> Phil> + def __init__(self, symbol, value):
> Phil> + self.sym = symbol
> Phil> + self.val = value
>
> As far as I can tell this is always instantiated with value == None.
> That seems wrong.
In most cases, value will be None. The typical case is if symbol is a
gdb.Symbol, and value is None, then GDB will read that value for you.
The documentation patch gives all of the different combinations of
assignment for these.
>
> Phil> + def fetch_frame_locals(self):
> Phil> + """Public utility method to fetch frame local variables for
> Phil> + the stored frame. Frame arguments are not fetched. If there
> Phil> + are not frame local variables, return None."""
>
> Typo: should be "there are no".
>
> [ fetch_frame_locals ]
> Phil> + if len(lvars) == 0:
> Phil> + return None
>
> It is curious that this is needed.
> Will whatever code eventually handles display of this do something
> different for an empty list?
If this function returns None, then GDB will not print any frame
locals. It can probably be written differently in the Python code.
But fetch_frame_locals has to return a None, or an iterator
> Phil> + def fetch_frame_args(self):
> Phil> + """Public utility method to fetch frame argument for the
> Phil> + stored frame. Frame arguments are the only type fetched. If
> Phil> + there are no frame arguments variables, return None."""
>
> I think "frame argument" - not plural.
>
> Phil> + def get_value(self, sym, block):
> Phil> + """Public utility method to fetch a value from a symbol."""
>
> What calls this?
Nothing, but I just left it there if the user wishes to use it. I can
remove it. Fetching a local/frame value has some caveats to it, so I
decided to leave it in there as a signpost. I can remove it.
> Phil> +class FrameWrapper (object):
> Phil> + """Interface for a Frame Wrapper."""
>
> If this is just for documentation, how about we just put it all in the
> docs and delete the class?
>
> Then we can rename "BaseFrameWrapper". I find the BaseFrameWrapper /
> FrameWrapper distinction a bit confusing.
This was something you requested when we were talking about it earlier
this year ;)
This is pretty much just the equivalent of a Java interface. But I
have no strong feelings on this either way.
> It seems strange to have both _get_sort_priority and _get_priority.
> Similarly elsewhere.
This was because the Python sort/filter functions (in invoke) stop if
it encounters an error. So if there is a badly implemented filter
(say one that does not have a "priority" attribute) then all frame
filters will not run. This was to try and make the system more
robust.
> Phil> +def _set_priority(filter_item, priority):
> Phil> + """ Internal worker function to set the frame-filter's priority.
> Phil> +
> Phil> + Arguments:
> Phil> + filter_item: An object conforming to the frame filter
> Phil> + interface.
> Phil> + priority: The priority to assign as an integer.
> Phil> +
> Phil> + Raises:
> Phil> + gdb.GdbError: When the priority attribute has not been
> Phil> + implemented.
> Phil> + """
> Phil> +
> Phil> + if hasattr(filter_item, "priority"):
> Phil> + filter_item.priority = priority
> Phil> + else:
> Phil> + raise gdb.GdbError("Cannot find class attribute 'priority'")
>
> Why check for priority before setting it? It seems simpler to just set
> it. Similarly elsewhere.
Sure, but I think if a class does not have an attribute which is
mandatory, we should not just set it blindly.
> Phil> + if hasattr(filter_item, "enabled"):
> Phil> + return filter_item.enabled
> Phil> + else:
> Phil> + raise gdb.GdbError("Cannot find class attribute 'enabled'")
>
> What is wrong with just fetching it and letting Python throw the
> exception?
This was a case again, of trying to make the exception more relevant
to the user where a frame filter is badly implemented. I have no
strong feelings here either.
> Phil> + sorted_frame_filters = copy.copy(all_filters)
> Phil> + sorted_frame_filters.sort(key = _get_sort_priority,
> Phil> + reverse = True)
>
> I think for Python 3 we recently switched to using 'sorted' instead of
> the in-place sort.
>
> Hmm, that patch doesn't seem to have gone in.
Sure I will take a look at it when it goes in
> Phil> + for ff in sorted_list:
> Phil> + frame_iterator = ff[1].filter(frame_iterator)
>
> I don't understand the "[1]" here.
keys() from a dictionary returns a tuple of key and value pairings.
This just calls the value side (ie, the filter itself)
>
> If the 'invoke' function is meant to be used from outside the new
> commands, it should probably not be in gdb.command.
It was easier to write "invoke" in Python than in C, and yes it is
only meant for GDB to call. Good point on the gdb.Command issue
though.
> Phil> + def print_list(self, title, filter_list):
> Phil> + """Print a list."""
>
> Just remove this text.
> It isn't useful.
>
> Phil> + def invoke(self, arg, from_tty):
> Phil> + """GDB calls this to perform the command."""
>
> Likewise.
(and others), this was a case of copying and pasting code from the
info pretty printers command. Mea Culpa, will fix.
> Phil> + DICTIONARY is the name of the frame filter dictionary on which to
> Phil> + operate. Named dictionaries are: "global" for the global frame
> Phil> + filter dictionary, "progspace" for the program space's framefilter
> Phil> + dictionary. If either of these two are not specified, the
> Phil> + dictionary name is assumed to be the name of the object-file name.
>
> It would be nice if these commands had completion methods that did the
> right thing.
Completion of the printer name?
> Phil> +extract_sym (PyObject *obj, char **name, struct symbol **sym,
> Phil> + const struct language_defn **language)
> [...]
> Phil> + *language = current_language;
>
> Why current_language and not python_language?
I guess I am not clear on the difference between the two?
> Phil> +static int
> Phil> +py_print_value (struct ui_out *out, struct value *val,
> Phil> + struct value_print_options opts,
> Phil> + int mi_print_type,
>
> Why is mi_print_type 'int' and not enum print_values?
Typo.
>
> Phil> + if (ui_out_is_mi_like_p (out))
> Phil> + {
> Phil> + struct type *type;
> Phil> +
> Phil> + type = check_typedef (value_type (val));
> Phil> + if (mi_print_type == PRINT_ALL_VALUES)
> Phil> + should_print = 1;
> Phil> + else if (mi_print_type == PRINT_SIMPLE_VALUES
> Phil> + && TYPE_CODE (type) != TYPE_CODE_ARRAY
> Phil> + && TYPE_CODE (type) != TYPE_CODE_STRUCT
> Phil> + && TYPE_CODE (type) != TYPE_CODE_UNION)
> Phil> + should_print = 1;
> Phil> + }
>
> Rather than making this all conditional, you could make the API simpler
> to understand by having the CLI always pass PRINT_ALL_VALUES.
I am not sure what you mean here with CLI, as this is an MI case?
> Phil> +/* Helper function to call a Python method and extract an iterator
> Phil> + from the result, error checking for Python exception and returns
> Phil> + that are not iterators. FILTER is the Python object to call, and
> Phil> + FUNC is the name of the method. Returns a PyObject, or NULL on
> Phil> + error with the appropriate exception set. This function can return
> Phil> + an iterator, or None. */
> Phil> +
> Phil> +static PyObject *
> Phil> +get_py_iter_from_func (PyObject *filter, char *func)
>
> Why not const char *?
Typo/Oops.
> Phil> + if (! PyIter_Check (result))
> Phil> + {
> Phil> + PyErr_Format (PyExc_RuntimeError,
> Phil> + _(" %s function must " \
> Phil> + "return an iterator."), func);
> Phil> + Py_DECREF (result);
> Phil> + return NULL;
> Phil> + }
> Phil> + else
> Phil> + {
> Phil> + PyObject *iterator = PyObject_GetIter (result);
>
> I think it doesn't really make sense to both do PyIter_Check and then
> call PyObject_GetIter.
>
> Generally these patches specify the API to work on iterators. But it
> seems to me that it is more flexible to work on iterables instead.
> That way code could return an iterator, or an array or tuple, depending
> on what is convenient to that code -- more Pythonic IMO.
I vaguely remember discussing with you, but I cannot remember the
outcome. Still, no objections from me.
> Phil> + char *sym_name,
>
> Why not const char *?
Oops, and to all others too.
> Phil> + struct value_print_options opts,
>
> Why by-value and not 'const struct value_print_options *opts'?
> I missed it earlier but the same applies to py_print_value.
I can't remember why, but I do not see a reason why, either. Will fix.
> Phil> + int mi_print_type,
> Phil> + int print_mi_args_flag,
> Phil> + const char *cli_print_frame_args_type,
>
> cli_print_frame_args_type isn't documented in the function comment.
> The comment refers to print_mi_args, not print_mi_args_flag.
> This seems like a lot of duplication to me.
> Why not use a single enum for all cases? That would be a lot simpler.
> It is fine to introduce a new enum just for this code.
Because CLI and MI use those enums internally, and each print values
differently depending on type. So I get those values from MI/CLI.
They provide them, I did not create them. But I still have to account
for their instructions to match existing CLI/MI expectations of output.
> Phil> + struct cleanup *inner_cleanup =
> Phil> + make_cleanup (null_cleanup, NULL);
> Phil> +
> Phil> + if (fa)
> Phil> + {
> Phil> + language = language_def (SYMBOL_LANGUAGE (fa->sym));
> Phil> + val = fa->val;
> Phil> + }
> Phil> + else
> Phil> + val = fv;
> Phil> +
> Phil> + /* MI has varying rules for tuples, but generally if there is only
> Phil> + one element in each item in the list, do not start a tuple. */
> Phil> + if (print_mi_args_flag || mi_print_type != PRINT_NO_VALUES)
> Phil> + {
> Phil> + inner_cleanup =
> Phil> + make_cleanup_ui_out_tuple_begin_end (out,
> Phil> + NULL);
>
> It isn't good to assign to the same cleanup variable like this.
> You can just ignore the return value from this call though.
> I think the name 'inner_cleanup' is odd here, considering that there is
> no outer scope.
That's just an error on my part, ignore the "inner_cleanup = " on the
second instance. The second instance is to deal with an oddity of
tuples in MI. Usually if there is only one item in a list, then a
tuple is not created. Except in one command, -stack-list-variables I
think, which always creates a tuple regardless. I will write more on
this in another email.
>
> Phil> + /* If frame argument is populated, check for entry-values and the
> Phil> + entry value options. */
> Phil> + if (fa)
> Phil> + {
> Phil> + struct ui_file *stb;
> Phil> +
> Phil> + stb = mem_fileopen ();
> Phil> +
> Phil> + fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym),
> Phil> + SYMBOL_LANGUAGE (fa->sym),
> Phil> + DMGL_PARAMS | DMGL_ANSI);
> Phil> + if (fa->entry_kind == print_entry_values_compact)
> Phil> + {
> Phil> + fputs_filtered ("=", stb);
> Phil> +
> Phil> + fprintf_symbol_filtered (stb, SYMBOL_PRINT_NAME (fa->sym),
> Phil> + SYMBOL_LANGUAGE (fa->sym),
> Phil> + DMGL_PARAMS | DMGL_ANSI);
> Phil> + }
> Phil> + if (fa->entry_kind == print_entry_values_only
> Phil> + || fa->entry_kind == print_entry_values_compact)
> Phil> + {
> Phil> + fputs_filtered ("@entry", stb);
> Phil> + }
> Phil> + ui_out_field_stream (out, "name", stb);
> Phil> + ui_file_delete (stb);
> Phil> + }
> Phil> + else
> Phil> + /* Otherwise, just output the name. */
> Phil> + ui_out_field_string (out, "name", sym_name);
>
> If similar code appears elsewhere in gdb, how about refactoring so it
> can be shared?
>
> This seems a bit questionable given that it calls mem_fileopen but never
> makes a cleanup for it.
That's a bug/memory leak.
>
> Phil> + /* For MI print the type. */
> Phil> + if (ui_out_is_mi_like_p (out)
> Phil> + && mi_print_type == PRINT_SIMPLE_VALUES)
>
> It seems weird to only do this for PRINT_SIMPLE_VALUES.
Depending what option is passed, sometimes type is printed sometimes
not. You can see for yourself with the -stack-list-* commands, and
the value type options specified. Personally, I would always print
the type, but I cannot, to stay compatible. Or I could be completely
wrong ;) But I do recall this being the case, and if I remember
correctly the testsuite accounts for non-frame-filter output too.
> Phil> +static int
> Phil> +enumerate_locals (PyObject *iter,
> Phil> + struct ui_out *out,
> Phil> + int mi_print_type,
> Phil> + int indent,
> Phil> + int print_mi_args_flag,
> Phil> + struct frame_info *frame)
>
> This seems to share a lot of code with enumerate_args.
> Can't they be merged?
>
> These functions call a bunch of ui_out functions outside of TRY_CATCH.
> I think that isn't ok -- CLI backtraces are filtered, meaning that it is
> possible for the user to type "q" at the page break, and this results in
> a gdb exception being thrown.
Ok, thanks.
>
> Phil> + make_cleanup_ui_out_list_begin_end (out, "locals");
> Phil> + if (locals_iter != Py_None)
> Phil> + if (! enumerate_locals (locals_iter, out, mi_print_type,
> Phil> + indent, 0, frame))
> Phil> + goto locals_error;
>
> In cases like this, does it really make sense to emit the "locals" field
> even when it is empty? Is this what MI ordinarily does?
Yes.
-stack-list-locals --all-values
^done,locals=[]
>
> Phil> + ui_out_field_fmt_int (out, 2, ui_left, "level",
> Phil> + level);
>
> I wonder though if the above should use ui_out_field_skip instead of
> ui_out_spaces. Or perhaps both if just the former doesn't result in the
> correct output.
I generally followed the case in the stack.c frame printing which "bt"
uses:
/* Do this regardless of SOURCE because we don't have any source
to list for this frame. */
if (print_level)
{
ui_out_text (uiout, "#");
ui_out_field_fmt_int (uiout, 2, ui_left, "level",
frame_relative_level (frame));
}
There is no alternative to that "if".
>
> Phil> + func = xstrdup (dup);
> Phil> + annotate_frame_function_name ();
> Phil> + ui_out_field_string (out, "func", func);
> Phil> + xfree (func);
> Phil> +
> Phil> + }
>
> Extra blank line.
>
> Also I think the name 'dup' here is misnomer.
> It isn't actually a dup of anything.
> And, there doesn't seem to be a reason to duplicate 'dup' just to print
> it and then free it. Why not just print it directly?
Error on my part.
>
> Phil> + int success = py_print_frame (item, print_level,
> Phil> + print_frame_info,
> Phil> + print_args,
> Phil> + print_locals,
> Phil> + mi_print_type,
> Phil> + cli_print_frame_args_type,
> Phil> + out, indent,
> Phil> + levels_printed);
> Phil> +
> Phil> + if (success == 0 && PyErr_Occurred ())
> Phil> + {
> Phil> + Py_DECREF (item);
> Phil> + goto error;
> Phil> + }
>
> Can you have success == 0 but no error?
> Why not return 1 in that case?
> The py_print_frame doc comment doesn't explain its return values.
>
> Oh, I see it returns a PY_BT_ constant. But then a check against 0 is
> confusing, it should check against the constants.
No this is an error on my part, as this is a recursive call. I
probably forgot to update it when I added the PY_BT_ constants.
> Phil> + module = PyImport_ImportModule ("gdb.command.frame_filters");
>
> Yeah, definitely should be a different module.
>
> I think 'invoke' should be renamed, too; and documented.
> It is nice for things like this if there is an obvious way from Python
> to accomplish the same thing that gdb does internally. We didn't think
> of this for value pretty printers (though you can still do it), but I'd
> like us to consider it for new additions.
It was very much simpler to write "invoke" in Python, than in C. In
fact where I could write something in Python, I did. Most of the C
code is just printing the frames.
> I think it really should be an iterable, not an iterator.
> So, remove the 'if' and call PyObject_GetIter unconditionally.
>
> Phil> + levels_printed = htab_create (20,
> Phil> + hash_printed_frame_entry,
> Phil> + eq_printed_frame_entry,
> Phil> + NULL);
>
> You're creating cleanups for everything except this.
> It doesn't really matter, since this code is not exposed to gdb
> exceptions, but I wonder why. Simpler to make it all uniform.
This was a late addition to track frames printed and is simply a
blimp. I think I did not create a cleanup for this as it will always
fall through to the free.
> I think this will double-free, due to the destructor.
>
> Phil> + Py_DECREF (self->printers);
> Phil> + Py_DECREF (self->frame_filters);
>
> Likewise.
Yeah, I forgot that decref'ing self, calls the object's custom dealloc
method if it has one, and these do. Already fixed this locally.
>
> Phil> + object->frame_filters = PyDict_New ();
> Phil> + if (!object->frame_filters)
> Phil> + {
> Phil> + Py_DECREF (object->printers);
>
> Double free.
>
> Phil> + Py_DECREF (object->printers);
> Phil> + Py_DECREF (object->frame_filters);
>
> Likewise.
>
> Phil> + self->frame_filters = PyDict_New ();
> Phil> + if (!self->frame_filters)
> Phil> + {
> Phil> + Py_DECREF (self->printers);
>
> Likewise.
>
> Phil> + Py_DECREF (self->printers);
> Phil> + Py_DECREF (self->frame_filters);
>
> Likewise.
Ditto, above.
> Phil> +PyObject *
> Phil> +pspy_get_frame_filters (PyObject *o, void *ignore)
> Phil> + object->frame_filters = PyDict_New ();
> Phil> + if (!object->frame_filters)
> Phil> + {
> Phil> + Py_DECREF (object->printers);
>
> Double free.
>
> Phil> + Py_DECREF (object->printers);
> Phil> + Py_DECREF (object->frame_filters);
>
> Likewise.
Ditto.
Thanks
Phil
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2012-12-07 13:41 ` Phil Muldoon
@ 2012-12-07 19:03 ` Tom Tromey
0 siblings, 0 replies; 16+ messages in thread
From: Tom Tromey @ 2012-12-07 19:03 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> I was just trying to show a case where you have frame wrappers,
Phil> wrapping other frame wrappers, etc, until you get to BaseFrameWrapper
Phil> which wraps a gdb.Frame. What notation should I use to illustrate
Phil> this?
Yeah, I don't know. Maybe spell it out more.
Phil> + if len(lvars) == 0:
Phil> + return None
Tom>
Tom> It is curious that this is needed.
Tom> Will whatever code eventually handles display of this do something
Tom> different for an empty list?
Phil> If this function returns None, then GDB will not print any frame
Phil> locals. It can probably be written differently in the Python code.
Phil> But fetch_frame_locals has to return a None, or an iterator
Yeah, but what happens if the iterator has no elements?
It seems to me that this should work the same as the None case.
But then you don't need the special check above.
Tom> It seems strange to have both _get_sort_priority and _get_priority.
Tom> Similarly elsewhere.
Phil> This was because the Python sort/filter functions (in invoke) stop if
Phil> it encounters an error. So if there is a badly implemented filter
Phil> (say one that does not have a "priority" attribute) then all frame
Phil> filters will not run. This was to try and make the system more
Phil> robust.
It seems even more robust to just use the safe variant everywhere.
Phil> Sure, but I think if a class does not have an attribute which is
Phil> mandatory, we should not just set it blindly.
I think it is fine. It is mandatory and documented as such.
Phil> + if hasattr(filter_item, "enabled"):
Phil> + return filter_item.enabled
Phil> + else:
Phil> + raise gdb.GdbError("Cannot find class attribute 'enabled'")
Tom>
Tom> What is wrong with just fetching it and letting Python throw the
Tom> exception?
Phil> This was a case again, of trying to make the exception more relevant
Phil> to the user where a frame filter is badly implemented. I have no
Phil> strong feelings here either.
I think the standard message is just as clear:
AttributeError: 'str' object has no attribute 'method'
Tom> If the 'invoke' function is meant to be used from outside the new
Tom> commands, it should probably not be in gdb.command.
Phil> It was easier to write "invoke" in Python than in C, and yes it is
Phil> only meant for GDB to call. Good point on the gdb.Command issue
Phil> though.
Yeah, I don't mind it being in Python. I think it is better that way
too.
Phil> + DICTIONARY is the name of the frame filter dictionary on which to
Phil> + operate. Named dictionaries are: "global" for the global frame
Phil> + filter dictionary, "progspace" for the program space's framefilter
Phil> + dictionary. If either of these two are not specified, the
Phil> + dictionary name is assumed to be the name of the object-file name.
Tom>
Tom> It would be nice if these commands had completion methods that did the
Tom> right thing.
Phil> Completion of the printer name?
Completion of all the arguments - the printer name but also the
dictionary.
Phil> +extract_sym (PyObject *obj, char **name, struct symbol **sym,
Phil> + const struct language_defn **language)
Tom> [...]
Phil> + *language = current_language;
Tom>
Tom> Why current_language and not python_language?
Phil> I guess I am not clear on the difference between the two?
There may not be a difference here, but it is weird to read the Python
code and see a reference to current_language when python_language
exists.
Tom>
Phil> + if (ui_out_is_mi_like_p (out))
Phil> + {
Phil> + struct type *type;
Phil> +
Phil> + type = check_typedef (value_type (val));
Phil> + if (mi_print_type == PRINT_ALL_VALUES)
Phil> + should_print = 1;
Phil> + else if (mi_print_type == PRINT_SIMPLE_VALUES
Phil> + && TYPE_CODE (type) != TYPE_CODE_ARRAY
Phil> + && TYPE_CODE (type) != TYPE_CODE_STRUCT
Phil> + && TYPE_CODE (type) != TYPE_CODE_UNION)
Phil> + should_print = 1;
Phil> + }
Tom>
Tom> Rather than making this all conditional, you could make the API simpler
Tom> to understand by having the CLI always pass PRINT_ALL_VALUES.
Phil> I am not sure what you mean here with CLI, as this is an MI case?
Sorry, what I mean is, don't check ui_out_is_mi_like_p at all, just
always respect the "print type", and have the CLI code pass in the
appropriate value to get the behavior it wants.
Phil> + int mi_print_type,
Phil> + int print_mi_args_flag,
Phil> + const char *cli_print_frame_args_type,
Tom>
Tom> cli_print_frame_args_type isn't documented in the function comment.
Tom> The comment refers to print_mi_args, not print_mi_args_flag.
Tom> This seems like a lot of duplication to me.
Tom> Why not use a single enum for all cases? That would be a lot simpler.
Tom> It is fine to introduce a new enum just for this code.
Phil> Because CLI and MI use those enums internally, and each print values
Phil> differently depending on type. So I get those values from MI/CLI.
Phil> They provide them, I did not create them. But I still have to account
Phil> for their instructions to match existing CLI/MI expectations of output.
I think it is better to present a simpler API to the callers.
It is fine to introduce new enums and to make the CLI and MI code
convert their requests. Or, better, unify the enums across the entire
code base.
Phil> + module = PyImport_ImportModule ("gdb.command.frame_filters");
Tom>
Tom> Yeah, definitely should be a different module.
Tom>
Tom> I think 'invoke' should be renamed, too; and documented.
Tom> It is nice for things like this if there is an obvious way from Python
Tom> to accomplish the same thing that gdb does internally. We didn't think
Tom> of this for value pretty printers (though you can still do it), but I'd
Tom> like us to consider it for new additions.
Phil> It was very much simpler to write "invoke" in Python, than in C. In
Phil> fact where I could write something in Python, I did. Most of the C
Phil> code is just printing the frames.
Yes, my comment isn't about C, but rather which module the function
lives in.
I think something like 'gdb.frames' would be better, analogous to
gdb.types.
And, rename the function and document it so that user code can call it
to easily iterate over filtered frames.
Tom> You're creating cleanups for everything except this.
Tom> It doesn't really matter, since this code is not exposed to gdb
Tom> exceptions, but I wonder why. Simpler to make it all uniform.
Phil> This was a late addition to track frames printed and is simply a
Phil> blimp. I think I did not create a cleanup for this as it will always
Phil> fall through to the free.
The question is just about uniformity.
All code in that function will fall through.
So I think it should either do explicit frees everywhere, or use
cleanups everywhere.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch][python] 1 of 5 - Frame filter Python C code changes.
@ 2013-03-11 22:13 Phil Muldoon
2013-03-21 20:55 ` Tom Tromey
0 siblings, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2013-03-11 22:13 UTC (permalink / raw)
To: gdb-patches
This email and patch covers the python/ changes for Python Frame Filters.
2013-03-11 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/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.
--
diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py
new file mode 100644
index 0000000..1ec733e
--- /dev/null
+++ b/gdb/python/lib/gdb/FrameDecorator.py
@@ -0,0 +1,288 @@
+# 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 == 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 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
+
+ @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 no frame local variables, return an empty list."""
+ 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(SymValueWrapper(sym, None))
+
+ 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 = []
+ try:
+ block = self.frame.block()
+ except:
+ return None
+
+ for sym in block:
+ if not sym.is_argument:
+ continue;
+ args.append(SymValueWrapper(sym,None))
+
+ return 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..109a06c
--- /dev/null
+++ b/gdb/python/lib/gdb/FrameIterator.py
@@ -0,0 +1,53 @@
+# 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 __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/__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..4750c91
--- /dev/null
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -0,0 +1,589 @@
+# 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 itertools
+
+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.
+ if hasattr(filter_item, "priority"):
+ return filter_item.priority
+ else:
+ return 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.
+ if hasattr(filter_item, "enabled"):
+ return filter_item.enabled
+ else:
+ return 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 _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.
+ """
+ return frame_filter.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 "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)
+
+# GDB Commands.
+class SetFilterPrefixCmd(gdb.Command):
+ """Prefix command for 'set' frame-filter related operations."""
+
+ def __init__(self):
+ super(SetFilterPrefixCmd, self).__init__("set python frame-filter",
+ gdb.COMMAND_DATA,
+ gdb.COMPLETE_COMMAND, True)
+class ShowFilterPrefixCmd(gdb.Command):
+ """Prefix command for 'show' frame-filter related operations."""
+ def __init__(self):
+ super(ShowFilterPrefixCmd, self).__init__("show python frame-filter",
+ gdb.COMMAND_DATA,
+ gdb.COMPLETE_COMMAND, 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: _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(_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, 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 = _return_list(list_op)
+
+ if list_op == "all":
+ for item in op_list:
+ _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)
+
+ _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
+ # dictioanries 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 = _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 = _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 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)
+
+ 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.")
+
+ 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 = _return_list(list_op)
+
+ try:
+ ff = op_list[frame_filter]
+ except KeyError:
+ msg = "frame-filter '" + str(name) + "' not found."
+ raise gdb.GdbError(msg)
+
+ _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 = _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)
+ 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)
+
+ 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.")
+
+ 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 = _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 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 = _return_list(text.split()[0].rstrip())
+ return _complete_frame_filter_name(word, printer_list)
+
+ def invoke(self, arg, from_tty):
+ 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)
+
+
+InfoFrameFilter()
+SetFilterPrefixCmd()
+ShowFilterPrefixCmd()
+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..2c48569
--- /dev/null
+++ b/gdb/python/lib/gdb/frames.py
@@ -0,0 +1,122 @@
+# Frame-filter commands.
+# Copyright (C) 2012, 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 gdb.command.frame_filters as ffc
+import collections
+
+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 = ffc._get_priority,
+ reverse = True)
+
+ sorted_frame_filters = filter(ffc._get_enabled,
+ sorted_frame_filters)
+
+ return sorted_frame_filters
+
+def execute_frame_filters(frame, frame_low, frame_high):
+ """ Internal function 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 a negative
+ integer 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:
+ # The end argument for islice is a count, not a position, so
+ # add one as frames start at zero.
+ frame_high = frame_high + 1;
+
+ sliced = itertools.islice(frame_iterator, frame_low, frame_high)
+
+ return sliced
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
new file mode 100644
index 0000000..7525d37
--- /dev/null
+++ b/gdb/python/py-framefilter.c
@@ -0,0 +1,1420 @@
+/* Python frame filters
+
+ Copyright (C) 2013 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 "demangle.h"
+#include "mi/mi-cmds.h"
+#include "python-internal.h"
+
+
+typedef enum mi_print_types
+{
+ MI_PRINT_ALL,
+ MI_PRINT_ARGS,
+ MI_PRINT_LOCALS
+} mi_print_types;
+
+
+/* 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)
+{
+ PyObject *result = PyObject_CallMethod (obj, "symbol", NULL);
+
+ if (! result)
+ return PY_BT_ERROR;
+
+ /* 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 PY_BT_ERROR;
+ *language = python_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 PY_BT_ERROR;
+ }
+
+ /* 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;
+ }
+
+ 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 PY_BT_ERROR;
+
+ /* 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 PY_BT_ERROR;
+
+ return 1;
+ }
+ }
+ else
+ *value = NULL;
+
+ return 1;
+}
+
+static int
+mi_should_print (struct symbol *sym, enum mi_print_types 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 (type == MI_PRINT_ALL)
+ print_me = 1;
+ else if (type == MI_PRINT_LOCALS)
+ print_me = ! SYMBOL_IS_ARGUMENT (sym);
+ else
+ print_me = SYMBOL_IS_ARGUMENT (sym);
+ }
+ return print_me;
+}
+
+/* Helper function which outputs a type name extracted from VAL to a
+ "type" field in the output stream OUT. 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)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ return 1;
+}
+
+/* Helper function which outputs a value to an output field in a
+ stream. OUT is the ui-out structure the value will be output to,
+ VAL is the value that will be printed, OPTS contains the value
+ printing options, ARGS_TYPE is an enumerater describing the
+ argument format, and LANGUAGE is the language_defn that the value
+ will be printed with. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception. */
+
+static int
+py_print_value (struct ui_out *out, struct value *val,
+ const struct value_print_options *opts,
+ enum py_frame_args args_type,
+ const struct language_defn *language)
+{
+ int should_print = 0;
+
+ /* MI does not print certain values, differentiated by type,
+ depending on what ARGS_TYPE indicates. Test type against option.
+ For CLI print all values. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES
+ || args_type == MI_PRINT_ALL_VALUES)
+
+ {
+ struct type *type;
+
+ type = check_typedef (value_type (val));
+ if (args_type == MI_PRINT_ALL_VALUES)
+ should_print = 1;
+ else if (args_type == MI_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
+ if (args_type != MI_PRINT_NO_VALUES)
+ 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)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+ }
+
+ return 1;
+}
+
+/* Helper function to call a Python method and extract an iterator
+ from the result. If the function returns anything but an iterator
+ the exception is preserved and NULL is returned. 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 NULL. */
+
+static PyObject *
+get_py_iter_from_func (PyObject *filter, char *func)
+{
+ PyObject *result = PyObject_CallMethod (filter, func, NULL);
+
+ if (result)
+ {
+ if (result == Py_None)
+ {
+ Py_DECREF (result);
+ Py_RETURN_NONE;
+ }
+ else
+ {
+ PyObject *iterator = PyObject_GetIter (result);
+
+ Py_DECREF (result);
+ return iterator;
+ }
+ }
+
+ return NULL;
+}
+
+/* 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,
+ ARGS_TYPE is an enumerater describing the argument format,
+ PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1"
+ in MI output in commands where both arguments and locals are
+ printed. */
+static int
+py_print_single_arg (struct ui_out *out,
+ const char *sym_name,
+ struct frame_arg *fa,
+ struct value *fv,
+ const struct value_print_options *opts,
+ enum py_frame_args args_type,
+ int print_args_field,
+ const struct language_defn *language)
+{
+ struct value *val;
+ volatile struct gdb_exception except;
+ struct cleanup *cleanups =
+ 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. The
+ exception is -stack-list-variables which emits an ARGS="1" field
+ if the value is a frame argument. This is denoted in this
+ function with PRINT_ARGS_FIELD which is flag from the caller to
+ emit the ARGS field. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ if (print_args_field || args_type != MI_PRINT_NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 ();
+ make_cleanup_ui_file_delete (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_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);
+ }
+ 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_args_field)
+ ui_out_field_int (out, "arg", 1);
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* For MI print the type, but only for simple values. This seems
+ weird, but this is how MI choose to format the various output
+ types. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (! py_print_type (out, val))
+ goto error;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_value (value_type (val));
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* If the output is to the CLI, and the user option "set print
+ frame-arguments" is set to none, just output "...". */
+ if (args_type == CLI_NO_VALUES)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_field_string (out, "value", "...");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else
+ {
+ /* Otherwise, print the value for both MI and the CLI, except
+ for the case of MI_PRINT_NO_VALUES. */
+ if (args_type != MI_PRINT_NO_VALUES)
+ {
+ if (! py_print_value (out, val, opts, args_type, language))
+ goto error;
+ }
+ }
+
+ do_cleanups (cleanups);
+
+ return 1;
+
+ error:
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
+
+/* 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
+ iterable object, OUT is the output stream, ARGS_TYPE is an
+ enumerater describing the argument format, PRINT_ARGS_FIELD is a
+ flag which indicates if we output "ARGS=1" in MI output in commands
+ where both arguments and locals are printed, and FRAME is the
+ backing frame. */
+
+static int
+enumerate_args (PyObject *iter,
+ struct ui_out *out,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ volatile struct gdb_exception except;
+
+ get_user_print_options (&opts);
+
+ if (args_type == CLI_SCALAR_VALUES)
+ {
+ /* True in "summary" mode, false otherwise. */
+ opts.summary = 1;
+ }
+
+ opts.deref_ref = 1;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* 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 = PY_BT_ERROR;
+
+ 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, MI_PRINT_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);
+ gdbpy_convert_exception (except);
+ 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,
+ args_type, print_args_field, NULL);
+
+ if (entryarg.entry_kind != print_entry_values_no)
+ {
+ if (arg.entry_kind != print_entry_values_only)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ ui_out_wrap_hint (out, " ");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ py_print_single_arg (out, NULL, &entryarg, NULL, &opts,
+ args_type, print_args_field, 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,
+ args_type, print_args_field, 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)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else if (PyErr_Occurred ())
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_end ();
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ return 1;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+
+/* Helper function to loop over variables provided by the
+ "frame_locals" Python API. Elements in the iterable must conform
+ to the "Symbol Value" interface. ITER is the Python iterable
+ object, OUT is the output stream, INDENT is whether we should
+ indent the output (for CLI), ARGS_TYPE is an enumerater describing
+ the argument format, PRINT_ARGS_FIELD is flag which indicates
+ whether to output the ARGS field in the case of
+ -stack-list-variables and FRAME is the backing frame. */
+
+static int
+enumerate_locals (PyObject *iter,
+ struct ui_out *out,
+ int indent,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+
+ 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 = PY_BT_ERROR;
+ struct symbol *sym;
+ 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);
+
+ if (sym && ui_out_is_mi_like_p (out) && ! mi_should_print (sym, MI_PRINT_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);
+ gdbpy_convert_exception (except);
+ 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_args_field || args_type != MI_PRINT_NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+ else
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* If the output is not MI we indent locals. */
+ ui_out_spaces (out, (8 + (indent * 2)));
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_field_string (out, "name", sym_name);
+ xfree (sym_name);
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " = ");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (! py_print_type (out, val))
+ goto error;
+ }
+
+ if (args_type != MI_PRINT_NO_VALUES)
+ {
+ py_print_value (out, val, &opts, args_type, language);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, "\n");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ do_cleanups (cleanups);
+ }
+
+ if (! item && PyErr_Occurred ())
+ goto error;
+
+ return 1;
+
+ error:
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for -stack-list-variables. */
+
+static int
+py_mi_print_variables (PyObject *filter, struct ui_out *out,
+ struct value_print_options *opts,
+ enum py_frame_args 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 (! enumerate_args (args_iter, out, args_type, 1, frame))
+ goto error;
+
+ if (! enumerate_locals (locals_iter, out, 1, args_type, 1,
+ frame))
+
+ goto error;
+
+ do_cleanups (old_chain);
+ return 1;
+
+ error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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,
+ enum py_frame_args args_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 (! enumerate_locals (locals_iter, out, indent, args_type,
+ 0, frame))
+ goto locals_error;
+
+ do_cleanups (old_chain);
+ return 1;
+
+ locals_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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,
+ enum py_frame_args 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);
+ volatile struct gdb_exception except;
+
+ if (! args_iter)
+ goto args_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "args");
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " (");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ if (! enumerate_args (args_iter, out, args_type, 0, frame))
+ goto args_error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, ")");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ do_cleanups (old_chain);
+ return 1;
+
+ args_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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. FLAGS is an integer
+ describing the various print options. The FLAGS variables is
+ described in "apply_frame_filter" function. ARGS_TYPE is an
+ enumerater describing the argument format. OUT is the output
+ stream to print, 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 flags, enum py_frame_args 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;
+ int print_level, print_frame_info, print_args, print_locals;
+ PyObject *result;
+ volatile struct gdb_exception except;
+
+ /* 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;
+
+ 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. */
+ 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;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason > 0)
+ {
+ Py_DECREF (result);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ Py_DECREF (result);
+
+ /* stack-list-variables. */
+ if (print_locals && print_args && ! print_frame_info)
+ {
+ if (! py_mi_print_variables (filter, out, &opts,
+ 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)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_spaces (out, indent);
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* 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;
+ volatile struct gdb_exception except;
+
+ slot = (struct frame_info **) htab_find_slot (levels_printed,
+ frame, INSERT);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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)
+ ui_out_field_skip (out, "level");
+ 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 (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_frame_info)
+ {
+ /* Print address to the address field. If an address is not provided,
+ print nothing. */
+ if (opts.addressprint && has_addr)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_address ();
+ ui_out_field_core_addr (out, "addr", gdbarch, address);
+ annotate_frame_address_end ();
+ ui_out_text (out, " in ");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* Print frame function name. */
+ if (PyObject_HasAttrString (filter, "function"))
+ {
+ PyObject *result = PyObject_CallMethod (filter, "function", NULL);
+
+ if (result)
+ {
+ if (result != Py_None)
+ {
+ char *function = PyString_AsString (result);
+
+ if (! function)
+ {
+ Py_DECREF (result);
+ goto error;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_function_name ();
+ ui_out_field_string (out, "func", function);
+ }
+ if (except.reason > 0)
+ {
+ Py_DECREF (result);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ 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, args_type, frame))
+ goto error;
+ }
+
+ /* File name/source/line number information. */
+ if (print_frame_info)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_source_begin ();
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "filename"))
+ {
+ PyObject *result = PyObject_CallMethod (filter, "filename",
+ NULL);
+ if (result)
+ {
+ if (result != Py_None)
+ {
+ char *filename = PyString_AsString (result);
+
+ if (! filename)
+ {
+ Py_DECREF (result);
+ goto error;
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 ();
+ }
+ if (except.reason > 0)
+ {
+ Py_DECREF (result);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ 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);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ":");
+ annotate_frame_source_line ();
+ ui_out_field_int (out, "line", line);
+ }
+ if (except.reason > 0)
+ {
+ Py_DECREF (result);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ 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))
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_end ();
+ ui_out_text (out, "\n");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_locals)
+ {
+ if (! py_print_locals (filter, out, args_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, flags, args_type, out, indent,
+ levels_printed);
+
+ if (success == PY_BT_ERROR && PyErr_Occurred ())
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ }
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* In MI now we can signal the end. */
+ if (ui_out_is_mi_like_p (out))
+ ui_out_text (out, "\n");
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ 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, int
+ frame_low, int frame_high)
+{
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+ PyObject *module, *sort_func, *iterable, *frame_obj, *iterator,
+ *py_frame_low, *py_frame_high;
+
+ frame_obj = frame_info_to_frame_object (frame);
+ if (! frame_obj)
+ return NULL;
+ make_cleanup_py_decref (frame_obj);
+
+ module = PyImport_ImportModule ("gdb.frames");
+ if (! module)
+ goto error;
+ make_cleanup_py_decref (module);
+
+ sort_func = PyObject_GetAttrString (module, "execute_frame_filters");
+ if (! sort_func)
+ goto error;
+ make_cleanup_py_decref (sort_func);
+
+ py_frame_low = PyInt_FromLong (frame_low);
+ if (! py_frame_low)
+ goto error;
+ make_cleanup_py_decref (py_frame_low);
+
+ py_frame_high = PyInt_FromLong (frame_high);
+ if (! py_frame_high)
+ goto error;
+ make_cleanup_py_decref (py_frame_high);
+
+ iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj,
+ py_frame_low,
+ py_frame_high,
+ NULL);
+ do_cleanups (cleanups);
+
+ if (! iterable)
+ return NULL;
+
+ if (iterable != Py_None)
+ {
+ iterator = PyObject_GetIter (iterable);
+ Py_DECREF (iterable);
+ }
+ else
+ Py_RETURN_NONE;
+
+ return iterator;
+
+ error:
+ do_cleanups (cleanups);
+ return NULL;
+}
+
+/* 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 the make-up 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. ARGS_TYPE is an enumerator describing the argument
+ format, OUT is the output stream to print. FRAME_LOW is the
+ beginning of the slice of frames to print, and FRAME_HIGH is the
+ upper limit of the frames to count. */
+
+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+
+{
+ struct gdbarch *gdbarch = NULL;
+ struct cleanup *cleanups;
+ int success = PY_BT_ERROR;
+ PyObject *iterable;
+ volatile struct gdb_exception except;
+ PyObject *item;
+ htab_t levels_printed;
+
+ cleanups = ensure_python_env (gdbarch, current_language);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason > 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ iterable = bootstrap_python_frame_filters (frame, frame_low, frame_high);
+
+ if (! iterable)
+ goto error;
+
+ /* If iterable is None, then there are no 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)
+ {
+ success = PY_BT_NO_FILTERS;
+ goto done;
+ }
+
+ levels_printed = htab_create (20,
+ htab_hash_pointer,
+ htab_eq_pointer,
+ NULL);
+ make_cleanup_htab_delete (levels_printed);
+
+ while ((item = PyIter_Next (iterable)))
+ {
+ success = py_print_frame (item, flags, args_type, out, 0,
+ levels_printed);
+
+ /* Do not exit on error printing a single frame. Print the
+ error and continue with other frames. */
+ if (success == PY_BT_ERROR)
+ gdbpy_print_stack ();
+
+ Py_DECREF (item);
+ }
+
+ done:
+ do_cleanups (cleanups);
+ return success;
+
+ error:
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index db51f50..d3d9f55 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);
Py_TYPE (self)->tp_free (self);
}
@@ -81,6 +84,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -91,6 +101,7 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return (PyObject *) self;
}
+
PyObject *
objfpy_get_printers (PyObject *o, void *ignore)
{
@@ -129,6 +140,47 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this object file. */
+PyObject *
+objfpy_get_frame_filters (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +277,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -270,6 +329,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 45a5193..104b36d 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);
Py_TYPE (self)->tp_free (self);
}
@@ -89,6 +92,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -137,6 +147,47 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this program space. */
+PyObject *
+pspy_get_frame_filters (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +272,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -257,6 +315,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 b280c8c..890b65d 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -48,6 +48,28 @@ 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;
+
+ 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, 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 753ba96..dcb6dd3 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -246,9 +246,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 *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -299,6 +301,7 @@ void gdbpy_initialize_new_objfile_event (void);
void gdbpy_initialize_arch (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 cd8ddfb..5165b56 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1397,6 +1397,14 @@ free_type_printers (void *arg)
{
}
+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+{
+ return PY_BT_NO_FILTERS;
+}
+
#endif /* HAVE_PYTHON */
\f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 24e3077..2f8205e 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,56 @@ 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,
+ };
+
+/* A choice of the different frame argument printing strategies that
+ can occur in different cases of frame filter instantiation. */
+typedef enum py_frame_args
+{
+ /* Print no values for arguments when invoked from the MI. */
+ MI_PRINT_NO_VALUES = PRINT_NO_VALUES,
+
+ MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
+
+ /* Print only simple values (what MI defines as "simple") for
+ arguments when invoked from the MI. */
+ MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES,
+
+ /* Print no values for arguments when invoked from the CLI. */
+ CLI_NO_VALUES,
+
+ /* Print only scalar values for arguments when invoked from the
+ CLI. */
+ CLI_SCALAR_VALUES,
+
+ /* Print all values for arguments when invoked from the
+ CLI. */
+ CLI_ALL_VALUES,
+
+ /* NO_OP for commands that do not print frame arguments */
+ NO_VALUES
+} py_frame_args;
+
extern void finish_python_initialization (void);
void eval_python_from_control_command (struct command_line *);
@@ -41,6 +92,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 py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high);
+
void preserve_python_values (struct objfile *objfile, htab_t copied_types);
void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-03-11 22:13 Phil Muldoon
@ 2013-03-21 20:55 ` Tom Tromey
2013-03-22 17:15 ` Tom Tromey
2013-03-26 17:17 ` Phil Muldoon
0 siblings, 2 replies; 16+ messages in thread
From: Tom Tromey @ 2013-03-21 20:55 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
Phil> This email and patch covers the python/ changes for Python Frame Filters.
Thanks.
On irc you mentioned that performance wasn't great.
I'm wondering if you are working on the frame stash fix.
I think it would be good for that to go in first, so that there isn't a
window where frame filter performance is bad.
Phil> diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py
Phil> + @staticmethod
Phil> + def _is_limited_frame(frame):
Phil> + """Internal utility to determine if the frame is special or
Phil> + limited."""
Phil> + sal = frame.find_sal()
Phil> +
Phil> + if (not sal.symtab or not sal.symtab.filename
Phil> + or frame == gdb.DUMMY_FRAME
Phil> + or frame == gdb.SIGTRAMP_FRAME):
These should compare 'frame.type()'.
Phil> + def function(self):
Phil> + """ Return the name of the frame's function, first determining
Phil> + if it is a special frame. If not, try to determine filename
Phil> + from GDB's frame internal function API. Finally, if a name
Phil> + cannot be determined return the address."""
Phil> +
Phil> + if not isinstance(self.base, gdb.Frame):
Phil> + if hasattr(self.base, "function"):
Phil> + return self.base.function()
If self.base doesn't have "function", is falling through the right thing
to do? It seemed odd to me. If it is correct, I suggest a comment.
Alternatively, why the check for gdb.Frame? Nothing else does this.
Phil> + if func == None:
Phil> + unknown = format(" 0x%08x in" % pc)
Extra space.
Also this seems weird to me. Why not return None and let whatever
prints the frame deal with this detail?
Phil> + if (not sal.symtab or not sal.symtab.filename):
Extra parens.
Phil> + return {
Phil> + gdb.SYMBOL_LOC_STATIC: True,
Phil> + gdb.SYMBOL_LOC_REGISTER: True,
Phil> + gdb.SYMBOL_LOC_ARG: True,
Phil> + gdb.SYMBOL_LOC_REF_ARG: True,
Phil> + gdb.SYMBOL_LOC_LOCAL: True,
Phil> + gdb.SYMBOL_LOC_REGPARM_ADDR: True,
Phil> + gdb.SYMBOL_LOC_COMPUTED: True
Phil> + }.get(sym_type, False)
Does this make a new hash each time through?
(I don't know.)
If so let me suggest using a global frozenset instead.
Phil> + def fetch_frame_locals(self):
Phil> + """Public utility method to fetch frame local variables for
Phil> + the stored frame. Frame arguments are not fetched. If there
Phil> + are no frame local variables, return an empty list."""
Phil> + lvars = []
Phil> + try:
Phil> + block = self.frame.block()
Phil> + except:
Phil> + return None
Phil> +
Phil> + for sym in block:
Phil> + if sym.is_argument:
Phil> + continue;
Phil> + if self.fetch_b(sym):
Phil> + lvars.append(SymValueWrapper(sym, None))
Phil> +
Phil> + return lvars
FrameDecorator.frame_locals claims to return an iterator but this
returns an array.
I think it is better to update the docs to say it returns an iterable.
This probably applies elsewhere too.
This could return a real iterator rather than an array.
That might conceivably matter for frames with many variables.
Doesn't this need to traverse the blocks upward until it finds the
function's outermost block?
Phil> + def fetch_frame_args(self):
Doesn't this one also need to find the function's outermost block?
A test case with a breakpoint inside a nested block would show this.
Phil> + def get_value(self, sym, block):
Is this function ever used?
Phil> + """Public utility method to fetch a value from a symbol."""
Phil> + if len(sym.linkage_name):
Phil> + nsym, is_field_of_this = gdb.lookup_symbol(sym.linkage_name, block)
Phil> + if nsym != None:
Phil> + if nsym.addr_class != gdb.SYMBOL_LOC_REGISTER:
Phil> + sym = nsym
I wonder if this is really needed.
Phil> + if len(sorted_frame_filters) == 0:
Phil> + print " No frame filters registered."
I think we have to use print(...) now, for Python 3 compatibility.
Phil> + if argv[0] == "all" and argc > 1:
Phil> + raise gdb.GdbError(cmd_name + " with 'all' " \
Phil> + "you may not specify a filter.")
It seems like there should be a ":" in the output after the command
name.
Phil> + if all_flag == True:
Phil> + filter_locations = ["all", "global","progspace"]
Missing space after the second comma.
Phil> + else:
Phil> + filter_locations = ["global","progspace"]
Space.
Phil> +
Phil> + # If we only have one completion, complete it and return it.
Phil> + if len(flist) == 1:
Phil> + flist[0] = flist[0][len(text)-len(word):]
Phil> +
Phil> + # Otherwise, return an empty list, or a list of frame filter
I don't understand why the one completion branch chops up the string,
but the other branch doesn't.
Phil> + # dictioanries that the previous filter operation returned.
Typo, "dictionaries".
Phil> + def complete(self,text,word):
Spaces. There are more of these.
Phil> +class SetFrameFilterPriority(gdb.Command):
Phil> + """GDB command to set the priority of the specified frame-filter.
Phil> +
Phil> + Usage: set python frame-filter priority dictionary name priority
I think the variable parts should be in all caps here.
I found the duplication of "priority" confusing...
Using caps here is a GNU convention, so I think at least all the
user-facing doc strings should be updated.
Phil> + PRIORITY is the new priority to set the frame filter.
I think this rewording.
Phil> + try:
Phil> + self._set_filter_priority(command_tuple)
Phil> + except gdb.GdbError as e:
Phil> + # Print the error, instead of raising it.
Phil> + gdb.write(e.message+"\n")
I think a GdbError should already be handled this way.
See (info "(gdb) Exception Handling").
Phil> +import gdb.command.frame_filters as ffc
I think the 'command' package should be a leaf.
If you're importing things from there, it is a sign that they should be
in this module instead (gdb.frames).
Also, I think we have a rule against renaming on import like this.
Phil> +def execute_frame_filters(frame, frame_low, frame_high):
If this is called by the gdb core, there should at least be a comment to
that effect.
Phil> + frame_high: The high range of the slice. If this is a negative
Phil> + integer then it indicates all frames until the end of the
Phil> + stack from frame_low.
This disagrees with the code:
Phil> + # -1 for frame_high means until the end of the backtrace. Set to
Phil> + # None if that is the case, to indicate to itertools.islice to
Phil> + # slice to the end of the iterator.
Phil> + if frame_high == -1:
Phil> + frame_high = None
I think one or the other should change.
Phil> + else:
Phil> + # The end argument for islice is a count, not a position, so
Phil> + # add one as frames start at zero.
Phil> + frame_high = frame_high + 1;
I think it isn't a "count", since otherwise you'd have to subtract
frame_low.
Phil> +typedef enum mi_print_types
Phil> +{
Phil> + MI_PRINT_ALL,
Phil> + MI_PRINT_ARGS,
Phil> + MI_PRINT_LOCALS
Phil> +} mi_print_types;
I don't think this is ever used as a typedef, so I suggest dropping the
"typedef" part.
I didn't see any uses of MI_PRINT_ALL.
Phil> + /* For 'symbol' callback, the function can return a symbol or a
Phil> + string. */
Phil> + if (PyString_Check (result))
I think you want gdbpy_is_string here.
Phil> + *language = python_language;
I have a suspicion that this can cause weird behavior.
Shouldn't each frame carry its language with it?
At least to some extent? Or else could you explain it more?
If it is really ok, then it deserves a comment.
Phil> + if (language_mode == language_mode_auto)
Phil> + *language = language_def (SYMBOL_LANGUAGE (*sym));
Phil> + else
Phil> + *language = current_language;
Why current_language and not python_language here?
If this is correct then why not the same above?
Phil> + }
Phil> +
Phil> + return 1;
This function returns PY_BT_ERROR in other places.
It should use PY_BT_ values for all the returns.
PY_BT_* should be in an enum and that should be used instead of int.
I will note it again at that spot.
Phil> +static int
Phil> +extract_value (PyObject *obj, struct value **value)
This is another int / PY_BT_ function:
Phil> + return PY_BT_ERROR;
[...]
Phil> + return 1;
There are more of these, like py_print_type and py_print_value.
Phil> +
Phil> +static int
Phil> +mi_should_print (struct symbol *sym, enum mi_print_types type)
Needs an intro comment.
Phil> +static int
Phil> +py_print_value (struct ui_out *out, struct value *val,
Phil> + const struct value_print_options *opts,
Phil> + enum py_frame_args args_type,
Phil> + const struct language_defn *language)
[...]
Phil> + type = check_typedef (value_type (val));
It seems like this has to be wrapped in a TRY_CATCH.
Isn't one of the callers in a Python context?
And if not, there's no need to bother with TRY_CATCH and exception
conversion elsewhere in this function.
Phil> + else
Phil> + if (args_type != MI_PRINT_NO_VALUES)
Phil> + should_print = 1;
Wrong formatting here, should be "else if ...".
Phil> +static PyObject *
Phil> +get_py_iter_from_func (PyObject *filter, char *func)
Phil> +{
Phil> + PyObject *result = PyObject_CallMethod (filter, func, NULL);
Phil> +
Phil> + if (result)
New style, if (result != NULL)
Phil> + {
Phil> + if (result == Py_None)
Phil> + {
Phil> + Py_DECREF (result);
Phil> + Py_RETURN_NONE;
Phil> + }
You can just 'return result' here and avoid the reference counting
business.
Phil> + if (fa)
fa != NULL
There's a few of these... unfortunately the coding standard changed
between your patch submissions.
Phil> + if (! success)
I think this should be checking against PY_BT_ERROR.
And likewise elsewhere.
Phil> + if (sym && ui_out_is_mi_like_p (out) && ! mi_should_print (sym, MI_PRINT_ARGS))
Phil> + continue;
'sym != NULL', line is too long, and I think the "continue" leaks
sym_name.
Phil> + TRY_CATCH (except, RETURN_MASK_ALL)
Phil> + {
Phil> + read_frame_arg (sym, frame, &arg, &entryarg);
Phil> + }
Phil> + if (except.reason > 0)
I think this should be except.reason < 0.
This occurs in a number of places.
Phil> + if (arg.entry_kind != print_entry_values_only)
Phil> + py_print_single_arg (out, NULL, &arg, NULL, &opts,
Phil> + args_type, print_args_field, NULL);
What if py_print_single_arg returns PY_BT_ERROR?
Phil> + py_print_single_arg (out, NULL, &entryarg, NULL, &opts,
Phil> + args_type, print_args_field, NULL);
Likewise.
Phil> + {
Phil> + /* If the object has provided a value, we just print that. */
Phil> + if (val)
Phil> + py_print_single_arg (out, sym_name, NULL, val, &opts,
Phil> + args_type, print_args_field, language);
The "!= NULL" thing, plus the error checking thing.
There are many more instances of the missing NULL check.
Phil> + if (item)
Phil> + {
Phil> + TRY_CATCH (except, RETURN_MASK_ALL)
Phil> + {
Phil> + ui_out_text (out, ", ");
Phil> + }
Phil> + if (except.reason > 0)
Phil> + {
Phil> + gdbpy_convert_exception (except);
Phil> + goto error;
This can leak 'item' on exception.
Phil> + TRY_CATCH (except, RETURN_MASK_ALL)
Phil> + {
Phil> + annotate_arg_end ();
Phil> + }
Phil> + if (except.reason > 0)
Phil> + {
Phil> + gdbpy_convert_exception (except);
Phil> + goto error;
Phil> + }
Here too.
Phil> +static int
Phil> +enumerate_locals (PyObject *iter,
Phil> + struct ui_out *out,
Phil> + int indent,
Phil> + enum py_frame_args args_type,
Phil> + int print_args_field,
Phil> + struct frame_info *frame)
This sure has a lot of overlap with enumerate_args.
Phil> + if (sym && ui_out_is_mi_like_p (out) && ! mi_should_print (sym, MI_PRINT_LOCALS))
Phil> + continue;
Same issues as above.
Phil> + TRY_CATCH (except, RETURN_MASK_ALL)
Phil> + {
Phil> + ui_out_field_string (out, "name", sym_name);
Phil> + xfree (sym_name);
This will leak sym_name if ui_out_field_string throws.
Phil> + if (args_type != MI_PRINT_NO_VALUES)
Phil> + {
Phil> + py_print_value (out, val, &opts, args_type, language);
Phil> + }
Error checking.
Phil> + do_cleanups (cleanups);
Phil> + }
I think this is calling do_cleanups in the loop, but initializing it
outside the loop. This should crash if there is more than one local.
Phil> +static int
Phil> +py_print_frame (PyObject *filter, int flags, enum py_frame_args args_type,
Phil> + struct ui_out *out, int indent, htab_t levels_printed)
Phil> + PyObject *result = PyObject_CallMethod (filter, "function", NULL);
This shadows a variable named 'result' in the outer scope.
I'd prefer to avoid shadowing.
This occurs in a few spots. You could rename the outer variable if you
prefer.
Phil> + slot = (struct frame_info **) htab_find_slot (levels_printed,
Phil> + frame, INSERT);
[...]
Phil> + if (*slot != NULL && (*slot) == frame)
I think just *slot == frame suffices here. Can we really have
FRAME==NULL? If we can then I think this code won't really work right,
since I think you can't store a NULL into an htab.
Phil> + line = PyLong_AsLong (result);
Extra space.
Phil> + /* For MI we need to deal with the "children" list population of
Phil> + elided frames, so if MI output detected do not send newline. */
MI ignores ui_out_text, so you don't need the check.
Anyway the next thing printed is the locals, so this comment seems
wrong.
Phil> + /* Finally recursively print elided frames, if any. */
Phil> + elided = get_py_iter_from_func (filter, "elided");
Phil> + if (! elided)
Phil> + goto error;
I didn't notice this before, but get_py_iter_from_func doesn't check to
see if the object has the indicated method. The other spots that call
PyObject_CallMethod do this check.
Phil> + if (elided != Py_None)
Phil> + {
Phil> + PyObject *item;
Phil> +
Phil> + make_cleanup_py_decref (elided);
This leaks elided in the case where it is Py_None.
You can just hoist the cleanup.
Phil> + while ((item = PyIter_Next (elided)))
There is no error check after the loop exits, but there should be.
Phil> + {
Phil> + int success = py_print_frame (item, flags, args_type, out, indent,
Phil> + levels_printed);
Extra space after "=".
Phil> + TRY_CATCH (except, RETURN_MASK_ALL)
Phil> + {
Phil> + /* In MI now we can signal the end. */
Phil> + if (ui_out_is_mi_like_p (out))
Phil> + ui_out_text (out, "\n");
Phil> + }
Phil> + if (except.reason > 0)
Phil> + {
Phil> + gdbpy_convert_exception (except);
Phil> + goto error;
Phil> + }
I don't think this does anything.
See mi-out.c:mi_text.
Phil> +static PyObject *
Phil> +bootstrap_python_frame_filters (struct frame_info *frame, int
Phil> + frame_low, int frame_high)
Wrong spot for a line break, should be before the "int".
Phil> + PyObject *module, *sort_func, *iterable, *frame_obj, *iterator,
Phil> + *py_frame_low, *py_frame_high;
I'd prefer a ";" at the end of the first line and "PyObject" to be
repeated.
Phil> + frame_obj = frame_info_to_frame_object (frame);
Phil> + if (! frame_obj)
Phil> + return NULL;
This leaves a dangling cleanup.
It should be 'goto error'.
Phil> + if (iterable != Py_None)
Phil> + {
Phil> + iterator = PyObject_GetIter (iterable);
Phil> + Py_DECREF (iterable);
Phil> + }
Phil> + else
Phil> + Py_RETURN_NONE;
This leaves a dangling ref in the Py_None case.
You have to always decref iterable.
Phil> +int apply_frame_filter (struct frame_info *frame, int flags,
Line break after the first "int".
Phil> + while ((item = PyIter_Next (iterable)))
This loop needs an error check after it.
Phil> @@ -91,6 +101,7 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Phil> return (PyObject *) self;
Phil> }
Phil> +
Phil> PyObject *
Phil> objfpy_get_printers (PyObject *o, void *ignore)
Phil> {
Spurious newline addition.
Phil> +int apply_frame_filter (struct frame_info *frame, int flags,
Newline after first "int".
Phil> +/* Python frame-filter status returns constants. */
Phil> +static const int PY_BT_ERROR = 0;
Phil> +static const int PY_BT_COMPLETED = 1;
Phil> +static const int PY_BT_NO_FILTERS = 2;
I think this should be an enum.
Phil> + MI_PRINT_NO_VALUES = PRINT_NO_VALUES,
Surprising that this is different from CLI_NO_VALUES.
Phil> + MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
This one doesn't seem to be any different from the CLI variant.
If so, please merge them. We may not be able to tame the insanity but
we can reduce it a little bit... :)
Phil> + /* NO_OP for commands that do not print frame arguments */
Phil> + NO_VALUES
I didn't see this one used.
It seems horrible to have 3 options for "no values".
Phil> +} py_frame_args;
I think the typedef can be dropped.
AFAICT the whole patch uses the enum tag.
thanks,
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-03-21 20:55 ` Tom Tromey
@ 2013-03-22 17:15 ` Tom Tromey
2013-03-26 17:17 ` Phil Muldoon
1 sibling, 0 replies; 16+ messages in thread
From: Tom Tromey @ 2013-03-22 17:15 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
Phil> + else:
Phil> + # The end argument for islice is a count, not a position, so
Phil> + # add one as frames start at zero.
Phil> + frame_high = frame_high + 1;
Tom> I think it isn't a "count", since otherwise you'd have to subtract
Tom> frame_low.
Ok, I looked at the CLI patch and it seems to be passing in "count"
here. So maybe this note can be addressed by renaming the parameter.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-03-21 20:55 ` Tom Tromey
2013-03-22 17:15 ` Tom Tromey
@ 2013-03-26 17:17 ` Phil Muldoon
2013-03-27 18:36 ` Tom Tromey
1 sibling, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2013-03-26 17:17 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 21/03/13 20:55, Tom Tromey wrote:
>
> On irc you mentioned that performance wasn't great.
> I'm wondering if you are working on the frame stash fix.
> I think it would be good for that to go in first, so that there isn't a
> window where frame filter performance is bad.
Frame printing is about 20% slower, and that is really only noticeable
on backtraces of 10,000 frames or more. Frame filtering and
application of decorators is instant. So performance is comparable in
all but the most demanding of situations.
I think a frame stash using a hash table is doable, but I think it is
overkill because I am extremely suspicious of the frame to frame
object code. As we get the older() and newer() frames we invalidate
the frame stash. The reason for getting the two nearest older and
newer frames is dubious in the extreme. A frame will always have a
frame level - that is GDB assigned. Even completely busted inferior
frames, will be properly encapsulated in a GDB frame, and, have a
level assigned.
The only other scenario I can think of for the logic is if GDB itself
has encountered corruption in the call stack and this is to preserve
the last place. But at that point why are we hiding that from the
user? Things are likely going to fail, and fail badly, soon. My
proposal is just to use the existing find_frame_by_id function and not
bother with this other stuff. That will stop invalidating the frame
stash and likely solve the performance issue.
Cheers,
Phil
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-03-26 17:17 ` Phil Muldoon
@ 2013-03-27 18:36 ` Tom Tromey
0 siblings, 0 replies; 16+ messages in thread
From: Tom Tromey @ 2013-03-27 18:36 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> I think a frame stash using a hash table is doable, but I think it is
Phil> overkill because I am extremely suspicious of the frame to frame
Phil> object code.
I assume you are going to fix the frame to frame object code instead?
And this will fix the 20% penalty?
I'm not totally sure it should be "instead" rather than "as well as",
since it seems like some gdb.Frame access patterns will have bad
behavior with the current stash.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch][python] 1 of 5 - Frame filter Python C code changes.
@ 2013-04-22 16:01 Phil Muldoon
2013-04-26 6:47 ` Tom Tromey
0 siblings, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2013-04-22 16:01 UTC (permalink / raw)
To: gdb-patches
This email and patch covers the python/ changes for Python Frame Filters.
2013-04-22 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/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.
--
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..8791a91
--- /dev/null
+++ b/gdb/python/lib/gdb/FrameIterator.py
@@ -0,0 +1,53 @@
+# 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 __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/__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..e1800f6
--- /dev/null
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -0,0 +1,456 @@
+# 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:
+ raise gdb.GdbError("set frame-filter priority " \
+ "takes exactly three arguments.")
+
+ 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)
+ 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:
+ raise gdb.GdbError("show frame-filter priority " \
+ "takes exactly two arguments.")
+
+ 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)
+ 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..2287181
--- /dev/null
+++ b/gdb/python/lib/gdb/frames.py
@@ -0,0 +1,235 @@
+# 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.
+ if hasattr(filter_item, "priority"):
+ return filter_item.priority
+ else:
+ return 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.
+ if hasattr(filter_item, "enabled"):
+ return filter_item.enabled
+ else:
+ return 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
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
new file mode 100644
index 0000000..95cc2f2
--- /dev/null
+++ b/gdb/python/py-framefilter.c
@@ -0,0 +1,1526 @@
+/* Python frame filters
+
+ Copyright (C) 2013 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 "demangle.h"
+#include "mi/mi-cmds.h"
+#include "python-internal.h"
+
+enum mi_print_types
+{
+ MI_PRINT_ARGS,
+ MI_PRINT_LOCALS
+};
+
+/* Helper function to extract a symbol, a name and a 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 enum py_bt_status
+extract_sym (PyObject *obj, char **name, struct symbol **sym,
+ const struct language_defn **language)
+{
+ PyObject *result = PyObject_CallMethod (obj, "symbol", NULL);
+
+ if (result == NULL)
+ return PY_BT_ERROR;
+
+ /* For 'symbol' callback, the function can return a symbol or a
+ string. */
+ if (gdbpy_is_string (result))
+ {
+ *name = python_string_to_host_string (result);
+ Py_DECREF (result);
+
+ if (*name == NULL)
+ return PY_BT_ERROR;
+ /* If the API returns a string (and not a symbol), then there is
+ no symbol derived language available and the frame filter has
+ either overridden the symbol with a string, or supplied a
+ entirely synthetic symbol/value pairing. In that case, use
+ python_language. */
+ *language = python_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 == NULL)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Unexpected value. Expecting a "
+ "gdb.Symbol or a Python string."));
+ return PY_BT_ERROR;
+ }
+
+ /* Duplicate the symbol name, so the caller has consistency
+ in garbage collection. */
+ *name = xstrdup (SYMBOL_PRINT_NAME (*sym));
+
+ /* If a symbol is specified attempt to determine the language
+ from the symbol. If mode is not "auto", then the language
+ has been explicitly set, use that. */
+ if (language_mode == language_mode_auto)
+ *language = language_def (SYMBOL_LANGUAGE (*sym));
+ else
+ *language = current_language;
+ }
+
+ return PY_BT_OK;
+}
+
+/* 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 enum py_bt_status
+extract_value (PyObject *obj, struct value **value)
+{
+ if (PyObject_HasAttrString (obj, "value"))
+ {
+ PyObject *vresult = PyObject_CallMethod (obj, "value", NULL);
+
+ if (vresult == NULL)
+ return PY_BT_ERROR;
+
+ /* 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 PY_BT_OK;
+ }
+ else
+ {
+ *value = convert_value_from_python (vresult);
+ Py_DECREF (vresult);
+
+ if (*value == NULL)
+ return PY_BT_ERROR;
+
+ return 1;
+ }
+ }
+ else
+ *value = NULL;
+
+ return PY_BT_OK;
+}
+
+/* MI prints only certain values according to the type of symbol and
+ also what the user has specified. SYM is the symbol to check, and
+ MI_PRINT_TYPES is an enum specifying what the user wants emitted
+ for the MI command in question. */
+static int
+mi_should_print (struct symbol *sym, enum mi_print_types 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 (type == MI_PRINT_LOCALS)
+ print_me = ! SYMBOL_IS_ARGUMENT (sym);
+ else
+ print_me = SYMBOL_IS_ARGUMENT (sym);
+ }
+ return print_me;
+}
+
+/* Helper function which outputs a type name extracted from VAL to a
+ "type" field in the output stream OUT. 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 enum py_bt_status
+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)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function which outputs a value to an output field in a
+ stream. OUT is the ui-out structure the value will be output to,
+ VAL is the value that will be printed, OPTS contains the value
+ printing options, ARGS_TYPE is an enumerater describing the
+ argument format, and LANGUAGE is the language_defn that the value
+ will be printed with. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception. */
+
+static enum py_bt_status
+py_print_value (struct ui_out *out, struct value *val,
+ const struct value_print_options *opts,
+ int indent,
+ enum py_frame_args args_type,
+ const struct language_defn *language)
+{
+ int should_print = 0;
+ volatile struct gdb_exception except;
+ int local_indent = (4 * indent);
+
+ /* Never set an indent level for common_val_print if MI. */
+ if (ui_out_is_mi_like_p (out))
+ local_indent = 0;
+
+ /* MI does not print certain values, differentiated by type,
+ depending on what ARGS_TYPE indicates. Test type against option.
+ For CLI print all values. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES
+ || args_type == MI_PRINT_ALL_VALUES)
+
+ {
+ struct type *type = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = check_typedef (value_type (val));
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ if (args_type == MI_PRINT_ALL_VALUES)
+ should_print = 1;
+ else if (args_type == MI_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 if (args_type != NO_VALUES)
+ should_print = 1;
+
+ if (should_print)
+ {
+ 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, indent, opts, language);
+ ui_out_field_stream (out, "value", stb);
+ do_cleanups (cleanup);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function to call a Python method and extract an iterator
+ from the result. If the function returns anything but an iterator
+ the exception is preserved and NULL is returned. 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 NULL. */
+
+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 != NULL)
+ {
+ if (result == Py_None)
+ {
+ return result;
+ }
+ else
+ {
+ PyObject *iterator = PyObject_GetIter (result);
+
+ Py_DECREF (result);
+ return iterator;
+ }
+ }
+ }
+ else
+ Py_RETURN_NONE;
+
+ return NULL;
+}
+
+/* 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,
+ ARGS_TYPE is an enumerater describing the argument format,
+ PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1"
+ in MI output in commands where both arguments and locals are
+ printed. */
+static enum py_bt_status
+py_print_single_arg (struct ui_out *out,
+ const char *sym_name,
+ struct frame_arg *fa,
+ struct value *fv,
+ const struct value_print_options *opts,
+ enum py_frame_args args_type,
+ int print_args_field,
+ const struct language_defn *language)
+{
+ struct value *val;
+ volatile struct gdb_exception except;
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+
+ if (fa != NULL)
+ {
+ 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. The
+ exception is -stack-list-variables which emits an ARGS="1" field
+ if the value is a frame argument. This is denoted in this
+ function with PRINT_ARGS_FIELD which is flag from the caller to
+ emit the ARGS field. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ if (print_args_field || args_type != NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_begin ();
+
+ /* If frame argument is populated, check for entry-values and the
+ entry value options. */
+ if (fa != NULL)
+ {
+ struct ui_file *stb;
+
+ stb = mem_fileopen ();
+ make_cleanup_ui_file_delete (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_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);
+ }
+ 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_args_field)
+ ui_out_field_int (out, "arg", 1);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* For MI print the type, but only for simple values. This seems
+ weird, but this is how MI choose to format the various output
+ types. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (! py_print_type (out, val))
+ goto error;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_value (value_type (val));
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* 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) && args_type == NO_VALUES)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_field_string (out, "value", "...");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else
+ {
+ /* Otherwise, print the value for both MI and the CLI, except
+ for the case of MI_PRINT_NO_VALUES. */
+ if (args_type != NO_VALUES)
+ {
+ if (! py_print_value (out, val, opts, 0, args_type, language))
+ goto error;
+ }
+ }
+
+ do_cleanups (cleanups);
+
+ return PY_BT_OK;
+
+ error:
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
+
+/* 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
+ iterable object, OUT is the output stream, ARGS_TYPE is an
+ enumerater describing the argument format, PRINT_ARGS_FIELD is a
+ flag which indicates if we output "ARGS=1" in MI output in commands
+ where both arguments and locals are printed, and FRAME is the
+ backing frame. */
+
+static enum py_bt_status
+enumerate_args (PyObject *iter,
+ struct ui_out *out,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ volatile struct gdb_exception except;
+
+ get_user_print_options (&opts);
+
+ if (args_type == CLI_SCALAR_VALUES)
+ {
+ /* True in "summary" mode, false otherwise. */
+ opts.summary = 1;
+ }
+
+ opts.deref_ref = 1;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* 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 == NULL && PyErr_Occurred ())
+ goto error;
+
+ while (item)
+ {
+ const struct language_defn *language;
+ char *sym_name;
+ struct symbol *sym;
+ struct value *val;
+ int success = PY_BT_ERROR;
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ 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, MI_PRINT_ARGS))
+ {
+ xfree (sym_name);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it using
+ read_frame_args and account for entry values, if any. */
+ if (val == NULL)
+ {
+ struct frame_arg arg, entryarg;
+
+ /* If there is no value, and also no symbol, set error and
+ exit. */
+ if (sym == NULL)
+ {
+ 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);
+ gdbpy_convert_exception (except);
+ 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)
+ {
+ if (py_print_single_arg (out, NULL, &arg,
+ NULL, &opts,
+ args_type,
+ print_args_field,
+ NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ if (entryarg.entry_kind != print_entry_values_no)
+ {
+ if (arg.entry_kind != print_entry_values_only)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ ui_out_wrap_hint (out, " ");
+ }
+ if (except.reason < 0)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (py_print_single_arg (out, NULL, &entryarg, NULL,
+ &opts, args_type,
+ print_args_field, NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ xfree (arg.error);
+ xfree (entryarg.error);
+ }
+ else
+ {
+ /* If the object has provided a value, we just print that. */
+ if (val != NULL)
+ {
+ if (py_print_single_arg (out, sym_name, NULL, val, &opts,
+ args_type, print_args_field,
+ language) == PY_BT_ERROR)
+ {
+ xfree (sym_name);
+ goto error;
+ }
+ }
+ }
+
+ 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 != NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else if (PyErr_Occurred ())
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_end ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+
+/* Helper function to loop over variables provided by the
+ "frame_locals" Python API. Elements in the iterable must conform
+ to the "Symbol Value" interface. ITER is the Python iterable
+ object, OUT is the output stream, INDENT is whether we should
+ indent the output (for CLI), ARGS_TYPE is an enumerater describing
+ the argument format, PRINT_ARGS_FIELD is flag which indicates
+ whether to output the ARGS field in the case of
+ -stack-list-variables and FRAME is the backing frame. */
+
+static enum py_bt_status
+enumerate_locals (PyObject *iter,
+ struct ui_out *out,
+ int indent,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+
+ 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 = PY_BT_ERROR;
+ struct symbol *sym;
+ volatile struct gdb_exception except;
+ int local_indent = 8 + (8 * indent);
+
+ make_cleanup (null_cleanup, NULL);
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ xfree (sym_name);
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+
+ if (sym != NULL && ui_out_is_mi_like_p (out)
+ && ! mi_should_print (sym, MI_PRINT_LOCALS))
+ {
+ xfree (sym_name);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it. */
+ if (val == NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ val = read_var_value (sym, frame);
+ }
+ if (except.reason < 0)
+ {
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ 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_args_field || args_type != NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+ else
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* If the output is not MI we indent locals. */
+ ui_out_spaces (out, local_indent);
+ }
+ if (except.reason < 0)
+ {
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_field_string (out, "name", sym_name);
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " = ");
+ }
+ if (except.reason < 0)
+ {
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ xfree (sym_name);
+
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (! py_print_type (out, val))
+ goto error;
+ }
+
+ /* CLI always prints values for locals. MI uses the
+ simple/no/all system. */
+ if (! ui_out_is_mi_like_p (out))
+ {
+ int val_indent = (indent + 1) * 4;
+
+ if (py_print_value (out, val, &opts, val_indent, args_type,
+ language) == PY_BT_ERROR)
+ goto error;
+ }
+ else
+ {
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, &opts, 0, args_type,
+ language) == PY_BT_ERROR)
+ goto error;
+ }
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ do_cleanups (cleanups);
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ return PY_BT_OK;
+
+ error:
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for -stack-list-variables. */
+
+static enum py_bt_status
+py_mi_print_variables (PyObject *filter, struct ui_out *out,
+ struct value_print_options *opts,
+ enum py_frame_args 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 == NULL)
+ goto error;
+
+ locals_iter = get_py_iter_from_func (filter, "frame_locals");
+ if (locals_iter == NULL)
+ 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, args_type, 1, frame))
+ goto error;
+
+ if (locals_iter != Py_None)
+ if (! enumerate_locals (locals_iter, out, 1, args_type, 1,
+ frame))
+ goto error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing locals. This function largely just
+ creates the wrapping tuple, and calls enumerate_locals. */
+
+static enum py_bt_status
+py_print_locals (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args args_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 == NULL)
+ goto locals_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "locals");
+
+ if (locals_iter != Py_None)
+ if (! enumerate_locals (locals_iter, out, indent, args_type,
+ 0, frame))
+ goto locals_error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;;
+
+ locals_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing frame arguments. This function
+ largely just creates the wrapping tuple, and calls
+ enumerate_args. */
+
+static enum py_bt_status
+py_print_args (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args 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);
+ volatile struct gdb_exception except;
+
+ if (args_iter == NULL)
+ goto args_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "args");
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " (");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ if (args_iter != Py_None)
+ if (! enumerate_args (args_iter, out, args_type, 0, frame))
+ goto args_error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, ")");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ args_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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. FLAGS is an integer
+ describing the various print options. The FLAGS variables is
+ described in "apply_frame_filter" function. ARGS_TYPE is an
+ enumerater describing the argument format. OUT is the output
+ stream to print, 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 enum py_bt_status
+py_print_frame (PyObject *filter, int flags, enum py_frame_args 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 *py_inf_frame, *elided;
+ int print_level, print_frame_info, print_args, print_locals;
+ volatile struct gdb_exception except;
+
+ /* 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;
+
+ 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. */
+ py_inf_frame = PyObject_CallMethod (filter, "inferior_frame", NULL);
+ if (py_inf_frame == NULL)
+ goto error;
+
+ frame = frame_object_to_frame_info (py_inf_frame);;
+
+ Py_DECREF (py_inf_frame);
+
+ if (frame == NULL)
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+
+ /* stack-list-variables. */
+ if (print_locals && print_args && ! print_frame_info)
+ {
+ if (! py_mi_print_variables (filter, out, &opts,
+ 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)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_spaces (out, indent*4);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* 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 != NULL)
+ {
+ 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;
+ volatile struct gdb_exception except;
+
+ slot = (struct frame_info **) htab_find_slot (levels_printed,
+ frame, INSERT);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 == frame)
+ ui_out_field_skip (out, "level");
+ 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 (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_frame_info)
+ {
+ /* Print address to the address field. If an address is not provided,
+ print nothing. */
+ if (opts.addressprint && has_addr)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_address ();
+ ui_out_field_core_addr (out, "addr", gdbarch, address);
+ annotate_frame_address_end ();
+ ui_out_text (out, " in ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* Print frame function name. */
+ if (PyObject_HasAttrString (filter, "function"))
+ {
+ PyObject *py_func = PyObject_CallMethod (filter, "function", NULL);
+
+ if (py_func != NULL)
+ {
+ const char *function = NULL;
+
+ if (gdbpy_is_string (py_func))
+ {
+ function = PyString_AsString (py_func);
+
+ if (function == NULL)
+ {
+ Py_DECREF (py_func);
+ goto error;
+ }
+ }
+ else if (PyLong_Check (py_func))
+ {
+ CORE_ADDR addr = PyLong_AsUnsignedLongLong (py_func);
+ struct bound_minimal_symbol msymbol;
+
+ if (PyErr_Occurred ())
+ goto error;
+
+ msymbol = lookup_minimal_symbol_by_pc (addr);
+ if (msymbol.minsym != NULL)
+ function = SYMBOL_PRINT_NAME (msymbol.minsym);
+ }
+ else if (py_func != Py_None)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("FrameDecorator.function: expecting a " \
+ "String, integer or None."));
+ Py_DECREF (py_func);
+ goto error;
+ }
+
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_function_name ();
+ if (function == NULL)
+ ui_out_field_skip (out, "func");
+ else
+ ui_out_field_string (out, "func", function);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_func);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_func);
+ }
+ else
+ goto error;
+ }
+
+
+ /* Frame arguments. Check the result, and error if something went
+ wrong. */
+ if (print_args)
+ {
+ if (! py_print_args (filter, out, args_type, frame))
+ goto error;
+ }
+
+ /* File name/source/line number information. */
+ if (print_frame_info)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_source_begin ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "filename"))
+ {
+ PyObject *py_fn = PyObject_CallMethod (filter, "filename",
+ NULL);
+ if (py_fn != NULL)
+ {
+ if (py_fn != Py_None)
+ {
+ char *filename = PyString_AsString (py_fn);
+
+ if (filename == NULL)
+ {
+ Py_DECREF (py_fn);
+ goto error;
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_fn);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_fn);
+ }
+ else
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "line"))
+ {
+ PyObject *py_line = PyObject_CallMethod (filter, "line", NULL);
+ int line;
+
+ if (py_line != NULL)
+ {
+ if (py_line != Py_None)
+ {
+ line = PyLong_AsLong (py_line);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ":");
+ annotate_frame_source_line ();
+ ui_out_field_int (out, "line", line);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_line);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_line);
+ }
+ 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))
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_end ();
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_locals)
+ {
+ if (! py_print_locals (filter, out, args_type, indent,
+ frame))
+ goto error;
+ }
+
+ /* Finally recursively print elided frames, if any. */
+ elided = get_py_iter_from_func (filter, "elided");
+ if (elided == NULL)
+ goto error;
+
+ make_cleanup_py_decref (elided);
+ if (elided != Py_None)
+ {
+ PyObject *item;
+
+ make_cleanup_ui_out_list_begin_end (out, "children");
+
+ if (! ui_out_is_mi_like_p (out))
+ indent++;
+
+ while ((item = PyIter_Next (elided)))
+ {
+ int success = py_print_frame (item, flags, args_type, out, indent,
+ levels_printed);
+
+ if (success == PY_BT_ERROR && PyErr_Occurred ())
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ }
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+ }
+
+
+ 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,
+ int frame_low, int frame_high)
+{
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+ PyObject *module, *sort_func, *iterable, *frame_obj, *iterator;
+ PyObject *py_frame_low, *py_frame_high;
+
+ frame_obj = frame_info_to_frame_object (frame);
+ if (frame_obj == NULL)
+ goto error;
+ make_cleanup_py_decref (frame_obj);
+
+ module = PyImport_ImportModule ("gdb.frames");
+ if (module == NULL)
+ goto error;
+ make_cleanup_py_decref (module);
+
+ sort_func = PyObject_GetAttrString (module, "execute_frame_filters");
+ if (sort_func == NULL)
+ goto error;
+ make_cleanup_py_decref (sort_func);
+
+ py_frame_low = PyInt_FromLong (frame_low);
+ if (py_frame_low == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_low);
+
+ py_frame_high = PyInt_FromLong (frame_high);
+ if (py_frame_high == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_high);
+
+ iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj,
+ py_frame_low,
+ py_frame_high,
+ NULL);
+ if (iterable == NULL)
+ goto error;
+
+ do_cleanups (cleanups);
+
+ if (iterable != Py_None)
+ {
+ iterator = PyObject_GetIter (iterable);
+ Py_DECREF (iterable);
+ }
+ else
+ {
+ return iterable;
+ }
+
+ return iterator;
+
+ error:
+ do_cleanups (cleanups);
+ return NULL;
+}
+
+/* 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 the make-up 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. ARGS_TYPE is an enumerator describing the argument
+ format, OUT is the output stream to print. FRAME_LOW is the
+ beginning of the slice of frames to print, and FRAME_HIGH is the
+ upper limit of the frames to count. */
+
+int
+apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+
+{
+ struct gdbarch *gdbarch = NULL;
+ struct cleanup *cleanups;
+ int success = PY_BT_ERROR;
+ PyObject *iterable;
+ volatile struct gdb_exception except;
+ PyObject *item;
+ htab_t levels_printed;
+
+ cleanups = ensure_python_env (gdbarch, current_language);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ iterable = bootstrap_python_frame_filters (frame, frame_low, frame_high);
+
+ if (iterable == NULL)
+ goto error;
+
+ /* If iterable is None, then there are no 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)
+ {
+ success = PY_BT_NO_FILTERS;
+ goto done;
+ }
+
+ levels_printed = htab_create (20,
+ htab_hash_pointer,
+ htab_eq_pointer,
+ NULL);
+ make_cleanup_htab_delete (levels_printed);
+
+ while ((item = PyIter_Next (iterable)))
+ {
+ success = py_print_frame (item, flags, args_type, out, 0,
+ levels_printed);
+
+ /* Do not exit on error printing a single frame. Print the
+ error and continue with other frames. */
+ if (success == PY_BT_ERROR)
+ gdbpy_print_stack ();
+
+ Py_DECREF (item);
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ done:
+ do_cleanups (cleanups);
+ return success;
+
+ error:
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index db51f50..6fa3035 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);
Py_TYPE (self)->tp_free (self);
}
@@ -81,6 +84,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -129,6 +139,47 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this object file. */
+PyObject *
+objfpy_get_frame_filters (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +276,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -270,6 +328,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 45a5193..104b36d 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);
Py_TYPE (self)->tp_free (self);
}
@@ -89,6 +92,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -137,6 +147,47 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this program space. */
+PyObject *
+pspy_get_frame_filters (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +272,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -257,6 +315,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 b280c8c..890b65d 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -48,6 +48,28 @@ 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;
+
+ 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, 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 ea97226..7337bff 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -251,9 +251,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 *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -304,6 +306,7 @@ void gdbpy_initialize_new_objfile_event (void);
void gdbpy_initialize_arch (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 67d06e5..95d8cf8 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1393,6 +1393,14 @@ free_type_printers (void *arg)
{
}
+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+{
+ return PY_BT_NO_FILTERS;
+}
+
#endif /* HAVE_PYTHON */
\f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 24e3077..c854ba6 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,66 @@ 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 return values. */
+enum py_bt_status
+ {
+ /* Return when an error has occurred in processing frame filters,
+ or when printing the stack. */
+ PY_BT_ERROR,
+
+ /* Return from internal routines to indicate that the function
+ succeeded. */
+ PY_BT_OK,
+
+ /* Return when the frame filter process is complete, and all
+ operations have succeeded. */
+ PY_BT_COMPLETED,
+
+ /* Return when the frame filter process is complete, but there
+ were no filter registered and enabled to process. */
+ PY_BT_NO_FILTERS
+ };
+
+/* 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,
+ };
+
+/* A choice of the different frame argument printing strategies that
+ can occur in different cases of frame filter instantiation. */
+typedef enum py_frame_args
+{
+ /* Print no values for arguments when invoked from the MI. */
+ NO_VALUES = PRINT_NO_VALUES,
+
+ MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
+
+ /* Print only simple values (what MI defines as "simple") for
+ arguments when invoked from the MI. */
+ MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES,
+
+
+ /* Print only scalar values for arguments when invoked from the
+ CLI. */
+ CLI_SCALAR_VALUES,
+
+ /* Print all values for arguments when invoked from the
+ CLI. */
+ CLI_ALL_VALUES
+} py_frame_args;
+
extern void finish_python_initialization (void);
void eval_python_from_control_command (struct command_line *);
@@ -41,6 +102,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 py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high);
+
void preserve_python_values (struct objfile *objfile, htab_t copied_types);
void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-04-22 16:01 Phil Muldoon
@ 2013-04-26 6:47 ` Tom Tromey
0 siblings, 0 replies; 16+ messages in thread
From: Tom Tromey @ 2013-04-26 6:47 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> This email and patch covers the python/ changes for Python Frame Filters.
Thanks.
Phil> diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py
...
Phil> +class FrameIterator(object):
...
Phil> + def __getitem__(self, index):
Phil> + """__getitem__ implementation.
Phil> +
Phil> + Arguments:
Phil> + index: A specific index to fetch."""
Phil> +
Phil> + return next(itertools.islice(self.frame, index, index+1))
I don't think this will actually work.
self.frame isn't an iterator, so I don't think islice will do the right
thing here.
I don't think this method is needed. Iterators only really need 'next'
(though supplying __iter__ is also nice).
Phil> + if hasattr(filter_item, "priority"):
Phil> + return filter_item.priority
Phil> + else:
Phil> + return 0
You can use getattr(filter_item, "priority", 0) here
Phil> + if hasattr(filter_item, "enabled"):
Phil> + return filter_item.enabled
Phil> + else:
Phil> + return False
getattr here too.
Phil> +static enum py_bt_status
Phil> +extract_sym (PyObject *obj, char **name, struct symbol **sym,
Phil> + const struct language_defn **language)
It occurs to me now that this return value convention is going to make
this code less amenable to analysis with the CPython checker.
Hmm, maybe it would work ok if we set PY_BT_ERROR = -1. Then we could
use the "negative means failure" attribute.
Phil> +static enum py_bt_status
Phil> +extract_value (PyObject *obj, struct value **value)
...
Phil> + return 1;
Should be one of the enum values, I guess PY_BT_OK.
Phil> +static enum py_bt_status
Phil> +py_print_value (struct ui_out *out, struct value *val,
Many of the callers of this make assumptions about the return value,
e.g.:
+ if (! py_print_type (out, val))
+ goto error;
These have to be checks against enum constants.
The same goes for py_print_value and py_print_single_arg and more.
I feel like this has come up in every review.
Phil> + /* Never set an indent level for common_val_print if MI. */
Phil> + if (ui_out_is_mi_like_p (out))
Phil> + local_indent = 0;
The second line here is intended too much.
Phil> + /* MI does not print certain values, differentiated by type,
Phil> + depending on what ARGS_TYPE indicates. Test type against option.
Phil> + For CLI print all values. */
Phil> + if (args_type == MI_PRINT_SIMPLE_VALUES
Phil> + || args_type == MI_PRINT_ALL_VALUES)
Phil> +
Phil> + {
Extra blank line here.
Phil> +static enum py_bt_status
Phil> +py_print_single_arg (struct ui_out *out,
...
Phil> + /* MI has varying rules for tuples, but generally if there is only
Phil> + one element in each item in the list, do not start a tuple. The
Phil> + exception is -stack-list-variables which emits an ARGS="1" field
Phil> + if the value is a frame argument. This is denoted in this
Phil> + function with PRINT_ARGS_FIELD which is flag from the caller to
Phil> + emit the ARGS field. */
Phil> + if (ui_out_is_mi_like_p (out))
Phil> + {
Phil> + if (print_args_field || args_type != NO_VALUES)
Phil> + make_cleanup_ui_out_tuple_begin_end (out, NULL);
Phil> + }
Phil> +
Phil> + TRY_CATCH (except, RETURN_MASK_ALL)
Phil> + {
I think the make_cleanup_ui_out_tuple_begin_end call should probably go
inside this TRY_CATCH.
Phil> +static enum py_bt_status
Phil> +enumerate_locals (PyObject *iter,
...
Phil> + make_cleanup (null_cleanup, NULL);
I don't think this is needed.
Phil> + do_cleanups (cleanups);
Phil> + }
I think this call is wrong. In particular if the loop runs more than
once, it will try to re-do a cleanup that was already run.
Maybe you meant to introduce a new inner cleanup in the loop, and that
was what the null_cleanup was for.
Phil> + if (item == NULL && PyErr_Occurred ())
Phil> + goto error;
Phil> +
Phil> + return PY_BT_OK;
Phil> +
Phil> + error:
Phil> + do_cleanups (cleanups);
Phil> + return PY_BT_ERROR;
Similarly, this should do the cleanups along both branches, or neither
branch.
Phil> +static enum py_bt_status
Phil> +py_print_frame (PyObject *filter, int flags, enum py_frame_args args_type,
...
Phil> + /* stack-list-variables. */
Phil> + if (print_locals && print_args && ! print_frame_info)
Phil> + {
Phil> + if (! py_mi_print_variables (filter, out, &opts,
Phil> + args_type, frame))
Phil> + goto error;
Phil> + else
Phil> + return PY_BT_COMPLETED;
Phil> + }
This early return has to run the cleanups.
Phil> + int success = py_print_frame (item, flags, args_type, out, indent,
Phil> + levels_printed);
Phil> +
Phil> + if (success == PY_BT_ERROR && PyErr_Occurred ())
It seems like it should be impossible to see PY_BT_ERROR and not have
PyErr_Occurred be true. If it can happen then it seems like that is an
API contract violation.
Phil> +int
Phil> +apply_frame_filter (struct frame_info *frame, int flags,
Seems like this should return enum py_bt_status.
It needs to be updated in a few spots.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch][python] 1 of 5 - Frame filter Python C code changes.
@ 2013-05-06 8:23 Phil Muldoon
2013-05-06 20:39 ` Tom Tromey
0 siblings, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2013-05-06 8:23 UTC (permalink / raw)
To: gdb-patches
This email and patch covers the python/ changes for Python Frame Filters.
2013-05-06 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/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.
--
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..e1800f6
--- /dev/null
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -0,0 +1,456 @@
+# 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:
+ raise gdb.GdbError("set frame-filter priority " \
+ "takes exactly three arguments.")
+
+ 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)
+ 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:
+ raise gdb.GdbError("show frame-filter priority " \
+ "takes exactly two arguments.")
+
+ 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)
+ 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
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
new file mode 100644
index 0000000..d62c596
--- /dev/null
+++ b/gdb/python/py-framefilter.c
@@ -0,0 +1,1528 @@
+/* Python frame filters
+
+ Copyright (C) 2013 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 "demangle.h"
+#include "mi/mi-cmds.h"
+#include "python-internal.h"
+
+enum mi_print_types
+{
+ MI_PRINT_ARGS,
+ MI_PRINT_LOCALS
+};
+
+/* Helper function to extract a symbol, a name and a 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
+ PY_BT_ERROR on error with the appropriate Python exception set, and
+ PY_BT_OK on success. */
+
+static enum py_bt_status
+extract_sym (PyObject *obj, char **name, struct symbol **sym,
+ const struct language_defn **language)
+{
+ PyObject *result = PyObject_CallMethod (obj, "symbol", NULL);
+
+ if (result == NULL)
+ return PY_BT_ERROR;
+
+ /* For 'symbol' callback, the function can return a symbol or a
+ string. */
+ if (gdbpy_is_string (result))
+ {
+ *name = python_string_to_host_string (result);
+ Py_DECREF (result);
+
+ if (*name == NULL)
+ return PY_BT_ERROR;
+ /* If the API returns a string (and not a symbol), then there is
+ no symbol derived language available and the frame filter has
+ either overridden the symbol with a string, or supplied a
+ entirely synthetic symbol/value pairing. In that case, use
+ python_language. */
+ *language = python_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 == NULL)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Unexpected value. Expecting a "
+ "gdb.Symbol or a Python string."));
+ return PY_BT_ERROR;
+ }
+
+ /* Duplicate the symbol name, so the caller has consistency
+ in garbage collection. */
+ *name = xstrdup (SYMBOL_PRINT_NAME (*sym));
+
+ /* If a symbol is specified attempt to determine the language
+ from the symbol. If mode is not "auto", then the language
+ has been explicitly set, use that. */
+ if (language_mode == language_mode_auto)
+ *language = language_def (SYMBOL_LANGUAGE (*sym));
+ else
+ *language = current_language;
+ }
+
+ return PY_BT_OK;
+}
+
+/* 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 PY_BT_ERROR
+ on error with the appropriate Python exception set, and PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+extract_value (PyObject *obj, struct value **value)
+{
+ if (PyObject_HasAttrString (obj, "value"))
+ {
+ PyObject *vresult = PyObject_CallMethod (obj, "value", NULL);
+
+ if (vresult == NULL)
+ return PY_BT_ERROR;
+
+ /* 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 PY_BT_OK;
+ }
+ else
+ {
+ *value = convert_value_from_python (vresult);
+ Py_DECREF (vresult);
+
+ if (*value == NULL)
+ return PY_BT_ERROR;
+
+ return PY_BT_OK;
+ }
+ }
+ else
+ *value = NULL;
+
+ return PY_BT_OK;
+}
+
+/* MI prints only certain values according to the type of symbol and
+ also what the user has specified. SYM is the symbol to check, and
+ MI_PRINT_TYPES is an enum specifying what the user wants emitted
+ for the MI command in question. */
+static int
+mi_should_print (struct symbol *sym, enum mi_print_types 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 (type == MI_PRINT_LOCALS)
+ print_me = ! SYMBOL_IS_ARGUMENT (sym);
+ else
+ print_me = SYMBOL_IS_ARGUMENT (sym);
+ }
+ return print_me;
+}
+
+/* Helper function which outputs a type name extracted from VAL to a
+ "type" field in the output stream OUT. 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 PY_BT_ERROR on error, with
+ any GDB exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+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)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function which outputs a value to an output field in a
+ stream. OUT is the ui-out structure the value will be output to,
+ VAL is the value that will be printed, OPTS contains the value
+ printing options, ARGS_TYPE is an enumerator describing the
+ argument format, and LANGUAGE is the language_defn that the value
+ will be printed with. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+py_print_value (struct ui_out *out, struct value *val,
+ const struct value_print_options *opts,
+ int indent,
+ enum py_frame_args args_type,
+ const struct language_defn *language)
+{
+ int should_print = 0;
+ volatile struct gdb_exception except;
+ int local_indent = (4 * indent);
+
+ /* Never set an indent level for common_val_print if MI. */
+ if (ui_out_is_mi_like_p (out))
+ local_indent = 0;
+
+ /* MI does not print certain values, differentiated by type,
+ depending on what ARGS_TYPE indicates. Test type against option.
+ For CLI print all values. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES
+ || args_type == MI_PRINT_ALL_VALUES)
+ {
+ struct type *type = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = check_typedef (value_type (val));
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ if (args_type == MI_PRINT_ALL_VALUES)
+ should_print = 1;
+ else if (args_type == MI_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 if (args_type != NO_VALUES)
+ should_print = 1;
+
+ if (should_print)
+ {
+ 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, indent, opts, language);
+ ui_out_field_stream (out, "value", stb);
+ do_cleanups (cleanup);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function to call a Python method and extract an iterator
+ from the result. If the function returns anything but an iterator
+ the exception is preserved and NULL is returned. 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 NULL. */
+
+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 != NULL)
+ {
+ if (result == Py_None)
+ {
+ return result;
+ }
+ else
+ {
+ PyObject *iterator = PyObject_GetIter (result);
+
+ Py_DECREF (result);
+ return iterator;
+ }
+ }
+ }
+ else
+ Py_RETURN_NONE;
+
+ return NULL;
+}
+
+/* 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,
+ ARGS_TYPE is an enumerator describing the argument format,
+ PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1"
+ in MI output in commands where both arguments and locals are
+ printed. Returns PY_BT_ERROR on error, with any GDB exceptions
+ converted to a Python exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_print_single_arg (struct ui_out *out,
+ const char *sym_name,
+ struct frame_arg *fa,
+ struct value *fv,
+ const struct value_print_options *opts,
+ enum py_frame_args args_type,
+ int print_args_field,
+ const struct language_defn *language)
+{
+ struct value *val;
+ volatile struct gdb_exception except;
+
+ if (fa != NULL)
+ {
+ language = language_def (SYMBOL_LANGUAGE (fa->sym));
+ val = fa->val;
+ }
+ else
+ val = fv;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+
+ /* 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. The
+ exception is -stack-list-variables which emits an ARGS="1" field
+ if the value is a frame argument. This is denoted in this
+ function with PRINT_ARGS_FIELD which is flag from the caller to
+ emit the ARGS field. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ if (print_args_field || args_type != NO_VALUES)
+ 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 != NULL)
+ {
+ struct ui_file *stb;
+
+ stb = mem_fileopen ();
+ make_cleanup_ui_file_delete (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_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);
+ }
+ 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_args_field)
+ ui_out_field_int (out, "arg", 1);
+
+ /* For MI print the type, but only for simple values. This seems
+ weird, but this is how MI choose to format the various output
+ types. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (py_print_type (out, val) == PY_BT_ERROR)
+ {
+ do_cleanups (cleanups);
+ 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) && args_type == NO_VALUES)
+ ui_out_field_string (out, "value", "...");
+ else
+ {
+ /* Otherwise, print the value for both MI and the CLI, except
+ for the case of MI_PRINT_NO_VALUES. */
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, opts, 0, args_type, language)
+ == PY_BT_ERROR)
+ {
+ do_cleanups (cleanups);
+ goto error;
+ }
+ }
+ }
+
+ do_cleanups (cleanups);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+/* 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
+ iterable object, OUT is the output stream, ARGS_TYPE is an
+ enumerator describing the argument format, PRINT_ARGS_FIELD is a
+ flag which indicates if we output "ARGS=1" in MI output in commands
+ where both arguments and locals are printed, and FRAME is the
+ backing frame. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+enumerate_args (PyObject *iter,
+ struct ui_out *out,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ volatile struct gdb_exception except;
+
+ get_user_print_options (&opts);
+
+ if (args_type == CLI_SCALAR_VALUES)
+ {
+ /* True in "summary" mode, false otherwise. */
+ opts.summary = 1;
+ }
+
+ opts.deref_ref = 1;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* 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 == NULL && PyErr_Occurred ())
+ goto error;
+
+ while (item)
+ {
+ const struct language_defn *language;
+ char *sym_name;
+ struct symbol *sym;
+ struct value *val;
+ enum py_bt_status success = PY_BT_ERROR;
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ 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, MI_PRINT_ARGS))
+ {
+ xfree (sym_name);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it using
+ read_frame_args and account for entry values, if any. */
+ if (val == NULL)
+ {
+ struct frame_arg arg, entryarg;
+
+ /* If there is no value, and also no symbol, set error and
+ exit. */
+ if (sym == NULL)
+ {
+ 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);
+ gdbpy_convert_exception (except);
+ 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)
+ {
+ if (py_print_single_arg (out, NULL, &arg,
+ NULL, &opts,
+ args_type,
+ print_args_field,
+ NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ if (entryarg.entry_kind != print_entry_values_no)
+ {
+ if (arg.entry_kind != print_entry_values_only)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ ui_out_wrap_hint (out, " ");
+ }
+ if (except.reason < 0)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (py_print_single_arg (out, NULL, &entryarg, NULL,
+ &opts, args_type,
+ print_args_field, NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ xfree (arg.error);
+ xfree (entryarg.error);
+ }
+ else
+ {
+ /* If the object has provided a value, we just print that. */
+ if (val != NULL)
+ {
+ if (py_print_single_arg (out, sym_name, NULL, val, &opts,
+ args_type, print_args_field,
+ language) == PY_BT_ERROR)
+ {
+ xfree (sym_name);
+ goto error;
+ }
+ }
+ }
+
+ 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 != NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else if (PyErr_Occurred ())
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_end ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+
+/* Helper function to loop over variables provided by the
+ "frame_locals" Python API. Elements in the iterable must conform
+ to the "Symbol Value" interface. ITER is the Python iterable
+ object, OUT is the output stream, INDENT is whether we should
+ indent the output (for CLI), ARGS_TYPE is an enumerator describing
+ the argument format, PRINT_ARGS_FIELD is flag which indicates
+ whether to output the ARGS field in the case of
+ -stack-list-variables and FRAME is the backing frame. Returns
+ PY_BT_ERROR on error, with any GDB exceptions converted to a Python
+ exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+enumerate_locals (PyObject *iter,
+ struct ui_out *out,
+ int indent,
+ enum py_frame_args args_type,
+ int print_args_field,
+ 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;
+ enum py_bt_status success = PY_BT_ERROR;
+ struct symbol *sym;
+ volatile struct gdb_exception except;
+ int local_indent = 8 + (8 * indent);
+ struct cleanup *locals_cleanups;
+
+ locals_cleanups = make_cleanup_py_decref (item);
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ make_cleanup (xfree, sym_name);
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ if (sym != NULL && ui_out_is_mi_like_p (out)
+ && ! mi_should_print (sym, MI_PRINT_LOCALS))
+ {
+ do_cleanups (locals_cleanups);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it. */
+ if (val == NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ val = read_var_value (sym, frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ do_cleanups (locals_cleanups);
+ 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_args_field || args_type != NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ {
+ /* If the output is not MI we indent locals. */
+ ui_out_spaces (out, local_indent);
+ }
+
+ ui_out_field_string (out, "name", sym_name);
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " = ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (py_print_type (out, val) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+
+ /* CLI always prints values for locals. MI uses the
+ simple/no/all system. */
+ if (! ui_out_is_mi_like_p (out))
+ {
+ int val_indent = (indent + 1) * 4;
+
+ if (py_print_value (out, val, &opts, val_indent, args_type,
+ language) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+ else
+ {
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, &opts, 0, args_type,
+ language) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+ }
+
+ do_cleanups (locals_cleanups);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+/* Helper function for -stack-list-variables. Returns PY_BT_ERROR on
+ error, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_mi_print_variables (PyObject *filter, struct ui_out *out,
+ struct value_print_options *opts,
+ enum py_frame_args 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 == NULL)
+ goto error;
+
+ locals_iter = get_py_iter_from_func (filter, "frame_locals");
+ if (locals_iter == NULL)
+ 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, args_type, 1, frame) == PY_BT_ERROR)
+ goto error;
+
+ if (locals_iter != Py_None)
+ if (enumerate_locals (locals_iter, out, 1, args_type, 1, frame)
+ == PY_BT_ERROR)
+ goto error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing locals. This function largely just
+ creates the wrapping tuple, and calls enumerate_locals. Returns
+ PY_BT_ERROR on error, or PY_BT_OK on success.*/
+
+static enum py_bt_status
+py_print_locals (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args args_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 == NULL)
+ goto locals_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "locals");
+
+ if (locals_iter != Py_None)
+ if (enumerate_locals (locals_iter, out, indent, args_type,
+ 0, frame) == PY_BT_ERROR)
+ goto locals_error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;;
+
+ locals_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing frame arguments. This function
+ largely just creates the wrapping tuple, and calls enumerate_args.
+ Returns PY_BT_ERROR on error, with any GDB exceptions converted to
+ a Python exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_print_args (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args 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);
+ volatile struct gdb_exception except;
+
+ if (args_iter == NULL)
+ goto args_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "args");
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " (");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ if (args_iter != Py_None)
+ if (enumerate_args (args_iter, out, args_type, 0, frame) == PY_BT_ERROR)
+ goto args_error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, ")");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ args_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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. FLAGS is an integer
+ describing the various print options. The FLAGS variables is
+ described in "apply_frame_filter" function. ARGS_TYPE is an
+ enumerator describing the argument format. OUT is the output
+ stream to print, 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). Returns PY_BT_ERROR on error, with any
+ GDB exceptions converted to a Python exception, or PY_BT_COMPLETED
+ on success. */
+
+static enum py_bt_status
+py_print_frame (PyObject *filter, int flags, enum py_frame_args 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 *py_inf_frame, *elided;
+ int print_level, print_frame_info, print_args, print_locals;
+ volatile struct gdb_exception except;
+
+ /* 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;
+
+ 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. */
+ py_inf_frame = PyObject_CallMethod (filter, "inferior_frame", NULL);
+ if (py_inf_frame == NULL)
+ goto error;
+
+ frame = frame_object_to_frame_info (py_inf_frame);;
+
+ Py_DECREF (py_inf_frame);
+
+ if (frame == NULL)
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+
+ /* stack-list-variables. */
+ if (print_locals && print_args && ! print_frame_info)
+ {
+ if (py_mi_print_variables (filter, out, &opts,
+ args_type, frame) == PY_BT_ERROR)
+ goto error;
+ else
+ {
+ do_cleanups (cleanup_stack);
+ 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)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_spaces (out, indent*4);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* 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 != NULL)
+ {
+ 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;
+ volatile struct gdb_exception except;
+
+ slot = (struct frame_info **) htab_find_slot (levels_printed,
+ frame, INSERT);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 == frame)
+ ui_out_field_skip (out, "level");
+ 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 (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_frame_info)
+ {
+ /* Print address to the address field. If an address is not provided,
+ print nothing. */
+ if (opts.addressprint && has_addr)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_address ();
+ ui_out_field_core_addr (out, "addr", gdbarch, address);
+ annotate_frame_address_end ();
+ ui_out_text (out, " in ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* Print frame function name. */
+ if (PyObject_HasAttrString (filter, "function"))
+ {
+ PyObject *py_func = PyObject_CallMethod (filter, "function", NULL);
+
+ if (py_func != NULL)
+ {
+ const char *function = NULL;
+
+ if (gdbpy_is_string (py_func))
+ {
+ function = PyString_AsString (py_func);
+
+ if (function == NULL)
+ {
+ Py_DECREF (py_func);
+ goto error;
+ }
+ }
+ else if (PyLong_Check (py_func))
+ {
+ CORE_ADDR addr = PyLong_AsUnsignedLongLong (py_func);
+ struct bound_minimal_symbol msymbol;
+
+ if (PyErr_Occurred ())
+ goto error;
+
+ msymbol = lookup_minimal_symbol_by_pc (addr);
+ if (msymbol.minsym != NULL)
+ function = SYMBOL_PRINT_NAME (msymbol.minsym);
+ }
+ else if (py_func != Py_None)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("FrameDecorator.function: expecting a " \
+ "String, integer or None."));
+ Py_DECREF (py_func);
+ goto error;
+ }
+
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_function_name ();
+ if (function == NULL)
+ ui_out_field_skip (out, "func");
+ else
+ ui_out_field_string (out, "func", function);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_func);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_func);
+ }
+ else
+ goto error;
+ }
+
+
+ /* Frame arguments. Check the result, and error if something went
+ wrong. */
+ if (print_args)
+ {
+ if (py_print_args (filter, out, args_type, frame) == PY_BT_ERROR)
+ goto error;
+ }
+
+ /* File name/source/line number information. */
+ if (print_frame_info)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_source_begin ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "filename"))
+ {
+ PyObject *py_fn = PyObject_CallMethod (filter, "filename",
+ NULL);
+ if (py_fn != NULL)
+ {
+ if (py_fn != Py_None)
+ {
+ char *filename = PyString_AsString (py_fn);
+
+ if (filename == NULL)
+ {
+ Py_DECREF (py_fn);
+ goto error;
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_fn);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_fn);
+ }
+ else
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "line"))
+ {
+ PyObject *py_line = PyObject_CallMethod (filter, "line", NULL);
+ int line;
+
+ if (py_line != NULL)
+ {
+ if (py_line != Py_None)
+ {
+ line = PyLong_AsLong (py_line);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ":");
+ annotate_frame_source_line ();
+ ui_out_field_int (out, "line", line);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_line);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_line);
+ }
+ 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))
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_end ();
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_locals)
+ {
+ if (py_print_locals (filter, out, args_type, indent,
+ frame) == PY_BT_ERROR)
+ goto error;
+ }
+
+ /* Finally recursively print elided frames, if any. */
+ elided = get_py_iter_from_func (filter, "elided");
+ if (elided == NULL)
+ goto error;
+
+ make_cleanup_py_decref (elided);
+ if (elided != Py_None)
+ {
+ PyObject *item;
+
+ make_cleanup_ui_out_list_begin_end (out, "children");
+
+ if (! ui_out_is_mi_like_p (out))
+ indent++;
+
+ while ((item = PyIter_Next (elided)))
+ {
+ enum py_bt_status success = py_print_frame (item, flags,
+ args_type, out,
+ indent,
+ levels_printed);
+
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ }
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+ }
+
+
+ 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,
+ int frame_low, int frame_high)
+{
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+ PyObject *module, *sort_func, *iterable, *frame_obj, *iterator;
+ PyObject *py_frame_low, *py_frame_high;
+
+ frame_obj = frame_info_to_frame_object (frame);
+ if (frame_obj == NULL)
+ goto error;
+ make_cleanup_py_decref (frame_obj);
+
+ module = PyImport_ImportModule ("gdb.frames");
+ if (module == NULL)
+ goto error;
+ make_cleanup_py_decref (module);
+
+ sort_func = PyObject_GetAttrString (module, "execute_frame_filters");
+ if (sort_func == NULL)
+ goto error;
+ make_cleanup_py_decref (sort_func);
+
+ py_frame_low = PyInt_FromLong (frame_low);
+ if (py_frame_low == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_low);
+
+ py_frame_high = PyInt_FromLong (frame_high);
+ if (py_frame_high == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_high);
+
+ iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj,
+ py_frame_low,
+ py_frame_high,
+ NULL);
+ if (iterable == NULL)
+ goto error;
+
+ do_cleanups (cleanups);
+
+ if (iterable != Py_None)
+ {
+ iterator = PyObject_GetIter (iterable);
+ Py_DECREF (iterable);
+ }
+ else
+ {
+ return iterable;
+ }
+
+ return iterator;
+
+ error:
+ do_cleanups (cleanups);
+ return NULL;
+}
+
+/* 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 the make-up 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. ARGS_TYPE is an enumerator describing the argument
+ format, OUT is the output stream to print. FRAME_LOW is the
+ beginning of the slice of frames to print, and FRAME_HIGH is the
+ upper limit of the frames to count. Returns PY_BT_ERROR on error,
+ or PY_BT_COMPLETED on success.*/
+
+enum py_bt_status
+apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+
+{
+ struct gdbarch *gdbarch = NULL;
+ struct cleanup *cleanups;
+ enum py_bt_status success = PY_BT_ERROR;
+ PyObject *iterable;
+ volatile struct gdb_exception except;
+ PyObject *item;
+ htab_t levels_printed;
+
+ cleanups = ensure_python_env (gdbarch, current_language);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ iterable = bootstrap_python_frame_filters (frame, frame_low, frame_high);
+
+ if (iterable == NULL)
+ goto error;
+
+ /* If iterable is None, then there are no 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)
+ {
+ success = PY_BT_NO_FILTERS;
+ goto done;
+ }
+
+ levels_printed = htab_create (20,
+ htab_hash_pointer,
+ htab_eq_pointer,
+ NULL);
+ make_cleanup_htab_delete (levels_printed);
+
+ while ((item = PyIter_Next (iterable)))
+ {
+ success = py_print_frame (item, flags, args_type, out, 0,
+ levels_printed);
+
+ /* Do not exit on error printing a single frame. Print the
+ error and continue with other frames. */
+ if (success == PY_BT_ERROR)
+ gdbpy_print_stack ();
+
+ Py_DECREF (item);
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ done:
+ do_cleanups (cleanups);
+ return success;
+
+ error:
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index db51f50..6fa3035 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);
Py_TYPE (self)->tp_free (self);
}
@@ -81,6 +84,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -129,6 +139,47 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this object file. */
+PyObject *
+objfpy_get_frame_filters (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +276,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -270,6 +328,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 45a5193..104b36d 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);
Py_TYPE (self)->tp_free (self);
}
@@ -89,6 +92,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -137,6 +147,47 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this program space. */
+PyObject *
+pspy_get_frame_filters (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +272,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -257,6 +315,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 b280c8c..890b65d 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -48,6 +48,28 @@ 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;
+
+ 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, 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 ea97226..7337bff 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -251,9 +251,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 *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -304,6 +306,7 @@ void gdbpy_initialize_new_objfile_event (void);
void gdbpy_initialize_arch (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 67d06e5..95d8cf8 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1393,6 +1393,14 @@ free_type_printers (void *arg)
{
}
+int apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+{
+ return PY_BT_NO_FILTERS;
+}
+
#endif /* HAVE_PYTHON */
\f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 24e3077..1a1e5c2 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,66 @@ 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 return values. */
+enum py_bt_status
+ {
+ /* Return when an error has occurred in processing frame filters,
+ or when printing the stack. */
+ PY_BT_ERROR = -1,
+
+ /* Return from internal routines to indicate that the function
+ succeeded. */
+ PY_BT_OK = 1,
+
+ /* Return when the frame filter process is complete, and all
+ operations have succeeded. */
+ PY_BT_COMPLETED = 2,
+
+ /* Return when the frame filter process is complete, but there
+ were no filter registered and enabled to process. */
+ PY_BT_NO_FILTERS = 3
+ };
+
+/* 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,
+ };
+
+/* A choice of the different frame argument printing strategies that
+ can occur in different cases of frame filter instantiation. */
+typedef enum py_frame_args
+{
+ /* Print no values for arguments when invoked from the MI. */
+ NO_VALUES = PRINT_NO_VALUES,
+
+ MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
+
+ /* Print only simple values (what MI defines as "simple") for
+ arguments when invoked from the MI. */
+ MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES,
+
+
+ /* Print only scalar values for arguments when invoked from the
+ CLI. */
+ CLI_SCALAR_VALUES,
+
+ /* Print all values for arguments when invoked from the
+ CLI. */
+ CLI_ALL_VALUES
+} py_frame_args;
+
extern void finish_python_initialization (void);
void eval_python_from_control_command (struct command_line *);
@@ -41,6 +102,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
const struct value_print_options *options,
const struct language_defn *language);
+enum py_bt_status apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high);
+
void preserve_python_values (struct objfile *objfile, htab_t copied_types);
void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-05-06 8:23 Phil Muldoon
@ 2013-05-06 20:39 ` Tom Tromey
2013-05-09 13:23 ` Phil Muldoon
0 siblings, 1 reply; 16+ messages in thread
From: Tom Tromey @ 2013-05-06 20:39 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> +int apply_frame_filter (struct frame_info *frame, int flags,
Phil> + enum py_frame_args args_type,
Phil> + struct ui_out *out, int frame_low,
Phil> + int frame_high)
Phil> +{
Phil> + return PY_BT_NO_FILTERS;
Phil> +}
The return type here disagrees with the declaration in python.h.
This patch is ok with the above fixed, if it also builds ok without
Python.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-05-06 20:39 ` Tom Tromey
@ 2013-05-09 13:23 ` Phil Muldoon
2013-05-09 15:14 ` Tom Tromey
0 siblings, 1 reply; 16+ messages in thread
From: Phil Muldoon @ 2013-05-09 13:23 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 06/05/13 21:39, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
>
> Phil> +int apply_frame_filter (struct frame_info *frame, int flags,
> Phil> + enum py_frame_args args_type,
> Phil> + struct ui_out *out, int frame_low,
> Phil> + int frame_high)
> Phil> +{
> Phil> + return PY_BT_NO_FILTERS;
> Phil> +}
>
> The return type here disagrees with the declaration in python.h.
>
> This patch is ok with the above fixed, if it also builds ok without
> Python.
Modified patch attached. Confirming it will build without Python.
Beyond the change you wanted above, I had to fix a regression I found
in the testsuite. The issue is when you type:
"info set"
into GDB, it iterates through all "set" commands and callw them without
parameters . The problem is that: "set python frame-filter" must have
arguments to work (the dictionary and the frame-filter). If not, then
it will raise a Gdb.Error which will halt the "info set" command,
leaving only a partial list printed (the regression for this was in
gdb.base/default.exp).
Several commands that require inferiors to be active do print errors
in the output, (mainly the $_probe variables). Is it okay to print an
error message (but not raise an exception) and continue in this case?
I have attached two patches. The first one is a delta to the attached
patch, so you can see the changes I made in an easy manner. The
second is the whole python/ patchset for the change you requested
above.
What do you think?
Cheers,
Phil
2013-05-09 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/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.
--
patch 1, against python/ patch to address changes in regression:
diff --git a/gdb/python/lib/gdb/command/frame_filters.py b/gdb/python/lib/gdb/command/frame_filters.py
index e1800f6..1b73059 100644
--- a/gdb/python/lib/gdb/command/frame_filters.py
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -317,8 +317,9 @@ class SetFrameFilterPriority(gdb.Command):
argv = gdb.string_to_argv(arg);
argc = len(argv)
if argc != 3:
- raise gdb.GdbError("set frame-filter priority " \
- "takes exactly three arguments.")
+ print("set frame-filter priority " \
+ "takes exactly three arguments.")
+ return None
return argv
@@ -357,7 +358,8 @@ class SetFrameFilterPriority(gdb.Command):
def invoke(self, arg, from_tty):
command_tuple = self._parse_pri_arg(arg)
- self._set_filter_priority(command_tuple)
+ 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.
@@ -395,8 +397,9 @@ class ShowFrameFilterPriority(gdb.Command):
argv = gdb.string_to_argv(arg);
argc = len(argv)
if argc != 2:
- raise gdb.GdbError("show frame-filter priority " \
- "takes exactly two arguments.")
+ print("show frame-filter priority " \
+ "takes exactly two arguments.")
+ return None
return argv
@@ -436,6 +439,8 @@ class ShowFrameFilterPriority(gdb.Command):
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:
--
patch 2, entire python/ patchset:
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
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
new file mode 100644
index 0000000..d62c596
--- /dev/null
+++ b/gdb/python/py-framefilter.c
@@ -0,0 +1,1528 @@
+/* Python frame filters
+
+ Copyright (C) 2013 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 "demangle.h"
+#include "mi/mi-cmds.h"
+#include "python-internal.h"
+
+enum mi_print_types
+{
+ MI_PRINT_ARGS,
+ MI_PRINT_LOCALS
+};
+
+/* Helper function to extract a symbol, a name and a 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
+ PY_BT_ERROR on error with the appropriate Python exception set, and
+ PY_BT_OK on success. */
+
+static enum py_bt_status
+extract_sym (PyObject *obj, char **name, struct symbol **sym,
+ const struct language_defn **language)
+{
+ PyObject *result = PyObject_CallMethod (obj, "symbol", NULL);
+
+ if (result == NULL)
+ return PY_BT_ERROR;
+
+ /* For 'symbol' callback, the function can return a symbol or a
+ string. */
+ if (gdbpy_is_string (result))
+ {
+ *name = python_string_to_host_string (result);
+ Py_DECREF (result);
+
+ if (*name == NULL)
+ return PY_BT_ERROR;
+ /* If the API returns a string (and not a symbol), then there is
+ no symbol derived language available and the frame filter has
+ either overridden the symbol with a string, or supplied a
+ entirely synthetic symbol/value pairing. In that case, use
+ python_language. */
+ *language = python_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 == NULL)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Unexpected value. Expecting a "
+ "gdb.Symbol or a Python string."));
+ return PY_BT_ERROR;
+ }
+
+ /* Duplicate the symbol name, so the caller has consistency
+ in garbage collection. */
+ *name = xstrdup (SYMBOL_PRINT_NAME (*sym));
+
+ /* If a symbol is specified attempt to determine the language
+ from the symbol. If mode is not "auto", then the language
+ has been explicitly set, use that. */
+ if (language_mode == language_mode_auto)
+ *language = language_def (SYMBOL_LANGUAGE (*sym));
+ else
+ *language = current_language;
+ }
+
+ return PY_BT_OK;
+}
+
+/* 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 PY_BT_ERROR
+ on error with the appropriate Python exception set, and PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+extract_value (PyObject *obj, struct value **value)
+{
+ if (PyObject_HasAttrString (obj, "value"))
+ {
+ PyObject *vresult = PyObject_CallMethod (obj, "value", NULL);
+
+ if (vresult == NULL)
+ return PY_BT_ERROR;
+
+ /* 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 PY_BT_OK;
+ }
+ else
+ {
+ *value = convert_value_from_python (vresult);
+ Py_DECREF (vresult);
+
+ if (*value == NULL)
+ return PY_BT_ERROR;
+
+ return PY_BT_OK;
+ }
+ }
+ else
+ *value = NULL;
+
+ return PY_BT_OK;
+}
+
+/* MI prints only certain values according to the type of symbol and
+ also what the user has specified. SYM is the symbol to check, and
+ MI_PRINT_TYPES is an enum specifying what the user wants emitted
+ for the MI command in question. */
+static int
+mi_should_print (struct symbol *sym, enum mi_print_types 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 (type == MI_PRINT_LOCALS)
+ print_me = ! SYMBOL_IS_ARGUMENT (sym);
+ else
+ print_me = SYMBOL_IS_ARGUMENT (sym);
+ }
+ return print_me;
+}
+
+/* Helper function which outputs a type name extracted from VAL to a
+ "type" field in the output stream OUT. 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 PY_BT_ERROR on error, with
+ any GDB exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+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)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function which outputs a value to an output field in a
+ stream. OUT is the ui-out structure the value will be output to,
+ VAL is the value that will be printed, OPTS contains the value
+ printing options, ARGS_TYPE is an enumerator describing the
+ argument format, and LANGUAGE is the language_defn that the value
+ will be printed with. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+py_print_value (struct ui_out *out, struct value *val,
+ const struct value_print_options *opts,
+ int indent,
+ enum py_frame_args args_type,
+ const struct language_defn *language)
+{
+ int should_print = 0;
+ volatile struct gdb_exception except;
+ int local_indent = (4 * indent);
+
+ /* Never set an indent level for common_val_print if MI. */
+ if (ui_out_is_mi_like_p (out))
+ local_indent = 0;
+
+ /* MI does not print certain values, differentiated by type,
+ depending on what ARGS_TYPE indicates. Test type against option.
+ For CLI print all values. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES
+ || args_type == MI_PRINT_ALL_VALUES)
+ {
+ struct type *type = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = check_typedef (value_type (val));
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ if (args_type == MI_PRINT_ALL_VALUES)
+ should_print = 1;
+ else if (args_type == MI_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 if (args_type != NO_VALUES)
+ should_print = 1;
+
+ if (should_print)
+ {
+ 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, indent, opts, language);
+ ui_out_field_stream (out, "value", stb);
+ do_cleanups (cleanup);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function to call a Python method and extract an iterator
+ from the result. If the function returns anything but an iterator
+ the exception is preserved and NULL is returned. 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 NULL. */
+
+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 != NULL)
+ {
+ if (result == Py_None)
+ {
+ return result;
+ }
+ else
+ {
+ PyObject *iterator = PyObject_GetIter (result);
+
+ Py_DECREF (result);
+ return iterator;
+ }
+ }
+ }
+ else
+ Py_RETURN_NONE;
+
+ return NULL;
+}
+
+/* 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,
+ ARGS_TYPE is an enumerator describing the argument format,
+ PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1"
+ in MI output in commands where both arguments and locals are
+ printed. Returns PY_BT_ERROR on error, with any GDB exceptions
+ converted to a Python exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_print_single_arg (struct ui_out *out,
+ const char *sym_name,
+ struct frame_arg *fa,
+ struct value *fv,
+ const struct value_print_options *opts,
+ enum py_frame_args args_type,
+ int print_args_field,
+ const struct language_defn *language)
+{
+ struct value *val;
+ volatile struct gdb_exception except;
+
+ if (fa != NULL)
+ {
+ language = language_def (SYMBOL_LANGUAGE (fa->sym));
+ val = fa->val;
+ }
+ else
+ val = fv;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+
+ /* 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. The
+ exception is -stack-list-variables which emits an ARGS="1" field
+ if the value is a frame argument. This is denoted in this
+ function with PRINT_ARGS_FIELD which is flag from the caller to
+ emit the ARGS field. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ if (print_args_field || args_type != NO_VALUES)
+ 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 != NULL)
+ {
+ struct ui_file *stb;
+
+ stb = mem_fileopen ();
+ make_cleanup_ui_file_delete (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_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);
+ }
+ 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_args_field)
+ ui_out_field_int (out, "arg", 1);
+
+ /* For MI print the type, but only for simple values. This seems
+ weird, but this is how MI choose to format the various output
+ types. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (py_print_type (out, val) == PY_BT_ERROR)
+ {
+ do_cleanups (cleanups);
+ 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) && args_type == NO_VALUES)
+ ui_out_field_string (out, "value", "...");
+ else
+ {
+ /* Otherwise, print the value for both MI and the CLI, except
+ for the case of MI_PRINT_NO_VALUES. */
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, opts, 0, args_type, language)
+ == PY_BT_ERROR)
+ {
+ do_cleanups (cleanups);
+ goto error;
+ }
+ }
+ }
+
+ do_cleanups (cleanups);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+/* 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
+ iterable object, OUT is the output stream, ARGS_TYPE is an
+ enumerator describing the argument format, PRINT_ARGS_FIELD is a
+ flag which indicates if we output "ARGS=1" in MI output in commands
+ where both arguments and locals are printed, and FRAME is the
+ backing frame. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+enumerate_args (PyObject *iter,
+ struct ui_out *out,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ volatile struct gdb_exception except;
+
+ get_user_print_options (&opts);
+
+ if (args_type == CLI_SCALAR_VALUES)
+ {
+ /* True in "summary" mode, false otherwise. */
+ opts.summary = 1;
+ }
+
+ opts.deref_ref = 1;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* 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 == NULL && PyErr_Occurred ())
+ goto error;
+
+ while (item)
+ {
+ const struct language_defn *language;
+ char *sym_name;
+ struct symbol *sym;
+ struct value *val;
+ enum py_bt_status success = PY_BT_ERROR;
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ 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, MI_PRINT_ARGS))
+ {
+ xfree (sym_name);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it using
+ read_frame_args and account for entry values, if any. */
+ if (val == NULL)
+ {
+ struct frame_arg arg, entryarg;
+
+ /* If there is no value, and also no symbol, set error and
+ exit. */
+ if (sym == NULL)
+ {
+ 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);
+ gdbpy_convert_exception (except);
+ 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)
+ {
+ if (py_print_single_arg (out, NULL, &arg,
+ NULL, &opts,
+ args_type,
+ print_args_field,
+ NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ if (entryarg.entry_kind != print_entry_values_no)
+ {
+ if (arg.entry_kind != print_entry_values_only)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ ui_out_wrap_hint (out, " ");
+ }
+ if (except.reason < 0)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (py_print_single_arg (out, NULL, &entryarg, NULL,
+ &opts, args_type,
+ print_args_field, NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ xfree (arg.error);
+ xfree (entryarg.error);
+ }
+ else
+ {
+ /* If the object has provided a value, we just print that. */
+ if (val != NULL)
+ {
+ if (py_print_single_arg (out, sym_name, NULL, val, &opts,
+ args_type, print_args_field,
+ language) == PY_BT_ERROR)
+ {
+ xfree (sym_name);
+ goto error;
+ }
+ }
+ }
+
+ 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 != NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else if (PyErr_Occurred ())
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_end ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+
+/* Helper function to loop over variables provided by the
+ "frame_locals" Python API. Elements in the iterable must conform
+ to the "Symbol Value" interface. ITER is the Python iterable
+ object, OUT is the output stream, INDENT is whether we should
+ indent the output (for CLI), ARGS_TYPE is an enumerator describing
+ the argument format, PRINT_ARGS_FIELD is flag which indicates
+ whether to output the ARGS field in the case of
+ -stack-list-variables and FRAME is the backing frame. Returns
+ PY_BT_ERROR on error, with any GDB exceptions converted to a Python
+ exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+enumerate_locals (PyObject *iter,
+ struct ui_out *out,
+ int indent,
+ enum py_frame_args args_type,
+ int print_args_field,
+ 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;
+ enum py_bt_status success = PY_BT_ERROR;
+ struct symbol *sym;
+ volatile struct gdb_exception except;
+ int local_indent = 8 + (8 * indent);
+ struct cleanup *locals_cleanups;
+
+ locals_cleanups = make_cleanup_py_decref (item);
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ make_cleanup (xfree, sym_name);
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ if (sym != NULL && ui_out_is_mi_like_p (out)
+ && ! mi_should_print (sym, MI_PRINT_LOCALS))
+ {
+ do_cleanups (locals_cleanups);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it. */
+ if (val == NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ val = read_var_value (sym, frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ do_cleanups (locals_cleanups);
+ 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_args_field || args_type != NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ {
+ /* If the output is not MI we indent locals. */
+ ui_out_spaces (out, local_indent);
+ }
+
+ ui_out_field_string (out, "name", sym_name);
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " = ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (py_print_type (out, val) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+
+ /* CLI always prints values for locals. MI uses the
+ simple/no/all system. */
+ if (! ui_out_is_mi_like_p (out))
+ {
+ int val_indent = (indent + 1) * 4;
+
+ if (py_print_value (out, val, &opts, val_indent, args_type,
+ language) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+ else
+ {
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, &opts, 0, args_type,
+ language) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+ }
+
+ do_cleanups (locals_cleanups);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+/* Helper function for -stack-list-variables. Returns PY_BT_ERROR on
+ error, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_mi_print_variables (PyObject *filter, struct ui_out *out,
+ struct value_print_options *opts,
+ enum py_frame_args 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 == NULL)
+ goto error;
+
+ locals_iter = get_py_iter_from_func (filter, "frame_locals");
+ if (locals_iter == NULL)
+ 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, args_type, 1, frame) == PY_BT_ERROR)
+ goto error;
+
+ if (locals_iter != Py_None)
+ if (enumerate_locals (locals_iter, out, 1, args_type, 1, frame)
+ == PY_BT_ERROR)
+ goto error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing locals. This function largely just
+ creates the wrapping tuple, and calls enumerate_locals. Returns
+ PY_BT_ERROR on error, or PY_BT_OK on success.*/
+
+static enum py_bt_status
+py_print_locals (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args args_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 == NULL)
+ goto locals_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "locals");
+
+ if (locals_iter != Py_None)
+ if (enumerate_locals (locals_iter, out, indent, args_type,
+ 0, frame) == PY_BT_ERROR)
+ goto locals_error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;;
+
+ locals_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing frame arguments. This function
+ largely just creates the wrapping tuple, and calls enumerate_args.
+ Returns PY_BT_ERROR on error, with any GDB exceptions converted to
+ a Python exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_print_args (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args 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);
+ volatile struct gdb_exception except;
+
+ if (args_iter == NULL)
+ goto args_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "args");
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " (");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ if (args_iter != Py_None)
+ if (enumerate_args (args_iter, out, args_type, 0, frame) == PY_BT_ERROR)
+ goto args_error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, ")");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ args_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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. FLAGS is an integer
+ describing the various print options. The FLAGS variables is
+ described in "apply_frame_filter" function. ARGS_TYPE is an
+ enumerator describing the argument format. OUT is the output
+ stream to print, 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). Returns PY_BT_ERROR on error, with any
+ GDB exceptions converted to a Python exception, or PY_BT_COMPLETED
+ on success. */
+
+static enum py_bt_status
+py_print_frame (PyObject *filter, int flags, enum py_frame_args 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 *py_inf_frame, *elided;
+ int print_level, print_frame_info, print_args, print_locals;
+ volatile struct gdb_exception except;
+
+ /* 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;
+
+ 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. */
+ py_inf_frame = PyObject_CallMethod (filter, "inferior_frame", NULL);
+ if (py_inf_frame == NULL)
+ goto error;
+
+ frame = frame_object_to_frame_info (py_inf_frame);;
+
+ Py_DECREF (py_inf_frame);
+
+ if (frame == NULL)
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+
+ /* stack-list-variables. */
+ if (print_locals && print_args && ! print_frame_info)
+ {
+ if (py_mi_print_variables (filter, out, &opts,
+ args_type, frame) == PY_BT_ERROR)
+ goto error;
+ else
+ {
+ do_cleanups (cleanup_stack);
+ 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)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_spaces (out, indent*4);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* 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 != NULL)
+ {
+ 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;
+ volatile struct gdb_exception except;
+
+ slot = (struct frame_info **) htab_find_slot (levels_printed,
+ frame, INSERT);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 == frame)
+ ui_out_field_skip (out, "level");
+ 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 (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_frame_info)
+ {
+ /* Print address to the address field. If an address is not provided,
+ print nothing. */
+ if (opts.addressprint && has_addr)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_address ();
+ ui_out_field_core_addr (out, "addr", gdbarch, address);
+ annotate_frame_address_end ();
+ ui_out_text (out, " in ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* Print frame function name. */
+ if (PyObject_HasAttrString (filter, "function"))
+ {
+ PyObject *py_func = PyObject_CallMethod (filter, "function", NULL);
+
+ if (py_func != NULL)
+ {
+ const char *function = NULL;
+
+ if (gdbpy_is_string (py_func))
+ {
+ function = PyString_AsString (py_func);
+
+ if (function == NULL)
+ {
+ Py_DECREF (py_func);
+ goto error;
+ }
+ }
+ else if (PyLong_Check (py_func))
+ {
+ CORE_ADDR addr = PyLong_AsUnsignedLongLong (py_func);
+ struct bound_minimal_symbol msymbol;
+
+ if (PyErr_Occurred ())
+ goto error;
+
+ msymbol = lookup_minimal_symbol_by_pc (addr);
+ if (msymbol.minsym != NULL)
+ function = SYMBOL_PRINT_NAME (msymbol.minsym);
+ }
+ else if (py_func != Py_None)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("FrameDecorator.function: expecting a " \
+ "String, integer or None."));
+ Py_DECREF (py_func);
+ goto error;
+ }
+
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_function_name ();
+ if (function == NULL)
+ ui_out_field_skip (out, "func");
+ else
+ ui_out_field_string (out, "func", function);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_func);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_func);
+ }
+ else
+ goto error;
+ }
+
+
+ /* Frame arguments. Check the result, and error if something went
+ wrong. */
+ if (print_args)
+ {
+ if (py_print_args (filter, out, args_type, frame) == PY_BT_ERROR)
+ goto error;
+ }
+
+ /* File name/source/line number information. */
+ if (print_frame_info)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_source_begin ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "filename"))
+ {
+ PyObject *py_fn = PyObject_CallMethod (filter, "filename",
+ NULL);
+ if (py_fn != NULL)
+ {
+ if (py_fn != Py_None)
+ {
+ char *filename = PyString_AsString (py_fn);
+
+ if (filename == NULL)
+ {
+ Py_DECREF (py_fn);
+ goto error;
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_fn);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_fn);
+ }
+ else
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "line"))
+ {
+ PyObject *py_line = PyObject_CallMethod (filter, "line", NULL);
+ int line;
+
+ if (py_line != NULL)
+ {
+ if (py_line != Py_None)
+ {
+ line = PyLong_AsLong (py_line);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ":");
+ annotate_frame_source_line ();
+ ui_out_field_int (out, "line", line);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_line);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_line);
+ }
+ 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))
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_end ();
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_locals)
+ {
+ if (py_print_locals (filter, out, args_type, indent,
+ frame) == PY_BT_ERROR)
+ goto error;
+ }
+
+ /* Finally recursively print elided frames, if any. */
+ elided = get_py_iter_from_func (filter, "elided");
+ if (elided == NULL)
+ goto error;
+
+ make_cleanup_py_decref (elided);
+ if (elided != Py_None)
+ {
+ PyObject *item;
+
+ make_cleanup_ui_out_list_begin_end (out, "children");
+
+ if (! ui_out_is_mi_like_p (out))
+ indent++;
+
+ while ((item = PyIter_Next (elided)))
+ {
+ enum py_bt_status success = py_print_frame (item, flags,
+ args_type, out,
+ indent,
+ levels_printed);
+
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ }
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+ }
+
+
+ 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,
+ int frame_low, int frame_high)
+{
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+ PyObject *module, *sort_func, *iterable, *frame_obj, *iterator;
+ PyObject *py_frame_low, *py_frame_high;
+
+ frame_obj = frame_info_to_frame_object (frame);
+ if (frame_obj == NULL)
+ goto error;
+ make_cleanup_py_decref (frame_obj);
+
+ module = PyImport_ImportModule ("gdb.frames");
+ if (module == NULL)
+ goto error;
+ make_cleanup_py_decref (module);
+
+ sort_func = PyObject_GetAttrString (module, "execute_frame_filters");
+ if (sort_func == NULL)
+ goto error;
+ make_cleanup_py_decref (sort_func);
+
+ py_frame_low = PyInt_FromLong (frame_low);
+ if (py_frame_low == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_low);
+
+ py_frame_high = PyInt_FromLong (frame_high);
+ if (py_frame_high == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_high);
+
+ iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj,
+ py_frame_low,
+ py_frame_high,
+ NULL);
+ if (iterable == NULL)
+ goto error;
+
+ do_cleanups (cleanups);
+
+ if (iterable != Py_None)
+ {
+ iterator = PyObject_GetIter (iterable);
+ Py_DECREF (iterable);
+ }
+ else
+ {
+ return iterable;
+ }
+
+ return iterator;
+
+ error:
+ do_cleanups (cleanups);
+ return NULL;
+}
+
+/* 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 the make-up 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. ARGS_TYPE is an enumerator describing the argument
+ format, OUT is the output stream to print. FRAME_LOW is the
+ beginning of the slice of frames to print, and FRAME_HIGH is the
+ upper limit of the frames to count. Returns PY_BT_ERROR on error,
+ or PY_BT_COMPLETED on success.*/
+
+enum py_bt_status
+apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+
+{
+ struct gdbarch *gdbarch = NULL;
+ struct cleanup *cleanups;
+ enum py_bt_status success = PY_BT_ERROR;
+ PyObject *iterable;
+ volatile struct gdb_exception except;
+ PyObject *item;
+ htab_t levels_printed;
+
+ cleanups = ensure_python_env (gdbarch, current_language);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ iterable = bootstrap_python_frame_filters (frame, frame_low, frame_high);
+
+ if (iterable == NULL)
+ goto error;
+
+ /* If iterable is None, then there are no 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)
+ {
+ success = PY_BT_NO_FILTERS;
+ goto done;
+ }
+
+ levels_printed = htab_create (20,
+ htab_hash_pointer,
+ htab_eq_pointer,
+ NULL);
+ make_cleanup_htab_delete (levels_printed);
+
+ while ((item = PyIter_Next (iterable)))
+ {
+ success = py_print_frame (item, flags, args_type, out, 0,
+ levels_printed);
+
+ /* Do not exit on error printing a single frame. Print the
+ error and continue with other frames. */
+ if (success == PY_BT_ERROR)
+ gdbpy_print_stack ();
+
+ Py_DECREF (item);
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ done:
+ do_cleanups (cleanups);
+ return success;
+
+ error:
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index db51f50..6fa3035 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);
Py_TYPE (self)->tp_free (self);
}
@@ -81,6 +84,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -129,6 +139,47 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this object file. */
+PyObject *
+objfpy_get_frame_filters (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +276,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -270,6 +328,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 45a5193..104b36d 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);
Py_TYPE (self)->tp_free (self);
}
@@ -89,6 +92,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -137,6 +147,47 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this program space. */
+PyObject *
+pspy_get_frame_filters (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +272,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -257,6 +315,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 b280c8c..890b65d 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -48,6 +48,28 @@ 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;
+
+ 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, 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 ea97226..7337bff 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -251,9 +251,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 *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -304,6 +306,7 @@ void gdbpy_initialize_new_objfile_event (void);
void gdbpy_initialize_arch (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 67d06e5..3e2a852 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1393,6 +1393,15 @@ free_type_printers (void *arg)
{
}
+enum py_bt_status
+apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+{
+ return PY_BT_NO_FILTERS;
+}
+
#endif /* HAVE_PYTHON */
\f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 24e3077..1a1e5c2 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,66 @@ 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 return values. */
+enum py_bt_status
+ {
+ /* Return when an error has occurred in processing frame filters,
+ or when printing the stack. */
+ PY_BT_ERROR = -1,
+
+ /* Return from internal routines to indicate that the function
+ succeeded. */
+ PY_BT_OK = 1,
+
+ /* Return when the frame filter process is complete, and all
+ operations have succeeded. */
+ PY_BT_COMPLETED = 2,
+
+ /* Return when the frame filter process is complete, but there
+ were no filter registered and enabled to process. */
+ PY_BT_NO_FILTERS = 3
+ };
+
+/* 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,
+ };
+
+/* A choice of the different frame argument printing strategies that
+ can occur in different cases of frame filter instantiation. */
+typedef enum py_frame_args
+{
+ /* Print no values for arguments when invoked from the MI. */
+ NO_VALUES = PRINT_NO_VALUES,
+
+ MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
+
+ /* Print only simple values (what MI defines as "simple") for
+ arguments when invoked from the MI. */
+ MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES,
+
+
+ /* Print only scalar values for arguments when invoked from the
+ CLI. */
+ CLI_SCALAR_VALUES,
+
+ /* Print all values for arguments when invoked from the
+ CLI. */
+ CLI_ALL_VALUES
+} py_frame_args;
+
extern void finish_python_initialization (void);
void eval_python_from_control_command (struct command_line *);
@@ -41,6 +102,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
const struct value_print_options *options,
const struct language_defn *language);
+enum py_bt_status apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high);
+
void preserve_python_values (struct objfile *objfile, htab_t copied_types);
void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-05-09 13:23 ` Phil Muldoon
@ 2013-05-09 15:14 ` Tom Tromey
2013-05-10 10:42 ` Phil Muldoon
0 siblings, 1 reply; 16+ messages in thread
From: Tom Tromey @ 2013-05-09 15:14 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> I have attached two patches. The first one is a delta to the attached
Phil> patch, so you can see the changes I made in an easy manner. The
Phil> second is the whole python/ patchset for the change you requested
Phil> above.
Phil> What do you think?
I think this is fine.
This patch is ok.
Tom
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [patch][python] 1 of 5 - Frame filter Python C code changes.
2013-05-09 15:14 ` Tom Tromey
@ 2013-05-10 10:42 ` Phil Muldoon
0 siblings, 0 replies; 16+ messages in thread
From: Phil Muldoon @ 2013-05-10 10:42 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 09/05/13 16:14, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
>
> Phil> I have attached two patches. The first one is a delta to the attached
> Phil> patch, so you can see the changes I made in an easy manner. The
> Phil> second is the whole python/ patchset for the change you requested
> Phil> above.
>
> Phil> What do you think?
>
> I think this is fine.
> This patch is ok.
This patch has now been committed, thanks.
Cheers,
Phil
--
2013-05-10 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/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.
--
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
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
new file mode 100644
index 0000000..d62c596
--- /dev/null
+++ b/gdb/python/py-framefilter.c
@@ -0,0 +1,1528 @@
+/* Python frame filters
+
+ Copyright (C) 2013 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 "demangle.h"
+#include "mi/mi-cmds.h"
+#include "python-internal.h"
+
+enum mi_print_types
+{
+ MI_PRINT_ARGS,
+ MI_PRINT_LOCALS
+};
+
+/* Helper function to extract a symbol, a name and a 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
+ PY_BT_ERROR on error with the appropriate Python exception set, and
+ PY_BT_OK on success. */
+
+static enum py_bt_status
+extract_sym (PyObject *obj, char **name, struct symbol **sym,
+ const struct language_defn **language)
+{
+ PyObject *result = PyObject_CallMethod (obj, "symbol", NULL);
+
+ if (result == NULL)
+ return PY_BT_ERROR;
+
+ /* For 'symbol' callback, the function can return a symbol or a
+ string. */
+ if (gdbpy_is_string (result))
+ {
+ *name = python_string_to_host_string (result);
+ Py_DECREF (result);
+
+ if (*name == NULL)
+ return PY_BT_ERROR;
+ /* If the API returns a string (and not a symbol), then there is
+ no symbol derived language available and the frame filter has
+ either overridden the symbol with a string, or supplied a
+ entirely synthetic symbol/value pairing. In that case, use
+ python_language. */
+ *language = python_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 == NULL)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Unexpected value. Expecting a "
+ "gdb.Symbol or a Python string."));
+ return PY_BT_ERROR;
+ }
+
+ /* Duplicate the symbol name, so the caller has consistency
+ in garbage collection. */
+ *name = xstrdup (SYMBOL_PRINT_NAME (*sym));
+
+ /* If a symbol is specified attempt to determine the language
+ from the symbol. If mode is not "auto", then the language
+ has been explicitly set, use that. */
+ if (language_mode == language_mode_auto)
+ *language = language_def (SYMBOL_LANGUAGE (*sym));
+ else
+ *language = current_language;
+ }
+
+ return PY_BT_OK;
+}
+
+/* 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 PY_BT_ERROR
+ on error with the appropriate Python exception set, and PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+extract_value (PyObject *obj, struct value **value)
+{
+ if (PyObject_HasAttrString (obj, "value"))
+ {
+ PyObject *vresult = PyObject_CallMethod (obj, "value", NULL);
+
+ if (vresult == NULL)
+ return PY_BT_ERROR;
+
+ /* 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 PY_BT_OK;
+ }
+ else
+ {
+ *value = convert_value_from_python (vresult);
+ Py_DECREF (vresult);
+
+ if (*value == NULL)
+ return PY_BT_ERROR;
+
+ return PY_BT_OK;
+ }
+ }
+ else
+ *value = NULL;
+
+ return PY_BT_OK;
+}
+
+/* MI prints only certain values according to the type of symbol and
+ also what the user has specified. SYM is the symbol to check, and
+ MI_PRINT_TYPES is an enum specifying what the user wants emitted
+ for the MI command in question. */
+static int
+mi_should_print (struct symbol *sym, enum mi_print_types 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 (type == MI_PRINT_LOCALS)
+ print_me = ! SYMBOL_IS_ARGUMENT (sym);
+ else
+ print_me = SYMBOL_IS_ARGUMENT (sym);
+ }
+ return print_me;
+}
+
+/* Helper function which outputs a type name extracted from VAL to a
+ "type" field in the output stream OUT. 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 PY_BT_ERROR on error, with
+ any GDB exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+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)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function which outputs a value to an output field in a
+ stream. OUT is the ui-out structure the value will be output to,
+ VAL is the value that will be printed, OPTS contains the value
+ printing options, ARGS_TYPE is an enumerator describing the
+ argument format, and LANGUAGE is the language_defn that the value
+ will be printed with. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+py_print_value (struct ui_out *out, struct value *val,
+ const struct value_print_options *opts,
+ int indent,
+ enum py_frame_args args_type,
+ const struct language_defn *language)
+{
+ int should_print = 0;
+ volatile struct gdb_exception except;
+ int local_indent = (4 * indent);
+
+ /* Never set an indent level for common_val_print if MI. */
+ if (ui_out_is_mi_like_p (out))
+ local_indent = 0;
+
+ /* MI does not print certain values, differentiated by type,
+ depending on what ARGS_TYPE indicates. Test type against option.
+ For CLI print all values. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES
+ || args_type == MI_PRINT_ALL_VALUES)
+ {
+ struct type *type = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = check_typedef (value_type (val));
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+
+ if (args_type == MI_PRINT_ALL_VALUES)
+ should_print = 1;
+ else if (args_type == MI_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 if (args_type != NO_VALUES)
+ should_print = 1;
+
+ if (should_print)
+ {
+ 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, indent, opts, language);
+ ui_out_field_stream (out, "value", stb);
+ do_cleanups (cleanup);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ return PY_BT_ERROR;
+ }
+ }
+
+ return PY_BT_OK;
+}
+
+/* Helper function to call a Python method and extract an iterator
+ from the result. If the function returns anything but an iterator
+ the exception is preserved and NULL is returned. 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 NULL. */
+
+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 != NULL)
+ {
+ if (result == Py_None)
+ {
+ return result;
+ }
+ else
+ {
+ PyObject *iterator = PyObject_GetIter (result);
+
+ Py_DECREF (result);
+ return iterator;
+ }
+ }
+ }
+ else
+ Py_RETURN_NONE;
+
+ return NULL;
+}
+
+/* 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,
+ ARGS_TYPE is an enumerator describing the argument format,
+ PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1"
+ in MI output in commands where both arguments and locals are
+ printed. Returns PY_BT_ERROR on error, with any GDB exceptions
+ converted to a Python exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_print_single_arg (struct ui_out *out,
+ const char *sym_name,
+ struct frame_arg *fa,
+ struct value *fv,
+ const struct value_print_options *opts,
+ enum py_frame_args args_type,
+ int print_args_field,
+ const struct language_defn *language)
+{
+ struct value *val;
+ volatile struct gdb_exception except;
+
+ if (fa != NULL)
+ {
+ language = language_def (SYMBOL_LANGUAGE (fa->sym));
+ val = fa->val;
+ }
+ else
+ val = fv;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+
+ /* 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. The
+ exception is -stack-list-variables which emits an ARGS="1" field
+ if the value is a frame argument. This is denoted in this
+ function with PRINT_ARGS_FIELD which is flag from the caller to
+ emit the ARGS field. */
+ if (ui_out_is_mi_like_p (out))
+ {
+ if (print_args_field || args_type != NO_VALUES)
+ 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 != NULL)
+ {
+ struct ui_file *stb;
+
+ stb = mem_fileopen ();
+ make_cleanup_ui_file_delete (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_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);
+ }
+ 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_args_field)
+ ui_out_field_int (out, "arg", 1);
+
+ /* For MI print the type, but only for simple values. This seems
+ weird, but this is how MI choose to format the various output
+ types. */
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (py_print_type (out, val) == PY_BT_ERROR)
+ {
+ do_cleanups (cleanups);
+ 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) && args_type == NO_VALUES)
+ ui_out_field_string (out, "value", "...");
+ else
+ {
+ /* Otherwise, print the value for both MI and the CLI, except
+ for the case of MI_PRINT_NO_VALUES. */
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, opts, 0, args_type, language)
+ == PY_BT_ERROR)
+ {
+ do_cleanups (cleanups);
+ goto error;
+ }
+ }
+ }
+
+ do_cleanups (cleanups);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+/* 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
+ iterable object, OUT is the output stream, ARGS_TYPE is an
+ enumerator describing the argument format, PRINT_ARGS_FIELD is a
+ flag which indicates if we output "ARGS=1" in MI output in commands
+ where both arguments and locals are printed, and FRAME is the
+ backing frame. Returns PY_BT_ERROR on error, with any GDB
+ exceptions converted to a Python exception, or PY_BT_OK on
+ success. */
+
+static enum py_bt_status
+enumerate_args (PyObject *iter,
+ struct ui_out *out,
+ enum py_frame_args args_type,
+ int print_args_field,
+ struct frame_info *frame)
+{
+ PyObject *item;
+ struct value_print_options opts;
+ volatile struct gdb_exception except;
+
+ get_user_print_options (&opts);
+
+ if (args_type == CLI_SCALAR_VALUES)
+ {
+ /* True in "summary" mode, false otherwise. */
+ opts.summary = 1;
+ }
+
+ opts.deref_ref = 1;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ /* 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 == NULL && PyErr_Occurred ())
+ goto error;
+
+ while (item)
+ {
+ const struct language_defn *language;
+ char *sym_name;
+ struct symbol *sym;
+ struct value *val;
+ enum py_bt_status success = PY_BT_ERROR;
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ 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, MI_PRINT_ARGS))
+ {
+ xfree (sym_name);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it using
+ read_frame_args and account for entry values, if any. */
+ if (val == NULL)
+ {
+ struct frame_arg arg, entryarg;
+
+ /* If there is no value, and also no symbol, set error and
+ exit. */
+ if (sym == NULL)
+ {
+ 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);
+ gdbpy_convert_exception (except);
+ 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)
+ {
+ if (py_print_single_arg (out, NULL, &arg,
+ NULL, &opts,
+ args_type,
+ print_args_field,
+ NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ if (entryarg.entry_kind != print_entry_values_no)
+ {
+ if (arg.entry_kind != print_entry_values_only)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ ui_out_wrap_hint (out, " ");
+ }
+ if (except.reason < 0)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (py_print_single_arg (out, NULL, &entryarg, NULL,
+ &opts, args_type,
+ print_args_field, NULL) == PY_BT_ERROR)
+ {
+ xfree (arg.error);
+ xfree (entryarg.error);
+ xfree (sym_name);
+ goto error;
+ }
+ }
+
+ xfree (arg.error);
+ xfree (entryarg.error);
+ }
+ else
+ {
+ /* If the object has provided a value, we just print that. */
+ if (val != NULL)
+ {
+ if (py_print_single_arg (out, sym_name, NULL, val, &opts,
+ args_type, print_args_field,
+ language) == PY_BT_ERROR)
+ {
+ xfree (sym_name);
+ goto error;
+ }
+ }
+ }
+
+ 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 != NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ", ");
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ else if (PyErr_Occurred ())
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_arg_end ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (item);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+
+/* Helper function to loop over variables provided by the
+ "frame_locals" Python API. Elements in the iterable must conform
+ to the "Symbol Value" interface. ITER is the Python iterable
+ object, OUT is the output stream, INDENT is whether we should
+ indent the output (for CLI), ARGS_TYPE is an enumerator describing
+ the argument format, PRINT_ARGS_FIELD is flag which indicates
+ whether to output the ARGS field in the case of
+ -stack-list-variables and FRAME is the backing frame. Returns
+ PY_BT_ERROR on error, with any GDB exceptions converted to a Python
+ exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+enumerate_locals (PyObject *iter,
+ struct ui_out *out,
+ int indent,
+ enum py_frame_args args_type,
+ int print_args_field,
+ 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;
+ enum py_bt_status success = PY_BT_ERROR;
+ struct symbol *sym;
+ volatile struct gdb_exception except;
+ int local_indent = 8 + (8 * indent);
+ struct cleanup *locals_cleanups;
+
+ locals_cleanups = make_cleanup_py_decref (item);
+
+ success = extract_sym (item, &sym_name, &sym, &language);
+ if (success == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ make_cleanup (xfree, sym_name);
+
+ success = extract_value (item, &val);
+ if (success == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ if (sym != NULL && ui_out_is_mi_like_p (out)
+ && ! mi_should_print (sym, MI_PRINT_LOCALS))
+ {
+ do_cleanups (locals_cleanups);
+ continue;
+ }
+
+ /* If the object did not provide a value, read it. */
+ if (val == NULL)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ val = read_var_value (sym, frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ do_cleanups (locals_cleanups);
+ 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_args_field || args_type != NO_VALUES)
+ make_cleanup_ui_out_tuple_begin_end (out, NULL);
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ {
+ /* If the output is not MI we indent locals. */
+ ui_out_spaces (out, local_indent);
+ }
+
+ ui_out_field_string (out, "name", sym_name);
+
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " = ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+
+ if (args_type == MI_PRINT_SIMPLE_VALUES)
+ {
+ if (py_print_type (out, val) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+
+ /* CLI always prints values for locals. MI uses the
+ simple/no/all system. */
+ if (! ui_out_is_mi_like_p (out))
+ {
+ int val_indent = (indent + 1) * 4;
+
+ if (py_print_value (out, val, &opts, val_indent, args_type,
+ language) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+ else
+ {
+ if (args_type != NO_VALUES)
+ {
+ if (py_print_value (out, val, &opts, 0, args_type,
+ language) == PY_BT_ERROR)
+ {
+ do_cleanups (locals_cleanups);
+ goto error;
+ }
+ }
+ }
+
+ do_cleanups (locals_cleanups);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ return PY_BT_OK;
+
+ error:
+ return PY_BT_ERROR;
+}
+
+/* Helper function for -stack-list-variables. Returns PY_BT_ERROR on
+ error, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_mi_print_variables (PyObject *filter, struct ui_out *out,
+ struct value_print_options *opts,
+ enum py_frame_args 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 == NULL)
+ goto error;
+
+ locals_iter = get_py_iter_from_func (filter, "frame_locals");
+ if (locals_iter == NULL)
+ 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, args_type, 1, frame) == PY_BT_ERROR)
+ goto error;
+
+ if (locals_iter != Py_None)
+ if (enumerate_locals (locals_iter, out, 1, args_type, 1, frame)
+ == PY_BT_ERROR)
+ goto error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing locals. This function largely just
+ creates the wrapping tuple, and calls enumerate_locals. Returns
+ PY_BT_ERROR on error, or PY_BT_OK on success.*/
+
+static enum py_bt_status
+py_print_locals (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args args_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 == NULL)
+ goto locals_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "locals");
+
+ if (locals_iter != Py_None)
+ if (enumerate_locals (locals_iter, out, indent, args_type,
+ 0, frame) == PY_BT_ERROR)
+ goto locals_error;
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;;
+
+ locals_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* Helper function for printing frame arguments. This function
+ largely just creates the wrapping tuple, and calls enumerate_args.
+ Returns PY_BT_ERROR on error, with any GDB exceptions converted to
+ a Python exception, or PY_BT_OK on success. */
+
+static enum py_bt_status
+py_print_args (PyObject *filter,
+ struct ui_out *out,
+ enum py_frame_args 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);
+ volatile struct gdb_exception except;
+
+ if (args_iter == NULL)
+ goto args_error;
+
+ make_cleanup_ui_out_list_begin_end (out, "args");
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_args ();
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, " (");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ if (args_iter != Py_None)
+ if (enumerate_args (args_iter, out, args_type, 0, frame) == PY_BT_ERROR)
+ goto args_error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! ui_out_is_mi_like_p (out))
+ ui_out_text (out, ")");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto args_error;
+ }
+
+ do_cleanups (old_chain);
+ return PY_BT_OK;
+
+ args_error:
+ do_cleanups (old_chain);
+ return PY_BT_ERROR;
+}
+
+/* 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. FLAGS is an integer
+ describing the various print options. The FLAGS variables is
+ described in "apply_frame_filter" function. ARGS_TYPE is an
+ enumerator describing the argument format. OUT is the output
+ stream to print, 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). Returns PY_BT_ERROR on error, with any
+ GDB exceptions converted to a Python exception, or PY_BT_COMPLETED
+ on success. */
+
+static enum py_bt_status
+py_print_frame (PyObject *filter, int flags, enum py_frame_args 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 *py_inf_frame, *elided;
+ int print_level, print_frame_info, print_args, print_locals;
+ volatile struct gdb_exception except;
+
+ /* 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;
+
+ 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. */
+ py_inf_frame = PyObject_CallMethod (filter, "inferior_frame", NULL);
+ if (py_inf_frame == NULL)
+ goto error;
+
+ frame = frame_object_to_frame_info (py_inf_frame);;
+
+ Py_DECREF (py_inf_frame);
+
+ if (frame == NULL)
+ goto error;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+
+ /* stack-list-variables. */
+ if (print_locals && print_args && ! print_frame_info)
+ {
+ if (py_mi_print_variables (filter, out, &opts,
+ args_type, frame) == PY_BT_ERROR)
+ goto error;
+ else
+ {
+ do_cleanups (cleanup_stack);
+ 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)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_spaces (out, indent*4);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* 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 != NULL)
+ {
+ 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;
+ volatile struct gdb_exception except;
+
+ slot = (struct frame_info **) htab_find_slot (levels_printed,
+ frame, INSERT);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 == frame)
+ ui_out_field_skip (out, "level");
+ 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 (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_frame_info)
+ {
+ /* Print address to the address field. If an address is not provided,
+ print nothing. */
+ if (opts.addressprint && has_addr)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_address ();
+ ui_out_field_core_addr (out, "addr", gdbarch, address);
+ annotate_frame_address_end ();
+ ui_out_text (out, " in ");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ /* Print frame function name. */
+ if (PyObject_HasAttrString (filter, "function"))
+ {
+ PyObject *py_func = PyObject_CallMethod (filter, "function", NULL);
+
+ if (py_func != NULL)
+ {
+ const char *function = NULL;
+
+ if (gdbpy_is_string (py_func))
+ {
+ function = PyString_AsString (py_func);
+
+ if (function == NULL)
+ {
+ Py_DECREF (py_func);
+ goto error;
+ }
+ }
+ else if (PyLong_Check (py_func))
+ {
+ CORE_ADDR addr = PyLong_AsUnsignedLongLong (py_func);
+ struct bound_minimal_symbol msymbol;
+
+ if (PyErr_Occurred ())
+ goto error;
+
+ msymbol = lookup_minimal_symbol_by_pc (addr);
+ if (msymbol.minsym != NULL)
+ function = SYMBOL_PRINT_NAME (msymbol.minsym);
+ }
+ else if (py_func != Py_None)
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("FrameDecorator.function: expecting a " \
+ "String, integer or None."));
+ Py_DECREF (py_func);
+ goto error;
+ }
+
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_function_name ();
+ if (function == NULL)
+ ui_out_field_skip (out, "func");
+ else
+ ui_out_field_string (out, "func", function);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_func);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_func);
+ }
+ else
+ goto error;
+ }
+
+
+ /* Frame arguments. Check the result, and error if something went
+ wrong. */
+ if (print_args)
+ {
+ if (py_print_args (filter, out, args_type, frame) == PY_BT_ERROR)
+ goto error;
+ }
+
+ /* File name/source/line number information. */
+ if (print_frame_info)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_source_begin ();
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "filename"))
+ {
+ PyObject *py_fn = PyObject_CallMethod (filter, "filename",
+ NULL);
+ if (py_fn != NULL)
+ {
+ if (py_fn != Py_None)
+ {
+ char *filename = PyString_AsString (py_fn);
+
+ if (filename == NULL)
+ {
+ Py_DECREF (py_fn);
+ goto error;
+ }
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ 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 ();
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_fn);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_fn);
+ }
+ else
+ goto error;
+ }
+
+ if (PyObject_HasAttrString (filter, "line"))
+ {
+ PyObject *py_line = PyObject_CallMethod (filter, "line", NULL);
+ int line;
+
+ if (py_line != NULL)
+ {
+ if (py_line != Py_None)
+ {
+ line = PyLong_AsLong (py_line);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ui_out_text (out, ":");
+ annotate_frame_source_line ();
+ ui_out_field_int (out, "line", line);
+ }
+ if (except.reason < 0)
+ {
+ Py_DECREF (py_line);
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+ Py_DECREF (py_line);
+ }
+ 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))
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ annotate_frame_end ();
+ ui_out_text (out, "\n");
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+ }
+
+ if (print_locals)
+ {
+ if (py_print_locals (filter, out, args_type, indent,
+ frame) == PY_BT_ERROR)
+ goto error;
+ }
+
+ /* Finally recursively print elided frames, if any. */
+ elided = get_py_iter_from_func (filter, "elided");
+ if (elided == NULL)
+ goto error;
+
+ make_cleanup_py_decref (elided);
+ if (elided != Py_None)
+ {
+ PyObject *item;
+
+ make_cleanup_ui_out_list_begin_end (out, "children");
+
+ if (! ui_out_is_mi_like_p (out))
+ indent++;
+
+ while ((item = PyIter_Next (elided)))
+ {
+ enum py_bt_status success = py_print_frame (item, flags,
+ args_type, out,
+ indent,
+ levels_printed);
+
+ if (success == PY_BT_ERROR)
+ {
+ Py_DECREF (item);
+ goto error;
+ }
+
+ Py_DECREF (item);
+ }
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+ }
+
+
+ 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,
+ int frame_low, int frame_high)
+{
+ struct cleanup *cleanups =
+ make_cleanup (null_cleanup, NULL);
+ PyObject *module, *sort_func, *iterable, *frame_obj, *iterator;
+ PyObject *py_frame_low, *py_frame_high;
+
+ frame_obj = frame_info_to_frame_object (frame);
+ if (frame_obj == NULL)
+ goto error;
+ make_cleanup_py_decref (frame_obj);
+
+ module = PyImport_ImportModule ("gdb.frames");
+ if (module == NULL)
+ goto error;
+ make_cleanup_py_decref (module);
+
+ sort_func = PyObject_GetAttrString (module, "execute_frame_filters");
+ if (sort_func == NULL)
+ goto error;
+ make_cleanup_py_decref (sort_func);
+
+ py_frame_low = PyInt_FromLong (frame_low);
+ if (py_frame_low == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_low);
+
+ py_frame_high = PyInt_FromLong (frame_high);
+ if (py_frame_high == NULL)
+ goto error;
+ make_cleanup_py_decref (py_frame_high);
+
+ iterable = PyObject_CallFunctionObjArgs (sort_func, frame_obj,
+ py_frame_low,
+ py_frame_high,
+ NULL);
+ if (iterable == NULL)
+ goto error;
+
+ do_cleanups (cleanups);
+
+ if (iterable != Py_None)
+ {
+ iterator = PyObject_GetIter (iterable);
+ Py_DECREF (iterable);
+ }
+ else
+ {
+ return iterable;
+ }
+
+ return iterator;
+
+ error:
+ do_cleanups (cleanups);
+ return NULL;
+}
+
+/* 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 the make-up 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. ARGS_TYPE is an enumerator describing the argument
+ format, OUT is the output stream to print. FRAME_LOW is the
+ beginning of the slice of frames to print, and FRAME_HIGH is the
+ upper limit of the frames to count. Returns PY_BT_ERROR on error,
+ or PY_BT_COMPLETED on success.*/
+
+enum py_bt_status
+apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+
+{
+ struct gdbarch *gdbarch = NULL;
+ struct cleanup *cleanups;
+ enum py_bt_status success = PY_BT_ERROR;
+ PyObject *iterable;
+ volatile struct gdb_exception except;
+ PyObject *item;
+ htab_t levels_printed;
+
+ cleanups = ensure_python_env (gdbarch, current_language);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ {
+ gdbpy_convert_exception (except);
+ goto error;
+ }
+
+ iterable = bootstrap_python_frame_filters (frame, frame_low, frame_high);
+
+ if (iterable == NULL)
+ goto error;
+
+ /* If iterable is None, then there are no 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)
+ {
+ success = PY_BT_NO_FILTERS;
+ goto done;
+ }
+
+ levels_printed = htab_create (20,
+ htab_hash_pointer,
+ htab_eq_pointer,
+ NULL);
+ make_cleanup_htab_delete (levels_printed);
+
+ while ((item = PyIter_Next (iterable)))
+ {
+ success = py_print_frame (item, flags, args_type, out, 0,
+ levels_printed);
+
+ /* Do not exit on error printing a single frame. Print the
+ error and continue with other frames. */
+ if (success == PY_BT_ERROR)
+ gdbpy_print_stack ();
+
+ Py_DECREF (item);
+ }
+
+ if (item == NULL && PyErr_Occurred ())
+ goto error;
+
+ done:
+ do_cleanups (cleanups);
+ return success;
+
+ error:
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return PY_BT_ERROR;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index db51f50..6fa3035 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);
Py_TYPE (self)->tp_free (self);
}
@@ -81,6 +84,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -129,6 +139,47 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this object file. */
+PyObject *
+objfpy_get_frame_filters (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +276,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -270,6 +328,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 45a5193..104b36d 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);
Py_TYPE (self)->tp_free (self);
}
@@ -89,6 +92,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
return NULL;
}
+ self->frame_filters = PyDict_New ();
+ if (!self->frame_filters)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
+
self->type_printers = PyList_New (0);
if (!self->type_printers)
{
@@ -137,6 +147,47 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+/* Return the Python dictionary attribute containing frame filters for
+ this program space. */
+PyObject *
+pspy_get_frame_filters (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_filters);
+ return self->frame_filters;
+}
+
+/* Set this object file's frame filters dictionary to 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,6 +272,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->frame_filters = PyDict_New ();
+ if (!object->frame_filters)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
object->type_printers = PyList_New (0);
if (!object->type_printers)
{
@@ -257,6 +315,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 b280c8c..890b65d 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -48,6 +48,28 @@ 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;
+
+ 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, 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 ea97226..7337bff 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -251,9 +251,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 *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -304,6 +306,7 @@ void gdbpy_initialize_new_objfile_event (void);
void gdbpy_initialize_arch (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 67d06e5..3e2a852 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1393,6 +1393,15 @@ free_type_printers (void *arg)
{
}
+enum py_bt_status
+apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high)
+{
+ return PY_BT_NO_FILTERS;
+}
+
#endif /* HAVE_PYTHON */
\f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 24e3077..1a1e5c2 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,66 @@ 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 return values. */
+enum py_bt_status
+ {
+ /* Return when an error has occurred in processing frame filters,
+ or when printing the stack. */
+ PY_BT_ERROR = -1,
+
+ /* Return from internal routines to indicate that the function
+ succeeded. */
+ PY_BT_OK = 1,
+
+ /* Return when the frame filter process is complete, and all
+ operations have succeeded. */
+ PY_BT_COMPLETED = 2,
+
+ /* Return when the frame filter process is complete, but there
+ were no filter registered and enabled to process. */
+ PY_BT_NO_FILTERS = 3
+ };
+
+/* 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,
+ };
+
+/* A choice of the different frame argument printing strategies that
+ can occur in different cases of frame filter instantiation. */
+typedef enum py_frame_args
+{
+ /* Print no values for arguments when invoked from the MI. */
+ NO_VALUES = PRINT_NO_VALUES,
+
+ MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
+
+ /* Print only simple values (what MI defines as "simple") for
+ arguments when invoked from the MI. */
+ MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES,
+
+
+ /* Print only scalar values for arguments when invoked from the
+ CLI. */
+ CLI_SCALAR_VALUES,
+
+ /* Print all values for arguments when invoked from the
+ CLI. */
+ CLI_ALL_VALUES
+} py_frame_args;
+
extern void finish_python_initialization (void);
void eval_python_from_control_command (struct command_line *);
@@ -41,6 +102,11 @@ int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
const struct value_print_options *options,
const struct language_defn *language);
+enum py_bt_status apply_frame_filter (struct frame_info *frame, int flags,
+ enum py_frame_args args_type,
+ struct ui_out *out, int frame_low,
+ int frame_high);
+
void preserve_python_values (struct objfile *objfile, htab_t copied_types);
void gdbpy_load_auto_scripts_for_objfile (struct objfile *objfile);
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2013-05-10 10:42 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-30 14:31 [patch][python] 1 of 5 - Frame filter Python C code changes Phil Muldoon
2012-12-05 17:03 ` Tom Tromey
2012-12-07 13:41 ` Phil Muldoon
2012-12-07 19:03 ` Tom Tromey
2013-03-11 22:13 Phil Muldoon
2013-03-21 20:55 ` Tom Tromey
2013-03-22 17:15 ` Tom Tromey
2013-03-26 17:17 ` Phil Muldoon
2013-03-27 18:36 ` Tom Tromey
2013-04-22 16:01 Phil Muldoon
2013-04-26 6:47 ` Tom Tromey
2013-05-06 8:23 Phil Muldoon
2013-05-06 20:39 ` Tom Tromey
2013-05-09 13:23 ` Phil Muldoon
2013-05-09 15:14 ` Tom Tromey
2013-05-10 10:42 ` Phil Muldoon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox