public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Andrew Burgess <andrew.burgess@embecosm.com>
To: gdb-patches@sourceware.org
Subject: [PATCH 4/5] gdb/python: New method to access list of register groups
Date: Wed, 17 Jun 2020 18:38:08 +0100	[thread overview]
Message-ID: <360af15dc7ea2e2e20f2b9ff6529467b7e0a0614.1592415322.git.andrew.burgess@embecosm.com> (raw)
In-Reply-To: <cover.1592415321.git.andrew.burgess@embecosm.com>

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
	<reggroup>' command.
	* python.texi (Architectures In Python): Document new
	register_groups method.
	(Registers In Python): Document two new object types related to
	register groups.
---
 gdb/ChangeLog                                 |  19 ++
 gdb/NEWS                                      |   5 +
 gdb/doc/ChangeLog                             |   9 +
 gdb/doc/gdb.texinfo                           |   1 +
 gdb/doc/python.texi                           |  32 +++
 gdb/python/py-arch.c                          |  18 ++
 gdb/python/py-registers.c                     | 195 ++++++++++++++++++
 gdb/python/python-internal.h                  |   1 +
 gdb/testsuite/ChangeLog                       |   4 +
 .../gdb.python/py-arch-reg-groups.exp         |  87 ++++++++
 10 files changed, 371 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-arch-reg-groups.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 0dc467dc95e..1c684427b1d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -101,6 +101,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/gdb.texinfo b/gdb/doc/gdb.texinfo
index 59e3e75d18a..56cd10ec2e2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12344,6 +12344,7 @@
 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 7503063af61..6ecc3ea63f0 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5725,6 +5725,13 @@
 empty string, then the register group @dfn{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
@@ -5747,6 +5754,31 @@
 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 b78e7426c98..3a6bc276d5b 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, &reggroup_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,
+		    &reggroup_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 (void)
        (PyObject *) &register_descriptor_object_type) < 0)
     return -1;
 
+  reggroup_iterator_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&reggroup_iterator_object_type) < 0)
+    return -1;
+  if (gdb_pymodule_addobject
+      (gdb_module, "RegisterGroupsIterator",
+       (PyObject *) &reggroup_iterator_object_type) < 0)
+    return -1;
+
+  reggroup_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&reggroup_object_type) < 0)
+    return -1;
+  if (gdb_pymodule_addobject
+      (gdb_module, "RegisterGroup",
+       (PyObject *) &reggroup_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;
@@ -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 91cac319059..3f4fd2a886d 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<thread_object> create_thread_object (struct thread_info *tp);
 gdbpy_ref<> thread_to_thread_object (thread_info *thr);;
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 <http://www.gnu.org/licenses/>.
+
+# 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"
-- 
2.25.4


  parent reply	other threads:[~2020-06-17 17:38 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-17 17:38 [PATCH 0/5] Python Unwinders and Inline Frames Andrew Burgess
2020-06-17 17:38 ` [PATCH 1/5] gdb: Remove deprecated_set_gdbarch_data Andrew Burgess
2020-06-18 21:11   ` Tom Tromey
2020-06-17 17:38 ` [PATCH 2/5] gdb/python: Add architecture method to gdb.PendingFrame Andrew Burgess
2020-06-17 18:20   ` Eli Zaretskii
2020-06-18 21:18   ` Tom Tromey
2020-06-17 17:38 ` [PATCH 3/5] gdb/python: Add gdb.Architecture.registers method Andrew Burgess
2020-06-17 18:25   ` Eli Zaretskii
2020-06-18 21:24   ` Tom Tromey
2020-06-17 17:38 ` Andrew Burgess [this message]
2020-06-17 18:27   ` [PATCH 4/5] gdb/python: New method to access list of register groups Eli Zaretskii
2020-06-17 18:40   ` Christian Biesinger
2020-06-18  8:44     ` Andrew Burgess
2020-06-18 21:27   ` Tom Tromey
2020-06-17 17:38 ` [PATCH 5/5] gdb: Fix Python unwinders and inline frames Andrew Burgess
2020-06-17 21:14   ` Luis Machado
2020-06-18  8:25     ` Andrew Burgess
2020-06-18 10:29       ` Luis Machado
2020-06-18 17:42         ` Andrew Burgess
2020-06-18 21:35   ` Tom Tromey
2020-06-22 15:47   ` Andrew Burgess
2020-06-23 14:16     ` Luis Machado
2020-07-02 21:07     ` Andrew Burgess
2020-07-06 17:43       ` Andrew Burgess

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=360af15dc7ea2e2e20f2b9ff6529467b7e0a0614.1592415322.git.andrew.burgess@embecosm.com \
    --to=andrew.burgess@embecosm.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).