Add support for writing unwinders in Python.

gdb/ChangeLog:

	* Makefile.in (SUBDIR_PYTHON_OBJS): Add py-unwind.o.
	(SUBDIR_PYTHON_SRCS): Add py-unwind.c.
	(py-unwind.o): New recipe.
	* NEWS: mention Python frame unwinding.
	* data-directory/Makefile.in (PYTHON_FILE_LIST): Add
	gdb/unwinder.py and gdb/command/unwinder.py
	* python/lib/gdb/__init__.py (packages): Add frame_unwinders
	list.
	(execute_unwinders): New function.
	* python/lib/gdb/command/unwinders.py: New file.
	* python/lib/gdb/unwinder.py: New file.
	* python/py-objfile.c (objfile_object): Add frame_unwinders field.
	(objfpy_dealloc): Decrement frame_unwinders reference count.
	(objfpy_initialize): Create frame_unwinders list.
	(objfpy_get_frame_unwinders): New function.
	(objfpy_set_frame_unwinders): Ditto.
	(objfile_getset): Add frame_unwinders attribute to Objfile.
	* python/py-progspace.c (pspace_object): Add frame_unwinders field.
	(pspy_dealloc): Decrement frame_unwinders reference count.
	(pspy_initialize): Create frame_unwinders list.
	(pspy_get_frame_unwinders): New function.
	(pspy_set_frame_unwinders): Ditto.
	(pspy_getset): Add frame_unwinders attribute to gdb.Progspace.
	* python/py-unwind.c: New file.
	* python/python-internal.h (pspy_get_name_unwinders): New prototype.
	(objpy_get_frame_unwinders): New prototype.
	(gdbpy_initialize_unwind): New prototype.
	* python/python.c (gdbpy_apply_type_printers): Call
	gdbpy_initialize_unwind.

gdb/doc/ChangeLog:

	* doc/python.texi (Writing a Frame Unwinder in Python): Add
	section.

gdb/testsuite/ChangeLog:

	* gdb.python/py-unwind-maint.c: New file.
	* gdb.python/py-unwind-maint.exp: New test.
	* gdb.python/py-unwind-maint.py: New file.
	* gdb.python/py-unwind.c: New file.
	* gdb.python/py-unwind.exp: New test.
	* gdb.python/py-unwind.py: New test.
This commit is contained in:
Sasha Smundak
2015-04-01 11:49:12 -07:00
committed by Doug Evans
parent 79730a3b26
commit d11916aa89
21 changed files with 1808 additions and 2 deletions

View File

