This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC] [PATCH] Provide the ability to write the frame unwinder in Python


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__))


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]