[3/3] gdb/python: add gdbpy_register_subsystem mechanism

Message ID d9aa843eab3abcf572a7bac05d7b8c9facb53eee.1664729134.git.aburgess@redhat.com
State New
Headers
Series New mechanism to initialise Python components in GDB |

Commit Message

Andrew Burgess Oct. 2, 2022, 4:53 p.m. UTC
  Currently, when we add a new python sub-system to GDB,
e.g. py-inferior.c, we end up having to create a new function like
gdbpy_initialize_inferior, which then has to be called from the
function do_start_initialization in python.c.

In some cases (or currently just the one case), like py-micmd.c, we
have two functions gdbpy_initialize_micommands, and
gdbpy_finalize_micommands, with the second being called from
finalize_python which is also in python.c.

This commit proposes a mechanism to manage these initialization and
finalization calls, this means that adding a new Python subsystem will
no longer require changes to python.c, instead, the initialization and
finalization functions will be registered directly from the sub-system
file, e.g. py-inferior.c, or py-micmd.c.

A new function gdbpy_register_subsystem will be added which takes two
callbacks, the first is an initialization callback,
e.g. gdbpy_initialize_micommands, and the second is a finalization
callback, e.g. gdbpy_finalize_micommands.  The second callback is
optional, and defaults to nullptr.

All callbacks registered through gdbpy_register_subsystem are added to
a vector, and in python.c we iterate over the vector and all the
various functions as needed.

The only question is when should gdbpy_register_subsystem be called?
The answer I think is to make use of GDB's existing _initialize_*
function mechanism.  A few of the python sub-system files already
include an _initialize_* function, but most needed to have one added.

One possible problem with this change is that there is now no
guaranteed ordering of how the various sub-systems are initialized (or
finalized).  The previous two commits have removed all the implicit
dependencies that existed between sub-systems, and replaced them with
more explicit dependencies.  With those two commits in place I now
believe that there are no dependencies on the order in which the
gdbpy_initialize_* functions are called.

To satisfy myself that this was true I ran the testsuite with the
patch as presented here, but also, I modified GDB so that the
gdbpy_initialize_* functions were called in reverse order, and ran the
testsuite again.  I saw no regressions in either case.

As there's only a single finalize call right now then we can be sure
that there's no dependencies there.

One other change of note was for gdbpy_initialize_gdb_readline, this
function previously returned void.  In order to make this function
have the correct signature I've updated its return type to int, and we
now return 0 to indicate success.

All of the other initialize (and finalize) functions have been made
static within their respective sub-system files.

There should be no user visible changes after this commit.
---
 gdb/python/py-arch.c             |  11 +++-
 gdb/python/py-auto-load.c        |   9 ++-
 gdb/python/py-block.c            |   9 ++-
 gdb/python/py-breakpoint.c       |   7 ++-
 gdb/python/py-cmd.c              |   9 ++-
 gdb/python/py-connection.c       |   4 +-
 gdb/python/py-disasm.c           |  11 +++-
 gdb/python/py-event.c            |   9 ++-
 gdb/python/py-evtregistry.c      |   9 ++-
 gdb/python/py-finishbreakpoint.c |  11 +++-
 gdb/python/py-frame.c            |   9 ++-
 gdb/python/py-function.c         |   9 ++-
 gdb/python/py-gdb-readline.c     |  10 ++-
 gdb/python/py-inferior.c         |  11 +++-
 gdb/python/py-infthread.c        |  11 +++-
 gdb/python/py-instruction.c      |   9 ++-
 gdb/python/py-lazy-string.c      |   9 ++-
 gdb/python/py-linetable.c        |   9 ++-
 gdb/python/py-membuf.c           |  11 +++-
 gdb/python/py-micmd.c            |   9 ++-
 gdb/python/py-objfile.c          |   9 ++-
 gdb/python/py-param.c            |   9 ++-
 gdb/python/py-progspace.c        |   9 ++-
 gdb/python/py-record-btrace.c    |   9 ++-
 gdb/python/py-record.c           |   9 ++-
 gdb/python/py-registers.c        |  11 +++-
 gdb/python/py-symbol.c           |   9 ++-
 gdb/python/py-symtab.c           |   9 ++-
 gdb/python/py-tui.c              |   9 ++-
 gdb/python/py-type.c             |   9 ++-
 gdb/python/py-unwind.c           |  34 ++++++-----
 gdb/python/py-value.c            |   9 ++-
 gdb/python/py-xmethods.c         |   9 ++-
 gdb/python/python-internal.h     | 102 ++++++++++---------------------
 gdb/python/python.c              |  73 +++++++++++-----------
 35 files changed, 349 insertions(+), 156 deletions(-)
  

