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

@ -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,