2013-05-10 Phil Muldoon <pmuldoon@redhat.com>
authorPhil Muldoon <pmuldoon@redhat.com>
Fri, 10 May 2013 10:26:03 +0000 (10:26 +0000)
committerPhil Muldoon <pmuldoon@redhat.com>
Fri, 10 May 2013 10:26:03 +0000 (10:26 +0000)
* stack.c (backtrace_command_1): Add "no-filters", and Python frame
filter logic.
(backtrace_command): Add "no-filters" option parsing.
(_initialize_stack): Alter help to reflect "no-filters" option.
* Makefile.in (SUBDIR_PYTHON_OBS): Add py-framefilter.o
(SUBDIR_PYTHON_SRCS): Add py-framefilter.c
(py-frame.o): Add target
* data-directory/Makefile.in (PYTHON_DIR): Add Python frame
filter files.
* python/python.h: Add new frame filter constants, and flag enum.
(apply_frame_filter): Add definition.
* python/python.c (apply_frame_filter): New non-Python
enabled function.
* python/py-utils.c (py_xdecref): New function.
(make_cleanup_py_xdecref): Ditto.
* python/py-objfile.c: Declare frame_filters dictionary.
(objfpy_dealloc): Add frame_filters dealloc.
(objfpy_new): Initialize frame_filters attribute.
(objfile_to_objfile_object): Ditto.
(objfpy_get_frame_filters): New function.
(objfpy_set_frame_filters): New function.
* python/py-progspace.c: Declare frame_filters dictionary.
(pspy_dealloc): Add frame_filters dealloc.
(pspy_new): Initialize frame_filters attribute.
(pspacee_to_pspace_object): Ditto.
(pspy_get_frame_filters): New function.
(pspy_set_frame_filters): New function.
* python/py-framefilter.c: New file.
* python/lib/gdb/command/frame_filters.py: New file.
* python/lib/gdb/frames.py: New file.
* python/lib/gdb/__init__.py: Initialize global frame_filters
dictionary
* python/lib/gdb/FrameDecorator.py: New file.
* python/lib/gdb/FrameIterator.py: New file.
* mi/mi-cmds.c (mi_cmds): Add frame filters command.
* mi/mi-cmds.h: Declare.
* mi/mi-cmd-stack.c (mi_cmd_stack_list_frames): Add
--no-frame-filter logic, and Python frame filter logic.
(stack_enable_frame_filters): New function.
(parse_no_frame_option): Ditto.
(mi_cmd_stack_list_frames): Add --no-frame-filter and Python frame
filter logic.
(mi_cmd_stack_list_locals): Ditto.
(mi_cmd_stack_list_args): Ditto.
(mi_cmd_stack_list_variables): Ditto.
* NEWS: Add frame filter note.

2013-05-10  Phil Muldoon  <pmuldoon@redhat.com>

* gdb.python/py-framefilter.py: New File.
* gdb.python/py-framefilter-mi.exp: Ditto.
* gdb.python/py-framefilter.c: Ditto.
* gdb.python/py-framefilter-mi.exp: Ditto.
* gdb.python/py-framefilter-mi.c: Ditto,
* gdb.python/py-framefilter-gdb.py.in: Ditto.

2013-05-10 Phil Muldoon  <pmuldoon@redhat.com>

* gdb.texinfo (Backtrace): Add "no-filter" argument.
(Python API): Add Frame Filters API, Frame Wrapper API,
Writing a Frame Filter/Wrapper, Managing Management of Frame
Filters chapter entries.
(Frame Filters API): New Node.
(Frame Wrapper API): New Node.
(Writing a Frame Filter): New Node.
(Managing Frame Filters): New Node.
(Progspaces In Python): Add note about frame_filters attribute.
(Objfiles in Python): Ditto.
(GDB/MI Stack Manipulation): Add -enable-frame-filters command,
@anchors and --no-frame-filters option to -stack-list-variables,
-stack-list-frames, -stack-list-locals and -stack-list-arguments
commands.

29 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/data-directory/Makefile.in
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/mi/mi-cmd-stack.c
gdb/mi/mi-cmds.c
gdb/mi/mi-cmds.h
gdb/python/lib/gdb/FrameDecorator.py [new file with mode: 0644]
gdb/python/lib/gdb/FrameIterator.py [new file with mode: 0644]
gdb/python/lib/gdb/__init__.py
gdb/python/lib/gdb/command/frame_filters.py [new file with mode: 0644]
gdb/python/lib/gdb/frames.py [new file with mode: 0644]
gdb/python/py-framefilter.c [new file with mode: 0644]
gdb/python/py-objfile.c
gdb/python/py-progspace.c
gdb/python/py-utils.c
gdb/python/python-internal.h
gdb/python/python.c
gdb/python/python.h
gdb/stack.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-framefilter-gdb.py.in [new file with mode: 0644]
gdb/testsuite/gdb.python/py-framefilter-mi.c [new file with mode: 0644]
gdb/testsuite/gdb.python/py-framefilter-mi.exp [new file with mode: 0644]
gdb/testsuite/gdb.python/py-framefilter.c [new file with mode: 0644]
gdb/testsuite/gdb.python/py-framefilter.exp [new file with mode: 0644]
gdb/testsuite/gdb.python/py-framefilter.py [new file with mode: 0644]

index c3399badcf7240375ecd9dce9ac0441c865e242e..2510f1a8d4204706489da1b91f8ef25381b05524 100644 (file)
@@ -1,3 +1,52 @@
+2013-05-10  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * stack.c (backtrace_command_1): Add "no-filters", and Python frame
+       filter logic.
+       (backtrace_command): Add "no-filters" option parsing.
+       (_initialize_stack): Alter help to reflect "no-filters" option.
+       * Makefile.in (SUBDIR_PYTHON_OBS): Add py-framefilter.o
+       (SUBDIR_PYTHON_SRCS): Add py-framefilter.c
+       (py-frame.o): Add target
+       * data-directory/Makefile.in (PYTHON_DIR): Add Python frame
+       filter files.
+       * python/python.h: Add new frame filter constants, and flag enum.
+       (apply_frame_filter): Add definition.
+       * python/python.c (apply_frame_filter): New non-Python
+       enabled function.
+       * python/py-utils.c (py_xdecref): New function.
+       (make_cleanup_py_xdecref): Ditto.
+       * python/py-objfile.c: Declare frame_filters dictionary.
+       (objfpy_dealloc): Add frame_filters dealloc.
+       (objfpy_new): Initialize frame_filters attribute.
+       (objfile_to_objfile_object): Ditto.
+       (objfpy_get_frame_filters): New function.
+       (objfpy_set_frame_filters): New function.
+       * python/py-progspace.c: Declare frame_filters dictionary.
+       (pspy_dealloc): Add frame_filters dealloc.
+       (pspy_new): Initialize frame_filters attribute.
+       (pspacee_to_pspace_object): Ditto.
+       (pspy_get_frame_filters): New function.
+       (pspy_set_frame_filters): New function.
+       * python/py-framefilter.c: New file.
+       * python/lib/gdb/command/frame_filters.py: New file.
+       * python/lib/gdb/frames.py: New file.
+       * python/lib/gdb/__init__.py: Initialize global frame_filters
+       dictionary
+       * python/lib/gdb/FrameDecorator.py: New file.
+       * python/lib/gdb/FrameIterator.py: New file.
+       * mi/mi-cmds.c (mi_cmds): Add frame filters command.
+       * mi/mi-cmds.h: Declare.
+       * mi/mi-cmd-stack.c (mi_cmd_stack_list_frames): Add
+       --no-frame-filter logic, and Python frame filter logic.
+       (stack_enable_frame_filters): New function.
+       (parse_no_frame_option): Ditto.
+       (mi_cmd_stack_list_frames): Add --no-frame-filter and Python frame
+       filter logic.
+       (mi_cmd_stack_list_locals): Ditto.
+       (mi_cmd_stack_list_args): Ditto.
+       (mi_cmd_stack_list_variables): Ditto.
+       * NEWS: Add frame filter note.
+
 2013-05-09  Doug Evans  <dje@google.com>
 
        * symfile.c (syms_from_objfile_1): Delete args offsets, num_offsets.
index 2597ddb87737cddc932f7684a7413ae3b7c5f626..91278945b9d53ea3a34a8ea56a142ceaccc1d8b8 100644 (file)
@@ -288,6 +288,7 @@ SUBDIR_PYTHON_OBS = \
        py-exitedevent.o \
        py-finishbreakpoint.o \
        py-frame.o \
+       py-framefilter.o \
        py-function.o \
        py-gdb-readline.o \
        py-inferior.o \
@@ -322,6 +323,7 @@ SUBDIR_PYTHON_SRCS = \
        python/py-exitedevent.c \
        python/py-finishbreakpoint.c \
        python/py-frame.c \
+       python/py-framefilter.c \
        python/py-function.c \
        python/py-gdb-readline.c \
        python/py-inferior.c \
@@ -2144,6 +2146,10 @@ py-frame.o: $(srcdir)/python/py-frame.c
        $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-frame.c
        $(POSTCOMPILE)
 
+py-framefilter.o: $(srcdir)/python/py-framefilter.c
+       $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-framefilter.c
+       $(POSTCOMPILE)
+
 py-function.o: $(srcdir)/python/py-function.c
        $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-function.c
        $(POSTCOMPILE)
index 65284dc32d4e344a334cdcb8f14878b6d3256067..7cd164600acb0815a460683be5c9f758efa896db 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 7.6
 
+* Python scripting
+
+  ** Frame filters and frame decorators have been added.
+
 * New targets
 
 Nios II ELF                    nios2*-*-elf
index d98ac77145927b62d7a48151447cd949547dd65b..13433edaad9cacef8dc497442229ae4832946d87 100644 (file)
@@ -53,10 +53,14 @@ PYTHON_DIR = python
 PYTHON_INSTALL_DIR = $(DESTDIR)$(GDB_DATADIR)/$(PYTHON_DIR)
 PYTHON_FILES = \
        gdb/__init__.py \
+       gdb/frames.py \
+       gdb/FrameIterator.py \
+       gdb/FrameDecorator.py \
        gdb/types.py \
        gdb/printing.py \
        gdb/prompt.py \
        gdb/command/__init__.py \
+       gdb/command/frame_filters.py \
        gdb/command/type_printers.py \
        gdb/command/pretty_printers.py \
        gdb/command/prompt.py \
index 80f08a539bda0563b98a3bd87e213fb62648d238..908fbb40a0b32c7496cacde216937ea071c7e73d 100644 (file)
@@ -1,3 +1,20 @@
+2013-05-10 Phil Muldoon  <pmuldoon@redhat.com>
+
+       * gdb.texinfo (Backtrace): Add "no-filter" argument.
+       (Python API): Add Frame Filters API, Frame Wrapper API,
+       Writing a Frame Filter/Wrapper, Managing Management of Frame
+       Filters chapter entries.
+       (Frame Filters API): New Node.
+       (Frame Wrapper API): New Node.
+       (Writing a Frame Filter): New Node.
+       (Managing Frame Filters): New Node.
+       (Progspaces In Python): Add note about frame_filters attribute.
+       (Objfiles in Python): Ditto.
+       (GDB/MI Stack Manipulation): Add -enable-frame-filters command,
+       @anchors and --no-frame-filters option to -stack-list-variables,
+       -stack-list-frames, -stack-list-locals and -stack-list-arguments
+       commands.
+
 2013-05-08  Joel Brobecker  <brobecker@adacore.com>
 
        * gdbint.texinfo (Native Debugging): Add "AIX Shared Library
index f685cd208a67fcde2df964530ef1eee2eab86f5e..1869d74de5aa8f6f4a3325125ef9453fefa06e24 100644 (file)
@@ -6465,6 +6465,7 @@ currently executing frame and describes it briefly, similar to the
 @menu
 * Frames::                      Stack frames
 * Backtrace::                   Backtraces
+* Frame Filter Management::     Managing frame filters
 * Selection::                   Selecting a frame
 * Frame Info::                  Information on a frame
 
@@ -6552,6 +6553,7 @@ line per frame, for many frames, starting with the currently executing
 frame (frame zero), followed by its caller (frame one), and on up the
 stack.
 
+@anchor{backtrace-command}
 @table @code
 @kindex backtrace
 @kindex bt @r{(@code{backtrace})}
@@ -6577,6 +6579,19 @@ Similar, but print only the outermost @var{n} frames.
 @itemx bt full -@var{n}
 Print the values of the local variables also.  @var{n} specifies the
 number of frames to print, as described above.
