<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
<html>
|
<!-- Copyright (C) 1988-2021 Free Software Foundation, Inc.
|
|
Permission is granted to copy, distribute and/or modify this document
|
under the terms of the GNU Free Documentation License, Version 1.3 or
|
any later version published by the Free Software Foundation; with the
|
Invariant Sections being "Free Software" and "Free Software Needs
|
Free Documentation", with the Front-Cover Texts being "A GNU Manual,"
|
and with the Back-Cover Texts as in (a) below.
|
|
(a) The FSF's Back-Cover Text is: "You are free to copy and modify
|
this GNU Manual. Buying copies from GNU Press supports the FSF in
|
developing GNU and promoting software freedom." -->
|
<!-- Created by GNU Texinfo 5.1, http://www.gnu.org/software/texinfo/ -->
|
<head>
|
<title>Debugging with GDB: Writing a Frame Filter</title>
|
|
<meta name="description" content="Debugging with GDB: Writing a Frame Filter">
|
<meta name="keywords" content="Debugging with GDB: Writing a Frame Filter">
|
<meta name="resource-type" content="document">
|
<meta name="distribution" content="global">
|
<meta name="Generator" content="makeinfo">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<link href="index.html#Top" rel="start" title="Top">
|
<link href="Concept-Index.html#Concept-Index" rel="index" title="Concept Index">
|
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
|
<link href="Python-API.html#Python-API" rel="up" title="Python API">
|
<link href="Unwinding-Frames-in-Python.html#Unwinding-Frames-in-Python" rel="next" title="Unwinding Frames in Python">
|
<link href="Frame-Decorator-API.html#Frame-Decorator-API" rel="previous" title="Frame Decorator API">
|
<style type="text/css">
|
<!--
|
a.summary-letter {text-decoration: none}
|
blockquote.smallquotation {font-size: smaller}
|
div.display {margin-left: 3.2em}
|
div.example {margin-left: 3.2em}
|
div.indentedblock {margin-left: 3.2em}
|
div.lisp {margin-left: 3.2em}
|
div.smalldisplay {margin-left: 3.2em}
|
div.smallexample {margin-left: 3.2em}
|
div.smallindentedblock {margin-left: 3.2em; font-size: smaller}
|
div.smalllisp {margin-left: 3.2em}
|
kbd {font-style:oblique}
|
pre.display {font-family: inherit}
|
pre.format {font-family: inherit}
|
pre.menu-comment {font-family: serif}
|
pre.menu-preformatted {font-family: serif}
|
pre.smalldisplay {font-family: inherit; font-size: smaller}
|
pre.smallexample {font-size: smaller}
|
pre.smallformat {font-family: inherit; font-size: smaller}
|
pre.smalllisp {font-size: smaller}
|
span.nocodebreak {white-space:nowrap}
|
span.nolinebreak {white-space:nowrap}
|
span.roman {font-family:serif; font-weight:normal}
|
span.sansserif {font-family:sans-serif; font-weight:normal}
|
ul.no-bullet {list-style: none}
|
-->
|
</style>
|
|
|
</head>
|
|
<body lang="en" bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#800080" alink="#FF0000">
|
<a name="Writing-a-Frame-Filter"></a>
|
<div class="header">
|
<p>
|
Next: <a href="Unwinding-Frames-in-Python.html#Unwinding-Frames-in-Python" accesskey="n" rel="next">Unwinding Frames in Python</a>, Previous: <a href="Frame-Decorator-API.html#Frame-Decorator-API" accesskey="p" rel="previous">Frame Decorator API</a>, Up: <a href="Python-API.html#Python-API" accesskey="u" rel="up">Python API</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html#Concept-Index" title="Index" rel="index">Index</a>]</p>
|
</div>
|
<hr>
|
<a name="Writing-a-Frame-Filter-1"></a>
|
<h4 class="subsubsection">23.2.2.11 Writing a Frame Filter</h4>
|
<a name="index-writing-a-frame-filter"></a>
|
|
<p>There are three basic elements that a frame filter must implement: it
|
must correctly implement the documented interface (see <a href="Frame-Filter-API.html#Frame-Filter-API">Frame Filter API</a>), it must register itself with <small>GDB</small>, and finally, it must
|
decide if it is to work on the data provided by <small>GDB</small>. 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.
|
</p>
|
<div class="smallexample">
|
<pre class="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
|
</pre></div>
|
|
<p>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).
|
</p>
|
<p>The first step is attribute creation and assignment, and as shown in
|
the comments the filter assigns the following attributes: <code>name</code>,
|
<code>priority</code> and whether the filter should be enabled with the
|
<code>enabled</code> attribute.
|
</p>
|
<p>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</code>. As noted earlier, <code>gdb.frame_filters</code>
|
is a dictionary that is initialized in the <code>gdb</code> module when
|
<small>GDB</small> 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</code> or
|
<code>progspace</code> dictionaries as they are specific to the program
|
currently loaded in <small>GDB</small>. The global dictionary is always
|
present in <small>GDB</small> and is never unloaded. Any filters registered
|
with the global dictionary will exist until <small>GDB</small> 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. See <a href="Python-Auto_002dloading.html#Python-Auto_002dloading">Python Auto-loading</a>, for further information on auto-loading Python scripts.
|
</p>
|
<p><small>GDB</small> 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</code>
|
attribute. When a user manages frame filters (see <a href="Frame-Filter-Management.html#Frame-Filter-Management">Frame Filter Management</a>), the names <small>GDB</small> will display are those contained
|
in the <code>name</code> attribute.
|
</p>
|
<p>The final step of this example is the implementation of the
|
<code>filter</code> method. As shown in the example comments, we define the
|
<code>filter</code> 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</code> attribute set, but decide not to operate on
|
any frames.
|
</p>
|
<p>In the next example, the frame filter operates on all frames and
|
utilizes a frame decorator to perform some work on the frames.
|
See <a href="Frame-Decorator-API.html#Frame-Decorator-API">Frame Decorator API</a>, for further information on the frame
|
decorator interface.
|
</p>
|
<p>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</code>
|
method, the example defers actions to the frame decorator. Frame
|
decorators are only processed when <small>GDB</small> prints the backtrace.
|
</p>
|
<p>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
|
<small>GDB</small> performs this iteration when it prints the frames.
|
</p>
|
<p>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 <small>GDB</small> 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.
|
</p>
|
<div class="smallexample">
|
<pre class="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
|
</pre></div>
|
|
<p>This frame filter is somewhat similar to the earlier example, except
|
that the <code>filter</code> method applies a frame decorator object called
|
<code>InlinedFrameDecorator</code> to each element in the iterator. The
|
<code>imap</code> Python method is light-weight. It does not proactively
|
iterate over the iterator, but rather creates a new iterator which
|
wraps the existing one.
|
</p>
|
<p>Below is the frame decorator for this example.
|
</p>
|
<div class="smallexample">
|
<pre class="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
|
</pre></div>
|
|
<p>This frame decorator only defines and overrides the <code>function</code>
|
method. It lets the supplied <code>FrameDecorator</code>, which is shipped
|
with <small>GDB</small>, perform the other work associated with printing
|
this frame.
|
</p>
|
<p>The combination of these two objects create this output from a
|
backtrace:
|
</p>
|
<div class="smallexample">
|
<pre class="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
|
</pre></div>
|
|
<p>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
|
<small>GDB</small> iterates over the iterator produced by the frame filters,
|
<small>GDB</small> executes each frame decorator which then makes a decision
|
on what to print in the <code>function</code> callback. Using a strategy
|
like this is a way to defer decisions on the frame content to printing
|
time.
|
</p>
|
<a name="Eliding-Frames"></a>
|
<h4 class="subheading">Eliding Frames</h4>
|
|
<p>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</code> frame
|
decorator interface might be preferable.
|
</p>
|
<p>This example approaches the issue with the <code>elided</code> 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.
|
</p>
|
<p>This example comprises of three sections.
|
</p>
|
<div class="smallexample">
|
<pre class="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)
|
</pre></div>
|
|
<p>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</code>) with a custom iterator called
|
<code>ElidingInlineIterator</code>. This again defers actions to when
|
<small>GDB</small> prints the backtrace, as the iterator is not traversed
|
until printing.
|
</p>
|
<p>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.
|
</p>
|
<div class="smallexample">
|
<pre class="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])
|
</pre></div>
|
|
<p>This iterator implements the Python iterator protocol. When the
|
<code>next</code> function is called (when <small>GDB</small> prints each frame),
|
the iterator checks if this frame decorator, <code>frame</code>, 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</code>, which it fetches. It then creates and returns a
|
frame decorator, <code>ElidingFrameDecorator</code>, which contains both the
|
elided frame, and the eliding frame.
|
</p>
|
<div class="smallexample">
|
<pre class="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)
|
</pre></div>
|
|
<p>This frame decorator overrides one function and returns the inlined
|
frame in the <code>elided</code> method. As before it lets
|
<code>FrameDecorator</code> do the rest of the work involved in printing
|
this frame. This produces the following output.
|
</p>
|
<div class="smallexample">
|
<pre class="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
|
</pre></div>
|
|
<p>In that output, <code>max</code> which has been inlined into <code>main</code> is
|
printed hierarchically. Another approach would be to combine the
|
<code>function</code> method, and the <code>elided</code> method to both print a
|
marker in the inlined frame, and also show the hierarchical
|
relationship.
|
</p>
|
<hr>
|
<div class="header">
|
<p>
|
Next: <a href="Unwinding-Frames-in-Python.html#Unwinding-Frames-in-Python" accesskey="n" rel="next">Unwinding Frames in Python</a>, Previous: <a href="Frame-Decorator-API.html#Frame-Decorator-API" accesskey="p" rel="previous">Frame Decorator API</a>, Up: <a href="Python-API.html#Python-API" accesskey="u" rel="up">Python API</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html#Concept-Index" title="Index" rel="index">Index</a>]</p>
|
</div>
|
|
|
|
</body>
|
</html>
|