diff options
author | Sasha Smundak <asmundak@google.com> | 2015-04-01 11:49:12 -0700 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2015-04-01 11:49:12 -0700 |
commit | d11916aa89c43071c08c1f9b4550a01f8eec78e3 (patch) | |
tree | 7befd0c4b5e47eba48fec1b2b03888dcffbadd7b /gdb/python | |
parent | 79730a3b2683dba745663fa3b907f564bee8a0ef (diff) | |
download | binutils-gdb-d11916aa89c43071c08c1f9b4550a01f8eec78e3.tar.gz |
Add support for writing unwinders in Python.
gdb/ChangeLog:
* Makefile.in (SUBDIR_PYTHON_OBJS): Add py-unwind.o.
(SUBDIR_PYTHON_SRCS): Add py-unwind.c.
(py-unwind.o): New recipe.
* NEWS: mention Python frame unwinding.
* data-directory/Makefile.in (PYTHON_FILE_LIST): Add
gdb/unwinder.py and gdb/command/unwinder.py
* python/lib/gdb/__init__.py (packages): Add frame_unwinders
list.
(execute_unwinders): New function.
* python/lib/gdb/command/unwinders.py: New file.
* python/lib/gdb/unwinder.py: New file.
* python/py-objfile.c (objfile_object): Add frame_unwinders field.
(objfpy_dealloc): Decrement frame_unwinders reference count.
(objfpy_initialize): Create frame_unwinders list.
(objfpy_get_frame_unwinders): New function.
(objfpy_set_frame_unwinders): Ditto.
(objfile_getset): Add frame_unwinders attribute to Objfile.
* python/py-progspace.c (pspace_object): Add frame_unwinders field.
(pspy_dealloc): Decrement frame_unwinders reference count.
(pspy_initialize): Create frame_unwinders list.
(pspy_get_frame_unwinders): New function.
(pspy_set_frame_unwinders): Ditto.
(pspy_getset): Add frame_unwinders attribute to gdb.Progspace.
* python/py-unwind.c: New file.
* python/python-internal.h (pspy_get_name_unwinders): New prototype.
(objpy_get_frame_unwinders): New prototype.
(gdbpy_initialize_unwind): New prototype.
* python/python.c (gdbpy_apply_type_printers): Call
gdbpy_initialize_unwind.
gdb/doc/ChangeLog:
* doc/python.texi (Writing a Frame Unwinder in Python): Add
section.
gdb/testsuite/ChangeLog:
* gdb.python/py-unwind-maint.c: New file.
* gdb.python/py-unwind-maint.exp: New test.
* gdb.python/py-unwind-maint.py: New file.
* gdb.python/py-unwind.c: New file.
* gdb.python/py-unwind.exp: New test.
* gdb.python/py-unwind.py: New test.
Diffstat (limited to 'gdb/python')
-rw-r--r-- | gdb/python/lib/gdb/__init__.py | 38 | ||||
-rw-r--r-- | gdb/python/lib/gdb/command/unwinders.py | 198 | ||||
-rw-r--r-- | gdb/python/lib/gdb/unwinder.py | 94 | ||||
-rw-r--r-- | gdb/python/py-objfile.c | 53 | ||||
-rw-r--r-- | gdb/python/py-progspace.c | 53 | ||||
-rw-r--r-- | gdb/python/py-unwind.c | 788 | ||||
-rw-r--r-- | gdb/python/python-internal.h | 4 | ||||
-rw-r--r-- | gdb/python/python.c | 3 |
8 files changed, 1229 insertions, 2 deletions
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 92b06f27e8d..81789e5f6a4 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 00000000000..8530b35d438 --- /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 00000000000..3554e9c2bec --- /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 157d200b433..c9528c31e25 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 93fbc14d097..17da3d17e81 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 00000000000..bcfea4bbe87 --- /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, ®num)) + { + PyErr_SetString (PyExc_ValueError, "Bad register"); + return NULL; + } + { + struct value *value; + size_t data_size; + + if (pyo_reg_value == NULL + || (value = value_object_to_value (pyo_reg_value)) == NULL) + { + PyErr_SetString (PyExc_ValueError, "Bad register value"); + return NULL; + } + data_size = register_size (pending_frame->gdbarch, regnum); + if (data_size != TYPE_LENGTH (value_type (value))) + { + PyErr_Format ( + PyExc_ValueError, + "The value of the register returned by the Python " + "sniffer has unexpected size: %u instead of %u.", + (unsigned) TYPE_LENGTH (value_type (value)), + (unsigned) data_size); + return NULL; + } + } + { + int i; + saved_reg *reg; + + for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++) + { + if (regnum == reg->number) + { + Py_DECREF (reg->value); + break; + } + } + if (reg == NULL) + { + reg = VEC_safe_push (saved_reg, unwind_info->saved_regs, NULL); + reg->number = regnum; + } + Py_INCREF (pyo_reg_value); + reg->value = pyo_reg_value; + } + Py_RETURN_NONE; +} + +/* UnwindInfo cleanup. */ + +static void +unwind_infopy_dealloc (PyObject *self) +{ + unwind_info_object *unwind_info = (unwind_info_object *) self; + int i; + saved_reg *reg; + + Py_XDECREF (unwind_info->pending_frame); + for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++) + Py_DECREF (reg->value); + VEC_free (saved_reg, unwind_info->saved_regs); + Py_TYPE (self)->tp_free (self); +} + +/* Called by the Python interpreter to obtain string representation + of the PendingFrame object. */ + +static PyObject * +pending_framepy_str (PyObject *self) +{ + struct frame_info *frame = ((pending_frame_object *) self)->frame_info; + const char *sp_str = NULL; + const char *pc_str = NULL; + + if (frame == NULL) + return PyString_FromString ("Stale PendingFrame instance"); + TRY + { + sp_str = core_addr_to_string_nz (get_frame_sp (frame)); + pc_str = core_addr_to_string_nz (get_frame_pc (frame)); + } + CATCH (except, RETURN_MASK_ALL) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + END_CATCH + + return PyString_FromFormat ("SP=%s,PC=%s", sp_str, pc_str); +} + +/* Implementation of gdb.PendingFrame.read_register (self, reg) -> gdb.Value. + Returns the value of register REG as gdb.Value instance. */ + +static PyObject * +pending_framepy_read_register (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + struct value *val = NULL; + int regnum; + PyObject *pyo_reg_id; + + if (pending_frame->frame_info == NULL) + { + PyErr_SetString (PyExc_ValueError, + "Attempting to read register from stale PendingFrame"); + return NULL; + } + if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id)) + return NULL; + if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num)) + { + PyErr_SetString (PyExc_ValueError, "Bad register"); + return NULL; + } + + TRY + { + val = get_frame_register_value (pending_frame->frame_info, regnum); + if (val == NULL) + PyErr_Format (PyExc_ValueError, + "Cannot read register %d from frame.", + regnum); + } + CATCH (except, RETURN_MASK_ALL) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + END_CATCH + + return val == NULL ? NULL : value_to_value_object (val); +} + +/* Implementation of + PendingFrame.create_unwind_info (self, frameId) -> UnwindInfo. */ + +static PyObject * +pending_framepy_create_unwind_info (PyObject *self, PyObject *args) +{ + PyObject *pyo_frame_id; + CORE_ADDR sp; + CORE_ADDR pc; + CORE_ADDR special; + + if (!PyArg_ParseTuple (args, "O:create_unwind_info", &pyo_frame_id)) + return NULL; + if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp)) + { + PyErr_SetString (PyExc_ValueError, + _("frame_id should have 'sp' attribute.")); + return NULL; + } + + /* The logic of building frame_id depending on the attributes of + the frame_id object: + Has Has Has Function to call + 'sp'? 'pc'? 'special'? + ------|------|--------------|------------------------- + Y N * frame_id_build_wild (sp) + Y Y N frame_id_build (sp, pc) + Y Y Y frame_id_build_special (sp, pc, special) + */ + if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "pc", &pc)) + return pyuw_create_unwind_info (self, frame_id_build_wild (sp)); + if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "special", &special)) + return pyuw_create_unwind_info (self, frame_id_build (sp, pc)); + else + return pyuw_create_unwind_info (self, + frame_id_build_special (sp, pc, special)); +} + +/* Invalidate PendingFrame instance. */ + +static void +pending_frame_invalidate (void *pyo_pending_frame) +{ + if (pyo_pending_frame != NULL) + ((pending_frame_object *) pyo_pending_frame)->frame_info = NULL; +} + +/* frame_unwind.this_id method. */ + +static void +pyuw_this_id (struct frame_info *this_frame, void **cache_ptr, + struct frame_id *this_id) +{ + *this_id = ((cached_frame_info *) *cache_ptr)->frame_id; + if (pyuw_debug >= 1) + { + fprintf_unfiltered (gdb_stdlog, "%s: frame_id: ", __FUNCTION__); + fprint_frame_id (gdb_stdlog, *this_id); + fprintf_unfiltered (gdb_stdlog, "\n"); + } +} + +/* frame_unwind.prev_register. */ + +static struct value * +pyuw_prev_register (struct frame_info *this_frame, void **cache_ptr, + int regnum) +{ + cached_frame_info *cached_frame = *cache_ptr; + struct reg_info *reg_info = cached_frame->reg; + struct reg_info *reg_info_end = reg_info + cached_frame->reg_count; + + TRACE_PY_UNWIND (1, "%s (frame=%p,...,reg=%d)\n", __FUNCTION__, this_frame, + regnum); + for (; reg_info < reg_info_end; ++reg_info) + { + if (regnum == reg_info->number) + return frame_unwind_got_bytes (this_frame, regnum, reg_info->data); + } + + return frame_unwind_got_optimized (this_frame, regnum); +} + +/* Frame sniffer dispatch. */ + +static int +pyuw_sniffer (const struct frame_unwind *self, struct frame_info *this_frame, + void **cache_ptr) +{ + struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data); + struct cleanup *cleanups = ensure_python_env (gdbarch, current_language); + PyObject *pyo_execute; + PyObject *pyo_pending_frame; + PyObject *pyo_unwind_info; + cached_frame_info *cached_frame; + + TRACE_PY_UNWIND (3, "%s (SP=%s, PC=%s)\n", __FUNCTION__, + paddress (gdbarch, get_frame_sp (this_frame)), + paddress (gdbarch, get_frame_pc (this_frame))); + + /* Create PendingFrame instance to pass to sniffers. */ + pyo_pending_frame = (PyObject *) PyObject_New (pending_frame_object, + &pending_frame_object_type); + if (pyo_pending_frame == NULL) + goto error; + ((pending_frame_object *) pyo_pending_frame)->gdbarch = gdbarch; + ((pending_frame_object *) pyo_pending_frame)->frame_info = this_frame; + make_cleanup (pending_frame_invalidate, (void *) pyo_pending_frame); + make_cleanup_py_decref (pyo_pending_frame); + + /* Run unwinders. */ + if (gdb_python_module == NULL + || ! PyObject_HasAttrString (gdb_python_module, "execute_unwinders")) + { + PyErr_SetString (PyExc_NameError, + "Installation error: gdb.execute_unwinders function " + "is missing"); + goto error; + } + pyo_execute = PyObject_GetAttrString (gdb_python_module, "execute_unwinders"); + if (pyo_execute == NULL) + goto error; + make_cleanup_py_decref (pyo_execute); + pyo_unwind_info + = PyObject_CallFunctionObjArgs (pyo_execute, pyo_pending_frame, NULL); + if (pyo_unwind_info == NULL) + goto error; + make_cleanup_py_decref (pyo_unwind_info); + if (pyo_unwind_info == Py_None) + goto cannot_unwind; + + /* Received UnwindInfo, cache data. */ + if (PyObject_IsInstance (pyo_unwind_info, + (PyObject *) &unwind_info_object_type) <= 0) + error (_("A Unwinder should return gdb.UnwindInfo instance.")); + + { + unwind_info_object *unwind_info = (unwind_info_object *) pyo_unwind_info; + int reg_count = VEC_length (saved_reg, unwind_info->saved_regs); + saved_reg *reg; + int i; + + cached_frame = xmalloc (sizeof (*cached_frame) + + reg_count * sizeof (cached_frame->reg[0])); + cached_frame->gdbarch = gdbarch; + cached_frame->frame_id = unwind_info->frame_id; + cached_frame->reg_count = reg_count; + + /* Populate registers array. */ + for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++) + { + struct value *value = value_object_to_value (reg->value); + size_t data_size = register_size (gdbarch, reg->number); + + cached_frame->reg[i].number = reg->number; + + /* `value' validation was done before, just assert. */ + gdb_assert (value != NULL); + gdb_assert (data_size == TYPE_LENGTH (value_type (value))); + gdb_assert (data_size <= MAX_REGISTER_SIZE); + + memcpy (cached_frame->reg[i].data, value_contents (value), data_size); + } + } + + *cache_ptr = cached_frame; + do_cleanups (cleanups); + return 1; + + error: + gdbpy_print_stack (); + /* Fallthrough. */ + cannot_unwind: + do_cleanups (cleanups); + return 0; +} + +/* Frame cache release shim. */ + +static void +pyuw_dealloc_cache (struct frame_info *this_frame, void *cache) +{ + TRACE_PY_UNWIND (3, "%s: enter", __FUNCTION__); + xfree (cache); +} + +struct pyuw_gdbarch_data_type +{ + /* Has the unwinder shim been prepended? */ + int unwinder_registered; +}; + +static void * +pyuw_gdbarch_data_init (struct gdbarch *gdbarch) +{ + return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct pyuw_gdbarch_data_type); +} + +/* New inferior architecture callback: register the Python unwinders + intermediary. */ + +static void +pyuw_on_new_gdbarch (struct gdbarch *newarch) +{ + struct pyuw_gdbarch_data_type *data = + gdbarch_data (newarch, pyuw_gdbarch_data); + + if (!data->unwinder_registered) + { + struct frame_unwind *unwinder + = GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind); + + unwinder->type = NORMAL_FRAME; + unwinder->stop_reason = default_frame_unwind_stop_reason; + unwinder->this_id = pyuw_this_id; + unwinder->prev_register = pyuw_prev_register; + unwinder->unwind_data = (void *) newarch; + unwinder->sniffer = pyuw_sniffer; + unwinder->dealloc_cache = pyuw_dealloc_cache; + frame_unwind_prepend_unwinder (newarch, unwinder); + data->unwinder_registered = 1; + } +} + +/* Initialize unwind machinery. */ + +int +gdbpy_initialize_unwind (void) +{ + int rc; + add_setshow_zuinteger_cmd + ("py-unwind", class_maintenance, &pyuw_debug, + _("Set Python unwinder debugging."), + _("Show Python unwinder debugging."), + _("When non-zero, Python unwinder debugging is enabled."), + NULL, + NULL, + &setdebuglist, &showdebuglist); + pyuw_gdbarch_data + = gdbarch_data_register_post_init (pyuw_gdbarch_data_init); + observer_attach_architecture_changed (pyuw_on_new_gdbarch); + + if (PyType_Ready (&pending_frame_object_type) < 0) + return -1; + rc = gdb_pymodule_addobject (gdb_module, "PendingFrame", + (PyObject *) &pending_frame_object_type); + if (rc) + return rc; + + if (PyType_Ready (&unwind_info_object_type) < 0) + return -1; + return gdb_pymodule_addobject (gdb_module, "UnwindInfo", + (PyObject *) &unwind_info_object_type); +} + +static PyMethodDef pending_frame_object_methods[] = +{ + { "read_register", pending_framepy_read_register, METH_VARARGS, + "read_register (REG) -> gdb.Value\n" + "Return the value of the REG in the frame." }, + { "create_unwind_info", + pending_framepy_create_unwind_info, METH_VARARGS, + "create_unwind_info (FRAME_ID) -> gdb.UnwindInfo\n" + "Construct UnwindInfo for this PendingFrame, using FRAME_ID\n" + "to identify it." }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject pending_frame_object_type = +{ + PyVarObject_HEAD_INIT (NULL, 0) + "gdb.PendingFrame", /* tp_name */ + sizeof (pending_frame_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + pending_framepy_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "GDB PendingFrame object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pending_frame_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ +}; + +static PyMethodDef unwind_info_object_methods[] = +{ + { "add_saved_register", + unwind_infopy_add_saved_register, METH_VARARGS, + "add_saved_register (REG, VALUE) -> None\n" + "Set the value of the REG in the previous frame to VALUE." }, + { NULL } /* Sentinel */ +}; + +static PyTypeObject unwind_info_object_type = +{ + PyVarObject_HEAD_INIT (NULL, 0) + "gdb.UnwindInfo", /* tp_name */ + sizeof (unwind_info_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + unwind_infopy_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + unwind_infopy_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "GDB UnwindInfo object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + unwind_info_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ +}; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 4c4d32a5ad5..0581b33f4df 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 58c7c92312e..1da63fd5c0f 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"); |