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:
parent
79730a3b26
commit
d11916aa89
21 changed files with 1808 additions and 2 deletions
|
@ -1,3 +1,35 @@
|
|||
2015-04-01 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
|
||||
* 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.
|
||||
|
||||
2015-04-01 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* infrun.c (resume): Check currently_stepping after clearing
|
||||
|
|
|
@ -404,6 +404,7 @@ SUBDIR_PYTHON_OBS = \
|
|||
py-symtab.o \
|
||||
py-threadevent.o \
|
||||
py-type.o \
|
||||
py-unwind.o \
|
||||
py-utils.o \
|
||||
py-value.o \
|
||||
py-varobj.o
|
||||
|
@ -443,6 +444,7 @@ SUBDIR_PYTHON_SRCS = \
|
|||
python/py-symtab.c \
|
||||
python/py-threadevent.c \
|
||||
python/py-type.c \
|
||||
python/py-unwind.c \
|
||||
python/py-utils.c \
|
||||
python/py-value.c \
|
||||
python/py-varobj.c
|
||||
|
@ -2639,6 +2641,10 @@ py-type.o: $(srcdir)/python/py-type.c
|
|||
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-type.c
|
||||
$(POSTCOMPILE)
|
||||
|
||||
py-unwind.o: $(srcdir)/python/py-unwind.c
|
||||
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-unwind.c
|
||||
$(POSTCOMPILE)
|
||||
|
||||
py-utils.o: $(srcdir)/python/py-utils.c
|
||||
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-utils.c
|
||||
$(POSTCOMPILE)
|
||||
|
|
1
gdb/NEWS
1
gdb/NEWS
|
@ -29,6 +29,7 @@
|
|||
** gdb.Objfile objects have a new attribute "username",
|
||||
which is the name of the objfile as specified by the user,
|
||||
without, for example, resolving symlinks.
|
||||
** You can now write frame unwinders in Python.
|
||||
|
||||
* New commands
|
||||
|
||||
|
|
|
@ -62,11 +62,13 @@ PYTHON_FILE_LIST = \
|
|||
gdb/FrameDecorator.py \
|
||||
gdb/types.py \
|
||||
gdb/printing.py \
|
||||
gdb/unwinder.py \
|
||||
gdb/prompt.py \
|
||||
gdb/xmethod.py \
|
||||
gdb/command/__init__.py \
|
||||
gdb/command/xmethods.py \
|
||||
gdb/command/frame_filters.py \
|
||||
gdb/command/unwinders.py \
|
||||
gdb/command/type_printers.py \
|
||||
gdb/command/pretty_printers.py \
|
||||
gdb/command/prompt.py \
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2015-04-01 Sasha Smundak <asmundak@google.com>
|
||||
|
||||
* doc/python.texi (Writing a Frame Unwinder in Python): Add
|
||||
section.
|
||||
|
||||
2015-03-31 Sergio Durigan Junior <sergiodj@redhat.com>
|
||||
|
||||
PR corefiles/16092
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,7 +28,7 @@ class _GdbFile (object):
|
|||
# These two are needed in Python 3
|
||||
encoding = "UTF-8"
|
||||
errors = "strict"
|
||||
|
||||
|
||||
def close(self):
|
||||
# Do nothing.
|
||||
return None
|
||||
|
@ -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 _gdb.objfiles():
|
||||
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()
|
||||
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__))
|
||||
|
|
198
gdb/python/lib/gdb/command/unwinders.py
Normal file
198
gdb/python/lib/gdb/command/unwinders.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
# Unwinder commands.
|
||||
# Copyright 2015 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gdb
|
||||
import re
|
||||
|
||||
|
||||
def validate_regexp(exp, idstring):
|
||||
try:
|
||||
return re.compile(exp)
|
||||
except SyntaxError:
|
||||
raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp))
|
||||
|
||||
|
||||
def parse_unwinder_command_args(arg):
|
||||
"""Internal utility to parse unwinder command argv.
|
||||
|
||||
Arguments:
|
||||
arg: The arguments to the command. The format is:
|
||||
[locus-regexp [name-regexp]]
|
||||
|
||||
Returns:
|
||||
A 2-tuple of compiled regular expressions.
|
||||
|
||||
Raises:
|
||||
SyntaxError: an error processing ARG
|
||||
"""
|
||||
|
||||
argv = gdb.string_to_argv(arg)
|
||||
argc = len(argv)
|
||||
if argc > 2:
|
||||
raise SyntaxError("Too many arguments.")
|
||||
locus_regexp = ""
|
||||
name_regexp = ""
|
||||
if argc >= 1:
|
||||
locus_regexp = argv[0]
|
||||
if argc >= 2:
|
||||
name_regexp = argv[1]
|
||||
return (validate_regexp(locus_regexp, "locus"),
|
||||
validate_regexp(name_regexp, "unwinder"))
|
||||
|
||||
|
||||
class InfoUnwinder(gdb.Command):
|
||||
"""GDB command to list unwinders.
|
||||
|
||||
Usage: info unwinder [locus-regexp [name-regexp]]
|
||||
|
||||
LOCUS-REGEXP is a regular expression matching the location of the
|
||||
unwinder. If it is omitted, all registered unwinders from all
|
||||
loci are listed. A locus can be 'global', 'progspace' to list
|
||||
the unwinders from the current progspace, or a regular expression
|
||||
matching filenames of objfiles.
|
||||
|
||||
NAME-REGEXP is a regular expression to filter unwinder names. If
|
||||
this omitted for a specified locus, then all registered unwinders
|
||||
in the locus are listed.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(InfoUnwinder, self).__init__("info unwinder",
|
||||
gdb.COMMAND_STACK)
|
||||
|
||||
def list_unwinders(self, title, unwinders, name_re):
|
||||
"""Lists the unwinders whose name matches regexp.
|
||||
|
||||
Arguments:
|
||||
title: The line to print before the list.
|
||||
unwinders: The list of the unwinders.
|
||||
name_re: unwinder name filter.
|
||||
"""
|
||||
if not unwinders:
|
||||
return
|
||||
print title
|
||||
for unwinder in unwinders:
|
||||
if name_re.match(unwinder.name):
|
||||
print(" %s%s" % (unwinder.name,
|
||||
"" if unwinder.enabled else " [disabled]"))
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
locus_re, name_re = parse_unwinder_command_args(arg)
|
||||
if locus_re.match("global"):
|
||||
self.list_unwinders("Global:", gdb.frame_unwinders,
|
||||
name_re)
|
||||
if locus_re.match("progspace"):
|
||||
cp = gdb.current_progspace()
|
||||
self.list_unwinders("Progspace %s:" % cp.filename,
|
||||
cp.frame_unwinders, name_re)
|
||||
for objfile in gdb.objfiles():
|
||||
if locus_re.match(objfile.filename):
|
||||
self.list_unwinders("Objfile %s:" % objfile.filename,
|
||||
objfile.frame_unwinders, name_re)
|
||||
|
||||
|
||||
def do_enable_unwinder1(unwinders, name_re, flag):
|
||||
"""Enable/disable unwinders whose names match given regex.
|
||||
|
||||
Arguments:
|
||||
unwinders: The list of unwinders.
|
||||
name_re: Unwinder name filter.
|
||||
flag: Enable/disable.
|
||||
|
||||
Returns:
|
||||
The number of unwinders affected.
|
||||
"""
|
||||
total = 0
|
||||
for unwinder in unwinders:
|
||||
if name_re.match(unwinder.name):
|
||||
unwinder.enabled = flag
|
||||
total += 1
|
||||
return total
|
||||
|
||||
|
||||
def do_enable_unwinder(arg, flag):
|
||||
"""Enable/disable unwinder(s)."""
|
||||
(locus_re, name_re) = parse_unwinder_command_args(arg)
|
||||
total = 0
|
||||
if locus_re.match("global"):
|
||||
total += do_enable_unwinder1(gdb.frame_unwinders, name_re, flag)
|
||||
if locus_re.match("progspace"):
|
||||
total += do_enable_unwinder1(gdb.current_progspace().frame_unwinders,
|
||||
name_re, flag)
|
||||
for objfile in gdb.objfiles():
|
||||
if locus_re.match(objfile.filename):
|
||||
total += do_enable_unwinder1(objfile.frame_unwinders, name_re,
|
||||
flag)
|
||||
print("%d unwinder%s %s" % (total, "" if total == 1 else "s",
|
||||
"enabled" if flag else "disabled"))
|
||||
|
||||
|
||||
class EnableUnwinder(gdb.Command):
|
||||
"""GDB command to enable unwinders.
|
||||
|
||||
Usage: enable unwinder [locus-regexp [name-regexp]]
|
||||
|
||||
LOCUS-REGEXP is a regular expression specifying the unwinders to
|
||||
enable. It can 'global', 'progspace', or the name of an objfile
|
||||
within that progspace.
|
||||
|
||||
NAME_REGEXP is a regular expression to filter unwinder names. If
|
||||
this omitted for a specified locus, then all registered unwinders
|
||||
in the locus are affected.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(EnableUnwinder, self).__init__("enable unwinder",
|
||||
gdb.COMMAND_STACK)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
"""GDB calls this to perform the command."""
|
||||
do_enable_unwinder(arg, True)
|
||||
|
||||
|
||||
class DisableUnwinder(gdb.Command):
|
||||
"""GDB command to disable the specified unwinder.
|
||||
|
||||
Usage: disable unwinder [locus-regexp [name-regexp]]
|
||||
|
||||
LOCUS-REGEXP is a regular expression specifying the unwinders to
|
||||
disable. It can 'global', 'progspace', or the name of an objfile
|
||||
within that progspace.
|
||||
|
||||
NAME_REGEXP is a regular expression to filter unwinder names. If
|
||||
this omitted for a specified locus, then all registered unwinders
|
||||
in the locus are affected.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(DisableUnwinder, self).__init__("disable unwinder",
|
||||
gdb.COMMAND_STACK)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
"""GDB calls this to perform the command."""
|
||||
do_enable_unwinder(arg, False)
|
||||
|
||||
|
||||
def register_unwinder_commands():
|
||||
"""Installs the unwinder commands."""
|
||||
InfoUnwinder()
|
||||
EnableUnwinder()
|
||||
DisableUnwinder()
|
||||
|
||||
|
||||
register_unwinder_commands()
|
94
gdb/python/lib/gdb/unwinder.py
Normal file
94
gdb/python/lib/gdb/unwinder.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
# Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Unwinder class and register_unwinder function."""
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
class Unwinder(object):
|
||||
"""Base class (or a template) for frame unwinders written in Python.
|
||||
|
||||
An unwinder has a single method __call__ and the attributes
|
||||
described below.
|
||||
|
||||
Attributes:
|
||||
name: The name of the unwinder.
|
||||
enabled: A boolean indicating whether the unwinder is enabled.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
name: An identifying name for the unwinder.
|
||||
"""
|
||||
self.name = name
|
||||
self.enabled = True
|
||||
|
||||
def __call__(self, pending_frame):
|
||||
"""GDB calls this method to unwind a frame.
|
||||
|
||||
Arguments:
|
||||
pending_frame: gdb.PendingFrame instance.
|
||||
|
||||
Returns:
|
||||
gdb.UnwindInfo instance.
|
||||
"""
|
||||
raise NotImplementedError("Unwinder __call__.")
|
||||
|
||||
|
||||
def register_unwinder(locus, unwinder, replace=False):
|
||||
"""Register unwinder in given locus.
|
||||
|
||||
The unwinder is prepended to the locus's unwinders list. Unwinder
|
||||
name should be unique.
|
||||
|
||||
Arguments:
|
||||
locus: Either an objfile, progspace, or None (in which case
|
||||
the unwinder is registered globally).
|
||||
unwinder: An object of a gdb.Unwinder subclass
|
||||
replace: If True, replaces existing unwinder with the same name.
|
||||
Otherwise, raises exception if unwinder with the same
|
||||
name already exists.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
|
||||
Raises:
|
||||
RuntimeError: Unwinder name is not unique
|
||||
TypeError: Bad locus type
|
||||
"""
|
||||
if locus is None:
|
||||
if gdb.parameter("verbose"):
|
||||
gdb.write("Registering global %s unwinder ...\n" % unwinder.name)
|
||||
locus = gdb
|
||||
elif isinstance(locus, gdb.Objfile) or isinstance(locus, gdb.Progspace):
|
||||
if gdb.parameter("verbose"):
|
||||
gdb.write("Registering %s unwinder for %s ...\n" %
|
||||
(unwinder.name, locus.filename))
|
||||
else:
|
||||
raise TypeError("locus should be gdb.Objfile or gdb.Progspace or None")
|
||||
|
||||
i = 0
|
||||
for needle in locus.frame_unwinders:
|
||||
if needle.name == unwinder.name:
|
||||
if replace:
|
||||
del locus.frame_unwinders[i]
|
||||
else:
|
||||
raise RuntimeError("Unwinder %s already exists." %
|
||||
unwinder.name)
|
||||
i += 1
|
||||
locus.frame_unwinders.insert(0, unwinder)
|
|
@ -42,6 +42,10 @@ typedef struct
|
|||
|
||||
/* The frame filter list of functions. */
|
||||
PyObject *frame_filters;
|
||||
|
||||
/* The list of frame unwinders. */
|
||||
PyObject *frame_unwinders;
|
||||
|
||||
/* The type-printer list. */
|
||||
PyObject *type_printers;
|
||||
|
||||
|
@ -184,6 +188,7 @@ objfpy_dealloc (PyObject *o)
|
|||
Py_XDECREF (self->dict);
|
||||
Py_XDECREF (self->printers);
|
||||
Py_XDECREF (self->frame_filters);
|
||||
Py_XDECREF (self->frame_unwinders);
|
||||
Py_XDECREF (self->type_printers);
|
||||
Py_XDECREF (self->xmethods);
|
||||
Py_TYPE (self)->tp_free (self);
|
||||
|
@ -206,6 +211,10 @@ objfpy_initialize (objfile_object *self)
|
|||
if (self->frame_filters == NULL)
|
||||
return 0;
|
||||
|
||||
self->frame_unwinders = PyList_New (0);
|
||||
if (self->frame_unwinders == NULL)
|
||||
return 0;
|
||||
|
||||
self->type_printers = PyList_New (0);
|
||||
if (self->type_printers == NULL)
|
||||
return 0;
|
||||
|
@ -313,6 +322,48 @@ objfpy_set_frame_filters (PyObject *o, PyObject *filters, void *ignore)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Return the frame unwinders attribute for this object file. */
|
||||
|
||||
PyObject *
|
||||
objfpy_get_frame_unwinders (PyObject *o, void *ignore)
|
||||
{
|
||||
objfile_object *self = (objfile_object *) o;
|
||||
|
||||
Py_INCREF (self->frame_unwinders);
|
||||
return self->frame_unwinders;
|
||||
}
|
||||
|
||||
/* Set this object file's frame unwinders list to UNWINDERS. */
|
||||
|
||||
static int
|
||||
objfpy_set_frame_unwinders (PyObject *o, PyObject *unwinders, void *ignore)
|
||||
{
|
||||
PyObject *tmp;
|
||||
objfile_object *self = (objfile_object *) o;
|
||||
|
||||
if (!unwinders)
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
_("Cannot delete the frame unwinders attribute."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!PyList_Check (unwinders))
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
_("The frame_unwinders attribute must be a list."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Take care in case the LHS and RHS are related somehow. */
|
||||
tmp = self->frame_unwinders;
|
||||
Py_INCREF (unwinders);
|
||||
self->frame_unwinders = unwinders;
|
||||
Py_XDECREF (tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the 'type_printers' attribute. */
|
||||
|
||||
static PyObject *
|
||||
|
@ -651,6 +702,8 @@ static PyGetSetDef objfile_getset[] =
|
|||
"Pretty printers.", NULL },
|
||||
{ "frame_filters", objfpy_get_frame_filters,
|
||||
objfpy_set_frame_filters, "Frame Filters.", NULL },
|
||||
{ "frame_unwinders", objfpy_get_frame_unwinders,
|
||||
objfpy_set_frame_unwinders, "Frame Unwinders", NULL },
|
||||
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
|
||||
"Type printers.", NULL },
|
||||
{ "xmethods", objfpy_get_xmethods, NULL,
|
||||
|
|
|
@ -41,6 +41,10 @@ typedef struct
|
|||
|
||||
/* The frame filter list of functions. */
|
||||
PyObject *frame_filters;
|
||||
|
||||
/* The frame unwinder list. */
|
||||
PyObject *frame_unwinders;
|
||||
|
||||
/* The type-printer list. */
|
||||
PyObject *type_printers;
|
||||
|
||||
|
@ -82,6 +86,7 @@ pspy_dealloc (PyObject *self)
|
|||
Py_XDECREF (ps_self->dict);
|
||||
Py_XDECREF (ps_self->printers);
|
||||
Py_XDECREF (ps_self->frame_filters);
|
||||
Py_XDECREF (ps_self->frame_unwinders);
|
||||
Py_XDECREF (ps_self->type_printers);
|
||||
Py_XDECREF (ps_self->xmethods);
|
||||
Py_TYPE (self)->tp_free (self);
|
||||
|
@ -104,6 +109,10 @@ pspy_initialize (pspace_object *self)
|
|||
if (self->frame_filters == NULL)
|
||||
return 0;
|
||||
|
||||
self->frame_unwinders = PyList_New (0);
|
||||
if (self->frame_unwinders == NULL)
|
||||
return 0;
|
||||
|
||||
self->type_printers = PyList_New (0);
|
||||
if (self->type_printers == NULL)
|
||||
return 0;
|
||||
|
@ -211,6 +220,48 @@ pspy_set_frame_filters (PyObject *o, PyObject *frame, void *ignore)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Return the list of the frame unwinders for this program space. */
|
||||
|
||||
PyObject *
|
||||
pspy_get_frame_unwinders (PyObject *o, void *ignore)
|
||||
{
|
||||
pspace_object *self = (pspace_object *) o;
|
||||
|
||||
Py_INCREF (self->frame_unwinders);
|
||||
return self->frame_unwinders;
|
||||
}
|
||||
|
||||
/* Set this program space's list of the unwinders to UNWINDERS. */
|
||||
|
||||
static int
|
||||
pspy_set_frame_unwinders (PyObject *o, PyObject *unwinders, void *ignore)
|
||||
{
|
||||
PyObject *tmp;
|
||||
pspace_object *self = (pspace_object *) o;
|
||||
|
||||
if (!unwinders)
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
"cannot delete the frame unwinders list");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!PyList_Check (unwinders))
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
"the frame unwinders attribute must be a list");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Take care in case the LHS and RHS are related somehow. */
|
||||
tmp = self->frame_unwinders;
|
||||
Py_INCREF (unwinders);
|
||||
self->frame_unwinders = unwinders;
|
||||
Py_XDECREF (tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the 'type_printers' attribute. */
|
||||
|
||||
static PyObject *
|
||||
|
@ -345,6 +396,8 @@ static PyGetSetDef pspace_getset[] =
|
|||
"Pretty printers.", NULL },
|
||||
{ "frame_filters", pspy_get_frame_filters, pspy_set_frame_filters,
|
||||
"Frame filters.", NULL },
|
||||
{ "frame_unwinders", pspy_get_frame_unwinders, pspy_set_frame_unwinders,
|
||||
"Frame unwinders.", NULL },
|
||||
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
|
||||
"Type printers.", NULL },
|
||||
{ "xmethods", pspy_get_xmethods, NULL,
|
||||
|
|
788
gdb/python/py-unwind.c
Normal file
788
gdb/python/py-unwind.c
Normal file
|
@ -0,0 +1,788 @@
|
|||
/* Python frame unwinder interface.
|
||||
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
#include "arch-utils.h"
|
||||
#include "frame-unwind.h"
|
||||
#include "gdb_obstack.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "language.h"
|
||||
#include "observer.h"
|
||||
#include "python-internal.h"
|
||||
#include "regcache.h"
|
||||
#include "valprint.h"
|
||||
#include "user-regs.h"
|
||||
|
||||
#define TRACE_PY_UNWIND(level, args...) if (pyuw_debug >= level) \
|
||||
{ fprintf_unfiltered (gdb_stdlog, args); }
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
|
||||
/* Frame we are unwinding. */
|
||||
struct frame_info *frame_info;
|
||||
|
||||
/* Its architecture, passed by the sniffer caller. */
|
||||
struct gdbarch *gdbarch;
|
||||
} pending_frame_object;
|
||||
|
||||
/* Saved registers array item. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int number;
|
||||
PyObject *value;
|
||||
} saved_reg;
|
||||
DEF_VEC_O (saved_reg);
|
||||
|
||||
/* The data we keep for the PyUnwindInfo: pending_frame, saved registers
|
||||
and frame ID. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
|
||||
/* gdb.PendingFrame for the frame we are unwinding. */
|
||||
PyObject *pending_frame;
|
||||
|
||||
/* Its ID. */
|
||||
struct frame_id frame_id;
|
||||
|
||||
/* Saved registers array. */
|
||||
VEC (saved_reg) *saved_regs;
|
||||
} unwind_info_object;
|
||||
|
||||
/* The data we keep for a frame we can unwind: frame ID and an array of
|
||||
(register_number, register_value) pairs. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Frame ID. */
|
||||
struct frame_id frame_id;
|
||||
|
||||
/* GDB Architecture. */
|
||||
struct gdbarch *gdbarch;
|
||||
|
||||
/* Length of the `reg' array below. */
|
||||
int reg_count;
|
||||
|
||||
struct reg_info
|
||||
{
|
||||
/* Register number. */
|
||||
int number;
|
||||
|
||||
/* Register data bytes pointer. */
|
||||
gdb_byte data[MAX_REGISTER_SIZE];
|
||||
} reg[];
|
||||
} cached_frame_info;
|
||||
|
||||
static PyTypeObject pending_frame_object_type
|
||||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("pending_frame_object");
|
||||
|
||||
static PyTypeObject unwind_info_object_type
|
||||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("unwind_info_object");
|
||||
|
||||
static unsigned int pyuw_debug = 0;
|
||||
|
||||
static struct gdbarch_data *pyuw_gdbarch_data;
|
||||
|
||||
/* Parses register id, which can be either a number or a name.
|
||||
Returns 1 on success, 0 otherwise. */
|
||||
|
||||
static int
|
||||
pyuw_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
|
||||
int *reg_num)
|
||||
{
|
||||
if (pyo_reg_id == NULL)
|
||||
return 0;
|
||||
if (gdbpy_is_string (pyo_reg_id))
|
||||
{
|
||||
const char *reg_name = gdbpy_obj_to_string (pyo_reg_id);
|
||||
|
||||
if (reg_name == NULL)
|
||||
return 0;
|
||||
*reg_num = user_reg_map_name_to_regnum (gdbarch, reg_name,
|
||||
strlen (reg_name));
|
||||
return *reg_num >= 0;
|
||||
}
|
||||
else if (PyInt_Check (pyo_reg_id))
|
||||
{
|
||||
long value;
|
||||
if (gdb_py_int_as_long (pyo_reg_id, &value) && (int) value == value)
|
||||
{
|
||||
*reg_num = (int) value;
|
||||
return user_reg_map_regnum_to_name (gdbarch, *reg_num) != NULL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert gdb.Value instance to inferior's pointer. Return 1 on success,
|
||||
0 on failure. */
|
||||
|
||||
static int
|
||||
pyuw_value_obj_to_pointer (PyObject *pyo_value, CORE_ADDR *addr)
|
||||
{
|
||||
int rc = 0;
|
||||
struct value *value;
|
||||
|
||||
TRY
|
||||
{
|
||||
if ((value = value_object_to_value (pyo_value)) != NULL)
|
||||
{
|
||||
*addr = unpack_pointer (value_type (value),
|
||||
value_contents (value));
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
CATCH (except, RETURN_MASK_ALL)
|
||||
{
|
||||
gdbpy_convert_exception (except);
|
||||
}
|
||||
END_CATCH
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Get attribute from an object and convert it to the inferior's
|
||||
pointer value. Return 1 if attribute exists and its value can be
|
||||
converted. Otherwise, if attribute does not exist or its value is
|
||||
None, return 0. In all other cases set Python error and return
|
||||
0. */
|
||||
|
||||
static int
|
||||
pyuw_object_attribute_to_pointer (PyObject *pyo, const char *attr_name,
|
||||
CORE_ADDR *addr)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (PyObject_HasAttrString (pyo, attr_name))
|
||||
{
|
||||
PyObject *pyo_value = PyObject_GetAttrString (pyo, attr_name);
|
||||
struct value *value;
|
||||
|
||||
if (pyo_value != NULL && pyo_value != Py_None)
|
||||
{
|
||||
rc = pyuw_value_obj_to_pointer (pyo_value, addr);
|
||||
if (!rc)
|
||||
PyErr_Format (
|
||||
PyExc_ValueError,
|
||||
_("The value of the '%s' attribute is not a pointer."),
|
||||
attr_name);
|
||||
}
|
||||
Py_XDECREF (pyo_value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called by the Python interpreter to obtain string representation
|
||||
of the UnwindInfo object. */
|
||||
|
||||
static PyObject *
|
||||
unwind_infopy_str (PyObject *self)
|
||||
{
|
||||
struct ui_file *strfile = mem_fileopen ();
|
||||
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
||||
pending_frame_object *pending_frame
|
||||
= (pending_frame_object *) (unwind_info->pending_frame);
|
||||
PyObject *result;
|
||||
|
||||
fprintf_unfiltered (strfile, "Frame ID: ");
|
||||
fprint_frame_id (strfile, unwind_info->frame_id);
|
||||
{
|
||||
char *sep = "";
|
||||
int i;
|
||||
struct value_print_options opts;
|
||||
saved_reg *reg;
|
||||
|
||||
get_user_print_options (&opts);
|
||||
fprintf_unfiltered (strfile, "\nSaved registers: (");
|
||||
for (i = 0;
|
||||
i < VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg);
|
||||
i++)
|
||||
{
|
||||
struct value *value = value_object_to_value (reg->value);
|
||||
|
||||
fprintf_unfiltered (strfile, "%s(%d, ", sep, reg->number);
|
||||
if (value != NULL)
|
||||
{
|
||||
TRY
|
||||
{
|
||||
value_print (value, strfile, &opts);
|
||||
fprintf_unfiltered (strfile, ")");
|
||||
}
|
||||
CATCH (except, RETURN_MASK_ALL)
|
||||
{
|
||||
GDB_PY_HANDLE_EXCEPTION (except);
|
||||
}
|
||||
END_CATCH
|
||||
}
|
||||
else
|
||||
fprintf_unfiltered (strfile, "<BAD>)");
|
||||
sep = ", ";
|
||||
}
|
||||
fprintf_unfiltered (strfile, ")");
|
||||
}
|
||||
{
|
||||
char *s = ui_file_xstrdup (strfile, NULL);
|
||||
|
||||
result = PyString_FromString (s);
|
||||
xfree (s);
|
||||
}
|
||||
ui_file_delete (strfile);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Create UnwindInfo instance for given PendingFrame and frame ID.
|
||||
Sets Python error and returns NULL on error. */
|
||||
|
||||
static PyObject *
|
||||
pyuw_create_unwind_info (PyObject *pyo_pending_frame,
|
||||
struct frame_id frame_id)
|
||||
{
|
||||
unwind_info_object *unwind_info
|
||||
= PyObject_New (unwind_info_object, &unwind_info_object_type);
|
||||
|
||||
if (((pending_frame_object *) pyo_pending_frame)->frame_info == NULL)
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError,
|
||||
"Attempting to use stale PendingFrame");
|
||||
return NULL;
|
||||
}
|
||||
unwind_info->frame_id = frame_id;
|
||||
Py_INCREF (pyo_pending_frame);
|
||||
unwind_info->pending_frame = pyo_pending_frame;
|
||||
unwind_info->saved_regs = VEC_alloc (saved_reg, 4);
|
||||
return (PyObject *) unwind_info;
|
||||
}
|
||||
|
||||
/* The implementation of
|
||||
gdb.UnwindInfo.add_saved_register (REG, VALUE) -> None. */
|
||||
|
||||
static PyObject *
|
||||
unwind_infopy_add_saved_register (PyObject *self, PyObject *args)
|
||||
{
|
||||
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
||||
pending_frame_object *pending_frame
|
||||
= (pending_frame_object *) (unwind_info->pending_frame);
|
||||
PyObject *pyo_reg_id;
|
||||
PyObject *pyo_reg_value;
|
||||
int regnum;
|
||||
|
||||
if (pending_frame->frame_info == NULL)
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError,
|
||||
"UnwindInfo instance refers to a stale PendingFrame");
|
||||
return NULL;
|
||||
}
|
||||
if (!PyArg_UnpackTuple (args, "previous_frame_register", 2, 2,
|
||||
&pyo_reg_id, &pyo_reg_value))
|
||||
return NULL;
|
||||
if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError, "Bad register");
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
struct value *value;
|
||||
size_t data_size;
|
||||
|
||||
if (pyo_reg_value == NULL
|
||||
|| (value = value_object_to_value (pyo_reg_value)) == NULL)
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError, "Bad register value");
|
||||
return NULL;
|
||||
}
|
||||
data_size = register_size (pending_frame->gdbarch, regnum);
|
||||
if (data_size != TYPE_LENGTH (value_type (value)))
|
||||
{
|
||||
PyErr_Format (
|
||||
PyExc_ValueError,
|
||||
"The value of the register returned by the Python "
|
||||
"sniffer has unexpected size: %u instead of %u.",
|
||||
(unsigned) TYPE_LENGTH (value_type (value)),
|
||||
(unsigned) data_size);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
{
|
||||
int i;
|
||||
saved_reg *reg;
|
||||
|
||||
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
||||
{
|
||||
if (regnum == reg->number)
|
||||
{
|
||||
Py_DECREF (reg->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reg == NULL)
|
||||
{
|
||||
reg = VEC_safe_push (saved_reg, unwind_info->saved_regs, NULL);
|
||||
reg->number = regnum;
|
||||
}
|
||||
Py_INCREF (pyo_reg_value);
|
||||
reg->value = pyo_reg_value;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* UnwindInfo cleanup. */
|
||||
|
||||
static void
|
||||
unwind_infopy_dealloc (PyObject *self)
|
||||
{
|
||||
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
||||
int i;
|
||||
saved_reg *reg;
|
||||
|
||||
Py_XDECREF (unwind_info->pending_frame);
|
||||
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
||||
Py_DECREF (reg->value);
|
||||
VEC_free (saved_reg, unwind_info->saved_regs);
|
||||
Py_TYPE (self)->tp_free (self);
|
||||
}
|
||||
|
||||
/* Called by the Python interpreter to obtain string representation
|
||||
of the PendingFrame object. */
|
||||
|
||||
static PyObject *
|
||||
pending_framepy_str (PyObject *self)
|
||||
{
|
||||
struct frame_info *frame = ((pending_frame_object *) self)->frame_info;
|
||||
const char *sp_str = NULL;
|
||||
const char *pc_str = NULL;
|
||||
|
||||
if (frame == NULL)
|
||||
return PyString_FromString ("Stale PendingFrame instance");
|
||||
TRY
|
||||
{
|
||||
sp_str = core_addr_to_string_nz (get_frame_sp (frame));
|
||||
pc_str = core_addr_to_string_nz (get_frame_pc (frame));
|
||||
}
|
||||
CATCH (except, RETURN_MASK_ALL)
|
||||
{
|
||||
GDB_PY_HANDLE_EXCEPTION (except);
|
||||
}
|
||||
END_CATCH
|
||||
|
||||
return PyString_FromFormat ("SP=%s,PC=%s", sp_str, pc_str);
|
||||
}
|
||||
|
||||
/* Implementation of gdb.PendingFrame.read_register (self, reg) -> gdb.Value.
|
||||
Returns the value of register REG as gdb.Value instance. */
|
||||
|
||||
static PyObject *
|
||||
pending_framepy_read_register (PyObject *self, PyObject *args)
|
||||
{
|
||||
pending_frame_object *pending_frame = (pending_frame_object *) self;
|
||||
struct value *val = NULL;
|
||||
int regnum;
|
||||
PyObject *pyo_reg_id;
|
||||
|
||||
if (pending_frame->frame_info == NULL)
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError,
|
||||
"Attempting to read register from stale PendingFrame");
|
||||
return NULL;
|
||||
}
|
||||
if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id))
|
||||
return NULL;
|
||||
if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError, "Bad register");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRY
|
||||
{
|
||||
val = get_frame_register_value (pending_frame->frame_info, regnum);
|
||||
if (val == NULL)
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
"Cannot read register %d from frame.",
|
||||
regnum);
|
||||
}
|
||||
CATCH (except, RETURN_MASK_ALL)
|
||||
{
|
||||
GDB_PY_HANDLE_EXCEPTION (except);
|
||||
}
|
||||
END_CATCH
|
||||
|
||||
return val == NULL ? NULL : value_to_value_object (val);
|
||||
}
|
||||
|
||||
/* Implementation of
|
||||
PendingFrame.create_unwind_info (self, frameId) -> UnwindInfo. */
|
||||
|
||||
static PyObject *
|
||||
pending_framepy_create_unwind_info (PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *pyo_frame_id;
|
||||
CORE_ADDR sp;
|
||||
CORE_ADDR pc;
|
||||
CORE_ADDR special;
|
||||
|
||||
if (!PyArg_ParseTuple (args, "O:create_unwind_info", &pyo_frame_id))
|
||||
return NULL;
|
||||
if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp))
|
||||
{
|
||||
PyErr_SetString (PyExc_ValueError,
|
||||
_("frame_id should have 'sp' attribute."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The logic of building frame_id depending on the attributes of
|
||||
the frame_id object:
|
||||
Has Has Has Function to call
|
||||
'sp'? 'pc'? 'special'?
|
||||
------|------|--------------|-------------------------
|
||||
Y N * frame_id_build_wild (sp)
|
||||
Y Y N frame_id_build (sp, pc)
|
||||
Y Y Y frame_id_build_special (sp, pc, special)
|
||||
*/
|
||||
if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "pc", &pc))
|
||||
return pyuw_create_unwind_info (self, frame_id_build_wild (sp));
|
||||
if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "special", &special))
|
||||
return pyuw_create_unwind_info (self, frame_id_build (sp, pc));
|
||||
else
|
||||
return pyuw_create_unwind_info (self,
|
||||
frame_id_build_special (sp, pc, special));
|
||||
}
|
||||
|
||||
/* Invalidate PendingFrame instance. */
|
||||
|
||||
static void
|
||||
pending_frame_invalidate (void *pyo_pending_frame)
|
||||
{
|
||||
if (pyo_pending_frame != NULL)
|
||||
((pending_frame_object *) pyo_pending_frame)->frame_info = NULL;
|
||||
}
|
||||
|
||||
/* frame_unwind.this_id method. */
|
||||
|
||||
static void
|
||||
pyuw_this_id (struct frame_info *this_frame, void **cache_ptr,
|
||||
struct frame_id *this_id)
|
||||
{
|
||||
*this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
|
||||
if (pyuw_debug >= 1)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog, "%s: frame_id: ", __FUNCTION__);
|
||||
fprint_frame_id (gdb_stdlog, *this_id);
|
||||
fprintf_unfiltered (gdb_stdlog, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* frame_unwind.prev_register. */
|
||||
|
||||
static struct value *
|
||||
pyuw_prev_register (struct frame_info *this_frame, void **cache_ptr,
|
||||
int regnum)
|
||||
{
|
||||
cached_frame_info *cached_frame = *cache_ptr;
|
||||
struct reg_info *reg_info = cached_frame->reg;
|
||||
struct reg_info *reg_info_end = reg_info + cached_frame->reg_count;
|
||||
|
||||
TRACE_PY_UNWIND (1, "%s (frame=%p,...,reg=%d)\n", __FUNCTION__, this_frame,
|
||||
regnum);
|
||||
for (; reg_info < reg_info_end; ++reg_info)
|
||||
{
|
||||
if (regnum == reg_info->number)
|
||||
return frame_unwind_got_bytes (this_frame, regnum, reg_info->data);
|
||||
}
|
||||
|
||||
return frame_unwind_got_optimized (this_frame, regnum);
|
||||
}
|
||||
|
||||
/* Frame sniffer dispatch. */
|
||||
|
||||
static int
|
||||
pyuw_sniffer (const struct frame_unwind *self, struct frame_info *this_frame,
|
||||
void **cache_ptr)
|
||||
{
|
||||
struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
|
||||
struct cleanup *cleanups = ensure_python_env (gdbarch, current_language);
|
||||
PyObject *pyo_execute;
|
||||
PyObject *pyo_pending_frame;
|
||||
PyObject *pyo_unwind_info;
|
||||
cached_frame_info *cached_frame;
|
||||
|
||||
TRACE_PY_UNWIND (3, "%s (SP=%s, PC=%s)\n", __FUNCTION__,
|
||||
paddress (gdbarch, get_frame_sp (this_frame)),
|
||||
paddress (gdbarch, get_frame_pc (this_frame)));
|
||||
|
||||
/* Create PendingFrame instance to pass to sniffers. */
|
||||
pyo_pending_frame = (PyObject *) PyObject_New (pending_frame_object,
|
||||
&pending_frame_object_type);
|
||||
if (pyo_pending_frame == NULL)
|
||||
goto error;
|
||||
((pending_frame_object *) pyo_pending_frame)->gdbarch = gdbarch;
|
||||
((pending_frame_object *) pyo_pending_frame)->frame_info = this_frame;
|
||||
make_cleanup (pending_frame_invalidate, (void *) pyo_pending_frame);
|
||||
make_cleanup_py_decref (pyo_pending_frame);
|
||||
|
||||
/* Run unwinders. */
|
||||
if (gdb_python_module == NULL
|
||||
|| ! PyObject_HasAttrString (gdb_python_module, "execute_unwinders"))
|
||||
{
|
||||
PyErr_SetString (PyExc_NameError,
|
||||
"Installation error: gdb.execute_unwinders function "
|
||||
"is missing");
|
||||
goto error;
|
||||
}
|
||||
pyo_execute = PyObject_GetAttrString (gdb_python_module, "execute_unwinders");
|
||||
if (pyo_execute == NULL)
|
||||
goto error;
|
||||
make_cleanup_py_decref (pyo_execute);
|
||||
pyo_unwind_info
|
||||
= PyObject_CallFunctionObjArgs (pyo_execute, pyo_pending_frame, NULL);
|
||||
if (pyo_unwind_info == NULL)
|
||||
goto error;
|
||||
make_cleanup_py_decref (pyo_unwind_info);
|
||||
if (pyo_unwind_info == Py_None)
|
||||
goto cannot_unwind;
|
||||
|
||||
/* Received UnwindInfo, cache data. */
|
||||
if (PyObject_IsInstance (pyo_unwind_info,
|
||||
(PyObject *) &unwind_info_object_type) <= 0)
|
||||
error (_("A Unwinder should return gdb.UnwindInfo instance."));
|
||||
|
||||
{
|
||||
unwind_info_object *unwind_info = (unwind_info_object *) pyo_unwind_info;
|
||||
int reg_count = VEC_length (saved_reg, unwind_info->saved_regs);
|
||||
saved_reg *reg;
|
||||
int i;
|
||||
|
||||
cached_frame = xmalloc (sizeof (*cached_frame) +
|
||||
reg_count * sizeof (cached_frame->reg[0]));
|
||||
cached_frame->gdbarch = gdbarch;
|
||||
cached_frame->frame_id = unwind_info->frame_id;
|
||||
cached_frame->reg_count = reg_count;
|
||||
|
||||
/* Populate registers array. */
|
||||
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
||||
{
|
||||
struct value *value = value_object_to_value (reg->value);
|
||||
size_t data_size = register_size (gdbarch, reg->number);
|
||||
|
||||
cached_frame->reg[i].number = reg->number;
|
||||
|
||||
/* `value' validation was done before, just assert. */
|
||||
gdb_assert (value != NULL);
|
||||
gdb_assert (data_size == TYPE_LENGTH (value_type (value)));
|
||||
gdb_assert (data_size <= MAX_REGISTER_SIZE);
|
||||
|
||||
memcpy (cached_frame->reg[i].data, value_contents (value), data_size);
|
||||
}
|
||||
}
|
||||
|
||||
*cache_ptr = cached_frame;
|
||||
do_cleanups (cleanups);
|
||||
return 1;
|
||||
|
||||
error:
|
||||
gdbpy_print_stack ();
|
||||
/* Fallthrough. */
|
||||
cannot_unwind:
|
||||
do_cleanups (cleanups);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Frame cache release shim. */
|
||||
|
||||
static void
|
||||
pyuw_dealloc_cache (struct frame_info *this_frame, void *cache)
|
||||
{
|
||||
TRACE_PY_UNWIND (3, "%s: enter", __FUNCTION__);
|
||||
xfree (cache);
|
||||
}
|
||||
|
||||
struct pyuw_gdbarch_data_type
|
||||
{
|
||||
/* Has the unwinder shim been prepended? */
|
||||
int unwinder_registered;
|
||||
};
|
||||
|
||||
static void *
|
||||
pyuw_gdbarch_data_init (struct gdbarch *gdbarch)
|
||||
{
|
||||
return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct pyuw_gdbarch_data_type);
|
||||
}
|
||||
|
||||
/* New inferior architecture callback: register the Python unwinders
|
||||
intermediary. */
|
||||
|
||||
static void
|
||||
pyuw_on_new_gdbarch (struct gdbarch *newarch)
|
||||
{
|
||||
struct pyuw_gdbarch_data_type *data =
|
||||
gdbarch_data (newarch, pyuw_gdbarch_data);
|
||||
|
||||
if (!data->unwinder_registered)
|
||||
{
|
||||
struct frame_unwind *unwinder
|
||||
= GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind);
|
||||
|
||||
unwinder->type = NORMAL_FRAME;
|
||||
unwinder->stop_reason = default_frame_unwind_stop_reason;
|
||||
unwinder->this_id = pyuw_this_id;
|
||||
unwinder->prev_register = pyuw_prev_register;
|
||||
unwinder->unwind_data = (void *) newarch;
|
||||
unwinder->sniffer = pyuw_sniffer;
|
||||
unwinder->dealloc_cache = pyuw_dealloc_cache;
|
||||
frame_unwind_prepend_unwinder (newarch, unwinder);
|
||||
data->unwinder_registered = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize unwind machinery. */
|
||||
|
||||
int
|
||||
gdbpy_initialize_unwind (void)
|
||||
{
|
||||
int rc;
|
||||
add_setshow_zuinteger_cmd
|
||||
("py-unwind", class_maintenance, &pyuw_debug,
|
||||
_("Set Python unwinder debugging."),
|
||||
_("Show Python unwinder debugging."),
|
||||
_("When non-zero, Python unwinder debugging is enabled."),
|
||||
NULL,
|
||||
NULL,
|
||||
&setdebuglist, &showdebuglist);
|
||||
pyuw_gdbarch_data
|
||||
= gdbarch_data_register_post_init (pyuw_gdbarch_data_init);
|
||||
observer_attach_architecture_changed (pyuw_on_new_gdbarch);
|
||||
|
||||
if (PyType_Ready (&pending_frame_object_type) < 0)
|
||||
return -1;
|
||||
rc = gdb_pymodule_addobject (gdb_module, "PendingFrame",
|
||||
(PyObject *) &pending_frame_object_type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (PyType_Ready (&unwind_info_object_type) < 0)
|
||||
return -1;
|
||||
return gdb_pymodule_addobject (gdb_module, "UnwindInfo",
|
||||
(PyObject *) &unwind_info_object_type);
|
||||
}
|
||||
|
||||
static PyMethodDef pending_frame_object_methods[] =
|
||||
{
|
||||
{ "read_register", pending_framepy_read_register, METH_VARARGS,
|
||||
"read_register (REG) -> gdb.Value\n"
|
||||
"Return the value of the REG in the frame." },
|
||||
{ "create_unwind_info",
|
||||
pending_framepy_create_unwind_info, METH_VARARGS,
|
||||
"create_unwind_info (FRAME_ID) -> gdb.UnwindInfo\n"
|
||||
"Construct UnwindInfo for this PendingFrame, using FRAME_ID\n"
|
||||
"to identify it." },
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject pending_frame_object_type =
|
||||
{
|
||||
PyVarObject_HEAD_INIT (NULL, 0)
|
||||
"gdb.PendingFrame", /* tp_name */
|
||||
sizeof (pending_frame_object), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
0, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
pending_framepy_str, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"GDB PendingFrame object", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
pending_frame_object_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
};
|
||||
|
||||
static PyMethodDef unwind_info_object_methods[] =
|
||||
{
|
||||
{ "add_saved_register",
|
||||
unwind_infopy_add_saved_register, METH_VARARGS,
|
||||
"add_saved_register (REG, VALUE) -> None\n"
|
||||
"Set the value of the REG in the previous frame to VALUE." },
|
||||
{ NULL } /* Sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject unwind_info_object_type =
|
||||
{
|
||||
PyVarObject_HEAD_INIT (NULL, 0)
|
||||
"gdb.UnwindInfo", /* tp_name */
|
||||
sizeof (unwind_info_object), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
unwind_infopy_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
unwind_infopy_str, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
"GDB UnwindInfo object", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
unwind_info_object_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
};
|
|
@ -391,12 +391,14 @@ PyObject *pspace_to_pspace_object (struct program_space *)
|
|||
CPYCHECKER_RETURNS_BORROWED_REF;
|
||||
PyObject *pspy_get_printers (PyObject *, void *);
|
||||
PyObject *pspy_get_frame_filters (PyObject *, void *);
|
||||
PyObject *pspy_get_frame_unwinders (PyObject *, void *);
|
||||
PyObject *pspy_get_xmethods (PyObject *, void *);
|
||||
|
||||
PyObject *objfile_to_objfile_object (struct objfile *)
|
||||
CPYCHECKER_RETURNS_BORROWED_REF;
|
||||
PyObject *objfpy_get_printers (PyObject *, void *);
|
||||
PyObject *objfpy_get_frame_filters (PyObject *, void *);
|
||||
PyObject *objfpy_get_frame_unwinders (PyObject *, void *);
|
||||
PyObject *objfpy_get_xmethods (PyObject *, void *);
|
||||
PyObject *gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw);
|
||||
|
||||
|
@ -491,6 +493,8 @@ int gdbpy_initialize_arch (void)
|
|||
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
|
||||
int gdbpy_initialize_xmethods (void)
|
||||
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
|
||||
int gdbpy_initialize_unwind (void)
|
||||
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
|
||||
|
||||
struct cleanup *make_cleanup_py_decref (PyObject *py);
|
||||
struct cleanup *make_cleanup_py_xdecref (PyObject *py);
|
||||
|
|
|
@ -1821,7 +1821,8 @@ message == an error message without a stack will be printed."),
|
|||
|| gdbpy_initialize_new_objfile_event () < 0
|
||||
|| gdbpy_initialize_clear_objfiles_event () < 0
|
||||
|| gdbpy_initialize_arch () < 0
|
||||
|| gdbpy_initialize_xmethods () < 0)
|
||||
|| gdbpy_initialize_xmethods () < 0
|
||||
|| gdbpy_initialize_unwind () < 0)
|
||||
goto fail;
|
||||
|
||||
gdbpy_to_string_cst = PyString_FromString ("to_string");
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
2015-04-01 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.
|
||||
|
||||
2015-04-01 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdb.threads/manythreads.exp (interrupt_and_wait): Pass $message
|
||||
|
|
24
gdb/testsuite/gdb.python/py-unwind-maint.c
Normal file
24
gdb/testsuite/gdb.python/py-unwind-maint.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2015 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
return i; /* next-line */
|
||||
}
|
64
gdb/testsuite/gdb.python/py-unwind-maint.exp
Normal file
64
gdb/testsuite/gdb.python/py-unwind-maint.exp
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This file is part of the GDB testsuite. It tests Python-based
|
||||
# unwinding CLI.
|
||||
|
||||
load_lib gdb-python.exp
|
||||
|
||||
standard_testfile
|
||||
|
||||
if {[prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Skip all tests if Python scripting is not enabled.
|
||||
if { [skip_python_tests] } { continue }
|
||||
|
||||
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
|
||||
|
||||
if ![runto_main ] then {
|
||||
fail "Can't run to main"
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_test "source ${pyfile}" "Python script imported" "import python scripts"
|
||||
|
||||
gdb_test_sequence "info unwinder" "Show all unwinders" {
|
||||
"Global:"
|
||||
" global_unwinder"
|
||||
"Progspace .*py-unwind-maint:"
|
||||
"py_unwind_maint_ps_unwinder"
|
||||
}
|
||||
|
||||
gdb_breakpoint ${srcfile}:[gdb_get_line_number "next-line"]
|
||||
|
||||
gdb_test_sequence "continue" "Unwinders called" {
|
||||
"py_unwind_maint_ps_unwinder called"
|
||||
"global_unwinder called"
|
||||
}
|
||||
|
||||
gdb_test "disable unwinder global .*" "1 unwinder disabled" "Unwinder disabled"
|
||||
|
||||
gdb_test_sequence "info unwinder" "Show with global unwinder disabled" {
|
||||
"Global:"
|
||||
" global_unwinder \\[disabled\\]"
|
||||
"Progspace .*py-unwind-maint:"
|
||||
" py_unwind_maint_ps_unwinder"
|
||||
}
|
||||
|
||||
gdb_test_sequence "where" "Global unwinder disabled" {
|
||||
"py_unwind_maint_ps_unwinder called\r\n#0 main"
|
||||
}
|
59
gdb/testsuite/gdb.python/py-unwind-maint.py
Normal file
59
gdb/testsuite/gdb.python/py-unwind-maint.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This file is part of the GDB testsuite. It tests python unwinders.
|
||||
|
||||
import re
|
||||
import gdb.types
|
||||
from gdb.unwinder import Unwinder, register_unwinder
|
||||
|
||||
class TestGlobalUnwinder(Unwinder):
|
||||
def __init__(self):
|
||||
super(TestGlobalUnwinder, self).__init__("global_unwinder")
|
||||
|
||||
def __call__(self, unwinder_info):
|
||||
print "%s called" % self.name
|
||||
return None
|
||||
|
||||
class TestProgspaceUnwinder(Unwinder):
|
||||
def __init__(self, name):
|
||||
super(TestProgspaceUnwinder, self).__init__("%s_ps_unwinder" % name)
|
||||
|
||||
def __call__(self, unwinder_info):
|
||||
print "%s called" % self.name
|
||||
return None
|
||||
|
||||
class TestObjfileUnwinder(Unwinder):
|
||||
def __init__(self, name):
|
||||
super(TestObjfileUnwinder, self).__init__("%s_obj_unwinder" % name)
|
||||
|
||||
def __call__(self, unwinder_info):
|
||||
print "%s called" % self.name
|
||||
return None
|
||||
|
||||
|
||||
|
||||
gdb.unwinder.register_unwinder(None, TestGlobalUnwinder())
|
||||
saw_runtime_error = False
|
||||
try:
|
||||
gdb.unwinder.register_unwinder(None, TestGlobalUnwinder(), replace=False)
|
||||
except RuntimeError:
|
||||
saw_runtime_error = True
|
||||
if not saw_runtime_error:
|
||||
raise RuntimeError("Missing runtime error from register_unwinder.")
|
||||
gdb.unwinder.register_unwinder(None, TestGlobalUnwinder(), replace=True)
|
||||
gdb.unwinder.register_unwinder(gdb.current_progspace(),
|
||||
TestProgspaceUnwinder("py_unwind_maint"))
|
||||
print "Python script imported"
|
81
gdb/testsuite/gdb.python/py-unwind.c
Normal file
81
gdb/testsuite/gdb.python/py-unwind.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* This test program is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2015 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This is the test program loaded into GDB by the py-unwind test. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void *
|
||||
swap_value (void **location, void *new_value)
|
||||
{
|
||||
void *old_value = *location;
|
||||
*location = new_value;
|
||||
return old_value;
|
||||
}
|
||||
|
||||
static void
|
||||
bad_layout(void **variable_ptr, void *fp)
|
||||
{
|
||||
fprintf (stderr, "First variable should be allocated one word below "
|
||||
"the frame. Got variable's address %p, frame at %p instead.\n",
|
||||
variable_ptr, fp);
|
||||
abort();
|
||||
}
|
||||
|
||||
#define MY_FRAME (__builtin_frame_address (0))
|
||||
|
||||
static void
|
||||
corrupt_frame_inner (void)
|
||||
{
|
||||
/* Save outer frame address, then corrupt the unwind chain by
|
||||
setting the outer frame address in it to self. This is
|
||||
ABI-specific: the first word of the frame contains previous frame
|
||||
address in amd64. */
|
||||
void *previous_fp = swap_value ((void **) MY_FRAME, MY_FRAME);
|
||||
|
||||
/* Verify the compiler allocates the first local variable one word
|
||||
below frame. This is where the test unwinder expects to find the
|
||||
correct outer frame address. */
|
||||
if (&previous_fp + 1 != (void **) MY_FRAME)
|
||||
bad_layout (&previous_fp + 1, MY_FRAME);
|
||||
|
||||
/* Now restore it so that we can return. The test sets the
|
||||
breakpoint just before this happens, and GDB will not be able to
|
||||
show the backtrace without JIT reader. */
|
||||
swap_value ((void **) MY_FRAME, previous_fp); /* break backtrace-broken */
|
||||
}
|
||||
|
||||
static void
|
||||
corrupt_frame_outer (void)
|
||||
{
|
||||
/* See above for the explanation of the code here. This function
|
||||
corrupts its frame, too, and then calls the inner one. */
|
||||
void *previous_fp = swap_value ((void **) MY_FRAME, MY_FRAME);
|
||||
if (&previous_fp + 1 != (void **) MY_FRAME)
|
||||
bad_layout (&previous_fp, MY_FRAME);
|
||||
corrupt_frame_inner ();
|
||||
swap_value ((void **) MY_FRAME, previous_fp);
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
corrupt_frame_outer ();
|
||||
return 0;
|
||||
}
|
54
gdb/testsuite/gdb.python/py-unwind.exp
Normal file
54
gdb/testsuite/gdb.python/py-unwind.exp
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This file is part of the GDB testsuite. It verifies that frame
|
||||
# unwinders can be implemented in Python.
|
||||
|
||||
load_lib gdb-python.exp
|
||||
|
||||
standard_testfile
|
||||
|
||||
if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Skip all tests if Python scripting is not enabled.
|
||||
if { [skip_python_tests] } { continue }
|
||||
|
||||
# This test runs on a specific platform.
|
||||
if { ! [istarget x86_64-*]} { continue }
|
||||
|
||||
# The following tests require execution.
|
||||
|
||||
if ![runto_main] then {
|
||||
fail "Can't run to main"
|
||||
return 0
|
||||
}
|
||||
|
||||
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
|
||||
|
||||
gdb_breakpoint [gdb_get_line_number "break backtrace-broken"]
|
||||
|
||||
gdb_test "source ${pyfile}" "Python script imported" \
|
||||
"import python scripts"
|
||||
|
||||
gdb_continue_to_breakpoint "break backtrace-broken"
|
||||
gdb_test_sequence "where" "Backtrace restored by unwinder" {
|
||||
"\\r\\n#0 .* corrupt_frame_inner \\(\\) at "
|
||||
"\\r\\n#1 .* corrupt_frame_outer \\(\\) at "
|
||||
"\\r\\n#2 .* main \\(.*\\) at"
|
||||
}
|
||||
|
||||
|
99
gdb/testsuite/gdb.python/py-unwind.py
Normal file
99
gdb/testsuite/gdb.python/py-unwind.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
# Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import gdb
|
||||
from gdb.unwinder import Unwinder
|
||||
|
||||
class FrameId(object):
|
||||
|
||||
def __init__(self, sp, pc):
|
||||
self._sp = sp
|
||||
self._pc = pc
|
||||
|
||||
@property
|
||||
def sp(self):
|
||||
return self._sp
|
||||
|
||||
@property
|
||||
def pc(self):
|
||||
return self._pc
|
||||
|
||||
|
||||
class TestUnwinder(Unwinder):
|
||||
AMD64_RBP = 6
|
||||
AMD64_RSP = 7
|
||||
AMD64_RIP = 16
|
||||
|
||||
def __init__(self):
|
||||
Unwinder.__init__(self, "test unwinder")
|
||||
self.char_ptr_t = gdb.lookup_type("unsigned char").pointer()
|
||||
self.char_ptr_ptr_t = self.char_ptr_t.pointer()
|
||||
|
||||
def _read_word(self, address):
|
||||
return address.cast(self.char_ptr_ptr_t).dereference()
|
||||
|
||||
def __call__(self, pending_frame):
|
||||
"""Test unwinder written in Python.
|
||||
|
||||
This unwinder can unwind the frames that have been deliberately
|
||||
corrupted in a specific way (functions in the accompanying
|
||||
py-unwind.c file do that.)
|
||||
This code is only on AMD64.
|
||||
On AMD64 $RBP points to the innermost frame (unless the code
|
||||
was compiled with -fomit-frame-pointer), which contains the
|
||||
address of the previous frame at offset 0. The functions
|
||||
deliberately corrupt their frames as follows:
|
||||
Before After
|
||||
Corruption: Corruption:
|
||||
+--------------+ +--------------+
|
||||
RBP-8 | | | Previous RBP |
|
||||
+--------------+ +--------------+
|
||||
RBP + Previous RBP | | RBP |
|
||||
+--------------+ +--------------+
|
||||
RBP+8 | Return RIP | | Return RIP |
|
||||
+--------------+ +--------------+
|
||||
Old SP | | | |
|
||||
|
||||
This unwinder recognizes the corrupt frames by checking that
|
||||
*RBP == RBP, and restores previous RBP from the word above it.
|
||||
"""
|
||||
try:
|
||||
# NOTE: the registers in Unwinder API can be referenced
|
||||
# either by name or by number. The code below uses both
|
||||
# to achieve more coverage.
|
||||
bp = pending_frame.read_register("rbp").cast(self.char_ptr_t)
|
||||
if self._read_word(bp) != bp:
|
||||
return None
|
||||
# Found the frame that the test program has corrupted for us.
|
||||
# The correct BP for the outer frame has been saved one word
|
||||
# above, previous IP and SP are at the expected places.
|
||||
previous_bp = self._read_word(bp - 8)
|
||||
previous_ip = self._read_word(bp + 8)
|
||||
previous_sp = bp + 16
|
||||
|
||||
frame_id = FrameId(
|
||||
pending_frame.read_register(TestUnwinder.AMD64_RSP),
|
||||
pending_frame.read_register(TestUnwinder.AMD64_RIP))
|
||||
unwind_info = pending_frame.create_unwind_info(frame_id)
|
||||
unwind_info.add_saved_register(TestUnwinder.AMD64_RBP,
|
||||
previous_bp)
|
||||
unwind_info.add_saved_register("rip", previous_ip)
|
||||
unwind_info.add_saved_register("rsp", previous_sp)
|
||||
return unwind_info
|
||||
except (gdb.error, RuntimeError):
|
||||
return None
|
||||
|
||||
gdb.unwinder.register_unwinder(None, TestUnwinder(), True)
|
||||
print("Python script imported")
|
Loading…
Reference in a new issue