2008-09-25 Thiago Jung Bauermann gdb/ * Makefile.in (SUBDIR_PYTHON_OBS): Add python-value.o. (SUBDIR_PYTHON_SRCS): Add python-value.c. (python-value.o): New target. * configure.ac (CONFIG_OBS): Add python-value.o. (CONFIG_SRCS): Add python/python-value.c * python-internal.h (value_object_type): Add external declaration. (gdbpy_get_value_from_history, value_to_value_object, convert_value_from_python, gdbpy_initialize_values): Add function prototype. * python/python-value.c: New file. * python/python.c (GdbMethods): Add gdbpy_get_value_from_history. (_initialize_python): Call gdbpy_initialize_values. * python/python.h (values_in_python): Add external declaration. * value.c (value_prepend_to_list, value_remove_from_list): New functions. (preserve_values): Iterate over values_in_python list as well. * value.h (value_prepend_to_list, value_remove_from_list): Add function prototypes. gdb/doc/ * gdb.texinfo. (Values From Inferior): New subsubsection. gdb/testsuite/ * gdb.python/python-values.c: New file. * gdb.python/python-values.exp: New file. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 003467d..2cfd0d8 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -271,10 +271,12 @@ SUBDIR_TUI_CFLAGS= \ # SUBDIR_PYTHON_OBS = \ python.o \ - python-utils.o + python-utils.o \ + python-value.o SUBDIR_PYTHON_SRCS = \ python/python.c \ - python/python-utils.c + python/python-utils.c \ + python/python-value.c SUBDIR_PYTHON_DEPS = SUBDIR_PYTHON_LDFLAGS= SUBDIR_PYTHON_CFLAGS= @@ -1839,6 +1841,10 @@ python-utils.o: $(srcdir)/python/python-utils.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c $(POSTCOMPILE) +python-value.o: $(srcdir)/python/python-value.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-value.c + $(POSTCOMPILE) + # diff --git a/gdb/configure.ac b/gdb/configure.ac index 680fba0..677a6ae 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -624,10 +624,10 @@ if test "${have_libpython}" = yes; then AC_MSG_RESULT(${PYTHON_CFLAGS}) fi else - # Even if Python support is not compiled in, we need to have this file + # Even if Python support is not compiled in, we need to have these files # included in order to recognize the GDB command "python". - CONFIG_OBS="$CONFIG_OBS python.o" - CONFIG_SRCS="$CONFIG_SRCS python/python.c" + CONFIG_OBS="$CONFIG_OBS python.o python-value.o" + CONFIG_SRCS="$CONFIG_SRCS python/python.c python/python-value.c" fi AC_SUBST(PYTHON_CFLAGS) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 891dfb2..8758d1e 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17393,6 +17393,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. @menu * Basic Python:: Basic Python Functions. * Exception Handling:: +* Values From Inferior:: @end menu @node Basic Python @@ -17471,6 +17472,36 @@ 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. +@node Values From Inferior +@subsubsection Values From Inferior + +@value{GDBN} provides values it obtains from the inferior program in an +object of type @code{gdb.Value}. This object keeps track of information +related to the value such as its type, the address where it is kept in +the inferior, and so on. + +You can directly use @code{gdb.Value} objects in places which make sense +for the type of the value they contain. For instance, you can use the +value of an integer variable in the inferior as if it were a Python +integer variable. + +If the @code{gdb.Value} object is of a structure type or an instance of a +class, its elements and methods are provided using dictionary syntax. +For example, if @code{some_val} is a @code{gdb.Value} instance holding +a structure, you can access its @code{foo} element with: + +@smallexample +bar = some_val['foo'] +@end smallexample + +The following method is provided: + +@defmethod Value dereference +If the @code{gdb.Value} object is of a pointer type, you can dereference +it using the @code{dereference} method. This gives you a new +@code{gdb.Value} object for the pointed-to contents. +@end defmethod + @node Interpreters @chapter Command Interpreters @cindex command interpreters diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index f850448..72f7a5f 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -43,11 +43,18 @@ typedef Py_intptr_t Py_ssize_t; #error "Unable to find usable Python.h" #endif -struct block; -struct symbol; -struct symtab_and_line; +struct value; extern PyObject *gdb_module; +extern PyTypeObject value_object_type; + +PyObject *gdbpy_get_value_from_history (PyObject *self, PyObject *args); + +PyObject *value_to_value_object (struct value *v); + +struct value *convert_value_from_python (PyObject *obj); + +void gdbpy_initialize_values (void); struct cleanup *make_cleanup_py_decref (PyObject *py); diff --git a/gdb/python/python-value.c b/gdb/python/python-value.c new file mode 100644 index 0000000..26b8ae1 --- /dev/null +++ b/gdb/python/python-value.c @@ -0,0 +1,665 @@ +/* Python interface to values. + + Copyright (C) 2008 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 "charset.h" +#include "value.h" +#include "exceptions.h" +#include "language.h" +#include "dfp.h" + +/* List of all values which are currently exposed to Python. It is + maintained so that when an objfile is discarded, preserve_values + can copy the values' types if needed. This is declared + unconditionally to reduce the number of uses of HAVE_PYTHON in the + generic code. */ +struct value *values_in_python; + +#ifdef HAVE_PYTHON + +#include "python-internal.h" + +typedef struct { + PyObject_HEAD + struct value *value; + int owned_by_gdb; +} value_object; + +static void valpy_dealloc (PyObject *obj); +static PyObject *valpy_new (PyTypeObject *subtype, PyObject *args, + PyObject *keywords); +static Py_ssize_t valpy_length (PyObject *self); +static PyObject *valpy_getitem (PyObject *self, PyObject *key); +static int valpy_setitem (PyObject *self, PyObject *key, PyObject *value); +static PyObject *valpy_str (PyObject *self); +static PyObject *valpy_add (PyObject *self, PyObject *other); +static PyObject *valpy_subtract (PyObject *self, PyObject *other); +static PyObject *valpy_multiply (PyObject *self, PyObject *other); +static PyObject *valpy_divide (PyObject *self, PyObject *other); +static PyObject *valpy_remainder (PyObject *self, PyObject *other); +static PyObject *valpy_power (PyObject *self, PyObject *other, PyObject *unused); +static PyObject *valpy_negative (PyObject *self); +static PyObject *valpy_positive (PyObject *self); +static PyObject *valpy_absolute (PyObject *self); +static int valpy_nonzero (PyObject *self); +static PyObject *valpy_richcompare (PyObject *self, PyObject *other, int op); +static PyObject *valpy_dereference (PyObject *self, PyObject *args); + +static PyMethodDef value_object_methods[] = { + { "dereference", valpy_dereference, METH_NOARGS, "Dereferences the value." }, + {NULL} /* Sentinel */ +}; + +static PyNumberMethods value_object_as_number = { + valpy_add, + valpy_subtract, + valpy_multiply, + valpy_divide, + valpy_remainder, + NULL, /* nb_divmod */ + valpy_power, /* nb_power */ + valpy_negative, /* nb_negative */ + valpy_positive, /* nb_positive */ + valpy_absolute, /* nb_absolute */ + valpy_nonzero /* nb_nonzero */ +}; + +static PyMappingMethods value_object_as_mapping = { + valpy_length, + valpy_getitem, + valpy_setitem +}; + +PyTypeObject value_object_type = { + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Value", /*tp_name*/ + sizeof (value_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + valpy_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + &value_object_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &value_object_as_mapping, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + valpy_str, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/ + "GDB value object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + valpy_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + value_object_methods /* tp_methods */ +}; + + +/* Called by the Python interpreter when deallocating a value object. */ +static void +valpy_dealloc (PyObject *obj) +{ + value_object *self = (value_object *) obj; + + value_remove_from_list (&values_in_python, self->value); + + if (!self->owned_by_gdb) + value_free (self->value); + self->ob_type->tp_free (self); +} + +/* Called when a new gdb.Value object needs to be allocated. */ +static PyObject * +valpy_new (PyTypeObject *subtype, PyObject *args, PyObject *keywords) +{ + struct value *value = NULL; /* Initialize to appease gcc warning. */ + value_object *value_obj; + volatile struct gdb_exception except; + + if (PyTuple_Size (args) != 1) + { + PyErr_SetString (PyExc_TypeError, _("Value object creation takes only " \ + "1 argument")); + return NULL; + } + + value_obj = (value_object *) subtype->tp_alloc (subtype, 1); + if (value_obj == NULL) + { + PyErr_SetString (PyExc_MemoryError, _("Could not allocate memory to " \ + "create Value object.")); + return NULL; + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + value = convert_value_from_python (PyTuple_GetItem (args, 0)); + } + if (except.reason < 0) + { + subtype->tp_free (value_obj); + return PyErr_Format (except.reason == RETURN_QUIT + ? PyExc_KeyboardInterrupt : PyExc_TypeError, + "%s", except.message); + } + + value_obj->value = value; + release_value (value); + value_prepend_to_list (&values_in_python, value); + + return (PyObject *) value_obj; +} + +/* Given a value of a pointer type, apply the C unary * operator to it. */ +static PyObject * +valpy_dereference (PyObject *self, PyObject *args) +{ + struct value *res_val = NULL; /* Initialize to appease gcc warning. */ + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + res_val = value_ind (((value_object *) self)->value); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return value_to_value_object (res_val); +} + +static Py_ssize_t +valpy_length (PyObject *self) +{ + /* We don't support getting the number of elements in a struct / class. */ + PyErr_SetString (PyExc_NotImplementedError, + "Invalid operation on gdb.Value."); + return -1; +} + +/* Given string name of an element inside structure, return its value + object. */ +static PyObject * +valpy_getitem (PyObject *self, PyObject *key) +{ + value_object *self_value = (value_object *) self; + char *field; + struct value *res_val = NULL; /* Initialize to appease gcc warning. */ + struct cleanup *old; + volatile struct gdb_exception except; + + field = python_string_to_target_string (key); + if (field == NULL) + return NULL; + + old = make_cleanup (xfree, field); + + TRY_CATCH (except, RETURN_MASK_ALL) + { + res_val = value_struct_elt (&self_value->value, NULL, field, 0, NULL); + } + GDB_PY_HANDLE_EXCEPTION (except); + + do_cleanups (old); + + return value_to_value_object (res_val); +} + +static int +valpy_setitem (PyObject *self, PyObject *key, PyObject *value) +{ + PyErr_Format (PyExc_NotImplementedError, + _("Setting of struct elements is not currently supported.")); + return -1; +} + +/* Called by the Python interpreter to obtain string representation + of the object. */ +static PyObject * +valpy_str (PyObject *self) +{ + char *s = NULL; + long dummy; + struct ui_file *stb; + struct cleanup *old_chain; + PyObject *result; + volatile struct gdb_exception except; + + stb = mem_fileopen (); + old_chain = make_cleanup_ui_file_delete (stb); + + TRY_CATCH (except, RETURN_MASK_ALL) + { + common_val_print (((value_object *) self)->value, stb, 0, 0, 0, + Val_pretty_default, current_language); + s = ui_file_xstrdup (stb, &dummy); + } + GDB_PY_HANDLE_EXCEPTION (except); + + do_cleanups (old_chain); + + result = PyUnicode_Decode (s, strlen (s), host_charset (), NULL); + xfree (s); + + return result; +} + +enum valpy_opcode +{ + VALPY_ADD, + VALPY_SUB, + VALPY_MUL, + VALPY_DIV, + VALPY_REM, + VALPY_POW +}; + +/* Returns a value object which is the sum of this value with the given + integer argument. */ +static PyObject * +valpy_binop (enum valpy_opcode opcode, PyObject *self, PyObject *other) +{ + long l; + double d; + struct value *res_val = NULL; /* Initialize to appease gcc warning. */ + struct value *other_val; + value_object *self_value; + volatile struct gdb_exception except; + + /* If the gdb.Value object is the second operand, then it will be passed + to us as the OTHER argument, and SELF will be an entirely different + kind of object, altogether. Swap them to avoid surprises. */ + if (!PyObject_TypeCheck (self, &value_object_type)) + { + PyObject *tmp; + + tmp = self; + self = other; + other = tmp; + } + + self_value = (value_object *) self; + + if (PyObject_TypeCheck (other, &value_object_type)) + other_val = ((value_object *) other)->value; + else if (PyInt_Check (other)) + { + l = PyInt_AsLong (other); + if (PyErr_Occurred ()) + return Py_NotImplemented; + + other_val = value_from_longest (builtin_type_int, l); + } + else if (PyFloat_Check (other)) + { + d = PyFloat_AsDouble (other); + if (PyErr_Occurred ()) + return Py_NotImplemented; + + other_val = value_from_double (builtin_type_double, d); + } + else + /* If the types cannot be added, Python documentation says to return + NotImplemented (http://docs.python.org/ref/numeric-types.html). */ + return Py_NotImplemented; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + switch (opcode) + { + case VALPY_ADD: + res_val = value_add (self_value->value, other_val); + break; + case VALPY_SUB: + res_val = value_sub (self_value->value, other_val); + break; + case VALPY_MUL: + res_val = value_binop (self_value->value, other_val, BINOP_MUL); + break; + case VALPY_DIV: + res_val = value_binop (self_value->value, other_val, BINOP_DIV); + break; + case VALPY_REM: + res_val = value_binop (self_value->value, other_val, BINOP_REM); + break; + case VALPY_POW: + res_val = value_binop (self_value->value, other_val, BINOP_EXP); + break; + } + } + GDB_PY_HANDLE_EXCEPTION (except); + + return value_to_value_object (res_val); +} + +static PyObject * +valpy_add (PyObject *self, PyObject *other) +{ + return valpy_binop (VALPY_ADD, self, other); +} + +static PyObject * +valpy_subtract (PyObject *self, PyObject *other) +{ + return valpy_binop (VALPY_SUB, self, other); +} + +static PyObject * +valpy_multiply (PyObject *self, PyObject *other) +{ + return valpy_binop (VALPY_MUL, self, other); +} + +static PyObject * +valpy_divide (PyObject *self, PyObject *other) +{ + return valpy_binop (VALPY_DIV, self, other); +} + +static PyObject * +valpy_remainder (PyObject *self, PyObject *other) +{ + return valpy_binop (VALPY_REM, self, other); +} + +static PyObject * +valpy_power (PyObject *self, PyObject *other, PyObject *unused) +{ + /* We don't support the ternary form of pow. I don't know how to express + that, so let's just throw NotImplementedError to at least do something + about it. */ + if (unused != Py_None) + { + PyErr_SetString (PyExc_NotImplementedError, + "Invalid operation on gdb.Value."); + return NULL; + } + + return valpy_binop (VALPY_POW, self, other); +} + +static PyObject * +valpy_negative (PyObject *self) +{ + struct value *val = NULL; + volatile struct gdb_exception except; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + val = value_neg (((value_object *) self)->value); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return value_to_value_object (val); +} + +static PyObject * +valpy_positive (PyObject *self) +{ + struct value *copy = value_copy (((value_object *) self)->value); + + return value_to_value_object (copy); +} + +static PyObject * +valpy_absolute (PyObject *self) +{ + if (value_less (((value_object *) self)->value, + value_from_longest (builtin_type_int, 0))) + return valpy_negative (self); + else + return valpy_positive (self); +} + +/* Implements boolean evaluation of gdb.Value. */ +static int +valpy_nonzero (PyObject *self) +{ + value_object *self_value = (value_object *) self; + struct type *type; + + type = check_typedef (value_type (self_value->value)); + + if (is_integral_type (type)) + return !!value_as_long (self_value->value); + else if (TYPE_CODE (type) == TYPE_CODE_FLT) + return value_as_double (self_value->value) != 0; + else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) + return !decimal_is_zero (value_contents (self_value->value), + TYPE_LENGTH (type)); + else + { + PyErr_SetString (PyExc_TypeError, _("Attempted truth testing on invalid " + "gdb.Value type.")); + return 0; + } +} + +/* Implements comparison operations for value objects. */ +static PyObject * +valpy_richcompare (PyObject *self, PyObject *other, int op) +{ + int result = 0; + struct value *value_self, *value_other; + volatile struct gdb_exception except; + + if (PyObject_TypeCheck (other, &value_object_type)) + value_other = ((value_object *) other)->value; + else if (PyInt_Check (other)) + { + LONGEST l; + + l = PyInt_AsLong (other); + if (PyErr_Occurred ()) + return NULL; + + value_other = value_from_longest (builtin_type_int, l); + } + else if (PyFloat_Check (other)) + { + DOUBLEST d; + + d = PyFloat_AsDouble (other); + if (PyErr_Occurred ()) + return NULL; + + value_other = value_from_double (builtin_type_double, d); + } + else if (PyString_Check (other) || PyUnicode_Check (other)) + { + char *str; + + str = python_string_to_target_string (other); + value_other = value_from_string (str); + xfree (str); + } + else if (other == Py_None) + /* Comparing with None is special. From what I can tell, in Python + None is smaller than anything else. */ + switch (op) { + case Py_LT: + case Py_LE: + case Py_EQ: + Py_RETURN_FALSE; + case Py_NE: + case Py_GT: + case Py_GE: + Py_RETURN_TRUE; + default: + /* Can't happen. */ + PyErr_SetString (PyExc_NotImplementedError, + "Invalid operation on gdb.Value."); + return NULL; + } + else + { + PyErr_SetString (PyExc_NotImplementedError, + "Operation not supported on gdb.Value of this type."); + return NULL; + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + switch (op) { + case Py_LT: + result = value_less (((value_object *) self)->value, value_other); + break; + case Py_LE: + result = value_less (((value_object *) self)->value, value_other) + || value_equal (((value_object *) self)->value, value_other); + break; + case Py_EQ: + result = value_equal (((value_object *) self)->value, value_other); + break; + case Py_NE: + result = !value_equal (((value_object *) self)->value, value_other); + break; + case Py_GT: + result = value_less (value_other, ((value_object *) self)->value); + break; + case Py_GE: + result = value_less (value_other, ((value_object *) self)->value) + || value_equal (((value_object *) self)->value, value_other); + break; + default: + /* Can't happen. */ + PyErr_SetString (PyExc_NotImplementedError, + "Invalid operation on gdb.Value."); + return NULL; + } + } + GDB_PY_HANDLE_EXCEPTION (except); + + if (result == 1) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +/* Returns an object for a value which is released from the all_values chain, + so its lifetime is not bound to the execution of a command. */ +PyObject * +value_to_value_object (struct value *val) +{ + value_object *val_obj; + + val_obj = PyObject_New (value_object, &value_object_type); + if (val_obj != NULL) + { + val_obj->value = val; + release_value (val); + value_prepend_to_list (&values_in_python, val); + } + + return (PyObject *) val_obj; +} + +/* Try to convert a Python value to a gdb value. If the value cannot + be converted, throw a gdb exception. */ + +struct value * +convert_value_from_python (PyObject *obj) +{ + struct value *value = NULL; /* -Wall */ + PyObject *target_str, *unicode_str; + struct cleanup *old; + + if (! obj) + error (_("Internal error while converting Python value.")); + + if (PyBool_Check (obj)) + value = value_from_longest (builtin_type_bool, obj == Py_True); + else if (PyInt_Check (obj)) + value = value_from_longest (builtin_type_int, PyInt_AsLong (obj)); + else if (PyLong_Check (obj)) + { + LONGEST l = PyLong_AsLongLong (obj); + if (! PyErr_Occurred ()) + value = value_from_longest (builtin_type_long, l); + } + else if (PyFloat_Check (obj)) + { + double d = PyFloat_AsDouble (obj); + if (! PyErr_Occurred ()) + value = value_from_double (builtin_type_double, d); + } + else if (PyString_Check (obj) || PyUnicode_Check (obj)) + { + char *s; + + s = python_string_to_target_string (obj); + if (s == NULL) + return NULL; + + old = make_cleanup (xfree, s); + value = value_from_string (s); + do_cleanups (old); + } + else if (PyObject_TypeCheck (obj, &value_object_type)) + value = ((value_object *) obj)->value; + else + error (_("Could not convert Python object: %s"), + PyString_AsString (PyObject_Str (obj))); + + if (PyErr_Occurred ()) + error (_("Error converting Python value.")); + + return value; +} + +/* Returns value object in the ARGth position in GDB's history. */ +PyObject * +gdbpy_get_value_from_history (PyObject *self, PyObject *args) +{ + int i; + struct value *res_val = NULL; /* Initialize to appease gcc warning. */ + volatile struct gdb_exception except; + + if (!PyArg_ParseTuple (args, "i", &i)) + return NULL; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + res_val = access_value_history (i); + } + GDB_PY_HANDLE_EXCEPTION (except); + + return value_to_value_object (res_val); +} + +void +gdbpy_initialize_values (void) +{ + value_object_type.tp_new = valpy_new; + if (PyType_Ready (&value_object_type) < 0) + return; + + Py_INCREF (&value_object_type); + PyModule_AddObject (gdb_module, "Value", (PyObject *) &value_object_type); + + values_in_python = NULL; +} + +#endif /* HAVE_PYTHON */ diff --git a/gdb/python/python.c b/gdb/python/python.c index 7186914..2159271 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -52,6 +52,8 @@ static PyObject *gdbpy_flush (PyObject *, PyObject *); static PyMethodDef GdbMethods[] = { + { "get_value_from_history", gdbpy_get_value_from_history, METH_VARARGS, + "Get a value from history" }, { "execute", execute_gdb_command, METH_VARARGS, "Execute a gdb command" }, { "get_parameter", get_parameter, METH_VARARGS, @@ -407,6 +409,8 @@ 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_initialize_values (); + PyRun_SimpleString ("import gdb"); /* Create a couple objects which are used for Python's stdout and diff --git a/gdb/python/python.h b/gdb/python/python.h index 00ff159..102fef6 100644 --- a/gdb/python/python.h +++ b/gdb/python/python.h @@ -22,6 +22,8 @@ #include "value.h" +extern struct value *values_in_python; + void eval_python_from_control_command (struct command_line *); #endif /* GDB_PYTHON_H */ diff --git a/gdb/testsuite/gdb.python/python-values.c b/gdb/testsuite/gdb.python/python-values.c new file mode 100644 index 0000000..82cfd9a --- /dev/null +++ b/gdb/testsuite/gdb.python/python-values.c @@ -0,0 +1,41 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2008 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 . */ + +struct s +{ + int a; + int b; +}; + +union u +{ + int a; + float b; +}; + +int +main (int argc, char *argv[]) +{ + struct s s; + union u u; + + s.a = 3; + s.b = 5; + u.a = 7; + + return 0; /* break to inspect struct and union */ +} diff --git a/gdb/testsuite/gdb.python/python-values.exp b/gdb/testsuite/gdb.python/python-values.exp new file mode 100644 index 0000000..7b9ea03 --- /dev/null +++ b/gdb/testsuite/gdb.python/python-values.exp @@ -0,0 +1,278 @@ +# Copyright (C) 2008 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 values to Python. + +if $tracelevel then { + strace $tracelevel +} + +set testfile "python-values" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "Couldn't compile ${srcfile}" + return -1 +} + +# 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 +} + +proc test_value_creation {} { + global gdb_prompt + + gdb_test_multiple "python i = gdb.Value (True)" "create boolean value" { + -re "Traceback.*$gdb_prompt $" {fail "create boolean value"} + -re "$gdb_prompt $" {pass "create boolean value"} + } + + gdb_test_multiple "python i = gdb.Value (5)" "create integer value" { + -re "Traceback.*$gdb_prompt $" {fail "create integer value"} + -re "$gdb_prompt $" {pass "create integer value"} + } + + gdb_test_multiple "python i = gdb.Value (5L)" "create long value" { + -re "Traceback.*$gdb_prompt $" {fail "create long value"} + -re "$gdb_prompt $" {pass "create long value"} + } + + gdb_test_multiple "python f = gdb.Value (1.25)" "create double value" { + -re "Traceback.*$gdb_prompt $" {fail "create double value"} + -re "$gdb_prompt $" {pass "create double value"} + } + + gdb_test_multiple "python a = gdb.Value ('string test')" "create 8-bit string value" { + -re "Traceback.*$gdb_prompt $" {fail "create 8-bit string value"} + -re "$gdb_prompt $" {pass "create 8-bit string value"} + } + + gdb_test "python print a" "\"string test\"" "print 8-bit string" + gdb_test "python print a.__class__" "" "verify type of 8-bit string" + + gdb_test_multiple "python a = gdb.Value (u'unicode test')" "create unicode value" { + -re "Traceback.*$gdb_prompt $" {fail "create unicode value"} + -re "$gdb_prompt $" {pass "create unicode value"} + } + + gdb_test "python print a" "\"unicode test\"" "print Unicode string" + gdb_test "python print a.__class__" "" "verify type of unicode string" +} + +proc test_value_numeric_ops {} { + global gdb_prompt + + gdb_test_multiple "python i = gdb.Value (5)" "create first integer value" { + -re "Traceback.*$gdb_prompt $" {fail "create first integer value"} + -re "$gdb_prompt $" {} + } + + gdb_test_multiple "python j = gdb.Value (2)" "create second integer value" { + -re "Traceback.*$gdb_prompt $" {fail "create second integer value"} + -re "$gdb_prompt $" {} + } + + gdb_test_multiple "python f = gdb.Value (1.25)" "create first double value" { + -re "Traceback.*$gdb_prompt $" {fail "create first double value"} + -re "$gdb_prompt $" {} + } + + gdb_test_multiple "python g = gdb.Value (2.5)" "create second double value" { + -re "Traceback.*$gdb_prompt $" {fail "create second double value"} + -re "$gdb_prompt $" {} + } + + gdb_test "python print 'result = ' + str(i+j)" " = 7" "add two integer values" + gdb_test "python print (i+j).__class__" "" "verify type of integer add result" + + gdb_test "python print 'result = ' + str(f+g)" " = 3.75" "add two double values" + gdb_test "python print 'result = ' + str(i-j)" " = 3" "subtract two integer values" + gdb_test "python print 'result = ' + str(f-g)" " = -1.25" "subtract two double values" + gdb_test "python print 'result = ' + str(i*j)" " = 10" "multiply two integer values" + gdb_test "python print 'result = ' + str(f*g)" " = 3.125" "multiply two double values" + gdb_test "python print 'result = ' + str(i/j)" " = 2" "divide two integer values" + gdb_test "python print 'result = ' + str(f/g)" " = 0.5" "divide two double values" + gdb_test "python print 'result = ' + str(i%j)" " = 1" "take remainder of two integer values" + # Remainder of float is implemented in Python but not in GDB's value system. + + gdb_test "python print 'result = ' + str(i**j)" " = 25" "integer value raised to the power of another integer value" + gdb_test "python print 'result = ' + str(g**j)" " = 6.25" "double value raised to the power of integer value" + + gdb_test "python print 'result = ' + str(-i)" " = -5" "negated integer value" + gdb_test "python print 'result = ' + str(+i)" " = 5" "positive integer value" + gdb_test "python print 'result = ' + str(-f)" " = -1.25" "negated double value" + gdb_test "python print 'result = ' + str(+f)" " = 1.25" "positive double value" + gdb_test "python print 'result = ' + str(abs(j-i))" " = 3" "absolute of integer value" + gdb_test "python print 'result = ' + str(abs(f-g))" " = 1.25" "absolute of double value" + + # Test gdb.Value mixed with Python types. + + gdb_test "python print 'result = ' + str(i+1)" " = 6" "add integer value with python integer" + gdb_test "python print (i+1).__class__" "" "verify type of mixed integer add result" + gdb_test "python print 'result = ' + str(f+1.5)" " = 2.75" "add double value with python float" + + gdb_test "python print 'result = ' + str(1+i)" " = 6" "add python integer with integer value" + gdb_test "python print 'result = ' + str(1.5+f)" " = 2.75" "add python float with double value" + + # Test some invalid operations. + + gdb_test_multiple "python print 'result = ' + str(i+'foo')" "catch error in python type conversion" { + -re "unsupported operand type.*$gdb_prompt $" {pass "catch error in python type conversion"} + -re "result = .*$gdb_prompt $" {fail "catch error in python type conversion"} + -re "$gdb_prompt $" {fail "catch error in python type conversion"} + } + + gdb_test_multiple "python print 'result = ' + str(i+gdb.Value('foo'))" "catch throw of GDB error" { + -re "Traceback.*$gdb_prompt $" {pass "catch throw of GDB error"} + -re "result = .*$gdb_prompt $" {fail "catch throw of GDB error"} + -re "$gdb_prompt $" {fail "catch throw of GDB error"} + } +} + +proc test_value_boolean {} { + # First, define a useful function to test booleans. + gdb_py_test_multiple "define function to test booleans" \ + "python" "" \ + "def test_bool (val):" "" \ + " if val:" "" \ + " print 'yay'" "" \ + " else:" "" \ + " print 'nay'" "" \ + "end" "" + + gdb_test "py test_bool (gdb.Value (True))" "yay" "check evaluation of true boolean value in expression" + + gdb_test "py test_bool (gdb.Value (False))" "nay" "check evaluation of false boolean value in expression" + + gdb_test "py test_bool (gdb.Value (5))" "yay" "check evaluation of true integer value in expression" + + gdb_test "py test_bool (gdb.Value (0))" "nay" "check evaluation of false integer value in expression" + + gdb_test "py test_bool (gdb.Value (5.2))" "yay" "check evaluation of true integer value in expression" + + gdb_test "py test_bool (gdb.Value (0.0))" "nay" "check evaluation of false integer value in expression" +} + +proc test_value_compare {} { + gdb_test "py print gdb.Value (1) < gdb.Value (1)" "False" "less than, equal" + gdb_test "py print gdb.Value (1) < gdb.Value (2)" "True" "less than, less" + gdb_test "py print gdb.Value (2) < gdb.Value (1)" "False" "less than, greater" + gdb_test "py print gdb.Value (2) < None" "False" "less than, None" + + gdb_test "py print gdb.Value (1) <= gdb.Value (1)" "True" "less or equal, equal" + gdb_test "py print gdb.Value (1) <= gdb.Value (2)" "True" "less or equal, less" + gdb_test "py print gdb.Value (2) <= gdb.Value (1)" "False" "less or equal, greater" + gdb_test "py print gdb.Value (2) <= None" "False" "less or equal, None" + + gdb_test "py print gdb.Value (1) == gdb.Value (1)" "True" "equality of gdb.Values" + gdb_test "py print gdb.Value (1) == gdb.Value (2)" "False" "inequality of gdb.Values" + gdb_test "py print gdb.Value (1) == 1.0" "True" "equality of gdb.Value with Python value" + gdb_test "py print gdb.Value (1) == 2" "False" "inequality of gdb.Value with Python value" + gdb_test "py print gdb.Value (1) == None" "False" "inequality of gdb.Value with None" + + gdb_test "py print gdb.Value (1) != gdb.Value (1)" "False" "inequality, false" + gdb_test "py print gdb.Value (1) != gdb.Value (2)" "True" "inequality, true" + gdb_test "py print gdb.Value (1) != None" "True" "inequality, None" + + gdb_test "py print gdb.Value (1) > gdb.Value (1)" "False" "greater than, equal" + gdb_test "py print gdb.Value (1) > gdb.Value (2)" "False" "greater than, less" + gdb_test "py print gdb.Value (2) > gdb.Value (1)" "True" "greater than, greater" + gdb_test "py print gdb.Value (2) > None" "True" "greater than, None" + + gdb_test "py print gdb.Value (1) >= gdb.Value (1)" "True" "greater or equal, equal" + gdb_test "py print gdb.Value (1) >= gdb.Value (2)" "False" "greater or equal, less" + gdb_test "py print gdb.Value (2) >= gdb.Value (1)" "True" "greater or equal, greater" + gdb_test "py print gdb.Value (2) >= None" "True" "greater or equal, None" +} + +proc test_value_in_inferior {} { + global gdb_prompt + global testfile + + gdb_breakpoint [gdb_get_line_number "break to inspect struct and union"] + gdb_start_cmd + + # Avoid race condition where a continue command in gdb_continue_to_breakpoint + # is issued too early. + gdb_test "" "$gdb_prompt" + + gdb_continue_to_breakpoint "break to inspect struct and union" + + # Just get inferior variable s in the value history, available to python. + gdb_test "print s" " = {a = 3, b = 5}" "" + + gdb_test_multiple "python s = gdb.get_value_from_history (0)" "get value from history" { + -re "Traceback.*$gdb_prompt $" {fail "get value from history"} + -re "$gdb_prompt $" {pass "get value from history"} + } + + gdb_test "python print 'result = ' + str(s\['a'\])" " = 3" "acess element inside struct using 8-bit string name" + gdb_test "python print 'result = ' + str(s\[u'a'\])" " = 3" "acess element inside struct using unicode name" + + # Test dereferencing the argv pointer + + # Just get inferior variable argv the value history, available to python. + gdb_test "print argv" " = \\(char \\*\\*\\) 0x.*" "" + + gdb_test_multiple "python argv = gdb.get_value_from_history (0)" "" { + -re "Traceback.*$gdb_prompt $" {fail "get value from history"} + -re "$gdb_prompt $" {} + } + + gdb_test_multiple "python arg0 = argv.dereference ()" "" { + -re "Traceback.*$gdb_prompt $" {fail "dereference value"} + -re "$gdb_prompt $" {pass "dereference value"} + } + + # Check that the dereferenced value is sane + gdb_test "python print arg0" "0x.*$testfile\"" "verify dereferenced value" +} + + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +gdb_test_multiple "python print 'hello, world!'" "verify python support" { + -re "not supported.*$gdb_prompt $" { + unsupported "python support is disabled" + return -1 + } + -re "$gdb_prompt $" {} +} + +test_value_creation +test_value_numeric_ops +test_value_boolean +test_value_compare +test_value_in_inferior diff --git a/gdb/value.c b/gdb/value.c index b38bae0..b652174 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -37,6 +37,8 @@ #include "dfp.h" #include "objfiles.h" +#include "python/python.h" + /* Prototypes for exported functions. */ void _initialize_values (void); @@ -130,8 +132,8 @@ struct value /* Values are stored in a chain, so that they can be deleted easily over calls to the inferior. Values assigned to internal - variables or put into the value history are taken off this - list. */ + variables, put into the value history or exposed to Python are + taken off this list. */ struct value *next; /* Register number if the value is from a register. */ @@ -257,6 +259,31 @@ allocate_repeat_value (struct type *type, int count) type, range_type)); } +/* Needed if another module needs to maintain its on list of values. */ +void +value_prepend_to_list (struct value **head, struct value *val) +{ + val->next = *head; + *head = val; +} + +/* Needed if another module needs to maintain its on list of values. */ +void +value_remove_from_list (struct value **head, struct value *val) +{ + struct value *prev; + + if (*head == val) + *head = (*head)->next; + else + for (prev = *head; prev->next; prev = prev->next) + if (prev->next == val) + { + prev->next = val->next; + break; + } +} + /* Accessor methods. */ struct value * @@ -916,6 +943,7 @@ preserve_values (struct objfile *objfile) htab_t copied_types; struct value_history_chunk *cur; struct internalvar *var; + struct value *val; int i; /* Create the hash table. We allocate on the objfile's obstack, since @@ -930,6 +958,9 @@ preserve_values (struct objfile *objfile) for (var = internalvars; var; var = var->next) preserve_one_value (var->value, objfile, copied_types); + for (val = values_in_python; val; val = val->next) + preserve_one_value (val, objfile, copied_types); + htab_delete (copied_types); } diff --git a/gdb/value.h b/gdb/value.h index 2aac9b2..45a6ced 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -40,9 +40,15 @@ struct language_defn; struct value; +/* Needed if another module needs to maintain its on list of values. */ + +void value_prepend_to_list (struct value **head, struct value *val); +void value_remove_from_list (struct value **head, struct value *val); + /* Values are stored in a chain, so that they can be deleted easily - over calls to the inferior. Values assigned to internal variables - or put into the value history are taken off this list. */ + over calls to the inferior. Values assigned to internal variables, + put into the value history or exposed to Python are taken off this + list. */ struct value *value_next (struct value *);