Add python gdb.GdbError and gdb.string_to_argv.

* NEWS: Document them.
	* python/py-cmd.c (cmdpy_function): Don't print a traceback if
	the exception is gdb.GdbError.  Print a second traceback if there's
	an error computing the error message.
	(gdbpy_string_to_argv): New function.
	* python/py-utils.c (gdbpy_obj_to_string): New function.
	(gdbpy_exception_to_string): New function.
	* python/python-internal.h (gdbpy_string_to_argv): Declare.
	(gdbpy_obj_to_string, gdbpy_exception_to_string): Declare.
	(gdbpy_gdberror_exc): Declare.
	* python/python.c (gdbpy_gdberror_exc): New global.
	(_initialize_python): Initialize gdbpy_gdberror_exc and create
	gdb.GdbError.
	(GdbMethods): Add string_to_argv.

	doc/
	* gdb.texinfo (Exception Handling): Document gdb.GdbError.
	(Commands In Python): Document gdb.string_to_argv.

	testsuite/
	* gdb.python/py-cmd.exp: Add tests for gdb.GdbError and
	gdb.string_to_argv.
This commit is contained in:
Doug Evans 2010-05-25 15:27:17 +00:00
parent 8e45593ff3
commit 07ca107c2d
10 changed files with 255 additions and 13 deletions

View File

@ -1,3 +1,21 @@
2010-05-25 Doug Evans <dje@google.com>
Add python gdb.GdbError and gdb.string_to_argv.
* NEWS: Document them.
* python/py-cmd.c (cmdpy_function): Don't print a traceback if
the exception is gdb.GdbError. Print a second traceback if there's
an error computing the error message.
(gdbpy_string_to_argv): New function.
* python/py-utils.c (gdbpy_obj_to_string): New function.
(gdbpy_exception_to_string): New function.
* python/python-internal.h (gdbpy_string_to_argv): Declare.
(gdbpy_obj_to_string, gdbpy_exception_to_string): Declare.
(gdbpy_gdberror_exc): Declare.
* python/python.c (gdbpy_gdberror_exc): New global.
(_initialize_python): Initialize gdbpy_gdberror_exc and create
gdb.GdbError.
(GdbMethods): Add string_to_argv.
2010-05-21 Pierre Muller <muller@ics.u-strasbg.fr>
* windows-nat.c (display_selector): Call GetLastError to give better

View File

@ -87,7 +87,9 @@ is now deprecated.
set/show in the CLI.
** New functions gdb.target_charset, gdb.target_wide_charset,
gdb.progspaces, and gdb.current_progspace.
gdb.progspaces, gdb.current_progspace, and gdb.string_to_argv.
** New exception gdb.GdbError.
** Pretty-printers are now also looked up in the current program space.

View File

@ -1,3 +1,8 @@
2010-05-25 Doug Evans <dje@google.com>
* gdb.texinfo (Exception Handling): Document gdb.GdbError.
(Commands In Python): Document gdb.string_to_argv.
2010-05-02 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdbint.texinfo (Host Definition): Remove items NORETURN and

View File

@ -20082,6 +20082,30 @@ message as its value, and the Python call stack backtrace at the
Python statement closest to where the @value{GDBN} error occured as the
traceback.
@findex gdb.GdbError
When implementing @value{GDBN} commands in Python via @code{gdb.Command},
it is useful to be able to throw an exception that doesn't cause a
traceback to be printed. For example, the user may have invoked the
command incorrectly. Use the @code{gdb.GdbError} exception
to handle this case. Example:
@smallexample
(gdb) python
>class HelloWorld (gdb.Command):
> """Greet the whole world."""
> def __init__ (self):
> super (HelloWorld, self).__init__ ("hello-world", gdb.COMMAND_OBSCURE)
> def invoke (self, args, from_tty):
> argv = gdb.string_to_argv (args)
> if len (argv) != 0:
> raise gdb.GdbError ("hello-world takes no arguments")
> print "Hello, World!"
>HelloWorld ()
>end
(gdb) hello-world 42
hello-world takes no arguments
@end smallexample
@node Values From Inferior
@subsubsection Values From Inferior
@cindex values from inferior, with Python
@ -20773,6 +20797,20 @@ 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.
@findex gdb.string_to_argv
To break @var{argument} up into an argv-like string use
@code{gdb.string_to_argv}. This function behaves identically to
@value{GDBN}'s internal argument lexer @code{buildargv}.
It is recommended to use this for consistency.
Arguments are separated by spaces and may be quoted.
Example:
@smallexample
print gdb.string_to_argv ("1 2\ \\\"3 '4 \"5' \"6 '7\"")
['1', '2 "3', '4 "5', "6 '7"]
@end smallexample
@end defmethod
@cindex completion of Python commands

View File