+
+@item backtrace no-filters
+@itemx bt no-filters
+@itemx bt no-filters @var{n}
+@itemx bt no-filters -@var{n}
+@itemx bt no-filters full
+@itemx bt no-filters full @var{n}
+@itemx bt no-filters full -@var{n}
+Do not run Python frame filters on this backtrace.  @xref{Frame
+Filter API}, for more information.  Additionally use @ref{disable
+frame-filter all} to turn off all frame filters.  This is only
+relevant when @value{GDBN} has been configured with @code{Python}
+support.
 @end table
 
 @kindex where
@@ -6727,6 +6742,149 @@ Display an absolute filename.
 Show the current way to display filenames.
 @end table
 
+@node Frame Filter Management
+@section Management of Frame Filters.
+@cindex managing frame filters
+
+Frame filters are Python based utilities to manage and decorate the
+output of frames.  @xref{Frame Filter API}, for further information.
+
+Managing frame filters is performed by several commands available
+within @value{GDBN}, detailed here.
+
+@table @code
+@kindex info frame-filter
+@item info frame-filter
+Print a list of installed frame filters from all dictionaries, showing
+their name, priority and enabled status.
+
+@kindex disable frame-filter
+@anchor{disable frame-filter all}
+@item disable frame-filter @var{filter-dictionary} @var{filter-name}
+Disable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.  When @code{all} is specified, all frame filters
+across all dictionaries are disabled.  @var{filter-name} is the name
+of the frame filter and is used when @code{all} is not the option for
+@var{filter-dictionary}.  A disabled frame-filter is not deleted, it
+may be enabled again later.
+
+@kindex enable frame-filter
+@item enable frame-filter @var{filter-dictionary} @var{filter-name}
+Enable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.  When @code{all} is specified, all frame filters across
+all dictionaries are enabled.  @var{filter-name} is the name of the frame
+filter and is used when @code{all} is not the option for
+@var{filter-dictionary}.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+  Priority  Enabled  Name
+  1000      No       PrimaryFunctionFilter
+  100       Yes      Reverse
+
+progspace /build/test frame-filters:
+  Priority  Enabled  Name
+  100       Yes      ProgspaceFilter
+
+objfile /build/test frame-filters:
+  Priority  Enabled  Name
+  999       Yes      BuildProgra Filter
+
+(gdb) disable frame-filter /build/test BuildProgramFilter
+(gdb) info frame-filter
+
+global frame-filters:
+  Priority  Enabled  Name
+  1000      No       PrimaryFunctionFilter
+  100       Yes      Reverse
+
+progspace /build/test frame-filters:
+  Priority  Enabled  Name
+  100       Yes      ProgspaceFilter
+
+objfile /build/test frame-filters:
+  Priority  Enabled  Name
+  999       No       BuildProgramFilter
+
+(gdb) enable frame-filter global PrimaryFunctionFilter
+(gdb) info frame-filter
+
+global frame-filters:
+  Priority  Enabled  Name
+  1000      Yes      PrimaryFunctionFilter
+  100       Yes      Reverse
+
+progspace /build/test frame-filters:
+  Priority  Enabled  Name
+  100       Yes      ProgspaceFilter
+
+objfile /build/test frame-filters:
+  Priority  Enabled  Name
+  999       No       BuildProgramFilter
+@end smallexample
+
+@kindex set frame-filter priority
+@item set frame-filter priority @var{filter-dictionary} @var{filter-name} @var{priority}
+Set the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}.  @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.  @var{priority} is an integer.
+
+@kindex show frame-filter priority
+@item show frame-filter priority @var{filter-dictionary} @var{filter-name}
+Show the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}.  @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+  Priority  Enabled  Name
+  1000      Yes      PrimaryFunctionFilter
+  100       Yes      Reverse
+
+progspace /build/test frame-filters:
+  Priority  Enabled  Name
+  100       Yes      ProgspaceFilter
+
+objfile /build/test frame-filters:
+  Priority  Enabled  Name
+  999       No       BuildProgramFilter
+
+(gdb) set frame-filter priority global Reverse 50
+(gdb) info frame-filter
+
+global frame-filters:
+  Priority  Enabled  Name
+  1000      Yes      PrimaryFunctionFilter
+  50        Yes      Reverse
+
+progspace /build/test frame-filters:
+  Priority  Enabled  Name
+  100       Yes      ProgspaceFilter
+
+objfile /build/test frame-filters:
+  Priority  Enabled  Name
+  999       No       BuildProgramFilter
+@end smallexample
+@end table
+
 @node Selection
 @section Selecting a Frame
 
@@ -23026,6 +23184,9 @@ optional arguments while skipping others.  Example:
 * Selecting Pretty-Printers::   How GDB chooses a pretty-printer.
 * Writing a Pretty-Printer::    Writing a Pretty-Printer.
 * Type Printing API::          Pretty-printing types.
+* Frame Filter API::            Filtering Frames.
+* Frame Decorator API::         Decorating Frames.
+* Writing a Frame Filter::      Writing a Frame Filter.
 * Inferiors In Python::         Python representation of inferiors (processes)
 * Events In Python::            Listening for events from @value{GDBN}.
 * Threads In Python::           Accessing inferior threads from Python.
@@ -24405,6 +24566,636 @@ done then type printers would have to make use of the event system in
 order to avoid holding information that could become stale as the
 inferior changed.
 
