+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.
py-exitedevent.o \
py-finishbreakpoint.o \
py-frame.o \
+ py-framefilter.o \
py-function.o \
py-gdb-readline.o \
py-inferior.o \
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 \
$(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)
*** Changes since GDB 7.6
+* Python scripting
+
+ ** Frame filters and frame decorators have been added.
+
* New targets
Nios II ELF nios2*-*-elf
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 \
+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
@menu
* Frames:: Stack frames
* Backtrace:: Backtraces
+* Frame Filter Management:: Managing frame filters
* Selection:: Selecting a frame
* Frame Info:: Information on a frame
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})}
@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
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
* 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.
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
@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
@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 ()
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
@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
(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
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.
@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
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
@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
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.
(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
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
#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 };
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
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
{
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);
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
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
{
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
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);
}
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
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),
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. */
--- /dev/null
+# 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
--- /dev/null
+# 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
# 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__))
--- /dev/null
+# 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()
--- /dev/null
+# 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
--- /dev/null
+/* 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;
+}
/* 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;
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);
}
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)
{
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 *
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)
{
"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 }
/* 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;
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);
}
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)
{
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 *
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)
{
"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 }
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.
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);
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);
{
}
+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
#define GDB_PYTHON_H
#include "value.h"
+#include "mi/mi-cmds.h"
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 *);
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);
#include "psymtab.h"
#include "symfile.h"
+#include "python/python.h"
void (*deprecated_selected_frame_level_changed_hook) (int);
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."));
{
struct frame_info *current;
+ py_start = count;
count = -count;
current = trailing;
count = -1;
}
+ else
+ {
+ py_start = 0;
+ py_end = count;
+ }
}
else
- count = -1;
+ {
+ py_end = -1;
+ count = -1;
+ }
if (info_verbose)
{
}
}
- 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);
}
}
- /* 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));
+ }
}
}
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)
{
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, " ");
}
}
- 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);
}
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
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)
{
+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
--- /dev/null
+# 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()
--- /dev/null
+/* 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);
+}
--- /dev/null
+# 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"
--- /dev/null
+/* 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);
+}
--- /dev/null
+# 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"
--- /dev/null
+# 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()