@ -144,6 +144,7 @@ optional arguments while skipping others. Example:
* Frame Filter API:: Filtering Frames.
* Frame Decorator API:: Decorating Frames.
* Writing a Frame Filter:: Writing a Frame Filter.
* Unwinding Frames in Python:: Writing frame unwinder.
* Xmethods In Python:: Adding and replacing methods of C++ classes.
* Xmethod API:: Xmethod types.
* Writing an Xmethod:: Writing an xmethod.
@ -2178,6 +2179,148 @@ printed hierarchically. Another approach would be to combine the
marker in the inlined frame, and also show the hierarchical
relationship.
@node Unwinding Frames in Python
@subsubsection Unwinding Frames in Python
@cindex unwinding frames in Python
In @value{GDBN} terminology ``unwinding'' is the process of finding
the previous frame (that is, caller's) from the current one. An
unwinder has three methods. The first one checks if it can handle
given frame (``sniff'' it). For the frames it can sniff an unwinder
provides two additional methods: it can return frame's ID, and it can
fetch registers from the previous frame. A running @value{GDBN}
mantains a list of the unwinders and calls each unwinder's sniffer in
turn until it finds the one that recognizes the current frame. There
is an API to register an unwinder.
The unwinders that come with @value{GDBN} handle standard frames.
However, mixed language applications (for example, an application
running Java Virtual Machine) sometimes use frame layouts that cannot
be handled by the @value{GDBN} unwinders. You can write Python code
that can handle such custom frames.
You implement a frame unwinder in Python as a class with which has two
attributes, @code{name} and @code{enabled}, with obvious meanings, and
a single method @code{__call__}, which examines a given frame and
returns an object (an instance of @code{gdb.UnwindInfo class)}
describing it. If an unwinder does not recognize a frame, it should
return @code{None}. The code in @value{GDBN} that enables writing
unwinders in Python uses this object to return frame's ID and previous
frame registers when @value{GDBN} core asks for them.
@subheading Unwinder Input
An object passed to an unwinder (a @code{gdb.PendingFrame} instance)
provides a method to read frame's registers:
@defun PendingFrame.read_register (reg)
This method returns the contents of the register @var{regn} in the
frame as a @code{gdb.Value} object. @var{reg} can be either a
register number or a register name; the values are platform-specific.
They are usually found in the corresponding
@file{@var{platform}-tdep.h} file in the @value{GDBN} source tree.
@end defun
It also provides a factory method to create a @code{gdb.UnwindInfo}
instance to be returned to @value{GDBN}:
@defun PendingFrame.create_unwind_info (frame_id)
Returns a new @code{gdb.UnwindInfo} instance identified by given
@var{frame_id}. The argument is used to build @value{GDBN}'s frame ID
using one of functions provided by @value{GDBN}. @var{frame_id}'s attributes
determine which function will be used, as follows:
@table @code
@item sp, pc, special
@code{frame_id_build_special (@var{frame_id}.sp, @var{frame_id}.pc, @var{frame_id}.special)}
@item sp, pc
@code{frame_id_build (@var{frame_id}.sp, @var{frame_id}.pc)}
This is the most common case.
@item sp
@code{frame_id_build_wild (@var{frame_id}.sp)}
@end table
The attribute values should be @code{gdb.Value}
@end defun
@subheading Unwinder Output: UnwindInfo
Use @code{PendingFrame.create_unwind_info} method described above to
create a @code{gdb.UnwindInfo} instance. Use the following method to
specify caller registers that have been saved in this frame:
@defun gdb.UnwindInfo.add_saved_register (reg, value)
@var{reg} identifies the register. It can be a number or a name, just
as for the @code{PendingFrame.read_register} method above.
@var{value} is a register value (a @code{gdb.Value} object).
@end defun
@subheading Unwinder Skeleton Code
@value{GDBN} comes with the module containing the base @code{Unwinder}
class. Derive your unwinder class from it and structure the code as
follows:
@smallexample
from gdb.unwinders import Unwinder
class FrameId(object):
def __init__(self, sp, pc):
self.sp = sp
self.pc = pc
class MyUnwinder(Unwinder):
def __init__(....):
supe(MyUnwinder, self).__init___(<expects unwinder name argument>)
def __call__(pending_frame):
if not <we recognize frame>:
return None
# Create UnwindInfo. Usually the frame is identified by the stack
# pointer and the program counter.
sp = pending_frame.read_register(<SP number>)
pc = pending_frame.read_register(<PC number>)
unwind_info = pending_frame.create_unwind_info(FrameId(sp, pc))
# Find the values of the registers in the caller's frame and
# save them in the result:
unwind_info.add_saved_register(<register>, <value>)
....
# Return the result:
return unwind_info
@end smallexample
@subheading Registering a Unwinder
An object file, a program space, and the @value{GDBN} proper can have
unwinders registered with it.
The @code{gdb.unwinders} module provides the function to register a
unwinder:
@defun gdb.unwinder.register_unwinder (locus, unwinder, replace=False)
@var{locus} is specifies an object file or a program space to which
@var{unwinder} is added. Passing @code{None} or @code{gdb} adds
@var{unwinder} to the @value{GDBN}'s global unwinder list. The newly
added @var{unwinder} will be called before any other unwinder from the
same locus. Two unwinders in the same locus cannot have the same
name. An attempt to add a unwinder with already existing name raises
an exception unless @var{replace} is @code{True}, in which case the
old unwinder is deleted.
@end defun
@subheading Unwinder Precedence
@value{GDBN} first calls the unwinders from all the object files in no
particular order, then the unwinders from the current program space,
and finally the unwinders from @value{GDBN}.
@node Xmethods In Python
@subsubsection Xmethods In Python
@cindex xmethods in Python