+@node Frame Filter API
+@subsubsection Filtering Frames.
+@cindex frame filters api
+
+Frame filters are Python objects that manipulate the visibility of a
+frame or frames when a backtrace (@pxref{Backtrace}) is printed by
+@value{GDBN}.
+
+Only commands that print a backtrace, or, in the case of @sc{gdb/mi}
+commands (@pxref{GDB/MI}), those that return a collection of frames
+are affected.  The commands that work with frame filters are:
+
+@code{backtrace} (@pxref{backtrace-command,, The backtrace command}),
+@code{-stack-list-frames}
+(@pxref{-stack-list-frames,, The -stack-list-frames command}),
+@code{-stack-list-variables} (@pxref{-stack-list-variables,, The
+-stack-list-variables command}), @code{-stack-list-arguments}
+@pxref{-stack-list-arguments,, The -stack-list-arguments command}) and
+@code{-stack-list-locals} (@pxref{-stack-list-locals,, The
+-stack-list-locals command}).
+
+A frame filter works by taking an iterator as an argument, applying
+actions to the contents of that iterator, and returning another
+iterator (or, possibly, the same iterator it was provided in the case
+where the filter does not perform any operations).  Typically, frame
+filters utilize tools such as the Python's @code{itertools} module to
+work with and create new iterators from the source iterator.
+Regardless of how a filter chooses to apply actions, it must not alter
+the underlying @value{GDBN} frame or frames, or attempt to alter the
+call-stack within @value{GDBN}.  This preserves data integrity within
+@value{GDBN}.  Frame filters are executed on a priority basis and care
+should be taken that some frame filters may have been executed before,
+and that some frame filters will be executed after.
+
+An important consideration when designing frame filters, and well
+worth reflecting upon, is that frame filters should avoid unwinding
+the call stack if possible.  Some stacks can run very deep, into the
+tens of thousands in some cases.  To search every frame when a frame
+filter executes may be too expensive at that step.  The frame filter
+cannot know how many frames it has to iterate over, and it may have to
+iterate through them all.  This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames.  If
+the filter can defer unwinding frames until frame decorators are
+executed, after the last filter has executed, it should.  @xref{Frame
+Decorator API}, for more information on decorators.  Also, there are
+examples for both frame decorators and filters in later chapters.
+@xref{Writing a Frame Filter}, for more information.
+
+The Python dictionary @code{gdb.frame_filters} contains key/object
+pairings that comprise a frame filter.  Frame filters in this
+dictionary are called @code{global} frame filters, and they are
+available when debugging all inferiors.  These frame filters must
+register with the dictionary directly.  In addition to the
+@code{global} dictionary, there are other dictionaries that are loaded
+with different inferiors via auto-loading (@pxref{Python
+Auto-loading}).  The two other areas where frame filter dictionaries
+can be found are: @code{gdb.Progspace} which contains a
+@code{frame_filters} dictionary attribute, and each @code{gdb.Objfile}
+object which also contains a @code{frame_filters} dictionary
+attribute.
+
+When a command is executed from @value{GDBN} that is compatible with
+frame filters, @value{GDBN} combines the @code{global},
+@code{gdb.Progspace} and all @code{gdb.Objfile} dictionaries currently
+loaded.  All of the @code{gdb.Objfile} dictionaries are combined, as
+several frames, and thus several object files, might be in use.
+@value{GDBN} then prunes any frame filter whose @code{enabled}
+attribute is @code{False}.  This pruned list is then sorted according
+to the @code{priority} attribute in each filter.
+
+Once the dictionaries are combined, pruned and sorted, @value{GDBN}
+creates an iterator which wraps each frame in the call stack in a
+@code{FrameDecorator} object, and calls each filter in order.  The
+output from the previous filter will always be the input to the next
+filter, and so on.
+
+Frame filters have a mandatory interface which each frame filter must
+implement, defined here:
+
+@defun FrameFilter.filter (iterator)
+@value{GDBN} will call this method on a frame filter when it has
+reached the order in the priority list for that filter.
+
+For example, if there are four frame filters:
+
+@smallexample
+Name         Priority
+
+Filter1      5
+Filter2      10
+Filter3      100
+Filter4      1
+@end smallexample
+
+The order that the frame filters will be called is:
+
+@smallexample
+Filter3 -> Filter2 -> Filter1 -> Filter4
+@end smallexample
+
+Note that the output from @code{Filter3} is passed to the input of
+@code{Filter2}, and so on.
+
+This @code{filter} method is passed a Python iterator.  This iterator
+contains a sequence of frame decorators that wrap each
+@code{gdb.Frame}, or a frame decorator that wraps another frame
+decorator.  The first filter that is executed in the sequence of frame
+filters will receive an iterator entirely comprised of default
+@code{FrameDecorator} objects.  However, after each frame filter is
+executed, the previous frame filter may have wrapped some or all of
+the frame decorators with their own frame decorator.  As frame
+decorators must also conform to a mandatory interface, these
+decorators can be assumed to act in a uniform manner (@pxref{Frame
+Decorator API}).
+
+This method must return an object conforming to the Python iterator
+protocol.  Each item in the iterator must be an object conforming to
+the frame decorator interface.  If a frame filter does not wish to
+perform any operations on this iterator, it should return that
+iterator untouched.
+
+This method is not optional.  If it does not exist, @value{GDBN} will
+raise and print an error.
+@end defun
+
+@defvar FrameFilter.name
+The @code{name} attribute must be Python string which contains the
+name of the filter displayed by @value{GDBN} (@pxref{Frame Filter
+Management}).  This attribute may contain any combination of letters
+or numbers.  Care should be taken to ensure that it is unique.  This
+attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.enabled
+The @code{enabled} attribute must be Python boolean.  This attribute
+indicates to @value{GDBN} whether the frame filter is enabled, and
+should be considered when frame filters are executed.  If
+@code{enabled} is @code{True}, then the frame filter will be executed
+when any of the backtrace commands detailed earlier in this chapter
+are executed.  If @code{enabled} is @code{False}, then the frame
+filter will not be executed.  This attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.priority
+The @code{priority} attribute must be Python integer.  This attribute
+controls the order of execution in relation to other frame filters.
+There are no imposed limits on the range of @code{priority} other than
+it must be a valid integer.  The higher the @code{priority} attribute,
+the sooner the frame filter will be executed in relation to other
+frame filters.  Although @code{priority} can be negative, it is
+recommended practice to assume zero is the lowest priority that a
+frame filter can be assigned.  Frame filters that have the same
+priority are executed in unsorted order in that priority slot.  This
+attribute is mandatory.
+@end defvar
+
+@node Frame Decorator API
+@subsubsection Decorating Frames.
+@cindex frame decorator api
+
+Frame decorators are sister objects to frame filters (@pxref{Frame
+Filter API}).  Frame decorators are applied by a frame filter and can
+only be used in conjunction with frame filters.
+
+The purpose of a frame decorator is to customize the printed content
+of each @code{gdb.Frame} in commands where frame filters are executed.
+This concept is called decorating a frame.  Frame decorators decorate
+a @code{gdb.Frame} with Python code contained within each API call.
+This separates the actual data contained in a @code{gdb.Frame} from
+the decorated data produced by a frame decorator.  This abstraction is
+necessary to maintain integrity of the data contained in each
+@code{gdb.Frame}.
+
+Frame decorators have a mandatory interface, defined below.
+
+@value{GDBN} already contains a frame decorator called
+@code{FrameDecorator}.  This contains substantial amounts of
+boilerplate code to decorate the content of a @code{gdb.Frame}.  It is
+recommended that other frame decorators inherit and extend this
+object, and only to override the methods needed.
+
+@defun FrameDecorator.elided (self)
+
+The @code{elided} method groups frames together in a hierarchical
+system.  An example would be an interpreter, where multiple low-level
+frames make up a single call in the interpreted language.  In this
+example, the frame filter would elide the low-level frames and present
+a single high-level frame, representing the call in the interpreted
+language, to the user.
+
+The @code{elided} function must return an iterable and this iterable
+must contain the frames that are being elided wrapped in a suitable
+frame decorator.  If no frames are being elided this function may
+return an empty iterable, or @code{None}.  Elided frames are indented
+from normal frames in a @code{CLI} backtrace, or in the case of
+@code{GDB/MI}, are placed in the @code{children} field of the eliding
+frame.
+
+It is the frame filter's task to also filter out the elided frames from
+the source iterator.  This will avoid printing the frame twice.
+@end defun
+
+@defun FrameDecorator.function (self)
+
+This method returns the name of the function in the frame that is to
+be printed.
+
+This method must return a Python string describing the function, or
+@code{None}.
+
+If this function returns @code{None}, @value{GDBN} will not print any
+data for this field.
+@end defun
+
+@defun FrameDecorator.address (self)
+
+This method returns the address of the frame that is to be printed.
+
+This method must return a Python numeric integer type of sufficient
+size to describe the address of the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.filename (self)
+
+This method returns the filename and path associated with this frame.
+
+This method must return a Python string containing the filename and
+the path to the object file backing the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.line (self):
+
+This method returns the line number associated with the current
+position within the function addressed by this frame.
+
+This method must return a Python integer type, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.frame_args (self)
+@anchor{frame_args}
+
+This method must return an iterable, or @code{None}.  Returning an
+empty iterable, or @code{None} means frame arguments will not be
+printed for this frame.  This iterable must contain objects that
+implement two methods, described here.
+
+This object must implement a @code{argument} method which takes a
+single @code{self} parameter and must return a @code{gdb.Symbol}
+(@pxref{Symbols In Python}), or a Python string.  The object must also
+implement a @code{value} method which takes a single @code{self}
+parameter and must return a @code{gdb.Value} (@pxref{Values From
+Inferior}), a Python value, or @code{None}.  If the @code{value}
+method returns @code{None}, and the @code{argument} method returns a
+@code{gdb.Symbol}, @value{GDBN} will look-up and print the value of
+the @code{gdb.Symbol} automatically.
+
+A brief example:
+
+@smallexample
+class SymValueWrapper():
+
+    def __init__(self, symbol, value):
+        self.sym = symbol
+        self.val = value
+
+    def value(self):
+        return self.val
+
+    def symbol(self):
+        return self.sym
+
+class SomeFrameDecorator()
+...
+...
+    def frame_args(self):
+        args = []
+        try:
+            block = self.inferior_frame.block()
+        except:
+            return None
+
+        # Iterate over all symbols in a block.  Only add
+        # symbols that are arguments.
+        for sym in block:
+            if not sym.is_argument:
+                continue
+            args.append(SymValueWrapper(sym,None))
+
+        # Add example synthetic argument.
+        args.append(SymValueWrapper(``foo'', 42))
+
+        return args
+@end smallexample
+@end defun
+
+@defun FrameDecorator.frame_locals (self)
+
+This method must return an iterable or @code{None}.  Returning an
+empty iterable, or @code{None} means frame local arguments will not be
+printed for this frame.
+
+The object interface, the description of the various strategies for
+reading frame locals, and the example are largely similar to those
+described in the @code{frame_args} function, (@pxref{frame_args,,The
+frame filter frame_args function}).  Below is a modified example:
+
+@smallexample
+class SomeFrameDecorator()
+...
+...
+    def frame_locals(self):
+        vars = []
+        try:
+            block = self.inferior_frame.block()
+        except:
+            return None
+
+        # Iterate over all symbols in a block.  Add all
+        # symbols, except arguments.
+        for sym in block:
+            if sym.is_argument:
+                continue
+            vars.append(SymValueWrapper(sym,None))
+
+        # Add an example of a synthetic local variable.
+        vars.append(SymValueWrapper(``bar'', 99))
+
+        return vars
+@end smallexample
+@end defun
+
+@defun FrameDecorator.inferior_frame (self):
+
+This method must return the underlying @code{gdb.Frame} that this
+frame decorator is decorating.  @value{GDBN} requires the underlying
+frame for internal frame information to determine how to print certain
+values when printing a frame.
+@end defun
+
+@node Writing a Frame Filter
+@subsubsection Writing a Frame Filter
+@cindex writing a frame filter
+
+There are three basic elements that a frame filter must implement: it
+must correctly implement the documented interface (@pxref{Frame Filter
+API}), it must register itself with @value{GDBN}, and finally, it must
+decide if it is to work on the data provided by @value{GDBN}.  In all
+cases, whether it works on the iterator or not, each frame filter must
+return an iterator.  A bare-bones frame filter follows the pattern in
+the following example.
+
+@smallexample
+import gdb
+
+class FrameFilter():
+
+    def __init__(self):
+        # Frame filter attribute creation.
+        #
+        # 'name' is the name of the filter that GDB will display.
+        #
+        # 'priority' is the priority of the filter relative to other
+        # filters.
+        #
+        # 'enabled' is a boolean that indicates whether this filter is
+        # enabled and should be executed.
+
+        self.name = "Foo"
+        self.priority = 100
+        self.enabled = True
+
+        # Register this frame filter with the global frame_filters
+        # dictionary.
+        gdb.frame_filters[self.name] = self
+
+    def filter(self, frame_iter):
+        # Just return the iterator.
+        return frame_iter
+@end smallexample
+
+The frame filter in the example above implements the three
+requirements for all frame filters.  It implements the API, self
+registers, and makes a decision on the iterator (in this case, it just
+returns the iterator untouched).
+
+The first step is attribute creation and assignment, and as shown in
+the comments the filter assigns the following attributes:  @code{name},
+@code{priority} and whether the filter should be enabled with the
+@code{enabled} attribute.
+
+The second step is registering the frame filter with the dictionary or
+dictionaries that the frame filter has interest in.  As shown in the
+comments, this filter just registers itself with the global dictionary
+@code{gdb.frame_filters}.  As noted earlier, @code{gdb.frame_filters}
+is a dictionary that is initialized in the @code{gdb} module when
+@value{GDBN} starts.  What dictionary a filter registers with is an
+important consideration.  Generally, if a filter is specific to a set
+of code, it should be registered either in the @code{objfile} or
+@code{progspace} dictionaries as they are specific to the program
+currently loaded in @value{GDBN}.  The global dictionary is always
+present in @value{GDBN} and is never unloaded.  Any filters registered
+with the global dictionary will exist until @value{GDBN} exits.  To
+avoid filters that may conflict, it is generally better to register
+frame filters against the dictionaries that more closely align with
+the usage of the filter currently in question.  @xref{Python
+Auto-loading}, for further information on auto-loading Python scripts.
+
+@value{GDBN} takes a hands-off approach to frame filter registration,
+therefore it is the frame filter's responsibility to ensure
+registration has occurred, and that any exceptions are handled
+appropriately.  In particular, you may wish to handle exceptions
+relating to Python dictionary key uniqueness.  It is mandatory that
+the dictionary key is the same as frame filter's @code{name}
+attribute.  When a user manages frame filters (@pxref{Frame Filter
+Management}), the names @value{GDBN} will display are those contained
+in the @code{name} attribute.
+
+The final step of this example is the implementation of the
+@code{filter} method.  As shown in the example comments, we define the
+@code{filter} method and note that the method must take an iterator,
+and also must return an iterator.  In this bare-bones example, the
+frame filter is not very useful as it just returns the iterator
+untouched.  However this is a valid operation for frame filters that
+have the @code{enabled} attribute set, but decide not to operate on
+any frames.
+
+In the next example, the frame filter operates on all frames and
+utilizes a frame decorator to perform some work on the frames.
+@xref{Frame Decorator API}, for further information on the frame
+decorator interface.
+
+This example works on inlined frames.  It highlights frames which are
+inlined by tagging them with an ``[inlined]'' tag.  By applying a
+frame decorator to all frames with the Python @code{itertools imap}
+method, the example defers actions to the frame decorator.  Frame
+decorators are only processed when @value{GDBN} prints the backtrace.
+
+This introduces a new decision making topic: whether to perform
+decision making operations at the filtering step, or at the printing
+step.  In this example's approach, it does not perform any filtering
+decisions at the filtering step beyond mapping a frame decorator to
+each frame.  This allows the actual decision making to be performed
+when each frame is printed.  This is an important consideration, and
+well worth reflecting upon when designing a frame filter.  An issue
+that frame filters should avoid is unwinding the stack if possible.
+Some stacks can run very deep, into the tens of thousands in some
+cases.  To search every frame to determine if it is inlined ahead of
+time may be too expensive at the filtering step.  The frame filter
+cannot know how many frames it has to iterate over, and it would have
+to iterate through them all.  This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames.
+
+In this example decision making can be deferred to the printing step.
+As each frame is printed, the frame decorator can examine each frame
+in turn when @value{GDBN} iterates.  From a performance viewpoint,
+this is the most appropriate decision to make as it avoids duplicating
+the effort that the printing step would undertake anyway.  Also, if
+there are many frame filters unwinding the stack during filtering, it
+can substantially delay the printing of the backtrace which will
+result in large memory usage, and a poor user experience.
+
+@smallexample
+class InlineFilter():
+
+    def __init__(self):
+        self.name = "InlinedFrameFilter"
+        self.priority = 100
+        self.enabled = True
+        gdb.frame_filters[self.name] = self
+
+    def filter(self, frame_iter):
+        frame_iter = itertools.imap(InlinedFrameDecorator,
+                                    frame_iter)
+        return frame_iter
+@end smallexample
+
+This frame filter is somewhat similar to the earlier example, except
+that the @code{filter} method applies a frame decorator object called
+@code{InlinedFrameDecorator} to each element in the iterator.  The
+@code{imap} Python method is light-weight.  It does not proactively
+iterate over the iterator, but rather creates a new iterator which
+wraps the existing one.
+
+Below is the frame decorator for this example.
+
+@smallexample
+class InlinedFrameDecorator(FrameDecorator):
+
+    def __init__(self, fobj):
+        super(InlinedFrameDecorator, self).__init__(fobj)
+
+    def function(self):
+        frame = fobj.inferior_frame()
+        name = str(frame.name())
+
+        if frame.type() == gdb.INLINE_FRAME:
+            name = name + " [inlined]"
+
+        return name
+@end smallexample
+
+This frame decorator only defines and overrides the @code{function}
+method.  It lets the supplied @code{FrameDecorator}, which is shipped
+with @value{GDBN}, perform the other work associated with printing
+this frame.
+
+The combination of these two objects create this output from a
+backtrace:
+
+@smallexample
+#0  0x004004e0 in bar () at inline.c:11
+#1  0x00400566 in max [inlined] (b=6, a=12) at inline.c:21
+#2  0x00400566 in main () at inline.c:31
+@end smallexample
+
+So in the case of this example, a frame decorator is applied to all
+frames, regardless of whether they may be inlined or not.  As
+@value{GDBN} iterates over the iterator produced by the frame filters,
+@value{GDBN} executes each frame decorator which then makes a decision
+on what to print in the @code{function} callback.  Using a strategy
+like this is a way to defer decisions on the frame content to printing
+time.
+
+@subheading Eliding Frames
+
+It might be that the above example is not desirable for representing
+inlined frames, and a hierarchical approach may be preferred.  If we
+want to hierarchically represent frames, the @code{elided} frame
+decorator interface might be preferable.
+
+This example approaches the issue with the @code{elided} method.  This
+example is quite long, but very simplistic.  It is out-of-scope for
+this section to write a complete example that comprehensively covers
+all approaches of finding and printing inlined frames.  However, this
+example illustrates the approach an author might use.
+
+This example comprises of three sections.
+
+@smallexample
+class InlineFrameFilter():
+
+    def __init__(self):
+        self.name = "InlinedFrameFilter"
+        self.priority = 100
+        self.enabled = True
+        gdb.frame_filters[self.name] = self
+
+    def filter(self, frame_iter):
+        return ElidingInlineIterator(frame_iter)
+@end smallexample
+
+This frame filter is very similar to the other examples.  The only
+difference is this frame filter is wrapping the iterator provided to
+it (@code{frame_iter}) with a custom iterator called
+@code{ElidingInlineIterator}.  This again defers actions to when
+@value{GDBN} prints the backtrace, as the iterator is not traversed
+until printing.
+
+The iterator for this example is as follows.  It is in this section of
+the example where decisions are made on the content of the backtrace.
+
+@smallexample
+class ElidingInlineIterator:
+    def __init__(self, ii):
+        self.input_iterator = ii
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        frame = next(self.input_iterator)
+
+        if frame.inferior_frame().type() != gdb.INLINE_FRAME:
+            return frame
+
+        try:
+            eliding_frame = next(self.input_iterator)
+        except StopIteration:
+            return frame
+        return ElidingFrameDecorator(eliding_frame, [frame])
+@end smallexample
+
+This iterator implements the Python iterator protocol.  When the
+@code{next} function is called (when @value{GDBN} prints each frame),
+the iterator checks if this frame decorator, @code{frame}, is wrapping
+an inlined frame.  If it is not, it returns the existing frame decorator
+untouched.  If it is wrapping an inlined frame, it assumes that the
+inlined frame was contained within the next oldest frame,
+@code{eliding_frame}, which it fetches.  It then creates and returns a
+frame decorator, @code{ElidingFrameDecorator}, which contains both the
+elided frame, and the eliding frame.
+
+@smallexample
+class ElidingInlineDecorator(FrameDecorator):
+
+    def __init__(self, frame, elided_frames):
+        super(ElidingInlineDecorator, self).__init__(frame)
+        self.frame = frame
+        self.elided_frames = elided_frames
+
+    def elided(self):
+        return iter(self.elided_frames)
+@end smallexample
+
+This frame decorator overrides one function and returns the inlined
+frame in the @code{elided} method.  As before it lets
+@code{FrameDecorator} do the rest of the work involved in printing
+this frame.  This produces the following output.
+
+@smallexample
+#0  0x004004e0 in bar () at inline.c:11
+#2  0x00400529 in main () at inline.c:25
+    #1  0x00400529 in max (b=6, a=12) at inline.c:15
+@end smallexample
+
+In that output, @code{max} which has been inlined into @code{main} is
+printed hierarchically.  Another approach would be to combine the
+@code{function} method, and the @code{elided} method to both print a
+marker in the inlined frame, and also show the hierarchical
+relationship.
+
 @node Inferiors In Python
 @subsubsection Inferiors In Python
 @cindex inferiors in Python
