| /* 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); |
| } |