From: Andrew Burgess Date: Sun, 7 Jun 2020 09:08:01 +0000 (+0100) Subject: gdb/python: New method to access list of register groups X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=64cb3757a9df273b990adf4f28333324edc3cae8;p=binutils-gdb.git gdb/python: New method to access list of register groups Add a new method gdb.Architecture.register_groups which returns a new object of type gdb.RegisterGroupsIterator. This new iterator then returns objects of type gdb.RegisterGroup. Each gdb.RegisterGroup object just wraps a single reggroup pointer, and (currently) has just one read-only property 'name' that is a string, the name of the register group. As with the previous commit (adding gdb.RegisterDescriptor) I made gdb.RegisterGroup an object rather than just a string in case we want to add additional properties in the future. gdb/ChangeLog: * NEWS: Mention additions to Python API. * python/py-arch.c (archpy_register_groups): New function. (arch_object_methods): Add 'register_groups' method. * python/py-registers.c (reggroup_iterator_object): New struct. (reggroup_object): New struct. (gdbpy_new_reggroup): New function. (gdbpy_reggroup_to_string): New function. (gdbpy_reggroup_name): New function. (gdbpy_reggroup_iter): New function. (gdbpy_reggroup_iter_next): New function. (gdbpy_new_reggroup_iterator): New function (gdbpy_initialize_registers): Register new types. (reggroup_iterator_object_type): Define new Python type. (gdbpy_reggroup_getset): New static global. (reggroup_object_type): Define new Python type. * python/python-internal.h gdb/testsuite/ChangeLog: * gdb.python/py-arch-reg-groups.exp: New file. gdb/doc/ChangeLog: * gdb.texi (Registers): Add @anchor for 'info registers ' command. * python.texi (Architectures In Python): Document new register_groups method. (Registers In Python): Document two new object types related to register groups. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e3abc3d651f..98096bf8cd8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,22 @@ +2020-07-06 Andrew Burgess + + * NEWS: Mention additions to Python API. + * python/py-arch.c (archpy_register_groups): New function. + (arch_object_methods): Add 'register_groups' method. + * python/py-registers.c (reggroup_iterator_object): New struct. + (reggroup_object): New struct. + (gdbpy_new_reggroup): New function. + (gdbpy_reggroup_to_string): New function. + (gdbpy_reggroup_name): New function. + (gdbpy_reggroup_iter): New function. + (gdbpy_reggroup_iter_next): New function. + (gdbpy_new_reggroup_iterator): New function + (gdbpy_initialize_registers): Register new types. + (reggroup_iterator_object_type): Define new Python type. + (gdbpy_reggroup_getset): New static global. + (reggroup_object_type): Define new Python type. + * python/python-internal.h + 2020-07-06 Andrew Burgess * Makefile.in (SUBDIR_PYTHON_SRCS): Add py-registers.c diff --git a/gdb/NEWS b/gdb/NEWS index 84019a6036e..ded544d6400 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -125,6 +125,11 @@ GNU/Linux/RISC-V (gdbserver) riscv*-*-linux* gdb.RegisterDescriptor objects. The new RegisterDescriptor is a way to query the registers available for an architecture. + ** New gdb.Architecture.register_groups method that returns a + gdb.RegisterGroupIterator object, an iterator that returns + gdb.RegisterGroup objects. The new RegisterGroup is a way to + discover the available register groups. + *** Changes in GDB 9 * 'thread-exited' event is now available in the annotations interface. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index d513d7486a5..ee6a866258d 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,12 @@ +2020-07-06 Andrew Burgess + + * gdb.texi (Registers): Add @anchor for 'info registers + ' command. + * python.texi (Architectures In Python): Document new + register_groups method. + (Registers In Python): Document two new object types related to + register groups. + 2020-07-06 Andrew Burgess * python.texi (Python API): Add new section to the menu. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index fbe9f850af4..fb6fd9eb870 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -12435,6 +12435,7 @@ and vector registers (in the selected stack frame). Print the names and values of all registers, including floating-point and vector registers (in the selected stack frame). +@anchor{info_registers_reggroup} @item info registers @var{reggroup} @dots{} Print the name and value of the registers in each of the specified @var{reggroup}s. The @var{reggroup} can be any of those returned by diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 0ed2764b102..4fb994ca6d9 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -5725,6 +5725,13 @@ the name of a register group. If @var{reggroup} is omitted, or is the empty string, then the register group @samp{all} is assumed. @end defun +@anchor{gdbpy_architecture_reggroups} +@defun Architecture.register_groups () +Return a @code{gdb.RegisterGroupsIterator} (@pxref{Registers In +Python}) for all of the register groups available for the +@code{gdb.Architecture}. +@end defun + @node Registers In Python @subsubsection Registers In Python @cindex Registers In Python @@ -5748,6 +5755,31 @@ A @code{gdb.RegisterDescriptor} has the following read-only properties: The name of this register. @end defvar +Python code can also request from a @code{gdb.Architecture} +information about the set of register groups available on a given +architecture +(@pxref{gdbpy_architecture_reggroups,,@code{Architecture.register_groups}}). + +Every register can be a member of zero or more register groups. Some +register groups are used internally within @value{GDBN} to control +things like which registers must be saved when calling into the +program being debugged (@pxref{Calling,,Calling Program Functions}). +Other register groups exist to allow users to easily see related sets +of registers in commands like @code{info registers} +(@pxref{info_registers_reggroup,,@code{info registers +@var{reggroup}}}). + +The register groups information is returned as a +@code{gdb.RegisterGroupsIterator}, which is an iterator that in turn +returns @code{gdb.RegisterGroup} objects. + +A @code{gdb.RegisterGroup} object has the following read-only +properties: + +@defvar RegisterGroup.name +A string that is the name of this register group. +@end defvar + @node TUI Windows In Python @subsubsection Implementing new TUI windows @cindex Python TUI Windows diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c index 15f9f50d7d7..d9eaf81a30a 100644 --- a/gdb/python/py-arch.c +++ b/gdb/python/py-arch.c @@ -248,6 +248,20 @@ archpy_registers (PyObject *self, PyObject *args, PyObject *kw) return gdbpy_new_register_descriptor_iterator (gdbarch, group_name); } +/* Implementation of gdb.Architecture.register_groups (self) -> Iterator. + Returns an iterator that will give up all valid register groups in the + architecture SELF. */ + +static PyObject * +archpy_register_groups (PyObject *self, PyObject *args) +{ + struct gdbarch *gdbarch = NULL; + + /* Extract the gdbarch from the self object. */ + ARCHPY_REQUIRE_VALID (self, gdbarch); + return gdbpy_new_reggroup_iterator (gdbarch); +} + /* Initializes the Architecture class in the gdb module. */ int @@ -276,6 +290,10 @@ END_PC." }, "registers ([ group-name ]) -> Iterator.\n\ Return an iterator of register descriptors for the registers in register\n\ group GROUP-NAME." }, + { "register_groups", archpy_register_groups, + METH_NOARGS, + "register_groups () -> Iterator.\n\ +Return an iterator over all of the register groups in this architecture." }, {NULL} /* Sentinel */ }; diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c index 6ccd17ef860..db0fe37eecb 100644 --- a/gdb/python/py-registers.c +++ b/gdb/python/py-registers.c @@ -56,6 +56,67 @@ typedef struct { extern PyTypeObject register_descriptor_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("register_descriptor_object"); +/* Structure for iterator over register groups. */ +typedef struct { + PyObject_HEAD + + /* The last register group returned. Initially this will be NULL. */ + struct reggroup *reggroup; + + /* Pointer back to the architecture we're finding registers for. */ + struct gdbarch *gdbarch; +} reggroup_iterator_object; + +extern PyTypeObject reggroup_iterator_object_type + CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("reggroup_iterator_object"); + +/* A register group object. */ +typedef struct { + PyObject_HEAD + + /* The register group being described. */ + struct reggroup *reggroup; +} reggroup_object; + +extern PyTypeObject reggroup_object_type + CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("reggroup_object"); + +/* Create a new gdb.RegisterGroup object wrapping REGGROUP. */ + +static PyObject * +gdbpy_new_reggroup (struct reggroup *reggroup) +{ + /* Create a new object and fill in its details. */ + reggroup_object *group + = PyObject_New (reggroup_object, ®group_object_type); + if (group == NULL) + return NULL; + group->reggroup = reggroup; + return (PyObject *) group; +} + +/* Convert a gdb.RegisterGroup to a string, it just returns the name of + the register group. */ + +static PyObject * +gdbpy_reggroup_to_string (PyObject *self) +{ + reggroup_object *group = (reggroup_object *) self; + struct reggroup *reggroup = group->reggroup; + + const char *name = reggroup_name (reggroup); + return PyString_FromString (name); +} + +/* Implement gdb.RegisterGroup.name (self) -> String. + Return a string that is the name of this register group. */ + +static PyObject * +gdbpy_reggroup_name (PyObject *self, void *closure) +{ + return gdbpy_reggroup_to_string (self); +} + /* Create an return a new gdb.RegisterDescriptor object. */ static PyObject * gdbpy_new_register_descriptor (struct gdbarch *gdbarch, @@ -96,6 +157,54 @@ gdbpy_register_descriptor_name (PyObject *self, void *closure) return gdbpy_register_descriptor_to_string (self); } +/* Return a reference to the gdb.RegisterGroupsIterator object. */ + +static PyObject * +gdbpy_reggroup_iter (PyObject *self) +{ + Py_INCREF (self); + return self; +} + +/* Return the next gdb.RegisterGroup object from the iterator. */ + +static PyObject * +gdbpy_reggroup_iter_next (PyObject *self) +{ + reggroup_iterator_object *iter_obj + = (reggroup_iterator_object *) self; + struct gdbarch *gdbarch = iter_obj->gdbarch; + + struct reggroup *next_group = reggroup_next (gdbarch, iter_obj->reggroup); + if (next_group == NULL) + { + PyErr_SetString (PyExc_StopIteration, _("No more groups")); + return NULL; + } + + iter_obj->reggroup = next_group; + return gdbpy_new_reggroup (iter_obj->reggroup); +} + +/* Return a new gdb.RegisterGroupsIterator over all the register groups in + GDBARCH. */ + +PyObject * +gdbpy_new_reggroup_iterator (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != nullptr); + + /* Create a new object and fill in its internal state. */ + reggroup_iterator_object *iter + = PyObject_New (reggroup_iterator_object, + ®group_iterator_object_type); + if (iter == NULL) + return NULL; + iter->reggroup = NULL; + iter->gdbarch = gdbarch; + return (PyObject *) iter; +} + /* 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 @@ -190,6 +299,22 @@ gdbpy_initialize_registers () (PyObject *) ®ister_descriptor_object_type) < 0) return -1; + reggroup_iterator_object_type.tp_new = PyType_GenericNew; + if (PyType_Ready (®group_iterator_object_type) < 0) + return -1; + if (gdb_pymodule_addobject + (gdb_module, "RegisterGroupsIterator", + (PyObject *) ®group_iterator_object_type) < 0) + return -1; + + reggroup_object_type.tp_new = PyType_GenericNew; + if (PyType_Ready (®group_object_type) < 0) + return -1; + if (gdb_pymodule_addobject + (gdb_module, "RegisterGroup", + (PyObject *) ®group_object_type) < 0) + return -1; + register_descriptor_iterator_object_type.tp_new = PyType_GenericNew; if (PyType_Ready (®ister_descriptor_iterator_object_type) < 0) return -1; @@ -267,3 +392,73 @@ PyTypeObject register_descriptor_object_type = { 0, /*tp_members */ gdbpy_register_descriptor_getset /*tp_getset */ }; + +PyTypeObject reggroup_iterator_object_type = { + PyVarObject_HEAD_INIT (NULL, 0) + "gdb.RegisterGroupsIterator", /*tp_name*/ + sizeof (reggroup_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 register groups iterator object", /*tp_doc */ + 0, /*tp_traverse */ + 0, /*tp_clear */ + 0, /*tp_richcompare */ + 0, /*tp_weaklistoffset */ + gdbpy_reggroup_iter, /*tp_iter */ + gdbpy_reggroup_iter_next, /*tp_iternext */ + 0 /*tp_methods */ +}; + +static gdb_PyGetSetDef gdbpy_reggroup_getset[] = { + { "name", gdbpy_reggroup_name, NULL, + "The name of this register group.", NULL }, + { NULL } /* Sentinel */ +}; + +PyTypeObject reggroup_object_type = { + PyVarObject_HEAD_INIT (NULL, 0) + "gdb.RegisterGroup", /*tp_name*/ + sizeof (reggroup_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_reggroup_to_string, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "GDB register group 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_reggroup_getset /*tp_getset */ +}; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 758dc553ce1..1e6dcf3dbb0 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -475,6 +475,7 @@ PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch); PyObject *gdbpy_new_register_descriptor_iterator (struct gdbarch *gdbarch, const char *group_name); +PyObject *gdbpy_new_reggroup_iterator (struct gdbarch *gdbarch); gdbpy_ref create_thread_object (struct thread_info *tp); gdbpy_ref<> thread_to_thread_object (thread_info *thr);; diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 9562c2cffaa..63fb5e667e0 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2020-07-06 Andrew Burgess + + * gdb.python/py-arch-reg-groups.exp: New file. + 2020-07-06 Andrew Burgess * gdb.python/py-arch-reg-names.exp: New file. diff --git a/gdb/testsuite/gdb.python/py-arch-reg-groups.exp b/gdb/testsuite/gdb.python/py-arch-reg-groups.exp new file mode 100644 index 00000000000..ea9aa77b0fa --- /dev/null +++ b/gdb/testsuite/gdb.python/py-arch-reg-groups.exp @@ -0,0 +1,87 @@ +# Copyright 2020 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 . + +# Check the gdb.Architecture.register_groups functionality. + +load_lib gdb-python.exp +standard_testfile py-arch.c + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if ![runto_main] { + return -1 +} + +# First, use 'maint print reggroups' to get a list of all register +# groups. +set groups {} +set test "maint print reggroups" +gdb_test_multiple $test $test { + -re ".*Group\[ \t\]+Type\[ \t\]+\r\n" { + exp_continue + } + -re "^ (\[^ \t\]+)\[ \t\]+\[^\r\n\]+\r\n" { + lappend groups $expect_out(1,string) + exp_continue + } + -re "^$gdb_prompt " { + } +} +gdb_assert {[llength $groups] > 0} \ + "Found at least one register group" + +# Now get the same register names using Python API. +gdb_py_test_silent_cmd \ + "python frame = gdb.selected_frame()" "get frame" 0 +gdb_py_test_silent_cmd \ + "python arch = frame.architecture()" "get arch" 0 +gdb_py_test_silent_cmd \ + "python groups = list (arch.register_groups ())" \ + "get register groups" 0 +gdb_py_test_silent_cmd \ + "python groups = map (lambda obj: obj.name, groups)" \ + "convert groups to names" 0 + +set py_groups {} +gdb_test_multiple "python print (\"\\n\".join (groups))" \ + "register groups from python" { + -re "^python print \[^\r\n\]+\r\n" { + exp_continue + } + -re "^(\[^\r\n\]+)\r\n" { + lappend py_groups $expect_out(1,string) + exp_continue + } + -re "^$gdb_prompt " { + } + } + +gdb_assert {[llength $py_groups] > 0} \ + "Found at least one register group from python" +gdb_assert {[llength $py_groups] == [llength $groups]} \ + "Same numnber of registers groups found" + +set found_non_match 0 +for { set i 0 } { $i < [llength $groups] } { incr i } { + if {[lindex $groups $i] != [lindex $py_groups $i]} { + set found_non_match 1 + } +} +gdb_assert { $found_non_match == 0 } "all register groups match"