summaryrefslogtreecommitdiff
path: root/gdb/python/py-micmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python/py-micmd.c')
-rw-r--r--gdb/python/py-micmd.c739
1 files changed, 739 insertions, 0 deletions
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
new file mode 100644
index 00000000000..79e2575f789
--- /dev/null
+++ b/gdb/python/py-micmd.c
@@ -0,0 +1,739 @@
+/* MI Command Set for GDB, the GNU debugger.
+
+ Copyright (C) 2019-2022 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/>. */
+
+/* GDB/MI commands implemented in Python. */
+
+#include "defs.h"
+#include "python-internal.h"
+#include "arch-utils.h"
+#include "charset.h"
+#include "language.h"
+#include "mi/mi-cmds.h"
+#include "mi/mi-parse.h"
+#include "cli/cli-cmds.h"
+#include <string>
+
+/* Debugging of Python MI commands. */
+
+static bool pymicmd_debug;
+
+/* Implementation of "show debug py-micmd". */
+
+static void
+show_pymicmd_debug (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ gdb_printf (file, _("Python MI command debugging is %s.\n"), value);
+}
+
+/* Print a "py-micmd" debug statement. */
+
+#define pymicmd_debug_printf(fmt, ...) \
+ debug_prefixed_printf_cond (pymicmd_debug, "py-micmd", fmt, ##__VA_ARGS__)
+
+/* Print a "py-micmd" enter/exit debug statements. */
+
+#define PYMICMD_SCOPED_DEBUG_ENTER_EXIT \
+ scoped_debug_enter_exit (pymicmd_debug, "py-micmd")
+
+struct mi_command_py;
+
+/* Representation of a Python gdb.MICommand object. */
+
+struct micmdpy_object
+{
+ PyObject_HEAD
+
+ /* The object representing this command in the MI command table. This
+ pointer can be nullptr if the command is not currently installed into
+ the MI command table (see gdb.MICommand.installed property). */
+ struct mi_command_py *mi_command;
+
+ /* The string representing the name of this command, without the leading
+ dash. This string is never nullptr once the Python object has been
+ initialised.
+
+ The memory for this string was allocated with malloc, and needs to be
+ deallocated with free when the Python object is deallocated.
+
+ When the MI_COMMAND field is not nullptr, then the mi_command_py
+ object's name will point back to this string. */
+ char *mi_command_name;
+};
+
+/* The MI command implemented in Python. */
+
+struct mi_command_py : public mi_command
+{
+ /* Constructs a new mi_command_py object. NAME is command name without
+ leading dash. OBJECT is a reference to a Python object implementing
+ the command. This object must inherit from gdb.MICommand and must
+ implement the invoke method. */
+
+ mi_command_py (const char *name, micmdpy_object *object)
+ : mi_command (name, nullptr),
+ m_pyobj (gdbpy_ref<micmdpy_object>::new_reference (object))
+ {
+ pymicmd_debug_printf ("this = %p", this);
+ m_pyobj->mi_command = this;
+ }
+
+ ~mi_command_py ()
+ {
+ /* The Python object representing a MI command contains a pointer back
+ to this c++ object. We can safely set this pointer back to nullptr
+ now, to indicate the Python object no longer references a valid c++
+ object.
+
+ However, the Python object also holds the storage for our name
+ string. We can't clear that here as our parent's destructor might
+ still want to reference that string. Instead we rely on the Python
+ object deallocator to free that memory, and reset the pointer. */
+ m_pyobj->mi_command = nullptr;
+
+ pymicmd_debug_printf ("this = %p", this);
+ };
+
+ /* Validate that CMD_OBJ, a non-nullptr pointer, is installed into the MI
+ command table correctly. This function looks up the command in the MI
+ command table and checks that the object we get back references
+ CMD_OBJ. This function is only intended for calling within a
+ gdb_assert. This function performs many assertions internally, and
+ then always returns true. */
+ static void validate_installation (micmdpy_object *cmd_obj);
+
+ /* Update M_PYOBJ to NEW_PYOBJ. The pointer from M_PYOBJ that points
+ back to this object is swapped with the pointer in NEW_PYOBJ, which
+ must be nullptr, so that NEW_PYOBJ now points back to this object.
+ Additionally our parent's name string is stored in M_PYOBJ, so we
+ swap the name string with NEW_PYOBJ.
+
+ Before this call M_PYOBJ is the Python object representing this MI
+ command object. After this call has completed, NEW_PYOBJ now
+ represents this MI command object. */
+ void swap_python_object (micmdpy_object *new_pyobj)
+ {
+ /* Current object has a backlink, new object doesn't have a backlink. */
+ gdb_assert (m_pyobj->mi_command != nullptr);
+ gdb_assert (new_pyobj->mi_command == nullptr);
+
+ /* Clear the current M_PYOBJ's backlink, set NEW_PYOBJ's backlink. */
+ std::swap (new_pyobj->mi_command, m_pyobj->mi_command);
+
+ /* Both object have names. */
+ gdb_assert (m_pyobj->mi_command_name != nullptr);
+ gdb_assert (new_pyobj->mi_command_name != nullptr);
+
+ /* mi_command::m_name is the string owned by the current object. */
+ gdb_assert (m_pyobj->mi_command_name == this->name ());
+
+ /* The name in mi_command::m_name is owned by the current object. Rather
+ than changing the value of mi_command::m_name (which is not accessible
+ from here) to point to the name owned by the new object, swap the names
+ of the two objects, since we know they are identical strings. */
+ gdb_assert (strcmp (new_pyobj->mi_command_name,
+ m_pyobj->mi_command_name) == 0);
+ std::swap (new_pyobj->mi_command_name, m_pyobj->mi_command_name);
+
+ /* Take a reference to the new object, drop the reference to the current
+ object. */
+ m_pyobj = gdbpy_ref<micmdpy_object>::new_reference (new_pyobj);
+ }
+
+ /* Called when the MI command is invoked. */
+ virtual void invoke(struct mi_parse *parse) const override;
+
+private:
+ /* The Python object representing this MI command. */
+ gdbpy_ref<micmdpy_object> m_pyobj;
+};
+
+using mi_command_py_up = std::unique_ptr<mi_command_py>;
+
+extern PyTypeObject micmdpy_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("micmdpy_object");
+
+/* Holds a Python object containing the string 'invoke'. */
+
+static PyObject *invoke_cst;
+
+/* Convert KEY_OBJ into a string that can be used as a field name in MI
+ output. KEY_OBJ must be a Python string object, and must only contain
+ characters suitable for use as an MI field name.
+
+ If KEY_OBJ is not a string, or if KEY_OBJ contains invalid characters,
+ then an error is thrown. Otherwise, KEY_OBJ is converted to a string
+ and returned. */
+
+static gdb::unique_xmalloc_ptr<char>
+py_object_to_mi_key (PyObject *key_obj)
+{
+ /* The key must be a string. */
+ if (!PyUnicode_Check (key_obj))
+ {
+ gdbpy_ref<> key_repr (PyObject_Repr (key_obj));
+ gdb::unique_xmalloc_ptr<char> key_repr_string;
+ if (key_repr != nullptr)
+ key_repr_string = python_string_to_target_string (key_repr.get ());
+ if (key_repr_string == nullptr)
+ gdbpy_handle_exception ();
+
+ gdbpy_error (_("non-string object used as key: %s"),
+ key_repr_string.get ());
+ }
+
+ gdb::unique_xmalloc_ptr<char> key_string
+ = python_string_to_target_string (key_obj);
+ if (key_string == nullptr)
+ gdbpy_handle_exception ();
+
+ /* Predicate function, returns true if NAME is a valid field name for use
+ in MI result output, otherwise, returns false. */
+ auto is_valid_key_name = [] (const char *name) -> bool
+ {
+ gdb_assert (name != nullptr);
+
+ if (*name == '\0' || !isalpha (*name))
+ return false;
+
+ for (; *name != '\0'; ++name)
+ if (!isalnum (*name) && *name != '_' && *name != '-')
+ return false;
+
+ return true;
+ };
+
+ if (!is_valid_key_name (key_string.get ()))
+ {
+ if (*key_string.get () == '\0')
+ gdbpy_error (_("Invalid empty key in MI result"));
+ else
+ gdbpy_error (_("Invalid key in MI result: %s"), key_string.get ());
+ }
+
+ return key_string;
+}
+
+/* Serialize RESULT and print it in MI format to the current_uiout.
+ FIELD_NAME is used as the name of this result field.
+
+ RESULT can be a dictionary, a sequence, an iterator, or an object that
+ can be converted to a string, these are converted to the matching MI
+ output format (dictionaries as tuples, sequences and iterators as lists,
+ and strings as named fields).
+
+ If anything goes wrong while formatting the output then an error is
+ thrown.
+
+ This function is the recursive inner core of serialize_mi_result, and
+ should only be called from that function. */
+
+static void
+serialize_mi_result_1 (PyObject *result, const char *field_name)
+{
+ struct ui_out *uiout = current_uiout;
+
+ if (PyDict_Check (result))
+ {
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ ui_out_emit_tuple tuple_emitter (uiout, field_name);
+ while (PyDict_Next (result, &pos, &key, &value))
+ {
+ gdb::unique_xmalloc_ptr<char> key_string
+ (py_object_to_mi_key (key));
+ serialize_mi_result_1 (value, key_string.get ());
+ }
+ }
+ else if (PySequence_Check (result) && !PyUnicode_Check (result))
+ {
+ ui_out_emit_list list_emitter (uiout, field_name);
+ Py_ssize_t len = PySequence_Size (result);
+ if (len == -1)
+ gdbpy_handle_exception ();
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ gdbpy_ref<> item (PySequence_ITEM (result, i));
+ if (item == nullptr)
+ gdbpy_handle_exception ();
+ serialize_mi_result_1 (item.get (), nullptr);
+ }
+ }
+ else if (PyIter_Check (result))
+ {
+ gdbpy_ref<> item;
+ ui_out_emit_list list_emitter (uiout, field_name);
+ while (true)
+ {
+ item.reset (PyIter_Next (result));
+ if (item == nullptr)
+ {
+ if (PyErr_Occurred () != nullptr)
+ gdbpy_handle_exception ();
+ break;
+ }
+ serialize_mi_result_1 (item.get (), nullptr);
+ }
+ }
+ else
+ {
+ gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
+ if (string == nullptr)
+ gdbpy_handle_exception ();
+ uiout->field_string (field_name, string.get ());
+ }
+}
+
+/* Serialize RESULT and print it in MI format to the current_uiout.
+
+ This function handles the top-level result initially returned from the
+ invoke method of the Python command implementation. At the top-level
+ the result must be a dictionary. The values within this dictionary can
+ be a wider range of types. Handling the values of the top-level
+ dictionary is done by serialize_mi_result_1, see that function for more
+ details.
+
+ If anything goes wrong while parsing and printing the MI output then an
+ error is thrown. */
+
+static void
+serialize_mi_result (PyObject *result)
+{
+ /* At the top-level, the result must be a dictionary. */
+
+ if (!PyDict_Check (result))
+ gdbpy_error (_("Result from invoke must be a dictionary"));
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next (result, &pos, &key, &value))
+ {
+ gdb::unique_xmalloc_ptr<char> key_string
+ (py_object_to_mi_key (key));
+ serialize_mi_result_1 (value, key_string.get ());
+ }
+}
+
+/* Called when the MI command is invoked. PARSE contains the parsed
+ command line arguments from the user. */
+
+void
+mi_command_py::invoke (struct mi_parse *parse) const
+{
+ PYMICMD_SCOPED_DEBUG_ENTER_EXIT;
+
+ pymicmd_debug_printf ("this = %p, name = %s", this, name ());
+
+ mi_parse_argv (parse->args, parse);
+
+ if (parse->argv == nullptr)
+ error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
+
+
+ gdbpy_enter enter_py;
+
+ /* Place all the arguments into a list which we pass as a single argument
+ to the MI command's invoke method. */
+ gdbpy_ref<> argobj (PyList_New (parse->argc));
+ if (argobj == nullptr)
+ gdbpy_handle_exception ();
+
+ for (int i = 0; i < parse->argc; ++i)
+ {
+ gdbpy_ref<> str (PyUnicode_Decode (parse->argv[i],
+ strlen (parse->argv[i]),
+ host_charset (), nullptr));
+ if (PyList_SetItem (argobj.get (), i, str.release ()) < 0)
+ gdbpy_handle_exception ();
+ }
+
+ gdb_assert (this->m_pyobj != nullptr);
+ gdb_assert (PyErr_Occurred () == nullptr);
+ gdbpy_ref<> result
+ (PyObject_CallMethodObjArgs ((PyObject *) this->m_pyobj.get (), invoke_cst,
+ argobj.get (), nullptr));
+ if (result == nullptr)
+ gdbpy_handle_exception ();
+
+ if (result != Py_None)
+ serialize_mi_result (result.get ());
+}
+
+/* See declaration above. */
+
+void
+mi_command_py::validate_installation (micmdpy_object *cmd_obj)
+{
+ gdb_assert (cmd_obj != nullptr);
+ mi_command_py *cmd = cmd_obj->mi_command;
+ gdb_assert (cmd != nullptr);
+ const char *name = cmd_obj->mi_command_name;
+ gdb_assert (name != nullptr);
+ gdb_assert (name == cmd->name ());
+ mi_command *mi_cmd = mi_cmd_lookup (name);
+ gdb_assert (mi_cmd == cmd);
+ gdb_assert (cmd->m_pyobj == cmd_obj);
+}
+
+/* Return CMD as an mi_command_py if it is a Python MI command, else
+ nullptr. */
+
+static mi_command_py *
+as_mi_command_py (mi_command *cmd)
+{
+ return dynamic_cast<mi_command_py *> (cmd);
+}
+
+/* Uninstall OBJ, making the MI command represented by OBJ unavailable for
+ use by the user. On success 0 is returned, otherwise -1 is returned
+ and a Python exception will be set. */
+
+static int
+micmdpy_uninstall_command (micmdpy_object *obj)
+{
+ PYMICMD_SCOPED_DEBUG_ENTER_EXIT;
+
+ gdb_assert (obj->mi_command != nullptr);
+ gdb_assert (obj->mi_command_name != nullptr);
+
+ pymicmd_debug_printf ("name = %s", obj->mi_command_name);
+
+ /* Remove the command from the internal MI table of commands. This will
+ cause the mi_command_py object to be deleted, which will clear the
+ backlink in OBJ. */
+ bool removed = remove_mi_cmd_entry (obj->mi_command->name ());
+ gdb_assert (removed);
+ gdb_assert (obj->mi_command == nullptr);
+
+ return 0;
+}
+
+/* Install OBJ as a usable MI command. Return 0 on success, and -1 on
+ error, in which case, a Python error will have been set.
+
+ After successful completion the command name associated with OBJ will
+ be installed in the MI command table (so it can be found if the user
+ enters that command name), additionally, OBJ will have been added to
+ the gdb._mi_commands dictionary (using the command name as its key),
+ this will ensure that OBJ remains live even if the user gives up all
+ references. */
+
+static int
+micmdpy_install_command (micmdpy_object *obj)
+{
+ PYMICMD_SCOPED_DEBUG_ENTER_EXIT;
+
+ gdb_assert (obj->mi_command == nullptr);
+ gdb_assert (obj->mi_command_name != nullptr);
+
+ pymicmd_debug_printf ("name = %s", obj->mi_command_name);
+
+ /* Look up this command name in MI_COMMANDS, a command with this name may
+ already exist. */
+ mi_command *cmd = mi_cmd_lookup (obj->mi_command_name);
+ mi_command_py *cmd_py = as_mi_command_py (cmd);
+
+ if (cmd != nullptr && cmd_py == nullptr)
+ {
+ /* There is already an MI command registered with that name, and it's not
+ a Python one. Forbid replacing a non-Python MI command. */
+ PyErr_SetString (PyExc_RuntimeError,
+ _("unable to add command, name is already in use"));
+ return -1;
+ }
+
+ if (cmd_py != nullptr)
+ {
+ /* There is already a Python MI command registered with that name, swap
+ in the new gdb.MICommand implementation. */
+ cmd_py->swap_python_object (obj);
+ }
+ else
+ {
+ /* There's no MI command registered with that name at all, create one. */
+ mi_command_py_up mi_cmd (new mi_command_py (obj->mi_command_name, obj));
+
+ /* Add the command to the gdb internal MI command table. */
+ bool result = insert_mi_cmd_entry (std::move (mi_cmd));
+ gdb_assert (result);
+ }
+
+ return 0;
+}
+
+/* Implement gdb.MICommand.__init__. The init method takes the name of
+ the MI command as the first argument, which must be a string, starting
+ with a single dash. */
+
+static int
+micmdpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PYMICMD_SCOPED_DEBUG_ENTER_EXIT;
+
+ micmdpy_object *cmd = (micmdpy_object *) self;
+
+ static const char *keywords[] = { "name", nullptr };
+ const char *name;
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s", keywords,
+ &name))
+ return -1;
+
+ /* Validate command name */
+ const int name_len = strlen (name);
+ if (name_len == 0)
+ {
+ PyErr_SetString (PyExc_ValueError, _("MI command name is empty."));
+ return -1;
+ }
+ else if ((name_len < 2) || (name[0] != '-') || !isalnum (name[1]))
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("MI command name does not start with '-'"
+ " followed by at least one letter or digit."));
+ return -1;
+ }
+ else
+ {
+ for (int i = 2; i < name_len; i++)
+ {
+ if (!isalnum (name[i]) && name[i] != '-')
+ {
+ PyErr_Format
+ (PyExc_ValueError,
+ _("MI command name contains invalid character: %c."),
+ name[i]);
+ return -1;
+ }
+ }
+
+ /* Skip over the leading dash. For the rest of this function the
+ dash is not important. */
+ ++name;
+ }
+
+ /* If this object already has a name set, then this object has been
+ initialized before. We handle this case a little differently. */
+ if (cmd->mi_command_name != nullptr)
+ {
+ /* First, we don't allow the user to change the MI command name.
+ Supporting this would be tricky as we would need to delete the
+ mi_command_py from the MI command table, however, the user might
+ be trying to perform this reinitialization from within the very
+ command we're about to delete... it all gets very messy.
+
+ So, for now at least, we don't allow this. This doesn't seem like
+ an excessive restriction. */
+ if (strcmp (cmd->mi_command_name, name) != 0)
+ {
+ PyErr_SetString
+ (PyExc_ValueError,
+ _("can't reinitialize object with a different command name"));
+ return -1;
+ }
+
+ /* If there's already an object registered with the MI command table,
+ then we're done. That object must be a mi_command_py, which
+ should reference back to this micmdpy_object. */
+ if (cmd->mi_command != nullptr)
+ {
+ mi_command_py::validate_installation (cmd);
+ return 0;
+ }
+ }
+ else
+ cmd->mi_command_name = xstrdup (name);
+
+ /* Now we can install this mi_command_py in the MI command table. */
+ return micmdpy_install_command (cmd);
+}
+
+/* Called when a gdb.MICommand object is deallocated. */
+
+static void
+micmdpy_dealloc (PyObject *obj)
+{
+ PYMICMD_SCOPED_DEBUG_ENTER_EXIT;
+
+ micmdpy_object *cmd = (micmdpy_object *) obj;
+
+ /* If the Python object failed to initialize, then the name field might
+ be nullptr. */
+ pymicmd_debug_printf ("obj = %p, name = %s", cmd,
+ (cmd->mi_command_name == nullptr
+ ? "(null)" : cmd->mi_command_name));
+
+ /* As the mi_command_py object holds a reference to the micmdpy_object,
+ the only way the dealloc function can be called is if the mi_command_py
+ object has been deleted, in which case the following assert will
+ hold. */
+ gdb_assert (cmd->mi_command == nullptr);
+
+ /* Free the memory that holds the command name. */
+ xfree (cmd->mi_command_name);
+ cmd->mi_command_name = nullptr;
+
+ /* Finally, free the memory for this Python object. */
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* Python initialization for the MI commands components. */
+
+int
+gdbpy_initialize_micommands ()
+{
+ micmdpy_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&micmdpy_object_type) < 0)
+ return -1;
+
+ if (gdb_pymodule_addobject (gdb_module, "MICommand",
+ (PyObject *) &micmdpy_object_type)
+ < 0)
+ return -1;
+
+ invoke_cst = PyUnicode_FromString ("invoke");
+ if (invoke_cst == nullptr)
+ return -1;
+
+ return 0;
+}
+
+void
+gdbpy_finalize_micommands ()
+{
+ /* mi_command_py objects hold references to micmdpy_object objects. They must
+ be dropped before the Python interpreter is finalized. Do so by removing
+ those MI command entries, thus deleting the mi_command_py objects. */
+ remove_mi_cmd_entries ([] (mi_command *cmd)
+ {
+ return as_mi_command_py (cmd) != nullptr;
+ });
+}
+
+/* Get the gdb.MICommand.name attribute, returns a string, the name of this
+ MI command. */
+
+static PyObject *
+micmdpy_get_name (PyObject *self, void *closure)
+{
+ struct micmdpy_object *micmd_obj = (struct micmdpy_object *) self;
+
+ gdb_assert (micmd_obj->mi_command_name != nullptr);
+ std::string name_str = string_printf ("-%s", micmd_obj->mi_command_name);
+ return PyUnicode_FromString (name_str.c_str ());
+}
+
+/* Get the gdb.MICommand.installed property. Returns true if this MI
+ command is installed into the MI command table, otherwise returns
+ false. */
+
+static PyObject *
+micmdpy_get_installed (PyObject *self, void *closure)
+{
+ struct micmdpy_object *micmd_obj = (struct micmdpy_object *) self;
+
+ if (micmd_obj->mi_command == nullptr)
+ Py_RETURN_FALSE;
+ Py_RETURN_TRUE;
+}
+
+/* Set the gdb.MICommand.installed property. The property can be set to
+ either true or false. Setting the property to true will cause the
+ command to be installed into the MI command table (if it isn't
+ already), while setting this property to false will cause the command
+ to be removed from the MI command table (if it is present). */
+
+static int
+micmdpy_set_installed (PyObject *self, PyObject *newvalue, void *closure)
+{
+ struct micmdpy_object *micmd_obj = (struct micmdpy_object *) self;
+
+ bool installed_p = PyObject_IsTrue (newvalue);
+ if (installed_p == (micmd_obj->mi_command != nullptr))
+ return 0;
+
+ if (installed_p)
+ return micmdpy_install_command (micmd_obj);
+ else
+ return micmdpy_uninstall_command (micmd_obj);
+}
+
+/* The gdb.MICommand properties. */
+
+static gdb_PyGetSetDef micmdpy_object_getset[] = {
+ { "name", micmdpy_get_name, nullptr, "The command's name.", nullptr },
+ { "installed", micmdpy_get_installed, micmdpy_set_installed,
+ "Is this command installed for use.", nullptr },
+ { nullptr } /* Sentinel. */
+};
+
+/* The gdb.MICommand descriptor. */
+
+PyTypeObject micmdpy_object_type = {
+ PyVarObject_HEAD_INIT (nullptr, 0) "gdb.MICommand", /*tp_name */
+ sizeof (micmdpy_object), /*tp_basicsize */
+ 0, /*tp_itemsize */
+ micmdpy_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 */
+ 0, /*tp_str */
+ 0, /*tp_getattro */
+ 0, /*tp_setattro */
+ 0, /*tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags */
+ "GDB mi-command 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 */
+ micmdpy_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ micmdpy_init, /* tp_init */
+ 0, /* tp_alloc */
+};
+
+void _initialize_py_micmd ();
+void
+_initialize_py_micmd ()
+{
+ add_setshow_boolean_cmd
+ ("py-micmd", class_maintenance, &pymicmd_debug,
+ _("Set Python micmd debugging."),
+ _("Show Python micmd debugging."),
+ _("When on, Python micmd debugging is enabled."),
+ nullptr,
+ show_pymicmd_debug,
+ &setdebuglist, &showdebuglist);
+}