This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[RFC][python] Add support for commands implemented in Python
- From: Thiago Jung Bauermann <bauerman at br dot ibm dot com>
- To: gdb-patches ml <gdb-patches at sourceware dot org>
- Date: Mon, 02 Feb 2009 11:13:25 -0200
- Subject: [RFC][python] Add support for commands implemented in Python
Hi,
This patch allows implementing CLI commands in Python.
It assumes the "fixes for existing Python code" patch has been applied.
Initially, I was going to submit just the patch for convenience
functions in Python, but it has a small dependency on this one, so I
decided to submit this as well.
Regtested on ppc-linux. WDYT?
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
gdb/
2009-02-02 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-decode.c (set_cmd_completer): Add self parameter to
completer prototype.
(add_cmd): Initialize destroyer member of cmd_list_element. Use
make_symbol_completion_list_fn as completer.
(delete_cmd): Call destroyer if one is set.
* cli/cli-decode.h (cmd_list_element): Add cmd parameter to
completer member. Add destroyer member.
(set_cmd_completer): Add self parameter to
completer prototype.
* command.h (set_cmd_completer): Add cmd parameter to
completer prototype.
* completer.c (noop_completer, filename_completer,
location_completer, expression_completer, command_completer): Adapt
to new completer prototype.
(complete_line_internal): Pass new parameter to completer function.
* completer.h (noop_completer, filename_completer,
location_completer, expression_completer, command_completer): Adapt
prototypes to new completer prototype.
* interps.c (interpreter_completer): Adapt to new completer
prototype.
* python/python-cmd.c: New file.
* python/python-internal.h (gdbpy_initialize_commands): Add
prototype.
(gdbpy_doc_cst): Add forward declaration.
* python/python.c (gdbpy_doc_cst): Declare.
(_initialize_python): Call gdbpy_initialize_commands. Initialize
gdbpy_doc_cst.
* symtab.c (make_symbol_completion_list_fn): New function.
* symtab.h (make_symbol_completion_list_fn): Add prototype.
gdb/doc/
2009-02-02 Tom Tromey <tromey@redhat.com>
* gdb.texinfo (Python API): Add entry for Commands In Python.
(Commands In Python): New node.
gdb/testsuite/
2009-02-02 Thiago Jung Bauermann <bauerman@br.ibm.com>
* gdb.python/python-cmd.exp: New file.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6a4f77d..7400702 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -270,10 +270,12 @@ SUBDIR_TUI_CFLAGS= \
#
SUBDIR_PYTHON_OBS = \
python.o \
+ python-cmd.o \
python-utils.o \
python-value.o
SUBDIR_PYTHON_SRCS = \
python/python.c \
+ python/python-cmd.c \
python/python-utils.c \
python/python-value.c
SUBDIR_PYTHON_DEPS =
@@ -1836,6 +1838,10 @@ python.o: $(srcdir)/python/python.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python.c
$(POSTCOMPILE)
+python-cmd.o: $(srcdir)/python/python-cmd.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
+ $(POSTCOMPILE)
+
python-utils.o: $(srcdir)/python/python-utils.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c
$(POSTCOMPILE)
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 556c027..8760ebf 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -132,7 +132,8 @@ cmd_type (struct cmd_list_element *cmd)
void
set_cmd_completer (struct cmd_list_element *cmd,
- char **(*completer) (char *text, char *word))
+ char **(*completer) (struct cmd_list_element *self,
+ char *text, char *word))
{
cmd->completer = completer; /* Ok. */
}
@@ -207,7 +208,8 @@ add_cmd (char *name, enum command_class class, void (*fun) (char *, int),
c->prefixname = NULL;
c->allow_unknown = 0;
c->abbrev_flag = 0;
- set_cmd_completer (c, make_symbol_completion_list);
+ set_cmd_completer (c, make_symbol_completion_list_fn);
+ c->destroyer = NULL;
c->type = not_set_cmd;
c->var = NULL;
c->var_type = var_boolean;
@@ -688,6 +690,8 @@ delete_cmd (char *name, struct cmd_list_element **list,
{
if (strcmp (iter->name, name) == 0)
{
+ if (iter->destroyer)
+ iter->destroyer (iter, iter->context);
if (iter->hookee_pre)
iter->hookee_pre->hook_pre = 0;
*prehook = iter->hook_pre;
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 56ea9bf..26ca2f7 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -167,7 +167,12 @@ struct cmd_list_element
returned relative to this position. For example, suppose TEXT is "foo"
and we want to complete to "foobar". If WORD is "oo", return
"oobar"; if WORD is "baz/foo", return "baz/foobar". */
- char **(*completer) (char *text, char *word);
+ char **(*completer) (struct cmd_list_element *cmd, 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"). */
@@ -242,7 +247,8 @@ extern void set_cmd_sfunc (struct cmd_list_element *cmd,
struct cmd_list_element * c));
extern void set_cmd_completer (struct cmd_list_element *cmd,
- char **(*completer) (char *text, char *word));
+ char **(*completer) (struct cmd_list_element *self,
+ char *text, char *word));
/* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
around in cmd objects to test the value of the commands sfunc(). */
diff --git a/gdb/command.h b/gdb/command.h
index b3f7013..bed615c 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -138,7 +138,8 @@ extern void set_cmd_sfunc (struct cmd_list_element *cmd,
cmd_sfunc_ftype *sfunc);
extern void set_cmd_completer (struct cmd_list_element *cmd,
- char **(*completer) (char *text, char *word));
+ char **(*completer) (struct cmd_list_element *cmd,
+ char *text, char *word));
/* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
around in cmd objects to test the value of the commands sfunc(). */
diff --git a/gdb/completer.c b/gdb/completer.c
index b1b0cf3..c96efcf 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -105,14 +105,14 @@ readline_line_completion_function (const char *text, int matches)
/* This can be used for functions which don't want to complete on symbols
but don't want to complete on anything else either. */
char **
-noop_completer (char *text, char *prefix)
+noop_completer (struct cmd_list_element *ignore, char *text, char *prefix)
{
return NULL;
}
/* Complete on filenames. */
char **
-filename_completer (char *text, char *word)
+filename_completer (struct cmd_list_element *ignore, char *text, char *word)
{
int subsequent_name;
char **return_val;
@@ -195,7 +195,7 @@ filename_completer (char *text, char *word)
This is intended to be used in commands that set breakpoints etc. */
char **
-location_completer (char *text, char *word)
+location_completer (struct cmd_list_element *ignore, char *text, char *word)
{
int n_syms = 0, n_files = 0;
char ** fn_list = NULL;
@@ -384,7 +384,7 @@ add_struct_fields (struct type *type, int *nextp, char **output,
names, but some language parsers also have support for completing
field names. */
char **
-expression_completer (char *text, char *word)
+expression_completer (struct cmd_list_element *ignore, char *text, char *word)
{
struct type *type;
char *fieldname, *p;
@@ -428,7 +428,7 @@ expression_completer (char *text, char *word)
;
/* Not ideal but it is what we used to do before... */
- return location_completer (p, word);
+ return location_completer (ignore, p, word);
}
/* Here are some useful test cases for completion. FIXME: These should
@@ -623,7 +623,7 @@ complete_line_internal (const char *text, char *line_buffer, int point,
p--)
;
}
- list = (*c->completer) (p, word);
+ list = (*c->completer) (c, p, word);
}
}
else
@@ -691,7 +691,7 @@ complete_line_internal (const char *text, char *line_buffer, int point,
p--)
;
}
- list = (*c->completer) (p, word);
+ list = (*c->completer) (c, p, word);
}
}
}
@@ -709,7 +709,7 @@ complete_line (const char *text, char *line_buffer, int point)
/* Complete on command names. Used by "help". */
char **
-command_completer (char *text, char *word)
+command_completer (struct cmd_list_element *ignore, char *text, char *word)
{
return complete_line_internal (word, text, strlen (text), 1);
}
diff --git a/gdb/completer.h b/gdb/completer.h
index da1c381..6adbf1b 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -21,15 +21,15 @@ extern char **complete_line (const char *text, char *line_buffer, int point);
extern char *readline_line_completion_function (const char *text, int matches);
-extern char **noop_completer (char *, char *);
+extern char **noop_completer (struct cmd_list_element *, char *, char *);
-extern char **filename_completer (char *, char *);
+extern char **filename_completer (struct cmd_list_element *, char *, char *);
-extern char **expression_completer (char *, char *);
+extern char **expression_completer (struct cmd_list_element *, char *, char *);
-extern char **location_completer (char *, char *);
+extern char **location_completer (struct cmd_list_element *, char *, char *);
-extern char **command_completer (char *, char *);
+extern char **command_completer (struct cmd_list_element *, char *, char *);
extern char *get_gdb_completer_quote_characters (void);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2c6c2ea..45e3a91 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18064,6 +18064,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* Basic Python:: Basic Python Functions.
* Exception Handling::
* Values From Inferior::
+* Commands In Python:: Implementing new commands in Python.
@end menu
@node Basic Python
@@ -18244,6 +18245,224 @@ The optional @var{errors} argument is the same as the corresponding
argument to Python's @code{string.decode} method.
@end defmethod
+@node Commands In Python
+@subsubsection Commands In Python
+
+@cindex commands in python
+@cindex python commands
+@tindex Command
+@tindex gdb.Command
+You can implement new @value{GDBN} CLI commands in Python. A CLI
+command is implemented using an instance of the @code{gdb.Command}
+class, most commonly using a subclass.
+
+@defmethod Command __init__ name @var{command-class} @r{[}@var{completer-class} @var{prefix}@r{]}
+The object initializer for @code{Command} registers the new command
+with @value{GDBN}. This initializer is normally invoked from the
+subclass' own @code{__init__} method.
+
+@var{name} is the name of the command. If @var{name} consists of
+multiple words, then the initial words are looked for as prefix
+commands. In this case, if one of the prefix commands does not exist,
+an exception is raised.
+
+There is no support for multi-line commands.
+
+@var{command-class} should be one of the @samp{COMMAND_} constants
+defined below. This argument tells @value{GDBN} how to categorize the
+new command in the help system.
+
+@var{completer-class} is an optional argument. If given, it should be
+one of the @samp{COMPLETE_} constants defined below. This argument
+tells @value{GDBN} how to perform completion for this command. If not
+given, @value{GDBN} will attempt to complete using the object's
+@code{complete} method (see below); if no such method is found, an
+error will occur when completion is attempted.
+
+@var{prefix} is an optional argument. If @code{True}, then the new
+command is a prefix command; sub-commands of this command may be
+registered.
+
+The help text for the new command is taken from the Python
+documentation string for the command's class, if there is one. If
+there is no documentation string, a default value is used.
+@end defmethod
+
+@defmethod Command dont_repeat
+By default, a @value{GDBN} command is repeated when the user enters a
+blank line at the command prompt. A command can suppress this
+behavior by invoking the @code{dont_repeat} method. This is similar
+to the user command @code{dont-repeat}, see @ref{Define, dont-repeat}.
+@end defmethod
+
+@defmethod Command invoke argument from_tty
+This method is called by @value{GDBN} when this command is invoked.
+
+@var{argument} is the argument to the command. The argument
+ordinarily is a string, but may be @code{None}, meaning that there was
+no argument.
+
+@var{from_tty} is a boolean argument. When true, this means that the
+command was entered by the user at the terminal; when false it means
+that the command came from elsewhere.
+
+If this method throws an exception, it is turned into a @value{GDBN}
+@code{error} call. Otherwise, the return value is ignored.
+@end defmethod
+
+@defmethod Command complete text word
+This method is called by @value{GDBN} when the user attempts @key{TAB}
+completion on this command.
+
+The arguments @var{text} and @var{word} are both strings. @var{text}
+holds the complete command line up to the cursor's location.
+@var{word} holds the last word of the command line; this is computed
+using a word-breaking heuristic.
+
+The @code{invoke} method can return several values:
+@itemize @bullet
+@item
+If the return value is a sequence, the contents of the sequence are
+used as the completions. It is up to @code{invoke} to ensure that the
+contents actually do complete the word. A zero-length sequence is
+allowed, it means that there were no completions available. Only
+string elements of the sequence are used; other elements in the
+sequence are ignored.
+
+@item
+If the return value is one of the @samp{COMPLETE_} constants defined
+below, then the corresponding @value{GDBN}-internal completion
+function is invoked, and its result is used.
+
+@item
+All other results are treated as though there were no available
+completions.
+@end itemize
+@end defmethod
+
+
+When a new command is registered, it must be declared as a member of
+some general class of commands. This is used to classify the command
+in the on-line help system. The available classifications are
+represented by constants defined in the @code{gdb} module:
+
+@table @code
+@findex COMMAND_NONE
+@findex gdb.COMMAND_NONE
+@item COMMAND_NONE
+The command does not belong to any particular class.
+
+@findex COMMAND_RUN
+@findex gdb.COMMAND_RUN
+@item COMMAND_RUN
+The command is related to running the inferior.
+
+@findex COMMAND_VARS
+@findex gdb.COMMAND_VARS
+@item COMMAND_VARS
+The command is related to variables.
+
+@findex COMMAND_STACK
+@findex gdb.COMMAND_STACK
+@item COMMAND_STACK
+The command has to do with manipulation of the stack.
+
+@findex COMMAND_FILES
+@findex gdb.COMMAND_FILES
+@item COMMAND_FILES
+This class is used for file-related commands.
+
+@findex COMMAND_SUPPORT
+@findex gdb.COMMAND_SUPPORT
+@item COMMAND_SUPPORT
+This should be used for ``support facilities'', generally meaning
+things that are useful to the user when interacting with @value{GDBN},
+but not related to the state of the inferior.
+
+@findex COMMAND_INFO
+@findex gdb.COMMAND_INFO
+@item COMMAND_INFO
+The command is an @samp{info}-related command, that is, related to the
+state of @value{GDBN} itself.
+
+@findex COMMAND_BREAKPOINT
+@findex gdb.COMMAND_BREAKPOINT
+@item COMMAND_BREAKPOINT
+The command has to do with breakpoints.
+
+@findex COMMAND_TRACE
+@findex gdb.COMMAND_TRACE
+@item COMMAND_TRACE
+The command has to do with tracepoints.
+
+@findex COMMAND_OBSCURE
+@findex gdb.COMMAND_OBSCURE
+@item COMMAND_OBSCURE
+The command is only used in unusual circumstances, or is not of
+general interest to users.
+
+@findex COMMAND_MAINTENANCE
+@findex gdb.COMMAND_MAINTENANCE
+@item COMMAND_MAINTENANCE
+The command is only useful to @value{GDBN} maintainers.
+@end table
+
+
+A new command can use a predefined completion function, either by
+specifying it via an argument at initialization, or by return it from
+the @code{complete} method. These predefined completion constants are
+all defined in the @code{gdb} module:
+
+@table @code
+@findex COMPLETE_NONE
+@findex gdb.COMPLETE_NONE
+@item COMPLETE_NONE
+This constant means that no completion should be done.
+
+@findex COMPLETE_FILENAME
+@findex gdb.COMPLETE_FILENAME
+@item COMPLETE_FILENAME
+This constant means that filename completion should be performed.
+
+@findex COMPLETE_LOCATION
+@findex gdb.COMPLETE_LOCATION
+@item COMPLETE_LOCATION
+This constant means that location completion should be done.
+
+@findex COMPLETE_COMMAND
+@findex gdb.COMPLETE_COMMAND
+@item COMPLETE_COMMAND
+This constant means that completion should examine @value{GDBN}
+command names.
+
+@findex COMPLETE_SYMBOL
+@findex gdb.COMPLETE_SYMBOL
+@item COMPLETE_SYMBOL
+This constant means that completion should be done using symbol names
+as the source.
+@end table
+
+The following code snippet shows how a trivial CLI command can be
+implemented in Python:
+
+@smallexample
+class HelloWorld (gdb.Command):
+ """Greet the whole world."""
+
+ def __init__ (self):
+ super (HelloWorld, self).__init__ ("hello-world", gdb.COMMAND_OBSCURE)
+
+ def invoke (self, arg, from_tty):
+ print "Hello, World!"
+
+HelloWorld ()
+@end smallexample
+
+The last line instantiates the class, and is necessary to trigger the
+registration of the command with @value{GDBN}. Depending on how the
+Python code is read into @value{GDBN}, you may need to import the
+@code{gdb} module explicitly.
+
@node Interpreters
@chapter Command Interpreters
@cindex command interpreters
diff --git a/gdb/interps.c b/gdb/interps.c
index 6814a72..da05ee2 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -71,7 +71,8 @@ struct interp
/* Functions local to this file. */
static void initialize_interps (void);
-static char **interpreter_completer (char *text, char *word);
+static char **interpreter_completer (struct cmd_list_element *cmd,
+ char *text, char *word);
/* The magic initialization routine for this module. */
@@ -416,7 +417,7 @@ interpreter_exec_cmd (char *args, int from_tty)
/* List the possible interpreters which could complete the given text. */
static char **
-interpreter_completer (char *text, char *word)
+interpreter_completer (struct cmd_list_element *ignore, char *text, char *word)
{
int alloced = 0;
int textlen;
diff --git a/gdb/python/python-cmd.c b/gdb/python/python-cmd.c
new file mode 100644
index 0000000..6f6aa59
--- /dev/null
+++ b/gdb/python/python-cmd.c
@@ -0,0 +1,586 @@
+/* gdb commands implemented in Python
+
+ Copyright (C) 2008, 2009 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"
+#include "completer.h"
+
+/* Struct representing built-in completion types. */
+struct cmdpy_completer
+{
+ /* Python symbol name. */
+ char *name;
+ /* Completion function. */
+ char **(*completer) (struct cmd_list_element *, char *, char *);
+};
+
+static struct cmdpy_completer completers[] =
+{
+ { "COMPLETE_NONE", noop_completer },
+ { "COMPLETE_FILENAME", filename_completer },
+ { "COMPLETE_LOCATION", location_completer },
+ { "COMPLETE_COMMAND", command_completer },
+ { "COMPLETE_SYMBOL", make_symbol_completion_list_fn },
+};
+
+#define N_COMPLETERS (sizeof (completers) / sizeof (completers[0]))
+
+/* 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. */
+ struct cmd_list_element *command;
+
+ /* For a prefix command, this is the list of sub-commands. */
+ struct cmd_list_element *sub_list;
+};
+
+typedef struct cmdpy_object cmdpy_object;
+
+static PyTypeObject cmdpy_object_type;
+
+
+/* Constants used by this module. */
+static PyObject *invoke_cst;
+static PyObject *complete_cst;
+
+
+
+/* Python function which wraps dont_repeat. */
+static PyObject *
+cmdpy_dont_repeat (PyObject *self, PyObject *args)
+{
+ dont_repeat ();
+ Py_RETURN_NONE;
+}
+
+
+
+/* Called if the gdb cmd_list_element is destroyed. */
+static void
+cmdpy_destroyer (struct cmd_list_element *self, void *context)
+{
+ cmdpy_object *cmd;
+ PyGILState_STATE state;
+
+ state = PyGILState_Ensure ();
+
+ /* Release our hold on the command object. */
+ cmd = (cmdpy_object *) context;
+ cmd->command = NULL;
+ Py_DECREF (cmd);
+
+ /* We allocated the name, doc string, and perhaps the prefix
+ name. */
+ xfree (self->name);
+ xfree (self->doc);
+ xfree (self->prefixname);
+
+ PyGILState_Release (state);
+}
+
+/* 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 *argobj, *ttyobj, *result;
+ struct cleanup *cleanup;
+ PyGILState_STATE state;
+
+ state = PyGILState_Ensure ();
+ cleanup = make_cleanup_py_restore_gil (&state);
+
+ if (! obj)
+ error (_("Invalid invocation of Python command object."));
+ if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
+ {
+ if (obj->command->prefixname)
+ {
+ /* A prefix command does not need an invoke method. */
+ do_cleanups (cleanup);
+ return;
+ }
+ error (_("Python command object missing 'invoke' method."));
+ }
+
+ if (! args)
+ {
+ argobj = Py_None;
+ Py_INCREF (argobj);
+ }
+ else
+ {
+ argobj = PyString_FromString (args);
+ if (! argobj)
+ error (_("Could not convert arguments to Python string."));
+ }
+ ttyobj = from_tty ? Py_True : Py_False;
+ Py_INCREF (ttyobj);
+ result = PyObject_CallMethodObjArgs ((PyObject *) obj, invoke_cst, argobj,
+ ttyobj, NULL);
+ Py_DECREF (argobj);
+ Py_DECREF (ttyobj);
+ if (! result)
+ {
+ PyObject *ptype, *pvalue, *ptraceback;
+ char *s, *str;
+
+ PyErr_Fetch (&ptype, &pvalue, &ptraceback);
+
+ if (pvalue && PyString_Check (pvalue))
+ {
+ /* Make a temporary copy of the string data. */
+ char *s = PyString_AsString (pvalue);
+ char *copy = alloca (strlen (s) + 1);
+ strcpy (copy, s);
+
+ PyErr_Restore (ptype, pvalue, ptraceback);
+ gdbpy_print_stack ();
+ error (_("Error occurred in Python command: %s"), copy);
+ }
+ else
+ {
+ PyErr_Restore (ptype, pvalue, ptraceback);
+ gdbpy_print_stack ();
+ error (_("Error occurred in Python command."));
+ }
+ }
+ Py_DECREF (result);
+ do_cleanups (cleanup);
+}
+
+/* Called by gdb for command completion. */
+static char **
+cmdpy_completer (struct cmd_list_element *command, char *text, char *word)
+{
+ cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command);
+ PyObject *textobj, *wordobj, *resultobj = NULL;
+ char **result = NULL;
+ struct cleanup *cleanup;
+ PyGILState_STATE state;
+
+ state = PyGILState_Ensure ();
+ cleanup = make_cleanup_py_restore_gil (&state);
+
+ if (! obj)
+ error (_("Invalid invocation of Python command object."));
+ if (! PyObject_HasAttr ((PyObject *) obj, complete_cst))
+ {
+ /* If there is no complete method, don't error -- instead, just
+ say that there are no completions. */
+ goto done;
+ }
+
+ textobj = PyString_FromString (text);
+ if (! textobj)
+ error (_("Could not convert argument to Python string."));
+ wordobj = PyString_FromString (word);
+ if (! wordobj)
+ error (_("Could not convert argument to Python string."));
+
+ resultobj = PyObject_CallMethodObjArgs ((PyObject *) obj, complete_cst,
+ textobj, wordobj, NULL);
+ Py_DECREF (textobj);
+ Py_DECREF (wordobj);
+ if (! resultobj)
+ {
+ /* Just swallow errors here. */
+ PyErr_Clear ();
+ goto done;
+ }
+ make_cleanup_py_decref (resultobj);
+
+ result = NULL;
+ if (PySequence_Check (resultobj))
+ {
+ Py_ssize_t i, len = PySequence_Size (resultobj);
+ Py_ssize_t out;
+ if (len < 0)
+ goto done;
+
+ result = (char **) xmalloc ((len + 1) * sizeof (char *));
+ for (i = out = 0; i < len; ++i)
+ {
+ int l;
+ PyObject *elt = PySequence_GetItem (resultobj, i);
+ if (elt == NULL || ! gdbpy_is_string (elt))
+ {
+ /* Skip problem elements. */
+ PyErr_Clear ();
+ continue;
+ }
+ result[out] = python_string_to_host_string (elt);
+ ++out;
+ }
+ result[out] = NULL;
+ }
+ else if (PyInt_Check (resultobj))
+ {
+ /* User code may also return one of the completion constants,
+ thus requesting that sort of completion. */
+ long value = PyInt_AsLong (resultobj);
+ if (value >= 0 && value < (long) N_COMPLETERS)
+ result = completers[value].completer (command, text, word);
+ }
+
+ done:
+
+ do_cleanups (cleanup);
+
+ return result;
+}
+
+/* Helper for cmdpy_init which locates the command list to use and
+ pulls out the command name.
+
+ TEXT is the command name list. The final word in the list is the
+ name of the new command. All earlier words must be existing prefix
+ commands.
+
+ *BASE_LIST is set to the final prefix command's list of
+ *sub-commands.
+
+ This function returns the xmalloc()d name of the new command. On
+ error sets the Python error and returns NULL. */
+static char *
+parse_command_name (char *text, struct cmd_list_element ***base_list)
+{
+ struct cmd_list_element *elt;
+ int len = strlen (text);
+ int i, lastchar;
+ char *prefix_text;
+ char *result;
+
+ /* Skip trailing whitespace. */
+ for (i = len - 1; i >= 0 && (text[i] == ' ' || text[i] == '\t'); --i)
+ ;
+ if (i < 0)
+ {
+ PyErr_SetString (PyExc_RuntimeError, _("no command name found"));
+ return NULL;
+ }
+ lastchar = i;
+
+ /* Find first character of the final word. */
+ for (; i > 0 && (isalnum (text[i - 1])
+ || text[i - 1] == '-'
+ || text[i - 1] == '_');
+ --i)
+ ;
+ result = xmalloc (lastchar - i + 2);
+ memcpy (result, &text[i], lastchar - i + 1);
+ result[lastchar - i + 1] = '\0';
+
+ /* Skip whitespace again. */
+ for (--i; i >= 0 && (text[i] == ' ' || text[i] == '\t'); --i)
+ ;
+ if (i < 0)
+ {
+ *base_list = &cmdlist;
+ return result;
+ }
+
+ prefix_text = xmalloc (i + 2);
+ memcpy (prefix_text, text, i + 1);
+ prefix_text[i + 1] = '\0';
+
+ text = prefix_text;
+ elt = lookup_cmd_1 (&text, cmdlist, NULL, 1);
+ if (!elt || elt == (struct cmd_list_element *) -1)
+ {
+ PyErr_Format (PyExc_RuntimeError, _("could not find command prefix %s"),
+ prefix_text);
+ xfree (prefix_text);
+ xfree (result);
+ return NULL;
+ }
+
+ if (elt->prefixlist)
+ {
+ xfree (prefix_text);
+ *base_list = elt->prefixlist;
+ return result;
+ }
+
+ PyErr_Format (PyExc_RuntimeError, _("'%s' is not a prefix command"),
+ prefix_text);
+ xfree (prefix_text);
+ xfree (result);
+ return NULL;
+}
+
+/* Object initializer; sets up gdb-side structures for command.
+
+ Use: __init__(NAME, CMDCLASS, [COMPLETERCLASS, [PREFIX]]).
+
+ NAME is the name of the command. It may consist of multiple words,
+ in which case the final word is the name of the new command, and
+ earlier words must be prefix commands.
+
+ CMDCLASS is the kind of command. It should be one of the COMMAND_*
+ constants defined in the gdb module.
+
+ COMPLETERCLASS is the kind of completer. If not given, the
+ "complete" method will be used. Otherwise, it should be one of the
+ COMPLETE_* constants defined in the gdb module.
+
+ If PREFIX is True, then this command is a prefix command.
+
+ 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;
+ int completetype = -1;
+ char *docstring = NULL;
+ volatile struct gdb_exception except;
+ struct cmd_list_element **cmd_list;
+ char *cmd_name, *pfx_name;
+ PyObject *is_prefix = NULL;
+ int cmp;
+
+ 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|iO", &name, &cmdtype,
+ &completetype, &is_prefix))
+ 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;
+ }
+
+ if (completetype < -1 || completetype >= (int) N_COMPLETERS)
+ {
+ PyErr_Format (PyExc_RuntimeError, _("invalid completion type argument"));
+ return -1;
+ }
+
+ cmd_name = parse_command_name (name, &cmd_list);
+ if (! cmd_name)
+ return -1;
+
+ pfx_name = NULL;
+ if (is_prefix != NULL)
+ {
+ cmp = PyObject_IsTrue (is_prefix);
+ if (cmp == 1)
+ {
+ int i, out;
+
+ /* Make a normalized form of the command name. */
+ pfx_name = xmalloc (strlen (name) + 2);
+
+ i = 0;
+ out = 0;
+ while (name[i])
+ {
+ /* Skip whitespace. */
+ while (name[i] == ' ' || name[i] == '\t')
+ ++i;
+ /* Copy non-whitespace characters. */
+ while (name[i] && name[i] != ' ' && name[i] != '\t')
+ pfx_name[out++] = name[i++];
+ /* Add a single space after each word -- including the final
+ word. */
+ pfx_name[out++] = ' ';
+ }
+ pfx_name[out] = '\0';
+ }
+ else if (cmp < 0)
+ return -1;
+ }
+ if (PyObject_HasAttr (self, gdbpy_doc_cst))
+ {
+ PyObject *ds_obj = PyObject_GetAttr (self, gdbpy_doc_cst);
+ if (ds_obj && gdbpy_is_string (ds_obj))
+ docstring = python_string_to_host_string (ds_obj);
+ }
+ if (! docstring)
+ docstring = xstrdup (_("This command is not documented."));
+
+ Py_INCREF (self);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cmd_list_element *cmd;
+
+ if (pfx_name)
+ {
+ int allow_unknown;
+
+ /* If we have our own "invoke" method, then allow unknown
+ sub-commands. */
+ allow_unknown = PyObject_HasAttr (self, invoke_cst);
+ cmd = add_prefix_cmd (cmd_name, (enum command_class) cmdtype,
+ NULL, docstring, &obj->sub_list,
+ pfx_name, allow_unknown, cmd_list);
+ }
+ else
+ cmd = add_cmd (cmd_name, (enum command_class) cmdtype, NULL,
+ docstring, cmd_list);
+
+ /* There appears to be no API to set this. */
+ cmd->func = cmdpy_function;
+ cmd->destroyer = cmdpy_destroyer;
+
+ obj->command = cmd;
+ set_cmd_context (cmd, self);
+ set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
+ : completers[completetype].completer));
+ }
+ if (except.reason < 0)
+ {
+ xfree (cmd_name);
+ xfree (docstring);
+ xfree (pfx_name);
+ Py_DECREF (self);
+ PyErr_Format (except.reason == RETURN_QUIT
+ ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+ "%s", except.message);
+ return -1;
+ }
+ return 0;
+}
+
+
+
+/* Initialize the 'commands' code. */
+void
+gdbpy_initialize_commands (void)
+{
+ int i;
+
+ 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;
+
+ for (i = 0; i < N_COMPLETERS; ++i)
+ {
+ if (PyModule_AddIntConstant (gdb_module, completers[i].name, i) < 0)
+ return;
+ }
+
+ Py_INCREF (&cmdpy_object_type);
+ PyModule_AddObject (gdb_module, "Command",
+ (PyObject *) &cmdpy_object_type);
+
+ invoke_cst = PyString_FromString ("invoke");
+ complete_cst = PyString_FromString ("complete");
+}
+
+
+
+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 */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ cmdpy_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew /* tp_new */
+};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 1457928..02dbfc4 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -70,6 +70,7 @@ PyObject *value_to_value_object (struct value *v);
struct value *convert_value_from_python (PyObject *obj);
void gdbpy_initialize_values (void);
+void gdbpy_initialize_commands (void);
struct cleanup *make_cleanup_py_decref (PyObject *py);
struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state);
@@ -94,4 +95,6 @@ char *python_string_to_host_string (PyObject *obj);
PyObject *target_string_to_unicode (const gdb_byte *str, int length);
int gdbpy_is_string (PyObject *obj);
+extern PyObject *gdbpy_doc_cst;
+
#endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 96bb5f5..4f97416 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -46,6 +46,8 @@ static PyMethodDef GdbMethods[];
PyObject *gdb_module;
+PyObject *gdbpy_doc_cst;
+
/* Given a command_line, return a command string suitable for passing
to Python. Lines in the string are separated by newlines. The
return value is allocated using xmalloc and the caller is
@@ -407,9 +409,12 @@ Enables or disables printing of Python stack traces."),
PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
gdbpy_initialize_values ();
+ gdbpy_initialize_commands ();
PyRun_SimpleString ("import gdb");
+ gdbpy_doc_cst = PyString_FromString ("__doc__");
+
/* Create a couple objects which are used for Python's stdout and
stderr. */
PyRun_SimpleString ("\
diff --git a/gdb/symtab.c b/gdb/symtab.c
index b9befed..97d7950 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3890,6 +3890,16 @@ make_symbol_completion_list (char *text, char *word)
return current_language->la_make_symbol_completion_list (text, word);
}
+/* Like make_symbol_completion_list, but suitable for use as a
+ completion function. */
+
+char **
+make_symbol_completion_list_fn (struct cmd_list_element *ignore,
+ char *text, char *word)
+{
+ return make_symbol_completion_list (text, word);
+}
+
/* Like make_symbol_completion_list, but returns a list of symbols
defined in a source file FILE. */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 2446d1e..8b086f3 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1238,6 +1238,8 @@ extern void select_source_symtab (struct symtab *);
extern char **default_make_symbol_completion_list (char *, char *);
extern char **make_symbol_completion_list (char *, char *);
+extern char **make_symbol_completion_list_fn (struct cmd_list_element *,
+ char *, char *);
extern char **make_file_symbol_completion_list (char *, char *, char *);
diff --git a/gdb/testsuite/gdb.python/python-cmd.exp b/gdb/testsuite/gdb.python/python-cmd.exp
new file mode 100644
index 0000000..6c73ff2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-cmd.exp
@@ -0,0 +1,107 @@
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It tests the mechanism
+# for defining new GDB commands in Python.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}...
+# Run a test named NAME, consisting of multiple lines of input.
+# After each input line INPUT, search for result line RESULT.
+# Succeed if all results are seen; fail otherwise.
+proc gdb_py_test_multiple {name args} {
+ global gdb_prompt
+ foreach {input result} $args {
+ if {[gdb_test_multiple $input "$name - $input" {
+ -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" {
+ pass "$name - $input"
+ }
+ }]} {
+ return 1
+ }
+ }
+ return 0
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdb_test_multiple "python print 'hello, world!'" "verify python support" {
+ -re "not supported.*$gdb_prompt $" {
+ unsupported "python support is disabled"
+ return -1
+ }
+ -re "$gdb_prompt $" {}
+}
+
+# Test a simple command.
+
+gdb_py_test_multiple "input simple command" \
+ "python" "" \
+ "class test_cmd (gdb.Command):" "" \
+ " def __init__ (self):" "" \
+ " super (test_cmd, self).__init__ (\"test_cmd\", gdb.COMMAND_OBSCURE)" "" \
+ " def invoke (self, arg, from_tty):" "" \
+ " print \"test_cmd output, arg = %s\" % arg" "" \
+ "test_cmd ()" "" \
+ "end" ""
+
+gdb_test "test_cmd ugh" "test_cmd output, arg = ugh" "call simple command"
+
+# Test a prefix command, and a subcommand within it.
+
+gdb_py_test_multiple "input prefix command" \
+ "python" "" \
+ "class prefix_cmd (gdb.Command):" "" \
+ " def __init__ (self):" "" \
+ " super (prefix_cmd, self).__init__ (\"prefix_cmd\", gdb.COMMAND_OBSCURE, gdb.COMPLETE_NONE, True)" "" \
+ " def invoke (self, arg, from_tty):" "" \
+ " print \"prefix_cmd output, arg = %s\" % arg" "" \
+ "prefix_cmd ()" "" \
+ "end" ""
+
+gdb_test "prefix_cmd ugh" "prefix_cmd output, arg = ugh" "call prefix command"
+
+gdb_py_test_multiple "input subcommand" \
+ "python" "" \
+ "class subcmd (gdb.Command):" "" \
+ " def __init__ (self):" "" \
+ " super (subcmd, self).__init__ (\"prefix_cmd subcmd\", gdb.COMMAND_OBSCURE)" "" \
+ " def invoke (self, arg, from_tty):" "" \
+ " print \"subcmd output, arg = %s\" % arg" "" \
+ "subcmd ()" "" \
+ "end" ""
+
+gdb_test "prefix_cmd subcmd ugh" "subcmd output, arg = ugh" "call subcmd"
+
+# Test a subcommand in an existing GDB prefix.
+
+gdb_py_test_multiple "input new subcommand" \
+ "python" "" \
+ "class newsubcmd (gdb.Command):" "" \
+ " def __init__ (self):" "" \
+ " super (newsubcmd, self).__init__ (\"info newsubcmd\", gdb.COMMAND_OBSCURE)" "" \
+ " def invoke (self, arg, from_tty):" "" \
+ " print \"newsubcmd output, arg = %s\" % arg" "" \
+ "newsubcmd ()" "" \
+ "end" ""
+
+gdb_test "info newsubcmd ugh" "newsubcmd output, arg = ugh" "call newsubcmd"