@@ -394,6 +394,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
@@ -433,6 +434,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
@@ -2608,6 +2610,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)
@@ -12,6 +12,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,17 +62,20 @@ PYTHON_FILE_LIST = \
gdb/FrameDecorator.py \
gdb/types.py \
gdb/printing.py \
+ gdb/sniffer.py \
gdb/prompt.py \
gdb/xmethod.py \
gdb/command/__init__.py \
gdb/command/xmethods.py \
gdb/command/frame_filters.py \
+ gdb/command/sniffers.py \
gdb/command/type_printers.py \
gdb/command/pretty_printers.py \
gdb/command/prompt.py \
gdb/command/explore.py \
gdb/function/__init__.py \
gdb/function/caller_is.py \
+ gdb/function/sniffers.py \
gdb/function/strfns.py \
gdb/printer/__init__.py \
gdb/printer/bound_registers.py
@@ -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 a frame unwinder in Python.
* Xmethods In Python:: Adding and replacing methods of C++ classes.
* Xmethod API:: Xmethod types.
* Writing an Xmethod:: Writing an xmethod.
@@ -2178,6 +2179,101 @@ 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 GDB terminology ``unwinding'' is the process of finding the
+previous frame (that is, caller's) from the current one. A running GDB
+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 GDB handle standard frames for each
+platform where GDB is running. However, mixed language applications
+(for example, and application running Java Virtual Machine) sometimes
+use frame layouts that cannot be handled by the GDB unwinders. You
+can write Python code that can handle such custom frames.
+
+You implement a sniffer 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 the data
+describing it (that is, when it recognizes it). GDB comes with the module
+containing the base @code{Sniffer} class for that. The sniffer looks as
+follows:
+@smallexample
+from gdb.sniffers import Sniffer
+
+class MySniffer(Sniffer):
+ def __init__(....):
+ super(MySniffer, self).__init___(<expects sniffer name argument>)
+ def __call__(sniffer_info):
+ if not <we recognize frame>:
+ return None
+ <find previous frame registers>
+ return (<registers>, <frame ID registers>)
+@end smallexample
+
+@subheading Examining The Current Frame
+
+@value{GDBN} passes a @code{gdb.SnifferInfo} instance when it calls
+sniffer's @code{__call__} method. This class has a single method:
+
+@defun SnifferInfo.read_register (self, regnum)
+This method returns the contents of the register @var{regnum} in the
+frame as a @code{gdb.Value} object. @var{regnum} values are
+platform-specific. They are usually defined in the corresponding
+xxx-@code{tdep.h} file in the gdb source tree.
+@end defun
+
+@subheading Returning Previous Frame
+
+If sniffer's @code{__call__} method recognizes the frame, it should
+return a (@var{registers}, @var{frame_id_register_numbers}) tuple.
+
+@var{registers} describe the registers that can be unwound (i.e., the
+registers from the previous frame that have been saved in the current
+frame described by @var{sniffer_info}). It is a tuple where each
+element is a (@var{regnum}, @var{regdata}) 2-tuple. @var{regnum} is
+a register number, and @var{regdata} is register contents (a
+@code{gdb.Value} object).
+
+@var{frame_id_register_numbers} is a tuple specifying the registers
+used to construct frame ID of the returned frame. It is a (@var{sp}),
+(@var{sp}, @var{pc}) or (@var{sp}, @var{pc}, @var{special}) tuple,
+where @var{sp}, @var{pc}, @var{special} are register numbers. The
+referenced registers should be present in @var{registers} tuple. The
+frame ID is constructed by calling
+@code{frame_id_build_wild}(@var{value}(@var{sp})),
+@code{frame_id_build}(@var{value}(@var{sp}), @var{value}(@var{pc})),
+or @code{frame_id_build}(@var{value}(@var{sp}), @var{value}(@var{pc}),
+@var{value}(@var{special})) respectively.
+
+@subheading Registering a Sniffer
+
+An object file, a program space, and the @value{GDBN} proper can have
+sniffers registered with it.
+
+The @code{gdb.sniffers} module provides the function to register a
+sniffer:
+
+@defun gdb.sniffer.register_sniffer (locus, sniffer, replace=False)
+@var{locus} is specifies an object file or a program space to which
+@var{sniffer} is added. Passing @code{None} or @code{gdb} adds
+@var{sniffer} to the @value{GDBN}'s global sniffer list. The newly
+added @var{sniffer} will be called before any other sniffer from the
+same locus. Two sniffers in the same locus cannot have the same
+name. An attempt to add a sniffer with already existing name raises an
+exception unless @var{replace} is @code{True}, in which case the old
+sniffer is deleted.
+@end defun
+
+@subheading Sniffer Precedence
+
+@value{GDBN} first calls the sniffers from all the object files in no
+particular order, then the sniffers from the current program space,
+and finally the sniffers from @value{GDBN}.
+
@node Xmethods In Python
@subsubsection Xmethods In Python
@cindex xmethods in Python
@@ -71,6 +71,8 @@ type_printers = []
xmethods = []
# Initial frame filters.
frame_filters = {}
+# Initial frame sniffers.
+frame_sniffers = []
# Convenience variable to GDB's python directory
PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
new file mode 100644
@@ -0,0 +1,198 @@
+# Sniffer commands.
+# Copyright 2013-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_sniffer_command_args(arg):
+ """Internal utility to parse sniffer 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, "sniffer"))
+
+
+class InfoSniffer(gdb.Command):
+ """GDB command to list sniffers.
+
+ Usage: info sniffer [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ sniffer. If it is omitted, all registered sniffers from all loci
+ are listed. A locus could be 'global', a regular expression
+ matching the current program space's filename, or a regular
+ expression matching filenames of objfiles. Locus could be
+ 'progspace' to specify that only sniffers from the current
+ progspace should be listed.
+
+ NAME-REGEXP is a regular expression to filter sniffer names.
+ If this omitted for a specified locus, then all registered
+ sniffers in the locus are listed.
+ """
+
+ def __init__(self):
+ super(InfoSniffer, self).__init__("info sniffer",
+ gdb.COMMAND_DATA)
+
+ def list_sniffers(self, title, sniffers, name_re):
+ """Lists the sniffers whose name matches regexp.
+
+ Arguments:
+ title: The line to print before the list.
+ sniffers: The list of the sniffers.
+ name_re: sniffer name filter.
+ """
+ if not sniffers:
+ return
+ print title
+ for sniffer in sniffers:
+ if name_re.match(sniffer.name):
+ print(" %s%s" % (sniffer.name,
+ "" if sniffer.enabled else "[disabled]"))
+
+ def invoke(self, arg, from_tty):
+ locus_re, name_re = parse_sniffer_command_args(arg)
+ if locus_re.match("global"):
+ self.list_sniffers("global sniffers:", gdb.frame_sniffers,
+ name_re)
+ if locus_re.match("progspace"):
+ cp = gdb.current_progspace()
+ self.list_sniffers("progspace %s sniffers:" % cp.filename,
+ cp.frame_sniffers, name_re)
+ for objfile in gdb.objfiles():
+ if locus_re.match(objfile.filename):
+ self.list_sniffers("objfile %s sniffers:" % objfile.filename,
+ objfile.frame_sniffers, name_re)
+
+
+def do_enable_sniffer1(sniffers, name_re, flag):
+ """Enable/disable sniffers whose names match given regex.
+
+ Arguments:
+ sniffers: The list of sniffers.
+ name_re: Sniffer name filter.
+ flag: Enable/disable.
+
+ Returns:
+ The number of sniffers affected.
+ """
+ total = 0
+ for sniffer in sniffers:
+ if name_re.match(sniffer.name):
+ sniffer.enabled = flag
+ total += 1
+ return total
+
+
+def do_enable_sniffer(arg, flag):
+ """Enable/disable sniffer(s)."""
+ (locus_re, name_re) = parse_sniffer_command_args(arg)
+ total = 0
+ if locus_re.match("global"):
+ total += do_enable_sniffer1(gdb.frame_sniffers, name_re, flag)
+ if locus_re.match("progspace"):
+ total += do_enable_sniffer1(gdb.current_progspace().frame_sniffers,
+ name_re, flag)
+ for objfile in gdb.objfiles():
+ if locus_re.match(objfile.filename):
+ total += do_enable_sniffer1(objfile.frame_sniffers, name_re,
+ flag)
+ print("%d sniffer%s %s" % (total, "" if total == 1 else "s",
+ "enabled" if flag else "disabled"))
+
+
+class EnableSniffer(gdb.Command):
+ """GDB command to enable sniffers.
+
+ Usage: enable sniffer [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the objects to examine.
+ Loci are "global", the program space's file, and the objfiles within
+ that program space.
+
+ NAME_REGEXP is a regular expression to filter sniffer names.
+ If this omitted for a specified locus, then all registered
+ sniffers in the locus are affected.
+ """
+
+ def __init__(self):
+ super(EnableSniffer, self).__init__("enable sniffer",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_sniffer(arg, True)
+
+
+class DisableSniffer(gdb.Command):
+ """GDB command to disable the specified sniffer.
+
+ Usage: disable sniffer [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the objects to examine.
+ Loci are "global", the program space's file, and the objfiles within
+ that program space.
+
+ NAME_REGEXP is a regular expression to filter sniffer names.
+ If this omitted for a specified locus, then all registered
+ sniffers in the locus are affected.
+ """
+
+ def __init__(self):
+ super(DisableSniffer, self).__init__("disable sniffer",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_sniffer(arg, False)
+
+
+def register_sniffer_commands():
+ """Installs the sniffer commands."""
+ InfoSniffer()
+ EnableSniffer()
+ DisableSniffer()
+
+
+register_sniffer_commands()
new file mode 100644
@@ -0,0 +1,53 @@
+# Copyright (C) 2013-2014 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/>.
+
+"""Internal functions for working with frame sniffers."""
+
+import gdb
+
+
+def execute_sniffers(sniffer_info):
+ """Internal function called from GDB to execute all sniffers.
+
+ Runs each currently enabled sniffer until it finds the one that can
+ unwind given frame.
+
+ Arguments:
+ sniffer_info: an instance of gdb.SnifferInfo.
+ Returns:
+ Unwind info or None. See the description of the value returned
+ by Sniffer.__call__ below.
+ """
+ for objfile in gdb.objfiles():
+ for sniffer in objfile.frame_sniffers:
+ if sniffer.enabled:
+ unwind_info = sniffer.__call__(sniffer_info)
+ if unwind_info is not None:
+ return unwind_info
+
+ current_progspace = gdb.current_progspace()
+ for sniffer in current_progspace.frame_sniffers:
+ if sniffer.enabled:
+ unwind_info = sniffer.__call__(sniffer_info)
+ if unwind_info is not None:
+ return unwind_info
+
+ for sniffer in gdb.frame_sniffers:
+ if sniffer.enabled:
+ unwind_info = sniffer.__call__(sniffer_info)
+ if unwind_info is not None:
+ return unwind_info
+
+ return None
new file mode 100644
@@ -0,0 +1,113 @@
+# Copyright (C) 2013-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/>.
+
+"""Sniffer class and register_sniffer function."""
+
+import gdb
+
+
+class Sniffer(object):
+ """Base class (or a template) for frame sniffers written in Python.
+
+ A sniffer has a single method __call__ and the attributes described below.
+
+ Attributes:
+ name: The name of the sniffer.
+ enabled: A boolean indicating whether the sniffer is enabled.
+ """
+
+ def __init__(self, name):
+ """Constructor.
+
+ Args:
+ name: An identifying name for the sniffer.
+ """
+ self.name = name
+ self.enabled = True
+
+ def __call__(self, sniffer_info):
+ """GDB calls this method to unwind a frame.
+
+ Arguments:
+ sniffer_info: An instance of gdb.SnifferInfo describing the frame.
+
+ Returns:
+ (REG_DATA, FRAME_ID_REGNUMS) tuple, or None if this sniffer cannot
+ unwind the frame.
+ REG_DATA is a tuple containing the registers in the unwound frame.
+ Each element in thr REG_DATA is a (REG_NUM, REG_VALUE) tuple,
+ where REG_NUM is platform-specific register number, and REG_VALUE
+ is register value (a gdb.Value instance).
+ FRAME_ID_REGNUMS is a tuple specifying the registers used
+ to construct the frame ID. For instance, on x86_64,
+ FRAME_ID_REGNUMS is (7, 16), as 7 is the stack pointer
+ register (RSP), and 16 is the instruction pointer (RIP).
+ FRAME_ID_REGNUMS is a (SP), (SP, PC), or (SP, PC, SPECIAL)
+ tuple, where SP, PC, and SPECIAL are register numbers. Depending
+ on the tuple, Python sniffer support code uses internal GDB
+ functions to construct frame ID as follows:
+ (SP) frame_id_build_wild (Value(SP))
+ (SP, PC) frame_id_build (Value(SP), Value(PC))
+ (SP, PC, SPECIAL) frame_id_build_special (Value(SP),
+ Value(PC), Value(SPECIAL)
+ The registers in the FRAME_ID_REGNUMS tuple should be among those
+ returned by REG_DATA.
+ The chapter "Stack Frames" in the GDB Internals Guide describes
+ frame ID.
+ """
+ raise NotImplementedError("Sniffer __call__")
+
+
+def register_sniffer(locus, sniffer, replace=False):
+ """Register sniffer in given locus.
+
+ The sniffer is prepended to the locus's sniffers list. Sniffer
+ name should be unique.
+
+ Arguments:
+ locus: Either an objfile, progspace, or None (in which case
+ the sniffer is registered globally).
+ sniffer: An object of a gdb.Sniffer subclass
+ replace: If True, replaces existing sniffer with the same name.
+ Otherwise, raises exception if sniffer with the same
+ ame already exists.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ ValueError: sniffer name contains ';'.
+ RuntimeError: Sniffer name is not unique.
+
+ """
+ if sniffer.name.find(";") >= 0:
+ raise ValueError("semicolon ';' in printer name")
+ if locus is None:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering global %s sniffer ...\n" % sniffer.name)
+ locus = gdb
+ else:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering %s sniffer for %s ...\n" %
+ (sniffer.name, locus.filename))
+ i = 0
+ for needle in locus.frame_sniffers:
+ if needle.name == sniffer.name:
+ if replace:
+ del locus.frame_sniffers[i]
+ else:
+ raise RuntimeError("sniffer %s already exists" % sniffer.name)
+ i += 1
+ locus.frame_sniffers.insert(0, sniffer)
@@ -42,6 +42,10 @@ typedef struct
/* The frame filter list of functions. */
PyObject *frame_filters;
+
+ /* The frame sniffers list of functions. */
+ PyObject *frame_sniffers;
+
/* The type-printer list. */
PyObject *type_printers;
@@ -181,6 +185,7 @@ objfpy_dealloc (PyObject *o)
Py_XDECREF (self->dict);
Py_XDECREF (self->printers);
Py_XDECREF (self->frame_filters);
+ Py_XDECREF (self->frame_sniffers);
Py_XDECREF (self->type_printers);
Py_XDECREF (self->xmethods);
Py_TYPE (self)->tp_free (self);
@@ -203,6 +208,10 @@ objfpy_initialize (objfile_object *self)
if (self->frame_filters == NULL)
return 0;
+ self->frame_sniffers = PyList_New (0);
+ if (self->frame_sniffers == NULL)
+ return 0;
+
self->type_printers = PyList_New (0);
if (self->type_printers == NULL)
return 0;
@@ -310,6 +319,48 @@ objfpy_set_frame_filters (PyObject *o, PyObject *filters, void *ignore)
return 0;
}
+/* Return the frame sniffers attribute for this object file. */
+
+PyObject *
+objfpy_get_frame_sniffers (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_sniffers);
+ return self->frame_sniffers;
+}
+
+/* Set this object file's frame sniffers list to SNIFFERS. */
+
+static int
+objfpy_set_frame_sniffers (PyObject *o, PyObject *sniffers, void *ignore)
+{
+ PyObject *tmp;
+ objfile_object *self = (objfile_object *) o;
+
+ if (!sniffers)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("Cannot delete the frame sniffers attribute."));
+ return -1;
+ }
+
+ if (!PyList_Check (sniffers))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("The frame_sniffers attribute must be a list."));
+ return -1;
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+ tmp = self->frame_sniffers;
+ Py_INCREF (sniffers);
+ self->frame_sniffers = sniffers;
+ Py_XDECREF (tmp);
+
+ return 0;
+}
+
/* Get the 'type_printers' attribute. */
static PyObject *
@@ -645,6 +696,8 @@ static PyGetSetDef objfile_getset[] =
"Pretty printers.", NULL },
{ "frame_filters", objfpy_get_frame_filters,
objfpy_set_frame_filters, "Frame Filters.", NULL },
+ { "frame_sniffers", objfpy_get_frame_sniffers,
+ objfpy_set_frame_sniffers, "Frame Sniffers", 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 sniffer list. */
+ PyObject *frame_sniffers;
+
/* 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_sniffers);
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_sniffers = PyList_New (0);
+ if (self->frame_sniffers == 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 sniffers for this program space. */
+
+PyObject *
+pspy_get_frame_sniffers (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_sniffers);
+ return self->frame_sniffers;
+}
+
+/* Set this program space's list of the sniffers to SNIFFERS. */
+
+static int
+pspy_set_frame_sniffers (PyObject *o, PyObject *sniffers, void *ignore)
+{
+ PyObject *tmp;
+ pspace_object *self = (pspace_object *) o;
+
+ if (!sniffers)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "cannot delete the frame sniffers list");
+ return -1;
+ }
+
+ if (!PyList_Check (sniffers))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "the frame sniffers attribute must be a list");
+ return -1;
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+ tmp = self->frame_sniffers;
+ Py_INCREF (sniffers);
+ self->frame_sniffers = sniffers;
+ 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_sniffers", pspy_get_frame_sniffers, pspy_set_frame_sniffers,
+ "Frame sniffers.", NULL },
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
"Type printers.", NULL },
{ "xmethods", pspy_get_xmethods, NULL,
new file mode 100644
@@ -0,0 +1,539 @@
+/* Python frame unwinder interface
+
+ Copyright (C) 2013-2014 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 "user-regs.h"
+
+#define TRACE_PY_UNWIND(level, args...) if (pyuw_debug >= level) \
+ { fprintf_unfiltered (gdb_stdlog, args); }
+
+typedef struct
+{
+ PyObject_HEAD
+ struct frame_info *frame_info;
+} sniffer_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
+{
+ struct frame_id frame_id;
+ struct gdbarch *gdbarch;
+ int reg_count;
+ struct reg_info
+ {
+ int number;
+ gdb_byte *data;
+ } reg[];
+} cached_frame_info;
+
+static PyTypeObject sniffer_info_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("sniffer_info_object");
+
+static unsigned int pyuw_debug = 0;
+
+static struct gdbarch_data *pyuw_gdbarch_data;
+
+/* Called by the Python interpreter to obtain string representation
+ of the SnifferInfo object. */
+
+static PyObject *
+sniffer_infopy_str (PyObject *self)
+{
+ char *s;
+ PyObject *result;
+ struct frame_info *frame = ((sniffer_info_object *)self)->frame_info;
+
+ s = xstrprintf ("SP=%s,PC=%s", core_addr_to_string_nz (get_frame_sp (frame)),
+ core_addr_to_string_nz (get_frame_pc (frame)));
+ result = PyString_FromString (s);
+ xfree (s);
+
+ return result;
+}
+
+/* Implementation of gdb.SnifferInfo.read_register (self, regnum) -> gdb.Value.
+ Returns the value of register REGNUM as pointer. */
+
+static PyObject *
+sniffer_infopy_read_register (PyObject *self, PyObject *args)
+{
+ volatile struct gdb_exception except;
+ int regnum;
+ struct value *val = NULL;
+
+ if (!PyArg_ParseTuple (args, "i", ®num))
+ return NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* Calling `value_of_register' will result in infinite recursion.
+ Call `deprecated_frame_register_read' instead. */
+ struct frame_info *frame = ((sniffer_info_object *) self)->frame_info;
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ gdb_byte buffer[MAX_REGISTER_SIZE];
+
+ if (deprecated_frame_register_read (frame, regnum, buffer))
+ val = value_from_contents (register_type (gdbarch, regnum), buffer);
+ if (val == NULL)
+ {
+ char *error_text
+ = xstrprintf (_("Cannot read register %d from frame"), regnum);
+ PyErr_SetString (PyExc_ValueError, error_text);
+ xfree (error_text);
+ }
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ return val == NULL ? NULL : value_to_value_object (val);
+}
+
+/* Create Python SnifferInfo object. */
+
+static PyObject *
+frame_info_to_sniffer_info_object (struct frame_info *frame)
+{
+ sniffer_info_object *sniffer_info
+ = PyObject_New (sniffer_info_object, &sniffer_info_object_type);
+
+ if (sniffer_info != NULL)
+ sniffer_info->frame_info = frame;
+
+ return (PyObject *) sniffer_info;
+}
+
+/* Parse Python Int, saving it at the given address. Returns 1 on success,
+ 0 otherwise. */
+
+static int
+pyuw_parse_int (PyObject *pyo_int, int *valuep)
+{
+ long long_value;
+ if (pyo_int == NULL || !PyInt_Check (pyo_int))
+ return 0;
+ long_value = PyInt_AsLong (pyo_int);
+ if (long_value != (int) long_value)
+ return 0;
+ *valuep = (int) long_value;
+ return 1;
+}
+
+/* Parse given tuple of Python Ints into an array. Returns the number
+ of items in the tuple, or -1 on error (bad tuple element type,
+ array too small). */
+
+static Py_ssize_t
+pyuw_parse_ints (PyObject *pyo_sequence, int *values, Py_ssize_t max_values)
+{
+ Py_ssize_t size;
+ Py_ssize_t i;
+
+ if (! PyTuple_Check (pyo_sequence))
+ return -1;
+ size = PyTuple_Size (pyo_sequence);
+ if (size < 0 || size > max_values)
+ return -1;
+ for (i = 0; i < size; ++i)
+ {
+ if (!pyuw_parse_int (PyTuple_GetItem (pyo_sequence, i), &values[i]))
+ return -1;
+ }
+ return i;
+}
+
+/* Retrieve register value for the cached unwind info as target pointer.
+ Return 1 on success, 0 on failure. */
+
+static int
+pyuw_reg_value (cached_frame_info *cached_frame, int regnum, CORE_ADDR *value)
+{
+ struct reg_info *reg_info = cached_frame->reg;
+ struct reg_info *reg_info_end = reg_info + cached_frame->reg_count;
+
+ for (; reg_info < reg_info_end; ++reg_info)
+ {
+ if (reg_info->number == regnum)
+ {
+ *value = unpack_pointer
+ (register_type (cached_frame->gdbarch, regnum), reg_info->data);
+ return 1;
+ }
+ }
+
+ error (_("Python sniffer uses register #%d for this_id, "
+ "but this register is not available"), regnum);
+}
+
+/* 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);
+}
+
+/* Parse frame ID tuple returned by the sniffer info GDB's frame_id and
+ save it in the cached frame. */
+
+static void
+pyuw_parse_frame_id (cached_frame_info *cached_frame,
+ PyObject *pyo_frame_id_regs)
+{
+ int regno[3];
+ CORE_ADDR sp, pc, special;
+
+ if (!PyTuple_Check (pyo_frame_id_regs))
+ error (_("The second element of the pair returned by a Python "
+ "sniffer should be a tuple"));
+
+ switch (pyuw_parse_ints (pyo_frame_id_regs, regno, ARRAY_SIZE (regno)))
+ {
+ case 1:
+ if (pyuw_reg_value (cached_frame, regno[0], &sp))
+ {
+ cached_frame->frame_id = frame_id_build_wild (sp);
+ return;
+ }
+ break;
+ case 2:
+ if (pyuw_reg_value (cached_frame, regno[0], &sp)
+ || pyuw_reg_value (cached_frame, regno[1], &pc))
+ {
+ cached_frame->frame_id = frame_id_build (sp, pc);
+ return;
+ }
+ break;
+ case 3:
+ if (pyuw_reg_value (cached_frame, regno[0], &sp)
+ || pyuw_reg_value (cached_frame, regno[1], &pc)
+ || pyuw_reg_value (cached_frame, regno[2], &special))
+ {
+ cached_frame->frame_id = frame_id_build_special (sp, pc, special);
+ return;
+ }
+ break;
+ }
+ error (_("Unwinder should return a tuple of 1 to 3 ints in the second item"));
+}
+
+/* Frame sniffer dispatch. */
+
+static int
+pyuw_sniffer (const struct frame_unwind *self, struct frame_info *this_frame,
+ void **cache_ptr)
+{
+ struct gdbarch *gdbarch;
+ struct cleanup *cleanups;
+ struct cleanup *cached_frame_cleanups;
+ PyObject *pyo_module;
+ PyObject *pyo_execute;
+ PyObject *pyo_sniffer_info;
+ PyObject *pyo_unwind_info;
+ cached_frame_info *cached_frame = NULL;
+
+ gdb_assert (*cache_ptr == NULL);
+ gdbarch = (void *)(self->unwind_data);
+ cleanups = ensure_python_env (gdbarch, current_language);
+ 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)));
+ pyo_sniffer_info = frame_info_to_sniffer_info_object (this_frame);
+ if (pyo_sniffer_info == NULL)
+ goto error;
+ make_cleanup_py_decref (pyo_sniffer_info);
+
+ if ((pyo_module = PyImport_ImportModule ("gdb.function.sniffers")) == NULL)
+ goto error;
+ make_cleanup_py_decref (pyo_module);
+
+ pyo_execute = PyObject_GetAttrString (pyo_module, "execute_sniffers");
+ if (pyo_execute == NULL)
+ goto error;
+ make_cleanup_py_decref (pyo_execute);
+
+ pyo_unwind_info
+ = PyObject_CallFunctionObjArgs (pyo_execute, pyo_sniffer_info, NULL);
+ if (pyo_unwind_info == NULL)
+ goto error;
+ make_cleanup_py_decref (pyo_unwind_info);
+ if (pyo_unwind_info == Py_None)
+ goto error;
+
+ /* Unwind_info is a (REGISTERS, FRAME_ID_REGNUMS) tuple. REGISTERS
+ * is a tuple of the (REG_NR, REG_VALUE) tuples. FRAME_ID_REGNUMS is
+ * the tuple of REGNO values. */
+ if (!PyTuple_Check (pyo_unwind_info)
+ || PyTuple_Size (pyo_unwind_info) != 2)
+ error (_("Sniffer should return a pair (REGISTERS, FRAME_ID_REGNUMS)"));
+
+ {
+ PyObject *pyo_registers = PyTuple_GetItem (pyo_unwind_info, 0);
+ int i;
+ int reg_count;
+ size_t cached_frame_size;
+ size_t gdb_bytes_count;
+ gdb_byte *gdb_data_free, *gdb_data_end;
+
+ if (pyo_registers == NULL)
+ goto error;
+ if (!PyTuple_Check (pyo_registers))
+ error (_("The first element of the returned pair should be a tuple"));
+
+ /* Figure out how much space we need to allocate. */
+ reg_count = PyTuple_Size (pyo_registers);
+ if (reg_count <= 0)
+ error (_("Register list should not be empty"));
+ /* We might overestimate `gdb_bytes_count', but it's not worth
+ parsing register numbers twice to calculate the exact number of
+ bytes needed. */
+ gdb_bytes_count = reg_count * MAX_REGISTER_SIZE;
+ cached_frame_size = sizeof (*cached_frame) +
+ reg_count * sizeof (cached_frame->reg[0]) +
+ gdb_bytes_count * sizeof (gdb_byte);
+
+ cached_frame = xmalloc (cached_frame_size);
+ cached_frame_cleanups = make_cleanup (xfree, cached_frame);
+ /* Allocations after this point will be discarded! */
+
+ gdb_data_end = (gdb_byte *)((char *) cached_frame + cached_frame_size);
+ gdb_data_free = gdb_data_end - gdb_bytes_count;
+
+ cached_frame->gdbarch = gdbarch;
+ cached_frame->reg_count = reg_count;
+
+ /* Populate registers array. */
+ for (i = 0; i < reg_count; i++)
+ {
+ PyObject *pyo_reg = PyTuple_GetItem (pyo_registers, i);
+ struct reg_info *reg = &(cached_frame->reg[i]);
+
+ if (pyo_reg == NULL)
+ goto error;
+
+ if (!PyTuple_Check (pyo_reg) || PyTuple_Size (pyo_reg) != 2
+ || !pyuw_parse_int (PyTuple_GetItem (pyo_reg, 0),
+ ®->number))
+ {
+ error (_("Python sniffer returned bad register tuple: "
+ "item #%d is not a (reg_no, reg_data) tuple "
+ "with integer reg_no and reg_data"), i);
+ }
+
+ {
+ PyObject *pyo_reg_value = PyTuple_GetItem (pyo_reg, 1);
+ struct value *value;
+ size_t data_size;
+
+ if (pyo_reg_value == NULL)
+ goto error;
+
+ if ((value = value_object_to_value (pyo_reg_value)) == NULL)
+ error (_("Python sniffer returned bad register tuple: "
+ "item #%d, register value should have type "
+ "gdb.Value type"), i);
+ data_size = register_size (gdbarch, reg->number);
+ if (data_size != TYPE_LENGTH (value_enclosing_type (value)))
+ {
+ error (_("The value of the register #%d returned by the "
+ "Python sniffer has unexpected size: %u instead "
+ "of %u"), reg->number,
+ (unsigned)(TYPE_LENGTH (value_enclosing_type (value))),
+ (unsigned)data_size);
+ }
+ gdb_assert ((gdb_data_free + data_size) <= gdb_data_end);
+ memcpy (gdb_data_free, value_contents (value), data_size);
+ reg->data = gdb_data_free;
+ gdb_data_free += data_size;
+ }
+ }
+ }
+
+ {
+ PyObject *pyo_frame_id_regs = PyTuple_GetItem (pyo_unwind_info, 1);
+ if (pyo_frame_id_regs == NULL)
+ goto error;
+ pyuw_parse_frame_id (cached_frame, pyo_frame_id_regs);
+ }
+
+ *cache_ptr = cached_frame;
+ discard_cleanups (cached_frame_cleanups);
+ do_cleanups (cleanups);
+ return 1;
+
+error:
+ 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 sniffers
+ 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);
+ TRACE_PY_UNWIND (1, "%s: registered unwinder for %s\n", __FUNCTION__,
+ gdbarch_bfd_arch_info (newarch)->printable_name);
+ data->unwinder_registered = 1;
+ }
+}
+
+/* Initialize unwind machinery. */
+
+int
+gdbpy_initialize_unwind (void)
+{
+ add_setshow_zuinteger_cmd
+ ("py-unwind", class_maintenance, &pyuw_debug,
+ _("Set Python unwinder debugging."),
+ _("Show Python unwinder debugging."),
+ _("When non-zero, Pythin 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 (&sniffer_info_object_type) < 0)
+ return -1;
+ return gdb_pymodule_addobject (gdb_module, "SnifferInfo",
+ (PyObject *) &sniffer_info_object_type);
+}
+
+static PyMethodDef sniffer_info_object_methods[] =
+{
+ { "read_register", sniffer_infopy_read_register, METH_VARARGS,
+ "read_register (register_name) -> gdb.Value\n\
+Return the value of the register in the frame." },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject sniffer_info_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.SnifferInfo", /* tp_name */
+ sizeof (sniffer_info_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 */
+ sniffer_infopy_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "GDB snifferInfo object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ sniffer_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 */
+};
@@ -390,12 +390,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_sniffers (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_sniffers (PyObject *, void *);
PyObject *objfpy_get_xmethods (PyObject *, void *);
PyObject *gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw);
@@ -490,6 +492,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);
@@ -1790,7 +1790,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");
new file mode 100644
@@ -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 */
+}
new file mode 100644
@@ -0,0 +1,64 @@
+# Copyright (C) 2010-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
+# pretty-printing for the 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 sniffer" "Show all sniffers" {
+ "global sniffers:"
+ " global_sniffer"
+ "progspace.*sniffers:"
+ "py_unwind_maint_ps_sniffer"
+}
+
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "next-line"]
+
+gdb_test_sequence "continue" "Sniffers called" {
+ "py_unwind_maint_ps_sniffer called"
+ "global_sniffer called"
+}
+
+gdb_test "disable sniffer global .*" "1 sniffer disabled" "Sniffer disabled"
+
+gdb_test_sequence "info sniffer" "Show with global sniffer disabled" {
+ "global sniffers:"
+ " global_sniffer\\[disabled\\]"
+ "progspace.*sniffers:"
+ " py_unwind_maint_ps_sniffer"
+}
+
+gdb_test_sequence "where" "Global sniffer disabled" {
+ "py_unwind_maint_ps_sniffer called\r\n#0 main"
+}
new file mode 100644
@@ -0,0 +1,59 @@
+# Copyright (C) 2010-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 sniffers.
+
+import re
+import gdb.types
+from gdb.sniffer import Sniffer, register_sniffer
+
+class TestGlobalSniffer(Sniffer):
+ def __init__(self):
+ super(TestGlobalSniffer, self).__init__("global_sniffer")
+
+ def __call__(self, sniffer_info):
+ print "%s called" % self.name
+ return None
+
+class TestProgspaceSniffer(Sniffer):
+ def __init__(self, name):
+ super(TestProgspaceSniffer, self).__init__("%s_ps_sniffer" % name)
+
+ def __call__(self, sniffer_info):
+ print "%s called" % self.name
+ return None
+
+class TestObjfileSniffer(Sniffer):
+ def __init__(self, name):
+ super(TestObjfileSniffer, self).__init__("%s_obj_sniffer" % name)
+
+ def __call__(self, sniffer_info):
+ print "%s called" % self.name
+ return None
+
+
+
+gdb.sniffer.register_sniffer(gdb, TestGlobalSniffer())
+saw_runtime_error = False
+try:
+ gdb.sniffer.register_sniffer(gdb, TestGlobalSniffer(), replace=False)
+except RuntimeError:
+ saw_runtime_error = True
+if not saw_runtime_error:
+ raise RuntimeError("Missing runtime error from register_sniffer")
+gdb.sniffer.register_sniffer(gdb, TestGlobalSniffer(), replace=True)
+gdb.sniffer.register_sniffer(gdb.current_progspace(),
+ TestProgspaceSniffer("py_unwind_maint"))
+print "Python script imported"
new file mode 100644
@@ -0,0 +1,70 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2011-2014 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;
+}
+
+#define MY_FRAME (__builtin_frame_address (0))
+
+static void
+break_backtrace (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 *outer_fp = swap_value ((void **) MY_FRAME, MY_FRAME);
+
+ /* Verify the compiler allocates the first local variable one word
+ below frame. This is where test JIT reader expects to find the
+ correct outer frame address. */
+ if (&outer_fp + 1 != (void **) MY_FRAME)
+ {
+ fprintf (stderr, "First variable should be allocated one word below "
+ "the frame, got variable's address %p, frame at %p instead\n",
+ &outer_fp, MY_FRAME);
+ abort ();
+ }
+
+ /* 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, outer_fp); /* break backtrace-broken */
+}
+
+static void
+break_backtrace_caller (void)
+{
+ break_backtrace ();
+}
+
+int
+main ()
+{
+ break_backtrace_caller ();
+}
new file mode 100644
@@ -0,0 +1,54 @@
+# Copyright (C) 2009-2014 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
+# sniffers 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 sniffer" {
+ "\\r\\n#0 .* break_backtrace \\(\\) at "
+ "\\r\\n#1 .* break_backtrace_caller \\(\\) at "
+ "\\r\\n#2 .* main \\(.*\\) at"
+}
+
+
new file mode 100644
@@ -0,0 +1,51 @@
+# Copyright (C) 2013-2014 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.sniffer import Sniffer
+
+class TestSniffer(Sniffer):
+ AMD64_RBP = 6
+ AMD64_RSP = 7
+ AMD64_RIP = 16
+
+ def __init__(self):
+ Sniffer.__init__(self, "test sniffer")
+ 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, sniffer_info):
+ "Sniffer written in Python."
+ bp = sniffer_info.read_register(
+ TestSniffer.AMD64_RBP).cast(self.char_ptr_t)
+ try:
+ if self._read_word(bp) != bp:
+ return None
+ # Found the frame that the test program fudged 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_sp = bp + 16
+ return (((TestSniffer.AMD64_RBP, self._read_word(bp - 8)),
+ (TestSniffer.AMD64_RIP, self._read_word(bp + 8)),
+ (TestSniffer.AMD64_RSP, bp + 16)),
+ (TestSniffer.AMD64_RSP, TestSniffer.AMD64_RIP))
+ except (gdb.error, RuntimeError):
+ return None
+
+gdb.sniffer.register_sniffer(None, TestSniffer())
+print("Python script imported")