@ -88,6 +88,7 @@ cmdpy_dont_repeat (PyObject *self, PyObject *args)
/* Called if the gdb cmd_list_element is destroyed. */
static void
cmdpy_destroyer (struct cmd_list_element *self, void *context)
{
@ -111,6 +112,7 @@ cmdpy_destroyer (struct cmd_list_element *self, void *context)
}
/* Called by gdb to invoke the command. */
static void
cmdpy_function (struct cmd_list_element *command, char *args, int from_tty)
{
@ -145,30 +147,51 @@ cmdpy_function (struct cmd_list_element *command, char *args, int from_tty)
ttyobj, NULL);
Py_DECREF (argobj);
Py_DECREF (ttyobj);
if (! result)
{
PyObject *ptype, *pvalue, *ptraceback;
char *msg;
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);
/* Try to fetch an error message contained within ptype, pvalue.
When fetching the error message we need to make our own copy,
we no longer own ptype, pvalue after the call to PyErr_Restore. */
strcpy (copy, s);
msg = gdbpy_exception_to_string (ptype, pvalue);
make_cleanup (xfree, msg);
if (msg == NULL)
{
/* An error occurred computing the string representation of the
error message. This is rare, but we should inform the user. */
printf_filtered (_("An error occurred in a Python command\n"
"and then another occurred computing the error message.\n"));
gdbpy_print_stack ();
}
/* Don't print the stack for gdb.GdbError exceptions.
It is generally used to flag user errors.
We also don't want to print "Error occurred in Python command"
for user errors. However, a missing message for gdb.GdbError
exceptions is arguably a bug, so we flag it as such. */
if (! PyErr_GivenExceptionMatches (ptype, gdbpy_gdberror_exc)
|| msg == NULL || *msg == '\0')
{
PyErr_Restore (ptype, pvalue, ptraceback);
gdbpy_print_stack ();
error (_("Error occurred in Python command: %s"), copy);
if (msg != NULL && *msg != '\0')
error (_("Error occurred in Python command: %s"), msg);
else
error (_("Error occurred in Python command."));
}
else
{
PyErr_Restore (ptype, pvalue, ptraceback);
gdbpy_print_stack ();
error (_("Error occurred in Python command."));
}
error ("%s", msg);
}
Py_DECREF (result);
do_cleanups (cleanup);
}
@ -589,3 +612,55 @@ static PyTypeObject cmdpy_object_type =
0, /* tp_alloc */
PyType_GenericNew /* tp_new */
};
/* Utility to build a buildargv-like result from ARGS.
This intentionally parses arguments the way libiberty/argv.c:buildargv
does. It splits up arguments in a reasonable way, and we want a standard
way of parsing arguments. Several gdb commands use buildargv to parse their
arguments. Plus we want to be able to write compatible python
implementations of gdb commands. */
PyObject *
gdbpy_string_to_argv (PyObject *self, PyObject *args)
{
PyObject *py_argv;
char *input;
if (!PyArg_ParseTuple (args, "s", &input))
return NULL;
py_argv = PyList_New (0);
/* buildargv uses NULL to represent an empty argument list, but we can't use
that in Python. Instead, if ARGS is "" then return an empty list.
This undoes the NULL -> "" conversion that cmdpy_function does. */
if (*input != '\0')
{
char **c_argv = gdb_buildargv (input);
int i;
for (i = 0; c_argv[i] != NULL; ++i)
{
PyObject *argp = PyString_FromString (c_argv[i]);
if (argp == NULL
|| PyList_Append (py_argv, argp) < 0)
{
if (argp != NULL)
{
Py_DECREF (argp);
}
Py_DECREF (py_argv);
freeargv (c_argv);
return NULL;
}
}
freeargv (c_argv);
}
return py_argv;
}

View File

@ -222,3 +222,53 @@ gdbpy_is_string (PyObject *obj)
{
return PyString_Check (obj) || PyUnicode_Check (obj);
}
/* Return the string representation of OBJ, i.e., str (obj).
Space for the result is malloc'd, the caller must free.
If the result is NULL a python error occurred, the caller must clear it. */
char *
gdbpy_obj_to_string (PyObject *obj)
{
PyObject *str_obj = PyObject_Str (obj);
if (str_obj != NULL)
{
char *msg = xstrdup (PyString_AsString (str_obj));
Py_DECREF (str_obj);
return msg;
}
return NULL;
}
/* Return the string representation of the exception represented by
TYPE, VALUE which is assumed to have been obtained with PyErr_Fetch,
i.e., the error indicator is currently clear.
Space for the result is malloc'd, the caller must free.
If the result is NULL a python error occurred, the caller must clear it. */
char *
gdbpy_exception_to_string (PyObject *ptype, PyObject *pvalue)
{
PyObject *str_obj = PyObject_Str (pvalue);
char *str;
/* There are a few cases to consider.
For example:
pvalue is a string when PyErr_SetString is used.
pvalue is not a string when raise "foo" is used, instead it is None
and ptype is "foo".
So the algorithm we use is to print `str (pvalue)' if it's not
None, otherwise we print `str (ptype)'.
Using str (aka PyObject_Str) will fetch the error message from
gdb.GdbError ("message"). */
if (pvalue && pvalue != Py_None)
str = gdbpy_obj_to_string (pvalue);
else
str = gdbpy_obj_to_string (ptype);
return str;
}

