This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [RFC] [PATCH] Provide the ability to write the frame unwinder in Python
- From: Doug Evans <dje at google dot com>
- To: Alexander Smundak <asmundak at google dot com>
- Cc: Andy Wingo <wingo at igalia dot com>, gdb-patches <gdb-patches at sourceware dot org>
- Date: Mon, 30 Mar 2015 10:45:18 -0700
- Subject: Re: [RFC] [PATCH] Provide the ability to write the frame unwinder in Python
- Authentication-results: sourceware.org; auth=none
- References: <CAHQ51u7NUoQ8w9c5mc-Eiz05b1Nub6zqj_Ne7vfgWb5EP9_X8w at mail dot gmail dot com> <21714 dot 40641 dot 510825 dot 30998 at ruffy2 dot mtv dot corp dot google dot com> <CAHQ51u5_ViLaEmv9e43R-wzuWw8dwNkb-2XgCRy5ELQq5FUAWg at mail dot gmail dot com> <54E71694 dot 1080304 at redhat dot com> <CAHQ51u75+9HYAVJXYNQa0gTnQtYKEgmSkyAhAPYp-y4HGtXssg at mail dot gmail dot com> <CAHQ51u6UZ7A47rpGgX0QGeYSTCz1eo_3jWHc=q2ZX3YhqcJ6iQ at mail dot gmail dot com> <87ioei31uj dot fsf at igalia dot com> <CAHQ51u4f+Vx7qXPm-KAAENOceaVogMbDMw6==N_nY+GrLr4Pgg at mail dot gmail dot com> <87d24p19tt dot fsf at igalia dot com> <54FD7DAA dot 7010603 at redhat dot com> <CAHQ51u7sUkGhkmvTaaO_Jo6Jn+kojfiMWHmc2=7OWHThAq6EKw at mail dot gmail dot com> <87twxrncld dot fsf at igalia dot com> <CAHQ51u60nHp1a2DXZ4srvRefyTtge1BUw7-=JuYqChHN_wUGyQ at mail dot gmail dot com> <87ioe1dvu2 dot fsf at igalia dot com> <CAHQ51u7KzQLSLC=QeLA=zd+TUkbbNzzndfeVLFWpjiR-pL8ang at mail dot gmail dot com> <87sid4atms dot fsf at igalia dot com> <CAHQ51u6=9BKf6YSTavbY7u_Mi6miKJ_Yo1QcaG=KsYtYzoWY_Q at mail dot gmail dot com> <CAHQ51u4ybxem1X89G7frW-N_gZK6q3rKLunxgf+7UPzpswCfNg at mail dot gmail dot com> <87r3smado6 dot fsf at igalia dot com> <CAHQ51u6+sCwSi3dYQXsz7orjq-yGjX6PayQduwVURx3DxpK=oA at mail dot gmail dot com> <21776 dot 28787 dot 615626 dot 171251 at ruffy2 dot mtv dot corp dot google dot com> <CAHQ51u49b6rcb3m7XM=0i=pfdG9A7wKeV8SXPOhpRps=s3-HSg at mail dot gmail dot com> <21781 dot 55769 dot 949230 dot 79805 at ruffy2 dot mtv dot corp dot google dot com> <CAHQ51u5=mNXcA_CY4hQuvVHSKuMbaVUYyHvhrgZEUJZdX2iL8Q at mail dot gmail dot com>
Alexander Smundak writes:
> Addressed eliz@ and dje@ comments.
>
> gdb/ChangeLog
>
> 2015-03-28 Sasha Smundak <asmundak@google.com>
>
> * 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
> * doc/python.texi (Writing a Frame Unwinder in Python): Add
> section.
> * 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/testsuite/ChangeLog
>
> 2015-03-28 Sasha Smundak <asmundak@google.com>
>
> * 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.
Hi. Just a few more nits.
> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
> index d725eb0..6b1878e 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -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
> +
> +A @code{gdb.UnwindInfo} object can be constructed by one of the
> +methods described above. Use the following method to set the caller
I'd replace "can be constructed by one of the methods describe above." with
"is constructed with the @code{PendingFrame.create_unwind_info}
method described above."
> +frame's registers:
> +
> +@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_instance
s/unwind_instance/unwind_info/
> diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
> index 92b06f2..0494959 100644
> --- a/gdb/python/lib/gdb/__init__.py
> +++ b/gdb/python/lib/gdb/__init__.py
> @@ -71,6 +71,42 @@ type_printers = []
> xmethods = []
> # Initial frame filters.
> frame_filters = {}
> +# Initial frame unwinders.
> +frame_unwinders = []
> +
> +def execute_unwinders(pending_frame):
> + """Internal function called from GDB to execute all unwinders.
> +
> + Runs each currently enabled unwinder until it finds the one that
> + can unwind given frame.
> +
> + Arguments:
> + pending_frame: gdb.PendingFrame instance.
> + Returns:
> + gdb.UnwindInfo instance or None.
> + """
> + for objfile in objfiles():
It's odd to call objfiles() here and _gdb.current_progspace() below.
Either the _gdb. prefix is necessary or it is not.
At the least let's be consistent here.
How about instead of adding _gdb. prefix here ...
> + for unwinder in objfile.frame_unwinders:
> + if unwinder.enabled:
> + unwind_info = unwinder(pending_frame)
> + if unwind_info is not None:
> + return unwind_info
> +
> + current_progspace = _gdb.current_progspace()
.... remove it here.
[and rerun the testsuite to verify]
> + for unwinder in current_progspace.frame_unwinders:
> + if unwinder.enabled:
> + unwind_info = unwinder(pending_frame)
> + if unwind_info is not None:
> + return unwind_info
> +
> + for unwinder in frame_unwinders:
> + if unwinder.enabled:
> + unwind_info = unwinder(pending_frame)
> + if unwind_info is not None:
> + return unwind_info
> +
> + return None
> +
>
> # Convenience variable to GDB's python directory
> PYTHONDIR = os.path.dirname(os.path.dirname(__file__))