Comments

Tom Tromey Oct. 14, 2022, 5:20 p.m. UTC | #1
>>>>> "Andrew" == Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:

Andrew> All callbacks registered through gdbpy_register_subsystem are added to
Andrew> a vector, and in python.c we iterate over the vector and all the
Andrew> various functions as needed.

Andrew> The only question is when should gdbpy_register_subsystem be called?
Andrew> The answer I think is to make use of GDB's existing _initialize_*
Andrew> function mechanism.  A few of the python sub-system files already
Andrew> include an _initialize_* function, but most needed to have one added.

This approach seems a little weird to me, in that it uses the
_initialize_* functions to hook into gdb's existing initialization
approach, but then registers a second layer of initialization functions
in a vector to be called a little later.

What if, instead, something like make-init-c was used, but specifically
for Python initialization (and finalization) functions?

Tom
  

Patch

diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c
index cf0978560f9..bd172d6f8c8 100644
--- a/gdb/python/py-arch.c
+++ b/gdb/python/py-arch.c
@@ -344,7 +344,7 @@  gdbpy_all_architecture_names (PyObject *self, PyObject *args)
 
 /* Initializes the Architecture class in the gdb module.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_arch (void)
 {
   arch_object_type.tp_new = PyType_GenericNew;
@@ -355,6 +355,15 @@  gdbpy_initialize_arch (void)
 				 (PyObject *) &arch_object_type);
 }
 
+void _initialize_py_arch ();
+void
+_initialize_py_arch ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_arch);
+}
+
+
+
 static PyMethodDef arch_object_methods [] = {
   { "name", archpy_name, METH_NOARGS,
     "name () -> String.\n\
diff --git a/gdb/python/py-auto-load.c b/gdb/python/py-auto-load.c
index 5d60460683a..50131806c18 100644
--- a/gdb/python/py-auto-load.c
+++ b/gdb/python/py-auto-load.c
@@ -56,7 +56,7 @@  info_auto_load_python_scripts (const char *pattern, int from_tty)
   auto_load_info_scripts (pattern, from_tty, &extension_language_python);
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_auto_load (void)
 {
   add_setshow_boolean_cmd ("python-scripts", class_support,
@@ -95,3 +95,10 @@  Print the list of automatically loaded Python scripts, deprecated."));
 
   return 0;
 }
+
+void _initialize_py_auto_load ();
+void
+_initialize_py_auto_load ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_auto_load);
+}
diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c
index b9aea3aca69..204571192bd 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -424,7 +424,7 @@  blpy_iter_is_valid (PyObject *self, PyObject *args)
   Py_RETURN_TRUE;
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_blocks (void)
 {
   block_object_type.tp_new = PyType_GenericNew;
@@ -443,6 +443,13 @@  gdbpy_initialize_blocks (void)
 				 (PyObject *) &block_syms_iterator_object_type);
 }
 
+void _initialize_py_block ();
+void
+_initialize_py_block ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_blocks);
+}
+
 
 
 static PyMethodDef block_object_methods[] = {
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 7a757432948..8c972761bf9 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -1231,7 +1231,7 @@  gdbpy_breakpoint_modified (struct breakpoint *b)
 
 
 /* Initialize the Python breakpoint code.  */
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_breakpoints (void)
 {
   int i;
@@ -1271,7 +1271,7 @@  gdbpy_initialize_breakpoints (void)
 
 /* Initialize the Python BreakpointLocation code.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_breakpoint_locations ()
 {
   if (PyType_Ready (&breakpoint_location_object_type) < 0)
@@ -1433,6 +1433,9 @@  _initialize_py_breakpoint ()
 	NULL,
 	show_pybp_debug,
 	&setdebuglist, &showdebuglist);
+
+  gdbpy_register_subsystem (gdbpy_initialize_breakpoints);
+  gdbpy_register_subsystem (gdbpy_initialize_breakpoint_locations);
 }
 
 /* Python function to set the enabled state of a breakpoint location.  */
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 5cc392af175..e32686da63e 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -551,7 +551,7 @@  cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 
 /* Initialize the 'commands' code.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_commands (void)
 {
   int i;
@@ -601,6 +601,13 @@  gdbpy_initialize_commands (void)
   return 0;
 }
 
+void _initialize_py_cmd ();
+void
+_initialize_py_cmd ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_commands);
+}
+
 
 
 static PyMethodDef cmdpy_object_methods[] =
diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c
index 84660414dd0..d9ffe0d314c 100644
--- a/gdb/python/py-connection.c
+++ b/gdb/python/py-connection.c
@@ -285,7 +285,7 @@  connpy_get_connection_details (PyObject *self, void *closure)
 
 /* Python specific initialization for this file.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_connection (void)
 {
   if (PyType_Ready (&connection_object_type) < 0)
@@ -445,6 +445,8 @@  _initialize_py_connection ()
 {
   gdb::observers::connection_removed.attach (connpy_connection_removed,
 					     "py-connection");
+
+  gdbpy_register_subsystem (gdbpy_initialize_connection);
 }
 
 /* Methods for the gdb.TargetConnection object type.  */
diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c
index c37452fcf72..118b50ee208 100644
--- a/gdb/python/py-disasm.c
+++ b/gdb/python/py-disasm.c
@@ -971,7 +971,7 @@  static struct PyModuleDef python_disassembler_module_def =
 
 /* Called to initialize the Python structures in this file.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_disasm ()
 {
   /* Create the _gdb.disassembler module, and add it to the _gdb module.  */
@@ -1005,6 +1005,15 @@  gdbpy_initialize_disasm ()
   return 0;
 }
 