@@ -25235,6 +26026,11 @@ The @code{type_printers} attribute is a list of type printer objects.
 @xref{Type Printing API}, for more information.
 @end defvar
 
+@defvar Progspace.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects.  @xref{Frame Filter API}, for more information.
+@end defvar
+
 @node Objfiles In Python
 @subsubsection Objfiles In Python
 
@@ -25285,6 +26081,11 @@ The @code{type_printers} attribute is a list of type printer objects.
 @xref{Type Printing API}, for more information.
 @end defvar
 
+@defvar Objfile.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects.  @xref{Frame Filter API}, for more information.
+@end defvar
+
 A @code{gdb.Objfile} object has the following methods:
 
 @defun Objfile.is_valid ()
@@ -26351,7 +27152,7 @@ No     my-foo-pretty-printers.py
 When reading an auto-loaded file, @value{GDBN} sets the
 @dfn{current objfile}.  This is available via the @code{gdb.current_objfile}
 function (@pxref{Objfiles In Python}).  This can be useful for
-registering objfile-specific pretty-printers.
+registering objfile-specific pretty-printers and frame-filters.
 
 @menu
 * objfile-gdb.py file::          The @file{@var{objfile}-gdb.py} file
@@ -30222,6 +31023,22 @@ Is this going away????
 @node GDB/MI Stack Manipulation
 @section @sc{gdb/mi} Stack Manipulation Commands
 
+@subheading The @code{-enable-frame-filters} Command
+@findex -enable-frame-filters
+
+@smallexample
+-enable-frame-filters
+@end smallexample
+
+@value{GDBN} allows Python-based frame filters to affect the output of
+the MI commands relating to stack traces.  As there is no way to
+implement this in a fully backward-compatible way, a front end must
+request that this functionality be enabled.
+
+Once enabled, this feature cannot be disabled.
+
+Note that if Python support has not been compiled into @value{GDBN},
+this command will still succeed (and do nothing).
 
 @subheading The @code{-stack-info-frame} Command
 @findex -stack-info-frame
@@ -30289,13 +31106,14 @@ For a stack with frame levels 0 through 11:
 (gdb)
 @end smallexample
 
+@anchor{-stack-list-arguments}
 @subheading The @code{-stack-list-arguments} Command
 @findex -stack-list-arguments
 
 @subsubheading Synopsis
 
 @smallexample
- -stack-list-arguments @var{print-values}
+ -stack-list-arguments [ --no-frame-filters ] @var{print-values}
     [ @var{low-frame} @var{high-frame} ]
 @end smallexample
 
@@ -30312,7 +31130,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of
 the variables; if it is 1 or @code{--all-values}, print also their
 values; and if it is 2 or @code{--simple-values}, print the name,
 type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions.  If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
+
 
 Use of this command to obtain arguments in a single frame is
 deprecated in favor of the @samp{-stack-list-variables} command.
@@ -30383,13 +31203,14 @@ args=[@{name="intarg",value="2"@},
 @c @subheading -stack-list-exception-handlers
 
 
+@anchor{-stack-list-frames}
 @subheading The @code{-stack-list-frames} Command
 @findex -stack-list-frames
 
 @subsubheading Synopsis
 
 @smallexample
- -stack-list-frames [ @var{low-frame} @var{high-frame} ]
+ -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ]
 @end smallexample
 
 List the frames currently on the stack.  For each frame it displays the
@@ -30419,7 +31240,9 @@ levels are between the two arguments (inclusive).  If the two arguments
 are equal, it shows the single frame at the corresponding level.  It is
 an error if @var{low-frame} is larger than the actual number of
 frames.  On the other hand, @var{high-frame} may be larger than the
-actual number of frames, in which case only existing frames will be returned.
+actual number of frames, in which case only existing frames will be
+returned.  If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
 
 @subsubheading @value{GDBN} Command
 
@@ -30489,11 +31312,12 @@ Show a single frame:
 
 @subheading The @code{-stack-list-locals} Command
 @findex -stack-list-locals
+@anchor{-stack-list-locals}
 
 @subsubheading Synopsis
 
 @smallexample
- -stack-list-locals @var{print-values}
+ -stack-list-locals [ --no-frame-filters ] @var{print-values}
 @end smallexample
 
 Display the local variable names for the selected frame.  If
@@ -30504,7 +31328,8 @@ type and value for simple data types, and the name and type for arrays,
 structures and unions.  In this last case, a frontend can immediately
 display the value of simple data types and create variable objects for
 other data types when the user wishes to explore their values in
-more detail.
+more detail.  If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
 
 This command is deprecated in favor of the
 @samp{-stack-list-variables} command.
@@ -30529,13 +31354,14 @@ This command is deprecated in favor of the
 (gdb)
 @end smallexample
 
+@anchor{-stack-list-variables}
 @subheading The @code{-stack-list-variables} Command
 @findex -stack-list-variables
 
 @subsubheading Synopsis
 
 @smallexample
- -stack-list-variables @var{print-values}
+ -stack-list-variables [ --no-frame-filters ] @var{print-values}
 @end smallexample
 
 Display the names of local variables and function arguments for the selected frame.  If
@@ -30543,7 +31369,8 @@ Display the names of local variables and function arguments for the selected fra
 the variables; if it is 1 or @code{--all-values}, print also their
 values; and if it is 2 or @code{--simple-values}, print the name,
 type and value for simple data types, and the name and type for arrays,
-structures and unions.  
+structures and unions.  If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
 
 @subsubheading Example
 
index 062cd5dc387d3665092081fb7e94076bf9d3a21a..1cd9a47586fcd35ca597a0acb784051bf1304a42 100644 (file)
 #include "language.h"
 #include "valprint.h"
 #include "exceptions.h"
+#include "utils.h"
+#include "mi-getopt.h"
+#include "python/python.h"
+#include <ctype.h>
 
 enum what_to_list { locals, arguments, all };
 
@@ -38,6 +42,28 @@ static void list_args_or_locals (enum what_to_list what,
                                 enum print_values values,
                                 struct frame_info *fi);
 
+/* True if we want to allow Python-based frame filters.  */
+static int frame_filters = 0;
+
+void
+mi_cmd_enable_frame_filters (char *command, char **argv, int argc)
+{
+  if (argc != 0)
+    error (_("-enable-frame-filters: no arguments allowed"));
+  frame_filters = 1;
+}
+
+/* Parse the --no-frame-filters option in commands where we cannot use
+   mi_getopt. */
+static int
+parse_no_frames_option (const char *arg)
+{
+  if (arg && (strcmp (arg, "--no-frame-filters") == 0))
+    return 1;
+
+  return 0;
+}
+
 /* Print a list of the stack frames.  Args can be none, in which case
    we want to print the whole backtrace, or a pair of numbers
    specifying the frame numbers at which to start and stop the
@@ -52,14 +78,46 @@ mi_cmd_stack_list_frames (char *command, char **argv, int argc)
   int i;
   struct cleanup *cleanup_stack;
   struct frame_info *fi;
+  enum py_bt_status result = PY_BT_ERROR;
+  int raw_arg = 0;
+  int oind = 0;
+  enum opt
+    {
+      NO_FRAME_FILTERS
+    };
+  static const struct mi_opt opts[] =
+    {
+      {"-no-frame-filters", NO_FRAME_FILTERS, 0},
+      { 0, 0, 0 }
+    };
 
-  if (argc > 2 || argc == 1)
-    error (_("-stack-list-frames: Usage: [FRAME_LOW FRAME_HIGH]"));
+  /* Parse arguments.  In this instance we are just looking for
+     --no-frame-filters.  */
+  while (1)
+    {
+      char *oarg;
+      int opt = mi_getopt ("-stack-list-frames", argc, argv,
+                          opts, &oind, &oarg);
+      if (opt < 0)
+       break;
+      switch ((enum opt) opt)
+       {
+       case NO_FRAME_FILTERS:
+         raw_arg = oind;
+         break;
+       }
+    }
+
+  /* After the last option is parsed, there should either be low -
+     high range, or no further arguments.  */
+  if ((argc - oind != 0) && (argc - oind != 2))
+    error (_("-stack-list-frames: Usage: [--no-frame-filters] [FRAME_LOW FRAME_HIGH]"));
 
-  if (argc == 2)
+  /* If there is a range, set it.  */
+  if (argc - oind == 2)
     {
-      frame_low = atoi (argv[0]);
-      frame_high = atoi (argv[1]);
+      frame_low = atoi (argv[0 + oind]);
+      frame_high = atoi (argv[1 + oind]);
     }
   else
     {
@@ -81,16 +139,37 @@ mi_cmd_stack_list_frames (char *command, char **argv, int argc)
 
   cleanup_stack = make_cleanup_ui_out_list_begin_end (current_uiout, "stack");
 
-  /* Now let's print the frames up to frame_high, or until there are
-     frames in the stack.  */
-  for (;
-       fi && (i <= frame_high || frame_high == -1);
-       i++, fi = get_prev_frame (fi))
+  if (! raw_arg && frame_filters)
     {
-      QUIT;
-      /* Print the location and the address always, even for level 0.
-         If args is 0, don't print the arguments.  */
-      print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */ );
+      int flags = PRINT_LEVEL | PRINT_FRAME_INFO;
+      int py_frame_low = frame_low;
+
+      /* We cannot pass -1 to frame_low, as that would signify a
+      relative backtrace from the tail of the stack.  So, in the case
+      of frame_low == -1, assign and increment it.  */
+      if (py_frame_low == -1)
+       py_frame_low++;
+
+      result = apply_frame_filter (get_current_frame (), flags,
+                                  NO_VALUES,  current_uiout,
+                                  py_frame_low, frame_high);
+    }
+
+  /* Run the inbuilt backtrace if there are no filters registered, or
+     if "--no-frame-filters" has been specified from the command.  */
+  if (! frame_filters || raw_arg  || result == PY_BT_NO_FILTERS)
+    {
+      /* Now let's print the frames up to frame_high, or until there are
+        frames in the stack.  */
+      for (;
+          fi && (i <= frame_high || frame_high == -1);
+          i++, fi = get_prev_frame (fi))
+       {
+         QUIT;
+         /* Print the location and the address always, even for level 0.
+            If args is 0, don't print the arguments.  */
+         print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */ );
+       }
     }
 
   do_cleanups (cleanup_stack);
