aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
diff options
context:
space:
mode:
authorAndrew Burgess <andrew.burgess@embecosm.com>2020-06-05 17:52:10 +0100
committerAndrew Burgess <andrew.burgess@embecosm.com>2020-07-06 15:06:06 +0100
commit0f767f942b027df6de60c42ed0e4a1dac7d0fd4b (patch)
tree8622c96b1d3daffec478f33519bf2011c87e63ac /gdb/python
parent87dbc77459930f8f65a6d7d1e1db498da4aa74d6 (diff)
downloadfsf-binutils-gdb-0f767f942b027df6de60c42ed0e4a1dac7d0fd4b.zip
fsf-binutils-gdb-0f767f942b027df6de60c42ed0e4a1dac7d0fd4b.tar.gz
fsf-binutils-gdb-0f767f942b027df6de60c42ed0e4a1dac7d0fd4b.tar.bz2
gdb/python: Add gdb.Architecture.registers method
This commit adds a new method gdb.Architecture.registers that returns an object of the new type gdb.RegisterDescriptorIterator. This iterator returns objects of the new type gdb.RegisterDescriptor. A RegisterDescriptor is not a way to read the value of a register, this is already covered by Frame.read_register, a RegisterDescriptor is simply a way to discover from Python, which registers are available for a given architecture. I did consider just returning a string, the name of each register, instead of a RegisterDescriptor, however, I'm aware that it we don't want to break the existing Python API in any way, so if I return just a string now, but in the future we want more information about a register then we would have to add a second API to get that information. By going straight to a descriptor object now, it is easy to add additional properties in the future should we wish to. Right now the only property of a register that a user can access is the name of the register. In future we might want to be able to ask the register about is register groups, or its type. gdb/ChangeLog: * Makefile.in (SUBDIR_PYTHON_SRCS): Add py-registers.c * python/py-arch.c (archpy_registers): New function. (arch_object_methods): Add 'registers' method. * python/py-registers.c: New file. * python/python-internal.h (gdbpy_new_register_descriptor_iterator): Declare. (gdbpy_initialize_registers): Declare. * python/python.c (do_start_initialization): Call gdbpy_initialize_registers. * NEWS: Mention additions to the Python API. gdb/testsuite/ChangeLog: * gdb.python/py-arch-reg-names.exp: New file. gdb/doc/ChangeLog: * python.texi (Python API): Add new section the menu. (Frames In Python): Add new @anchor. (Architectures In Python): Document new registers method. (Registers In Python): New section.
Diffstat (limited to 'gdb/python')
-rw-r--r--gdb/python/py-arch.c27
-rw-r--r--gdb/python/py-registers.c269
-rw-r--r--gdb/python/python-internal.h5
-rw-r--r--gdb/python/python.c1
4 files changed, 302 insertions, 0 deletions
diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c
index 853c7a9..15f9f50 100644
--- a/gdb/python/py-arch.c
+++ b/gdb/python/py-arch.c
@@ -226,6 +226,28 @@ archpy_disassemble (PyObject *self, PyObject *args, PyObject *kw)
return result_list.release ();
}
+/* Implementation of gdb.Architecture.registers (self, reggroup) -> Iterator.
+ Returns an iterator over register descriptors for registers in GROUP
+ within the architecture SELF. */
+
+static PyObject *
+archpy_registers (PyObject *self, PyObject *args, PyObject *kw)
+{
+ static const char *keywords[] = { "reggroup", NULL };
+ struct gdbarch *gdbarch = NULL;
+ const char *group_name = NULL;
+
+ /* Parse method arguments. */
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "|s", keywords,
+ &group_name))
+ return NULL;
+
+ /* Extract the gdbarch from the self object. */
+ ARCHPY_REQUIRE_VALID (self, gdbarch);
+
+ return gdbpy_new_register_descriptor_iterator (gdbarch, group_name);
+}
+
/* Initializes the Architecture class in the gdb module. */
int
@@ -249,6 +271,11 @@ Return the name of the architecture as a string value." },
"disassemble (start_pc [, end_pc [, count]]) -> List.\n\
Return a list of at most COUNT disassembled instructions from START_PC to\n\
END_PC." },
+ { "registers", (PyCFunction) archpy_registers,
+ METH_VARARGS | METH_KEYWORDS,
+ "registers ([ group-name ]) -> Iterator.\n\
+Return an iterator of register descriptors for the registers in register\n\
+group GROUP-NAME." },
{NULL} /* Sentinel */
};
diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c
new file mode 100644
index 0000000..6ccd17e
--- /dev/null
+++ b/gdb/python/py-registers.c
@@ -0,0 +1,269 @@
+/* Python interface to register, and register group information.
+
+ Copyright (C) 2020 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 "gdbarch.h"
+#include "arch-utils.h"
+#include "disasm.h"
+#include "reggroups.h"
+#include "python-internal.h"
+
+/* Structure for iterator over register descriptors. */
+typedef struct {
+ PyObject_HEAD
+
+ /* The register group that the user is iterating over. This will never
+ be NULL. */
+ struct reggroup *reggroup;
+
+ /* The next register number to lookup. Starts at 0 and counts up. */
+ int regnum;
+
+ /* Pointer back to the architecture we're finding registers for. */
+ struct gdbarch *gdbarch;
+} register_descriptor_iterator_object;
+
+extern PyTypeObject register_descriptor_iterator_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("register_descriptor_iterator_object");
+
+/* A register descriptor. */
+typedef struct {
+ PyObject_HEAD
+
+ /* The register this is a descriptor for. */
+ int regnum;
+
+ /* The architecture this is a register for. */
+ struct gdbarch *gdbarch;
+} register_descriptor_object;
+
+extern PyTypeObject register_descriptor_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("register_descriptor_object");
+
+/* Create an return a new gdb.RegisterDescriptor object. */
+static PyObject *
+gdbpy_new_register_descriptor (struct gdbarch *gdbarch,
+ int regnum)
+{
+ /* Create a new object and fill in its details. */
+ register_descriptor_object *reg
+ = PyObject_New (register_descriptor_object,
+ &register_descriptor_object_type);
+ if (reg == NULL)
+ return NULL;
+ reg->regnum = regnum;
+ reg->gdbarch = gdbarch;
+ return (PyObject *) reg;
+}
+
+/* Convert the register descriptor to a string. */
+
+static PyObject *
+gdbpy_register_descriptor_to_string (PyObject *self)
+{
+ register_descriptor_object *reg
+ = (register_descriptor_object *) self;
+ struct gdbarch *gdbarch = reg->gdbarch;
+ int regnum = reg->regnum;
+
+ const char *name = gdbarch_register_name (gdbarch, regnum);
+ return PyString_FromString (name);
+}
+
+/* Implement gdb.RegisterDescriptor.name attribute get function. Return a
+ string that is the name of this register. Due to checking when register
+ descriptors are created the name will never by the empty string. */
+
+static PyObject *
+gdbpy_register_descriptor_name (PyObject *self, void *closure)
+{
+ return gdbpy_register_descriptor_to_string (self);
+}
+
+/* Create and return a new gdb.RegisterDescriptorIterator object which
+ will iterate over all registers in GROUP_NAME for GDBARCH. If
+ GROUP_NAME is either NULL or the empty string then the ALL_REGGROUP is
+ used, otherwise lookup the register group matching GROUP_NAME and use
+ that.
+
+ This function can return NULL if GROUP_NAME isn't found. */
+
+PyObject *
+gdbpy_new_register_descriptor_iterator (struct gdbarch *gdbarch,
+ const char *group_name)
+{
+ struct reggroup *grp = NULL;
+
+ /* Lookup the requested register group, or find the default. */
+ if (group_name == NULL || *group_name == '\0')
+ grp = all_reggroup;
+ else
+ {
+ grp = reggroup_find (gdbarch, group_name);
+ if (grp == NULL)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("Unknown register group name."));
+ return NULL;
+ }
+ }
+ /* Create a new iterator object initialised for this architecture and
+ fill in all of the details. */
+ register_descriptor_iterator_object *iter
+ = PyObject_New (register_descriptor_iterator_object,
+ &register_descriptor_iterator_object_type);
+ if (iter == NULL)
+ return NULL;
+ iter->regnum = 0;
+ iter->gdbarch = gdbarch;
+ gdb_assert (grp != NULL);
+ iter->reggroup = grp;
+
+ return (PyObject *) iter;
+}
+
+/* Return a reference to the gdb.RegisterDescriptorIterator object. */
+
+static PyObject *
+gdbpy_register_descriptor_iter (PyObject *self)
+{
+ Py_INCREF (self);
+ return self;
+}
+
+/* Return the next register name. */
+
+static PyObject *
+gdbpy_register_descriptor_iter_next (PyObject *self)
+{
+ register_descriptor_iterator_object *iter_obj
+ = (register_descriptor_iterator_object *) self;
+ struct gdbarch *gdbarch = iter_obj->gdbarch;
+
+ do
+ {
+ if (iter_obj->regnum >= gdbarch_num_cooked_regs (gdbarch))
+ {
+ PyErr_SetString (PyExc_StopIteration, _("No more registers"));
+ return NULL;
+ }
+
+ const char *name = nullptr;
+ int regnum = iter_obj->regnum;
+ if (gdbarch_register_reggroup_p (gdbarch, regnum,
+ iter_obj->reggroup))
+ name = gdbarch_register_name (gdbarch, regnum);
+ iter_obj->regnum++;
+
+ if (name != nullptr && *name != '\0')
+ return gdbpy_new_register_descriptor (gdbarch, regnum);
+ }
+ while (true);
+}
+
+/* Initializes the new Python classes from this file in the gdb module. */
+
+int
+gdbpy_initialize_registers ()
+{
+ register_descriptor_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&register_descriptor_object_type) < 0)
+ return -1;
+ if (gdb_pymodule_addobject
+ (gdb_module, "RegisterDescriptor",
+ (PyObject *) &register_descriptor_object_type) < 0)
+ return -1;
+
+ register_descriptor_iterator_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&register_descriptor_iterator_object_type) < 0)
+ return -1;
+ return (gdb_pymodule_addobject
+ (gdb_module, "RegisterDescriptorIterator",
+ (PyObject *) &register_descriptor_iterator_object_type));
+}
+
+PyTypeObject register_descriptor_iterator_object_type = {
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.RegisterDescriptorIterator", /*tp_name*/
+ sizeof (register_descriptor_iterator_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*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
+ "GDB architecture register descriptor iterator object", /*tp_doc */
+ 0, /*tp_traverse */
+ 0, /*tp_clear */
+ 0, /*tp_richcompare */
+ 0, /*tp_weaklistoffset */
+ gdbpy_register_descriptor_iter, /*tp_iter */
+ gdbpy_register_descriptor_iter_next, /*tp_iternext */
+ 0 /*tp_methods */
+};
+
+static gdb_PyGetSetDef gdbpy_register_descriptor_getset[] = {
+ { "name", gdbpy_register_descriptor_name, NULL,
+ "The name of this register.", NULL },
+ { NULL } /* Sentinel */
+};
+
+PyTypeObject register_descriptor_object_type = {
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.RegisterDescriptor", /*tp_name*/
+ sizeof (register_descriptor_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*/
+ gdbpy_register_descriptor_to_string, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB architecture register descriptor object", /*tp_doc */
+ 0, /*tp_traverse */
+ 0, /*tp_clear */
+ 0, /*tp_richcompare */
+ 0, /*tp_weaklistoffset */
+ 0, /*tp_iter */
+ 0, /*tp_iternext */
+ 0, /*tp_methods */
+ 0, /*tp_members */
+ gdbpy_register_descriptor_getset /*tp_getset */
+};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index e352b30..758dc55 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -473,6 +473,9 @@ PyObject *gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
+PyObject *gdbpy_new_register_descriptor_iterator (struct gdbarch *gdbarch,
+ const char *group_name);
+
gdbpy_ref<thread_object> create_thread_object (struct thread_info *tp);
gdbpy_ref<> thread_to_thread_object (thread_info *thr);;
gdbpy_ref<inferior_object> inferior_to_inferior_object (inferior *inf);
@@ -540,6 +543,8 @@ int gdbpy_initialize_py_events (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_arch (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_registers ()
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_xmethods (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_unwind (void)
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 4bdd220..7787dce 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1759,6 +1759,7 @@ do_start_initialization ()
|| gdbpy_initialize_py_events () < 0
|| gdbpy_initialize_event () < 0
|| gdbpy_initialize_arch () < 0
+ || gdbpy_initialize_registers () < 0
|| gdbpy_initialize_xmethods () < 0
|| gdbpy_initialize_unwind () < 0
|| gdbpy_initialize_tui () < 0)