+void _initialize_py_disasm ();
+void
+_initialize_py_disasm ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_disasm);
+}
+
+
+
 /* Describe the gdb.disassembler.DisassembleInfo type.  */
 
 PyTypeObject disasm_info_object_type = {
diff --git a/gdb/python/py-event.c b/gdb/python/py-event.c
index 4d8a0b909c0..a540243aa6d 100644
--- a/gdb/python/py-event.c
+++ b/gdb/python/py-event.c
@@ -54,7 +54,7 @@  evpy_add_attribute (PyObject *event, const char *name, PyObject *attr)
 
 /* Initialize the Python event code.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_event (void)
 {
   return gdbpy_initialize_event_generic (&event_object_type,
@@ -114,6 +114,13 @@  evpy_emit_event (PyObject *event,
   return 0;
 }
 
+void _initialize_py_event ();
+void
+_initialize_py_event ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_event);
+}
+
 static gdb_PyGetSetDef event_object_getset[] =
 {
   { "__dict__", gdb_py_generic_dict, NULL,
diff --git a/gdb/python/py-evtregistry.c b/gdb/python/py-evtregistry.c
index f3a7f0ca244..9d83c54120c 100644
--- a/gdb/python/py-evtregistry.c
+++ b/gdb/python/py-evtregistry.c
@@ -102,7 +102,7 @@  evregpy_dealloc (PyObject *self)
 
 /* Initialize the Python event registry code.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_eventregistry (void)
 {
   if (PyType_Ready (&eventregistry_object_type) < 0)
@@ -123,6 +123,13 @@  evregpy_no_listeners_p (eventregistry_object *registry)
   return registry == nullptr || PyList_Size (registry->callbacks) == 0;
 }
 
+void _initialize_py_evtregistry ();
+void
+_initialize_py_evtregistry ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_eventregistry);
+}
+
 static PyMethodDef eventregistry_object_methods[] =
 {
   { "connect", evregpy_connect, METH_VARARGS, "Add function" },
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index 67637f16a39..46b188c7a93 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -405,7 +405,7 @@  bpfinishpy_handle_exit (struct inferior *inf)
 
 /* Initialize the Python finish breakpoint code.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_finishbreakpoints (void)
 {
   if (!gdbpy_breakpoint_init_breakpoint_type ())
@@ -426,6 +426,15 @@  gdbpy_initialize_finishbreakpoints (void)
   return 0;
 }
 
+void _initialize_py_finishbreakpoint ();
+void
+_initialize_py_finishbreakpoint ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_finishbreakpoints);
+}
+
+
+
 static gdb_PyGetSetDef finish_breakpoint_object_getset[] = {
   { "return_value", bpfinishpy_get_returnvalue, NULL,
   "gdb.Value object representing the return value, if any. \
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 8bd2e0bc6da..af7a5e88c75 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -713,7 +713,7 @@  frapy_richcompare (PyObject *self, PyObject *other, int op)
 
 /* Sets up the Frame API in the gdb module.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_frames (void)
 {
   frame_object_type.tp_new = PyType_GenericNew;
@@ -745,6 +745,13 @@  gdbpy_initialize_frames (void)
 				 (PyObject *) &frame_object_type);
 }
 
+void _initialize_py_frame ();
+void
+_initialize_py_frame ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_frames);
+}
+
 
 
 static PyMethodDef frame_object_methods[] = {
diff --git a/gdb/python/py-function.c b/gdb/python/py-function.c
index c314ee5da71..45e72f3d289 100644
--- a/gdb/python/py-function.c
+++ b/gdb/python/py-function.c
@@ -134,7 +134,7 @@  fnpy_init (PyObject *self, PyObject *args, PyObject *kwds)
 
 /* Initialize internal function support.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_functions (void)
 {
   fnpy_object_type.tp_new = PyType_GenericNew;
@@ -145,6 +145,13 @@  gdbpy_initialize_functions (void)
 				 (PyObject *) &fnpy_object_type);
 }
 
+void _initialize_py_function ();
+void
+_initialize_py_function ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_functions);
+}
+
 
 
 PyTypeObject fnpy_object_type =
diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c
index af388d5ed72..c7c38eea89d 100644
--- a/gdb/python/py-gdb-readline.c
+++ b/gdb/python/py-gdb-readline.c
@@ -85,7 +85,7 @@  gdbpy_readline_wrapper (FILE *sys_stdin, FILE *sys_stdout,
 
 /* Initialize Python readline support.  */
 
-void
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_gdb_readline (void)
 {
   /* Python's readline module conflicts with GDB's use of readline
@@ -109,5 +109,13 @@  class GdbRemoveReadlineFinder:\n\
 sys.meta_path.append(GdbRemoveReadlineFinder())\n\
 ") == 0)
     PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper;
+
+  return 0;
 }
 
+void _initialize_py_gdb_readline ();
+void
+_initialize_py_gdb_readline ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_gdb_readline);
+}
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 0066ae6d6a9..b2c4a11086f 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -833,7 +833,7 @@  gdbpy_selected_inferior (PyObject *self, PyObject *args)
 	  inferior_to_inferior_object (current_inferior ()).release ());
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_inferior (void)
 {
   if (PyType_Ready (&inferior_object_type) < 0)
@@ -869,6 +869,15 @@  gdbpy_initialize_inferior (void)
   return 0;
 }
 
+void _initialize_py_inferior ();
+void
+_initialize_py_inferior ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_inferior);
+}
+
+
+
 static gdb_PyGetSetDef inferior_object_getset[] =
 {
   { "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 34f60526ac9..1e474299f77 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -361,7 +361,7 @@  gdbpy_selected_thread (PyObject *self, PyObject *args)
   Py_RETURN_NONE;
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_thread (void)
 {
   if (PyType_Ready (&thread_object_type) < 0)
@@ -371,6 +371,15 @@  gdbpy_initialize_thread (void)
 				 (PyObject *) &thread_object_type);
 }
 
+void _initialize_py_infthread ();
+void
+_initialize_py_infthread ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_thread);
+}
+
+
+
 static gdb_PyGetSetDef thread_object_getset[] =
 {
   { "name", thpy_get_name, thpy_set_name,
diff --git a/gdb/python/py-instruction.c b/gdb/python/py-instruction.c
index fee5bba4015..3fe39411507 100644
--- a/gdb/python/py-instruction.c
+++ b/gdb/python/py-instruction.c
@@ -81,10 +81,17 @@  py_insn_get_insn_type ()
 
 /* Sets up the gdb.Instruction type.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_instruction (void)
 {
   if (py_insn_get_insn_type () == nullptr)
     return -1;
   return 0;
 }
+
+void _initialize_py_instruction ();
+void
+_initialize_py_instruction ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_instruction);
+}
diff --git a/gdb/python/py-lazy-string.c b/gdb/python/py-lazy-string.c
index 8cd8b91e91b..5f8afcef858 100644
--- a/gdb/python/py-lazy-string.c
+++ b/gdb/python/py-lazy-string.c
@@ -229,7 +229,7 @@  gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
   return (PyObject *) str_obj;
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_lazy_string (void)
 {
   if (PyType_Ready (&lazy_string_object_type) < 0)
@@ -291,6 +291,13 @@  gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR *addr,
   encoding->reset (lazy->encoding ? xstrdup (lazy->encoding) : NULL);
 }
 
+void _initialize_py_lazy_string ();
+void
+_initialize_py_lazy_string ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_lazy_string);
+}
+
 
 
 static PyMethodDef lazy_string_object_methods[] = {
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index 8e545febb17..53cc3c23d22 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -286,7 +286,7 @@  ltpy_dealloc (PyObject *self)
 /* Initialize LineTable, LineTableEntry and LineTableIterator
    objects.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_linetable (void)
 {
   if (PyType_Ready (&linetable_object_type) < 0)
@@ -446,6 +446,13 @@  ltpy_iter_is_valid (PyObject *self, PyObject *args)
   Py_RETURN_TRUE;
 }
 
+void _initialize_py_linetable ();
+void
+_initialize_py_linetable ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_linetable);
+}
+
 
 
 static PyMethodDef linetable_object_methods[] = {
diff --git a/gdb/python/py-membuf.c b/gdb/python/py-membuf.c
index d2a059a0e34..d4b830b7a40 100644
--- a/gdb/python/py-membuf.c
+++ b/gdb/python/py-membuf.c
@@ -99,7 +99,7 @@  get_buffer (PyObject *self, Py_buffer *buf, int flags)
 
 /* General Python initialization callback.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_membuf (void)
 {
   membuf_object_type.tp_new = PyType_GenericNew;
@@ -110,6 +110,15 @@  gdbpy_initialize_membuf (void)
 				 (PyObject *) &membuf_object_type);
 }
 
+void _initialize_py_membuf ();
+void
+_initialize_py_membuf ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_membuf);
+}
+
+
+
 static PyBufferProcs buffer_procs =
 {
   get_buffer
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index 79e2575f789..b72d66d8154 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -595,7 +595,7 @@  micmdpy_dealloc (PyObject *obj)
 
 /* Python initialization for the MI commands components.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_micommands ()
 {
   micmdpy_object_type.tp_new = PyType_GenericNew;
@@ -614,7 +614,9 @@  gdbpy_initialize_micommands ()
   return 0;
 }
 
-void
+/* Cleanup just before GDB shuts down the Python interpreter.  */
+
+static void
 gdbpy_finalize_micommands ()
 {
   /* mi_command_py objects hold references to micmdpy_object objects.  They must
@@ -736,4 +738,7 @@  _initialize_py_micmd ()
      nullptr,
      show_pymicmd_debug,
      &setdebuglist, &showdebuglist);
+
+  gdbpy_register_subsystem (gdbpy_initialize_micommands,
+			    gdbpy_finalize_micommands);
 }
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 757f9aac4fc..e2bcefb58c5 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -708,7 +708,7 @@  objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_objfile (void)
 {
   if (PyType_Ready (&objfile_object_type) < 0)
@@ -718,6 +718,13 @@  gdbpy_initialize_objfile (void)
 				 (PyObject *) &objfile_object_type);
 }
 
+void _initialize_py_objfile ();
+void
+_initialize_py_objfile ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_objfile);
+}
+
 
 
 static PyMethodDef objfile_object_methods[] =
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 5d509ba4658..17c6e365ea9 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -828,7 +828,7 @@  parmpy_dealloc (PyObject *obj)
 }
 
 /* Initialize the 'parameters' module.  */
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_parameters (void)
 {
   int i;
@@ -856,6 +856,13 @@  gdbpy_initialize_parameters (void)
 				 (PyObject *) &parmpy_object_type);
 }
 
+void _initialize_py_param ();
+void
+_initialize_py_param ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_parameters);
+}
+
 
 
 PyTypeObject parmpy_object_type =
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 4eb33e8292f..18ea7c0ca60 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -529,7 +529,7 @@  gdbpy_is_progspace (PyObject *obj)
   return PyObject_TypeCheck (obj, &pspace_object_type);
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_pspace (void)
 {
   if (PyType_Ready (&pspace_object_type) < 0)
@@ -539,6 +539,13 @@  gdbpy_initialize_pspace (void)
 				 (PyObject *) &pspace_object_type);
 }
 
