From d8906c6f0e46480df2c8ad6aec13e57c9af084f9 Mon Sep 17 00:00:00 2001 From: Thiago Jung Bauermann Date: Fri, 6 Feb 2009 21:33:59 +0000 Subject: [PATCH] gdb/ 2009-02-06 Tom Tromey * 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-06 Tom Tromey * gdb.texinfo (Python API): Add entry for Commands In Python. (Commands In Python): New node. gdb/testsuite/ 2009-02-06 Thiago Jung Bauermann * gdb.python/python-cmd.exp: New file. --- gdb/ChangeLog | 35 +++ gdb/Makefile.in | 6 + gdb/NEWS | 2 + gdb/cli/cli-decode.c | 8 +- gdb/cli/cli-decode.h | 10 +- gdb/command.h | 3 +- gdb/completer.c | 16 +- gdb/completer.h | 10 +- gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 254 +++++++++++++++++++ gdb/interps.c | 5 +- gdb/python/python-cmd.c | 585 +++++++++++++++++++++++++++++++++++++++++++ gdb/python/python-internal.h | 3 + gdb/python/python.c | 5 + gdb/symtab.c | 10 + gdb/symtab.h | 2 + gdb/testsuite/ChangeLog | 4 + 17 files changed, 943 insertions(+), 20 deletions(-) create mode 100644 gdb/python/python-cmd.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 6038287..0f28a82 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,38 @@ +2009-02-06 Tom Tromey + + * 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. + 2009-02-06 Pedro Alves * target.c (target_get_osdata): Check for equal or higher than 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/NEWS b/gdb/NEWS index a85caeb..d1abf0c 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -96,6 +96,8 @@ are treated as the standard definitions, regardless of context. GDB now has support for scripting using Python. Whether this is available is determined at configure time. + New GDB commands can now be written in Python. + * Ada tasking support Ada tasks can now be inspected in GDB. The following commands have 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 298cdd0..43fcf7a 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; @@ -412,7 +412,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; @@ -456,7 +456,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 @@ -651,7 +651,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 @@ -719,7 +719,7 @@ complete_line_internal (const char *text, char *line_buffer, int point, p--) ; } - list = (*c->completer) (p, word); + list = (*c->completer) (c, p, word); } } } @@ -737,7 +737,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 da1c38119..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/ChangeLog b/gdb/doc/ChangeLog index 0f5363c..98502db 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2009-02-06 Tom Tromey + + * gdb.texinfo (Python API): Add entry for Commands In Python. + (Commands In Python): New node. + 2009-02-05 Tom Tromey * gdb.texinfo (Values From Inferior): Document Value.string. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index a2ed0b8..354b888 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 @@ -18246,6 +18247,259 @@ 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 no +documentation string is provided, the default value ``This command is +not documented.'' 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 a string. It is the argument to the command, after +leading and trailing whitespace has been stripped. + +@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 +completion on this command. All forms of completion are handled by +this method, that is, the @key{TAB} and @key{M-?} key bindings, and +the @code{complete} 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{complete} 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{complete} 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 top-level +commands in the on-line help system; note that prefix commands are not +listed under their own category but rather that of their top-level +command. 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. A command in +this category will not be displayed in any of the help categories. + +@findex COMMAND_RUNNING +@findex gdb.COMMAND_RUNNING +@item COMMAND_RUN +The command is related to running the inferior. For example, +@code{start}, @code{step}, and @code{continue} are in this category. +Type @code{help running} at the @value{GDBN} prompt to see a list of +commands in this category. + +@findex COMMAND_DATA +@findex gdb.COMMAND_DATA +@item COMMAND_VARS +The command is related to data or variables. For example, +@code{call}, @code{find}, and @code{print} are in this category. Type +@code{help data} at the @value{GDBN} prompt to see a list of commands +in this category. + +@findex COMMAND_STACK +@findex gdb.COMMAND_STACK +@item COMMAND_STACK +The command has to do with manipulation of the stack. For example, +@code{backtrace}, @code{frame}, and @code{return} are in this +category. Type @code{help stack} at the @value{GDBN} prompt to see a +list of commands in this category. + +@findex COMMAND_FILES +@findex gdb.COMMAND_FILES +@item COMMAND_FILES +This class is used for file-related commands. For example, +@code{file}, @code{list} and @code{section} are in this category. +Type @code{help files} at the @value{GDBN} prompt to see a list of +commands in this category. + +@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. For example, +@code{help}, @code{make}, and @code{shell} are in this category. Type +@code{help support} at the @value{GDBN} prompt to see a list of +commands in this category. + +@findex COMMAND_STATUS +@findex gdb.COMMAND_STATUS +@item COMMAND_INFO +The command is an @samp{info}-related command, that is, related to the +state of @value{GDBN} itself. For example, @code{info}, @code{macro}, +and @code{show} are in this category. Type @code{help status} at the +@value{GDBN} prompt to see a list of commands in this category. + +@findex COMMAND_BREAKPOINTS +@findex gdb.COMMAND_BREAKPOINTS +@item COMMAND_BREAKPOINT +The command has to do with breakpoints. For example, @code{break}, +@code{clear}, and @code{delete} are in this category. Type @code{help +breakpoints} at the @value{GDBN} prompt to see a list of commands in +this category. + +@findex COMMAND_TRACEPOINTS +@findex gdb.COMMAND_TRACEPOINTS +@item COMMAND_TRACE +The command has to do with tracepoints. For example, @code{trace}, +@code{actions}, and @code{tfind} are in this category. Type +@code{help tracepoints} at the @value{GDBN} prompt to see a list of +commands in this category. + +@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. For example, @code{checkpoint}, +@code{fork}, and @code{stop} are in this category. Type @code{help +obscure} at the @value{GDBN} prompt to see a list of commands in this +category. + +@findex COMMAND_MAINTENANCE +@findex gdb.COMMAND_MAINTENANCE +@item COMMAND_MAINTENANCE +The command is only useful to @value{GDBN} maintainers. The +@code{maintenance} and @code{flushregs} commands are in this category. +Type @code{help internals} at the @value{GDBN} prompt to see a list of +commands in this category. +@end table + + +A new command can use a predefined completion function, either by +specifying it via an argument at initialization, or by returning 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. +@xref{Specify Location}. + +@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..36cde34 --- /dev/null +++ b/gdb/python/python-cmd.c @@ -0,0 +1,585 @@ +/* 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 . */ + + +#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; + + /* A prefix command requires storage for a list of its sub-commands. + A pointer to this is passed to add_prefix_command, and to add_cmd + for sub-commands of that prefix. If this Command is not a prefix + command, then this field is unused. */ + 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) + args = ""; + argobj = PyUnicode_Decode (args, strlen (args), host_charset (), NULL); + 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 = PyUnicode_Decode (text, strlen (text), host_charset (), NULL); + if (! textobj) + error (_("Could not convert argument to Python string.")); + wordobj = PyUnicode_Decode (word, strlen (word), host_charset (), NULL); + 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 are 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_RUNNING", class_run) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_DATA", 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_STATUS", class_info) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_BREAKPOINTS", + class_breakpoint) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_TRACEPOINTS", + 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/ChangeLog b/gdb/testsuite/ChangeLog index b77ea99..0b72438 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2009-02-06 Thiago Jung Bauermann + + * gdb.python/python-cmd.exp: New file. + 2009-02-06 Tristan Gingold * gdb.arch/i386-sse.c (main): Replace call to puts by an nop asm. -- 2.7.4