aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python')
-rw-r--r--gdb/python/lib/gdb/__init__.py38
-rw-r--r--gdb/python/lib/gdb/command/unwinders.py198
-rw-r--r--gdb/python/lib/gdb/unwinder.py94
-rw-r--r--gdb/python/py-objfile.c53
-rw-r--r--gdb/python/py-progspace.c53
-rw-r--r--gdb/python/py-unwind.c788
-rw-r--r--gdb/python/python-internal.h4
-rw-r--r--gdb/python/python.c3
8 files changed, 1229 insertions, 2 deletions
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 92b06f2..81789e5 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -28,7 +28,7 @@ class _GdbFile (object):
# These two are needed in Python 3
encoding = "UTF-8"
errors = "strict"
-
+
def close(self):
# Do nothing.
return None
@@ -71,6 +71,42 @@ type_printers = []
xmethods = []
# Initial frame filters.
frame_filters = {}
+# Initial frame unwinders.
+frame_unwinders = []
+
+def execute_unwinders(pending_frame):
+ """Internal function called from GDB to execute all unwinders.
+
+ Runs each currently enabled unwinder until it finds the one that
+ can unwind given frame.
+
+ Arguments:
+ pending_frame: gdb.PendingFrame instance.
+ Returns:
+ gdb.UnwindInfo instance or None.
+ """
+ for objfile in _gdb.objfiles():
+ for unwinder in objfile.frame_unwinders:
+ if unwinder.enabled:
+ unwind_info = unwinder(pending_frame)
+ if unwind_info is not None:
+ return unwind_info
+
+ current_progspace = _gdb.current_progspace()
+ for unwinder in current_progspace.frame_unwinders:
+ if unwinder.enabled:
+ unwind_info = unwinder(pending_frame)
+ if unwind_info is not None:
+ return unwind_info
+
+ for unwinder in frame_unwinders:
+ if unwinder.enabled:
+ unwind_info = unwinder(pending_frame)
+ if unwind_info is not None:
+ return unwind_info
+
+ return None
+
# Convenience variable to GDB's python directory
PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
diff --git a/gdb/python/lib/gdb/command/unwinders.py b/gdb/python/lib/gdb/command/unwinders.py
new file mode 100644
index 0000000..8530b35
--- /dev/null
+++ b/gdb/python/lib/gdb/command/unwinders.py
@@ -0,0 +1,198 @@
+# Unwinder commands.
+# Copyright 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+import re
+
+
+def validate_regexp(exp, idstring):
+ try:
+ return re.compile(exp)
+ except SyntaxError:
+ raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp))
+
+
+def parse_unwinder_command_args(arg):
+ """Internal utility to parse unwinder command argv.
+
+ Arguments:
+ arg: The arguments to the command. The format is:
+ [locus-regexp [name-regexp]]
+
+ Returns:
+ A 2-tuple of compiled regular expressions.
+
+ Raises:
+ SyntaxError: an error processing ARG
+ """
+
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+ raise SyntaxError("Too many arguments.")
+ locus_regexp = ""
+ name_regexp = ""
+ if argc >= 1:
+ locus_regexp = argv[0]
+ if argc >= 2:
+ name_regexp = argv[1]
+ return (validate_regexp(locus_regexp, "locus"),
+ validate_regexp(name_regexp, "unwinder"))
+
+
+class InfoUnwinder(gdb.Command):
+ """GDB command to list unwinders.
+
+ Usage: info unwinder [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ unwinder. If it is omitted, all registered unwinders from all
+ loci are listed. A locus can be 'global', 'progspace' to list
+ the unwinders from the current progspace, or a regular expression
+ matching filenames of objfiles.
+
+ NAME-REGEXP is a regular expression to filter unwinder names. If
+ this omitted for a specified locus, then all registered unwinders
+ in the locus are listed.
+ """
+
+ def __init__(self):
+ super(InfoUnwinder, self).__init__("info unwinder",
+ gdb.COMMAND_STACK)
+
+ def list_unwinders(self, title, unwinders, name_re):
+ """Lists the unwinders whose name matches regexp.
+
+ Arguments:
+ title: The line to print before the list.
+ unwinders: The list of the unwinders.
+ name_re: unwinder name filter.
+ """
+ if not unwinders:
+ return
+ print title
+ for unwinder in unwinders:
+ if name_re.match(unwinder.name):
+ print(" %s%s" % (unwinder.name,
+ "" if unwinder.enabled else " [disabled]"))
+
+ def invoke(self, arg, from_tty):
+ locus_re, name_re = parse_unwinder_command_args(arg)
+ if locus_re.match("global"):
+ self.list_unwinders("Global:", gdb.frame_unwinders,
+ name_re)
+ if locus_re.match("progspace"):
+ cp = gdb.current_progspace()
+ self.list_unwinders("Progspace %s:" % cp.filename,
+ cp.frame_unwinders, name_re)
+ for objfile in gdb.objfiles():
+ if locus_re.match(objfile.filename):
+ self.list_unwinders("Objfile %s:" % objfile.filename,
+ objfile.frame_unwinders, name_re)
+
+
+def do_enable_unwinder1(unwinders, name_re, flag):
+ """Enable/disable unwinders whose names match given regex.
+
+ Arguments:
+ unwinders: The list of unwinders.
+ name_re: Unwinder name filter.
+ flag: Enable/disable.
+
+ Returns:
+ The number of unwinders affected.
+ """
+ total = 0
+ for unwinder in unwinders:
+ if name_re.match(unwinder.name):
+ unwinder.enabled = flag
+ total += 1
+ return total
+
+
+def do_enable_unwinder(arg, flag):
+ """Enable/disable unwinder(s)."""
+ (locus_re, name_re) = parse_unwinder_command_args(arg)
+ total = 0
+ if locus_re.match("global"):
+ total += do_enable_unwinder1(gdb.frame_unwinders, name_re, flag)
+ if locus_re.match("progspace"):
+ total += do_enable_unwinder1(gdb.current_progspace().frame_unwinders,
+ name_re, flag)
+ for objfile in gdb.objfiles():
+ if locus_re.match(objfile.filename):
+ total += do_enable_unwinder1(objfile.frame_unwinders, name_re,
+ flag)
+ print("%d unwinder%s %s" % (total, "" if total == 1 else "s",
+ "enabled" if flag else "disabled"))
+
+
+class EnableUnwinder(gdb.Command):
+ """GDB command to enable unwinders.
+
+ Usage: enable unwinder [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression specifying the unwinders to
+ enable. It can 'global', 'progspace', or the name of an objfile
+ within that progspace.
+
+ NAME_REGEXP is a regular expression to filter unwinder names. If
+ this omitted for a specified locus, then all registered unwinders
+ in the locus are affected.
+
+ """
+
+ def __init__(self):
+ super(EnableUnwinder, self).__init__("enable unwinder",
+ gdb.COMMAND_STACK)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_unwinder(arg, True)
+
+
+class DisableUnwinder(gdb.Command):
+ """GDB command to disable the specified unwinder.
+
+ Usage: disable unwinder [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression specifying the unwinders to
+ disable. It can 'global', 'progspace', or the name of an objfile
+ within that progspace.
+
+ NAME_REGEXP is a regular expression to filter unwinder names. If
+ this omitted for a specified locus, then all registered unwinders
+ in the locus are affected.
+
+ """
+
+ def __init__(self):
+ super(DisableUnwinder, self).__init__("disable unwinder",
+ gdb.COMMAND_STACK)
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+ do_enable_unwinder(arg, False)
+
+
+def register_unwinder_commands():
+ """Installs the unwinder commands."""
+ InfoUnwinder()
+ EnableUnwinder()
+ DisableUnwinder()
+
+
+register_unwinder_commands()
diff --git a/gdb/python/lib/gdb/unwinder.py b/gdb/python/lib/gdb/unwinder.py
new file mode 100644
index 0000000..3554e9c
--- /dev/null
+++ b/gdb/python/lib/gdb/unwinder.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Unwinder class and register_unwinder function."""
+
+import gdb
+
+
+class Unwinder(object):
+ """Base class (or a template) for frame unwinders written in Python.
+
+ An unwinder has a single method __call__ and the attributes
+ described below.
+
+ Attributes:
+ name: The name of the unwinder.
+ enabled: A boolean indicating whether the unwinder is enabled.
+ """
+
+ def __init__(self, name):
+ """Constructor.
+
+ Args:
+ name: An identifying name for the unwinder.
+ """
+ self.name = name
+ self.enabled = True
+
+ def __call__(self, pending_frame):
+ """GDB calls this method to unwind a frame.
+
+ Arguments:
+ pending_frame: gdb.PendingFrame instance.
+
+ Returns:
+ gdb.UnwindInfo instance.
+ """
+ raise NotImplementedError("Unwinder __call__.")
+
+
+def register_unwinder(locus, unwinder, replace=False):
+ """Register unwinder in given locus.
+
+ The unwinder is prepended to the locus's unwinders list. Unwinder
+ name should be unique.
+
+ Arguments:
+ locus: Either an objfile, progspace, or None (in which case
+ the unwinder is registered globally).
+ unwinder: An object of a gdb.Unwinder subclass
+ replace: If True, replaces existing unwinder with the same name.
+ Otherwise, raises exception if unwinder with the same
+ name already exists.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ RuntimeError: Unwinder name is not unique
+ TypeError: Bad locus type
+ """
+ if locus is None:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering global %s unwinder ...\n" % unwinder.name)
+ locus = gdb
+ elif isinstance(locus, gdb.Objfile) or isinstance(locus, gdb.Progspace):
+ if gdb.parameter("verbose"):
+ gdb.write("Registering %s unwinder for %s ...\n" %
+ (unwinder.name, locus.filename))
+ else:
+ raise TypeError("locus should be gdb.Objfile or gdb.Progspace or None")
+
+ i = 0
+ for needle in locus.frame_unwinders:
+ if needle.name == unwinder.name:
+ if replace:
+ del locus.frame_unwinders[i]
+ else:
+ raise RuntimeError("Unwinder %s already exists." %
+ unwinder.name)
+ i += 1
+ locus.frame_unwinders.insert(0, unwinder)
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 157d200..c9528c3 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -42,6 +42,10 @@ typedef struct
/* The frame filter list of functions. */
PyObject *frame_filters;
+
+ /* The list of frame unwinders. */
+ PyObject *frame_unwinders;
+
/* The type-printer list. */
PyObject *type_printers;
@@ -184,6 +188,7 @@ objfpy_dealloc (PyObject *o)
Py_XDECREF (self->dict);
Py_XDECREF (self->printers);
Py_XDECREF (self->frame_filters);
+ Py_XDECREF (self->frame_unwinders);
Py_XDECREF (self->type_printers);
Py_XDECREF (self->xmethods);
Py_TYPE (self)->tp_free (self);
@@ -206,6 +211,10 @@ objfpy_initialize (objfile_object *self)
if (self->frame_filters == NULL)
return 0;
+ self->frame_unwinders = PyList_New (0);
+ if (self->frame_unwinders == NULL)
+ return 0;
+
self->type_printers = PyList_New (0);
if (self->type_printers == NULL)
return 0;
@@ -313,6 +322,48 @@ objfpy_set_frame_filters (PyObject *o, PyObject *filters, void *ignore)
return 0;
}
+/* Return the frame unwinders attribute for this object file. */
+
+PyObject *
+objfpy_get_frame_unwinders (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->frame_unwinders);
+ return self->frame_unwinders;
+}
+
+/* Set this object file's frame unwinders list to UNWINDERS. */
+
+static int
+objfpy_set_frame_unwinders (PyObject *o, PyObject *unwinders, void *ignore)
+{
+ PyObject *tmp;
+ objfile_object *self = (objfile_object *) o;
+
+ if (!unwinders)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("Cannot delete the frame unwinders attribute."));
+ return -1;
+ }
+
+ if (!PyList_Check (unwinders))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("The frame_unwinders attribute must be a list."));
+ return -1;
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+ tmp = self->frame_unwinders;
+ Py_INCREF (unwinders);
+ self->frame_unwinders = unwinders;
+ Py_XDECREF (tmp);
+
+ return 0;
+}
+
/* Get the 'type_printers' attribute. */
static PyObject *
@@ -651,6 +702,8 @@ static PyGetSetDef objfile_getset[] =
"Pretty printers.", NULL },
{ "frame_filters", objfpy_get_frame_filters,
objfpy_set_frame_filters, "Frame Filters.", NULL },
+ { "frame_unwinders", objfpy_get_frame_unwinders,
+ objfpy_set_frame_unwinders, "Frame Unwinders", NULL },
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
"Type printers.", NULL },
{ "xmethods", objfpy_get_xmethods, NULL,
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 93fbc14..17da3d1 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -41,6 +41,10 @@ typedef struct
/* The frame filter list of functions. */
PyObject *frame_filters;
+
+ /* The frame unwinder list. */
+ PyObject *frame_unwinders;
+
/* The type-printer list. */
PyObject *type_printers;
@@ -82,6 +86,7 @@ pspy_dealloc (PyObject *self)
Py_XDECREF (ps_self->dict);
Py_XDECREF (ps_self->printers);
Py_XDECREF (ps_self->frame_filters);
+ Py_XDECREF (ps_self->frame_unwinders);
Py_XDECREF (ps_self->type_printers);
Py_XDECREF (ps_self->xmethods);
Py_TYPE (self)->tp_free (self);
@@ -104,6 +109,10 @@ pspy_initialize (pspace_object *self)
if (self->frame_filters == NULL)
return 0;
+ self->frame_unwinders = PyList_New (0);
+ if (self->frame_unwinders == NULL)
+ return 0;
+
self->type_printers = PyList_New (0);
if (self->type_printers == NULL)
return 0;
@@ -211,6 +220,48 @@ pspy_set_frame_filters (PyObject *o, PyObject *frame, void *ignore)
return 0;
}
+/* Return the list of the frame unwinders for this program space. */
+
+PyObject *
+pspy_get_frame_unwinders (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->frame_unwinders);
+ return self->frame_unwinders;
+}
+
+/* Set this program space's list of the unwinders to UNWINDERS. */
+
+static int
+pspy_set_frame_unwinders (PyObject *o, PyObject *unwinders, void *ignore)
+{
+ PyObject *tmp;
+ pspace_object *self = (pspace_object *) o;
+
+ if (!unwinders)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "cannot delete the frame unwinders list");
+ return -1;
+ }
+
+ if (!PyList_Check (unwinders))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "the frame unwinders attribute must be a list");
+ return -1;
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+ tmp = self->frame_unwinders;
+ Py_INCREF (unwinders);
+ self->frame_unwinders = unwinders;
+ Py_XDECREF (tmp);
+
+ return 0;
+}
+
/* Get the 'type_printers' attribute. */
static PyObject *
@@ -345,6 +396,8 @@ static PyGetSetDef pspace_getset[] =
"Pretty printers.", NULL },
{ "frame_filters", pspy_get_frame_filters, pspy_set_frame_filters,
"Frame filters.", NULL },
+ { "frame_unwinders", pspy_get_frame_unwinders, pspy_set_frame_unwinders,
+ "Frame unwinders.", NULL },
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
"Type printers.", NULL },
{ "xmethods", pspy_get_xmethods, NULL,
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
new file mode 100644
index 0000000..bcfea4b
--- /dev/null
+++ b/gdb/python/py-unwind.c
@@ -0,0 +1,788 @@
+/* Python frame unwinder interface.
+
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "frame-unwind.h"
+#include "gdb_obstack.h"
+#include "gdbcmd.h"
+#include "language.h"
+#include "observer.h"
+#include "python-internal.h"
+#include "regcache.h"
+#include "valprint.h"
+#include "user-regs.h"
+
+#define TRACE_PY_UNWIND(level, args...) if (pyuw_debug >= level) \
+ { fprintf_unfiltered (gdb_stdlog, args); }
+
+typedef struct
+{
+ PyObject_HEAD
+
+ /* Frame we are unwinding. */
+ struct frame_info *frame_info;
+
+ /* Its architecture, passed by the sniffer caller. */
+ struct gdbarch *gdbarch;
+} pending_frame_object;
+
+/* Saved registers array item. */
+
+typedef struct
+{
+ int number;
+ PyObject *value;
+} saved_reg;
+DEF_VEC_O (saved_reg);
+
+/* The data we keep for the PyUnwindInfo: pending_frame, saved registers
+ and frame ID. */
+
+typedef struct
+{
+ PyObject_HEAD
+
+ /* gdb.PendingFrame for the frame we are unwinding. */
+ PyObject *pending_frame;
+
+ /* Its ID. */
+ struct frame_id frame_id;
+
+ /* Saved registers array. */
+ VEC (saved_reg) *saved_regs;
+} unwind_info_object;
+
+/* The data we keep for a frame we can unwind: frame ID and an array of
+ (register_number, register_value) pairs. */
+
+typedef struct
+{
+ /* Frame ID. */
+ struct frame_id frame_id;
+
+ /* GDB Architecture. */
+ struct gdbarch *gdbarch;
+
+ /* Length of the `reg' array below. */
+ int reg_count;
+
+ struct reg_info
+ {
+ /* Register number. */
+ int number;
+
+ /* Register data bytes pointer. */
+ gdb_byte data[MAX_REGISTER_SIZE];
+ } reg[];
+} cached_frame_info;
+
+static PyTypeObject pending_frame_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("pending_frame_object");
+
+static PyTypeObject unwind_info_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("unwind_info_object");
+
+static unsigned int pyuw_debug = 0;
+
+static struct gdbarch_data *pyuw_gdbarch_data;
+
+/* Parses register id, which can be either a number or a name.
+ Returns 1 on success, 0 otherwise. */
+
+static int
+pyuw_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
+ int *reg_num)
+{
+ if (pyo_reg_id == NULL)
+ return 0;
+ if (gdbpy_is_string (pyo_reg_id))
+ {
+ const char *reg_name = gdbpy_obj_to_string (pyo_reg_id);
+
+ if (reg_name == NULL)
+ return 0;
+ *reg_num = user_reg_map_name_to_regnum (gdbarch, reg_name,
+ strlen (reg_name));
+ return *reg_num >= 0;
+ }
+ else if (PyInt_Check (pyo_reg_id))
+ {
+ long value;
+ if (gdb_py_int_as_long (pyo_reg_id, &value) && (int) value == value)
+ {
+ *reg_num = (int) value;
+ return user_reg_map_regnum_to_name (gdbarch, *reg_num) != NULL;
+ }
+ }
+ return 0;
+}
+
+/* Convert gdb.Value instance to inferior's pointer. Return 1 on success,
+ 0 on failure. */
+
+static int
+pyuw_value_obj_to_pointer (PyObject *pyo_value, CORE_ADDR *addr)
+{
+ int rc = 0;
+ struct value *value;
+
+ TRY
+ {
+ if ((value = value_object_to_value (pyo_value)) != NULL)
+ {
+ *addr = unpack_pointer (value_type (value),
+ value_contents (value));
+ rc = 1;
+ }
+ }
+ CATCH (except, RETURN_MASK_ALL)
+ {
+ gdbpy_convert_exception (except);
+ }
+ END_CATCH
+ return rc;
+}
+
+/* Get attribute from an object and convert it to the inferior's
+ pointer value. Return 1 if attribute exists and its value can be
+ converted. Otherwise, if attribute does not exist or its value is
+ None, return 0. In all other cases set Python error and return
+ 0. */
+
+static int
+pyuw_object_attribute_to_pointer (PyObject *pyo, const char *attr_name,
+ CORE_ADDR *addr)
+{
+ int rc = 0;
+
+ if (PyObject_HasAttrString (pyo, attr_name))
+ {
+ PyObject *pyo_value = PyObject_GetAttrString (pyo, attr_name);
+ struct value *value;
+
+ if (pyo_value != NULL && pyo_value != Py_None)
+ {
+ rc = pyuw_value_obj_to_pointer (pyo_value, addr);
+ if (!rc)
+ PyErr_Format (
+ PyExc_ValueError,
+ _("The value of the '%s' attribute is not a pointer."),
+ attr_name);
+ }
+ Py_XDECREF (pyo_value);
+ }
+ return rc;
+}
+
+/* Called by the Python interpreter to obtain string representation
+ of the UnwindInfo object. */
+
+static PyObject *
+unwind_infopy_str (PyObject *self)
+{
+ struct ui_file *strfile = mem_fileopen ();
+ unwind_info_object *unwind_info = (unwind_info_object *) self;
+ pending_frame_object *pending_frame
+ = (pending_frame_object *) (unwind_info->pending_frame);
+ PyObject *result;
+
+ fprintf_unfiltered (strfile, "Frame ID: ");
+ fprint_frame_id (strfile, unwind_info->frame_id);
+ {
+ char *sep = "";
+ int i;
+ struct value_print_options opts;
+ saved_reg *reg;
+
+ get_user_print_options (&opts);
+ fprintf_unfiltered (strfile, "\nSaved registers: (");
+ for (i = 0;
+ i < VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg);
+ i++)
+ {
+ struct value *value = value_object_to_value (reg->value);
+
+ fprintf_unfiltered (strfile, "%s(%d, ", sep, reg->number);
+ if (value != NULL)
+ {
+ TRY
+ {
+ value_print (value, strfile, &opts);
+ fprintf_unfiltered (strfile, ")");
+ }
+ CATCH (except, RETURN_MASK_ALL)
+ {
+ GDB_PY_HANDLE_EXCEPTION (except);
+ }
+ END_CATCH
+ }
+ else
+ fprintf_unfiltered (strfile, "<BAD>)");
+ sep = ", ";
+ }
+ fprintf_unfiltered (strfile, ")");
+ }
+ {
+ char *s = ui_file_xstrdup (strfile, NULL);
+
+ result = PyString_FromString (s);
+ xfree (s);
+ }
+ ui_file_delete (strfile);
+ return result;
+}
+
+/* Create UnwindInfo instance for given PendingFrame and frame ID.
+ Sets Python error and returns NULL on error. */
+
+static PyObject *
+pyuw_create_unwind_info (PyObject *pyo_pending_frame,
+ struct frame_id frame_id)
+{
+ unwind_info_object *unwind_info
+ = PyObject_New (unwind_info_object, &unwind_info_object_type);
+
+ if (((pending_frame_object *) pyo_pending_frame)->frame_info == NULL)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ "Attempting to use stale PendingFrame");
+ return NULL;
+ }
+ unwind_info->frame_id = frame_id;
+ Py_INCREF (pyo_pending_frame);
+ unwind_info->pending_frame = pyo_pending_frame;
+ unwind_info->saved_regs = VEC_alloc (saved_reg, 4);
+ return (PyObject *) unwind_info;
+}
+
+/* The implementation of
+ gdb.UnwindInfo.add_saved_register (REG, VALUE) -> None. */
+
+static PyObject *
+unwind_infopy_add_saved_register (PyObject *self, PyObject *args)
+{
+ unwind_info_object *unwind_info = (unwind_info_object *) self;
+ pending_frame_object *pending_frame
+ = (pending_frame_object *) (unwind_info->pending_frame);
+ PyObject *pyo_reg_id;
+ PyObject *pyo_reg_value;
+ int regnum;
+
+ if (pending_frame->frame_info == NULL)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ "UnwindInfo instance refers to a stale PendingFrame");
+ return NULL;
+ }
+ if (!PyArg_UnpackTuple (args, "previous_frame_register", 2, 2,
+ &pyo_reg_id, &pyo_reg_value))
+ return NULL;
+ if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, &regnum))
+ {
+ PyErr_SetString (PyExc_ValueError, "Bad register");
+ return NULL;
+ }
+ {
+ struct value *value;
+ size_t data_size;
+
+ if (pyo_reg_value == NULL
+ || (value = value_object_to_value (pyo_reg_value)) == NULL)
+ {
+ PyErr_SetString (PyExc_ValueError, "Bad register value");
+ return NULL;
+ }
+ data_size = register_size (pending_frame->gdbarch, regnum);
+ if (data_size != TYPE_LENGTH (value_type (value)))
+ {
+ PyErr_Format (
+ PyExc_ValueError,
+ "The value of the register returned by the Python "
+ "sniffer has unexpected size: %u instead of %u.",
+ (unsigned) TYPE_LENGTH (value_type (value)),
+ (unsigned) data_size);
+ return NULL;
+ }
+ }
+ {
+ int i;
+ saved_reg *reg;
+
+ for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
+ {
+ if (regnum == reg->number)
+ {
+ Py_DECREF (reg->value);
+ break;
+ }
+ }
+ if (reg == NULL)
+ {
+ reg = VEC_safe_push (saved_reg, unwind_info->saved_regs, NULL);
+ reg->number = regnum;
+ }
+ Py_INCREF (pyo_reg_value);
+ reg->value = pyo_reg_value;
+ }
+ Py_RETURN_NONE;
+}
+
+/* UnwindInfo cleanup. */
+
+static void
+unwind_infopy_dealloc (PyObject *self)
+{
+ unwind_info_object *unwind_info = (unwind_info_object *) self;
+ int i;
+ saved_reg *reg;
+
+ Py_XDECREF (unwind_info->pending_frame);
+ for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
+ Py_DECREF (reg->value);
+ VEC_free (saved_reg, unwind_info->saved_regs);
+ Py_TYPE (self)->tp_free (self);
+}
+
+/* Called by the Python interpreter to obtain string representation
+ of the PendingFrame object. */
+
+static PyObject *
+pending_framepy_str (PyObject *self)
+{
+ struct frame_info *frame = ((pending_frame_object *) self)->frame_info;
+ const char *sp_str = NULL;
+ const char *pc_str = NULL;
+
+ if (frame == NULL)
+ return PyString_FromString ("Stale PendingFrame instance");
+ TRY
+ {
+ sp_str = core_addr_to_string_nz (get_frame_sp (frame));
+ pc_str = core_addr_to_string_nz (get_frame_pc (frame));
+ }
+ CATCH (except, RETURN_MASK_ALL)
+ {
+ GDB_PY_HANDLE_EXCEPTION (except);
+ }
+ END_CATCH
+
+ return PyString_FromFormat ("SP=%s,PC=%s", sp_str, pc_str);
+}
+
+/* Implementation of gdb.PendingFrame.read_register (self, reg) -> gdb.Value.
+ Returns the value of register REG as gdb.Value instance. */
+
+static PyObject *
+pending_framepy_read_register (PyObject *self, PyObject *args)
+{
+ pending_frame_object *pending_frame = (pending_frame_object *) self;
+ struct value *val = NULL;
+ int regnum;
+ PyObject *pyo_reg_id;
+
+ if (pending_frame->frame_info == NULL)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ "Attempting to read register from stale PendingFrame");
+ return NULL;
+ }
+ if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id))
+ return NULL;
+ if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, &regnum))
+ {
+ PyErr_SetString (PyExc_ValueError, "Bad register");
+ return NULL;
+ }
+
+ TRY
+ {
+ val = get_frame_register_value (pending_frame->frame_info, regnum);
+ if (val == NULL)
+ PyErr_Format (PyExc_ValueError,
+ "Cannot read register %d from frame.",
+ regnum);
+ }
+ CATCH (except, RETURN_MASK_ALL)
+ {
+ GDB_PY_HANDLE_EXCEPTION (except);
+ }
+ END_CATCH
+
+ return val == NULL ? NULL : value_to_value_object (val);
+}
+
+/* Implementation of
+ PendingFrame.create_unwind_info (self, frameId) -> UnwindInfo. */
+
+static PyObject *
+pending_framepy_create_unwind_info (PyObject *self, PyObject *args)
+{
+ PyObject *pyo_frame_id;
+ CORE_ADDR sp;
+ CORE_ADDR pc;
+ CORE_ADDR special;
+
+ if (!PyArg_ParseTuple (args, "O:create_unwind_info", &pyo_frame_id))
+ return NULL;
+ if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp))
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("frame_id should have 'sp' attribute."));
+ return NULL;
+ }
+
+ /* The logic of building frame_id depending on the attributes of
+ the frame_id object:
+ Has Has Has Function to call
+ 'sp'? 'pc'? 'special'?
+ ------|------|--------------|-------------------------
+ Y N * frame_id_build_wild (sp)
+ Y Y N frame_id_build (sp, pc)
+ Y Y Y frame_id_build_special (sp, pc, special)
+ */
+ if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "pc", &pc))
+ return pyuw_create_unwind_info (self, frame_id_build_wild (sp));
+ if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "special", &special))
+ return pyuw_create_unwind_info (self, frame_id_build (sp, pc));
+ else
+ return pyuw_create_unwind_info (self,
+ frame_id_build_special (sp, pc, special));
+}
+
+/* Invalidate PendingFrame instance. */
+
+static void
+pending_frame_invalidate (void *pyo_pending_frame)
+{
+ if (pyo_pending_frame != NULL)
+ ((pending_frame_object *) pyo_pending_frame)->frame_info = NULL;
+}
+
+/* frame_unwind.this_id method. */
+
+static void
+pyuw_this_id (struct frame_info *this_frame, void **cache_ptr,
+ struct frame_id *this_id)
+{
+ *this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
+ if (pyuw_debug >= 1)
+ {
+ fprintf_unfiltered (gdb_stdlog, "%s: frame_id: ", __FUNCTION__);
+ fprint_frame_id (gdb_stdlog, *this_id);
+ fprintf_unfiltered (gdb_stdlog, "\n");
+ }
+}
+
+/* frame_unwind.prev_register. */
+
+static struct value *
+pyuw_prev_register (struct frame_info *this_frame, void **cache_ptr,
+ int regnum)
+{
+ cached_frame_info *cached_frame = *cache_ptr;
+ struct reg_info *reg_info = cached_frame->reg;
+ struct reg_info *reg_info_end = reg_info + cached_frame->reg_count;
+
+ TRACE_PY_UNWIND (1, "%s (frame=%p,...,reg=%d)\n", __FUNCTION__, this_frame,
+ regnum);
+ for (; reg_info < reg_info_end; ++reg_info)
+ {
+ if (regnum == reg_info->number)
+ return frame_unwind_got_bytes (this_frame, regnum, reg_info->data);
+ }
+
+ return frame_unwind_got_optimized (this_frame, regnum);
+}
+
+/* Frame sniffer dispatch. */
+
+static int
+pyuw_sniffer (const struct frame_unwind *self, struct frame_info *this_frame,
+ void **cache_ptr)
+{
+ struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
+ struct cleanup *cleanups = ensure_python_env (gdbarch, current_language);
+ PyObject *pyo_execute;
+ PyObject *pyo_pending_frame;
+ PyObject *pyo_unwind_info;
+ cached_frame_info *cached_frame;
+
+ TRACE_PY_UNWIND (3, "%s (SP=%s, PC=%s)\n", __FUNCTION__,
+ paddress (gdbarch, get_frame_sp (this_frame)),
+ paddress (gdbarch, get_frame_pc (this_frame)));
+
+ /* Create PendingFrame instance to pass to sniffers. */
+ pyo_pending_frame = (PyObject *) PyObject_New (pending_frame_object,
+ &pending_frame_object_type);
+ if (pyo_pending_frame == NULL)
+ goto error;
+ ((pending_frame_object *) pyo_pending_frame)->gdbarch = gdbarch;
+ ((pending_frame_object *) pyo_pending_frame)->frame_info = this_frame;
+ make_cleanup (pending_frame_invalidate, (void *) pyo_pending_frame);
+ make_cleanup_py_decref (pyo_pending_frame);
+
+ /* Run unwinders. */
+ if (gdb_python_module == NULL
+ || ! PyObject_HasAttrString (gdb_python_module, "execute_unwinders"))
+ {
+ PyErr_SetString (PyExc_NameError,
+ "Installation error: gdb.execute_unwinders function "
+ "is missing");
+ goto error;
+ }
+ pyo_execute = PyObject_GetAttrString (gdb_python_module, "execute_unwinders");
+ if (pyo_execute == NULL)
+ goto error;
+ make_cleanup_py_decref (pyo_execute);
+ pyo_unwind_info
+ = PyObject_CallFunctionObjArgs (pyo_execute, pyo_pending_frame, NULL);
+ if (pyo_unwind_info == NULL)
+ goto error;
+ make_cleanup_py_decref (pyo_unwind_info);
+ if (pyo_unwind_info == Py_None)
+ goto cannot_unwind;
+
+ /* Received UnwindInfo, cache data. */
+ if (PyObject_IsInstance (pyo_unwind_info,
+ (PyObject *) &unwind_info_object_type) <= 0)
+ error (_("A Unwinder should return gdb.UnwindInfo instance."));
+
+ {
+ unwind_info_object *unwind_info = (unwind_info_object *) pyo_unwind_info;
+ int reg_count = VEC_length (saved_reg, unwind_info->saved_regs);
+ saved_reg *reg;
+ int i;
+
+ cached_frame = xmalloc (sizeof (*cached_frame) +
+ reg_count * sizeof (cached_frame->reg[0]));
+ cached_frame->gdbarch = gdbarch;
+ cached_frame->frame_id = unwind_info->frame_id;
+ cached_frame->reg_count = reg_count;
+
+ /* Populate registers array. */
+ for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
+ {
+ struct value *value = value_object_to_value (reg->value);
+ size_t data_size = register_size (gdbarch, reg->number);
+
+ cached_frame->reg[i].number = reg->number;
+
+ /* `value' validation was done before, just assert. */
+ gdb_assert (value != NULL);
+ gdb_assert (data_size == TYPE_LENGTH (value_type (value)));
+ gdb_assert (data_size <= MAX_REGISTER_SIZE);
+
+ memcpy (cached_frame->reg[i].data, value_contents (value), data_size);
+ }
+ }
+
+ *cache_ptr = cached_frame;
+ do_cleanups (cleanups);
+ return 1;
+
+ error:
+ gdbpy_print_stack ();
+ /* Fallthrough. */
+ cannot_unwind:
+ do_cleanups (cleanups);
+ return 0;
+}
+
+/* Frame cache release shim. */
+
+static void
+pyuw_dealloc_cache (struct frame_info *this_frame, void *cache)
+{
+ TRACE_PY_UNWIND (3, "%s: enter", __FUNCTION__);
+ xfree (cache);
+}
+
+struct pyuw_gdbarch_data_type
+{
+ /* Has the unwinder shim been prepended? */
+ int unwinder_registered;
+};
+
+static void *
+pyuw_gdbarch_data_init (struct gdbarch *gdbarch)
+{
+ return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct pyuw_gdbarch_data_type);
+}
+
+/* New inferior architecture callback: register the Python unwinders
+ intermediary. */
+
+static void
+pyuw_on_new_gdbarch (struct gdbarch *newarch)
+{
+ struct pyuw_gdbarch_data_type *data =
+ gdbarch_data (newarch, pyuw_gdbarch_data);
+
+ if (!data->unwinder_registered)
+ {
+ struct frame_unwind *unwinder
+ = GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind);
+
+ unwinder->type = NORMAL_FRAME;
+ unwinder->stop_reason = default_frame_unwind_stop_reason;
+ unwinder->this_id = pyuw_this_id;
+ unwinder->prev_register = pyuw_prev_register;
+ unwinder->unwind_data = (void *) newarch;
+ unwinder->sniffer = pyuw_sniffer;
+ unwinder->dealloc_cache = pyuw_dealloc_cache;
+ frame_unwind_prepend_unwinder (newarch, unwinder);
+ data->unwinder_registered = 1;
+ }
+}
+
+/* Initialize unwind machinery. */
+
+int
+gdbpy_initialize_unwind (void)
+{
+ int rc;
+ add_setshow_zuinteger_cmd
+ ("py-unwind", class_maintenance, &pyuw_debug,
+ _("Set Python unwinder debugging."),
+ _("Show Python unwinder debugging."),
+ _("When non-zero, Python unwinder debugging is enabled."),
+ NULL,
+ NULL,
+ &setdebuglist, &showdebuglist);
+ pyuw_gdbarch_data
+ = gdbarch_data_register_post_init (pyuw_gdbarch_data_init);
+ observer_attach_architecture_changed (pyuw_on_new_gdbarch);
+
+ if (PyType_Ready (&pending_frame_object_type) < 0)
+ return -1;
+ rc = gdb_pymodule_addobject (gdb_module, "PendingFrame",
+ (PyObject *) &pending_frame_object_type);
+ if (rc)
+ return rc;
+
+ if (PyType_Ready (&unwind_info_object_type) < 0)
+ return -1;
+ return gdb_pymodule_addobject (gdb_module, "UnwindInfo",
+ (PyObject *) &unwind_info_object_type);
+}
+
+static PyMethodDef pending_frame_object_methods[] =
+{
+ { "read_register", pending_framepy_read_register, METH_VARARGS,
+ "read_register (REG) -> gdb.Value\n"
+ "Return the value of the REG in the frame." },
+ { "create_unwind_info",
+ pending_framepy_create_unwind_info, METH_VARARGS,
+ "create_unwind_info (FRAME_ID) -> gdb.UnwindInfo\n"
+ "Construct UnwindInfo for this PendingFrame, using FRAME_ID\n"
+ "to identify it." },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject pending_frame_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.PendingFrame", /* tp_name */
+ sizeof (pending_frame_object), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ pending_framepy_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "GDB PendingFrame object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ pending_frame_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+};
+
+static PyMethodDef unwind_info_object_methods[] =
+{
+ { "add_saved_register",
+ unwind_infopy_add_saved_register, METH_VARARGS,
+ "add_saved_register (REG, VALUE) -> None\n"
+ "Set the value of the REG in the previous frame to VALUE." },
+ { NULL } /* Sentinel */
+};
+
+static PyTypeObject unwind_info_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.UnwindInfo", /* tp_name */
+ sizeof (unwind_info_object), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ unwind_infopy_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ unwind_infopy_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "GDB UnwindInfo object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ unwind_info_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 4c4d32a..0581b33 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -391,12 +391,14 @@ PyObject *pspace_to_pspace_object (struct program_space *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *pspy_get_printers (PyObject *, void *);
PyObject *pspy_get_frame_filters (PyObject *, void *);
+PyObject *pspy_get_frame_unwinders (PyObject *, void *);
PyObject *pspy_get_xmethods (PyObject *, void *);
PyObject *objfile_to_objfile_object (struct objfile *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *objfpy_get_printers (PyObject *, void *);
PyObject *objfpy_get_frame_filters (PyObject *, void *);
+PyObject *objfpy_get_frame_unwinders (PyObject *, void *);
PyObject *objfpy_get_xmethods (PyObject *, void *);
PyObject *gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw);
@@ -491,6 +493,8 @@ int gdbpy_initialize_arch (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_xmethods (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_unwind (void)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
struct cleanup *make_cleanup_py_decref (PyObject *py);
struct cleanup *make_cleanup_py_xdecref (PyObject *py);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 58c7c92..1da63fd 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1821,7 +1821,8 @@ message == an error message without a stack will be printed."),
|| gdbpy_initialize_new_objfile_event () < 0
|| gdbpy_initialize_clear_objfiles_event () < 0
|| gdbpy_initialize_arch () < 0
- || gdbpy_initialize_xmethods () < 0)
+ || gdbpy_initialize_xmethods () < 0
+ || gdbpy_initialize_unwind () < 0)
goto fail;
gdbpy_to_string_cst = PyString_FromString ("to_string");