+void _initialize_py_progspace ();
+void
+_initialize_py_progspace ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_pspace);
+}
+
 
 
 static gdb_PyGetSetDef pspace_getset[] =
diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c
index 85401010f0a..4a7dc09a45d 100644
--- a/gdb/python/py-record-btrace.c
+++ b/gdb/python/py-record-btrace.c
@@ -816,7 +816,7 @@  static PyMappingMethods btpy_list_mapping_methods =
 
 /* Sets up the btrace record API.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_btrace (void)
 {
   btpy_list_type.tp_new = PyType_GenericNew;
@@ -837,3 +837,10 @@  gdbpy_initialize_btrace (void)
 
   return PyType_Ready (&btpy_list_type);
 }
+
+void _initialize_py_record_btrace ();
+void
+_initialize_py_record_btrace ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_btrace);
+}
diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c
index fd5cfee84eb..04f5fc802b3 100644
--- a/gdb/python/py-record.c
+++ b/gdb/python/py-record.c
@@ -544,7 +544,7 @@  static gdb_PyGetSetDef recpy_gap_getset[] = {
 
 /* Sets up the record API in the gdb module.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_record (void)
 {
   recpy_record_type.tp_new = PyType_GenericNew;
@@ -648,3 +648,10 @@  gdbpy_stop_recording (PyObject *self, PyObject *args)
 
   Py_RETURN_NONE;
 }
+
+void _initialize_py_record ();
+void
+_initialize_py_record ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_record);
+}
diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c
index fe7481cea9e..542317d3970 100644
--- a/gdb/python/py-registers.c
+++ b/gdb/python/py-registers.c
@@ -427,7 +427,7 @@  gdbpy_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
 
 /* Initializes the new Python classes from this file in the gdb module.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_registers ()
 {
   register_descriptor_object_type.tp_new = PyType_GenericNew;
@@ -462,6 +462,15 @@  gdbpy_initialize_registers ()
 	   (PyObject *) &register_descriptor_iterator_object_type));
 }
 
+void _initialize_py_registers ();
+void
+_initialize_py_registers ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_registers);
+}
+
+
+
 static PyMethodDef register_descriptor_iterator_object_methods [] = {
   { "find", (PyCFunction) register_descriptor_iter_find,
     METH_VARARGS | METH_KEYWORDS,
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index e5fe5cf8c92..e1d7eca7f58 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -611,7 +611,7 @@  gdbpy_lookup_static_symbols (PyObject *self, PyObject *args, PyObject *kw)
   return return_list.release ();
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_symbols (void)
 {
   if (PyType_Ready (&symbol_object_type) < 0)
@@ -680,6 +680,13 @@  gdbpy_initialize_symbols (void)
 				 (PyObject *) &symbol_object_type);
 }
 
+void _initialize_py_symbol ();
+void
+_initialize_py_symbol ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_symbols);
+}
+
 
 
 static gdb_PyGetSetDef symbol_object_getset[] = {
diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index 03c274dfff6..826ffc19e13 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -509,7 +509,7 @@  symtab_object_to_symtab (PyObject *obj)
   return ((symtab_object *) obj)->symtab;
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_symtabs (void)
 {
   symtab_object_type.tp_new = PyType_GenericNew;
@@ -528,6 +528,13 @@  gdbpy_initialize_symtabs (void)
 				 (PyObject *) &sal_object_type);
 }
 
+void _initialize_py_symtab ();
+void
+_initialize_py_symtab ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_symtabs);
+}
+
 
 
 static gdb_PyGetSetDef symtab_object_getset[] = {
diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c
index 6a92251a705..b69fbf1f3c1 100644
--- a/gdb/python/py-tui.c
+++ b/gdb/python/py-tui.c
@@ -561,7 +561,7 @@  PyTypeObject gdbpy_tui_window_object_type =
 
 /* Initialize this module.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_tui ()
 {
 #ifdef TUI
@@ -572,3 +572,10 @@  gdbpy_initialize_tui ()
 
   return 0;
 }
+
+void _initialize_py_tui ();
+void
+_initialize_py_tui ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_tui);
+}
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index 3e558dee7ad..cf18470ba58 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -1465,7 +1465,7 @@  gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw)
   return type_to_type_object (type);
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_types (void)
 {
   if (PyType_Ready (&type_object_type) < 0)
@@ -1493,6 +1493,13 @@  gdbpy_initialize_types (void)
 				 (PyObject *) &field_object_type);
 }
 
+void _initialize_py_type ();
+void
+_initialize_py_type ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_types);
+}
+
 
 
 static gdb_PyGetSetDef type_object_getset[] =
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index 0a1b460e785..c0a35fda6f9 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -682,23 +682,9 @@  pyuw_on_new_gdbarch (struct gdbarch *newarch)
     }
 }
 
-void _initialize_py_unwind ();
-void
-_initialize_py_unwind ()
-{
-  add_setshow_boolean_cmd
-      ("py-unwind", class_maintenance, &pyuw_debug,
-	_("Set Python unwinder debugging."),
-	_("Show Python unwinder debugging."),
-	_("When on, Python unwinder debugging is enabled."),
-	NULL,
-	show_pyuw_debug,
-	&setdebuglist, &showdebuglist);
-}
-
 /* Initialize unwind machinery.  */
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_unwind (void)
 {
   gdb::observers::architecture_changed.attach (pyuw_on_new_gdbarch,
@@ -717,6 +703,24 @@  gdbpy_initialize_unwind (void)
       (PyObject *) &unwind_info_object_type);
 }
 