@@ -147,13 +226,34 @@ void
 mi_cmd_stack_list_locals (char *command, char **argv, int argc)
 {
   struct frame_info *frame;
+  int raw_arg = 0;
+  enum py_bt_status result = PY_BT_ERROR;
+  int print_value;
 
-  if (argc != 1)
-    error (_("-stack-list-locals: Usage: PRINT_VALUES"));
+  if (argc > 0)
+    raw_arg = parse_no_frames_option (argv[0]);
 
-   frame = get_selected_frame (NULL);
+  if (argc < 1 || argc > 2 || (argc == 2 && ! raw_arg)
+      || (argc == 1 && raw_arg))
+    error (_("-stack-list-locals: Usage: [--no-frame-filters] PRINT_VALUES"));
 
-   list_args_or_locals (locals, parse_print_values (argv[0]), frame);
+  frame = get_selected_frame (NULL);
+  print_value = parse_print_values (argv[raw_arg]);
+
+   if (! raw_arg && frame_filters)
+     {
+       int flags = PRINT_LEVEL | PRINT_LOCALS;
+
+       result = apply_frame_filter (frame, flags, print_value,
+                                   current_uiout, 0, 0);
+     }
+
+   /* Run the inbuilt backtrace if there are no filters registered, or
+      if "--no-frame-filters" has been specified from the command.  */
+   if (! frame_filters || raw_arg  || result == PY_BT_NO_FILTERS)
+     {
+       list_args_or_locals (locals, print_value, frame);
+     }
 }
 
 /* Print a list of the arguments for the current frame.  With argument
@@ -170,15 +270,20 @@ mi_cmd_stack_list_args (char *command, char **argv, int argc)
   struct cleanup *cleanup_stack_args;
   enum print_values print_values;
   struct ui_out *uiout = current_uiout;
+  int raw_arg = 0;
+  enum py_bt_status result = PY_BT_ERROR;
+
+  if (argc > 0)
+    raw_arg = parse_no_frames_option (argv[0]);
 
-  if (argc < 1 || argc > 3 || argc == 2)
-    error (_("-stack-list-arguments: Usage: "
-            "PRINT_VALUES [FRAME_LOW FRAME_HIGH]"));
+  if (argc < 1 || (argc > 3 && ! raw_arg) || (argc == 2 && ! raw_arg))
+    error (_("-stack-list-arguments: Usage: " \
+            "[--no-frame-filters] PRINT_VALUES [FRAME_LOW FRAME_HIGH]"));
 
-  if (argc == 3)
+  if (argc >= 3)
     {
-      frame_low = atoi (argv[1]);
-      frame_high = atoi (argv[2]);
+      frame_low = atoi (argv[1 + raw_arg]);
+      frame_high = atoi (argv[2 + raw_arg]);
     }
   else
     {
@@ -188,7 +293,7 @@ mi_cmd_stack_list_args (char *command, char **argv, int argc)
       frame_high = -1;
     }
 
-  print_values = parse_print_values (argv[0]);
+  print_values = parse_print_values (argv[raw_arg]);
 
   /* Let's position fi on the frame at which to start the
      display. Could be the innermost frame if the whole stack needs
@@ -203,21 +308,41 @@ mi_cmd_stack_list_args (char *command, char **argv, int argc)
   cleanup_stack_args
     = make_cleanup_ui_out_list_begin_end (uiout, "stack-args");
 
-  /* Now let's print the frames up to frame_high, or until there are
-     frames in the stack.  */
-  for (;
-       fi && (i <= frame_high || frame_high == -1);
-       i++, fi = get_prev_frame (fi))
+  if (! raw_arg && frame_filters)
     {
-      struct cleanup *cleanup_frame;
-
-      QUIT;
-      cleanup_frame = make_cleanup_ui_out_tuple_begin_end (uiout, "frame");
-      ui_out_field_int (uiout, "level", i);
-      list_args_or_locals (arguments, print_values, fi);
-      do_cleanups (cleanup_frame);
+      int flags = PRINT_LEVEL | PRINT_ARGS;
+      int py_frame_low = frame_low;
+
+      /* We cannot pass -1 to frame_low, as that would signify a
+      relative backtrace from the tail of the stack.  So, in the case
+      of frame_low == -1, assign and increment it.  */
+      if (py_frame_low == -1)
+       py_frame_low++;
+
+      result = apply_frame_filter (get_current_frame (), flags,
+                                  print_values, current_uiout,
+                                  py_frame_low, frame_high);
     }
 
+     /* Run the inbuilt backtrace if there are no filters registered, or
+      if "--no-frame-filters" has been specified from the command.  */
+   if (! frame_filters || raw_arg  || result == PY_BT_NO_FILTERS)
+     {
+      /* Now let's print the frames up to frame_high, or until there are
+        frames in the stack.  */
+      for (;
+          fi && (i <= frame_high || frame_high == -1);
+          i++, fi = get_prev_frame (fi))
+       {
+         struct cleanup *cleanup_frame;
+
+         QUIT;
+         cleanup_frame = make_cleanup_ui_out_tuple_begin_end (uiout, "frame");
+         ui_out_field_int (uiout, "level", i);
+         list_args_or_locals (arguments, print_values, fi);
+         do_cleanups (cleanup_frame);
+       }
+    }
   do_cleanups (cleanup_stack_args);
 }
 
@@ -230,13 +355,35 @@ void
 mi_cmd_stack_list_variables (char *command, char **argv, int argc)
 {
   struct frame_info *frame;
+  int raw_arg = 0;
+  enum py_bt_status result = PY_BT_ERROR;
+  int print_value;
 
-  if (argc != 1)
-    error (_("Usage: PRINT_VALUES"));
+  if (argc > 0)
+    raw_arg = parse_no_frames_option (argv[0]);
 
-  frame = get_selected_frame (NULL);
+  if (argc < 1 || argc > 2 || (argc == 2 && ! raw_arg)
+      || (argc == 1 && raw_arg))
+    error (_("-stack-list-variables: Usage: " \
+            "[--no-frame-filters] PRINT_VALUES"));
 
-  list_args_or_locals (all, parse_print_values (argv[0]), frame);
+   frame = get_selected_frame (NULL);
+   print_value = parse_print_values (argv[raw_arg]);
+
+   if (! raw_arg && frame_filters)
+     {
+       int flags = PRINT_LEVEL | PRINT_ARGS | PRINT_LOCALS;
+
+       result = apply_frame_filter (frame, flags, print_value,
+                                   current_uiout, 0, 0);
+     }
+
+   /* Run the inbuilt backtrace if there are no filters registered, or
+      if "--no-frame-filters" has been specified from the command.  */
+   if (! frame_filters || raw_arg  || result == PY_BT_NO_FILTERS)
+     {
+       list_args_or_locals (all, print_value, frame);
+     }
 }
 
 /* Print single local or argument.  ARG must be already read in.  For
index df173fe4936b3285f891d1267fb5beac42c1e007..c9efa815af0fa8adbc36ac366938db61a5187a3a 100644 (file)
@@ -86,6 +86,7 @@ static struct mi_cmd mi_cmds[] =
                 mi_cmd_data_write_register_values),
   DEF_MI_CMD_MI ("enable-timings", mi_cmd_enable_timings),
   DEF_MI_CMD_MI ("enable-pretty-printing", mi_cmd_enable_pretty_printing),
+  DEF_MI_CMD_MI ("enable-frame-filters", mi_cmd_enable_frame_filters),
   DEF_MI_CMD_MI ("environment-cd", mi_cmd_env_cd),
   DEF_MI_CMD_MI ("environment-directory", mi_cmd_env_dir),
   DEF_MI_CMD_MI ("environment-path", mi_cmd_env_path),
index fdf6f9ca54533b17c0b897965785d235d8b930e3..4b9fb7ade6cbf972a08cbdf002e7e711ca9a2363 100644 (file)
@@ -118,6 +118,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_show_attributes;
 extern mi_cmd_argv_ftype mi_cmd_var_show_format;
 extern mi_cmd_argv_ftype mi_cmd_var_update;
 extern mi_cmd_argv_ftype mi_cmd_enable_pretty_printing;
+extern mi_cmd_argv_ftype mi_cmd_enable_frame_filters;
 extern mi_cmd_argv_ftype mi_cmd_var_set_update_range;
 
 /* Description of a single command.  */
diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py
new file mode 100644 (file)
index 0000000..cacab4d
--- /dev/null
@@ -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 (file)
index 0000000..b3af94b
--- /dev/null
@@ -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
index 63115830925859dd37cb14fea661f7f243acc3b0..61f5b5ee79abb330cc4ab4c4396a27d5f0c7fc29 100644 (file)
@@ -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 (file)
index 0000000..1b73059
--- /dev/null
@@ -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 (file)
index 0000000..10dce8e
--- /dev/null
@@ -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 (file)
index 0000000..d62c596
--- /dev/null
@@ -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;
+}
index db51f508b5b39b767a42911a5a869776a6a669d6..6fa303561fb764c3fe8d16aa29257b5470a19a38 100644 (file)
@@ -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 }
index 45a5193b0ad24bafd579f04654c23560d57dc21b..104b36d1b91285cc25326f6105e39fdfe9b8ebf1 100644 (file)
@@ -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 }
index b280c8cc716ceb14158de3b9c2ce4ccfbe898f13..890b65d346e197a511ba98c4d105e200889f2698 100644 (file)
@@ -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.
index ea97226f473395c344dff8117f7a145e6c160e0a..7337bffc380d2038ef56369162003d87bb985a97 100644 (file)
@@ -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);
index 67d06e5364b2b3da4835a37ae038541c6ff607a2..3e2a852432c808560a9467cd2e3e2e869d3d804a 100644 (file)
@@ -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
index 24e3077a003c52b898bfb46275a2cc370d7b466a..1a1e5c233428e2bf586f9e82df994d021c171877 100644 (file)
@@ -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);
index e13a5a040eae44b41c76742bdac6d93567dbe921..d10e9b49d0289842d983628150958676455ebc9e 100644 (file)
@@ -54,6 +54,7 @@
 
 #include "psymtab.h"
 #include "symfile.h"
+#include "python/python.h"
 
 void (*deprecated_selected_frame_level_changed_hook) (int);
 
@@ -1651,13 +1652,15 @@ frame_info (char *addr_exp, int from_tty)
    frames.  */
 
 static void
-backtrace_command_1 (char *count_exp, int show_locals, int from_tty)
+backtrace_command_1 (char *count_exp, int show_locals, int no_filters,
+                    int from_tty)
 {
   struct frame_info *fi;
   int count;
   int i;
   struct frame_info *trailing;
-  int trailing_level;
+  int trailing_level, py_start = 0, py_end = 0;
+  enum py_bt_status result = PY_BT_ERROR;
 
   if (!target_has_stack)
     error (_("No stack."));
@@ -1676,6 +1679,7 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty)
        {
          struct frame_info *current;
 
+         py_start = count;
          count = -count;
 
          current = trailing;
@@ -1697,9 +1701,17 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty)
 
          count = -1;
        }
+      else
+       {
+         py_start = 0;
+         py_end = count;
+       }
     }
   else
-    count = -1;
+    {
+      py_end = -1;
+      count = -1;
+    }
 
   if (info_verbose)
     {
@@ -1719,16 +1731,40 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty)
        }
     }
 
