hc
2023-11-06 15ade055295d13f95d49e3d99b09f3bbfb4a43e7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
<!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> &nbsp; [<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 = &quot;Foo&quot;
        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&rsquo;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&rsquo;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 &ldquo;[inlined]&rdquo; 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&rsquo;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 = &quot;InlinedFrameFilter&quot;
        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 + &quot; [inlined]&quot;
 
        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 = &quot;InlinedFrameFilter&quot;
        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> &nbsp; [<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>