+void _initialize_py_unwind ();
+void
+_initialize_py_unwind ()
+{
+  add_setshow_boolean_cmd
+      ("py-unwind", class_maintenance, &pyuw_debug,
+	_("Set Python unwinder debugging."),
+	_("Show Python unwinder debugging."),
+	_("When on, Python unwinder debugging is enabled."),
+	NULL,
+	show_pyuw_debug,
+	&setdebuglist, &showdebuglist);
+
+  gdbpy_register_subsystem (gdbpy_initialize_unwind);
+}
+
+
+
 static PyMethodDef pending_frame_object_methods[] =
 {
   { "read_register", pending_framepy_read_register, METH_VARARGS,
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index 92a15304c34..5ca8aa0f0f7 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -2059,7 +2059,7 @@  gdbpy_is_value_object (PyObject *obj)
   return PyObject_TypeCheck (obj, &value_object_type);
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_values (void)
 {
   if (PyType_Ready (&value_object_type) < 0)
@@ -2069,6 +2069,13 @@  gdbpy_initialize_values (void)
 				 (PyObject *) &value_object_type);
 }
 
+void _initialize_py_value ();
+void
+_initialize_py_value ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_values);
+}
+
 
 
 static gdb_PyGetSetDef value_object_getset[] = {
diff --git a/gdb/python/py-xmethods.c b/gdb/python/py-xmethods.c
index 1467e52a960..adee4893b46 100644
--- a/gdb/python/py-xmethods.c
+++ b/gdb/python/py-xmethods.c
@@ -598,7 +598,7 @@  python_xmethod_worker::python_xmethod_worker (PyObject *py_worker,
   Py_INCREF (this_type);
 }
 
-int
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_xmethods (void)
 {
   py_match_method_name = PyUnicode_FromString (match_method_name);
@@ -612,3 +612,10 @@  gdbpy_initialize_xmethods (void)
 
   return 1;
 }
+
+void _initialize_py_xmethods ();
+void
+_initialize_py_xmethods ()
+{
+  gdbpy_register_subsystem (gdbpy_initialize_xmethods);
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index a4f54e78268..9630a0dab56 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -489,76 +489,38 @@  struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
 
 extern struct program_space *progspace_object_to_program_space (PyObject *obj);
 
-void gdbpy_initialize_gdb_readline (void);
-int gdbpy_initialize_auto_load (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_values (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_frames (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_instruction (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_btrace (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_record (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_symtabs (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_commands (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_symbols (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_symtabs (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_blocks (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_types (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_functions (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_pspace (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_objfile (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_breakpoints (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_breakpoint_locations ()
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_finishbreakpoints (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_lazy_string (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_linetable (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_parameters (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_thread (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_inferior (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_eventregistry (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_event (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)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_tui ()
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_membuf ()
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_connection ()
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-int gdbpy_initialize_micommands (void)
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
-void gdbpy_finalize_micommands ();
-int gdbpy_initialize_disasm ()
-  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+/* The type of a function that can be called just after GDB has setup the
+   Python interpreter.  This function will setup any additional Python
+   state required by a particular subsystem.  Return 0 if the setup was
+   successful, or return -1 if setup failed, in which case a Python
+   exception should have been raised.  */
+
+typedef int (*gdbpy_initialize_file_func_t) (void);
+
+/* The type of a function that can be called just before GDB shuts down
+   the Python interpreter.  This function can cleanup an Python state that
+   is cached within GDB, for example, if GDB is holding any references to
+   Python objects, these should be released before the Python interpreter
+   is shut down.
+
+   There is no error return in this case.  This function is only called
+   when GDB is already shutting down.  The function should make a best
+   effort to clean up, and then return.  */
+
+typedef void (*gdbpy_finalize_file_func_t) (void);
+
+/* Register the initialization (INIT) and finalization (FINI) functions for
+   a Python subsystem.  See the comments on the function types above for
+   when these functions will be called.
+
+   Either of these functions can be nullptr, in which case no function will
+   be called.
+
+   The FINI argument is optional, and defaults to nullptr (no function to
+   call).  */
+
+void gdbpy_register_subsystem (gdbpy_initialize_file_func_t init,
+			       gdbpy_finalize_file_func_t fini = nullptr);
 
 PyMODINIT_FUNC gdbpy_events_mod_func ();
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 516e2c983cf..afb4464aad7 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -196,6 +196,26 @@  const struct extension_language_defn extension_language_python =
 
 #ifdef HAVE_PYTHON
 
+/* A pair containing two function pointers.  The first pointer is to be
+   called just after GDB has setup the Python interpreter, and the second
+   is to be called just before GDB shuts down the Python interpreter.  */
+
+using callback_pair_t = std::pair<gdbpy_initialize_file_func_t,
+				  gdbpy_finalize_file_func_t>;
+
+/* A list of callback pairs.  */
+
+static std::vector<callback_pair_t> gdbpy_setup_callbacks;
+
+/* See python-internal.h.  */
+
+void
+gdbpy_register_subsystem (gdbpy_initialize_file_func_t init,
+			  gdbpy_finalize_file_func_t fini)
+{
+  gdbpy_setup_callbacks.emplace_back (init, fini);
+}
+
 /* Architecture and language to be used in callbacks from
    the Python interpreter.  */
 struct gdbarch *gdbpy_enter::python_gdbarch;
@@ -1934,7 +1954,14 @@  finalize_python (void *ignore)
   (void) PyGILState_Ensure ();
   gdbpy_enter::finalize ();
 
-  gdbpy_finalize_micommands ();
+  /* Every item in GDBPY_SETUP_CALLBACKS is a pair.  The second item in
+     the pair is (when not nullptr), a function to call to cleanup a
+     particular Python subsystem within GDB.  */
+  for (const auto &p : gdbpy_setup_callbacks)
+    {
+      if (p.second != nullptr)
+	p.second ();
+    }
 
   Py_Finalize ();
 
@@ -2122,42 +2149,14 @@  do_start_initialization ()
 				 gdbpy_gdberror_exc) < 0)
     return false;
 
-  gdbpy_initialize_gdb_readline ();
-
-  if (gdbpy_initialize_auto_load () < 0
-      || gdbpy_initialize_values () < 0
-      || gdbpy_initialize_disasm () < 0
-      || gdbpy_initialize_frames () < 0
-      || gdbpy_initialize_commands () < 0
-      || gdbpy_initialize_instruction () < 0
-      || gdbpy_initialize_record () < 0
-      || gdbpy_initialize_btrace () < 0
-      || gdbpy_initialize_symbols () < 0
-      || gdbpy_initialize_symtabs () < 0
-      || gdbpy_initialize_blocks () < 0
-      || gdbpy_initialize_functions () < 0
-      || gdbpy_initialize_parameters () < 0
-      || gdbpy_initialize_types () < 0
-      || gdbpy_initialize_pspace () < 0
-      || gdbpy_initialize_objfile () < 0
-      || gdbpy_initialize_breakpoints () < 0
-      || gdbpy_initialize_breakpoint_locations () < 0
-      || gdbpy_initialize_finishbreakpoints () < 0
-      || gdbpy_initialize_lazy_string () < 0
-      || gdbpy_initialize_linetable () < 0
-      || gdbpy_initialize_thread () < 0
-      || gdbpy_initialize_inferior () < 0
-      || gdbpy_initialize_eventregistry () < 0
-      || gdbpy_initialize_event () < 0
-      || gdbpy_initialize_arch () < 0
-      || gdbpy_initialize_registers () < 0
-      || gdbpy_initialize_xmethods () < 0
-      || gdbpy_initialize_unwind () < 0
-      || gdbpy_initialize_membuf () < 0
-      || gdbpy_initialize_connection () < 0
-      || gdbpy_initialize_tui () < 0
-      || gdbpy_initialize_micommands () < 0)
-    return false;
+  /* Every item in GDBPY_SETUP_CALLBACKS is a pair.  The first item in the
+     pair is (when not nullptr), a function to call to setup a particular
+     Python subsystem within GDB.  */
+  for (const auto &p : gdbpy_setup_callbacks)
+    {
+      if (p.first != nullptr && p.first () < 0)
+	return false;
+    }
 
 #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)	\
   if (gdbpy_initialize_event_generic (&name##_event_object_type, py_name) < 0) \