View File

@ -92,6 +92,7 @@ PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
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_string_to_argv (PyObject *self, PyObject *args);
PyObject *gdbpy_get_hook_function (const char *);
PyObject *gdbpy_parameter (PyObject *self, PyObject *args);
PyObject *gdbpy_parameter_value (enum var_types type, void *var);
@ -179,6 +180,9 @@ PyObject *python_string_to_target_python_string (PyObject *obj);
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);
char *gdbpy_obj_to_string (PyObject *obj);
char *gdbpy_exception_to_string (PyObject *ptype, PyObject *pvalue);
int gdbpy_is_lazy_string (PyObject *result);
gdb_byte *gdbpy_extract_lazy_string (PyObject *string,
struct type **str_type,
@ -197,4 +201,6 @@ extern PyObject *gdbpy_children_cst;
extern PyObject *gdbpy_to_string_cst;
extern PyObject *gdbpy_display_hint_cst;
extern PyObject *gdbpy_gdberror_exc;
#endif /* GDB_PYTHON_INTERNAL_H */

View File

@ -57,6 +57,8 @@ PyObject *gdbpy_children_cst;
PyObject *gdbpy_display_hint_cst;
PyObject *gdbpy_doc_cst;
/* The GdbError exception. */
PyObject *gdbpy_gdberror_exc;
/* Architecture and language to be used in callbacks from
the Python interpreter. */
@ -655,6 +657,9 @@ Enables or disables printing of Python stack traces."),
PyModule_AddStringConstant (gdb_module, "HOST_CONFIG", (char*) host_name);
PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
gdbpy_gdberror_exc = PyErr_NewException ("gdb.GdbError", NULL, NULL);
PyModule_AddObject (gdb_module, "GdbError", gdbpy_gdberror_exc);
gdbpy_initialize_auto_load ();
gdbpy_initialize_values ();
gdbpy_initialize_frames ();
@ -771,6 +776,12 @@ Return the name of the current target charset." },
"target_wide_charset () -> string.\n\
Return the name of the current target wide charset." },
{ "string_to_argv", gdbpy_string_to_argv, METH_VARARGS,
"string_to_argv (String) -> Array.\n\
Parse String and return an argv-like array.\n\
Arguments are separate by spaces and may be quoted."
},
{ "write", gdbpy_write, METH_VARARGS,
"Write a string using gdb's filtered stream." },
{ "flush", gdbpy_flush, METH_NOARGS,

View File

@ -1,3 +1,8 @@
2010-05-25 Doug Evans <dje@google.com>
* gdb.python/py-cmd.exp: Add tests for gdb.GdbError and
gdb.string_to_argv.
2010-05-24 Michael Snyder <msnyder@vmware.com>
* gdb.base/call-ar-st.exp: Replace send_gdb with gdb_test.

View File

@ -126,3 +126,35 @@ gdb_py_test_multiple "input new subcommand" \
"end" ""
gdb_test "info newsubcmd ugh" "newsubcmd output, arg = ugh" "call newsubcmd"
# Test a command that throws gdb.GdbError.
gdb_py_test_multiple "input command to throw error" \
"python" "" \
"class test_error_cmd (gdb.Command):" "" \
" def __init__ (self):" "" \
" super (test_error_cmd, self).__init__ (\"test_error_cmd\", gdb.COMMAND_OBSCURE)" "" \
" def invoke (self, arg, from_tty):" "" \
" raise gdb.GdbError ('you lose!')" "" \
"test_error_cmd ()" "" \
"end" ""
gdb_test "test_error_cmd ugh" "you lose!" "call error command"
# Test gdb.string_to_argv.
gdb_test "python print gdb.string_to_argv (\"1 2 3\")" \
{\['1', '2', '3'\]} \
"string_to_argv (\"1 2 3\")"
gdb_test "python print gdb.string_to_argv (\"'1 2' 3\")" \
{\['1 2', '3'\]} \
"string_to_argv (\"'1 2' 3\")"
gdb_test "python print gdb.string_to_argv ('\"1 2\" 3')" \
{\['1 2', '3'\]} \
"string_to_argv ('\"1 2\" 3')"
gdb_test "python print gdb.string_to_argv ('1\\ 2 3')" \
{\['1 2', '3'\]} \
"string_to_argv ('1\\ 2 3')"