@@ -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,111 @@ 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, create UnwindInfo instance from it>
+ <save frame's ID in UnwindInfo instance by calling one of
+ frame_id_build_xxx methods>
+ return <UnwindInfo instance>
+@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 Sniffer return: UnwindInfo
+
+If sniffer's @code{__call__} method recognizes the frame, it should
+return an instance of @code{gdb.UnwindInfo} class describing it.
+
+@code{UnwindInfo} has the following methods:
+
+@defun gdb.UnwindInfo.__init__ (sniffer_info, previous_frame_registers)
+@var{sniffer_info} is the SnifferInfo instance that has been passed to
+a sniffer on invocation.
+@var{previous_frame_registers} describes 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).
+@end defun
+
+@defun gdb.UnwindInfo.frame_id_build(sp, pc)
+Builds frame ID from the given @var{sp} and @var{pc} values.
+@end defun
+
+@defun gdb.UnwindInfo.frame_id_build_special(sp, pc, special)
+Builds frame ID from the given @var{sp}, @var{pc}, and @var{special}
+values.
+@end defun
+
+@defun gdb.UnwindInfo.frame_id_build_wild(sp)
+Builds frame ID from the given @var{sp} value.
+@end defun
+
+@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 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) 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/>.
+
+"""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,89 @@
+# 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/>.
+
+"""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:
+ gdb.UnwindInfo instance.
+ """
+ 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
+ name already exists.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ RuntimeError: Sniffer name is not unique.
+
+ """
+ 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,743 @@
+/* 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
+ struct frame_info *frame_info;
+} sniffer_info_object;
+
+/* The data we keep for the PyUnwindInfo: sniffer_info, previous
+ * frame's register set and frame ID. */
+
+typedef struct
+{
+ PyObject_HEAD
+
+ /* gdb.SnifferInfo for the frame we are unwinding. */
+ PyObject *sniffer_info;
+
+ /* Previous frame registers tuple (2-tuple per register). */
+ PyObject *previous_frame_registers;
+
+ /* Its ID. */
+ struct frame_id frame_id;
+} 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;
+ } reg[];
+} cached_frame_info;
+
+static PyTypeObject sniffer_info_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("sniffer_info_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;
+
+/* 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)
+ return 0;
+ /* Make a long logic check first. In Python 3.x, internally, all
+ integers are represented as longs. In Python 2.x, there is still
+ a differentiation internally between a PyInt and a PyLong.
+ Explicitly do this long check conversion first. In GDB, for
+ Python 3.x, we #ifdef PyInt = PyLong. This check has to be done
+ first to ensure we do not lose information in the conversion
+ process. */
+ else if (PyLong_Check (pyo_int))
+ {
+ LONGEST l = PyLong_AsLongLong (pyo_int);
+
+ if (PyErr_Occurred ())
+ return 0;
+ long_value = (long)l;
+ if (l != long_value)
+ return 0;
+ }
+ else if (PyInt_Check (pyo_int))
+ {
+ long_value = PyInt_AsLong (pyo_int);
+ if (PyErr_Occurred ())
+ return 0;
+ }
+ else
+ return 0;
+ if (long_value != (int) long_value)
+ return 0;
+ *valuep = (int) long_value;
+ return 1;
+}
+
+/* Parses (REGNUM, REGVALUE) tuple. Returns 1 on success, 0 otherwise. */
+static int
+pyuw_parse_register_tuple(PyObject *pyo_reg,
+ int *reg_num, struct value **reg_value)
+{
+
+ if (pyo_reg != NULL && PyTuple_Check (pyo_reg)
+ && PyTuple_Size (pyo_reg) == 2
+ && pyuw_parse_int (PyTuple_GetItem (pyo_reg, 0), reg_num))
+ {
+ PyObject *pyo_reg_value = PyTuple_GetItem (pyo_reg, 1);
+ if (pyo_reg_value != NULL
+ && (*reg_value = value_object_to_value (pyo_reg_value)) != NULL)
+ return 1;
+ }
+ return 0;
+}
+
+/* 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;
+
+ if (frame == NULL)
+ return PyString_FromString ("Stale SnifferInfo instance");
+ 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 gdb.Value instance. */
+
+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;
+ if (((sniffer_info_object *) self)->frame_info == NULL)
+ error (_("Attempting to read register from stale SnifferInfo"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct frame_info *frame = ((sniffer_info_object *) self)->frame_info;
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ char *error_text = NULL;
+
+ /* Validate regnum to prevent assert in `regcache_cooked_read_value'. */
+ if (user_reg_map_regnum_to_name (gdbarch, regnum) == NULL)
+ error_text = xstrprintf (_("Bad register number: %d."), regnum);
+ else
+ {
+ gdb_byte buffer[MAX_REGISTER_SIZE];
+
+ /* Call `deprecated_frame_register_read' -- calling
+ `value_of_register' would an assert in `get_frame_id'
+ because our frame is incomplete. */
+ if (deprecated_frame_register_read (frame, regnum, buffer))
+ val = value_from_contents (register_type (gdbarch, regnum),
+ buffer);
+ if (val == NULL)
+ error_text = xstrprintf (_("Cannot read register %d from frame."),
+ regnum);
+ }
+ if (error_text != NULL)
+ {
+ 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);
+
+ sniffer_info->frame_info = frame;
+ return (PyObject *) sniffer_info;
+}
+
+/* Invalidate SnifferInfo object. */
+static void
+sniffer_info_invalidate (PyObject *pyo_sniffer_info)
+{
+ if (pyo_sniffer_info == NULL)
+ return;
+ ((sniffer_info_object *) pyo_sniffer_info)->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);
+}
+
+/* Called by the Python interpreter to obtain string representation
+ if the UnwindInfo object. */
+static PyObject *
+unwind_infopy_str (PyObject *self)
+{
+ PyObject *result;
+ struct ui_file *strfile = mem_fileopen ();
+ unwind_info_object *pyo_unwind_info = (unwind_info_object *) self;
+
+ fprintf_unfiltered (strfile, "Frame ID: ");
+ fprint_frame_id (strfile, pyo_unwind_info->frame_id);
+ {
+ int reg_count = PyTuple_Size (pyo_unwind_info->previous_frame_registers);
+ int i;
+ char *sep = "";
+ struct value_print_options opts;
+
+ get_user_print_options (&opts);
+ fprintf_unfiltered (strfile, "\nPrevious frame registers: (");
+ for (i = 0; i < reg_count; i++)
+ {
+ int reg_num;
+ struct value *reg_value;
+ PyObject *pyo_reg = PyTuple_GetItem (
+ pyo_unwind_info->previous_frame_registers,i);
+
+ if (pyuw_parse_register_tuple (pyo_reg, ®_num, ®_value))
+ {
+ fprintf_unfiltered (strfile, "%s(%d, ", sep, reg_num);
+ value_print (reg_value, strfile, &opts);
+ fprintf_unfiltered (strfile, ")");
+ }
+ 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;
+}
+
+/* Convert gdb.Value object to COREADDR. */
+static int
+pyuw_value_obj_to_pointer (PyObject *pyo_value, CORE_ADDR *addr)
+{
+ struct value *value = value_object_to_value (pyo_value);
+
+ if (value == NULL)
+ return 0;
+ *addr = unpack_pointer (value_type (value), value_contents (value));
+ return 1;
+}
+
+/* Implementation of gdb.UnwindInfo.frame_id_build (self, SP, PC) -> None. */
+
+static PyObject *
+unwind_infopy_frame_id_build (PyObject *self, PyObject *args)
+{
+ PyObject *pyo_sp;
+ PyObject *pyo_pc;
+ CORE_ADDR sp;
+ CORE_ADDR pc;
+
+ if (!PyArg_ParseTuple (args, "OO:frame_id_build", &pyo_sp, &pyo_pc)
+ || !pyuw_value_obj_to_pointer (pyo_sp, &sp)
+ || !pyuw_value_obj_to_pointer (pyo_pc, &pc))
+ return NULL;
+
+ ((unwind_info_object *) self)->frame_id = frame_id_build (sp, pc);
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+/* Implementation of
+ gdb.UnwindInfo.frame_id_build_special (self, SP, PC, SPECIAL) -> None. */
+
+static PyObject *
+unwind_infopy_frame_id_build_special (PyObject *self, PyObject *args)
+{
+ PyObject *pyo_sp;
+ PyObject *pyo_pc;
+ PyObject *pyo_special;
+ CORE_ADDR sp;
+ CORE_ADDR pc;
+ CORE_ADDR special;
+
+ if (!PyArg_ParseTuple (args, "OOO:frame_id_build_special",
+ &pyo_sp, &pyo_pc, &pyo_special)
+ || !pyuw_value_obj_to_pointer (pyo_sp, &sp)
+ || !pyuw_value_obj_to_pointer (pyo_pc, &pc)
+ || !pyuw_value_obj_to_pointer (pyo_special, &special))
+ return NULL;
+
+ ((unwind_info_object *) self)->frame_id
+ = frame_id_build_special (sp, pc, special);
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+/* Implementation of gdb.UnwindInfo.frame_build_id_wild (self, SP) -> None. */
+
+static PyObject *
+unwind_infopy_frame_id_build_wild (PyObject *self, PyObject *args)
+{
+ PyObject *pyo_sp;
+ CORE_ADDR sp;
+
+ if (!PyArg_ParseTuple (args, "O:frame_id_build_wild", &pyo_sp)
+ || !pyuw_value_obj_to_pointer (pyo_sp, &sp))
+ return NULL;
+
+ ((unwind_info_object *) self)->frame_id = frame_id_build_wild (sp);
+ Py_INCREF (Py_None);
+ return Py_None;
+}
+
+/* Initialize new UnwindInfo object. */
+static int
+unwind_infopy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *pyo_sniffer_info;
+ PyObject *pyo_regs;
+
+ if (PyArg_UnpackTuple (args, "__init__", 2, 2, &pyo_sniffer_info, &pyo_regs)
+ && (pyo_sniffer_info != NULL
+ && PyObject_IsInstance (pyo_sniffer_info,
+ (PyObject *) &sniffer_info_object_type) > 0)
+ && pyo_regs != NULL
+ && PyTuple_Check (pyo_regs))
+ {
+ unwind_info_object *unwind_info = (unwind_info_object *) self;
+ unwind_info->frame_id = null_frame_id;
+ unwind_info->sniffer_info = pyo_sniffer_info;
+ Py_INCREF (pyo_sniffer_info);
+ unwind_info->previous_frame_registers = pyo_regs;
+ Py_INCREF (pyo_regs);
+ return 0;
+ }
+ return -1;
+}
+
+static void
+unwind_infopy_dealloc (PyObject *self)
+{
+ unwind_info_object *unwind_info = (unwind_info_object *) self;
+ Py_XDECREF (unwind_info->sniffer_info);
+ Py_XDECREF (unwind_info->previous_frame_registers);
+ Py_TYPE (self)->tp_free (self);
+}
+
+/* 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;
+
+ gdbarch = (struct gdbarch *) (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;
+ if (PyObject_IsInstance (pyo_unwind_info,
+ (PyObject *) &unwind_info_object_type) <= 0)
+ error (_("A Sniffer should return gdb.UnwindInfo instance."));
+
+ {
+ unwind_info_object *unwind_info = (unwind_info_object *) pyo_unwind_info;
+ PyObject *pyo_registers = unwind_info->previous_frame_registers;
+ int i;
+ int reg_count;
+ size_t cached_frame_size;
+ size_t gdb_bytes_count;
+ gdb_byte *gdb_data_free, *gdb_data_end;
+
+ if (frame_id_eq (unwind_info->frame_id, null_frame_id))
+ error (_("Sniffer should call one of the frame_id_build methods "
+ "on UnwindInfo instance before returning it."));
+ if (pyo_registers == NULL)
+ goto error;
+ if (!PyTuple_Check (pyo_registers))
+ error (_("Registers passed to UnwindInfo constructor 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->frame_id = unwind_info->frame_id;
+ cached_frame->reg_count = reg_count;
+
+ /* Populate registers array. */
+ for (i = 0; i < reg_count; i++)
+ {
+ struct reg_info *reg = &(cached_frame->reg[i]);
+ struct value *value;
+ size_t data_size;
+
+ if (!pyuw_parse_register_tuple (PyTuple_GetItem (pyo_registers, i),
+ ®->number, &value))
+ error (_("Python sniffer returned bad register tuple: "
+ "item #%d is not a (reg_no, reg_data) tuple "
+ "with integer reg_no and gdb.Value reg_data."), 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);
+
+ /* Should not overflow the buffer. */
+ 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;
+ }
+ }
+
+ *cache_ptr = cached_frame;
+ discard_cleanups (cached_frame_cleanups);
+ do_cleanups (cleanups);
+ sniffer_info_invalidate (pyo_sniffer_info);
+ return 1;
+
+error:
+ do_cleanups (cleanups);
+ sniffer_info_invalidate (pyo_sniffer_info);
+ 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)
+{
+ int rc;
+ 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;
+ rc = gdb_pymodule_addobject (gdb_module, "SnifferInfo",
+ (PyObject *) &sniffer_info_object_type);
+ if (rc)
+ return rc;
+
+ unwind_info_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&unwind_info_object_type) < 0)
+ return -1;
+ return gdb_pymodule_addobject (gdb_module, "UnwindInfo",
+ (PyObject *) &unwind_info_object_type);
+}
+
+static PyMethodDef sniffer_info_object_methods[] =
+{
+ { "read_register", sniffer_infopy_read_register, METH_VARARGS,
+ "read_register (REGNUM) -> gdb.Value\n\
+Return the value of the REGNUM 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 */
+};
+
+
+static PyMethodDef unwind_info_object_methods[] =
+{
+ { "frame_id_build", unwind_infopy_frame_id_build, METH_VARARGS,
+ "frame_id_build (SP, PC) -> None\n"
+ "Build frame ID from given SP and PC." },
+ { "frame_id_build_special", unwind_infopy_frame_id_build_special,
+ METH_VARARGS,
+ "frame_id_build_special (SP, PC, SPECIAL) -> None\n"
+ "Build frame ID from given SP, PC and SPECIAL." },
+ { "frame_id_build_wild", unwind_infopy_frame_id_build_wild, METH_VARARGS,
+ "frame_id_build_wild (SP) -> None\nBuild frame ID from given SP." },
+ { 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 */
+ unwind_infopy_init, /* 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) 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 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) 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,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;
+}
new file mode 100644
@@ -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
+# 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 .* corrupt_frame_inner \\(\\) at "
+ "\\r\\n#1 .* corrupt_frame_outer \\(\\) at "
+ "\\r\\n#2 .* main \\(.*\\) at"
+}
+
+
new file mode 100644
@@ -0,0 +1,81 @@
+# 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.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):
+ """Test sniffer written in Python.
+
+ This sniffer 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 sniffer recognizes the corrupt frames by checking that
+ *RBP == RBP, and restores previous RBP from the word above it.
+ """
+ try:
+ bp = sniffer_info.read_register(
+ TestSniffer.AMD64_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
+ unwind_info = gdb.UnwindInfo(
+ sniffer_info,
+ ((TestSniffer.AMD64_RBP, previous_bp),
+ (TestSniffer.AMD64_RIP, previous_ip),
+ (TestSniffer.AMD64_RSP, previous_sp)))
+ sp = sniffer_info.read_register(TestSniffer.AMD64_RSP)
+ ip = sniffer_info.read_register(TestSniffer.AMD64_RIP)
+ unwind_info.frame_id_build(sp, ip)
+ return unwind_info
+ except (gdb.error, RuntimeError):
+ return None
+
+gdb.sniffer.register_sniffer(None, TestSniffer(), True)
+print("Python script imported")