-  for (i = 0, fi = trailing; fi && count--; i++, fi = get_prev_frame (fi))
+  if (! no_filters)
     {
-      QUIT;
+      int flags = PRINT_LEVEL | PRINT_FRAME_INFO | PRINT_ARGS;
+      enum py_frame_args arg_type;
 
-      /* Don't use print_stack_frame; if an error() occurs it probably
-         means further attempts to backtrace would fail (on the other
-         hand, perhaps the code does or could be fixed to make sure
-         the frame->prev field gets set to NULL in that case).  */
-      print_frame_info (fi, 1, LOCATION, 1);
       if (show_locals)
+       flags |= PRINT_LOCALS;
+
+      if (!strcmp (print_frame_arguments, "scalars"))
+       arg_type = CLI_SCALAR_VALUES;
+      else if (!strcmp (print_frame_arguments, "all"))
+       arg_type = CLI_ALL_VALUES;
+      else
+       arg_type = NO_VALUES;
+
+      result = apply_frame_filter (get_current_frame (), flags, arg_type,
+                                  current_uiout, py_start, py_end);
+
+    }
+  /* Run the inbuilt backtrace if there are no filters registered, or
+     "no-filters" has been specified from the command.  */
+  if (no_filters ||  result == PY_BT_NO_FILTERS)
+    {
+      for (i = 0, fi = trailing; fi && count--; i++, fi = get_prev_frame (fi))
+       {
+         QUIT;
+
+         /* Don't use print_stack_frame; if an error() occurs it probably
+            means further attempts to backtrace would fail (on the other
+            hand, perhaps the code does or could be fixed to make sure
+            the frame->prev field gets set to NULL in that case).  */
+
+         print_frame_info (fi, 1, LOCATION, 1);
+         if (show_locals)
        {
          struct frame_id frame_id = get_frame_id (fi);
 
@@ -1744,24 +1780,25 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty)
            }
        }
 
-      /* Save the last frame to check for error conditions.  */
-      trailing = fi;
-    }
+         /* Save the last frame to check for error conditions.  */
+         trailing = fi;
+       }
 
-  /* If we've stopped before the end, mention that.  */
-  if (fi && from_tty)
-    printf_filtered (_("(More stack frames follow...)\n"));
+      /* If we've stopped before the end, mention that.  */
+      if (fi && from_tty)
+       printf_filtered (_("(More stack frames follow...)\n"));
 
-  /* If we've run out of frames, and the reason appears to be an error
-     condition, print it.  */
-  if (fi == NULL && trailing != NULL)
-    {
-      enum unwind_stop_reason reason;
+      /* If we've run out of frames, and the reason appears to be an error
+        condition, print it.  */
+      if (fi == NULL && trailing != NULL)
+       {
+         enum unwind_stop_reason reason;
 
-      reason = get_frame_unwind_stop_reason (trailing);
-      if (reason >= UNWIND_FIRST_ERROR)
-       printf_filtered (_("Backtrace stopped: %s\n"),
-                        frame_stop_reason_string (reason));
+         reason = get_frame_unwind_stop_reason (trailing);
+         if (reason >= UNWIND_FIRST_ERROR)
+           printf_filtered (_("Backtrace stopped: %s\n"),
+                            frame_stop_reason_string (reason));
+       }
     }
 }
 
