* [patch][python] 5 of 5 - Frame filter documentation changes
@ 2012-11-30 14:32 Phil Muldoon
2012-12-01 10:59 ` Eli Zaretskii
2012-12-05 20:49 ` Tom Tromey
0 siblings, 2 replies; 17+ messages in thread
From: Phil Muldoon @ 2012-11-30 14:32 UTC (permalink / raw)
To: gdb-patches
This patch/email address documentation changes for Python Frame
Filters.
2012-11-30 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Backtrace): Add "raw" 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/Wrapper): 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.
--
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index afe3845..5a0215d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6360,6 +6360,7 @@ line per frame, for many frames, starting with the currently executing
frame (frame zero), followed by its caller (frame one), and on up the
stack.
+@anchor{backtrace-command}
@table @code
@kindex backtrace
@kindex bt @r{(@code{backtrace})}
@@ -6385,6 +6386,17 @@ Similar, but print only the outermost @var{n} frames.
@itemx bt full -@var{n}
Print the values of the local variables also. @var{n} specifies the
number of frames to print, as described above.
+
+@item backtrace raw
+@itemx bt raw
+@itemx bt raw @var{n}
+@itemx bt raw -@var{n}
+@itemx bt raw full
+@itemx bt raw full @var{n}
+@itemx bt raw full -@var{n}
+Do not run Python frame filters on this backtrace. @xref{Frame
+Filters API}, for more information. This is only relevant when
+@value{GDBN} has been configured with @code{Python} support.
@end table
@kindex where
@@ -22738,6 +22750,10 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
* Writing a Pretty-Printer:: Writing a Pretty-Printer.
* Type Printing API:: Pretty-printing types.
+* Frame Filters API:: Filtering and Wrapping Frames.
+* Frame Wrapper API:: Wrapping and Decorating Frames.
+* Writing a Frame Filter/Wrapper:: Writing a Frame Filter and Wrapper.
+* Managing Frame Filters:: Management of Frame Filters.
* 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.
@@ -24095,6 +24111,759 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Frame Filters API
+@subsubsection Filtering and Wrapping Frames.
+@cindex Frame Filter/Wrappers 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 applying actions to an iterator that is passed
+to that frame filter as a parameter. Typically, frame filters utilize
+tools such as the Python's @code{itertools} module to modify the
+iterator. If the frame filter modifies the iterator, it returns that
+modified iterator, otherwise it returns the original iterator
+unmodified. A frame filter must not alter the underlying @value{GDBN}
+frame or frames, or attempt to alter the call-stack within
+@value{GDBN}. Frame filters may only work on the wrapping iterator.
+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. Each frame filter object
+takes a Python iterator, and returns a Python iterator. For further
+information on frame filters see, @ref{Writing a Frame
+Filter/Wrapper}.
+
+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 (a Python iterator) 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 wrappers that wrap each @code{gdb.Frame},
+or another frame wrapper that wraps a frame wrapper. The first filter
+that is executed in the sequence of frame filters will receive an
+iterator entirely comprised of @code{BaseFrameWrapper} objects.
+However, after each frame filter is executed, the previous frame
+filter may have wrapped some or all of the frame wrappers with their
+own frame wrapper. As Frame Wrappers must also conform to a mandatory
+interface, these wrappers can be assumed to act in a uniform manner
+(@pxref{Frame Wrapper 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 wrapper 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{Managing Frame
+Filters}). This attribute may contain any combination of letters,
+numbers and spaces. 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 is 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 Wrapper API
+@subsubsection Wrapping and Decorating Frames.
+@cindex Frame Wrapper API
+
+Frame wrappers are sister objects to frame filters (@pxref{Frame
+Filters API}). Frame wrappers are applied by a frame filter and can
+only be used in conjunction with frame filters.
+
+The purpose of a frame wrapper is to customize the printed content of
+each frame. Frame wrappers have a mandatory interface, defined below.
+A frame wrapper object works on a single frame, but a frame wrapper
+object can be applied to multiple frames.
+
+@value{GDBN} already contains a frame wrapper called
+@code{BaseFrameWrapper}. This contains substantial amounts of
+boilerplate code to print the content of frames. It is recommended
+that other frame wrappers inherit and extend this object, and only to
+override the methods needed. The Python code for
+@code{BaseFrameWrapper} can be found in
+@file{@var{data-directory}/python/gdb}
+
+@defun FrameWrapper.elided ()
+
+The @code{elided} method groups frames together in a hierarchical
+system. An example would be an interpreter call that occurs over many
+frames but might be better represented as a group of frames distinct
+from the other frames.
+
+The @code{elide} function must return an iterator that conforms to the
+Python iterator protocol. This iterator must contains the frames that
+are being elided wrapped in a suitable frame wrapper. If there are no
+frames being elided in this frame wrapper, this method must return a
+Python @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 task to also filter out the elided frames from
+the source iterator. This will avoid the frame being printed twice.
+@end defun
+
+@defun FrameWrapper.function ()
+
+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 a
+Python @code{None}.
+
+If this function returns a Python @code{None}, @value{GDBN} will not
+print any data for this field.
+@end defun
+
+@defun FrameWrapper.address ()
+
+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 a Python @code{None}.
+
+If this function returns a Python @code{None}, @value{GDBN} will not
+print any data for this field.
+@end defun
+
+@defun FrameWrapper.filename ()
+
+This method returns the filename associated with this frame.
+
+This method must return a Python string containing the filename, and
+optionally, the path to the filename of the frame, or a Python
+@code{None}.
+
+If this function returns a Python @code{None}, @value{GDBN} will not
+print any data for this field.
+@end defun
+
+
+@defun FrameWrapper.line ():
+
+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 a Python
+@code{None}.
+
+If this function returns a Python @code{None}, @value{GDBN} will not
+print any data for this field.
+@end defun
+
+@defun FrameWrapper.frame_args ()
+
+This method must return an iterator that conforms to the Python
+iterator protocol, or a Python @code{None}. This iterator must
+contain objects that implement two methods, described here.
+
+The object must implement an @code{argument} method which takes no
+parameters and must return a @code{gdb.Symbol} or a Python string. It
+must also implement a @code{value} method which takes no parameters
+and which must return a @code{gdb.Value}, a Python value, or
+@code{None}. If the @code{value} method returns a Python @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 SomeFrameWrapper ()
+...
+...
+ def frame_args (self):
+ fvars = []
+ fvars.append (SymValueWrapper (``foo'', 42))
+
+ return iter (fvars)
+@end smallexample
+
+Even if the @code{frame_args} method returns only a single object, it
+must be wrapped in an iterator.
+
+If this function returns a Python @code{None}, @value{GDBN} will not
+print arguments for this frame.
+@end defun
+
+@defun FrameWrapper.frame_locals ()
+
+This method must return an iterator that conforms to the Python
+iterator protocol, or a Python @code{None}. This iterator must
+contain objects that implement two methods, described here.
+
+The object must implement an @code{argument} method which takes no
+parameters and must return a @code{gdb.Symbol} or a Python string. It
+must also implement a @code{value} method which takes no parameters
+and which must return a @code{gdb.Value}, a Python value, or
+@code{None}. If the @code{value} method returns a Python @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 SomeFrameWrapper ()
+...
+...
+ def frame_locals (self):
+ fvars = []
+ fvars.append (SymValueWrapper (``foo'', 42))
+
+ return iter (fvars)
+@end smallexample
+
+Even if the @code{frame_locals} method returns only a single object, it
+must be wrapped in an iterator.
+
+If this function returns a Python @code{None}, @value{GDBN} will not
+print locals for this frame.
+@end defun
+
+@defun FrameWrapper.frame ():
+
+This method must return the underlying @code{gdb.Frame} that this
+frame wrapper is wrapping. @value{GDBN} requires the underlying frame
+for internal frame information to determine how to print certain
+values in frame printing
+@end defun
+
+@node Writing a Frame Filter/Wrapper
+@subsubsection Writing a Frame Filter and Wrapper
+@cindex Writing a Frame Filter/Wrapper
+
+The Python dictionary @code{gdb.frame_filters} contains key/object
+pairings that compromise a frame filter. These frame filters must
+register with the dictionary directly. Frame filters in this
+dictionary are called @code{global} frame filters, and they are
+available when debugging all inferiors. 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 contain a @code{frame_filters} dictionary attribute.
+
+Each frame filter object in these dictionaries is passed a single
+Python iterator argument and should return a Python iterator. Each
+frame filter object must conform to the frame filter interface
+definition (@pxref{Frame Filters API}). The iterator returned by the
+frame filter must contain only a collection of frame wrappers
+(@pxref{Frame Wrapper API}), conforming to the frame wrapper interface
+definition.
+
+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 object files might be in use.
+@value{GDBN} then prunes any frame filter where the @code{enabled}
+attribute is set to @code{False}. This pruned list is then sorted
+according to the @code{priority} attribute in each filter. Once the
+dictionaries are combined, sorted and pruned, @value{GDBN} then wraps
+all frames in the call-stack with a @code{BaseFrameWrapper} object,
+and calls each filter in priority order. The input to the first frame
+filter will be an initial iterator wrapping a collection of
+@code{BaseFrameWrapper} objects. The output from the previous filter
+will always be the input to the next filter, and so on.
+
+@subsubheading Implementing a frame filter
+
+There are three basic elements that a frame filter must implement: it
+must correctly implement the documented interface (@pxref{Frame
+Filters 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):
+ self.name = "Foo"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters [self.name] = self
+
+ def filter (self, frame_iter):
+ 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:
+
+@smallexample
+ self.name = "Foo"
+ self.priority = 100
+ self.enabled = True
+@end smallexample
+
+The second step is registering the frame filter with the dictionary or
+dictionaries that the frame filter has interest in:
+
+@smallexample
+ gdb.frame_filters [self.name] = self
+@end smallexample
+
+As noted earlier, @code{gdb.frame_filters} is a dictionary that is
+initialized in the @code{gdb} module when @value{GDBN} starts. In
+this example, the frame filter only wishes to register with the
+@code{global} dictionary.
+
+@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{Managing Frame
+Filters}), 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:
+
+@smallexample
+ def filter (self, frame_iter):
+ return frame_iter
+@end smallexample
+
+Note that the @code{filter} 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 wrapper to perform some work on the frames.
+@xref{Frame Wrapper API}, for further information on the frame wrapper
+interface.
+
+This example works on inlined frames. It highlights frames which are
+inlined by adding the function in which they are inlined into. By
+applying a frame wrapper to all frames with the Python @code{itertools
+imap} method, the example defers actions to the frame wrapper. Frame
+wrappers 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 wrapper 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. 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 wrapper can examine each frame in
+turn when @value{GDBN} iterates. From a performance viewpoint, this
+is the most appropriate decision to make. A backtrace from large or
+complex programs can constitute many thousands of frames. 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 = "Inlined Frame Class"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters [self.name] = self
+
+ def filter (self, frame_iter):
+ frame_iter = itertools.imap (InlinedFrameWrapper,
+ 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 wrapper object called
+@code{InlinedFrameWrapper} 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 wrapper for this example.
+
+@smallexample
+class InlinedFrameWrapper (BaseFrameWrapper):
+
+ def __init__(self, fobj):
+ super(InlinedFrameWrapper, self).__init__(fobj)
+ self.fobj = fobj
+
+ def function (self):
+ frame = self.inferior_frame()
+ name = str(frame.name())
+ function = str(frame.function())
+
+ if frame.type() == gdb.INLINE_FRAME:
+ name = name + " [inlined from "+ function +"]"
+
+ return name
+@end smallexample
+
+This frame wrapper only defines and overrides the @code{function}
+method. It lets the supplied @code{BaseFrameWrapper}, 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 from main] (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 wrapper 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 wrapper 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
+wrapper 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 = "Inlined Frame Class"
+ 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
+
+ eliding_frame = next(self.input_iterator)
+ return ElidingFrameWrapper(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 wrapper, @code{frame}, is wrapping
+an inlined frame. If it is not, it returns the existing frame wrapper
+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 wrapper, @code{ElidingFrameWrapper}, which contains both the
+elided frame, and the eliding frame.
+
+@smallexample
+class ElidingInlineWrapper(BaseFrameWrapper):
+
+ def __init__(self, frame, elided_frames):
+ super(ElidingInlineWrapper, self).__init__(frame)
+ self.frame = frame
+ self.elided_frames = elided_frames
+
+ def elided(self):
+ return iter(self.elided_frames)
+@end smallexample
+
+This frame wrapper overrides one function and returns the inlined
+frame in the @code{elided} method. As before it lets
+@code{BaseFrameWrapper} 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 Managing Frame Filters
+@subsubsection Management of Frame Filters.
+@cindex Managing Frame Filters
+
+There are several commands available within @value{GDBN} to manage
+frame filters, 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
+@item disable frame-filter @var{filter-dictionary} @var{filter-name}
+Disable a frame filter in the dictionary matching
+@var{filter-dictionary} and @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{filter-name} is the name of the frame filter. 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} and @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{filter-name} is the name of the frame filter.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 1000 No Capital Primary Function Filter
+ 100 Yes Reverse
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 100 Yes Progspace Filter 1
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 999 Yes Build Test Program Filter
+
+(gdb) disable frame-filter ``/build/test'' ``Build Test Program Filter''
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 1000 No Capital Primary Function Filter
+ 100 Yes Reverse
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 100 Yes Progspace Filter 1
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 999 No Build Test Program Filter
+
+(gdb) enable frame-filter global ``Capital Primary Function Filter''
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 1000 Yes Capital Primary Function Filter
+ 100 Yes Reverse
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 100 Yes Progspace Filter 1
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 999 No Build Test Program Filter
+
+@end smallexample
+
+@kindex set python frame-filter priority
+@item set python 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 python frame-filter priority
+@item show python 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 No Capital Primary Function Filter
+ 100 Yes Reverse
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 100 Yes Progspace Filter 1
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 999 Yes Build Test Program Filter
+
+(gdb) set python frame-filter priority global ``Reverse'' 50
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 1000 No Capital Primary Function Filter
+ 50 Yes Reverse
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 100 Yes Progspace Filter 1
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ ======== ======= ====
+ 999 No Build Test Program Filter
+@end smallexample
+@end table
+
@node Inferiors In Python
@subsubsection Inferiors In Python
@cindex inferiors in Python
@@ -24944,6 +25713,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Progspace.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filters API}, for more information.
+@end defvar
+
@node Objfiles In Python
@subsubsection Objfiles In Python
@@ -24994,6 +25768,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Objfile.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filters API}, for more information.
+@end defvar
+
A @code{gdb.Objfile} object has the following methods:
@defun Objfile.is_valid ()
@@ -25975,7 +26754,7 @@ No my-foo-pretty-printers.py
When reading an auto-loaded file, @value{GDBN} sets the
@dfn{current objfile}. This is available via the @code{gdb.current_objfile}
function (@pxref{Objfiles In Python}). This can be useful for
-registering objfile-specific pretty-printers.
+registering objfile-specific pretty-printers and frame-filters.
@menu
* objfile-gdb.py file:: The @file{@var{objfile}-gdb.py} file
@@ -29659,6 +30438,25 @@ Is this going away????
@node GDB/MI Stack Manipulation
@section @sc{gdb/mi} Stack Manipulation Commands
+@subheading The @code{-enable-frame-filters} Command
+@findex -enable-frame-filters
+
+@smallexample
+-enable-frame-filters
+@end smallexample
+
+@value{GDBN} allows Python-based frame filters to affect the output of
+the MI commands relating to stack traces. As there is no way to
+implement this in a fully backward-compatible way, a front end must
+request that this functionality be enabled.
+
+Once enabled, this feature cannot be disabled.
+
+Note that if Python support has not been compiled into @value{GDBN},
+this command will still succeed (and do nothing).
+
+This feature is currently (as of @value{GDBN} 7.6) experimental, and
+may work differently in future versions of @value{GDBN}.
@subheading The @code{-stack-info-frame} Command
@findex -stack-info-frame
@@ -29726,13 +30524,14 @@ For a stack with frame levels 0 through 11:
(gdb)
@end smallexample
+@anchor{-stack-list-arguments}
@subheading The @code{-stack-list-arguments} Command
@findex -stack-list-arguments
@subsubheading Synopsis
@smallexample
- -stack-list-arguments @var{print-values}
+ -stack-list-arguments [ --no-frame-filters ] @var{print-values}
[ @var{low-frame} @var{high-frame} ]
@end smallexample
@@ -29749,7 +30548,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
+
Use of this command to obtain arguments in a single frame is
deprecated in favor of the @samp{-stack-list-variables} command.
@@ -29820,13 +30621,14 @@ args=[@{name="intarg",value="2"@},
@c @subheading -stack-list-exception-handlers
+@anchor{-stack-list-frames}
@subheading The @code{-stack-list-frames} Command
@findex -stack-list-frames
@subsubheading Synopsis
@smallexample
- -stack-list-frames [ @var{low-frame} @var{high-frame} ]
+ -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ]
@end smallexample
List the frames currently on the stack. For each frame it displays the
@@ -29856,7 +30658,9 @@ levels are between the two arguments (inclusive). If the two arguments
are equal, it shows the single frame at the corresponding level. It is
an error if @var{low-frame} is larger than the actual number of
frames. On the other hand, @var{high-frame} may be larger than the
-actual number of frames, in which case only existing frames will be returned.
+actual number of frames, in which case only existing frames will be
+returned. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
@subsubheading @value{GDBN} Command
@@ -29926,11 +30730,12 @@ Show a single frame:
@subheading The @code{-stack-list-locals} Command
@findex -stack-list-locals
+@anchor{-stack-list-locals}
@subsubheading Synopsis
@smallexample
- -stack-list-locals @var{print-values}
+ -stack-list-locals [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the local variable names for the selected frame. If
@@ -29941,7 +30746,8 @@ type and value for simple data types, and the name and type for arrays,
structures and unions. In this last case, a frontend can immediately
display the value of simple data types and create variable objects for
other data types when the user wishes to explore their values in
-more detail.
+more detail. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
This command is deprecated in favor of the
@samp{-stack-list-variables} command.
@@ -29966,13 +30772,14 @@ This command is deprecated in favor of the
(gdb)
@end smallexample
+@anchor{-stack-list-variables}
@subheading The @code{-stack-list-variables} Command
@findex -stack-list-variables
@subsubheading Synopsis
@smallexample
- -stack-list-variables @var{print-values}
+ -stack-list-variables [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the names of local variables and function arguments for the selected frame. If
@@ -29980,7 +30787,8 @@ Display the names of local variables and function arguments for the selected fra
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
@subsubheading Example
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-11-30 14:32 [patch][python] 5 of 5 - Frame filter documentation changes Phil Muldoon
@ 2012-12-01 10:59 ` Eli Zaretskii
2012-12-05 19:11 ` Tom Tromey
2012-12-07 14:35 ` Phil Muldoon
2012-12-05 20:49 ` Tom Tromey
1 sibling, 2 replies; 17+ messages in thread
From: Eli Zaretskii @ 2012-12-01 10:59 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
> Date: Fri, 30 Nov 2012 14:31:59 +0000
> From: Phil Muldoon <pmuldoon@redhat.com>
>
> This patch/email address documentation changes for Python Frame
> Filters.
Thanks.
What about NEWS?
> +@item backtrace raw
> +@itemx bt raw
> +@itemx bt raw @var{n}
> +@itemx bt raw -@var{n}
> +@itemx bt raw full
> +@itemx bt raw full @var{n}
> +@itemx bt raw full -@var{n}
> +Do not run Python frame filters on this backtrace. @xref{Frame
> +Filters API}, for more information. This is only relevant when
> +@value{GDBN} has been configured with @code{Python} support.
I'd prefer 'no-filters' or some such, since 'raw' has no immediate
relation to "frame filters".
I suggest to mention here that frame filters can be disabled, with a
cross-reference to where commands to disable them are described.
Also, I think we should have a CLI commands to enable and disable them
all, because otherwise I see no simple way of getting
backward-compatible behavior.
> +@node Frame Filters API
> +@subsubsection Filtering and Wrapping Frames.
> +@cindex Frame Filter/Wrappers API
There's a separate node about frame wrapper API, so I think this
@cindex entry should be modified accordingly. Also, please start
index entries with a lower-case letter, unless caps are absolutely
necessary (as in proper names).
> +@value{GDBN}. Frame filters may only work on the wrapping iterator.
> +This preserves data integrity within @value{GDBN}.
Not sure the reader will understand what you mean by "wrapping
iterator" here. Perhaps explain or give a cross-reference.
> +takes a Python iterator, and returns a Python iterator. For further
> +information on frame filters see, @ref{Writing a Frame
> +Filter/Wrapper}. ^
No need for this comma.
> +@node Frame Wrapper API
> +@subsubsection Wrapping and Decorating Frames.
> +@cindex Frame Wrapper API
Index entries should start with a lower-case letter.
> +@defun FrameWrapper.elided ()
> +
> +The @code{elided} method groups frames together in a hierarchical
> +system. An example would be an interpreter call that occurs over many
> +frames but might be better represented as a group of frames distinct
> +from the other frames.
> +
> +The @code{elide} function must return an iterator that conforms to the
> +Python iterator protocol.
"elide" or "elided"?
> This iterator must contains the frames that
^^^^^^^^^^^^^
Either "must contain" or "contains" without "must".
> +It is the frame filter task to also filter out the elided frames from
^^^^^^
"filter's"
> +the source iterator. This will avoid the frame being printed twice.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"This will avoid printing the frame twice."
> +@defun FrameWrapper.filename ()
> +
> +This method returns the filename associated with this frame.
> +
> +This method must return a Python string containing the filename, and
> +optionally, the path to the filename of the frame, or a Python
> +@code{None}.
So does this method return one string or two, when it uses the
optional behavior? IOW, it is not clear whether "and" means another
output or the contents of a single output string.
> +@defun FrameWrapper.frame_args ()
> +
> +This method must return an iterator that conforms to the Python
> +iterator protocol, or a Python @code{None}. This iterator must
> +contain objects that implement two methods, described here.
> +
> +The object must implement an @code{argument} method which takes no
> +parameters and must return a @code{gdb.Symbol} or a Python string. It
> +must also implement a @code{value} method which takes no parameters
> +and which must return a @code{gdb.Value}, a Python value, or
> +@code{None}. If the @code{value} method returns a Python @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.
I would suggest to describe what are the symbol and the value here.
Maybe the reader will be able to guess that, but why expect them to
guess?
> +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
Is this example (which AFAIU simply shows what the default of None
will do) really useful? A useful example should show some non-trivial
use of the facility.
> +@defun FrameWrapper.frame_locals ()
> +
> +This method must return an iterator that conforms to the Python
> +iterator protocol, or a Python @code{None}. This iterator must
> +contain objects that implement two methods, described here.
> +
> +The object must implement an @code{argument} method which takes no
> +parameters and must return a @code{gdb.Symbol} or a Python string. It
> +must also implement a @code{value} method which takes no parameters
> +and which must return a @code{gdb.Value}, a Python value, or
> +@code{None}. If the @code{value} method returns a Python @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.
Likewise here: a description of what the symbol and the value express
would be beneficial.
> +@node Writing a Frame Filter/Wrapper
> +@subsubsection Writing a Frame Filter and Wrapper
> +@cindex Writing a Frame Filter/Wrapper
Lower-case letters in index entries, please.
> +The Python dictionary @code{gdb.frame_filters} contains key/object
> +pairings that compromise a frame filter. These frame filters must
^^^^^^^^^^
I assume you meant "comprise".
> +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 contain a @code{frame_filters} dictionary attribute.
^^^^^^^
"contains"
> +
> +Each frame filter object in these dictionaries is passed a single
> +Python iterator argument and should return a Python iterator. Each
> +frame filter object must conform to the frame filter interface
> +definition (@pxref{Frame Filters API}). The iterator returned by the
> +frame filter must contain only a collection of frame wrappers
> +(@pxref{Frame Wrapper API}), conforming to the frame wrapper interface
> +definition.
> +
> +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 object files might be in use.
^ ^ ^
I think you need commas in the indicated places, or else this sentence
can be misinterpreted. Also I'd insert "several" before "object", for
the same reason. That's assuming I understood correctly what you
meant in that sentence.
> +@value{GDBN} then prunes any frame filter where the @code{enabled}
> +attribute is set to @code{False}.
"any frame filters whose @code{enabled} attribute is @code{False}."
> +according to the @code{priority} attribute in each filter. Once the
> +dictionaries are combined, sorted and pruned, @value{GDBN} then wraps
^^^^^^^^^^^^^^^^^
"pruned and sorted" (as this is the order you just described).
> +all frames in the call-stack with a @code{BaseFrameWrapper} object,
> +and calls each filter in priority order.
The "priority" part should not be here, since the filters are already
sorted, right?
> The input to the first frame
> +filter will be an initial iterator wrapping a collection of
> +@code{BaseFrameWrapper} objects. The output from the previous filter
> +will always be the input to the next filter, and so on.
I would suggest to move all the text of this node up to here to the
"Frame Filters API" section. This text provides an overview of how
filters and wrappers work, which IMO is sorely missed before you dive
into describing the API itself. Having this overview there will allow
the reader to better understand what you describe in the correct
context. In any case, the text up to here has almost nothing to do
with "writing a filter/wrapper", which is the subject of this section.
> +@subsubheading Implementing a frame filter
If you take my suggestion above, this subsubheading will become
unnecessary.
> +The first step is attribute creation and assignment:
> +
> +@smallexample
> + self.name = "Foo"
> + self.priority = 100
> + self.enabled = True
> +@end smallexample
> +
> +The second step is registering the frame filter with the dictionary or
> +dictionaries that the frame filter has interest in:
> +
> +@smallexample
> + gdb.frame_filters [self.name] = self
> +@end smallexample
Instead of repeating the code fragments, how about adding comments to
the example that explain what its various parts do?
> +@item disable frame-filter @var{filter-dictionary} @var{filter-name}
> +Disable a frame filter in the dictionary matching
> +@var{filter-dictionary} and @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{filter-name} is the name of the frame filter. A disabled
> +frame-filter is not deleted, it may be enabled again later.
It would be good to say here how to disable all filters at once. (I
hope there is such a way.)
> +(gdb) info frame-filter
> +
> +global frame-filters:
> + Priority Enabled Name
> + ======== ======= ====
> + 1000 No Capital Primary Function Filter
> + 100 Yes Reverse
> +progspace /build/test frame-filters:
> + Priority Enabled Name
> + ======== ======= ====
> + 100 Yes Progspace Filter 1
> +objfile /build/test frame-filters:
> + Priority Enabled Name
> + ======== ======= ====
> + 999 Yes Build Test Program Filter
Wouldn't it be better to display all the filters in a single table,
adding the dictionary as another column?
> +(gdb) disable frame-filter ``/build/test'' ``Build Test Program Filter''
Why the quotes? they were never mentioned before. And in any case,
use ".." here, not ``..'', as the conversion is not done in @example,
AFAIR.
> +@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.
So in CLI, each filter can be enabled and disabled individually, but
in MI they can only be enabled en masse, and cannot be disabled? is
that a good idea?
> +This feature is currently (as of @value{GDBN} 7.6) experimental, and
> +may work differently in future versions of @value{GDBN}.
Do we have to have this version-specific note in the manual? They are
maintenance burden in the long run.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-12-01 10:59 ` Eli Zaretskii
@ 2012-12-05 19:11 ` Tom Tromey
2012-12-07 14:35 ` Phil Muldoon
1 sibling, 0 replies; 17+ messages in thread
From: Tom Tromey @ 2012-12-05 19:11 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Phil Muldoon, gdb-patches
>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
Eli> So in CLI, each filter can be enabled and disabled individually, but
Eli> in MI they can only be enabled en masse, and cannot be disabled? is
Eli> that a good idea?
In MI it is fine. It is no extra effort for an MI client to request
unfiltered output on each invocation it cares to.
The reason for the enablement command for MI is just so that clients
don't get filtered output without expecting it. This would work ok --
the output is backward compatible -- but could be surprising to users.
An MI client could disable filters individually. The commands are
always there.
Tom
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-11-30 14:32 [patch][python] 5 of 5 - Frame filter documentation changes Phil Muldoon
2012-12-01 10:59 ` Eli Zaretskii
@ 2012-12-05 20:49 ` Tom Tromey
2012-12-07 14:55 ` Phil Muldoon
1 sibling, 1 reply; 17+ messages in thread
From: Tom Tromey @ 2012-12-05 20:49 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> +A frame filter works by applying actions to an iterator that is passed
Phil> +to that frame filter as a parameter. Typically, frame filters utilize
Phil> +tools such as the Python's @code{itertools} module to modify the
Phil> +iterator. If the frame filter modifies the iterator, it returns that
Phil> +modified iterator, otherwise it returns the original iterator
Phil> +unmodified.
I think this is a somewhat confusing phrasing. The iterator is not
really modified; instead, the filter takes one iterator as an argument
and then returns an iterator (possibly even the same one).
Phil> +@defvar FrameFilter.name
Phil> +The @code{name} attribute must be Python string which contains the
Phil> +name of the filter displayed by @value{GDBN} (@pxref{Managing Frame
Phil> +Filters}). This attribute may contain any combination of letters,
Phil> +numbers and spaces. Care should be taken to ensure that it is unique.
Phil> +This attribute is mandatory.
Do we really want to allow spaces?
Won't that mess with argument parsing and/or command installation?
If we really want this then there should be tests for it.
(I didn't go back to see if there were already some...)
Phil> +@node Frame Wrapper API
Phil> +@subsubsection Wrapping and Decorating Frames.
Phil> +@cindex Frame Wrapper API
Phil> +
Phil> +Frame wrappers are sister objects to frame filters (@pxref{Frame
Phil> +Filters API}). Frame wrappers are applied by a frame filter and can
Phil> +only be used in conjunction with frame filters.
I tend to think we should have a name for the generic object other than
FrameWrapper.
Also I think maybe we should explain why we didn't just use the exact
same API as supplied by gdb.Frame.
Phil> +A frame wrapper object works on a single frame, but a frame wrapper
Phil> +object can be applied to multiple frames.
This reads weirdly.
Phil> +@value{GDBN} already contains a frame wrapper called
Phil> +@code{BaseFrameWrapper}. This contains substantial amounts of
Phil> +boilerplate code to print the content of frames.
It doesn't really print... and I think this should mention gdb.Frame
explicitly, rather than "frames".
Phil> +override the methods needed. The Python code for
Phil> +@code{BaseFrameWrapper} can be found in
Phil> +@file{@var{data-directory}/python/gdb}
I think if people really need to read the source then we probably should
write more documentation. Let's just drop this line.
Phil> +@defun FrameWrapper.elided ()
[...]
Phil> +The @code{elide} function must return an iterator that conforms to the
Phil> +Python iterator protocol.
Let's let elided return any iterable, not just an iterator.
Phil> +are being elided wrapped in a suitable frame wrapper. If there are no
Phil> +frames being elided in this frame wrapper, this method must return a
Phil> +Python @code{None}.
I think just "return @code{None}", here and elsewhere.
Phil> +@defun FrameWrapper.frame_args ()
Phil> +
Phil> +This method must return an iterator that conforms to the Python
Extra space after "an".
I think allowing any iterable here would be good.
Phil> +Even if the @code{frame_args} method returns only a single object, it
Phil> +must be wrapped in an iterator.
This can be dropped.
Phil> +@defun FrameWrapper.frame_locals ()
Since frame_args and frame_locals are both very similar, I think their
text should be shared somehow. E.g., duplicating all the text about the
returned objects seems difficult to maintain.
Phil> +Each frame filter object in these dictionaries is passed a single
Phil> +Python iterator argument and should return a Python iterator. Each
Phil> +frame filter object must conform to the frame filter interface
Phil> +definition (@pxref{Frame Filters API}). The iterator returned by the
Phil> +frame filter must contain only a collection of frame wrappers
Phil> +(@pxref{Frame Wrapper API}), conforming to the frame wrapper interface
Phil> +definition.
I'd say drop the first and last sentences and leave just the middle one.
I think the frame filter API is sufficiently described elsewhere.
Phil> +When a command is executed from @value{GDBN} that is compatible with
Phil> +frame filters, @value{GDBN} combines the @code{global},
Phil> +@code{gdb.Progspace} and all @code{gdb.ObjFile} dictionaries
Phil> +currently loaded. All of the @code{gdb.Objfile} dictionaries are
One of these "objfile"s is capitalized incorrectly.
Phil> +@smallexample
Phil> + gdb.frame_filters [self.name] = self
Phil> +@end smallexample
Phil> +
Phil> +As noted earlier, @code{gdb.frame_filters} is a dictionary that is
Phil> +initialized in the @code{gdb} module when @value{GDBN} starts. In
Phil> +this example, the frame filter only wishes to register with the
Phil> +@code{global} dictionary.
It would be good to mention that in general it is preferable not to
register filters globally. I'm not sure if the value pretty-printing
text mentions this, but worth a look to see what it says.
Phil> +frame filters should avoid is unwinding the stack if possible. To
Phil> +search every frame to determine if it is inlined ahead of time may be
Phil> +too expensive at the filtering step. The frame filter cannot know how
Phil> +many frames it has to iterate over, and it would have to iterate
Phil> +through them all. This ends up duplicating effort as @value{GDBN}
Phil> +performs this iteration when it prints the frames.
While I think we do need to mention that lazy iteration is very
important, I don't think this is the reason -- gdb only prints the
frames that the frame iterator supplies it, so there is no double
iteration. The issue is that eagerly unwinding the stack may just be
expensive, as stacks can get very deep.
Phil> +In this example decision making can be deferred to the printing step.
Phil> +As each frame is printed, the frame wrapper can examine each frame in
Phil> +turn when @value{GDBN} iterates. From a performance viewpoint, this
Phil> +is the most appropriate decision to make. A backtrace from large or
Phil> +complex programs can constitute many thousands of frames. Also, if
Phil> +there are many frame filters unwinding the stack during filtering, it
Phil> +can substantially delay the printing of the backtrace which will
Phil> +result in large memory usage, and a poor user experience.
I guess you said that too :)
So part of that previous paragraph can just be zapped, I think.
Phil> + def function (self):
Phil> + frame = self.inferior_frame()
Phil> + name = str(frame.name())
Phil> + function = str(frame.function())
Phil> +
Phil> + if frame.type() == gdb.INLINE_FRAME:
Phil> + name = name + " [inlined from "+ function +"]"
Does this really work?
It seems like it shouldn't.
Phil> + def next(self):
Phil> + frame = next(self.input_iterator)
Phil> + if frame.inferior_frame().type() != gdb.INLINE_FRAME:
Phil> + return frame
Phil> +
Phil> + eliding_frame = next(self.input_iterator)
Phil> + return ElidingFrameWrapper(eliding_frame, [frame])
This works, I think, since it probably isn't possible for gdb to make an
INLINE_FRAME that doesn't have a next frame.
However, this is maybe not a good way to write it, on the theory that
people will cut-and-paste it, and then wind up with incorrect code --
normally such code has to check for the StopIteration case explicitly,
to avoid dropping the outermost frame.
Phil> +@node Managing Frame Filters
Phil> +@subsubsection Management of Frame Filters.
Phil> +@cindex Managing Frame Filters
This node documents user commands, so I think it should go somewhere
else -- somewhere in the "Stack" chapter.
Phil> +global frame-filters:
Phil> + Priority Enabled Name
Phil> + ======== ======= ====
Phil> + 1000 No Capital Primary Function Filter
I forgot to mention it before, but it is odd that this table uses "==="
separators, when other tables printed by gdb do not.
Phil> +This feature is currently (as of @value{GDBN} 7.6) experimental, and
Phil> +may work differently in future versions of @value{GDBN}.
I'd rather we not say this.
Tom
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-12-01 10:59 ` Eli Zaretskii
2012-12-05 19:11 ` Tom Tromey
@ 2012-12-07 14:35 ` Phil Muldoon
2012-12-07 15:04 ` Eli Zaretskii
1 sibling, 1 reply; 17+ messages in thread
From: Phil Muldoon @ 2012-12-07 14:35 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On 12/01/2012 10:58 AM, Eli Zaretskii wrote:
>> Date: Fri, 30 Nov 2012 14:31:59 +0000
>> From: Phil Muldoon <pmuldoon@redhat.com>
>>
>> This patch/email address documentation changes for Python Frame
>> Filters.
>
> Thanks.
>
> What about NEWS?
Oops, will include in next patch.
>
>> +@item backtrace raw
>> +@itemx bt raw
>> +@itemx bt raw @var{n}
>> +@itemx bt raw -@var{n}
>> +@itemx bt raw full
>> +@itemx bt raw full @var{n}
>> +@itemx bt raw full -@var{n}
>> +Do not run Python frame filters on this backtrace. @xref{Frame
>> +Filters API}, for more information. This is only relevant when
>> +@value{GDBN} has been configured with @code{Python} support.
>
> I'd prefer 'no-filters' or some such, since 'raw' has no immediate
> relation to "frame filters".
Yeah this was something I brought up in the first email (0 of 5). I
have no strong preference in naming for the CLI as long as it is not a
pain to type. "no-filters" still seems long too me.
> I suggest to mention here that frame filters can be disabled, with a
> cross-reference to where commands to disable them are described.
> Also, I think we should have a CLI commands to enable and disable them
> all, because otherwise I see no simple way of getting
> backward-compatible behavior.
I did not add a command, because I was trying to be consistent in
functionality with other Python GDB elements. For example, there is
no way to disable pretty printers globally. I have no objection to
adding such a command, though.
>> +@node Frame Filters API
>> +@subsubsection Filtering and Wrapping Frames.
>> +@cindex Frame Filter/Wrappers API
>
> There's a separate node about frame wrapper API, so I think this
> @cindex entry should be modified accordingly. Also, please start
> index entries with a lower-case letter, unless caps are absolutely
> necessary (as in proper names).
Ok, thanks.
>
>> +@value{GDBN}. Frame filters may only work on the wrapping iterator.
>> +This preserves data integrity within @value{GDBN}.
>
> Not sure the reader will understand what you mean by "wrapping
> iterator" here. Perhaps explain or give a cross-reference.
Actually wrapping can go, and we can just let iterator remain, perhaps
"Frame filters may only work on a Python iterator".
>> +takes a Python iterator, and returns a Python iterator. For further
>> +information on frame filters see, @ref{Writing a Frame
>> +Filter/Wrapper}. ^
>
> No need for this comma.
Thanks, and to other straight-forward comments.
>> +@node Frame Wrapper API
>> +@subsubsection Wrapping and Decorating Frames.
>> +@cindex Frame Wrapper API
>
> Index entries should start with a lower-case letter.
>
>> +@defun FrameWrapper.elided ()
>> +
>> +The @code{elided} method groups frames together in a hierarchical
>> +system. An example would be an interpreter call that occurs over many
>> +frames but might be better represented as a group of frames distinct
>> +from the other frames.
>> +
>> +The @code{elide} function must return an iterator that conforms to the
>> +Python iterator protocol.
>
> "elide" or "elided"?
I don't care, but this function returns frames that have been elided
so it made sense to me. The eliding itself is done in the frame
filter or in another Python object.
> "This will avoid printing the frame twice."
>
>> +@defun FrameWrapper.filename ()
>> +
>> +This method returns the filename associated with this frame.
>> +
>> +This method must return a Python string containing the filename, and
>> +optionally, the path to the filename of the frame, or a Python
>> +@code{None}.
>
> So does this method return one string or two, when it uses the
> optional behavior? IOW, it is not clear whether "and" means another
> output or the contents of a single output string.
Badly worded. This should always return the filename and path.
>> +@defun FrameWrapper.frame_args ()
>> +
>> +This method must return an iterator that conforms to the Python
>> +iterator protocol, or a Python @code{None}. This iterator must
>> +contain objects that implement two methods, described here.
>> +
>> +The object must implement an @code{argument} method which takes no
>> +parameters and must return a @code{gdb.Symbol} or a Python string. It
>> +must also implement a @code{value} method which takes no parameters
>> +and which must return a @code{gdb.Value}, a Python value, or
>> +@code{None}. If the @code{value} method returns a Python @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.
>
> I would suggest to describe what are the symbol and the value here.
> Maybe the reader will be able to guess that, but why expect them to
> guess?
I really struggled with this section. As value and symbol are not
simple things to describe in the scope of this function definition. I
guess prefacing with a simple description is fine, but to my mind they
will always be inadequate. I took the notion that if a GDB Python
scriptwriter is writing a frame filter, the concept of symbols and values
should be assumed to be known.
>
>> +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
>
> Is this example (which AFAIU simply shows what the default of None
> will do) really useful? A useful example should show some non-trivial
> use of the facility.
This was another thing I struggled with. In this section I just
wanted to show the make-up of an object that would implement that
interface. The actual printing of symbols and values is much more
complex. There is ready-made code for this in "BaseFrameWrapper",
which I reference elsewhere. But for example, not all values are
printed in GDB backtrace, and the accountancy for these would go well
beyond the scope of a @smallexample.
>> +
>> +Each frame filter object in these dictionaries is passed a single
>> +Python iterator argument and should return a Python iterator. Each
>> +frame filter object must conform to the frame filter interface
>> +definition (@pxref{Frame Filters API}). The iterator returned by the
>> +frame filter must contain only a collection of frame wrappers
>> +(@pxref{Frame Wrapper API}), conforming to the frame wrapper interface
>> +definition.
>> +
>> +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 object files might be in use.
> ^ ^ ^
> I think you need commas in the indicated places, or else this sentence
> can be misinterpreted. Also I'd insert "several" before "object", for
> the same reason. That's assuming I understood correctly what you
> meant in that sentence.
Ok, will recompose.
>> +according to the @code{priority} attribute in each filter. Once the
>> +dictionaries are combined, sorted and pruned, @value{GDBN} then wraps
> ^^^^^^^^^^^^^^^^^
> "pruned and sorted" (as this is the order you just described).
>
>> +all frames in the call-stack with a @code{BaseFrameWrapper} object,
>> +and calls each filter in priority order.
>
> The "priority" part should not be here, since the filters are already
> sorted, right?
Yes, will remove.
>
>> The input to the first frame
>> +filter will be an initial iterator wrapping a collection of
>> +@code{BaseFrameWrapper} objects. The output from the previous filter
>> +will always be the input to the next filter, and so on.
>
> I would suggest to move all the text of this node up to here to the
> "Frame Filters API" section. This text provides an overview of how
> filters and wrappers work, which IMO is sorely missed before you dive
> into describing the API itself. Having this overview there will allow
> the reader to better understand what you describe in the correct
> context. In any case, the text up to here has almost nothing to do
> with "writing a filter/wrapper", which is the subject of this section.
What I tried to do with this functionality is provide four sections
that stood independently. Frame Filters API and Frame Wrappers API
which were API references. These would form a library of the API
calls. But writing a frame filter also implies knowledge of frame
wrappers. And putting this in the Frame Filters API seemed the wrong
place. It's an API section, not a description of how frame filters
and frame wrappers work together. So my opinion was that API sections
should only reference API like content. So I decided to write a
cookbook like section. This section, with suitable cross-references,
brought the knowledge stored in the two API sections and approached
the problem of how to write a Frame Filter and Frame Wrapper with an
example focused approach. I would still like to maintain this
approach if I can. (The last section I have not talked about here is
Managing Frame Filters, with GDB commands).
>> +The first step is attribute creation and assignment:
>> +
>> +@smallexample
>> + self.name = "Foo"
>> + self.priority = 100
>> + self.enabled = True
>> +@end smallexample
>> +
>> +The second step is registering the frame filter with the dictionary or
>> +dictionaries that the frame filter has interest in:
>> +
>> +@smallexample
>> + gdb.frame_filters [self.name] = self
>> +@end smallexample
>
> Instead of repeating the code fragments, how about adding comments to
> the example that explain what its various parts do?
I don't have a strong opinion, but repeating the very small code
fragments seemed more convenient than having to constantly scroll up
and reference the example when reading the describing text.
>> +@item disable frame-filter @var{filter-dictionary} @var{filter-name}
>> +Disable a frame filter in the dictionary matching
>> +@var{filter-dictionary} and @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{filter-name} is the name of the frame filter. A disabled
>> +frame-filter is not deleted, it may be enabled again later.
>
> It would be good to say here how to disable all filters at once. (I
> hope there is such a way.)
From the CLI there is no way, see above comment at beginning of this
email.
>> +(gdb) info frame-filter
>> +
>> +global frame-filters:
>> + Priority Enabled Name
>> + ======== ======= ====
>> + 1000 No Capital Primary Function Filter
>> + 100 Yes Reverse
>> +progspace /build/test frame-filters:
>> + Priority Enabled Name
>> + ======== ======= ====
>> + 100 Yes Progspace Filter 1
>> +objfile /build/test frame-filters:
>> + Priority Enabled Name
>> + ======== ======= ====
>> + 999 Yes Build Test Program Filter
>
> Wouldn't it be better to display all the filters in a single table,
> adding the dictionary as another column?
I made this output to be compatible with "info pretty-printers", which
also outputs them in this way. They use a similar
global/objfile/progspace approach.
>> +(gdb) disable frame-filter ``/build/test'' ``Build Test Program Filter''
>
> Why the quotes? they were never mentioned before. And in any case,
> use ".." here, not ``..'', as the conversion is not done in @example,
> AFAIR.
Quotes are needed so that options with spaces are correctly parsed
(like the name). Quotes are not needed for the first "/build/test"
option. I will remove those. EMACS is quite insistent on placing
those quotes in @smallexample. In the PDF output I think they are
output correctly, but I will fix.
>> +@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.
>
> So in CLI, each filter can be enabled and disabled individually, but
> in MI they can only be enabled en masse, and cannot be disabled? is
> that a good idea?
No. In MI they first have to be enabled globally. This is because
for elided frames we add a "children" field, and that may be
inconsistent with existing front-ends. We have no machine parsing
obligations with the CLI. Each MI command can disable frame filters
individually with the "--no-frame-filters" option when they are
globally set to be "on".
>
>> +This feature is currently (as of @value{GDBN} 7.6) experimental, and
>> +may work differently in future versions of @value{GDBN}.
>
> Do we have to have this version-specific note in the manual? They are
> maintenance burden in the long run.
I added this as the pretty printers feature also has a similar
statement. I can remove it.
Cheers,
Phil
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-12-05 20:49 ` Tom Tromey
@ 2012-12-07 14:55 ` Phil Muldoon
0 siblings, 0 replies; 17+ messages in thread
From: Phil Muldoon @ 2012-12-07 14:55 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 12/05/2012 08:49 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
>
> Phil> +A frame filter works by applying actions to an iterator that is passed
> Phil> +to that frame filter as a parameter. Typically, frame filters utilize
> Phil> +tools such as the Python's @code{itertools} module to modify the
> Phil> +iterator. If the frame filter modifies the iterator, it returns that
> Phil> +modified iterator, otherwise it returns the original iterator
> Phil> +unmodified.
>
> I think this is a somewhat confusing phrasing. The iterator is not
> really modified; instead, the filter takes one iterator as an argument
> and then returns an iterator (possibly even the same one).
I will reword this.
> Phil> +@defvar FrameFilter.name
> Phil> +The @code{name} attribute must be Python string which contains the
> Phil> +name of the filter displayed by @value{GDBN} (@pxref{Managing Frame
> Phil> +Filters}). This attribute may contain any combination of letters,
> Phil> +numbers and spaces. Care should be taken to ensure that it is unique.
> Phil> +This attribute is mandatory.
>
> Do we really want to allow spaces?
> Won't that mess with argument parsing and/or command installation?
>
> If we really want this then there should be tests for it.
> (I didn't go back to see if there were already some...)
As long as the argument (in this case, the name with spaces) is
enclosed in quotes no. There are tests for it, I believe.
> Phil> +@node Frame Wrapper API
> Phil> +@subsubsection Wrapping and Decorating Frames.
> Phil> +@cindex Frame Wrapper API
> Phil> +
> Phil> +Frame wrappers are sister objects to frame filters (@pxref{Frame
> Phil> +Filters API}). Frame wrappers are applied by a frame filter and can
> Phil> +only be used in conjunction with frame filters.
>
> I tend to think we should have a name for the generic object other than
> FrameWrapper.
>
> Also I think maybe we should explain why we didn't just use the exact
> same API as supplied by gdb.Frame.
What do you suggest for a name? A frame wrapper is essentially a
decorator onto top of a gdb.Frame, and gdb.Frames are immutable.
> Phil> +A frame wrapper object works on a single frame, but a frame wrapper
> Phil> +object can be applied to multiple frames.
>
> This reads weirdly.
It just stating that a Frame Wrapper decorates one frame, but one
frame wrapper object can wrap multiple frames. Not sure how to word
it differently. For example, a Frame Wrapper that capitalizes
function names. That is one frame wrapper written in Python. It
works on the frame it is wrapping. But this frame wrapper can be
applied to as many frames as you like, where it will capitalize each
function according to the content of the frame.
> Phil> +@value{GDBN} already contains a frame wrapper called
> Phil> +@code{BaseFrameWrapper}. This contains substantial amounts of
> Phil> +boilerplate code to print the content of frames.
>
> It doesn't really print... and I think this should mention gdb.Frame
> explicitly, rather than "frames".
Ok, will do.
> Phil> +override the methods needed. The Python code for
> Phil> +@code{BaseFrameWrapper} can be found in
> Phil> +@file{@var{data-directory}/python/gdb}
>
> I think if people really need to read the source then we probably should
> write more documentation. Let's just drop this line.
I was thinking in the case of the user overriding BaseFrameWrapper as
we suggest they do. It is something I would consider a handy base
reference. They may want to slightly alter how frame_locals are
printed, but might need a primer on how they are handled in the base
object. Anyway, no strong opinions here. I will remove it if you
feel I should.
> Phil> +@defun FrameWrapper.frame_locals ()
>
> Since frame_args and frame_locals are both very similar, I think their
> text should be shared somehow. E.g., duplicating all the text about the
> returned objects seems difficult to maintain.
Ok, was wondering how to do this across two method declarations in
texinfo. There did not seem a straightforward way.
> Phil> +@smallexample
> Phil> + gdb.frame_filters [self.name] = self
> Phil> +@end smallexample
> Phil> +
> Phil> +As noted earlier, @code{gdb.frame_filters} is a dictionary that is
> Phil> +initialized in the @code{gdb} module when @value{GDBN} starts. In
> Phil> +this example, the frame filter only wishes to register with the
> Phil> +@code{global} dictionary.
>
> It would be good to mention that in general it is preferable not to
> register filters globally. I'm not sure if the value pretty-printing
> text mentions this, but worth a look to see what it says.
Sure.
> Phil> +frame filters should avoid is unwinding the stack if possible. To
> Phil> +search every frame to determine if it is inlined ahead of time may be
> Phil> +too expensive at the filtering step. The frame filter cannot know how
> Phil> +many frames it has to iterate over, and it would have to iterate
> Phil> +through them all. This ends up duplicating effort as @value{GDBN}
> Phil> +performs this iteration when it prints the frames.
>
> While I think we do need to mention that lazy iteration is very
> important, I don't think this is the reason -- gdb only prints the
> frames that the frame iterator supplies it, so there is no double
> iteration. The issue is that eagerly unwinding the stack may just be
> expensive, as stacks can get very deep.
I think there is a case of dual iteration? If you unwind the entire
stack in the frame filter section looking for inlined frames, then
when frame filters have completed, GDB starts printing from the top of
that iterator again.
> Phil> +In this example decision making can be deferred to the printing step.
> Phil> +As each frame is printed, the frame wrapper can examine each frame in
> Phil> +turn when @value{GDBN} iterates. From a performance viewpoint, this
> Phil> +is the most appropriate decision to make. A backtrace from large or
> Phil> +complex programs can constitute many thousands of frames. Also, if
> Phil> +there are many frame filters unwinding the stack during filtering, it
> Phil> +can substantially delay the printing of the backtrace which will
> Phil> +result in large memory usage, and a poor user experience.
>
> I guess you said that too :)
> So part of that previous paragraph can just be zapped, I think.
Sure ;)
> Phil> + def function (self):
> Phil> + frame = self.inferior_frame()
> Phil> + name = str(frame.name())
> Phil> + function = str(frame.function())
> Phil> +
> Phil> + if frame.type() == gdb.INLINE_FRAME:
> Phil> + name = name + " [inlined from "+ function +"]"
>
> Does this really work?
> It seems like it shouldn't.
Yes, name() returns the inlined frame name, while function() returns
the actual frame where the inlined frame was inlined into. We talked
a little about this on IRC, and we may think name() is buggy. But not
sure if we can change that now.
> Phil> + def next(self):
> Phil> + frame = next(self.input_iterator)
> Phil> + if frame.inferior_frame().type() != gdb.INLINE_FRAME:
> Phil> + return frame
> Phil> +
> Phil> + eliding_frame = next(self.input_iterator)
> Phil> + return ElidingFrameWrapper(eliding_frame, [frame])
>
> This works, I think, since it probably isn't possible for gdb to make an
> INLINE_FRAME that doesn't have a next frame.
>
> However, this is maybe not a good way to write it, on the theory that
> people will cut-and-paste it, and then wind up with incorrect code --
> normally such code has to check for the StopIteration case explicitly,
> to avoid dropping the outermost frame.
I really struggled with the examples as writing a short, yet
meaningful example in the scope of a manual is really difficult. I am
taking suggestions/patches for better! ;)
> Phil> +@node Managing Frame Filters
> Phil> +@subsubsection Management of Frame Filters.
> Phil> +@cindex Managing Frame Filters
>
> This node documents user commands, so I think it should go somewhere
> else -- somewhere in the "Stack" chapter.
Sure, in the backtraces section?
> Phil> +global frame-filters:
> Phil> + Priority Enabled Name
> Phil> + ======== ======= ====
> Phil> + 1000 No Capital Primary Function Filter
>
> I forgot to mention it before, but it is odd that this table uses "==="
> separators, when other tables printed by gdb do not.
Ok will remove.
> Phil> +This feature is currently (as of @value{GDBN} 7.6) experimental, and
> Phil> +may work differently in future versions of @value{GDBN}.
As I mentioned to Eli, I included it as the pretty printing Python
support does, and I thought that it might be modus operandi for these
things. I will remove it.
Thanks,
Phil
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-12-07 14:35 ` Phil Muldoon
@ 2012-12-07 15:04 ` Eli Zaretskii
2012-12-07 15:34 ` Phil Muldoon
2012-12-07 15:54 ` Eli Zaretskii
0 siblings, 2 replies; 17+ messages in thread
From: Eli Zaretskii @ 2012-12-07 15:04 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
> Date: Fri, 07 Dec 2012 14:34:50 +0000
> From: Phil Muldoon <pmuldoon@redhat.com>
> CC: gdb-patches@sourceware.org
>
> >> +@defun FrameWrapper.elided ()
> >> +
> >> +The @code{elided} method groups frames together in a hierarchical
> >> +system. An example would be an interpreter call that occurs over many
> >> +frames but might be better represented as a group of frames distinct
> >> +from the other frames.
> >> +
> >> +The @code{elide} function must return an iterator that conforms to the
> >> +Python iterator protocol.
> >
> > "elide" or "elided"?
>
> I don't care, but this function returns frames that have been elided
> so it made sense to me. The eliding itself is done in the frame
> filter or in another Python object.
No, I meant that to say that the method is called "elided", but the
last sentence uses "elide". Does that reference some other function?
> >> +@defun FrameWrapper.frame_args ()
> >> +
> >> +This method must return an iterator that conforms to the Python
> >> +iterator protocol, or a Python @code{None}. This iterator must
> >> +contain objects that implement two methods, described here.
> >> +
> >> +The object must implement an @code{argument} method which takes no
> >> +parameters and must return a @code{gdb.Symbol} or a Python string. It
> >> +must also implement a @code{value} method which takes no parameters
> >> +and which must return a @code{gdb.Value}, a Python value, or
> >> +@code{None}. If the @code{value} method returns a Python @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.
> >
> > I would suggest to describe what are the symbol and the value here.
> > Maybe the reader will be able to guess that, but why expect them to
> > guess?
>
> I really struggled with this section. As value and symbol are not
> simple things to describe in the scope of this function definition.
Isn't the symbol the name of an argument and the value its
corresponding value? If so, just say that.
If that's not what this talks about, then please describe (in a mail
message) what they are as best you can, and I will suggest how to
reword it for the manual, if needed.
> I took the notion that if a GDB Python scriptwriter is writing a
> frame filter, the concept of symbols and values should be assumed to
> be known.
I agree, but that's not what was bugging me when I read that section.
> >> The input to the first frame
> >> +filter will be an initial iterator wrapping a collection of
> >> +@code{BaseFrameWrapper} objects. The output from the previous filter
> >> +will always be the input to the next filter, and so on.
> >
> > I would suggest to move all the text of this node up to here to the
> > "Frame Filters API" section. This text provides an overview of how
> > filters and wrappers work, which IMO is sorely missed before you dive
> > into describing the API itself. Having this overview there will allow
> > the reader to better understand what you describe in the correct
> > context. In any case, the text up to here has almost nothing to do
> > with "writing a filter/wrapper", which is the subject of this section.
>
>
> What I tried to do with this functionality is provide four sections
> that stood independently. Frame Filters API and Frame Wrappers API
> which were API references. These would form a library of the API
> calls. But writing a frame filter also implies knowledge of frame
> wrappers. And putting this in the Frame Filters API seemed the wrong
> place. It's an API section, not a description of how frame filters
> and frame wrappers work together. So my opinion was that API sections
> should only reference API like content. So I decided to write a
> cookbook like section. This section, with suitable cross-references,
> brought the knowledge stored in the two API sections and approached
> the problem of how to write a Frame Filter and Frame Wrapper with an
> example focused approach. I would still like to maintain this
> approach if I can. (The last section I have not talked about here is
> Managing Frame Filters, with GDB commands).
You are IMO assigning too much importance to the names of the
sections. Even if your concerns are justified, renaming the sections
is easy.
What _is_ important is to give the reader a way to become acquainted
with how to write this stuff by presenting the material in the order
that makes it easy to grasp it. For that, the reader must first have
some introduction and overview of the subject, and then the details.
The way you arranged the material, the overview is buried in the
cookbook section, which is not the right place. OTOH, the APIs are
described with only a minimal introduction, which leaves a lot in the
fog. With the overview moved to the beginning, I'm quite sure it will
be easier to understand how the pieces of this puzzle fit together.
If you want to re-iterate some of the overview in the cookbook
section, you can either repeat that or, better, suggest that the
reader re-reads that, and give a cross-reference.
Finally, if you bother about having an overview in a section named
"API", you could have a section that starts with an overview, then
2 subsections, each describing one of the 2 APIs you need to describe,
and finally a 3rd subsection with the cookbook.
The principle is to arrange the text for someone who reads all of
these sections in sequence, when they first learn about these
features. Afterwards, the readers are supposed to get to the stuff
they want via the index and the cross-references, so the arrangement
of text is less important for those readers.
> >> +(gdb) disable frame-filter ``/build/test'' ``Build Test Program Filter''
> >
> > Why the quotes? they were never mentioned before. And in any case,
> > use ".." here, not ``..'', as the conversion is not done in @example,
> > AFAIR.
>
> Quotes are needed so that options with spaces are correctly parsed
> (like the name). Quotes are not needed for the first "/build/test"
> option. I will remove those. EMACS is quite insistent on placing
> those quotes in @smallexample.
You are right about Emacs, but that's an Emacs bug. It doesn't do
that inside @example.
> In the PDF output I think they are output correctly, but I will fix.
They certainly don't look right in the Info output.
> >> +This feature is currently (as of @value{GDBN} 7.6) experimental, and
> >> +may work differently in future versions of @value{GDBN}.
> >
> > Do we have to have this version-specific note in the manual? They are
> > maintenance burden in the long run.
>
> I added this as the pretty printers feature also has a similar
> statement. I can remove it.
Please do remove it, version-specific information in the manual is a
maintenance burden.
Thanks.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-12-07 15:04 ` Eli Zaretskii
@ 2012-12-07 15:34 ` Phil Muldoon
2012-12-07 15:54 ` Eli Zaretskii
1 sibling, 0 replies; 17+ messages in thread
From: Phil Muldoon @ 2012-12-07 15:34 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On 12/07/2012 03:04 PM, Eli Zaretskii wrote:
>> Date: Fri, 07 Dec 2012 14:34:50 +0000
>> From: Phil Muldoon <pmuldoon@redhat.com>
>> CC: gdb-patches@sourceware.org
>>
>>>> +@defun FrameWrapper.elided ()
>>>> +
>>>> +The @code{elided} method groups frames together in a hierarchical
>>>> +system. An example would be an interpreter call that occurs over many
>>>> +frames but might be better represented as a group of frames distinct
>>>> +from the other frames.
>>>> +
>>>> +The @code{elide} function must return an iterator that conforms to the
>>>> +Python iterator protocol.
>>>
>>> "elide" or "elided"?
>>
>> I don't care, but this function returns frames that have been elided
>> so it made sense to me. The eliding itself is done in the frame
>> filter or in another Python object.
>
> No, I meant that to say that the method is called "elided", but the
> last sentence uses "elide". Does that reference some other function?
No it sure doesn't. Typo. I will fix it in the next patch.
>>>> +@defun FrameWrapper.frame_args ()
>>>> +
>>>> +This method must return an iterator that conforms to the Python
>>>> +iterator protocol, or a Python @code{None}. This iterator must
>>>> +contain objects that implement two methods, described here.
>>>> +
>>>> +The object must implement an @code{argument} method which takes no
>>>> +parameters and must return a @code{gdb.Symbol} or a Python string. It
>>>> +must also implement a @code{value} method which takes no parameters
>>>> +and which must return a @code{gdb.Value}, a Python value, or
>>>> +@code{None}. If the @code{value} method returns a Python @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.
>>>
>>> I would suggest to describe what are the symbol and the value here.
>>> Maybe the reader will be able to guess that, but why expect them to
>>> guess?
>>
>> I really struggled with this section. As value and symbol are not
>> simple things to describe in the scope of this function definition.
>
> Isn't the symbol the name of an argument and the value its
> corresponding value? If so, just say that.
Ok, thanks will do.
>>>> The input to the first frame
>>>> +filter will be an initial iterator wrapping a collection of
>>>> +@code{BaseFrameWrapper} objects. The output from the previous filter
>>>> +will always be the input to the next filter, and so on.
>>>
>>> I would suggest to move all the text of this node up to here to the
>>> "Frame Filters API" section. This text provides an overview of how
>>> filters and wrappers work, which IMO is sorely missed before you dive
>>> into describing the API itself. Having this overview there will allow
>>> the reader to better understand what you describe in the correct
>>> context. In any case, the text up to here has almost nothing to do
>>> with "writing a filter/wrapper", which is the subject of this section.
>>
>>
>> What I tried to do with this functionality is provide four sections
>> that stood independently. Frame Filters API and Frame Wrappers API
>> which were API references. These would form a library of the API
>> calls. But writing a frame filter also implies knowledge of frame
>> wrappers. And putting this in the Frame Filters API seemed the wrong
>> place. It's an API section, not a description of how frame filters
>> and frame wrappers work together. So my opinion was that API sections
>> should only reference API like content. So I decided to write a
>> cookbook like section. This section, with suitable cross-references,
>> brought the knowledge stored in the two API sections and approached
>> the problem of how to write a Frame Filter and Frame Wrapper with an
>> example focused approach. I would still like to maintain this
>> approach if I can. (The last section I have not talked about here is
>> Managing Frame Filters, with GDB commands).
>
> You are IMO assigning too much importance to the names of the
> sections. Even if your concerns are justified, renaming the sections
> is easy.
Ah I think I misread your initial request. I did not parse the "up to
here" phrase in conjunction with the position of your in-line comments
with the patch; I thought you wanted to move the whole node into frame
filters node. Thanks for explaining.
>>>> +(gdb) disable frame-filter ``/build/test'' ``Build Test Program Filter''
>>>
>>> Why the quotes? they were never mentioned before. And in any case,
>>> use ".." here, not ``..'', as the conversion is not done in @example,
>>> AFAIR.
>>
>> Quotes are needed so that options with spaces are correctly parsed
>> (like the name). Quotes are not needed for the first "/build/test"
>> option. I will remove those. EMACS is quite insistent on placing
>> those quotes in @smallexample.
>
> You are right about Emacs, but that's an Emacs bug. It doesn't do
> that inside @example.
>
>> In the PDF output I think they are output correctly, but I will fix.
>
> They certainly don't look right in the Info output.
Ok, will fix.
>>>> +This feature is currently (as of @value{GDBN} 7.6) experimental, and
>>>> +may work differently in future versions of @value{GDBN}.
>>>
>>> Do we have to have this version-specific note in the manual? They are
>>> maintenance burden in the long run.
>>
>> I added this as the pretty printers feature also has a similar
>> statement. I can remove it.
>
> Please do remove it, version-specific information in the manual is a
> maintenance burden.
Will do, thanks
Cheers
Phil
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2012-12-07 15:04 ` Eli Zaretskii
2012-12-07 15:34 ` Phil Muldoon
@ 2012-12-07 15:54 ` Eli Zaretskii
1 sibling, 0 replies; 17+ messages in thread
From: Eli Zaretskii @ 2012-12-07 15:54 UTC (permalink / raw)
To: pmuldoon; +Cc: gdb-patches
> Date: Fri, 07 Dec 2012 17:04:08 +0200
> From: Eli Zaretskii <eliz@gnu.org>
> Cc: gdb-patches@sourceware.org
>
> > >> +(gdb) disable frame-filter ``/build/test'' ``Build Test Program Filter''
> > >
> > > Why the quotes? they were never mentioned before. And in any case,
> > > use ".." here, not ``..'', as the conversion is not done in @example,
> > > AFAIR.
> >
> > Quotes are needed so that options with spaces are correctly parsed
> > (like the name). Quotes are not needed for the first "/build/test"
> > option. I will remove those. EMACS is quite insistent on placing
> > those quotes in @smallexample.
>
> You are right about Emacs, but that's an Emacs bug.
Now fixed for the upcoming release 24.3 of Emacs.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2013-04-26 11:54 ` Tom Tromey
@ 2013-05-10 10:52 ` Phil Muldoon
0 siblings, 0 replies; 17+ messages in thread
From: Phil Muldoon @ 2013-05-10 10:52 UTC (permalink / raw)
To: Tom Tromey, eliz; +Cc: gdb-patches
On 25/04/13 21:07, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
>
> Phil> This patch/email address documentation changes for Python Frame
> Phil> Filters.
>
> Phil> Eli signed off on this, Tom has some other changes.
>
> It looks good to me now.
> Thank you.
>
> Tom
>
This patch has now been committed, thanks.
Cheers,
Phil
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.
--
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f685cd2..1869d74 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6465,6 +6465,7 @@ currently executing frame and describes it briefly, similar to the
@menu
* Frames:: Stack frames
* Backtrace:: Backtraces
+* Frame Filter Management:: Managing frame filters
* Selection:: Selecting a frame
* Frame Info:: Information on a frame
@@ -6552,6 +6553,7 @@ line per frame, for many frames, starting with the currently executing
frame (frame zero), followed by its caller (frame one), and on up the
stack.
+@anchor{backtrace-command}
@table @code
@kindex backtrace
@kindex bt @r{(@code{backtrace})}
@@ -6577,6 +6579,19 @@ Similar, but print only the outermost @var{n} frames.
@itemx bt full -@var{n}
Print the values of the local variables also. @var{n} specifies the
number of frames to print, as described above.
+
+@item backtrace no-filters
+@itemx bt no-filters
+@itemx bt no-filters @var{n}
+@itemx bt no-filters -@var{n}
+@itemx bt no-filters full
+@itemx bt no-filters full @var{n}
+@itemx bt no-filters full -@var{n}
+Do not run Python frame filters on this backtrace. @xref{Frame
+Filter API}, for more information. Additionally use @ref{disable
+frame-filter all} to turn off all frame filters. This is only
+relevant when @value{GDBN} has been configured with @code{Python}
+support.
@end table
@kindex where
@@ -6727,6 +6742,149 @@ Display an absolute filename.
Show the current way to display filenames.
@end table
+@node Frame Filter Management
+@section Management of Frame Filters.
+@cindex managing frame filters
+
+Frame filters are Python based utilities to manage and decorate the
+output of frames. @xref{Frame Filter API}, for further information.
+
+Managing frame filters is performed by several commands available
+within @value{GDBN}, detailed here.
+
+@table @code
+@kindex info frame-filter
+@item info frame-filter
+Print a list of installed frame filters from all dictionaries, showing
+their name, priority and enabled status.
+
+@kindex disable frame-filter
+@anchor{disable frame-filter all}
+@item disable frame-filter @var{filter-dictionary} @var{filter-name}
+Disable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. When @code{all} is specified, all frame filters
+across all dictionaries are disabled. @var{filter-name} is the name
+of the frame filter and is used when @code{all} is not the option for
+@var{filter-dictionary}. A disabled frame-filter is not deleted, it
+may be enabled again later.
+
+@kindex enable frame-filter
+@item enable frame-filter @var{filter-dictionary} @var{filter-name}
+Enable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. When @code{all} is specified, all frame filters across
+all dictionaries are enabled. @var{filter-name} is the name of the frame
+filter and is used when @code{all} is not the option for
+@var{filter-dictionary}.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 No PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 Yes BuildProgra Filter
+
+(gdb) disable frame-filter /build/test BuildProgramFilter
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 No PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+
+(gdb) enable frame-filter global PrimaryFunctionFilter
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+@end smallexample
+
+@kindex set frame-filter priority
+@item set frame-filter priority @var{filter-dictionary} @var{filter-name} @var{priority}
+Set the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}. @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. @var{priority} is an integer.
+
+@kindex show frame-filter priority
+@item show frame-filter priority @var{filter-dictionary} @var{filter-name}
+Show the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}. @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+
+(gdb) set frame-filter priority global Reverse 50
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 50 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+@end smallexample
+@end table
+
@node Selection
@section Selecting a Frame
@@ -23026,6 +23184,9 @@ optional arguments while skipping others. Example:
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
* Writing a Pretty-Printer:: Writing a Pretty-Printer.
* Type Printing API:: Pretty-printing types.
+* Frame Filter API:: Filtering Frames.
+* Frame Decorator API:: Decorating Frames.
+* Writing a Frame Filter:: Writing a Frame Filter.
* Inferiors In Python:: Python representation of inferiors (processes)
* Events In Python:: Listening for events from @value{GDBN}.
* Threads In Python:: Accessing inferior threads from Python.
@@ -24405,6 +24566,636 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Frame Filter API
+@subsubsection Filtering Frames.
+@cindex frame filters api
+
+Frame filters are Python objects that manipulate the visibility of a
+frame or frames when a backtrace (@pxref{Backtrace}) is printed by
+@value{GDBN}.
+
+Only commands that print a backtrace, or, in the case of @sc{gdb/mi}
+commands (@pxref{GDB/MI}), those that return a collection of frames
+are affected. The commands that work with frame filters are:
+
+@code{backtrace} (@pxref{backtrace-command,, The backtrace command}),
+@code{-stack-list-frames}
+(@pxref{-stack-list-frames,, The -stack-list-frames command}),
+@code{-stack-list-variables} (@pxref{-stack-list-variables,, The
+-stack-list-variables command}), @code{-stack-list-arguments}
+@pxref{-stack-list-arguments,, The -stack-list-arguments command}) and
+@code{-stack-list-locals} (@pxref{-stack-list-locals,, The
+-stack-list-locals command}).
+
+A frame filter works by taking an iterator as an argument, applying
+actions to the contents of that iterator, and returning another
+iterator (or, possibly, the same iterator it was provided in the case
+where the filter does not perform any operations). Typically, frame
+filters utilize tools such as the Python's @code{itertools} module to
+work with and create new iterators from the source iterator.
+Regardless of how a filter chooses to apply actions, it must not alter
+the underlying @value{GDBN} frame or frames, or attempt to alter the
+call-stack within @value{GDBN}. This preserves data integrity within
+@value{GDBN}. Frame filters are executed on a priority basis and care
+should be taken that some frame filters may have been executed before,
+and that some frame filters will be executed after.
+
+An important consideration when designing frame filters, and well
+worth reflecting upon, is that frame filters should avoid unwinding
+the call stack if possible. Some stacks can run very deep, into the
+tens of thousands in some cases. To search every frame when a frame
+filter executes may be too expensive at that step. The frame filter
+cannot know how many frames it has to iterate over, and it may have to
+iterate through them all. This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames. If
+the filter can defer unwinding frames until frame decorators are
+executed, after the last filter has executed, it should. @xref{Frame
+Decorator API}, for more information on decorators. Also, there are
+examples for both frame decorators and filters in later chapters.
+@xref{Writing a Frame Filter}, for more information.
+
+The Python dictionary @code{gdb.frame_filters} contains key/object
+pairings that comprise a frame filter. Frame filters in this
+dictionary are called @code{global} frame filters, and they are
+available when debugging all inferiors. These frame filters must
+register with the dictionary directly. In addition to the
+@code{global} dictionary, there are other dictionaries that are loaded
+with different inferiors via auto-loading (@pxref{Python
+Auto-loading}). The two other areas where frame filter dictionaries
+can be found are: @code{gdb.Progspace} which contains a
+@code{frame_filters} dictionary attribute, and each @code{gdb.Objfile}
+object which also contains a @code{frame_filters} dictionary
+attribute.
+
+When a command is executed from @value{GDBN} that is compatible with
+frame filters, @value{GDBN} combines the @code{global},
+@code{gdb.Progspace} and all @code{gdb.Objfile} dictionaries currently
+loaded. All of the @code{gdb.Objfile} dictionaries are combined, as
+several frames, and thus several object files, might be in use.
+@value{GDBN} then prunes any frame filter whose @code{enabled}
+attribute is @code{False}. This pruned list is then sorted according
+to the @code{priority} attribute in each filter.
+
+Once the dictionaries are combined, pruned and sorted, @value{GDBN}
+creates an iterator which wraps each frame in the call stack in a
+@code{FrameDecorator} object, and calls each filter in order. The
+output from the previous filter will always be the input to the next
+filter, and so on.
+
+Frame filters have a mandatory interface which each frame filter must
+implement, defined here:
+
+@defun FrameFilter.filter (iterator)
+@value{GDBN} will call this method on a frame filter when it has
+reached the order in the priority list for that filter.
+
+For example, if there are four frame filters:
+
+@smallexample
+Name Priority
+
+Filter1 5
+Filter2 10
+Filter3 100
+Filter4 1
+@end smallexample
+
+The order that the frame filters will be called is:
+
+@smallexample
+Filter3 -> Filter2 -> Filter1 -> Filter4
+@end smallexample
+
+Note that the output from @code{Filter3} is passed to the input of
+@code{Filter2}, and so on.
+
+This @code{filter} method is passed a Python iterator. This iterator
+contains a sequence of frame decorators that wrap each
+@code{gdb.Frame}, or a frame decorator that wraps another frame
+decorator. The first filter that is executed in the sequence of frame
+filters will receive an iterator entirely comprised of default
+@code{FrameDecorator} objects. However, after each frame filter is
+executed, the previous frame filter may have wrapped some or all of
+the frame decorators with their own frame decorator. As frame
+decorators must also conform to a mandatory interface, these
+decorators can be assumed to act in a uniform manner (@pxref{Frame
+Decorator API}).
+
+This method must return an object conforming to the Python iterator
+protocol. Each item in the iterator must be an object conforming to
+the frame decorator interface. If a frame filter does not wish to
+perform any operations on this iterator, it should return that
+iterator untouched.
+
+This method is not optional. If it does not exist, @value{GDBN} will
+raise and print an error.
+@end defun
+
+@defvar FrameFilter.name
+The @code{name} attribute must be Python string which contains the
+name of the filter displayed by @value{GDBN} (@pxref{Frame Filter
+Management}). This attribute may contain any combination of letters
+or numbers. Care should be taken to ensure that it is unique. This
+attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.enabled
+The @code{enabled} attribute must be Python boolean. This attribute
+indicates to @value{GDBN} whether the frame filter is enabled, and
+should be considered when frame filters are executed. If
+@code{enabled} is @code{True}, then the frame filter will be executed
+when any of the backtrace commands detailed earlier in this chapter
+are executed. If @code{enabled} is @code{False}, then the frame
+filter will not be executed. This attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.priority
+The @code{priority} attribute must be Python integer. This attribute
+controls the order of execution in relation to other frame filters.
+There are no imposed limits on the range of @code{priority} other than
+it must be a valid integer. The higher the @code{priority} attribute,
+the sooner the frame filter will be executed in relation to other
+frame filters. Although @code{priority} can be negative, it is
+recommended practice to assume zero is the lowest priority that a
+frame filter can be assigned. Frame filters that have the same
+priority are executed in unsorted order in that priority slot. This
+attribute is mandatory.
+@end defvar
+
+@node Frame Decorator API
+@subsubsection Decorating Frames.
+@cindex frame decorator api
+
+Frame decorators are sister objects to frame filters (@pxref{Frame
+Filter API}). Frame decorators are applied by a frame filter and can
+only be used in conjunction with frame filters.
+
+The purpose of a frame decorator is to customize the printed content
+of each @code{gdb.Frame} in commands where frame filters are executed.
+This concept is called decorating a frame. Frame decorators decorate
+a @code{gdb.Frame} with Python code contained within each API call.
+This separates the actual data contained in a @code{gdb.Frame} from
+the decorated data produced by a frame decorator. This abstraction is
+necessary to maintain integrity of the data contained in each
+@code{gdb.Frame}.
+
+Frame decorators have a mandatory interface, defined below.
+
+@value{GDBN} already contains a frame decorator called
+@code{FrameDecorator}. This contains substantial amounts of
+boilerplate code to decorate the content of a @code{gdb.Frame}. It is
+recommended that other frame decorators inherit and extend this
+object, and only to override the methods needed.
+
+@defun FrameDecorator.elided (self)
+
+The @code{elided} method groups frames together in a hierarchical
+system. An example would be an interpreter, where multiple low-level
+frames make up a single call in the interpreted language. In this
+example, the frame filter would elide the low-level frames and present
+a single high-level frame, representing the call in the interpreted
+language, to the user.
+
+The @code{elided} function must return an iterable and this iterable
+must contain the frames that are being elided wrapped in a suitable
+frame decorator. If no frames are being elided this function may
+return an empty iterable, or @code{None}. Elided frames are indented
+from normal frames in a @code{CLI} backtrace, or in the case of
+@code{GDB/MI}, are placed in the @code{children} field of the eliding
+frame.
+
+It is the frame filter's task to also filter out the elided frames from
+the source iterator. This will avoid printing the frame twice.
+@end defun
+
+@defun FrameDecorator.function (self)
+
+This method returns the name of the function in the frame that is to
+be printed.
+
+This method must return a Python string describing the function, or
+@code{None}.
+
+If this function returns @code{None}, @value{GDBN} will not print any
+data for this field.
+@end defun
+
+@defun FrameDecorator.address (self)
+
+This method returns the address of the frame that is to be printed.
+
+This method must return a Python numeric integer type of sufficient
+size to describe the address of the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.filename (self)
+
+This method returns the filename and path associated with this frame.
+
+This method must return a Python string containing the filename and
+the path to the object file backing the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.line (self):
+
+This method returns the line number associated with the current
+position within the function addressed by this frame.
+
+This method must return a Python integer type, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.frame_args (self)
+@anchor{frame_args}
+
+This method must return an iterable, or @code{None}. Returning an
+empty iterable, or @code{None} means frame arguments will not be
+printed for this frame. This iterable must contain objects that
+implement two methods, described here.
+
+This object must implement a @code{argument} method which takes a
+single @code{self} parameter and must return a @code{gdb.Symbol}
+(@pxref{Symbols In Python}), or a Python string. The object must also
+implement a @code{value} method which takes a single @code{self}
+parameter and must return a @code{gdb.Value} (@pxref{Values From
+Inferior}), a Python value, or @code{None}. If the @code{value}
+method returns @code{None}, and the @code{argument} method returns a
+@code{gdb.Symbol}, @value{GDBN} will look-up and print the value of
+the @code{gdb.Symbol} automatically.
+
+A brief example:
+
+@smallexample
+class SymValueWrapper():
+
+ def __init__(self, symbol, value):
+ self.sym = symbol
+ self.val = value
+
+ def value(self):
+ return self.val
+
+ def symbol(self):
+ return self.sym
+
+class SomeFrameDecorator()
+...
+...
+ def frame_args(self):
+ args = []
+ try:
+ block = self.inferior_frame.block()
+ except:
+ return None
+
+ # Iterate over all symbols in a block. Only add
+ # symbols that are arguments.
+ for sym in block:
+ if not sym.is_argument:
+ continue
+ args.append(SymValueWrapper(sym,None))
+
+ # Add example synthetic argument.
+ args.append(SymValueWrapper(``foo'', 42))
+
+ return args
+@end smallexample
+@end defun
+
+@defun FrameDecorator.frame_locals (self)
+
+This method must return an iterable or @code{None}. Returning an
+empty iterable, or @code{None} means frame local arguments will not be
+printed for this frame.
+
+The object interface, the description of the various strategies for
+reading frame locals, and the example are largely similar to those
+described in the @code{frame_args} function, (@pxref{frame_args,,The
+frame filter frame_args function}). Below is a modified example:
+
+@smallexample
+class SomeFrameDecorator()
+...
+...
+ def frame_locals(self):
+ vars = []
+ try:
+ block = self.inferior_frame.block()
+ except:
+ return None
+
+ # Iterate over all symbols in a block. Add all
+ # symbols, except arguments.
+ for sym in block:
+ if sym.is_argument:
+ continue
+ vars.append(SymValueWrapper(sym,None))
+
+ # Add an example of a synthetic local variable.
+ vars.append(SymValueWrapper(``bar'', 99))
+
+ return vars
+@end smallexample
+@end defun
+
+@defun FrameDecorator.inferior_frame (self):
+
+This method must return the underlying @code{gdb.Frame} that this
+frame decorator is decorating. @value{GDBN} requires the underlying
+frame for internal frame information to determine how to print certain
+values when printing a frame.
+@end defun
+
+@node Writing a Frame Filter
+@subsubsection Writing a Frame Filter
+@cindex writing a frame filter
+
+There are three basic elements that a frame filter must implement: it
+must correctly implement the documented interface (@pxref{Frame Filter
+API}), it must register itself with @value{GDBN}, and finally, it must
+decide if it is to work on the data provided by @value{GDBN}. In all
+cases, whether it works on the iterator or not, each frame filter must
+return an iterator. A bare-bones frame filter follows the pattern in
+the following example.
+
+@smallexample
+import gdb
+
+class FrameFilter():
+
+ def __init__(self):
+ # Frame filter attribute creation.
+ #
+ # 'name' is the name of the filter that GDB will display.
+ #
+ # 'priority' is the priority of the filter relative to other
+ # filters.
+ #
+ # 'enabled' is a boolean that indicates whether this filter is
+ # enabled and should be executed.
+
+ self.name = "Foo"
+ self.priority = 100
+ self.enabled = True
+
+ # Register this frame filter with the global frame_filters
+ # dictionary.
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ # Just return the iterator.
+ return frame_iter
+@end smallexample
+
+The frame filter in the example above implements the three
+requirements for all frame filters. It implements the API, self
+registers, and makes a decision on the iterator (in this case, it just
+returns the iterator untouched).
+
+The first step is attribute creation and assignment, and as shown in
+the comments the filter assigns the following attributes: @code{name},
+@code{priority} and whether the filter should be enabled with the
+@code{enabled} attribute.
+
+The second step is registering the frame filter with the dictionary or
+dictionaries that the frame filter has interest in. As shown in the
+comments, this filter just registers itself with the global dictionary
+@code{gdb.frame_filters}. As noted earlier, @code{gdb.frame_filters}
+is a dictionary that is initialized in the @code{gdb} module when
+@value{GDBN} starts. What dictionary a filter registers with is an
+important consideration. Generally, if a filter is specific to a set
+of code, it should be registered either in the @code{objfile} or
+@code{progspace} dictionaries as they are specific to the program
+currently loaded in @value{GDBN}. The global dictionary is always
+present in @value{GDBN} and is never unloaded. Any filters registered
+with the global dictionary will exist until @value{GDBN} exits. To
+avoid filters that may conflict, it is generally better to register
+frame filters against the dictionaries that more closely align with
+the usage of the filter currently in question. @xref{Python
+Auto-loading}, for further information on auto-loading Python scripts.
+
+@value{GDBN} takes a hands-off approach to frame filter registration,
+therefore it is the frame filter's responsibility to ensure
+registration has occurred, and that any exceptions are handled
+appropriately. In particular, you may wish to handle exceptions
+relating to Python dictionary key uniqueness. It is mandatory that
+the dictionary key is the same as frame filter's @code{name}
+attribute. When a user manages frame filters (@pxref{Frame Filter
+Management}), the names @value{GDBN} will display are those contained
+in the @code{name} attribute.
+
+The final step of this example is the implementation of the
+@code{filter} method. As shown in the example comments, we define the
+@code{filter} method and note that the method must take an iterator,
+and also must return an iterator. In this bare-bones example, the
+frame filter is not very useful as it just returns the iterator
+untouched. However this is a valid operation for frame filters that
+have the @code{enabled} attribute set, but decide not to operate on
+any frames.
+
+In the next example, the frame filter operates on all frames and
+utilizes a frame decorator to perform some work on the frames.
+@xref{Frame Decorator API}, for further information on the frame
+decorator interface.
+
+This example works on inlined frames. It highlights frames which are
+inlined by tagging them with an ``[inlined]'' tag. By applying a
+frame decorator to all frames with the Python @code{itertools imap}
+method, the example defers actions to the frame decorator. Frame
+decorators are only processed when @value{GDBN} prints the backtrace.
+
+This introduces a new decision making topic: whether to perform
+decision making operations at the filtering step, or at the printing
+step. In this example's approach, it does not perform any filtering
+decisions at the filtering step beyond mapping a frame decorator to
+each frame. This allows the actual decision making to be performed
+when each frame is printed. This is an important consideration, and
+well worth reflecting upon when designing a frame filter. An issue
+that frame filters should avoid is unwinding the stack if possible.
+Some stacks can run very deep, into the tens of thousands in some
+cases. To search every frame to determine if it is inlined ahead of
+time may be too expensive at the filtering step. The frame filter
+cannot know how many frames it has to iterate over, and it would have
+to iterate through them all. This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames.
+
+In this example decision making can be deferred to the printing step.
+As each frame is printed, the frame decorator can examine each frame
+in turn when @value{GDBN} iterates. From a performance viewpoint,
+this is the most appropriate decision to make as it avoids duplicating
+the effort that the printing step would undertake anyway. Also, if
+there are many frame filters unwinding the stack during filtering, it
+can substantially delay the printing of the backtrace which will
+result in large memory usage, and a poor user experience.
+
+@smallexample
+class InlineFilter():
+
+ def __init__(self):
+ self.name = "InlinedFrameFilter"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ frame_iter = itertools.imap(InlinedFrameDecorator,
+ frame_iter)
+ return frame_iter
+@end smallexample
+
+This frame filter is somewhat similar to the earlier example, except
+that the @code{filter} method applies a frame decorator object called
+@code{InlinedFrameDecorator} to each element in the iterator. The
+@code{imap} Python method is light-weight. It does not proactively
+iterate over the iterator, but rather creates a new iterator which
+wraps the existing one.
+
+Below is the frame decorator for this example.
+
+@smallexample
+class InlinedFrameDecorator(FrameDecorator):
+
+ def __init__(self, fobj):
+ super(InlinedFrameDecorator, self).__init__(fobj)
+
+ def function(self):
+ frame = fobj.inferior_frame()
+ name = str(frame.name())
+
+ if frame.type() == gdb.INLINE_FRAME:
+ name = name + " [inlined]"
+
+ return name
+@end smallexample
+
+This frame decorator only defines and overrides the @code{function}
+method. It lets the supplied @code{FrameDecorator}, which is shipped
+with @value{GDBN}, perform the other work associated with printing
+this frame.
+
+The combination of these two objects create this output from a
+backtrace:
+
+@smallexample
+#0 0x004004e0 in bar () at inline.c:11
+#1 0x00400566 in max [inlined] (b=6, a=12) at inline.c:21
+#2 0x00400566 in main () at inline.c:31
+@end smallexample
+
+So in the case of this example, a frame decorator is applied to all
+frames, regardless of whether they may be inlined or not. As
+@value{GDBN} iterates over the iterator produced by the frame filters,
+@value{GDBN} executes each frame decorator which then makes a decision
+on what to print in the @code{function} callback. Using a strategy
+like this is a way to defer decisions on the frame content to printing
+time.
+
+@subheading Eliding Frames
+
+It might be that the above example is not desirable for representing
+inlined frames, and a hierarchical approach may be preferred. If we
+want to hierarchically represent frames, the @code{elided} frame
+decorator interface might be preferable.
+
+This example approaches the issue with the @code{elided} method. This
+example is quite long, but very simplistic. It is out-of-scope for
+this section to write a complete example that comprehensively covers
+all approaches of finding and printing inlined frames. However, this
+example illustrates the approach an author might use.
+
+This example comprises of three sections.
+
+@smallexample
+class InlineFrameFilter():
+
+ def __init__(self):
+ self.name = "InlinedFrameFilter"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ return ElidingInlineIterator(frame_iter)
+@end smallexample
+
+This frame filter is very similar to the other examples. The only
+difference is this frame filter is wrapping the iterator provided to
+it (@code{frame_iter}) with a custom iterator called
+@code{ElidingInlineIterator}. This again defers actions to when
+@value{GDBN} prints the backtrace, as the iterator is not traversed
+until printing.
+
+The iterator for this example is as follows. It is in this section of
+the example where decisions are made on the content of the backtrace.
+
+@smallexample
+class ElidingInlineIterator:
+ def __init__(self, ii):
+ self.input_iterator = ii
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ frame = next(self.input_iterator)
+
+ if frame.inferior_frame().type() != gdb.INLINE_FRAME:
+ return frame
+
+ try:
+ eliding_frame = next(self.input_iterator)
+ except StopIteration:
+ return frame
+ return ElidingFrameDecorator(eliding_frame, [frame])
+@end smallexample
+
+This iterator implements the Python iterator protocol. When the
+@code{next} function is called (when @value{GDBN} prints each frame),
+the iterator checks if this frame decorator, @code{frame}, is wrapping
+an inlined frame. If it is not, it returns the existing frame decorator
+untouched. If it is wrapping an inlined frame, it assumes that the
+inlined frame was contained within the next oldest frame,
+@code{eliding_frame}, which it fetches. It then creates and returns a
+frame decorator, @code{ElidingFrameDecorator}, which contains both the
+elided frame, and the eliding frame.
+
+@smallexample
+class ElidingInlineDecorator(FrameDecorator):
+
+ def __init__(self, frame, elided_frames):
+ super(ElidingInlineDecorator, self).__init__(frame)
+ self.frame = frame
+ self.elided_frames = elided_frames
+
+ def elided(self):
+ return iter(self.elided_frames)
+@end smallexample
+
+This frame decorator overrides one function and returns the inlined
+frame in the @code{elided} method. As before it lets
+@code{FrameDecorator} do the rest of the work involved in printing
+this frame. This produces the following output.
+
+@smallexample
+#0 0x004004e0 in bar () at inline.c:11
+#2 0x00400529 in main () at inline.c:25
+ #1 0x00400529 in max (b=6, a=12) at inline.c:15
+@end smallexample
+
+In that output, @code{max} which has been inlined into @code{main} is
+printed hierarchically. Another approach would be to combine the
+@code{function} method, and the @code{elided} method to both print a
+marker in the inlined frame, and also show the hierarchical
+relationship.
+
@node Inferiors In Python
@subsubsection Inferiors In Python
@cindex inferiors in Python
@@ -25235,6 +26026,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Progspace.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
@node Objfiles In Python
@subsubsection Objfiles In Python
@@ -25285,6 +26081,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Objfile.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
A @code{gdb.Objfile} object has the following methods:
@defun Objfile.is_valid ()
@@ -26351,7 +27152,7 @@ No my-foo-pretty-printers.py
When reading an auto-loaded file, @value{GDBN} sets the
@dfn{current objfile}. This is available via the @code{gdb.current_objfile}
function (@pxref{Objfiles In Python}). This can be useful for
-registering objfile-specific pretty-printers.
+registering objfile-specific pretty-printers and frame-filters.
@menu
* objfile-gdb.py file:: The @file{@var{objfile}-gdb.py} file
@@ -30222,6 +31023,22 @@ Is this going away????
@node GDB/MI Stack Manipulation
@section @sc{gdb/mi} Stack Manipulation Commands
+@subheading The @code{-enable-frame-filters} Command
+@findex -enable-frame-filters
+
+@smallexample
+-enable-frame-filters
+@end smallexample
+
+@value{GDBN} allows Python-based frame filters to affect the output of
+the MI commands relating to stack traces. As there is no way to
+implement this in a fully backward-compatible way, a front end must
+request that this functionality be enabled.
+
+Once enabled, this feature cannot be disabled.
+
+Note that if Python support has not been compiled into @value{GDBN},
+this command will still succeed (and do nothing).
@subheading The @code{-stack-info-frame} Command
@findex -stack-info-frame
@@ -30289,13 +31106,14 @@ For a stack with frame levels 0 through 11:
(gdb)
@end smallexample
+@anchor{-stack-list-arguments}
@subheading The @code{-stack-list-arguments} Command
@findex -stack-list-arguments
@subsubheading Synopsis
@smallexample
- -stack-list-arguments @var{print-values}
+ -stack-list-arguments [ --no-frame-filters ] @var{print-values}
[ @var{low-frame} @var{high-frame} ]
@end smallexample
@@ -30312,7 +31130,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
+
Use of this command to obtain arguments in a single frame is
deprecated in favor of the @samp{-stack-list-variables} command.
@@ -30383,13 +31203,14 @@ args=[@{name="intarg",value="2"@},
@c @subheading -stack-list-exception-handlers
+@anchor{-stack-list-frames}
@subheading The @code{-stack-list-frames} Command
@findex -stack-list-frames
@subsubheading Synopsis
@smallexample
- -stack-list-frames [ @var{low-frame} @var{high-frame} ]
+ -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ]
@end smallexample
List the frames currently on the stack. For each frame it displays the
@@ -30419,7 +31240,9 @@ levels are between the two arguments (inclusive). If the two arguments
are equal, it shows the single frame at the corresponding level. It is
an error if @var{low-frame} is larger than the actual number of
frames. On the other hand, @var{high-frame} may be larger than the
-actual number of frames, in which case only existing frames will be returned.
+actual number of frames, in which case only existing frames will be
+returned. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
@subsubheading @value{GDBN} Command
@@ -30489,11 +31312,12 @@ Show a single frame:
@subheading The @code{-stack-list-locals} Command
@findex -stack-list-locals
+@anchor{-stack-list-locals}
@subsubheading Synopsis
@smallexample
- -stack-list-locals @var{print-values}
+ -stack-list-locals [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the local variable names for the selected frame. If
@@ -30504,7 +31328,8 @@ type and value for simple data types, and the name and type for arrays,
structures and unions. In this last case, a frontend can immediately
display the value of simple data types and create variable objects for
other data types when the user wishes to explore their values in
-more detail.
+more detail. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
This command is deprecated in favor of the
@samp{-stack-list-variables} command.
@@ -30529,13 +31354,14 @@ This command is deprecated in favor of the
(gdb)
@end smallexample
+@anchor{-stack-list-variables}
@subheading The @code{-stack-list-variables} Command
@findex -stack-list-variables
@subsubheading Synopsis
@smallexample
- -stack-list-variables @var{print-values}
+ -stack-list-variables [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the names of local variables and function arguments for the selected frame. If
@@ -30543,7 +31369,8 @@ Display the names of local variables and function arguments for the selected fra
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
@subsubheading Example
^ permalink raw reply [flat|nested] 17+ messages in thread
* [patch][python] 5 of 5 - Frame filter documentation changes
@ 2013-05-06 8:24 Phil Muldoon
0 siblings, 0 replies; 17+ messages in thread
From: Phil Muldoon @ 2013-05-06 8:24 UTC (permalink / raw)
To: gdb-patches
This patch/email address documentation changes for Python Frame
Filters.
Eli signed off on this patch.
Tom signed off on this patch..
2013-05-06 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Backtrace): Add "raw" 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.
* NEWS: Add frame filter note.
--
diff --git a/gdb/NEWS b/gdb/NEWS
index 76b48e8..61dcffd 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
*** Changes since GDB 7.6
+* Python scripting
+
+ ** Frame filters and frame decorators have been added.
+
* New commands:
catch rethrow
Like "catch throw", but catches a re-thrown exception.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8bc1213..d2aab1e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6465,6 +6465,7 @@ currently executing frame and describes it briefly, similar to the
@menu
* Frames:: Stack frames
* Backtrace:: Backtraces
+* Frame Filter Management:: Managing frame filters
* Selection:: Selecting a frame
* Frame Info:: Information on a frame
@@ -6552,6 +6553,7 @@ line per frame, for many frames, starting with the currently executing
frame (frame zero), followed by its caller (frame one), and on up the
stack.
+@anchor{backtrace-command}
@table @code
@kindex backtrace
@kindex bt @r{(@code{backtrace})}
@@ -6577,6 +6579,19 @@ Similar, but print only the outermost @var{n} frames.
@itemx bt full -@var{n}
Print the values of the local variables also. @var{n} specifies the
number of frames to print, as described above.
+
+@item backtrace no-filters
+@itemx bt no-filters
+@itemx bt no-filters @var{n}
+@itemx bt no-filters -@var{n}
+@itemx bt no-filters full
+@itemx bt no-filters full @var{n}
+@itemx bt no-filters full -@var{n}
+Do not run Python frame filters on this backtrace. @xref{Frame
+Filter API}, for more information. Additionally use @ref{disable
+frame-filter all} to turn off all frame filters. This is only
+relevant when @value{GDBN} has been configured with @code{Python}
+support.
@end table
@kindex where
@@ -6727,6 +6742,149 @@ Display an absolute filename.
Show the current way to display filenames.
@end table
+@node Frame Filter Management
+@section Management of Frame Filters.
+@cindex managing frame filters
+
+Frame filters are Python based utilities to manage and decorate the
+output of frames. @xref{Frame Filter API}, for further information.
+
+Managing frame filters is performed by several commands available
+within @value{GDBN}, detailed here.
+
+@table @code
+@kindex info frame-filter
+@item info frame-filter
+Print a list of installed frame filters from all dictionaries, showing
+their name, priority and enabled status.
+
+@kindex disable frame-filter
+@anchor{disable frame-filter all}
+@item disable frame-filter @var{filter-dictionary} @var{filter-name}
+Disable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. When @code{all} is specified, all frame filters
+across all dictionaries are disabled. @var{filter-name} is the name
+of the frame filter and is used when @code{all} is not the option for
+@var{filter-dictionary}. A disabled frame-filter is not deleted, it
+may be enabled again later.
+
+@kindex enable frame-filter
+@item enable frame-filter @var{filter-dictionary} @var{filter-name}
+Enable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. When @code{all} is specified, all frame filters across
+all dictionaries are enabled. @var{filter-name} is the name of the frame
+filter and is used when @code{all} is not the option for
+@var{filter-dictionary}.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 No PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 Yes BuildProgra Filter
+
+(gdb) disable frame-filter /build/test BuildProgramFilter
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 No PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+
+(gdb) enable frame-filter global PrimaryFunctionFilter
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+@end smallexample
+
+@kindex set frame-filter priority
+@item set frame-filter priority @var{filter-dictionary} @var{filter-name} @var{priority}
+Set the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}. @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. @var{priority} is an integer.
+
+@kindex show frame-filter priority
+@item show frame-filter priority @var{filter-dictionary} @var{filter-name}
+Show the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}. @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+
+(gdb) set frame-filter priority global Reverse 50
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 50 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+@end smallexample
+@end table
+
@node Selection
@section Selecting a Frame
@@ -23001,6 +23159,9 @@ optional arguments while skipping others. Example:
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
* Writing a Pretty-Printer:: Writing a Pretty-Printer.
* Type Printing API:: Pretty-printing types.
+* Frame Filter API:: Filtering Frames.
+* Frame Decorator API:: Decorating Frames.
+* Writing a Frame Filter:: Writing a Frame Filter.
* Inferiors In Python:: Python representation of inferiors (processes)
* Events In Python:: Listening for events from @value{GDBN}.
* Threads In Python:: Accessing inferior threads from Python.
@@ -24380,6 +24541,636 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Frame Filter API
+@subsubsection Filtering Frames.
+@cindex frame filters api
+
+Frame filters are Python objects that manipulate the visibility of a
+frame or frames when a backtrace (@pxref{Backtrace}) is printed by
+@value{GDBN}.
+
+Only commands that print a backtrace, or, in the case of @sc{gdb/mi}
+commands (@pxref{GDB/MI}), those that return a collection of frames
+are affected. The commands that work with frame filters are:
+
+@code{backtrace} (@pxref{backtrace-command,, The backtrace command}),
+@code{-stack-list-frames}
+(@pxref{-stack-list-frames,, The -stack-list-frames command}),
+@code{-stack-list-variables} (@pxref{-stack-list-variables,, The
+-stack-list-variables command}), @code{-stack-list-arguments}
+@pxref{-stack-list-arguments,, The -stack-list-arguments command}) and
+@code{-stack-list-locals} (@pxref{-stack-list-locals,, The
+-stack-list-locals command}).
+
+A frame filter works by taking an iterator as an argument, applying
+actions to the contents of that iterator, and returning another
+iterator (or, possibly, the same iterator it was provided in the case
+where the filter does not perform any operations). Typically, frame
+filters utilize tools such as the Python's @code{itertools} module to
+work with and create new iterators from the source iterator.
+Regardless of how a filter chooses to apply actions, it must not alter
+the underlying @value{GDBN} frame or frames, or attempt to alter the
+call-stack within @value{GDBN}. This preserves data integrity within
+@value{GDBN}. Frame filters are executed on a priority basis and care
+should be taken that some frame filters may have been executed before,
+and that some frame filters will be executed after.
+
+An important consideration when designing frame filters, and well
+worth reflecting upon, is that frame filters should avoid unwinding
+the call stack if possible. Some stacks can run very deep, into the
+tens of thousands in some cases. To search every frame when a frame
+filter executes may be too expensive at that step. The frame filter
+cannot know how many frames it has to iterate over, and it may have to
+iterate through them all. This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames. If
+the filter can defer unwinding frames until frame decorators are
+executed, after the last filter has executed, it should. @xref{Frame
+Decorator API}, for more information on decorators. Also, there are
+examples for both frame decorators and filters in later chapters.
+@xref{Writing a Frame Filter}, for more information.
+
+The Python dictionary @code{gdb.frame_filters} contains key/object
+pairings that comprise a frame filter. Frame filters in this
+dictionary are called @code{global} frame filters, and they are
+available when debugging all inferiors. These frame filters must
+register with the dictionary directly. In addition to the
+@code{global} dictionary, there are other dictionaries that are loaded
+with different inferiors via auto-loading (@pxref{Python
+Auto-loading}). The two other areas where frame filter dictionaries
+can be found are: @code{gdb.Progspace} which contains a
+@code{frame_filters} dictionary attribute, and each @code{gdb.Objfile}
+object which also contains a @code{frame_filters} dictionary
+attribute.
+
+When a command is executed from @value{GDBN} that is compatible with
+frame filters, @value{GDBN} combines the @code{global},
+@code{gdb.Progspace} and all @code{gdb.Objfile} dictionaries currently
+loaded. All of the @code{gdb.Objfile} dictionaries are combined, as
+several frames, and thus several object files, might be in use.
+@value{GDBN} then prunes any frame filter whose @code{enabled}
+attribute is @code{False}. This pruned list is then sorted according
+to the @code{priority} attribute in each filter.
+
+Once the dictionaries are combined, pruned and sorted, @value{GDBN}
+creates an iterator which wraps each frame in the call stack in a
+@code{FrameDecorator} object, and calls each filter in order. The
+output from the previous filter will always be the input to the next
+filter, and so on.
+
+Frame filters have a mandatory interface which each frame filter must
+implement, defined here:
+
+@defun FrameFilter.filter (iterator)
+@value{GDBN} will call this method on a frame filter when it has
+reached the order in the priority list for that filter.
+
+For example, if there are four frame filters:
+
+@smallexample
+Name Priority
+
+Filter1 5
+Filter2 10
+Filter3 100
+Filter4 1
+@end smallexample
+
+The order that the frame filters will be called is:
+
+@smallexample
+Filter3 -> Filter2 -> Filter1 -> Filter4
+@end smallexample
+
+Note that the output from @code{Filter3} is passed to the input of
+@code{Filter2}, and so on.
+
+This @code{filter} method is passed a Python iterator. This iterator
+contains a sequence of frame decorators that wrap each
+@code{gdb.Frame}, or a frame decorator that wraps another frame
+decorator. The first filter that is executed in the sequence of frame
+filters will receive an iterator entirely comprised of default
+@code{FrameDecorator} objects. However, after each frame filter is
+executed, the previous frame filter may have wrapped some or all of
+the frame decorators with their own frame decorator. As frame
+decorators must also conform to a mandatory interface, these
+decorators can be assumed to act in a uniform manner (@pxref{Frame
+Decorator API}).
+
+This method must return an object conforming to the Python iterator
+protocol. Each item in the iterator must be an object conforming to
+the frame decorator interface. If a frame filter does not wish to
+perform any operations on this iterator, it should return that
+iterator untouched.
+
+This method is not optional. If it does not exist, @value{GDBN} will
+raise and print an error.
+@end defun
+
+@defvar FrameFilter.name
+The @code{name} attribute must be Python string which contains the
+name of the filter displayed by @value{GDBN} (@pxref{Frame Filter
+Management}). This attribute may contain any combination of letters
+or numbers. Care should be taken to ensure that it is unique. This
+attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.enabled
+The @code{enabled} attribute must be Python boolean. This attribute
+indicates to @value{GDBN} whether the frame filter is enabled, and
+should be considered when frame filters are executed. If
+@code{enabled} is @code{True}, then the frame filter will be executed
+when any of the backtrace commands detailed earlier in this chapter
+are executed. If @code{enabled} is @code{False}, then the frame
+filter will not be executed. This attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.priority
+The @code{priority} attribute must be Python integer. This attribute
+controls the order of execution in relation to other frame filters.
+There are no imposed limits on the range of @code{priority} other than
+it must be a valid integer. The higher the @code{priority} attribute,
+the sooner the frame filter will be executed in relation to other
+frame filters. Although @code{priority} can be negative, it is
+recommended practice to assume zero is the lowest priority that a
+frame filter can be assigned. Frame filters that have the same
+priority are executed in unsorted order in that priority slot. This
+attribute is mandatory.
+@end defvar
+
+@node Frame Decorator API
+@subsubsection Decorating Frames.
+@cindex frame decorator api
+
+Frame decorators are sister objects to frame filters (@pxref{Frame
+Filter API}). Frame decorators are applied by a frame filter and can
+only be used in conjunction with frame filters.
+
+The purpose of a frame decorator is to customize the printed content
+of each @code{gdb.Frame} in commands where frame filters are executed.
+This concept is called decorating a frame. Frame decorators decorate
+a @code{gdb.Frame} with Python code contained within each API call.
+This separates the actual data contained in a @code{gdb.Frame} from
+the decorated data produced by a frame decorator. This abstraction is
+necessary to maintain integrity of the data contained in each
+@code{gdb.Frame}.
+
+Frame decorators have a mandatory interface, defined below.
+
+@value{GDBN} already contains a frame decorator called
+@code{FrameDecorator}. This contains substantial amounts of
+boilerplate code to decorate the content of a @code{gdb.Frame}. It is
+recommended that other frame decorators inherit and extend this
+object, and only to override the methods needed.
+
+@defun FrameDecorator.elided (self)
+
+The @code{elided} method groups frames together in a hierarchical
+system. An example would be an interpreter, where multiple low-level
+frames make up a single call in the interpreted language. In this
+example, the frame filter would elide the low-level frames and present
+a single high-level frame, representing the call in the interpreted
+language, to the user.
+
+The @code{elided} function must return an iterable and this iterable
+must contain the frames that are being elided wrapped in a suitable
+frame decorator. If no frames are being elided this function may
+return an empty iterable, or @code{None}. Elided frames are indented
+from normal frames in a @code{CLI} backtrace, or in the case of
+@code{GDB/MI}, are placed in the @code{children} field of the eliding
+frame.
+
+It is the frame filter's task to also filter out the elided frames from
+the source iterator. This will avoid printing the frame twice.
+@end defun
+
+@defun FrameDecorator.function (self)
+
+This method returns the name of the function in the frame that is to
+be printed.
+
+This method must return a Python string describing the function, or
+@code{None}.
+
+If this function returns @code{None}, @value{GDBN} will not print any
+data for this field.
+@end defun
+
+@defun FrameDecorator.address (self)
+
+This method returns the address of the frame that is to be printed.
+
+This method must return a Python numeric integer type of sufficient
+size to describe the address of the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.filename (self)
+
+This method returns the filename and path associated with this frame.
+
+This method must return a Python string containing the filename and
+the path to the object file backing the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.line (self):
+
+This method returns the line number associated with the current
+position within the function addressed by this frame.
+
+This method must return a Python integer type, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.frame_args (self)
+@anchor{frame_args}
+
+This method must return an iterable, or @code{None}. Returning an
+empty iterable, or @code{None} means frame arguments will not be
+printed for this frame. This iterable must contain objects that
+implement two methods, described here.
+
+This object must implement a @code{argument} method which takes a
+single @code{self} parameter and must return a @code{gdb.Symbol}
+(@pxref{Symbols In Python}), or a Python string. The object must also
+implement a @code{value} method which takes a single @code{self}
+parameter and must return a @code{gdb.Value} (@pxref{Values From
+Inferior}), a Python value, or @code{None}. If the @code{value}
+method returns @code{None}, and the @code{argument} method returns a
+@code{gdb.Symbol}, @value{GDBN} will look-up and print the value of
+the @code{gdb.Symbol} automatically.
+
+A brief example:
+
+@smallexample
+class SymValueWrapper():
+
+ def __init__(self, symbol, value):
+ self.sym = symbol
+ self.val = value
+
+ def value(self):
+ return self.val
+
+ def symbol(self):
+ return self.sym
+
+class SomeFrameDecorator()
+...
+...
+ def frame_args(self):
+ args = []
+ try:
+ block = self.inferior_frame.block()
+ except:
+ return None
+
+ # Iterate over all symbols in a block. Only add
+ # symbols that are arguments.
+ for sym in block:
+ if not sym.is_argument:
+ continue
+ args.append(SymValueWrapper(sym,None))
+
+ # Add example synthetic argument.
+ args.append(SymValueWrapper(``foo'', 42))
+
+ return args
+@end smallexample
+@end defun
+
+@defun FrameDecorator.frame_locals (self)
+
+This method must return an iterable or @code{None}. Returning an
+empty iterable, or @code{None} means frame local arguments will not be
+printed for this frame.
+
+The object interface, the description of the various strategies for
+reading frame locals, and the example are largely similar to those
+described in the @code{frame_args} function, (@pxref{frame_args,,The
+frame filter frame_args function}). Below is a modified example:
+
+@smallexample
+class SomeFrameDecorator()
+...
+...
+ def frame_locals(self):
+ vars = []
+ try:
+ block = self.inferior_frame.block()
+ except:
+ return None
+
+ # Iterate over all symbols in a block. Add all
+ # symbols, except arguments.
+ for sym in block:
+ if sym.is_argument:
+ continue
+ vars.append(SymValueWrapper(sym,None))
+
+ # Add an example of a synthetic local variable.
+ vars.append(SymValueWrapper(``bar'', 99))
+
+ return vars
+@end smallexample
+@end defun
+
+@defun FrameDecorator.inferior_frame (self):
+
+This method must return the underlying @code{gdb.Frame} that this
+frame decorator is decorating. @value{GDBN} requires the underlying
+frame for internal frame information to determine how to print certain
+values when printing a frame.
+@end defun
+
+@node Writing a Frame Filter
+@subsubsection Writing a Frame Filter
+@cindex writing a frame filter
+
+There are three basic elements that a frame filter must implement: it
+must correctly implement the documented interface (@pxref{Frame Filter
+API}), it must register itself with @value{GDBN}, and finally, it must
+decide if it is to work on the data provided by @value{GDBN}. In all
+cases, whether it works on the iterator or not, each frame filter must
+return an iterator. A bare-bones frame filter follows the pattern in
+the following example.
+
+@smallexample
+import gdb
+
+class FrameFilter():
+
+ def __init__(self):
+ # Frame filter attribute creation.
+ #
+ # 'name' is the name of the filter that GDB will display.
+ #
+ # 'priority' is the priority of the filter relative to other
+ # filters.
+ #
+ # 'enabled' is a boolean that indicates whether this filter is
+ # enabled and should be executed.
+
+ self.name = "Foo"
+ self.priority = 100
+ self.enabled = True
+
+ # Register this frame filter with the global frame_filters
+ # dictionary.
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ # Just return the iterator.
+ return frame_iter
+@end smallexample
+
+The frame filter in the example above implements the three
+requirements for all frame filters. It implements the API, self
+registers, and makes a decision on the iterator (in this case, it just
+returns the iterator untouched).
+
+The first step is attribute creation and assignment, and as shown in
+the comments the filter assigns the following attributes: @code{name},
+@code{priority} and whether the filter should be enabled with the
+@code{enabled} attribute.
+
+The second step is registering the frame filter with the dictionary or
+dictionaries that the frame filter has interest in. As shown in the
+comments, this filter just registers itself with the global dictionary
+@code{gdb.frame_filters}. As noted earlier, @code{gdb.frame_filters}
+is a dictionary that is initialized in the @code{gdb} module when
+@value{GDBN} starts. What dictionary a filter registers with is an
+important consideration. Generally, if a filter is specific to a set
+of code, it should be registered either in the @code{objfile} or
+@code{progspace} dictionaries as they are specific to the program
+currently loaded in @value{GDBN}. The global dictionary is always
+present in @value{GDBN} and is never unloaded. Any filters registered
+with the global dictionary will exist until @value{GDBN} exits. To
+avoid filters that may conflict, it is generally better to register
+frame filters against the dictionaries that more closely align with
+the usage of the filter currently in question. @xref{Python
+Auto-loading}, for further information on auto-loading Python scripts.
+
+@value{GDBN} takes a hands-off approach to frame filter registration,
+therefore it is the frame filter's responsibility to ensure
+registration has occurred, and that any exceptions are handled
+appropriately. In particular, you may wish to handle exceptions
+relating to Python dictionary key uniqueness. It is mandatory that
+the dictionary key is the same as frame filter's @code{name}
+attribute. When a user manages frame filters (@pxref{Frame Filter
+Management}), the names @value{GDBN} will display are those contained
+in the @code{name} attribute.
+
+The final step of this example is the implementation of the
+@code{filter} method. As shown in the example comments, we define the
+@code{filter} method and note that the method must take an iterator,
+and also must return an iterator. In this bare-bones example, the
+frame filter is not very useful as it just returns the iterator
+untouched. However this is a valid operation for frame filters that
+have the @code{enabled} attribute set, but decide not to operate on
+any frames.
+
+In the next example, the frame filter operates on all frames and
+utilizes a frame decorator to perform some work on the frames.
+@xref{Frame Decorator API}, for further information on the frame
+decorator interface.
+
+This example works on inlined frames. It highlights frames which are
+inlined by tagging them with an ``[inlined]'' tag. By applying a
+frame decorator to all frames with the Python @code{itertools imap}
+method, the example defers actions to the frame decorator. Frame
+decorators are only processed when @value{GDBN} prints the backtrace.
+
+This introduces a new decision making topic: whether to perform
+decision making operations at the filtering step, or at the printing
+step. In this example's approach, it does not perform any filtering
+decisions at the filtering step beyond mapping a frame decorator to
+each frame. This allows the actual decision making to be performed
+when each frame is printed. This is an important consideration, and
+well worth reflecting upon when designing a frame filter. An issue
+that frame filters should avoid is unwinding the stack if possible.
+Some stacks can run very deep, into the tens of thousands in some
+cases. To search every frame to determine if it is inlined ahead of
+time may be too expensive at the filtering step. The frame filter
+cannot know how many frames it has to iterate over, and it would have
+to iterate through them all. This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames.
+
+In this example decision making can be deferred to the printing step.
+As each frame is printed, the frame decorator can examine each frame
+in turn when @value{GDBN} iterates. From a performance viewpoint,
+this is the most appropriate decision to make as it avoids duplicating
+the effort that the printing step would undertake anyway. Also, if
+there are many frame filters unwinding the stack during filtering, it
+can substantially delay the printing of the backtrace which will
+result in large memory usage, and a poor user experience.
+
+@smallexample
+class InlineFilter():
+
+ def __init__(self):
+ self.name = "InlinedFrameFilter"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ frame_iter = itertools.imap(InlinedFrameDecorator,
+ frame_iter)
+ return frame_iter
+@end smallexample
+
+This frame filter is somewhat similar to the earlier example, except
+that the @code{filter} method applies a frame decorator object called
+@code{InlinedFrameDecorator} to each element in the iterator. The
+@code{imap} Python method is light-weight. It does not proactively
+iterate over the iterator, but rather creates a new iterator which
+wraps the existing one.
+
+Below is the frame decorator for this example.
+
+@smallexample
+class InlinedFrameDecorator(FrameDecorator):
+
+ def __init__(self, fobj):
+ super(InlinedFrameDecorator, self).__init__(fobj)
+
+ def function(self):
+ frame = fobj.inferior_frame()
+ name = str(frame.name())
+
+ if frame.type() == gdb.INLINE_FRAME:
+ name = name + " [inlined]"
+
+ return name
+@end smallexample
+
+This frame decorator only defines and overrides the @code{function}
+method. It lets the supplied @code{FrameDecorator}, which is shipped
+with @value{GDBN}, perform the other work associated with printing
+this frame.
+
+The combination of these two objects create this output from a
+backtrace:
+
+@smallexample
+#0 0x004004e0 in bar () at inline.c:11
+#1 0x00400566 in max [inlined] (b=6, a=12) at inline.c:21
+#2 0x00400566 in main () at inline.c:31
+@end smallexample
+
+So in the case of this example, a frame decorator is applied to all
+frames, regardless of whether they may be inlined or not. As
+@value{GDBN} iterates over the iterator produced by the frame filters,
+@value{GDBN} executes each frame decorator which then makes a decision
+on what to print in the @code{function} callback. Using a strategy
+like this is a way to defer decisions on the frame content to printing
+time.
+
+@subheading Eliding Frames
+
+It might be that the above example is not desirable for representing
+inlined frames, and a hierarchical approach may be preferred. If we
+want to hierarchically represent frames, the @code{elided} frame
+decorator interface might be preferable.
+
+This example approaches the issue with the @code{elided} method. This
+example is quite long, but very simplistic. It is out-of-scope for
+this section to write a complete example that comprehensively covers
+all approaches of finding and printing inlined frames. However, this
+example illustrates the approach an author might use.
+
+This example comprises of three sections.
+
+@smallexample
+class InlineFrameFilter():
+
+ def __init__(self):
+ self.name = "InlinedFrameFilter"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ return ElidingInlineIterator(frame_iter)
+@end smallexample
+
+This frame filter is very similar to the other examples. The only
+difference is this frame filter is wrapping the iterator provided to
+it (@code{frame_iter}) with a custom iterator called
+@code{ElidingInlineIterator}. This again defers actions to when
+@value{GDBN} prints the backtrace, as the iterator is not traversed
+until printing.
+
+The iterator for this example is as follows. It is in this section of
+the example where decisions are made on the content of the backtrace.
+
+@smallexample
+class ElidingInlineIterator:
+ def __init__(self, ii):
+ self.input_iterator = ii
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ frame = next(self.input_iterator)
+
+ if frame.inferior_frame().type() != gdb.INLINE_FRAME:
+ return frame
+
+ try:
+ eliding_frame = next(self.input_iterator)
+ except StopIteration:
+ return frame
+ return ElidingFrameDecorator(eliding_frame, [frame])
+@end smallexample
+
+This iterator implements the Python iterator protocol. When the
+@code{next} function is called (when @value{GDBN} prints each frame),
+the iterator checks if this frame decorator, @code{frame}, is wrapping
+an inlined frame. If it is not, it returns the existing frame decorator
+untouched. If it is wrapping an inlined frame, it assumes that the
+inlined frame was contained within the next oldest frame,
+@code{eliding_frame}, which it fetches. It then creates and returns a
+frame decorator, @code{ElidingFrameDecorator}, which contains both the
+elided frame, and the eliding frame.
+
+@smallexample
+class ElidingInlineDecorator(FrameDecorator):
+
+ def __init__(self, frame, elided_frames):
+ super(ElidingInlineDecorator, self).__init__(frame)
+ self.frame = frame
+ self.elided_frames = elided_frames
+
+ def elided(self):
+ return iter(self.elided_frames)
+@end smallexample
+
+This frame decorator overrides one function and returns the inlined
+frame in the @code{elided} method. As before it lets
+@code{FrameDecorator} do the rest of the work involved in printing
+this frame. This produces the following output.
+
+@smallexample
+#0 0x004004e0 in bar () at inline.c:11
+#2 0x00400529 in main () at inline.c:25
+ #1 0x00400529 in max (b=6, a=12) at inline.c:15
+@end smallexample
+
+In that output, @code{max} which has been inlined into @code{main} is
+printed hierarchically. Another approach would be to combine the
+@code{function} method, and the @code{elided} method to both print a
+marker in the inlined frame, and also show the hierarchical
+relationship.
+
@node Inferiors In Python
@subsubsection Inferiors In Python
@cindex inferiors in Python
@@ -25210,6 +26001,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Progspace.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
@node Objfiles In Python
@subsubsection Objfiles In Python
@@ -25260,6 +26056,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Objfile.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
A @code{gdb.Objfile} object has the following methods:
@defun Objfile.is_valid ()
@@ -26326,7 +27127,7 @@ No my-foo-pretty-printers.py
When reading an auto-loaded file, @value{GDBN} sets the
@dfn{current objfile}. This is available via the @code{gdb.current_objfile}
function (@pxref{Objfiles In Python}). This can be useful for
-registering objfile-specific pretty-printers.
+registering objfile-specific pretty-printers and frame-filters.
@menu
* objfile-gdb.py file:: The @file{@var{objfile}-gdb.py} file
@@ -30197,6 +30998,22 @@ Is this going away????
@node GDB/MI Stack Manipulation
@section @sc{gdb/mi} Stack Manipulation Commands
+@subheading The @code{-enable-frame-filters} Command
+@findex -enable-frame-filters
+
+@smallexample
+-enable-frame-filters
+@end smallexample
+
+@value{GDBN} allows Python-based frame filters to affect the output of
+the MI commands relating to stack traces. As there is no way to
+implement this in a fully backward-compatible way, a front end must
+request that this functionality be enabled.
+
+Once enabled, this feature cannot be disabled.
+
+Note that if Python support has not been compiled into @value{GDBN},
+this command will still succeed (and do nothing).
@subheading The @code{-stack-info-frame} Command
@findex -stack-info-frame
@@ -30264,13 +31081,14 @@ For a stack with frame levels 0 through 11:
(gdb)
@end smallexample
+@anchor{-stack-list-arguments}
@subheading The @code{-stack-list-arguments} Command
@findex -stack-list-arguments
@subsubheading Synopsis
@smallexample
- -stack-list-arguments @var{print-values}
+ -stack-list-arguments [ --no-frame-filters ] @var{print-values}
[ @var{low-frame} @var{high-frame} ]
@end smallexample
@@ -30287,7 +31105,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
+
Use of this command to obtain arguments in a single frame is
deprecated in favor of the @samp{-stack-list-variables} command.
@@ -30358,13 +31178,14 @@ args=[@{name="intarg",value="2"@},
@c @subheading -stack-list-exception-handlers
+@anchor{-stack-list-frames}
@subheading The @code{-stack-list-frames} Command
@findex -stack-list-frames
@subsubheading Synopsis
@smallexample
- -stack-list-frames [ @var{low-frame} @var{high-frame} ]
+ -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ]
@end smallexample
List the frames currently on the stack. For each frame it displays the
@@ -30394,7 +31215,9 @@ levels are between the two arguments (inclusive). If the two arguments
are equal, it shows the single frame at the corresponding level. It is
an error if @var{low-frame} is larger than the actual number of
frames. On the other hand, @var{high-frame} may be larger than the
-actual number of frames, in which case only existing frames will be returned.
+actual number of frames, in which case only existing frames will be
+returned. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
@subsubheading @value{GDBN} Command
@@ -30464,11 +31287,12 @@ Show a single frame:
@subheading The @code{-stack-list-locals} Command
@findex -stack-list-locals
+@anchor{-stack-list-locals}
@subsubheading Synopsis
@smallexample
- -stack-list-locals @var{print-values}
+ -stack-list-locals [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the local variable names for the selected frame. If
@@ -30479,7 +31303,8 @@ type and value for simple data types, and the name and type for arrays,
structures and unions. In this last case, a frontend can immediately
display the value of simple data types and create variable objects for
other data types when the user wishes to explore their values in
-more detail.
+more detail. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
This command is deprecated in favor of the
@samp{-stack-list-variables} command.
@@ -30504,13 +31329,14 @@ This command is deprecated in favor of the
(gdb)
@end smallexample
+@anchor{-stack-list-variables}
@subheading The @code{-stack-list-variables} Command
@findex -stack-list-variables
@subsubheading Synopsis
@smallexample
- -stack-list-variables @var{print-values}
+ -stack-list-variables [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the names of local variables and function arguments for the selected frame. If
@@ -30518,7 +31344,8 @@ Display the names of local variables and function arguments for the selected fra
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
@subsubheading Example
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2013-04-22 17:03 Phil Muldoon
@ 2013-04-26 11:54 ` Tom Tromey
2013-05-10 10:52 ` Phil Muldoon
0 siblings, 1 reply; 17+ messages in thread
From: Tom Tromey @ 2013-04-26 11:54 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> This patch/email address documentation changes for Python Frame
Phil> Filters.
Phil> Eli signed off on this, Tom has some other changes.
It looks good to me now.
Thank you.
Tom
^ permalink raw reply [flat|nested] 17+ messages in thread
* [patch][python] 5 of 5 - Frame filter documentation changes
@ 2013-04-22 17:03 Phil Muldoon
2013-04-26 11:54 ` Tom Tromey
0 siblings, 1 reply; 17+ messages in thread
From: Phil Muldoon @ 2013-04-22 17:03 UTC (permalink / raw)
To: gdb-patches
This patch/email address documentation changes for Python Frame
Filters.
Eli signed off on this, Tom has some other changes.
2013-04-22 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Backtrace): Add "raw" 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.
* NEWS: Add frame filter addittion
--
diff --git a/gdb/NEWS b/gdb/NEWS
index 76b48e8..61dcffd 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
*** Changes since GDB 7.6
+* Python scripting
+
+ ** Frame filters and frame decorators have been added.
+
* New commands:
catch rethrow
Like "catch throw", but catches a re-thrown exception.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 47b1188..2c912dc 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6465,6 +6465,7 @@ currently executing frame and describes it briefly, similar to the
@menu
* Frames:: Stack frames
* Backtrace:: Backtraces
+* Frame Filter Management:: Managing frame filters
* Selection:: Selecting a frame
* Frame Info:: Information on a frame
@@ -6552,6 +6553,7 @@ line per frame, for many frames, starting with the currently executing
frame (frame zero), followed by its caller (frame one), and on up the
stack.
+@anchor{backtrace-command}
@table @code
@kindex backtrace
@kindex bt @r{(@code{backtrace})}
@@ -6577,6 +6579,19 @@ Similar, but print only the outermost @var{n} frames.
@itemx bt full -@var{n}
Print the values of the local variables also. @var{n} specifies the
number of frames to print, as described above.
+
+@item backtrace no-filters
+@itemx bt no-filters
+@itemx bt no-filters @var{n}
+@itemx bt no-filters -@var{n}
+@itemx bt no-filters full
+@itemx bt no-filters full @var{n}
+@itemx bt no-filters full -@var{n}
+Do not run Python frame filters on this backtrace. @xref{Frame
+Filter API}, for more information. Additionally use @ref{disable
+frame-filter all} to turn off all frame filters. This is only
+relevant when @value{GDBN} has been configured with @code{Python}
+support.
@end table
@kindex where
@@ -6727,6 +6742,149 @@ Display an absolute filename.
Show the current way to display filenames.
@end table
+@node Frame Filter Management
+@section Management of Frame Filters.
+@cindex managing frame filters
+
+Frame filters are Python based utilities to manage and decorate the
+output of frames. @xref{Frame Filter API}, for further information.
+
+Managing frame filters is performed by several commands available
+within @value{GDBN}, detailed here.
+
+@table @code
+@kindex info frame-filter
+@item info frame-filter
+Print a list of installed frame filters from all dictionaries, showing
+their name, priority and enabled status.
+
+@kindex disable frame-filter
+@anchor{disable frame-filter all}
+@item disable frame-filter @var{filter-dictionary} @var{filter-name}
+Disable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. When @code{all} is specified, all frame filters
+across all dictionaries are disabled. @var{filter-name} is the name
+of the frame filter and is used when @code{all} is not the option for
+@var{filter-dictionary}. A disabled frame-filter is not deleted, it
+may be enabled again later.
+
+@kindex enable frame-filter
+@item enable frame-filter @var{filter-dictionary} @var{filter-name}
+Enable a frame filter in the dictionary matching
+@var{filter-dictionary}, or @code{all}, and @var{filter-name}.
+@var{filter-dictionary} may be @code{all}, @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. When @code{all} is specified, all frame filters across
+all dictionaries are enabled. @var{filter-name} is the name of the frame
+filter and is used when @code{all} is not the option for
+@var{filter-dictionary}.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 No PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 Yes BuildProgra Filter
+
+(gdb) disable frame-filter /build/test BuildProgramFilter
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 No PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+
+(gdb) enable frame-filter global PrimaryFunctionFilter
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+@end smallexample
+
+@kindex set frame-filter priority
+@item set frame-filter priority @var{filter-dictionary} @var{filter-name} @var{priority}
+Set the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}. @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides. @var{priority} is an integer.
+
+@kindex show frame-filter priority
+@item show frame-filter priority @var{filter-dictionary} @var{filter-name}
+Show the @var{priority} of a frame filter in the dictionary matching
+@var{filter-dictionary}, and the frame filter name matching
+@var{filter-name}. @var{filter-dictionary} may be @code{global},
+@code{progspace} or the name of the object file where the frame filter
+dictionary resides.
+
+Example:
+
+@smallexample
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 100 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+
+(gdb) set frame-filter priority global Reverse 50
+(gdb) info frame-filter
+
+global frame-filters:
+ Priority Enabled Name
+ 1000 Yes PrimaryFunctionFilter
+ 50 Yes Reverse
+
+progspace /build/test frame-filters:
+ Priority Enabled Name
+ 100 Yes ProgspaceFilter
+
+objfile /build/test frame-filters:
+ Priority Enabled Name
+ 999 No BuildProgramFilter
+@end smallexample
+@end table
+
@node Selection
@section Selecting a Frame
@@ -23001,6 +23159,9 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* 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.
@@ -24351,6 +24512,636 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Frame Filter API
+@subsubsection Filtering Frames.
+@cindex frame filters api
+
+Frame filters are Python objects that manipulate the visibility of a
+frame or frames when a backtrace (@pxref{Backtrace}) is printed by
+@value{GDBN}.
+
+Only commands that print a backtrace, or, in the case of @sc{gdb/mi}
+commands (@pxref{GDB/MI}), those that return a collection of frames
+are affected. The commands that work with frame filters are:
+
+@code{backtrace} (@pxref{backtrace-command,, The backtrace command}),
+@code{-stack-list-frames}
+(@pxref{-stack-list-frames,, The -stack-list-frames command}),
+@code{-stack-list-variables} (@pxref{-stack-list-variables,, The
+-stack-list-variables command}), @code{-stack-list-arguments}
+@pxref{-stack-list-arguments,, The -stack-list-arguments command}) and
+@code{-stack-list-locals} (@pxref{-stack-list-locals,, The
+-stack-list-locals command}).
+
+A frame filter works by taking an iterator as an argument, applying
+actions to the contents of that iterator, and returning another
+iterator (or, possibly, the same iterator it was provided in the case
+where the filter does not perform any operations). Typically, frame
+filters utilize tools such as the Python's @code{itertools} module to
+work with and create new iterators from the source iterator.
+Regardless of how a filter chooses to apply actions, it must not alter
+the underlying @value{GDBN} frame or frames, or attempt to alter the
+call-stack within @value{GDBN}. This preserves data integrity within
+@value{GDBN}. Frame filters are executed on a priority basis and care
+should be taken that some frame filters may have been executed before,
+and that some frame filters will be executed after.
+
+An important consideration when designing frame filters, and well
+worth reflecting upon, is that frame filters should avoid unwinding
+the call stack if possible. Some stacks can run very deep, into the
+tens of thousands in some cases. To search every frame when a frame
+filter executes may be too expensive at that step. The frame filter
+cannot know how many frames it has to iterate over, and it may have to
+iterate through them all. This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames. If
+the filter can defer unwinding frames until frame decorators are
+executed, after the last filter has executed, it should. @xref{Frame
+Decorator API}, for more information on decorators. Also, there are
+examples for both frame decorators and filters in later chapters.
+@xref{Writing a Frame Filter}, for more information.
+
+The Python dictionary @code{gdb.frame_filters} contains key/object
+pairings that comprise a frame filter. Frame filters in this
+dictionary are called @code{global} frame filters, and they are
+available when debugging all inferiors. These frame filters must
+register with the dictionary directly. In addition to the
+@code{global} dictionary, there are other dictionaries that are loaded
+with different inferiors via auto-loading (@pxref{Python
+Auto-loading}). The two other areas where frame filter dictionaries
+can be found are: @code{gdb.Progspace} which contains a
+@code{frame_filters} dictionary attribute, and each @code{gdb.Objfile}
+object which also contains a @code{frame_filters} dictionary
+attribute.
+
+When a command is executed from @value{GDBN} that is compatible with
+frame filters, @value{GDBN} combines the @code{global},
+@code{gdb.Progspace} and all @code{gdb.Objfile} dictionaries currently
+loaded. All of the @code{gdb.Objfile} dictionaries are combined, as
+several frames, and thus several object files, might be in use.
+@value{GDBN} then prunes any frame filter whose @code{enabled}
+attribute is @code{False}. This pruned list is then sorted according
+to the @code{priority} attribute in each filter.
+
+Once the dictionaries are combined, pruned and sorted, @value{GDBN}
+creates an iterator which wraps each frame in the call stack in a
+@code{FrameDecorator} object, and calls each filter in order. The
+output from the previous filter will always be the input to the next
+filter, and so on.
+
+Frame filters have a mandatory interface which each frame filter must
+implement, defined here:
+
+@defun FrameFilter.filter (iterator)
+@value{GDBN} will call this method on a frame filter when it has
+reached the order in the priority list for that filter.
+
+For example, if there are four frame filters:
+
+@smallexample
+Name Priority
+
+Filter1 5
+Filter2 10
+Filter3 100
+Filter4 1
+@end smallexample
+
+The order that the frame filters will be called is:
+
+@smallexample
+Filter3 -> Filter2 -> Filter1 -> Filter4
+@end smallexample
+
+Note that the output from @code{Filter3} is passed to the input of
+@code{Filter2}, and so on.
+
+This @code{filter} method is passed a Python iterator. This iterator
+contains a sequence of frame decorators that wrap each
+@code{gdb.Frame}, or a frame decorator that wraps another frame
+decorator. The first filter that is executed in the sequence of frame
+filters will receive an iterator entirely comprised of default
+@code{FrameDecorator} objects. However, after each frame filter is
+executed, the previous frame filter may have wrapped some or all of
+the frame decorators with their own frame decorator. As frame
+decorators must also conform to a mandatory interface, these
+decorators can be assumed to act in a uniform manner (@pxref{Frame
+Decorator API}).
+
+This method must return an object conforming to the Python iterator
+protocol. Each item in the iterator must be an object conforming to
+the frame decorator interface. If a frame filter does not wish to
+perform any operations on this iterator, it should return that
+iterator untouched.
+
+This method is not optional. If it does not exist, @value{GDBN} will
+raise and print an error.
+@end defun
+
+@defvar FrameFilter.name
+The @code{name} attribute must be Python string which contains the
+name of the filter displayed by @value{GDBN} (@pxref{Frame Filter
+Management}). This attribute may contain any combination of letters
+or numbers. Care should be taken to ensure that it is unique. This
+attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.enabled
+The @code{enabled} attribute must be Python boolean. This attribute
+indicates to @value{GDBN} whether the frame filter is enabled, and
+should be considered when frame filters are executed. If
+@code{enabled} is @code{True}, then the frame filter will be executed
+when any of the backtrace commands detailed earlier in this chapter
+are executed. If @code{enabled} is @code{False}, then the frame
+filter will not be executed. This attribute is mandatory.
+@end defvar
+
+@defvar FrameFilter.priority
+The @code{priority} attribute must be Python integer. This attribute
+controls the order of execution in relation to other frame filters.
+There are no imposed limits on the range of @code{priority} other than
+it must be a valid integer. The higher the @code{priority} attribute,
+the sooner the frame filter will be executed in relation to other
+frame filters. Although @code{priority} can be negative, it is
+recommended practice to assume zero is the lowest priority that a
+frame filter can be assigned. Frame filters that have the same
+priority are executed in unsorted order in that priority slot. This
+attribute is mandatory.
+@end defvar
+
+@node Frame Decorator API
+@subsubsection Decorating Frames.
+@cindex frame decorator api
+
+Frame decorators are sister objects to frame filters (@pxref{Frame
+Filter API}). Frame decorators are applied by a frame filter and can
+only be used in conjunction with frame filters.
+
+The purpose of a frame decorator is to customize the printed content
+of each @code{gdb.Frame} in commands where frame filters are executed.
+This concept is called decorating a frame. Frame decorators decorate
+a @code{gdb.Frame} with Python code contained within each API call.
+This separates the actual data contained in a @code{gdb.Frame} from
+the decorated data produced by a frame decorator. This abstraction is
+necessary to maintain integrity of the data contained in each
+@code{gdb.Frame}.
+
+Frame decorators have a mandatory interface, defined below.
+
+@value{GDBN} already contains a frame decorator called
+@code{FrameDecorator}. This contains substantial amounts of
+boilerplate code to decorate the content of a @code{gdb.Frame}. It is
+recommended that other frame decorators inherit and extend this
+object, and only to override the methods needed.
+
+@defun FrameDecorator.elided (self)
+
+The @code{elided} method groups frames together in a hierarchical
+system. An example would be an interpreter, where multiple low-level
+frames make up a single call in the interpreted language. In this
+example, the frame filter would elide the low-level frames and present
+a single high-level frame, representing the call in the interpreted
+language, to the user.
+
+The @code{elided} function must return an iterable and this iterable
+must contain the frames that are being elided wrapped in a suitable
+frame decorator. If no frames are being elided this function may
+return an empty iterable, or @code{None}. Elided frames are indented
+from normal frames in a @code{CLI} backtrace, or in the case of
+@code{GDB/MI}, are placed in the @code{children} field of the eliding
+frame.
+
+It is the frame filter's task to also filter out the elided frames from
+the source iterator. This will avoid printing the frame twice.
+@end defun
+
+@defun FrameDecorator.function (self)
+
+This method returns the name of the function in the frame that is to
+be printed.
+
+This method must return a Python string describing the function, or
+@code{None}.
+
+If this function returns @code{None}, @value{GDBN} will not print any
+data for this field.
+@end defun
+
+@defun FrameDecorator.address (self)
+
+This method returns the address of the frame that is to be printed.
+
+This method must return a Python numeric integer type of sufficient
+size to describe the address of the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.filename (self)
+
+This method returns the filename and path associated with this frame.
+
+This method must return a Python string containing the filename and
+the path to the object file backing the frame, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.line (self):
+
+This method returns the line number associated with the current
+position within the function addressed by this frame.
+
+This method must return a Python integer type, or @code{None}.
+
+If this function returns a @code{None}, @value{GDBN} will not print
+any data for this field.
+@end defun
+
+@defun FrameDecorator.frame_args (self)
+@anchor{frame_args}
+
+This method must return an iterable, or @code{None}. Returning an
+empty iterable, or @code{None} means frame arguments will not be
+printed for this frame. This iterable must contain objects that
+implement two methods, described here.
+
+This object must implement a @code{argument} method which takes a
+single @code{self} parameter and must return a @code{gdb.Symbol}
+(@pxref{Symbols In Python}), or a Python string. The object must also
+implement a @code{value} method which takes a single @code{self}
+parameter and must return a @code{gdb.Value} (@pxref{Values From
+Inferior}), a Python value, or @code{None}. If the @code{value}
+method returns @code{None}, and the @code{argument} method returns a
+@code{gdb.Symbol}, @value{GDBN} will look-up and print the value of
+the @code{gdb.Symbol} automatically.
+
+A brief example:
+
+@smallexample
+class SymValueWrapper():
+
+ def __init__(self, symbol, value):
+ self.sym = symbol
+ self.val = value
+
+ def value(self):
+ return self.val
+
+ def symbol(self):
+ return self.sym
+
+class SomeFrameDecorator()
+...
+...
+ def frame_args(self):
+ args = []
+ try:
+ block = self.inferior_frame.block()
+ except:
+ return None
+
+ # Iterate over all symbols in a block. Only add
+ # symbols that are arguments.
+ for sym in block:
+ if not sym.is_argument:
+ continue
+ args.append(SymValueWrapper(sym,None))
+
+ # Add example synthetic argument.
+ args.append(SymValueWrapper(``foo'', 42))
+
+ return args
+@end smallexample
+@end defun
+
+@defun FrameDecorator.frame_locals (self)
+
+This method must return an iterable or @code{None}. Returning an
+empty iterable, or @code{None} means frame local arguments will not be
+printed for this frame.
+
+The object interface, the description of the various strategies for
+reading frame locals, and the example are largely similar to those
+described in the @code{frame_args} function, (@pxref{frame_args,,The
+frame filter frame_args function}). Below is a modified example:
+
+@smallexample
+class SomeFrameDecorator()
+...
+...
+ def frame_locals(self):
+ vars = []
+ try:
+ block = self.inferior_frame.block()
+ except:
+ return None
+
+ # Iterate over all symbols in a block. Add all
+ # symbols, except arguments.
+ for sym in block:
+ if sym.is_argument:
+ continue
+ vars.append(SymValueWrapper(sym,None))
+
+ # Add an example of a synthetic local variable.
+ vars.append(SymValueWrapper(``bar'', 99))
+
+ return vars
+@end smallexample
+@end defun
+
+@defun FrameDecorator.inferior_frame (self):
+
+This method must return the underlying @code{gdb.Frame} that this
+frame decorator is decorating. @value{GDBN} requires the underlying
+frame for internal frame information to determine how to print certain
+values when printing a frame.
+@end defun
+
+@node Writing a Frame Filter
+@subsubsection Writing a Frame Filter
+@cindex writing a frame filter
+
+There are three basic elements that a frame filter must implement: it
+must correctly implement the documented interface (@pxref{Frame Filter
+API}), it must register itself with @value{GDBN}, and finally, it must
+decide if it is to work on the data provided by @value{GDBN}. In all
+cases, whether it works on the iterator or not, each frame filter must
+return an iterator. A bare-bones frame filter follows the pattern in
+the following example.
+
+@smallexample
+import gdb
+
+class FrameFilter():
+
+ def __init__(self):
+ # Frame filter attribute creation.
+ #
+ # 'name' is the name of the filter that GDB will display.
+ #
+ # 'priority' is the priority of the filter relative to other
+ # filters.
+ #
+ # 'enabled' is a boolean that indicates whether this filter is
+ # enabled and should be executed.
+
+ self.name = "Foo"
+ self.priority = 100
+ self.enabled = True
+
+ # Register this frame filter with the global frame_filters
+ # dictionary.
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ # Just return the iterator.
+ return frame_iter
+@end smallexample
+
+The frame filter in the example above implements the three
+requirements for all frame filters. It implements the API, self
+registers, and makes a decision on the iterator (in this case, it just
+returns the iterator untouched).
+
+The first step is attribute creation and assignment, and as shown in
+the comments the filter assigns the following attributes: @code{name},
+@code{priority} and whether the filter should be enabled with the
+@code{enabled} attribute.
+
+The second step is registering the frame filter with the dictionary or
+dictionaries that the frame filter has interest in. As shown in the
+comments, this filter just registers itself with the global dictionary
+@code{gdb.frame_filters}. As noted earlier, @code{gdb.frame_filters}
+is a dictionary that is initialized in the @code{gdb} module when
+@value{GDBN} starts. What dictionary a filter registers with is an
+important consideration. Generally, if a filter is specific to a set
+of code, it should be registered either in the @code{objfile} or
+@code{progspace} dictionaries as they are specific to the program
+currently loaded in @value{GDBN}. The global dictionary is always
+present in @value{GDBN} and is never unloaded. Any filters registered
+with the global dictionary will exist until @value{GDBN} exits. To
+avoid filters that may conflict, it is generally better to register
+frame filters against the dictionaries that more closely align with
+the usage of the filter currently in question. @xref{Python
+Auto-loading}, for further information on auto-loading Python scripts.
+
+@value{GDBN} takes a hands-off approach to frame filter registration,
+therefore it is the frame filter's responsibility to ensure
+registration has occurred, and that any exceptions are handled
+appropriately. In particular, you may wish to handle exceptions
+relating to Python dictionary key uniqueness. It is mandatory that
+the dictionary key is the same as frame filter's @code{name}
+attribute. When a user manages frame filters (@pxref{Frame Filter
+Management}), the names @value{GDBN} will display are those contained
+in the @code{name} attribute.
+
+The final step of this example is the implementation of the
+@code{filter} method. As shown in the example comments, we define the
+@code{filter} method and note that the method must take an iterator,
+and also must return an iterator. In this bare-bones example, the
+frame filter is not very useful as it just returns the iterator
+untouched. However this is a valid operation for frame filters that
+have the @code{enabled} attribute set, but decide not to operate on
+any frames.
+
+In the next example, the frame filter operates on all frames and
+utilizes a frame decorator to perform some work on the frames.
+@xref{Frame Decorator API}, for further information on the frame
+decorator interface.
+
+This example works on inlined frames. It highlights frames which are
+inlined by tagging them with an ``[inlined]'' tag. By applying a
+frame decorator to all frames with the Python @code{itertools imap}
+method, the example defers actions to the frame decorator. Frame
+decorators are only processed when @value{GDBN} prints the backtrace.
+
+This introduces a new decision making topic: whether to perform
+decision making operations at the filtering step, or at the printing
+step. In this example's approach, it does not perform any filtering
+decisions at the filtering step beyond mapping a frame decorator to
+each frame. This allows the actual decision making to be performed
+when each frame is printed. This is an important consideration, and
+well worth reflecting upon when designing a frame filter. An issue
+that frame filters should avoid is unwinding the stack if possible.
+Some stacks can run very deep, into the tens of thousands in some
+cases. To search every frame to determine if it is inlined ahead of
+time may be too expensive at the filtering step. The frame filter
+cannot know how many frames it has to iterate over, and it would have
+to iterate through them all. This ends up duplicating effort as
+@value{GDBN} performs this iteration when it prints the frames.
+
+In this example decision making can be deferred to the printing step.
+As each frame is printed, the frame decorator can examine each frame
+in turn when @value{GDBN} iterates. From a performance viewpoint,
+this is the most appropriate decision to make as it avoids duplicating
+the effort that the printing step would undertake anyway. Also, if
+there are many frame filters unwinding the stack during filtering, it
+can substantially delay the printing of the backtrace which will
+result in large memory usage, and a poor user experience.
+
+@smallexample
+class InlineFilter():
+
+ def __init__(self):
+ self.name = "InlinedFrameFilter"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ frame_iter = itertools.imap(InlinedFrameDecorator,
+ frame_iter)
+ return frame_iter
+@end smallexample
+
+This frame filter is somewhat similar to the earlier example, except
+that the @code{filter} method applies a frame decorator object called
+@code{InlinedFrameDecorator} to each element in the iterator. The
+@code{imap} Python method is light-weight. It does not proactively
+iterate over the iterator, but rather creates a new iterator which
+wraps the existing one.
+
+Below is the frame decorator for this example.
+
+@smallexample
+class InlinedFrameDecorator(FrameDecorator):
+
+ def __init__(self, fobj):
+ super(InlinedFrameDecorator, self).__init__(fobj)
+
+ def function(self):
+ frame = fobj.inferior_frame()
+ name = str(frame.name())
+
+ if frame.type() == gdb.INLINE_FRAME:
+ name = name + " [inlined]"
+
+ return name
+@end smallexample
+
+This frame decorator only defines and overrides the @code{function}
+method. It lets the supplied @code{FrameDecorator}, which is shipped
+with @value{GDBN}, perform the other work associated with printing
+this frame.
+
+The combination of these two objects create this output from a
+backtrace:
+
+@smallexample
+#0 0x004004e0 in bar () at inline.c:11
+#1 0x00400566 in max [inlined] (b=6, a=12) at inline.c:21
+#2 0x00400566 in main () at inline.c:31
+@end smallexample
+
+So in the case of this example, a frame decorator is applied to all
+frames, regardless of whether they may be inlined or not. As
+@value{GDBN} iterates over the iterator produced by the frame filters,
+@value{GDBN} executes each frame decorator which then makes a decision
+on what to print in the @code{function} callback. Using a strategy
+like this is a way to defer decisions on the frame content to printing
+time.
+
+@subheading Eliding Frames
+
+It might be that the above example is not desirable for representing
+inlined frames, and a hierarchical approach may be preferred. If we
+want to hierarchically represent frames, the @code{elided} frame
+decorator interface might be preferable.
+
+This example approaches the issue with the @code{elided} method. This
+example is quite long, but very simplistic. It is out-of-scope for
+this section to write a complete example that comprehensively covers
+all approaches of finding and printing inlined frames. However, this
+example illustrates the approach an author might use.
+
+This example comprises of three sections.
+
+@smallexample
+class InlineFrameFilter():
+
+ def __init__(self):
+ self.name = "InlinedFrameFilter"
+ self.priority = 100
+ self.enabled = True
+ gdb.frame_filters[self.name] = self
+
+ def filter(self, frame_iter):
+ return ElidingInlineIterator(frame_iter)
+@end smallexample
+
+This frame filter is very similar to the other examples. The only
+difference is this frame filter is wrapping the iterator provided to
+it (@code{frame_iter}) with a custom iterator called
+@code{ElidingInlineIterator}. This again defers actions to when
+@value{GDBN} prints the backtrace, as the iterator is not traversed
+until printing.
+
+The iterator for this example is as follows. It is in this section of
+the example where decisions are made on the content of the backtrace.
+
+@smallexample
+class ElidingInlineIterator:
+ def __init__(self, ii):
+ self.input_iterator = ii
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ frame = next(self.input_iterator)
+
+ if frame.inferior_frame().type() != gdb.INLINE_FRAME:
+ return frame
+
+ try:
+ eliding_frame = next(self.input_iterator)
+ except StopIteration:
+ return frame
+ return ElidingFrameDecorator(eliding_frame, [frame])
+@end smallexample
+
+This iterator implements the Python iterator protocol. When the
+@code{next} function is called (when @value{GDBN} prints each frame),
+the iterator checks if this frame decorator, @code{frame}, is wrapping
+an inlined frame. If it is not, it returns the existing frame decorator
+untouched. If it is wrapping an inlined frame, it assumes that the
+inlined frame was contained within the next oldest frame,
+@code{eliding_frame}, which it fetches. It then creates and returns a
+frame decorator, @code{ElidingFrameDecorator}, which contains both the
+elided frame, and the eliding frame.
+
+@smallexample
+class ElidingInlineDecorator(FrameDecorator):
+
+ def __init__(self, frame, elided_frames):
+ super(ElidingInlineDecorator, self).__init__(frame)
+ self.frame = frame
+ self.elided_frames = elided_frames
+
+ def elided(self):
+ return iter(self.elided_frames)
+@end smallexample
+
+This frame decorator overrides one function and returns the inlined
+frame in the @code{elided} method. As before it lets
+@code{FrameDecorator} do the rest of the work involved in printing
+this frame. This produces the following output.
+
+@smallexample
+#0 0x004004e0 in bar () at inline.c:11
+#2 0x00400529 in main () at inline.c:25
+ #1 0x00400529 in max (b=6, a=12) at inline.c:15
+@end smallexample
+
+In that output, @code{max} which has been inlined into @code{main} is
+printed hierarchically. Another approach would be to combine the
+@code{function} method, and the @code{elided} method to both print a
+marker in the inlined frame, and also show the hierarchical
+relationship.
+
@node Inferiors In Python
@subsubsection Inferiors In Python
@cindex inferiors in Python
@@ -25181,6 +25972,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Progspace.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
@node Objfiles In Python
@subsubsection Objfiles In Python
@@ -25231,6 +26027,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Objfile.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
A @code{gdb.Objfile} object has the following methods:
@defun Objfile.is_valid ()
@@ -26249,7 +27050,7 @@ No my-foo-pretty-printers.py
When reading an auto-loaded file, @value{GDBN} sets the
@dfn{current objfile}. This is available via the @code{gdb.current_objfile}
function (@pxref{Objfiles In Python}). This can be useful for
-registering objfile-specific pretty-printers.
+registering objfile-specific pretty-printers and frame-filters.
@menu
* objfile-gdb.py file:: The @file{@var{objfile}-gdb.py} file
@@ -30120,6 +30921,22 @@ Is this going away????
@node GDB/MI Stack Manipulation
@section @sc{gdb/mi} Stack Manipulation Commands
+@subheading The @code{-enable-frame-filters} Command
+@findex -enable-frame-filters
+
+@smallexample
+-enable-frame-filters
+@end smallexample
+
+@value{GDBN} allows Python-based frame filters to affect the output of
+the MI commands relating to stack traces. As there is no way to
+implement this in a fully backward-compatible way, a front end must
+request that this functionality be enabled.
+
+Once enabled, this feature cannot be disabled.
+
+Note that if Python support has not been compiled into @value{GDBN},
+this command will still succeed (and do nothing).
@subheading The @code{-stack-info-frame} Command
@findex -stack-info-frame
@@ -30187,13 +31004,14 @@ For a stack with frame levels 0 through 11:
(gdb)
@end smallexample
+@anchor{-stack-list-arguments}
@subheading The @code{-stack-list-arguments} Command
@findex -stack-list-arguments
@subsubheading Synopsis
@smallexample
- -stack-list-arguments @var{print-values}
+ -stack-list-arguments [ --no-frame-filters ] @var{print-values}
[ @var{low-frame} @var{high-frame} ]
@end smallexample
@@ -30210,7 +31028,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
+
Use of this command to obtain arguments in a single frame is
deprecated in favor of the @samp{-stack-list-variables} command.
@@ -30281,13 +31101,14 @@ args=[@{name="intarg",value="2"@},
@c @subheading -stack-list-exception-handlers
+@anchor{-stack-list-frames}
@subheading The @code{-stack-list-frames} Command
@findex -stack-list-frames
@subsubheading Synopsis
@smallexample
- -stack-list-frames [ @var{low-frame} @var{high-frame} ]
+ -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ]
@end smallexample
List the frames currently on the stack. For each frame it displays the
@@ -30317,7 +31138,9 @@ levels are between the two arguments (inclusive). If the two arguments
are equal, it shows the single frame at the corresponding level. It is
an error if @var{low-frame} is larger than the actual number of
frames. On the other hand, @var{high-frame} may be larger than the
-actual number of frames, in which case only existing frames will be returned.
+actual number of frames, in which case only existing frames will be
+returned. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
@subsubheading @value{GDBN} Command
@@ -30387,11 +31210,12 @@ Show a single frame:
@subheading The @code{-stack-list-locals} Command
@findex -stack-list-locals
+@anchor{-stack-list-locals}
@subsubheading Synopsis
@smallexample
- -stack-list-locals @var{print-values}
+ -stack-list-locals [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the local variable names for the selected frame. If
@@ -30402,7 +31226,8 @@ type and value for simple data types, and the name and type for arrays,
structures and unions. In this last case, a frontend can immediately
display the value of simple data types and create variable objects for
other data types when the user wishes to explore their values in
-more detail.
+more detail. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
This command is deprecated in favor of the
@samp{-stack-list-variables} command.
@@ -30427,13 +31252,14 @@ This command is deprecated in favor of the
(gdb)
@end smallexample
+@anchor{-stack-list-variables}
@subheading The @code{-stack-list-variables} Command
@findex -stack-list-variables
@subsubheading Synopsis
@smallexample
- -stack-list-variables @var{print-values}
+ -stack-list-variables [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the names of local variables and function arguments for the selected frame. If
@@ -30441,7 +31267,8 @@ Display the names of local variables and function arguments for the selected fra
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
@subsubheading Example
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2013-03-22 19:54 ` Tom Tromey
@ 2013-04-19 14:24 ` Phil Muldoon
0 siblings, 0 replies; 17+ messages in thread
From: Phil Muldoon @ 2013-04-19 14:24 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
> FrameDecorator also stores this in an attribute, "base".
> Should that attribute just be declared public?
> And if not, shouldn't it be renamed to start with a "_"?
>
> Phil> + def function(self):
> Phil> + frame = self.inferior_frame()
> Phil> + name = str(frame.name())
> Phil> + function = str(frame.function())
> Phil> +
> Phil> + if frame.type() == gdb.INLINE_FRAME:
> Phil> + name = name + " [inlined from "+ function +"]"
>
> This actually violates the FrameDecorator guidelines, since it bypasses
> self.base to get the name from the inferior_frame.
>
> It seems like it should call self.base.name() instead.
> And, it should have a comment explaining why it needs to use
> inferior_frame to call function.
This frame decorator does not have an inferior_frame API declared, so
in the case of the call to inferior_frame, it calls the super class's
inferior_function API (which in this case, will return self.base).
I'll rename self.base to self._base, in the Frame Decorator. This
example is just deferring the call to the frame decorator that
actually wraps the gdb.Frame.
> Phil> + try:
> Phil> + eliding_frame = next(self.input_iterator)
> Phil> + except StopIteration:
> Phil> + return frame
> Phil> + return ElidingFrameDecorator(eliding_frame, [frame])
>
> What if there are multiple inline frames in a row?
> Wouldn't you want to elide all of them?
> That will make the example trickier though.
Yeah there are several edge cases I did not cover. I did write an
explanatory passage before this example noting that it just covers one
view, and, there are many other cases to deal with. I noted the scope
of a manual example can't encompass these.
Cheers,
Phil
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2013-03-11 22:14 Phil Muldoon
2013-03-12 17:24 ` Eli Zaretskii
@ 2013-03-22 19:54 ` Tom Tromey
2013-04-19 14:24 ` Phil Muldoon
1 sibling, 1 reply; 17+ messages in thread
From: Tom Tromey @ 2013-03-22 19:54 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
Phil> This patch/email address documentation changes for Python Frame
Phil> Filters.
Phil> * Python scripting
Phil> +** Frame filters and frame decorators have been added.
Phil> +
Phil> ** Vectors can be created with gdb.Type.vector.
The addition isn't indented properly.
You'll need to tweak this for trunk anyhow, since you'll be starting a
new "Python scripting" entry for 7.7.
Phil> +@node Frame Filter Management
Phil> +@section Management of Frame Filters.
Phil> +@cindex managing frame filters
Phil> +
Phil> +There are several commands available within @value{GDBN} to manage
Phil> +frame filters, detailed here.
I think this should first describe what a frame filter does, in a way
that ordinary users (not Python-writing users) will understand.
Phil> +@kindex set python frame-filter priority
Phil> +@item set python frame-filter priority @var{filter-dictionary} @var{filter-name} @var{priority}
Seeing this in the manual made me wonder why "python" appears here.
I don't think it matters to users. It seems like it is more to type,
without a corresponding benefit.
What do you think of dropping the "python" sub-command?
Phil> +* Frame Filter API:: Filtering Frames.
Phil> +* Frame Decorator API:: Decorating Frames.
Phil> +* Writing a Frame Filter/Decorator:: Writing a Frame Filter and Decorator.
I think this node would be just as clear, and would look better, if it
were just named "Writing a Frame Filter".
Phil> +@node Frame Filter API
Phil> +@subsubsection Filtering Frames.
Phil> +@cindex frame filters api
Phil> +to the @code{priority} attribute in each filter. Once the
Phil> +dictionaries are combined, pruned and sorted, @value{GDBN} then wraps
Phil> +all frames in the call-stack with a @code{FrameDecorator} object, and
Phil> +calls each filter in order. The input to the first frame filter will
I think it should say:
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.
That is, mention the iterator explicitly. The current text makes it
sound as though the stack is pre-emptively unwound, but that isn't true,
and it is important that developers understand this.
Phil> +@defun FrameDecorator.elided ()
I think these methods should mention 'self'.
That is what we did in the pretty-printing API documentation.
Phil> +The @code{elided} method groups frames together in a hierarchical
Phil> +system. An example would be an interpreter call that occurs over many
Phil> +frames but might be better represented as a group of frames distinct
Phil> +from the other frames.
I think the latter part of this is too vague.
How about -
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.
Phil> +frame decorator. If there are no frames being elided in this frame
Phil> +decorator, this method must return @code{None}.
What happens if it returns an empty iterable?
If that is ok, how about ", this method may return an empty iterable, or
@code{None}"?
Phil> +@defun FrameDecorator.frame_args ()
Phil> +@anchor{frame_args}
Phil> +
Phil> +This method must return an iterable or @code{None}. This iterable
Phil> +must contain objects that implement two methods, described here.
What if it returns an empty iterable instead?
Phil> +The object must implement an @code{argument} method which takes no
Phil> +parameters and must return a @code{gdb.Symbol} (@pxref{Symbols In
Phil> +Python}), or a Python string.
I think it should say "a single @code{self} parameter" rather than "no
parameters". Likewise for the other method.
Phil> +Even if the @code{frame_args} method returns only a single object, it
Phil> +must be iterable.
I would zap this sentence.
Phil> +Even if the @code{frame_locals} method returns only a single object, it
Phil> +must be iterable.
Here too.
Phil> +The second step is registering the frame filter with the dictionary or
Phil> +dictionaries that the frame filter has interest in.
I think this paragraph should xref to "Python Auto-loading" at some
point.
Phil> The global dictionary is always
Phil> +present, and so will the frame filters registered in it.
This reads weirdly.
Phil> +well worth reflecting upon when designing a frame filter. An issue
Phil> +that frame filters should avoid is unwinding the stack if possible.
I think the laziness issue should be pointed out in the main API
documentation somewhere. It's very important; it is fine to repeat it
here, but it should also be somewhere other than in an example.
Phil> +@smallexample
Phil> +class InlinedFrameDecorator(FrameDecorator):
Phil> +
Phil> + def __init__(self, fobj):
Phil> + super(InlinedFrameDecorator, self).__init__(fobj)
Phil> + self.fobj = fobj
self.fobj is never used.
FrameDecorator also stores this in an attribute, "base".
Should that attribute just be declared public?
And if not, shouldn't it be renamed to start with a "_"?
Phil> + def function(self):
Phil> + frame = self.inferior_frame()
Phil> + name = str(frame.name())
Phil> + function = str(frame.function())
Phil> +
Phil> + if frame.type() == gdb.INLINE_FRAME:
Phil> + name = name + " [inlined from "+ function +"]"
This actually violates the FrameDecorator guidelines, since it bypasses
self.base to get the name from the inferior_frame.
It seems like it should call self.base.name() instead.
And, it should have a comment explaining why it needs to use
inferior_frame to call function.
I'm a little surprised that calling the inferior frame's "function" does
what you want here. That seems like a bug TBH.
Phil> +This example approaches the issue with the @code{elided} method. This
Phil> +example is quite long, but very simplistic. It is out-of-scope for
I would say, s/simplistic/simple/.
Phil> + try:
Phil> + eliding_frame = next(self.input_iterator)
Phil> + except StopIteration:
Phil> + return frame
Phil> + return ElidingFrameDecorator(eliding_frame, [frame])
What if there are multiple inline frames in a row?
Wouldn't you want to elide all of them?
That will make the example trickier though.
Tom
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch][python] 5 of 5 - Frame filter documentation changes
2013-03-11 22:14 Phil Muldoon
@ 2013-03-12 17:24 ` Eli Zaretskii
2013-03-22 19:54 ` Tom Tromey
1 sibling, 0 replies; 17+ messages in thread
From: Eli Zaretskii @ 2013-03-12 17:24 UTC (permalink / raw)
To: Phil Muldoon; +Cc: gdb-patches
> Date: Mon, 11 Mar 2013 22:14:20 +0000
> From: Phil Muldoon <pmuldoon@redhat.com>
>
> This patch/email address documentation changes for Python Frame
> Filters.
OK.
Thanks.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [patch][python] 5 of 5 - Frame filter documentation changes
@ 2013-03-11 22:14 Phil Muldoon
2013-03-12 17:24 ` Eli Zaretskii
2013-03-22 19:54 ` Tom Tromey
0 siblings, 2 replies; 17+ messages in thread
From: Phil Muldoon @ 2013-03-11 22:14 UTC (permalink / raw)
To: gdb-patches
This patch/email address documentation changes for Python Frame
Filters.
2013-03-11 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Backtrace): Add "raw" 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/Wrapper): 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.
* NEWS: Add frame filter addittion
--
diff --git a/gdb/NEWS b/gdb/NEWS
index 05fa498..b1595ed 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -39,6 +39,8 @@ x86_64/Cygwin x86_64-*-cygwin*
* Python scripting
+** Frame filters and frame decorators have been added.
+
** Vectors can be created with gdb.Type.vector.
** Python's atexit.register now works in GDB.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5f39d2e..2033bc9 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6291,6 +6291,7 @@ currently executing frame and describes it briefly, similar to the
@menu
* Frames:: Stack frames
* Backtrace:: Backtraces
+* Frame Filter Management:: Managing frame filters
* Selection:: Selecting a frame
* Frame Info:: Information on a frame
@@ -6378,6 +6379,7 @@ line per frame, for many frames, starting with the currently executing
frame (frame zero), followed by its caller (frame one), and on up the
stack.
+@anchor{backtrace-command}
@table @code
@kindex backtrace
@kindex bt @r{(@code{backtrace})}
@@ -6403,6 +6405,19 @@ Similar, but print only the outermost @var{n} frames.
@itemx bt full -@var{n}
Print the values of the local variables also. @var{n} specifies the
number of frames to print, as described above.
+
+@item backtrace no-filters
+@itemx bt no-filters
+@itemx bt no-filters @var{n}
+@itemx bt no-filters -@var{n}
+@itemx bt no-filters full
+@itemx bt no-filters full @var{n}
+@itemx bt no-filters full -@var{n}
+Do not run Python frame filters on this backtrace. @xref{Frame
+Filter API}, for more information. Additionally use @ref{disable
+frame-filter all} to turn off all frame filters. This is only
+relevant when @value{GDBN} has been configured with @code{Python}
+support.
@end table
@kindex where
@@ -6552,6 +6567,146 @@ Display an absolute filename.
Show the current way to display filenames.
@end table
+@node Frame Filter Management
+@section Management of Frame Filters.
+@cindex managing frame filters
+
+There are several commands available within @value{GDBN} to manage
+frame filters, 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 python frame-filter priority
+@item set python 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 python frame-filter priority
+@item show python 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 python 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
@@ -22838,6 +22993,9 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* 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/Decorator:: Writing a Frame Filter and Decorator.
* 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.
@@ -24188,6 +24346,631 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Frame Filter API
+@subsubsection Filtering Frames.
+@cindex frame filters api
+
+Frame filters are Python objects that manipulate the visibility of a
+frame or frames when a backtrace (@pxref{Backtrace}) is printed by
+@value{GDBN}.
+
+Only commands that print a backtrace, or, in the case of @sc{gdb/mi}
+commands (@pxref{GDB/MI}), those that return a collection of frames
+are affected. The commands that work with frame filters are:
+
+@code{backtrace} (@pxref{backtrace-command,, The backtrace command}),
+@code{-stack-list-frames}
+(@pxref{-stack-list-frames,, The -stack-list-frames command}),
+@code{-stack-list-variables} (@pxref{-stack-list-variables,, The
+-stack-list-variables command}), @code{-stack-list-arguments}
+@pxref{-stack-list-arguments,, The -stack-list-arguments command}) and
+@code{-stack-list-locals} (@pxref{-stack-list-locals,, The
+-stack-list-locals command}).
+
+A frame filter works by taking an iterator as an argument, applying
+actions to the contents of that iterator, and returning another
+iterator (or, possibly, the same iterator it was provided in the case
+where the filter does not perform any operations). Typically, frame
+filters utilize tools such as the Python's @code{itertools} module to
+work with and create new iterators from the source iterator.
+Regardless of how a filter chooses to apply actions, it must not alter
+the underlying @value{GDBN} frame or frames, or attempt to alter the
+call-stack within @value{GDBN}. This preserves data integrity within
+@value{GDBN}. Frame filters are executed on a priority basis and care
+should be taken that some frame filters may have been executed before,
+and that some frame filters will be executed after.
+
+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.
+
+Each frame filter object in these dictionaries must conform to the
+frame filter interface definition defined below.
+
+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} then wraps
+all frames in the call-stack with a @code{FrameDecorator} object, and
+calls each filter in order. The input to the first frame filter will
+be an initial iterator wrapping a collection of @code{FrameDecorator}
+objects. 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 with 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 ()
+
+The @code{elided} method groups frames together in a hierarchical
+system. An example would be an interpreter call that occurs over many
+frames but might be better represented as a group of frames distinct
+from the other frames.
+
+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 there are no frames being elided in this frame
+decorator, this method must return @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 ()
+
+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 ()
+
+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 ()
+
+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 ():
+
+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 ()
+@anchor{frame_args}
+
+This method must return an iterable or @code{None}. This iterable
+must contain objects that implement two methods, described here.
+
+The object must implement an @code{argument} method which takes no
+parameters and must return a @code{gdb.Symbol} (@pxref{Symbols In
+Python}), or a Python string. It must also implement a @code{value}
+method which takes no parameters and which 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
+
+Even if the @code{frame_args} method returns only a single object, it
+must be iterable.
+
+If this function returns @code{None}, @value{GDBN} will not print
+arguments for this frame.
+@end defun
+
+@defun FrameDecorator.frame_locals ()
+
+This method must return an iterable, or @code{None}. 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
+
+Even if the @code{frame_locals} method returns only a single object, it
+must be iterable.
+
+If this function returns @code{None}, @value{GDBN} will not print
+locals for this frame.
+@end defun
+
+@defun FrameDecorator.frame ():
+
+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/Decorator
+@subsubsection Writing a Frame Filter and Decorator
+@cindex writing a frame filter/decorator
+
+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, and so will the frame filters registered in it. 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.
+
+@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 adding the function in which they are inlined into. 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)
+ self.fobj = fobj
+
+ def function(self):
+ frame = self.inferior_frame()
+ name = str(frame.name())
+ function = str(frame.function())
+
+ if frame.type() == gdb.INLINE_FRAME:
+ name = name + " [inlined from "+ function +"]"
+
+ 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 from main] (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
@@ -25018,6 +25801,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Progspace.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
@node Objfiles In Python
@subsubsection Objfiles In Python
@@ -25068,6 +25856,11 @@ The @code{type_printers} attribute is a list of type printer objects.
@xref{Type Printing API}, for more information.
@end defvar
+@defvar Objfile.frame_filters
+The @code{frame_filters} attribute is a dictionary of frame filter
+objects. @xref{Frame Filter API}, for more information.
+@end defvar
+
A @code{gdb.Objfile} object has the following methods:
@defun Objfile.is_valid ()
@@ -26086,7 +26879,7 @@ No my-foo-pretty-printers.py
When reading an auto-loaded file, @value{GDBN} sets the
@dfn{current objfile}. This is available via the @code{gdb.current_objfile}
function (@pxref{Objfiles In Python}). This can be useful for
-registering objfile-specific pretty-printers.
+registering objfile-specific pretty-printers and frame-filters.
@menu
* objfile-gdb.py file:: The @file{@var{objfile}-gdb.py} file
@@ -29957,6 +30750,22 @@ Is this going away????
@node GDB/MI Stack Manipulation
@section @sc{gdb/mi} Stack Manipulation Commands
+@subheading The @code{-enable-frame-filters} Command
+@findex -enable-frame-filters
+
+@smallexample
+-enable-frame-filters
+@end smallexample
+
+@value{GDBN} allows Python-based frame filters to affect the output of
+the MI commands relating to stack traces. As there is no way to
+implement this in a fully backward-compatible way, a front end must
+request that this functionality be enabled.
+
+Once enabled, this feature cannot be disabled.
+
+Note that if Python support has not been compiled into @value{GDBN},
+this command will still succeed (and do nothing).
@subheading The @code{-stack-info-frame} Command
@findex -stack-info-frame
@@ -30024,13 +30833,14 @@ For a stack with frame levels 0 through 11:
(gdb)
@end smallexample
+@anchor{-stack-list-arguments}
@subheading The @code{-stack-list-arguments} Command
@findex -stack-list-arguments
@subsubheading Synopsis
@smallexample
- -stack-list-arguments @var{print-values}
+ -stack-list-arguments [ --no-frame-filters ] @var{print-values}
[ @var{low-frame} @var{high-frame} ]
@end smallexample
@@ -30047,7 +30857,9 @@ If @var{print-values} is 0 or @code{--no-values}, print only the names of
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
+
Use of this command to obtain arguments in a single frame is
deprecated in favor of the @samp{-stack-list-variables} command.
@@ -30118,13 +30930,14 @@ args=[@{name="intarg",value="2"@},
@c @subheading -stack-list-exception-handlers
+@anchor{-stack-list-frames}
@subheading The @code{-stack-list-frames} Command
@findex -stack-list-frames
@subsubheading Synopsis
@smallexample
- -stack-list-frames [ @var{low-frame} @var{high-frame} ]
+ -stack-list-frames [ --no-frame-filters @var{low-frame} @var{high-frame} ]
@end smallexample
List the frames currently on the stack. For each frame it displays the
@@ -30154,7 +30967,9 @@ levels are between the two arguments (inclusive). If the two arguments
are equal, it shows the single frame at the corresponding level. It is
an error if @var{low-frame} is larger than the actual number of
frames. On the other hand, @var{high-frame} may be larger than the
-actual number of frames, in which case only existing frames will be returned.
+actual number of frames, in which case only existing frames will be
+returned. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
@subsubheading @value{GDBN} Command
@@ -30224,11 +31039,12 @@ Show a single frame:
@subheading The @code{-stack-list-locals} Command
@findex -stack-list-locals
+@anchor{-stack-list-locals}
@subsubheading Synopsis
@smallexample
- -stack-list-locals @var{print-values}
+ -stack-list-locals [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the local variable names for the selected frame. If
@@ -30239,7 +31055,8 @@ type and value for simple data types, and the name and type for arrays,
structures and unions. In this last case, a frontend can immediately
display the value of simple data types and create variable objects for
other data types when the user wishes to explore their values in
-more detail.
+more detail. If the option @code{--no-frame-filters} is supplied, then
+Python frame filters will not be executed.
This command is deprecated in favor of the
@samp{-stack-list-variables} command.
@@ -30264,13 +31081,14 @@ This command is deprecated in favor of the
(gdb)
@end smallexample
+@anchor{-stack-list-variables}
@subheading The @code{-stack-list-variables} Command
@findex -stack-list-variables
@subsubheading Synopsis
@smallexample
- -stack-list-variables @var{print-values}
+ -stack-list-variables [ --no-frame-filters ] @var{print-values}
@end smallexample
Display the names of local variables and function arguments for the selected frame. If
@@ -30278,7 +31096,8 @@ Display the names of local variables and function arguments for the selected fra
the variables; if it is 1 or @code{--all-values}, print also their
values; and if it is 2 or @code{--simple-values}, print the name,
type and value for simple data types, and the name and type for arrays,
-structures and unions.
+structures and unions. If the option @code{--no-frame-filters} is
+supplied, then Python frame filters will not be executed.
@subsubheading Example
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2013-05-10 10:52 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-30 14:32 [patch][python] 5 of 5 - Frame filter documentation changes Phil Muldoon
2012-12-01 10:59 ` Eli Zaretskii
2012-12-05 19:11 ` Tom Tromey
2012-12-07 14:35 ` Phil Muldoon
2012-12-07 15:04 ` Eli Zaretskii
2012-12-07 15:34 ` Phil Muldoon
2012-12-07 15:54 ` Eli Zaretskii
2012-12-05 20:49 ` Tom Tromey
2012-12-07 14:55 ` Phil Muldoon
2013-03-11 22:14 Phil Muldoon
2013-03-12 17:24 ` Eli Zaretskii
2013-03-22 19:54 ` Tom Tromey
2013-04-19 14:24 ` Phil Muldoon
2013-04-22 17:03 Phil Muldoon
2013-04-26 11:54 ` Tom Tromey
2013-05-10 10:52 ` Phil Muldoon
2013-05-06 8:24 Phil Muldoon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox