[RFC][patch 5/9] permit GDB commands implemented in Python

Thiago Jung Bauermann
2008-04-29T15:58:49Z

[-- Attachment #1: python-commands.diff --]
[-- Type: text/plain, Size: 15002 bytes --]

2008-04-29 Tom Tromey <tromey@redhat.com>

* Makefile.in (SUBDIR_PYTHON_OBS): Add python-cmd.o.
(SUBDIR_PYTHON_SRCS): Add python/cmd.c.
(python-cmd.o): New target.
* cli/cli-cmds.c (edit_command): Delete log10 code.
* cli/cli-decode.c (delete_cmd): Rewrote. Handle 'destroyer'
field.
(add_cmd): Initialize 'destroyer'.
* cli/cli-decode.h (struct cmd_list_element): Added 'destroyer'
field.
* python/cmd.c: New file.
* python/python-internal.h (gdbpy_initialize_commands): Declare.
* python/python.c (demand_python): Call gdbpy_initialize_commands.

Index: tromey.git/gdb/Makefile.in
===================================================================
--- tromey.git.orig/gdb/Makefile.in 2008-04-29 11:05:15.000000000 -0300
+++ tromey.git/gdb/Makefile.in 2008-04-29 11:05:20.000000000 -0300
@@ -263,11 +263,13 @@ SUBDIR_TUI_CFLAGS= \
SUBDIR_PYTHON_OBS = \
python.o \
python-breakpoint.o \
+ python-cmd.o \
python-hooks.o \
python-value.o
SUBDIR_PYTHON_SRCS = \
python/python.c \
python/breakpoint.c \
+ python/cmd.c \
python/hooks.c \
python/value.c
SUBDIR_PYTHON_DEPS =
@@ -3422,6 +3424,11 @@ python-breakpoint.o: $(srcdir)/python/br
$(breakpoint_h) $(gdbcmd_h)
$(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \
$(srcdir)/python/breakpoint.c -o python-breakpoint.o
+python-cmd.o: $(srcdir)/python/cmd.c $(defs_h) $(python_h) $(value_h) \
+ $(exceptions_h) $(python_internal_h) $(charset_h) \
+ $(gdbcmd_h) $(cli_decode_h)
+ $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \
+ $(srcdir)/python/cmd.c -o python-cmd.o
python-hooks.o: $(srcdir)/python/hooks.c $(defs_h) $(cli_decode_h) \
$(charset_h) $(gdb_events_h) $(python_h) $(python_internal_h)
$(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \
Index: tromey.git/gdb/cli/cli-cmds.c
===================================================================
--- tromey.git.orig/gdb/cli/cli-cmds.c 2008-04-29 10:57:39.000000000 -0300
+++ tromey.git/gdb/cli/cli-cmds.c 2008-04-29 11:05:20.000000000 -0300
@@ -615,8 +615,7 @@ edit_command (char *arg, int from_tty)
struct symtab_and_line sal;
struct symbol *sym;
char *arg1;
- int cmdlen, log10;
- unsigned m;
+ int cmdlen;
char *editor;
char *p, *fn;

@@ -692,10 +691,6 @@ edit_command (char *arg, int from_tty)
if ((editor = (char *) getenv ("EDITOR")) == NULL)
editor = "/bin/ex";

- /* Approximate base-10 log of line to 1 unit for digit count */
- for(log10=32, m=0x80000000; !(sal.line & m) && log10>0; log10--, m=m>>1);
- log10 = 1 + (int)((log10 + (0 == ((m-1) & sal.line)))/3.32192809);
-
/* If we don't already know the full absolute file name of the
source file, find it now. */
if (!sal.symtab->fullname)
Index: tromey.git/gdb/cli/cli-decode.c
===================================================================
--- tromey.git.orig/gdb/cli/cli-decode.c 2008-04-29 10:57:39.000000000 -0300
+++ tromey.git/gdb/cli/cli-decode.c 2008-04-29 11:05:20.000000000 -0300
@@ -190,6 +190,7 @@ add_cmd (char *name, enum command_class
c->allow_unknown = 0;
c->abbrev_flag = 0;
set_cmd_completer (c, make_symbol_completion_list);
+ c->destroyer = NULL;
c->type = not_set_cmd;
c->var = NULL;
c->var_type = var_boolean;
@@ -614,37 +615,32 @@ add_setshow_zinteger_cmd (char *name, en
void
delete_cmd (char *name, struct cmd_list_element **list)
{
- struct cmd_list_element *c;
- struct cmd_list_element *p;
+ struct cmd_list_element *iter;
+ struct cmd_list_element **previous_chain_ptr;
+
+ previous_chain_ptr = list;

- while (*list && strcmp ((*list)->name, name) == 0)
+ for (iter = *previous_chain_ptr; iter; iter = *previous_chain_ptr)
{
- if ((*list)->hookee_pre)
- (*list)->hookee_pre->hook_pre = 0; /* Hook slips out of its mouth */
- if ((*list)->hookee_post)
- (*list)->hookee_post->hook_post = 0; /* Hook slips out of its bottom */
- p = (*list)->next;
- xfree (* list);
- *list = p;
- }
+ if (strcmp (iter->name, name) == 0)
+ {
+ if (iter->destroyer)
+ iter->destroyer (iter, iter->context);
+ if (iter->hookee_pre)
+ iter->hookee_pre->hook_pre = 0;
+ if (iter->hookee_post)
+ iter->hookee_post->hook_post = 0;
+
+ /* Update the link. Note that we don't change
+ previous_chain_ptr; next time through the loop this must
+ stay the same. */
+ *previous_chain_ptr = iter->next;

- if (*list)
- for (c = *list; c->next;)
- {
- if (strcmp (c->next->name, name) == 0)
- {
- if (c->next->hookee_pre)
- c->next->hookee_pre->hook_pre = 0; /* hooked cmd gets away. */
- if (c->next->hookee_post)
- c->next->hookee_post->hook_post = 0; /* remove post hook */
- /* :( no fishing metaphore */
- p = c->next->next;
- xfree (c->next);
- c->next = p;
- }
- else
- c = c->next;
- }
+ xfree (iter);
+ }
+ else
+ previous_chain_ptr = &iter->next;
+ }
}
\f
/* Shorthands to the commands above. */
Index: tromey.git/gdb/cli/cli-decode.h
===================================================================
--- tromey.git.orig/gdb/cli/cli-decode.h 2008-04-29 10:57:39.000000000 -0300
+++ tromey.git/gdb/cli/cli-decode.h 2008-04-29 11:05:20.000000000 -0300
@@ -168,6 +168,11 @@ struct cmd_list_element
"oobar"; if WORD is "baz/foo", return "baz/foobar". */
char **(*completer) (char *text, char *word);

+ /* Destruction routine for this command. If non-NULL, this is
+ called when this command instance is destroyed. This may be
+ used to finalize the CONTEXT field, if needed. */
+ void (*destroyer) (struct cmd_list_element *self, void *context);
+
/* Type of "set" or "show" command (or SET_NOT_SET if not "set"
or "show"). */
cmd_types type;
Index: tromey.git/gdb/python/cmd.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ tromey.git/gdb/python/cmd.c 2008-04-29 11:05:40.000000000 -0300
@@ -0,0 +1,255 @@
+/* gdb commands implemented in Python
+
+ Copyright (C) 2008 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/>. */
+
+
+#include "defs.h"
+#include "value.h"
+#include "exceptions.h"
+#include "python-internal.h"
+#include "charset.h"
+#include "gdbcmd.h"
+#include "cli/cli-decode.h"
+
+/* A gdb command. For the time being only ordinary commands (not
+ set/show commands) are allowed. */
+struct cmdpy_object
+{
+ PyObject_HEAD
+
+ /* The corresponding gdb command object, or NULL if the command is
+ no longer installed. */
+ /* It isn't clear if we will ever care about this. */
+ struct cmd_list_element *command;
+};
+
+typedef struct cmdpy_object cmdpy_object;
+
+static PyObject *cmdpy_dont_repeat (PyObject *self, PyObject *args);
+
+static PyMethodDef cmdpy_object_methods[] =
+{
+ { "dont_repeat", cmdpy_dont_repeat, METH_NOARGS,
+ "Prevent command repetition when user enters empty line." },
+
+ { 0 }
+};
+
+static PyTypeObject cmdpy_object_type =
+{
+ PyObject_HEAD_INIT (NULL)
+ 0, /*ob_size*/
+ "gdb.Command", /*tp_name*/
+ sizeof (cmdpy_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_BASETYPE, /*tp_flags*/
+ "GDB command object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ cmdpy_object_methods /* tp_methods */
+};
+
+\f
+
+static PyObject *
+cmdpy_dont_repeat (PyObject *self, PyObject *args)
+{
+ dont_repeat ();
+ Py_RETURN_NONE;
+}
+
+
+\f
+
+/* Called if the gdb cmd_list_element is destroyed. */
+static void
+cmdpy_destroyer (struct cmd_list_element *self, void *context)
+{
+ /* Release our hold on the command object. */
+ cmdpy_object *cmd = (cmdpy_object *) context;
+ cmd->command = NULL;
+ Py_DECREF (cmd);
+
+ /* We allocated the name and doc string. */
+ xfree (self->name);
+ xfree (self->doc);
+}
+
+/* Called by gdb to invoke the command. */
+static void
+cmdpy_function (struct cmd_list_element *command, char *args, int from_tty)
+{
+ cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command);
+ PyObject *method, *argobj, *ttyobj, *result;
+
+ if (! obj)
+ error ("Invalid invocation of Python command object");
+ if (! PyObject_HasAttrString ((PyObject *) obj, "invoke"))
+ error ("Python command object missing 'invoke' method");
+
+ method = PyString_FromString ("invoke");
+ if (! args)
+ {
+ argobj = Py_None;
+ Py_INCREF (argobj);
+ }
+ else
+ {
+ argobj = PyString_FromString (args);
+ if (! argobj)
+ error ("Couldn't convert arguments to Python string");
+ }
+ ttyobj = from_tty ? Py_True : Py_False;
+ Py_INCREF (ttyobj);
+ result = PyObject_CallMethodObjArgs ((PyObject *) obj, method, argobj,
+ ttyobj, NULL);
+ Py_DECREF (method);
+ Py_DECREF (argobj);
+ Py_DECREF (ttyobj);
+ if (! result)
+ {
+ /* FIXME: pretty lame. */
+ PyErr_Print ();
+ error ("Error occurred in Python command");
+ }
+ Py_DECREF (result);
+}
+
+/* Object initializer; sets up gdb-side structures for command.
+
+ Use: __init__(NAME, CMDCLASS).
+
+ NAME is the name of the command; spaces in the name mean to look up
+ initial segments as prefixes.
+
+ CMDCLASS is the kind of command. It should be one of the COMMAND_*
+ constants defined in the gdb module.
+
+ The documentation for the command is taken from the doc string for
+ the python class.
+
+*/
+static int
+cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
+{
+ cmdpy_object *obj = (cmdpy_object *) self;
+ char *name;
+ int cmdtype;
+ volatile struct gdb_exception except;
+
+ if (obj->command)
+ {
+ /* Note: this is apparently not documented in Python. We return
+ 0 for success, -1 for failure. */
+ PyErr_Format (PyExc_RuntimeError, "Command object already initialized");
+ return -1;
+ }
+
+ if (! PyArg_ParseTuple (args, "si", &name, &cmdtype))
+ return -1;
+
+ if (cmdtype != no_class && cmdtype != class_run
+ && cmdtype != class_vars && cmdtype != class_stack
+ && cmdtype != class_files && cmdtype != class_support
+ && cmdtype != class_info && cmdtype != class_breakpoint
+ && cmdtype != class_trace && cmdtype != class_obscure
+ && cmdtype != class_maintenance)
+ {
+ PyErr_Format (PyExc_RuntimeError, "invalid command class argument");
+ return -1;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* FIXME: decoding NAME if it has spaces. Also, how to add new
+ prefix commands? */
+ struct cmd_list_element *cmd = add_cmd (xstrdup (name),
+ (enum command_class) cmdtype,
+ NULL,
+ xstrdup ("FIXME: docstring"),
+ &cmdlist /* FIXME */);
+ /* There appears to be no API to set this. */
+ cmd->func = cmdpy_function;
+ /* FIXME: route completion requests through user object. */
+
+ obj->command = cmd;
+ Py_INCREF (self);
+ set_cmd_context (cmd, self);
+ }
+ if (except.reason < 0)
+ {
+ PyErr_Format (except.reason == RETURN_QUIT
+ ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+ "%s", except.message);
+ return -1;
+ }
+ return 0;
+}
+
+\f
+
+void
+gdbpy_initialize_commands (void)
+{
+ cmdpy_object_type.tp_new = PyType_GenericNew;
+ cmdpy_object_type.tp_init = cmdpy_init;
+ if (PyType_Ready (&cmdpy_object_type) < 0)
+ return;
+
+ /* Note: alias and user seem to be special; pseudo appears to be
+ unused, and there is no reason to expose tui or xdb, I think. */
+ if (PyModule_AddIntConstant (gdb_module, "COMMAND_NONE", no_class) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_RUN", class_run) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_VARS", class_vars) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_STACK", class_stack) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_FILES", class_files) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_SUPPORT",
+ class_support) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_INFO", class_info) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_BREAKPOINT",
+ class_breakpoint) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_TRACE", class_trace) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_OBSCURE",
+ class_obscure) < 0
+ || PyModule_AddIntConstant (gdb_module, "COMMAND_MAINTENANCE",
+ class_maintenance) < 0)
+ return;
+
+ Py_INCREF (&cmdpy_object_type);
+ PyModule_AddObject (gdb_module, "Command",
+ (PyObject *) &cmdpy_object_type);
+}
Index: tromey.git/gdb/python/python-internal.h
===================================================================
--- tromey.git.orig/gdb/python/python-internal.h 2008-04-29 11:05:15.000000000 -0300
+++ tromey.git/gdb/python/python-internal.h 2008-04-29 11:05:20.000000000 -0300
@@ -44,6 +44,7 @@ PyObject *gdbpy_get_hook_function (const
void gdbpy_initialize_values (void);
void gdbpy_initialize_hooks (void);
void gdbpy_initialize_breakpoints (void);
+void gdbpy_initialize_commands (void);

/* Use this after a TRY_EXCEPT to throw the appropriate Python
exception. FIXME: throw different errors depending on
Index: tromey.git/gdb/python/python.c
===================================================================
--- tromey.git.orig/gdb/python/python.c 2008-04-29 11:05:15.000000000 -0300
+++ tromey.git/gdb/python/python.c 2008-04-29 11:05:20.000000000 -0300
@@ -70,6 +70,7 @@ demand_python ()
gdbpy_initialize_hooks ();
gdbpy_initialize_values ();
gdbpy_initialize_breakpoints ();
+ gdbpy_initialize_commands ();

PyRun_SimpleString ("import gdb");

--
--
[]'s
Thiago Jung Bauermann
Software Engineer
IBM Linux Technology Center