@@ -1769,7 +1806,8 @@ static void
 backtrace_command (char *arg, int from_tty)
 {
   struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
-  int fulltrace_arg = -1, arglen = 0, argc = 0;
+  int fulltrace_arg = -1, arglen = 0, argc = 0, no_filters  = -1;
+  int user_arg = 0;
 
   if (arg)
     {
@@ -1786,25 +1824,31 @@ backtrace_command (char *arg, int from_tty)
          for (j = 0; j < strlen (argv[i]); j++)
            argv[i][j] = tolower (argv[i][j]);
 
-         if (fulltrace_arg < 0 && subset_compare (argv[i], "full"))
-           fulltrace_arg = argc;
+         if (no_filters < 0 && subset_compare (argv[i], "no-filters"))
+           no_filters = argc;
          else
            {
-             arglen += strlen (argv[i]);
-             argc++;
+             if (fulltrace_arg < 0 && subset_compare (argv[i], "full"))
+               fulltrace_arg = argc;
+             else
+               {
+                 user_arg++;
+                 arglen += strlen (argv[i]);
+               }
            }
+         argc++;
        }
-      arglen += argc;
-      if (fulltrace_arg >= 0)
+      arglen += user_arg;
+      if (fulltrace_arg >= 0 || no_filters >= 0)
        {
          if (arglen > 0)
            {
              arg = xmalloc (arglen + 1);
              make_cleanup (xfree, arg);
              arg[0] = 0;
-             for (i = 0; i < (argc + 1); i++)
+             for (i = 0; i < argc; i++)
                {
-                 if (i != fulltrace_arg)
+                 if (i != fulltrace_arg && i != no_filters)
                    {
                      strcat (arg, argv[i]);
                      strcat (arg, " ");
@@ -1816,7 +1860,8 @@ backtrace_command (char *arg, int from_tty)
        }
     }
 
-  backtrace_command_1 (arg, fulltrace_arg >= 0 /* show_locals */, from_tty);
+  backtrace_command_1 (arg, fulltrace_arg >= 0 /* show_locals */,
+                      no_filters >= 0 /* no frame-filters */, from_tty);
 
   do_cleanups (old_chain);
 }
@@ -1824,7 +1869,7 @@ backtrace_command (char *arg, int from_tty)
 static void
 backtrace_full_command (char *arg, int from_tty)
 {
-  backtrace_command_1 (arg, 1 /* show_locals */, from_tty);
+  backtrace_command_1 (arg, 1 /* show_locals */, 0, from_tty);
 }
 \f
 
@@ -2558,7 +2603,9 @@ It can be a stack frame number or the address of the frame.\n"));
   add_com ("backtrace", class_stack, backtrace_command, _("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
 With a negative argument, print outermost -COUNT frames.\nUse of the \
-'full' qualifier also prints the values of the local variables.\n"));
+'full' qualifier also prints the values of the local variables.\n\
+Use of the 'no-filters' qualifier prohibits frame filters from executing\n\
+on this backtrace.\n"));
   add_com_alias ("bt", "backtrace", class_stack, 0);
   if (xdb_commands)
     {
index d01aa9e60df9a5fcba7445e23bf69120b522a396..c6351fc9a8ea416413ddf9dada24d9458d27c026 100644 (file)
@@ -1,3 +1,12 @@
+2013-05-10  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * gdb.python/py-framefilter.py: New File.
+       * gdb.python/py-framefilter-mi.exp: Ditto.
+       * gdb.python/py-framefilter.c: Ditto.
+       * gdb.python/py-framefilter-mi.exp: Ditto.
+       * gdb.python/py-framefilter-mi.c: Ditto,
+       * gdb.python/py-framefilter-gdb.py.in: Ditto.
+
 2013-05-08  Tom Tromey  <tromey@redhat.com>
 
        * gdb.base/solib-search.exp: Set test name for "set
diff --git a/gdb/testsuite/gdb.python/py-framefilter-gdb.py.in b/gdb/testsuite/gdb.python/py-framefilter-gdb.py.in
new file mode 100644 (file)
index 0000000..2c1d870
--- /dev/null
@@ -0,0 +1,48 @@
+# 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/>.
+
+# This file is part of the GDB testsuite.  It tests Python-based
+# frame-filters.
+import gdb
+import itertools
+from gdb.FrameDecorator import FrameDecorator
+
+
+class FrameObjFile ():
+
+    def __init__ (self):
+        self.name = "Filter1"
+        self.priority = 1
+        self.enabled = False
+        gdb.current_progspace().frame_filters ["Progspace" + self.name] = self
+        gdb.current_objfile().frame_filters ["ObjectFile" + self.name] = self
+
+    def filter (self, frame_iter):
+        return frame_iter
+
+class FrameObjFile2 ():
+
+    def __init__ (self):
+        self.name = "Filter2"
+        self.priority = 100
+        self.enabled = True
+        gdb.current_progspace().frame_filters ["Progspace" + self.name] = self
+        gdb.current_objfile().frame_filters ["ObjectFile" + self.name] = self
+
+    def filter (self, frame_iter):
+        return frame_iter
+
+FrameObjFile()
+FrameObjFile2()
diff --git a/gdb/testsuite/gdb.python/py-framefilter-mi.c b/gdb/testsuite/gdb.python/py-framefilter-mi.c
new file mode 100644 (file)
index 0000000..18a1d5b
--- /dev/null
@@ -0,0 +1,138 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 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/>.  */
+
+#include <stdlib.h>
+
+void funca(void);
+int count = 0;
+
+typedef struct
+{
+  char *nothing;
+  int f;
+  short s;
+} foobar;
+
+void end_func (int foo, char *bar, foobar *fb, foobar bf)
+{
+  const char *str = "The End";
+  const char *st2 = "Is Near";
+  int b = 12;
+  short c = 5;
+  {
+    int d = 15;
+    int e = 14;
+    const char *foo = "Inside block";
+    {
+      int f = 42;
+      int g = 19;
+      const char *bar = "Inside block x2";
+      {
+       short h = 9;
+       h = h +1;  /* Inner test breakpoint  */
+      }
+    }
+  }
+
+  return; /* Backtrace end breakpoint */
+}
+
+void funcb(int j)
+{
+  struct foo
+  {
+    int a;
+    int b;
+  };
+
+  struct foo bar;
+
+  bar.a = 42;
+  bar.b = 84;
+
+  funca();
+  return;
+}
+
+void funca(void)
+{
+  foobar fb;
+  foobar *bf;
+
+  if (count < 10)
+    {
+      count++;
+      funcb(count);
+    }
+
+  fb.nothing = "Foo Bar";
+  fb.f = 42;
+  fb.s = 19;
+
+  bf = malloc (sizeof (foobar));
+  bf->nothing = malloc (128);
+  bf->nothing = "Bar Foo";
+  bf->f = 24;
+  bf->s = 91;
+
+  end_func(21, "Param", bf, fb);
+  free (bf->nothing);
+  free (bf);
+  return;
+}
+
+
+void func1(void)
+{
+  funca();
+  return;
+}
+
+int func2(void)
+{
+  func1();
+  return 1;
+}
+
+void func3(int i)
+{
+  func2();
+
+  return;
+}
+
+int func4(int j)
+{
+  func3(j);
+
+  return 2;
+}
+
+int func5(int f, int d)
+{
+  int i = 0;
+  char *random = "random";
+  i=i+f;
+
+  func4(i);
+  return i;
+}
+
+main()
+{
+  func5(3,5);
+}
diff --git a/gdb/testsuite/gdb.python/py-framefilter-mi.exp b/gdb/testsuite/gdb.python/py-framefilter-mi.exp
new file mode 100644 (file)
index 0000000..54fedf8
--- /dev/null
@@ -0,0 +1,179 @@
+# 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/>.
+
+# This file is part of the GDB testsuite.  It tests Python-based
+# frame-filters.
+load_lib mi-support.exp
+load_lib gdb-python.exp
+
+set MIFLAGS "-i=mi2"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile py-framefilter-mi.c
+set pyfile py-framefilter.py
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DMI}] != "" } {
+    untested ${testfile}.exp
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+if {[lsearch -exact [mi_get_features] python] < 0} {
+    unsupported "python support is disabled"
+    return -1
+}
+
+mi_runto main
+
+set remote_python_file [remote_download host ${srcdir}/${subdir}/${pyfile}]
+
+mi_gdb_test "python execfile ('${remote_python_file}')" ".*\\^done." \
+    "Load python file"
+
+# Multiple blocks test
+mi_continue_to_line [gdb_get_line_number {Inner test breakpoint} ${srcfile}] \
+  "step to breakpoint"
+
+mi_gdb_test "-stack-list-locals --all-values" \
+    "\\^done,locals=\\\[{name=\"h\",value=\"9\"},{name=\"f\",value=\"42\"},{name=\"g\",value=\"19\"},{name=\"bar\",value=\"$hex \\\\\"Inside block x2\\\\\"\"},{name=\"d\",value=\"15\"},{name=\"e\",value=\"14\"},{name=\"foo\",value=\"$hex \\\\\"Inside block\\\\\"\"},{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \
+    "stack-list-locals --all-values"
+
+mi_gdb_test "-enable-frame-filters" ".*\\^done." "enable frame filters"
+mi_gdb_test "-stack-list-locals --all-values" \
+    "\\^done,locals=\\\[{name=\"h\",value=\"9\"},{name=\"f\",value=\"42\"},{name=\"g\",value=\"19\"},{name=\"bar\",value=\"$hex \\\\\"Inside block x2\\\\\"\"},{name=\"d\",value=\"15\"},{name=\"e\",value=\"14\"},{name=\"foo\",value=\"$hex \\\\\"Inside block\\\\\"\"},{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \
+    "stack-list-locals --all-values frame filters enabled"
+
+mi_continue_to_line [gdb_get_line_number {Backtrace end breakpoint} ${srcfile}] \
+  "step to breakpoint"
+
+mi_gdb_test "-stack-list-frames" \
+    "\\^done,stack=\\\[frame={level=\"0\",addr=\"$hex\",func=\"cnuf_dne\".*},frame={level=\"1\",addr=\"$hex\",func=\"acnuf\".*},frame={level=\"2\",addr=\"$hex\",func=\"bcnuf\".*},frame={level=\"3\",addr=\"$hex\",func=\"acnuf\".*},frame={level=\"22\",addr=\"$hex\",func=\"1cnuf\".*,children=\\\[frame={level=\"23\",addr=\"$hex\",func=\"func2\".*}\\\]},frame={level=\"24\",addr=\"$hex\",func=\"3cnuf\".*},frame={level=\"27\",addr=\"$hex\",func=\"niam\".*}\\\].*" \
+    "filtered stack listing"
+mi_gdb_test "-stack-list-frames 0 3" \
+    "\\^done,stack=\\\[frame={level=\"0\",addr=\"$hex\",func=\"cnuf_dne\".*},frame={level=\"1\",addr=\"$hex\",func=\"acnuf\".*},frame={level=\"2\",addr=\"$hex\",func=\"bcnuf\".*},frame={level=\"3\",addr=\"$hex\",func=\"acnuf\".*}\\\]" \
+    "filtered stack list 0 3"
+mi_gdb_test "-stack-list-frames 22 24" \
+    "\\^done,stack=\\\[frame={level=\"22\",addr=\"$hex\",func=\"1cnuf\".*,children=\\\[frame={level=\"23\",addr=\"$hex\",func=\"func2\".*}\\\]},frame={level=\"24\",addr=\"$hex\",func=\"3cnuf\".*}\\\]" \
+    "filtered stack list 22 24"
+
+#stack list arguments
+
+
+mi_gdb_test "-stack-list-arguments 0" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[name=\"foo\",name=\"bar\",name=\"fb\",name=\"bf\"\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[name=\"j\"\\\]},.*frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[name=\"f\",name=\"d\"\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 0"
+
+mi_gdb_test "-stack-list-arguments --no-frame-filters 0" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[name=\"foo\",name=\"bar\",name=\"fb\",name=\"bf\"\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[name=\"j\"\\\]},.*frame={level=\"22\",args=\\\[\\\]},frame={level=\"23\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[name=\"f\",name=\"d\"\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments --no-frame-filters 0"
+
+mi_gdb_test "-stack-list-arguments 0 0 3" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[name=\"foo\",name=\"bar\",name=\"fb\",name=\"bf\"\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[name=\"j\"\\\]},frame={level=\"3\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 0 0 3"
+
+mi_gdb_test "-stack-list-arguments 0 22 27" \
+    "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[name=\"f\",name=\"d\"\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 0 22 27"
+
+mi_gdb_test "-stack-list-arguments 1" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",value=\"21\"},{name=\"bar\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",value=\"$hex\"},{name=\"bf\",value=\"{nothing = $hex \\\\\"Foo Bar\\\\\", f = 42, s = 19}\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",value=\"3\"},{name=\"d\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 1"
+
+mi_gdb_test "-stack-list-arguments --no-frame-filters 1" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",value=\"21\"},{name=\"bar\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",value=\"$hex\"},{name=\"bf\",value=\"{nothing = $hex \\\\\"Foo Bar\\\\\", f = 42, s = 19}\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\]},frame={level=\"23\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",value=\"3\"},{name=\"d\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments --no-frame-filters 1"
+
+
+mi_gdb_test "-stack-list-arguments 1 0 3" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",value=\"21\"},{name=\"bar\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",value=\"$hex\"},{name=\"bf\",value=\"{nothing = $hex \\\\\"Foo Bar\\\\\", f = 42, s = 19}\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",value=\"10\"}\\\]},frame={level=\"3\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 1 0 3"
+
+mi_gdb_test "-stack-list-arguments 1 22 27" \
+    "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",value=\"3\"},{name=\"d\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 1 22 27"
+
+mi_gdb_test "-stack-list-arguments 2" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",type=\"int\",value=\"21\"},{name=\"bar\",type=\"char \\\*\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",type=\"foobar \\\*\",value=\"$hex\"},{name=\"bf\",type=\"foobar\"\}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",type=\"int\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 2"
+
+mi_gdb_test "-stack-list-arguments --no-frame-filters 2" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",type=\"int\",value=\"21\"},{name=\"bar\",type=\"char \\\*\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",type=\"foobar \\\*\",value=\"$hex\"},{name=\"bf\",type=\"foobar\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",type=\"int\",value=\"10\"}\\\]},.*frame={level=\"22\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments --no-frame-filters 2"
+
+
+mi_gdb_test "-stack-list-arguments 2 0 3" \
+    "\\^done,stack-args=\\\[frame={level=\"0\",args=\\\[{name=\"foo\",type=\"int\",value=\"21\"},{name=\"bar\",type=\"char \\\*\",value=\"$hex \\\\\"Param\\\\\"\"},{name=\"fb\",type=\"foobar \\\*\",value=\"$hex\"},{name=\"bf\",type=\"foobar\"}\\\]},frame={level=\"1\",args=\\\[\\\]},frame={level=\"2\",args=\\\[{name=\"j\",type=\"int\",value=\"10\"}\\\]},frame={level=\"3\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 2 0 3"
+
+mi_gdb_test "-stack-list-arguments 2 22 27" \
+    "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\],children=\\\[frame={level=\"23\",args=\\\[\\\]}\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments 2 22 27"
+
+mi_gdb_test "-stack-list-arguments --no-frame-filters 2 22 27" \
+    "\\^done,stack-args=\\\[frame={level=\"22\",args=\\\[\\\]},frame={level=\"23\",args=\\\[\\\]},.*frame={level=\"26\",args=\\\[{name=\"f\",type=\"int\",value=\"3\"},{name=\"d\",type=\"int\",value=\"5\"}\\\]},frame={level=\"27\",args=\\\[\\\]}\\\]" \
+    "stack-list-arguments --no-frame-filters 2 22 27"
+
+#stack-list-locals
+mi_gdb_test "-stack-list-locals --no-frame-filters 0" \
+    "\\^done,locals=\\\[name=\"str\",name=\"st2\",name=\"b\",name=\"c\"\\\]" \
+    "stack-list-locals --no-frame-filters 0"
+
+mi_gdb_test "-stack-list-locals --no-frame-filters 1" \
+    "\\^done,locals=\\\[{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \
+    "stack-list-locals --no-frame-filters 1"
+
+mi_gdb_test "-stack-list-locals --no-frame-filters 2" \
+    "\\^done,locals=\\\[{name=\"str\",type=\"const char \\\*\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",type=\"const char \\\*\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",type=\"int\",value=\"12\"},{name=\"c\",type=\"short\",value=\"5\"}\\\]" \
+    "stack-list-locals --no-frame-filters 2"
+
+mi_gdb_test "-stack-list-locals --no-frame-filters --no-values" \
+    "\\^done,locals=\\\[name=\"str\",name=\"st2\",name=\"b\",name=\"c\"\\\]" \
+    "stack-list-locals --no-frame-filters --no-values"
+
+mi_gdb_test "-stack-list-locals --no-frame-filters --all-values" \
+    "\\^done,locals=\\\[{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \
+    "stack-list-locals --no-frame-filters --all-values"
+
+mi_gdb_test "-stack-list-locals --no-frame-filters --simple-values" \
+    "\\^done,locals=\\\[{name=\"str\",type=\"const char \\\*\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",type=\"const char \\\*\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",type=\"int\",value=\"12\"},{name=\"c\",type=\"short\",value=\"5\"}\\\]" \
+    "stack-list-locals --no-frame-filters --simple-values"
+
+mi_gdb_test "-stack-list-locals 0" \
+    "\\^done,locals=\\\[name=\"str\",name=\"st2\",name=\"b\",name=\"c\"\\\]" \
+    "stack-list-locals 0"
+
+mi_gdb_test "-stack-list-locals 1" \
+    "\\^done,locals=\\\[{name=\"str\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",value=\"12\"},{name=\"c\",value=\"5\"}\\\]" \
+    "stack-list-locals 1"
+
+mi_gdb_test "-stack-list-locals 2" \
+    "\\^done,locals=\\\[{name=\"str\",type=\"const char \\\*\",value=\"$hex \\\\\"The End\\\\\"\"},{name=\"st2\",type=\"const char \\\*\",value=\"$hex \\\\\"Is Near\\\\\"\"},{name=\"b\",type=\"int\",value=\"12\"},{name=\"c\",type=\"short\",value=\"5\"}\\\]" \
+    "stack-list-locals 2"
+
+# stack-list-variables
+mi_gdb_test "-stack-list-variables --no-frame-filters 0" \
+    "\\^done,variables=\\\[{name=\"foo\",arg=\"1\"},{name=\"bar\",arg=\"1\"},{name=\"fb\",arg=\"1\"},{name=\"bf\",arg=\"1\"},{name=\"str\"},{name=\"st2\"},{name=\"b\"},{name=\"c\"}\\\]" \
+    "stack-list-variables --no-frame-filters 0"
+
+mi_gdb_test "-stack-list-variables 0" \
+    "\\^done,variables=\\\[{name=\"foo\",arg=\"1\"},{name=\"bar\",arg=\"1\"},{name=\"fb\",arg=\"1\"},{name=\"bf\",arg=\"1\"},{name=\"str\"},{name=\"st2\"},{name=\"b\"},{name=\"c\"}\\\]" \
+    "stack-list-variables 0"
diff --git a/gdb/testsuite/gdb.python/py-framefilter.c b/gdb/testsuite/gdb.python/py-framefilter.c
new file mode 100644 (file)
index 0000000..a5bf997
--- /dev/null
@@ -0,0 +1,155 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 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/>.  */
+
+#include <stdlib.h>
+
+void funca(void);
+int count = 0;
+
+typedef struct
+{
+  char *nothing;
+  int f;
+  short s;
+} foobar;
+
+void end_func (int foo, char *bar, foobar *fb, foobar bf)
+{
+  const char *str = "The End";
+  const char *st2 = "Is Near";
+  int b = 12;
+  short c = 5;
+
+  {
+    int d = 15;
+    int e = 14;
+    const char *foo = "Inside block";
+    {
+      int f = 42;
+      int g = 19;
+      const char *bar = "Inside block x2";
+      {
+       short h = 9;
+       h = h +1;  /* Inner test breakpoint  */
+      }
+    }
+  }
+
+  return; /* Backtrace end breakpoint */
+}
+
+void funcb(int j)
+{
+  struct foo
+  {
+    int a;
+    int b;
+  };
+
+  struct foo bar;
+
+  bar.a = 42;
+  bar.b = 84;
+
+  funca();
+  return;
+}
+
+void funca(void)
+{
+  foobar fb;
+  foobar *bf = NULL;
+
+  if (count < 10)
+    {
+      count++;
+      funcb(count);
+    }
+
+  fb.nothing = "Foo Bar";
+  fb.f = 42;
+  fb.s = 19;
+
+  bf = alloca (sizeof (foobar));
+  bf->nothing = alloca (128);
+  bf->nothing = "Bar Foo";
+  bf->f = 24;
+  bf->s = 91;
+
+  end_func(21, "Param", bf, fb);
+  return;
+}
+
+
+void func1(void)
+{
+  funca();
+  return;
+}
+
+int func2(int f)
+{
+  int c;
+  const char *elided = "Elided frame";
+  foobar fb;
+  foobar *bf = NULL;
+
+  fb.nothing = "Elided Foo Bar";
+  fb.f = 84;
+  fb.s = 38;
+
+  bf = alloca (sizeof (foobar));
+  bf->nothing = alloca (128);
+  bf->nothing = "Elided Bar Foo";
+  bf->f = 48;
+  bf->s = 182;
+
+  func1();
+  return 1;
+}
+
+void func3(int i)
+{
+  func2(i);
+
+  return;
+}
+
+int func4(int j)
+{
+  func3(j);
+
+  return 2;
+}
+
+int func5(int f, int d)
+{
+  int i = 0;
+  char *random = "random";
+  i=i+f;
+
+  func4(i);
+  return i;
+}
+
+main()
+{
+  int z = 32;
+  int y = 44;
+  const char *foo1 = "Test";
+  func5(3,5);
+}
diff --git a/gdb/testsuite/gdb.python/py-framefilter.exp b/gdb/testsuite/gdb.python/py-framefilter.exp
new file mode 100644 (file)
index 0000000..6c9946b
--- /dev/null
@@ -0,0 +1,239 @@
+# 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/>.
+
+# This file is part of the GDB testsuite.  It tests Python-based
+# frame-filters.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# We cannot use prepare_for_testing as we have to set the safe-patch
+# to check objfile and progspace printers.
+if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} {
+    return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# Make the -gdb.py script available to gdb, it is automagically loaded by gdb.
+# Care is taken to put it in the same directory as the binary so that
+# gdb will find it.
+set remote_obj_python_file \
+    [remote_download \
+        host ${srcdir}/${subdir}/${testfile}-gdb.py.in \
+        ${subdir}/${testfile}-gdb.py]
+
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_test_no_output "set auto-load safe-path ${remote_obj_python_file}" \
+    "set auto-load safe-path"
+gdb_load ${binfile}
+# Verify gdb loaded the script.
+gdb_test "info auto-load python-scripts" "Yes.*/${testfile}-gdb.py.*" \
+    "Test auto-load had loaded python scripts"
+
+if ![runto_main] then {
+    perror "couldn't run to breakpoint"
+    return
+}
+gdb_test_no_output "set python print-stack full" \
+    "Set python print-stack to full"
+
+# Load global frame-filters
+set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py]
+gdb_test_no_output "python execfile ('${remote_python_file}')" \
+    "Load python file"
+
+gdb_breakpoint [gdb_get_line_number "Backtrace end breakpoint"]
+gdb_breakpoint [gdb_get_line_number "Inner test breakpoint"]
+gdb_continue_to_breakpoint "Inner test breakpoint"
+
+# Test multiple local blocks.
+gdb_test "bt full no-filters" \
+    ".*#0.*end_func.*h = 9.*f = 42.*g = 19.*bar = $hex \"Inside block x2\".*d = 15.*e = 14.*foo = $hex \"Inside block\".*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*" \
+    "bt full no-filters"
+gdb_test "bt full" \
+    ".*#0.*cnuf_dne.*h = 9.*f = 42.*g = 19.*bar = $hex \"Inside block x2\".*d = 15.*e = 14.*foo = $hex \"Inside block\".*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*" \
+    "bt full with filters"
+
+gdb_continue_to_breakpoint "Backtrace end breakpoint"
+
+# Test set/show
+gdb_test "info frame-filter" \
+    ".*900.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \
+    "info frame filter before setting priority"
+gdb_test "show frame-filter priority global Elider" \
+    "Priority of filter 'Elider' in list 'global' is: 900" \
+    "show frame-filter priority global Elider before setting"
+gdb_test_no_output  "set frame-filter priority global Elider 1000" \
+    "set frame-filter priotiy global Elider 1000"
+gdb_test "show frame-filter priority global Elider" \
+        "Priority of filter 'Elider' in list 'global' is: 1000" \
+        "show frame-filter priority global Elider after setting"
+gdb_test "info frame-filter" \
+    ".*1000.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \
+    "info frame filter after setting priority"
+
+# Test enable/disable
+gdb_test "info frame-filter" \
+    ".*1000.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \
+    "info frame filter before disable frame filter"
+gdb_test_no_output  "disable frame-filter global Elider" \
+    "disable frame-filter global Elider"
+gdb_test "info frame-filter" \
+    ".*1000.*No.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \
+    "info frame filter after disable frame filter"
+gdb_test_no_output  "enable frame-filter global Elider" \
+    "enable frame-filter global Elider"
+gdb_test "info frame-filter" \
+    ".*1000.*Yes.*Elider.*100.*Yes.*Reverse.*10.*.*No.*Object.*1.*" \
+    "info frame filter after reenabling frame filter"
+
+# Test no-filters
+gdb_test "bt no-filters" \
+    ".*#0.*end_func.*#22.*in func1.*#27.*in main \\(\\).*" \
+    "bt no-filters"
+
+# Test reverse
+gdb_test "bt" \
+    ".*#0.*cnuf_dne.*#22.*in 1cnuf.*#27.*in niam \\(\\).*" \
+    "bt with frame filters"
+
+# Disable Reverse
+gdb_test_no_output "disable frame-filter global Reverse" \
+    "disable frame-filter global Reverse"
+gdb_test "bt" \
+    ".*#0.*end_func.*#22.*in func1.*#27.*in main \\(\\).*" \
+    "bt with frame-filter Reverse disabled"
+gdb_test "bt -2" \
+    ".*#26.*func5.*#27.*in main \\(\\).*" \
+    "bt -2 with frame-filter Reverse disabled"
+gdb_test "bt 3" \
+    ".*#0.*end_func.*#1.*in funca \\(\\).*#2.*in funcb \\(j=10\\).*" \
+    "bt 3 with frame-filter Reverse disabled"
+gdb_test "bt no-filter full" \
+    ".*#0.*end_func.*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*#1.*in funca \\(\\).*#2.*in funcb \\(j=10\\).*bar = \{a = 42, b = 84\}.*" \
+    "bt no-filters full with Reverse disabled"
+gdb_test "bt full" \
+    ".*#0.*end_func.*str = $hex \"The End\".*st2 = $hex \"Is Near\".*b = 12.*c = 5.*#1.*in funca \\(\\).*#2.*in funcb \\(j=10\\).*bar = \{a = 42, b = 84\}.*#22.*in func1 \\(\\).*#23.*in func2 \\(f=3\\).*elided = $hex \"Elided frame\".*fb = \{nothing = $hex \"Elided Foo Bar\", f = 84, s = 38\}.*bf = $hex.*" \
+    "bt full with Reverse disabled"
+
+# Test set print frame-arguments
+# none
+gdb_test_no_output "set print frame-arguments none" \
+    "turn off frame arguments"
+gdb_test "bt no-filter 1" \
+    "#0.*end_func \\(foo=\.\.\., bar=\.\.\., fb=\.\.\., bf=\.\.\.\\) at .*py-framefilter.c.*" \
+    "bt no-filter 1 no args"
+gdb_test "bt 1" \
+    "#0.*end_func \\(foo=\.\.\., bar=\.\.\., fb=\.\.\., bf=\.\.\.\\) at .*py-framefilter.c.*" \
+    "bt 1 no args"
+
+# scalars
+gdb_test_no_output "set print frame-arguments scalars" \
+    "turn frame arguments to scalars only"
+gdb_test "bt no-filter 1" \
+    "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\.\.\.\\) at .*py-framefilter.c.*" \
+    "bt no-filter 1 scalars"
+gdb_test "bt 1" \
+    "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\.\.\.\\) at .*py-framefilter.c.*" \
+    "bt 1 scalars"
+
+# all
+gdb_test_no_output "set print frame-arguments all" \
+    "turn on frame arguments"
+gdb_test "bt no-filter 1" \
+    "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\{nothing = $hex \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \
+    "bt no-filter 1 all args"
+gdb_test "bt 1" \
+    "#0.*end_func \\(foo=21, bar=$hex \"Param\", fb=$hex, bf=\{nothing = $hex \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \
+    "bt 1 all args"
+
+# set print address off
+gdb_test_no_output "set print address off" \
+    "Turn off address printing"
+gdb_test "bt no-filter 1" \
+    "#0  end_func \\(foo=21, bar=\"Param\", fb=, bf=\{nothing = \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \
+    "bt no-filter 1 no address"
+gdb_test "bt 1" \
+    "#0  end_func \\(foo=21, bar=\"Param\", fb=, bf=\{nothing = \"Foo Bar\", f = 42, s = 19\}\\) at .*py-framefilter.c.*" \
+    "bt 1 no addresss"
+
+remote_file host delete ${remote_python_file}
+
+# Test with no debuginfo
+
+# We cannot use prepare_for_testing as we have to set the safe-patch
+# to check objfile and progspace printers.
+if {[build_executable $testfile.exp $testfile $srcfile {nodebug}] == -1} {
+    return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# Make the -gdb.py script available to gdb, it is automagically loaded by gdb.
+# Care is taken to put it in the same directory as the binary so that
+# gdb will find it.
+set remote_obj_python_file \
+    [remote_download \
+        host ${srcdir}/${subdir}/${testfile}-gdb.py.in \
+        ${subdir}/${testfile}-gdb.py]
+
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_test_no_output "set auto-load safe-path ${remote_obj_python_file}" \
+    "set auto-load safe-path for no debug info"
+gdb_load ${binfile}
+
+# Verify gdb loaded the script.
+gdb_test "info auto-load python-scripts" "Yes.*/${testfile}-gdb.py.*" \
+    "Set autoload path for no debug info tests"
+if ![runto_main] then {
+    perror "couldn't run to breakpoint"
+    return
+}
+
+gdb_test_no_output "set python print-stack full" \
+    "set python print-stack full for no debuginfo tests"
+
+# Load global frame-filters
+set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py]
+gdb_test_no_output "python execfile ('${remote_python_file}')" \
+    "Load python file for no debuginfo tests"
+
+# Disable Reverse
+gdb_test_no_output "disable frame-filter global Reverse" \
+    "disable frame-filter gloval Reverse for no debuginfo"
+gdb_test "bt" \
+    ".*#0..*in main \\(\\).*" \
+    "bt for no debuginfo"
+gdb_test "bt full" \
+    ".*#0..*in main \\(\\).*" \
+    "bt full for no debuginfo"
+gdb_test "bt no-filters" \
+    ".*#0..*in main \\(\\).*" \
+    "bt no filters for no debuginfo"
+gdb_test "bt no-filters full" \
+    ".*#0..*in main \\(\\).*" \
+    "bt no-filters full no debuginfo"
diff --git a/gdb/testsuite/gdb.python/py-framefilter.py b/gdb/testsuite/gdb.python/py-framefilter.py
new file mode 100644 (file)
index 0000000..d31bbbe
--- /dev/null
@@ -0,0 +1,117 @@
+# 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/>.
+
+# This file is part of the GDB testsuite.  It tests Python-based
+# frame-filters.
+import gdb
+import itertools
+from gdb.FrameDecorator import FrameDecorator
+import copy
+
+class Reverse_Function (FrameDecorator):
+
+    def __init__(self, fobj):
+        super(Reverse_Function, self).__init__(fobj)
+        self.fobj = fobj
+
+    def function (self):
+        fname = str (self.fobj.function())
+        if (fname == None or fname == ""):
+            return None
+        else:
+            fname = fname[::-1]
+        return fname
+
+class Dummy (FrameDecorator):
+
+    def __init__(self, fobj):
+        super(Dummy, self).__init__(fobj)
+        self.fobj = fobj
+
+    def function (self):
+        return "Dummy function"
+
+    def address (self):
+        return 0x123
+
+    def filename (self):
+        return "Dummy filename"
+
+    def frame_args (self):
+        return [("Foo",gdb.Value(12)),("Bar","Stuff"), ("FooBar",42)]
+
+    def frame_locals (self):
+        return []
+
+    def line (self):
+        return 0
+
+    def elided (self):
+        return None
+
+class FrameFilter ():
+
+    def __init__ (self):
+        self.name = "Reverse"
+        self.priority = 100
+        self.enabled = True
+        gdb.frame_filters [self.name] = self
+
+    def filter (self, frame_iter):
+        frame_iter = itertools.imap (Reverse_Function,
+                                     frame_iter)
+        return frame_iter
+
+class ElidingFrameDecorator(FrameDecorator):
+
+    def __init__(self, frame, elided_frames):
+        super(ElidingFrameDecorator, self).__init__(frame)
+        self.elided_frames = elided_frames
+
+    def elided(self):
+        return iter(self.elided_frames)
+
+class ElidingIterator:
+    def __init__(self, ii):
+        self.input_iterator = ii
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        frame = next(self.input_iterator)
+        if str(frame.function()) != 'func1':
+            return frame
+
+        # Suppose we want to return the 'func1' frame but elide the
+        # next frame.  E.g., if call in our interpreter language takes
+        # two C frames to implement, and the first one we see is the
+        # "sentinel".
+        elided = next(self.input_iterator)
+        return ElidingFrameDecorator(frame, [elided])
+
+class FrameElider ():
+
+    def __init__ (self):
+        self.name = "Elider"
+        self.priority = 900
+        self.enabled = True
+        gdb.frame_filters [self.name] = self
+
+    def filter (self, frame_iter):
+        return ElidingIterator (frame_iter)
+
+FrameFilter()
+FrameElider()