From d7b32ed3aba175979887a8e870c27a6a7aa687ec Mon Sep 17 00:00:00 2001 From: Phil Muldoon Date: Thu, 29 Apr 2010 15:45:57 +0000 Subject: 2010-04-29 Phil Muldoon Tom Tromey Thiago Jung Bauermann * Makefile.in (SUBDIR_PYTHON_OBS): Add py-parameter. (SUBDIR_PYTHON_SRCS): Likewise. (py-parameter.o): New rule. * python/py-parameter.c: New file. * python/python-internal.h (gdbpy_initialize_parameter) (gdbpy_parameter, gdbpy_parameter_value) (gdbpy_parse_command_name): Declare. * python/py-cmd.c (parse_command_name): Rename to gdbpy_parse_command_name. (gdbpy_parse_command_name): Accept a starting list parameter and use over cmdlist. (cmdpy_init): Use gdbpy_parse_command_name. * python/python.c (parameter_to_python): Rename to gdbpy_parameter_to_python. Accept enum var_types and value. (gdbpy_parameter): Use gdbpy_parameter_value. (_initialize_python): Call gdbpy_initialize_parameters. 2010-04-29 Phil Muldoon * gdb.python/py-param.exp: New File. 2010-04-29 Phil Muldoon Tom Tromey Thiago Jung Bauermann * gdb.texinfo (Parameters In Python): New Node. --- gdb/ChangeLog | 21 ++ gdb/Makefile.in | 6 + gdb/NEWS | 4 +- gdb/doc/ChangeLog | 6 + gdb/doc/gdb.texinfo | 147 ++++++++ gdb/python/py-cmd.c | 14 +- gdb/python/py-param.c | 617 ++++++++++++++++++++++++++++++++++ gdb/python/python-internal.h | 9 + gdb/python/python.c | 24 +- gdb/testsuite/ChangeLog | 4 + gdb/testsuite/gdb.python/py-param.exp | 140 ++++++++ 11 files changed, 974 insertions(+), 18 deletions(-) create mode 100644 gdb/python/py-param.c create mode 100644 gdb/testsuite/gdb.python/py-param.exp (limited to 'gdb') diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e994911..dabcf10 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2010-04-29 Phil Muldoon + Tom Tromey + Thiago Jung Bauermann + + * Makefile.in (SUBDIR_PYTHON_OBS): Add py-parameter. + (SUBDIR_PYTHON_SRCS): Likewise. + (py-parameter.o): New rule. + * python/py-parameter.c: New file. + * python/python-internal.h (gdbpy_initialize_parameter) + (gdbpy_parameter, gdbpy_parameter_value) + (gdbpy_parse_command_name): Declare. + * python/py-cmd.c (parse_command_name): Rename to + gdbpy_parse_command_name. + (gdbpy_parse_command_name): Accept a starting list parameter and + use over cmdlist. + (cmdpy_init): Use gdbpy_parse_command_name. + * python/python.c (parameter_to_python): Rename to + gdbpy_parameter_to_python. Accept enum var_types and value. + (gdbpy_parameter): Use gdbpy_parameter_value. + (_initialize_python): Call gdbpy_initialize_parameters. + 2010-04-29 Matthew Gretton-Dann * MAINTAINERS: Add myself for write after approval privileges. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index cbbd813..754671f 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -275,6 +275,7 @@ SUBDIR_PYTHON_OBS = \ py-function.o \ py-lazy-string.o \ py-objfile.o \ + py-param.o \ py-prettyprint.o \ py-progspace.o \ py-symbol.o \ @@ -292,6 +293,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-function.c \ python/py-lazy-string.c \ python/py-objfile.c \ + python/py-param.c \ python/py-prettyprint.c \ python/py-progspace.c \ python/py-symbol.c \ @@ -2023,6 +2025,10 @@ py-objfile.o: $(srcdir)/python/py-objfile.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-objfile.c $(POSTCOMPILE) +py-param.o: $(srcdir)/python/py-param.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-param.c + $(POSTCOMPILE) + py-prettyprint.o: $(srcdir)/python/py-prettyprint.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-prettyprint.c $(POSTCOMPILE) diff --git a/gdb/NEWS b/gdb/NEWS index 99254c9..28201cd 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -80,7 +80,9 @@ is now deprecated. * Python scripting ** The GDB Python API now has access to breakpoints, symbols, symbol - tables, program spaces, and frame's code blocks. + tables, program spaces, and frame's code blocks. Additionally, GDB + Parameters can now be created from the API, and manipulated via + set/show in the CLI. ** New functions gdb.target_charset, gdb.target_wide_charset, gdb.progspaces, and gdb.current_progspace. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index c2bf0f8..b390c9c 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2010-04-29 Phil Muldoon + Tom Tromey + Thiago Jung Bauermann + + * gdb.texinfo (Parameters In Python): New Node. + 2010-04-29 Mihail Zenkov * gdb.texinfo: (Summary) Add mention about D language support. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 02bd212..e929481 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -19941,6 +19941,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Pretty Printing API:: Pretty-printing values. * Selecting Pretty-Printers:: How GDB chooses a pretty-printer. * Commands In Python:: Implementing new commands in Python. +* Parameters In Python:: Adding new @value{GDBN} parameters. * Functions In Python:: Writing new convenience functions. * Progspaces In Python:: Program spaces. * Objfiles In Python:: Object files. @@ -20962,6 +20963,152 @@ 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 Parameters In Python +@subsubsection Parameters In Python + +@cindex parameters in python +@cindex python parameters +@tindex gdb.Parameter +@tindex Parameter +You can implement new @value{GDBN} parameters using Python. A new +parameter is implemented as an instance of the @code{gdb.Parameter} +class. + +Parameters are exposed to the user via the @code{set} and +@code{show} commands. @xref{Help}. + +There are many parameters that already exist and can be set in +@value{GDBN}. Two examples are: @code{set follow fork} and +@code{set charset}. Setting these parameters influences certain +behavior in @value{GDBN}. Similarly, you can define parameters that +can be used to influence behavior in custom Python scripts and commands. + +@defmethod Parameter __init__ name @var{command-class} @var{parameter-class} @r{[}@var{enum-sequence}@r{]} +The object initializer for @code{Parameter} registers the new +parameter with @value{GDBN}. This initializer is normally invoked +from the subclass' own @code{__init__} method. + +@var{name} is the name of the new parameter. If @var{name} consists +of multiple words, then the initial words are looked for as prefix +parameters. An example of this can be illustrated with the +@code{set print} set of parameters. If @var{name} is +@code{print foo}, then @code{print} will be searched as the prefix +parameter. In this case the parameter can subsequently be accessed in +@value{GDBN} as @code{set print foo}. + +If @var{name} consists of multiple words, and no prefix parameter group +can be found, an exception is raised. + +@var{command-class} should be one of the @samp{COMMAND_} constants +(@pxref{Commands In Python}). This argument tells @value{GDBN} how to +categorize the new parameter in the help system. + +@var{parameter-class} should be one of the @samp{PARAM_} constants +defined below. This argument tells @value{GDBN} the type of the new +parameter; this information is used for input validation and +completion. + +If @var{parameter-class} is @code{PARAM_ENUM}, then +@var{enum-sequence} must be a sequence of strings. These strings +represent the possible values for the parameter. + +If @var{parameter-class} is not @code{PARAM_ENUM}, then the presence +of a fourth argument will cause an exception to be thrown. + +The help text for the new parameter is taken from the Python +documentation string for the parameter's class, if there is one. If +there is no documentation string, a default value is used. +@end defmethod + +@defivar Parameter set_doc +If this attribute exists, and is a string, then its value is used as +the help text for this parameter's @code{set} command. The value is +examined when @code{Parameter.__init__} is invoked; subsequent changes +have no effect. +@end defivar + +@defivar Parameter show_doc +If this attribute exists, and is a string, then its value is used as +the help text for this parameter's @code{show} command. The value is +examined when @code{Parameter.__init__} is invoked; subsequent changes +have no effect. +@end defivar + +@defivar Parameter value +The @code{value} attribute holds the underlying value of the +parameter. It can be read and assigned to just as any other +attribute. @value{GDBN} does validation when assignments are made. +@end defivar + + +When a new parameter is defined, its type must be specified. The +available types are represented by constants defined in the @code{gdb} +module: + +@table @code +@findex PARAM_BOOLEAN +@findex gdb.PARAM_BOOLEAN +@item PARAM_BOOLEAN +The value is a plain boolean. The Python boolean values, @code{True} +and @code{False} are the only valid values. + +@findex PARAM_AUTO_BOOLEAN +@findex gdb.PARAM_AUTO_BOOLEAN +@item PARAM_AUTO_BOOLEAN +The value has three possible states: true, false, and @samp{auto}. In +Python, true and false are represented using boolean constants, and +@samp{auto} is represented using @code{None}. + +@findex PARAM_UINTEGER +@findex gdb.PARAM_UINTEGER +@item PARAM_UINTEGER +The value is an unsigned integer. The value of 0 should be +interpreted to mean ``unlimited''. + +@findex PARAM_INTEGER +@findex gdb.PARAM_INTEGER +@item PARAM_INTEGER +The value is a signed integer. The value of 0 should be interpreted +to mean ``unlimited''. + +@findex PARAM_STRING +@findex gdb.PARAM_STRING +@item PARAM_STRING +The value is a string. When the user modifies the string, any escape +sequences, such as @samp{\t}, @samp{\f}, and octal escapes, are +translated into corresponding characters and encoded into the current +host charset. + +@findex PARAM_STRING_NOESCAPE +@findex gdb.PARAM_STRING_NOESCAPE +@item PARAM_STRING_NOESCAPE +The value is a string. When the user modifies the string, escapes are +passed through untranslated. + +@findex PARAM_OPTIONAL_FILENAME +@findex gdb.PARAM_OPTIONAL_FILENAME +@item PARAM_OPTIONAL_FILENAME +The value is a either a filename (a string), or @code{None}. + +@findex PARAM_FILENAME +@findex gdb.PARAM_FILENAME +@item PARAM_FILENAME +The value is a filename. This is just like +@code{PARAM_STRING_NOESCAPE}, but uses file names for completion. + +@findex PARAM_ZINTEGER +@findex gdb.PARAM_ZINTEGER +@item PARAM_ZINTEGER +The value is an integer. This is like @code{PARAM_INTEGER}, except 0 +is interpreted as itself. + +@findex PARAM_ENUM +@findex gdb.PARAM_ENUM +@item PARAM_ENUM +The value is a string, which must be one of a collection string +constants provided when the parameter is created. +@end table + @node Functions In Python @subsubsection Writing new convenience functions diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index d187d16..0998713 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -263,10 +263,14 @@ cmdpy_completer (struct cmd_list_element *command, char *text, char *word) *BASE_LIST is set to the final prefix command's list of *sub-commands. + START_LIST is the list in which the search starts. + 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) +char * +gdbpy_parse_command_name (char *text, + struct cmd_list_element ***base_list, + struct cmd_list_element **start_list) { struct cmd_list_element *elt; int len = strlen (text); @@ -299,7 +303,7 @@ parse_command_name (char *text, struct cmd_list_element ***base_list) ; if (i < 0) { - *base_list = &cmdlist; + *base_list = start_list; return result; } @@ -308,7 +312,7 @@ parse_command_name (char *text, struct cmd_list_element ***base_list) prefix_text[i + 1] = '\0'; text = prefix_text; - elt = lookup_cmd_1 (&text, cmdlist, NULL, 1); + elt = lookup_cmd_1 (&text, *start_list, NULL, 1); if (!elt || elt == (struct cmd_list_element *) -1) { PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."), @@ -399,7 +403,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw) return -1; } - cmd_name = parse_command_name (name, &cmd_list); + cmd_name = gdbpy_parse_command_name (name, &cmd_list, &cmdlist); if (! cmd_name) return -1; diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c new file mode 100644 index 0000000..36f96f5 --- /dev/null +++ b/gdb/python/py-param.c @@ -0,0 +1,617 @@ +/* GDB parameters implemented in Python + + Copyright (C) 2008, 2009, 2010 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" + +/* Parameter constants and their values. */ +struct parm_constant +{ + char *name; + int value; +}; + +struct parm_constant parm_constants[] = +{ + { "PARAM_BOOLEAN", var_boolean }, + { "PARAM_AUTO_BOOLEAN", var_auto_boolean }, + { "PARAM_UINTEGER", var_uinteger }, + { "PARAM_INTEGER", var_integer }, + { "PARAM_STRING", var_string }, + { "PARAM_STRING_NOESCAPE", var_string_noescape }, + { "PARAM_OPTIONAL_FILENAME", var_optional_filename }, + { "PARAM_FILENAME", var_filename }, + { "PARAM_ZINTEGER", var_zinteger }, + { "PARAM_ENUM", var_enum }, + { NULL, 0 } +}; + +/* A union that can hold anything described by enum var_types. */ +union parmpy_variable +{ + /* Hold an integer value, for boolean and integer types. */ + int intval; + + /* Hold an auto_boolean. */ + enum auto_boolean autoboolval; + + /* Hold an unsigned integer value, for uinteger. */ + unsigned int uintval; + + /* Hold a string, for the various string types. */ + char *stringval; + + /* Hold a string, for enums. */ + const char *cstringval; +}; + +/* A GDB parameter. */ +struct parmpy_object +{ + PyObject_HEAD + + /* The type of the parameter. */ + enum var_types type; + + /* The value of the parameter. */ + union parmpy_variable value; + + /* For an enum command, the possible values. The vector is + allocated with xmalloc, as is each element. It is + NULL-terminated. */ + const char **enumeration; +}; + +typedef struct parmpy_object parmpy_object; + +static PyTypeObject parmpy_object_type; + +/* Some handy string constants. */ +static PyObject *set_doc_cst; +static PyObject *show_doc_cst; + + + +/* Get an attribute. */ +static PyObject * +get_attr (PyObject *obj, PyObject *attr_name) +{ + if (PyString_Check (attr_name) + && ! strcmp (PyString_AsString (attr_name), "value")) + { + parmpy_object *self = (parmpy_object *) obj; + return gdbpy_parameter_value (self->type, &self->value); + } + + return PyObject_GenericGetAttr (obj, attr_name); +} + +/* Set a parameter value from a Python value. Return 0 on success, -1 + on failure. */ +static int +set_parameter_value (parmpy_object *self, PyObject *value) +{ + int cmp; + + switch (self->type) + { + case var_string: + case var_string_noescape: + case var_optional_filename: + case var_filename: + if (! gdbpy_is_string (value) + && (self->type == var_filename + || value != Py_None)) + { + PyErr_SetString (PyExc_RuntimeError, + _("String required for filename.")); + + return -1; + } + if (self->value.stringval) + xfree (self->value.stringval); + if (value == Py_None) + { + if (self->type == var_optional_filename) + self->value.stringval = xstrdup (""); + else + self->value.stringval = NULL; + } + else + self->value.stringval = python_string_to_host_string (value); + break; + + case var_enum: + { + int i; + char *str; + + if (! gdbpy_is_string (value)) + { + PyErr_SetString (PyExc_RuntimeError, + _("ENUM arguments must be a string.")); + return -1; + } + + str = python_string_to_host_string (value); + for (i = 0; self->enumeration[i]; ++i) + if (! strcmp (self->enumeration[i], str)) + break; + xfree (str); + if (! self->enumeration[i]) + { + PyErr_SetString (PyExc_RuntimeError, + _("The value must be member of an enumeration.")); + return -1; + } + self->value.cstringval = self->enumeration[i]; + break; + } + + case var_boolean: + if (! PyBool_Check (value)) + { + PyErr_SetString (PyExc_RuntimeError, + _("A boolean argument is required.")); + return -1; + } + cmp = PyObject_IsTrue (value); + if (cmp < 0) + return -1; + self->value.intval = cmp; + break; + + case var_auto_boolean: + if (! PyBool_Check (value) && value != Py_None) + { + PyErr_SetString (PyExc_RuntimeError, + _("A boolean or None is required")); + return -1; + } + + if (value == Py_None) + self->value.autoboolval = AUTO_BOOLEAN_AUTO; + else + { + cmp = PyObject_IsTrue (value); + if (cmp < 0 ) + return -1; + if (cmp == 1) + self->value.autoboolval = AUTO_BOOLEAN_TRUE; + else + self->value.autoboolval = AUTO_BOOLEAN_FALSE; + + break; + } + + case var_integer: + case var_zinteger: + case var_uinteger: + { + long l; + int ok; + + if (! PyInt_Check (value)) + { + PyErr_SetString (PyExc_RuntimeError, + _("The value must be integer.")); + return -1; + } + + l = PyInt_AsLong (value); + if (self->type == var_uinteger) + { + ok = (l >= 0 && l <= UINT_MAX); + if (l == 0) + l = UINT_MAX; + } + else if (self->type == var_integer) + { + ok = (l >= INT_MIN && l <= INT_MAX); + if (l == 0) + l = INT_MAX; + } + else + ok = (l >= INT_MIN && l <= INT_MAX); + + if (! ok) + { + PyErr_SetString (PyExc_RuntimeError, + _("Range exceeded.")); + return -1; + } + + self->value.intval = (int) l; + break; + } + + default: + PyErr_SetString (PyExc_RuntimeError, + _("Unhandled type in parameter value.")); + return -1; + } + + return 0; +} + +/* Set an attribute. */ +static int +set_attr (PyObject *obj, PyObject *attr_name, PyObject *val) +{ + if (PyString_Check (attr_name) + && ! strcmp (PyString_AsString (attr_name), "value")) + { + if (!val) + { + PyErr_SetString (PyExc_RuntimeError, + _("Cannot delete a parameter's value.")); + return -1; + } + return set_parameter_value ((parmpy_object *) obj, val); + } + + return PyObject_GenericSetAttr (obj, attr_name, val); +} + + + +/* A helper function that dispatches to the appropriate add_setshow + function. */ +static void +add_setshow_generic (int parmclass, enum command_class cmdclass, + char *cmd_name, parmpy_object *self, + char *set_doc, char *show_doc, char *help_doc, + struct cmd_list_element **set_list, + struct cmd_list_element **show_list) +{ + switch (parmclass) + { + case var_boolean: + add_setshow_boolean_cmd (cmd_name, cmdclass, &self->value.intval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_auto_boolean: + add_setshow_auto_boolean_cmd (cmd_name, cmdclass, + &self->value.autoboolval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_uinteger: + add_setshow_uinteger_cmd (cmd_name, cmdclass, &self->value.uintval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_integer: + add_setshow_integer_cmd (cmd_name, cmdclass, &self->value.intval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_string: + add_setshow_string_cmd (cmd_name, cmdclass, &self->value.stringval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_string_noescape: + add_setshow_string_noescape_cmd (cmd_name, cmdclass, + &self->value.stringval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_optional_filename: + add_setshow_optional_filename_cmd (cmd_name, cmdclass, + &self->value.stringval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_filename: + add_setshow_filename_cmd (cmd_name, cmdclass, &self->value.stringval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_zinteger: + add_setshow_zinteger_cmd (cmd_name, cmdclass, &self->value.intval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + break; + + case var_enum: + add_setshow_enum_cmd (cmd_name, cmdclass, self->enumeration, + &self->value.cstringval, + set_doc, show_doc, help_doc, + NULL, NULL, set_list, show_list); + /* Initialize the value, just in case. */ + self->value.cstringval = self->enumeration[0]; + break; + } +} + +/* A helper which computes enum values. Returns 1 on success, 0 on + error. */ +static int +compute_enum_values (parmpy_object *self, PyObject *enum_values) +{ + Py_ssize_t size, i; + + if (! enum_values) + { + PyErr_SetString (PyExc_RuntimeError, + _("An enumeration is required for PARAM_ENUM.")); + return 0; + } + + if (! PySequence_Check (enum_values)) + { + PyErr_SetString (PyExc_RuntimeError, + _("The enumeration is not a sequence.")); + return 0; + } + + size = PySequence_Size (enum_values); + if (size < 0) + return 0; + if (size == 0) + { + PyErr_SetString (PyExc_RuntimeError, + _("The enumeration is empty.")); + return 0; + } + + self->enumeration = xmalloc ((size + 1) * sizeof (char *)); + memset (self->enumeration, 0, (size + 1) * sizeof (char *)); + + for (i = 0; i < size; ++i) + { + PyObject *item = PySequence_GetItem (enum_values, i); + if (! item) + return 0; + if (! gdbpy_is_string (item)) + { + PyErr_SetString (PyExc_RuntimeError, + _("The enumeration item not a string.")); + return 0; + } + self->enumeration[i] = python_string_to_host_string (item); + } + + return 1; +} + +/* A helper function which returns a documentation string for an + object. */ +static char * +get_doc_string (PyObject *object, PyObject *attr) +{ + char *result = NULL; + if (PyObject_HasAttr (object, attr)) + { + PyObject *ds_obj = PyObject_GetAttr (object, attr); + if (ds_obj && gdbpy_is_string (ds_obj)) + result = python_string_to_host_string (ds_obj); + } + if (! result) + result = xstrdup (_("This command is not documented.")); + return result; +} + +/* Object initializer; sets up gdb-side structures for command. + + Use: __init__(NAME, CMDCLASS, PARMCLASS, [ENUM]) + + NAME is the name of the parameter. 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. + + PARMCLASS is the type of the parameter. It should be one of the + PARAM_* constants defined in the gdb module. + + If PARMCLASS is PARAM_ENUM, then the final argument should be a + collection of strings. These strings are the valid values for this + parameter. + + The documentation for the parameter is taken from the doc string + for the python class. + +*/ +static int +parmpy_init (PyObject *self, PyObject *args, PyObject *kwds) +{ + parmpy_object *obj = (parmpy_object *) self; + char *name; + char *set_doc, *show_doc, *doc; + char *cmd_name; + int parmclass, cmdtype; + PyObject *enum_values = NULL; + struct cmd_list_element *cmd_list; + struct cmd_list_element **set_list, **show_list; + volatile struct gdb_exception except; + + if (! PyArg_ParseTuple (args, "sii|O", &name, &cmdtype, &parmclass, + &enum_values)) + 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 (parmclass != var_boolean && parmclass != var_auto_boolean + && parmclass != var_uinteger && parmclass != var_integer + && parmclass != var_string && parmclass != var_string_noescape + && parmclass != var_optional_filename && parmclass != var_filename + && parmclass != var_zinteger && parmclass != var_enum) + { + PyErr_SetString (PyExc_RuntimeError, _("Invalid parameter class argument.")); + return -1; + } + + if (enum_values && parmclass != var_enum) + { + PyErr_SetString (PyExc_RuntimeError, + _("Only PARAM_ENUM accepts a fourth argument.")); + return -1; + } + if (parmclass == var_enum) + { + if (! compute_enum_values (obj, enum_values)) + return -1; + } + else + obj->enumeration = NULL; + obj->type = (enum var_types) parmclass; + memset (&obj->value, 0, sizeof (obj->value)); + + cmd_name = gdbpy_parse_command_name (name, &set_list, + &setlist); + + if (! cmd_name) + return -1; + xfree (cmd_name); + cmd_name = gdbpy_parse_command_name (name, &show_list, + &showlist); + if (! cmd_name) + return -1; + + set_doc = get_doc_string (self, set_doc_cst); + show_doc = get_doc_string (self, show_doc_cst); + doc = get_doc_string (self, gdbpy_doc_cst); + + Py_INCREF (self); + + TRY_CATCH (except, RETURN_MASK_ALL) + { + add_setshow_generic (parmclass, (enum command_class) cmdtype, + cmd_name, obj, + set_doc, show_doc, + doc, set_list, show_list); + } + if (except.reason < 0) + { + xfree (cmd_name); + xfree (set_doc); + xfree (show_doc); + xfree (doc); + Py_DECREF (self); + PyErr_Format (except.reason == RETURN_QUIT + ? PyExc_KeyboardInterrupt : PyExc_RuntimeError, + "%s", except.message); + return -1; + } + return 0; +} + + + +/* Initialize the 'parameters' module. */ +void +gdbpy_initialize_parameters (void) +{ + int i; + + if (PyType_Ready (&parmpy_object_type) < 0) + return; + + set_doc_cst = PyString_FromString ("set_doc"); + if (! set_doc_cst) + return; + show_doc_cst = PyString_FromString ("show_doc"); + if (! show_doc_cst) + return; + + for (i = 0; parm_constants[i].name; ++i) + { + if (PyModule_AddIntConstant (gdb_module, + parm_constants[i].name, + parm_constants[i].value) < 0) + return; + } + + Py_INCREF (&parmpy_object_type); + PyModule_AddObject (gdb_module, "Parameter", + (PyObject *) &parmpy_object_type); +} + + + +static PyTypeObject parmpy_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Parameter", /*tp_name*/ + sizeof (parmpy_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*/ + get_attr, /*tp_getattro*/ + set_attr, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB parameter object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + parmpy_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 d27c5d2..f41f32a 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -67,6 +67,9 @@ typedef int Py_ssize_t; a real symtab_and_line structure is needed. */ #include "symtab.h" +/* Also needed to parse enum var_types. */ +#include "command.h" + struct block; struct value; struct language_defn; @@ -90,6 +93,11 @@ PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw); PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length, const char *encoding, struct type *type); PyObject *gdbpy_get_hook_function (const char *); +PyObject *gdbpy_parameter (PyObject *self, PyObject *args); +PyObject *gdbpy_parameter_value (enum var_types type, void *var); +char *gdbpy_parse_command_name (char *text, + struct cmd_list_element ***base_list, + struct cmd_list_element **start_list); PyObject *symtab_and_line_to_sal_object (struct symtab_and_line sal); PyObject *symtab_to_symtab_object (struct symtab *symtab); @@ -126,6 +134,7 @@ void gdbpy_initialize_pspace (void); void gdbpy_initialize_objfile (void); void gdbpy_initialize_breakpoints (void); void gdbpy_initialize_lazy_string (void); +void gdbpy_initialize_parameters (void); struct cleanup *make_cleanup_py_decref (PyObject *py); diff --git a/gdb/python/python.c b/gdb/python/python.c index a1c1d8c..cf87b66 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -192,11 +192,10 @@ python_command (char *arg, int from_tty) /* Transform a gdb parameters's value into a Python value. May return NULL (and set a Python exception) on error. Helper function for get_parameter. */ - -static PyObject * -parameter_to_python (struct cmd_list_element *cmd) +PyObject * +gdbpy_parameter_value (enum var_types type, void *var) { - switch (cmd->var_type) + switch (type) { case var_string: case var_string_noescape: @@ -204,7 +203,7 @@ parameter_to_python (struct cmd_list_element *cmd) case var_filename: case var_enum: { - char *str = * (char **) cmd->var; + char *str = * (char **) var; if (! str) str = ""; return PyString_Decode (str, strlen (str), host_charset (), NULL); @@ -212,7 +211,7 @@ parameter_to_python (struct cmd_list_element *cmd) case var_boolean: { - if (* (int *) cmd->var) + if (* (int *) var) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -220,7 +219,7 @@ parameter_to_python (struct cmd_list_element *cmd) case var_auto_boolean: { - enum auto_boolean ab = * (enum auto_boolean *) cmd->var; + enum auto_boolean ab = * (enum auto_boolean *) var; if (ab == AUTO_BOOLEAN_TRUE) Py_RETURN_TRUE; else if (ab == AUTO_BOOLEAN_FALSE) @@ -230,15 +229,15 @@ parameter_to_python (struct cmd_list_element *cmd) } case var_integer: - if ((* (int *) cmd->var) == INT_MAX) + if ((* (int *) var) == INT_MAX) Py_RETURN_NONE; /* Fall through. */ case var_zinteger: - return PyLong_FromLong (* (int *) cmd->var); + return PyLong_FromLong (* (int *) var); case var_uinteger: { - unsigned int val = * (unsigned int *) cmd->var; + unsigned int val = * (unsigned int *) var; if (val == UINT_MAX) Py_RETURN_NONE; return PyLong_FromUnsignedLong (val); @@ -252,7 +251,7 @@ parameter_to_python (struct cmd_list_element *cmd) /* A Python function which returns a gdb parameter's value as a Python value. */ -static PyObject * +PyObject * gdbpy_parameter (PyObject *self, PyObject *args) { struct cmd_list_element *alias, *prefix, *cmd; @@ -278,7 +277,7 @@ gdbpy_parameter (PyObject *self, PyObject *args) if (! cmd->var) return PyErr_Format (PyExc_RuntimeError, _("`%s' is not a parameter."), arg); - return parameter_to_python (cmd); + return gdbpy_parameter_value (cmd->var_type, cmd->var); } /* Wrapper for target_charset. */ @@ -645,6 +644,7 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_symtabs (); gdbpy_initialize_blocks (); gdbpy_initialize_functions (); + gdbpy_initialize_parameters (); gdbpy_initialize_types (); gdbpy_initialize_pspace (); gdbpy_initialize_objfile (); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index c234c7d..5b276db 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2010-04-29 Phil Muldoon + + * gdb.python/py-param.exp: New File. + 2010-04-29 Mihail Zenkov * gdb.base/default.exp: Fix "set language" test. diff --git a/gdb/testsuite/gdb.python/py-param.exp b/gdb/testsuite/gdb.python/py-param.exp new file mode 100644 index 0000000..6c0ff97 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-param.exp @@ -0,0 +1,140 @@ +# Copyright (C) 2010 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 . + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing convenience functions to 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 +} + +# Run a command in GDB, and report a failure if a Python exception is thrown. +# If report_pass is true, report a pass if no exception is thrown. +proc gdb_py_test_silent_cmd {cmd name report_pass} { + global gdb_prompt + + gdb_test_multiple $cmd $name { + -re "Traceback.*$gdb_prompt $" { fail $name } + -re "$gdb_prompt $" { if $report_pass { pass $name } } + } +} + +# Start with a fresh gdb. +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +# Test a simple boolean parameter. +gdb_py_test_multiple "Simple gdb booleanparameter" \ + "python" "" \ + "class TestParam (gdb.Parameter):" "" \ + " \"\"\"When enabled, test param does something useful. When disabled, does nothing.\"\"\"" "" \ + " show_doc = \"Show whether the state of the Test Parameter does something useful\"" ""\ + " set_doc = \"Set whether the state of the Test Parameter does something useful\"" "" \ + " def __init__ (self, name):" "" \ + " super (TestParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_param = TestParam ('print test-param')" ""\ + "end" + +gdb_test "python print test_param.value" "True" "Test parameter value" +gdb_test "show print test-param" "Whether the state of the Test Parameter does something useful is on.*" "Show parameter on" +gdb_py_test_silent_cmd "set print test-param off" "Turn off parameter" 1 +gdb_test "show print test-param" "Whether the state of the Test Parameter does something useful is off.*" "Show parameter off" +gdb_test "python print test_param.value" "False" "Test parameter value" +gdb_test "help show print test-param" "Show whether the state of the Test Parameter does something useful.*" "Test show help" +gdb_test "help set print test-param" "Set whether the state of the Test Parameter does something useful.*" "Test set help" +gdb_test "help set print" "set print test-param -- Set whether the state of the Test Parameter.*" "Test general help" + +# Test an enum parameter. +gdb_py_test_multiple "enum gdb parameter" \ + "python" "" \ + "class TestEnumParam (gdb.Parameter):" "" \ + " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \ + " show_doc = \"Show the state of the enum\"" ""\ + " set_doc = \"Set the state of the enum\"" "" \ + " def __init__ (self, name):" "" \ + " super (TestEnumParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_ENUM, \[\"one\", \"two\"\])" "" \ + " self.value = \"one\"" "" \ + "test_enum_param = TestEnumParam ('print test-enum-param')" ""\ + "end" + +gdb_test "python print test_enum_param.value" "one" "Test enum parameter value" +gdb_test "show print test-enum-param" "The state of the enum is \"one\".*" "Show parameter is initial value" +gdb_py_test_silent_cmd "set print test-enum-param two" "Set parameter to enum value" 1 +gdb_test "show print test-enum-param" "The state of the enum is \"two\".*" "Show parameter is new value" +gdb_test "python print test_enum_param.value" "two" "Test enum parameter value" +gdb_test "set print test-enum-param three" "Undefined item: \"three\".*" "Set invalid enum parameter" + +# Test a file parameter. +gdb_py_test_multiple "file gdb parameter" \ + "python" "" \ + "class TestFileParam (gdb.Parameter):" "" \ + " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \ + " show_doc = \"Show the name of the file\"" ""\ + " set_doc = \"Set the name of the file\"" "" \ + " def __init__ (self, name):" "" \ + " super (TestFileParam, self).__init__ (name, gdb.COMMAND_FILES, gdb.PARAM_FILENAME)" "" \ + " self.value = \"foo.txt\"" "" \ + "test_file_param = TestFileParam ('test-file-param')" ""\ + "end" + +gdb_test "python print test_file_param.value" "foo.txt" "Test file parameter value" +gdb_test "show test-file-param" "The name of the file is \"foo.txt\".*" "Show initial file value" +gdb_py_test_silent_cmd "set test-file-param bar.txt" "Set new file parameter" 1 +gdb_test "show test-file-param" "The name of the file is \"bar.txt\".*" "Show new file value" +gdb_test "python print test_file_param.value" "bar.txt" "Test new file parameter value" +gdb_test "set test-file-param" "Argument required.*" + +# Test a file parameter. +gdb_py_test_multiple "file gdb parameter" \ + "python" "" \ + "class TestFileParam (gdb.Parameter):" "" \ + " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \ + " show_doc = \"Show the name of the file\"" ""\ + " set_doc = \"Set the name of the file\"" "" \ + " def __init__ (self, name):" "" \ + " super (TestFileParam, self).__init__ (name, gdb.COMMAND_FILES, gdb.PARAM_FILENAME)" "" \ + " self.value = \"foo.txt\"" "" \ + "test_file_param = TestFileParam ('test-file-param')" ""\ + "end" + +gdb_test "python print test_file_param.value" "foo.txt" "Test parameter value" +gdb_test "show test-file-param" "The name of the file is \"foo.txt\".*" "Show parameter on" +gdb_py_test_silent_cmd "set test-file-param bar.txt" "Turn off parameter" 1 +gdb_test "show test-file-param" "The name of the file is \"bar.txt\".*" "Show parameter on" +gdb_test "python print test_file_param.value" "bar.txt" "Test parameter value" +gdb_test "set test-file-param" "Argument required.*" -- cgit v1.1