* [rfc] expose gdb values to python
@ 2008-09-12 6:05 Thiago Jung Bauermann
2008-09-20 21:29 ` Tom Tromey
0 siblings, 1 reply; 28+ messages in thread
From: Thiago Jung Bauermann @ 2008-09-12 6:05 UTC (permalink / raw)
To: gdb-patches ml
Hi folks,
This is the patch exposing struct value to Python. The object it defines
is used quite a lot in other patches in the series, so it is central to
the Python scripting experience (so to say).
It implements a lot of Python __magic__ functions in order to make the
gdb.Value objects behave as much as possible as Python native objects
corresponding to the struct value they (the gdb.Value objects)
represent. Also, it makes structure elements available using dictionary
syntax (e.g., baz = foo['bar'] to access foo.bar).
Because of that, the documentation is short. There's not much new stuff
to talk about.
The testcase is complete. I believe it tests every feature of gdb.Value
objects. All tests pass.
There's only one thing that bothers me with this patch (I know, there
are two FIXMEs, but one of them doesn't bother me much. Hang on.): the
implementation of __str__ uses current_language in its call to
common_val_print. I believe it's better to avoid using current_language,
right? I don't think there's a way to get a sensible language_defn to
use here, so my only idea is to add an element to struct value which
holds the language associated with the value. This element would be
filled at the moment the value is created. Sounds like a lot of work...
WDYT?
The other FIXME is for the bogus implementation of valpy_length, which
is supposed to return the number of elements in the gdb.Value fake
dictionary. I had a quick look at how I'd enumerate all elements in a
struct/class to find a sensible answer, but I got scared by the code in
value_struct_elt and friends. Since this functionality doesn't seem to
impact basic use of gdb.Value objects, I thought I could get away for
now without implementing this... :-) Mmmm... it just occurred to me that
I should take a look at varobj code to see how it lists children
varobjs.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
:ADDPATCH python:
2008-09-12 Thiago Jung Bauermann <bauerman@br.ibm.com>
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.
Index: gdb.git/gdb/Makefile.in
===================================================================
--- gdb.git.orig/gdb/Makefile.in 2008-09-12 02:24:27.000000000 -0300
+++ gdb.git/gdb/Makefile.in 2008-09-12 02:28:11.000000000 -0300
@@ -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=
@@ -1846,6 +1848,10 @@ python-utils.o: $(srcdir)/python/python-
$(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)
+
#
# Dependency tracking. Most of this is conditional on GNU Make being
# found by configure; if GNU Make is not found, we fall back to a
Index: gdb.git/gdb/configure.ac
===================================================================
--- gdb.git.orig/gdb/configure.ac 2008-08-08 01:47:05.000000000 -0300
+++ gdb.git/gdb/configure.ac 2008-09-12 02:26:42.000000000 -0300
@@ -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)
Index: gdb.git/gdb/doc/gdb.texinfo
===================================================================
--- gdb.git.orig/gdb/doc/gdb.texinfo 2008-09-12 02:24:28.000000000 -0300
+++ gdb.git/gdb/doc/gdb.texinfo 2008-09-12 02:26:42.000000000 -0300
@@ -17631,6 +17631,7 @@ situation, a Python @code{KeyboardInterr
@menu
* Basic Python:: Basic Python Functions.
* Exception Handling::
+* Values From Inferior::
@end menu
@node Basic Python
@@ -17709,6 +17710,36 @@ message as its value, and the Python cal
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
Index: gdb.git/gdb/python/python-internal.h
===================================================================
--- gdb.git.orig/gdb/python/python-internal.h 2008-08-07 01:22:14.000000000 -0300
+++ gdb.git/gdb/python/python-internal.h 2008-09-12 02:26:42.000000000 -0300
@@ -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);
Index: gdb.git/gdb/python/python-value.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb.git/gdb/python/python-value.c 2008-09-12 02:26:42.000000000 -0300
@@ -0,0 +1,663 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#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)
+{
+ return 0; /* FIXME: dummy. */
+}
+
+/* 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)
+ {
+ /* FIXME: use current_language? */
+ 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 */
Index: gdb.git/gdb/python/python.c
===================================================================
--- gdb.git.orig/gdb/python/python.c 2008-09-12 02:24:28.000000000 -0300
+++ gdb.git/gdb/python/python.c 2008-09-12 02:26:42.000000000 -0300
@@ -52,6 +52,8 @@ static PyObject *gdbpy_flush (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,
@@ -398,6 +400,8 @@ Enables or disables printing of Python s
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
Index: gdb.git/gdb/python/python.h
===================================================================
--- gdb.git.orig/gdb/python/python.h 2008-08-07 01:22:14.000000000 -0300
+++ gdb.git/gdb/python/python.h 2008-09-12 02:26:42.000000000 -0300
@@ -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 */
Index: gdb.git/gdb/testsuite/gdb.python/python-values.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb.git/gdb/testsuite/gdb.python/python-values.c 2008-09-12 02:26:42.000000000 -0300
@@ -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 <http://www.gnu.org/licenses/>. */
+
+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 */
+}
Index: gdb.git/gdb/testsuite/gdb.python/python-values.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb.git/gdb/testsuite/gdb.python/python-values.exp 2008-09-12 02:26:42.000000000 -0300
@@ -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 <http://www.gnu.org/licenses/>.
+
+# 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__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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
Index: gdb.git/gdb/value.c
===================================================================
--- gdb.git.orig/gdb/value.c 2008-09-12 02:24:28.000000000 -0300
+++ gdb.git/gdb/value.c 2008-09-12 02:26:42.000000000 -0300
@@ -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
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);
}
Index: gdb.git/gdb/value.h
===================================================================
--- gdb.git.orig/gdb/value.h 2008-09-12 02:24:28.000000000 -0300
+++ gdb.git/gdb/value.h 2008-09-12 02:26:42.000000000 -0300
@@ -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 *);
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [rfc] expose gdb values to python 2008-09-12 6:05 [rfc] expose gdb values to python Thiago Jung Bauermann @ 2008-09-20 21:29 ` Tom Tromey 2008-09-21 4:27 ` Daniel Jacobowitz ` (2 more replies) 0 siblings, 3 replies; 28+ messages in thread From: Tom Tromey @ 2008-09-20 21:29 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches ml >>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes: Thiago> This is the patch exposing struct value to Python. Thank you so much, not just for this patch, but for all your work separating the mess into patches. Thiago> I believe it's better to avoid using current_language, right? Thiago> I don't think there's a way to get a sensible language_defn to Thiago> use here, so my only idea is to add an element to struct value Thiago> which holds the language associated with the value. This Thiago> element would be filled at the moment the value is created. My first reaction to this was "no way". But, I couldn't think of a concrete case where this would have bad results -- especially provided we restrict use of the language field to stringifying the value. Thiago> The other FIXME is for the bogus implementation of Thiago> valpy_length, which is supposed to return the number of Thiago> elements in the gdb.Value fake dictionary. I had a quick look Thiago> at how I'd enumerate all elements in a struct/class to find a Thiago> sensible answer, but I got scared by the code in Thiago> value_struct_elt and friends. I think there are a few ways to approach this. The underlying question is what type model Value presents. If a Value has a derived type, should we be able to access fields of the base class using v["f"]? Or should we need v["Base"]["f"]? There are some tradeoffs here. The "invisible access" approach is convenient. However, it runs into issues with odd programs -- say, multiple inheritance where a given name refers to multiple fields. The "raw" approach doesn't have this problem (and here, valpy_length is easy to write), but it is more cumbersome to use. There are some intermediate ideas, too, like allowing the invisible approach only when the field name is unique; or we could define the search order. (It is tempting to use the language's rules, but I suspect this might be too tricky to get right.) I tend to like something toward the raw side, partly because any cooked approach will still need some second way to deal with the underlying explicit types. I propose we decide these questions and implement this before checking in this patch. The semantics of Value are critical. Tom ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-20 21:29 ` Tom Tromey @ 2008-09-21 4:27 ` Daniel Jacobowitz 2008-09-25 4:33 ` Thiago Jung Bauermann 2008-09-25 4:49 ` Thiago Jung Bauermann 2008-10-01 5:48 ` Joel Brobecker 2 siblings, 1 reply; 28+ messages in thread From: Daniel Jacobowitz @ 2008-09-21 4:27 UTC (permalink / raw) To: Tom Tromey; +Cc: Thiago Jung Bauermann, gdb-patches ml On Fri, Sep 19, 2008 at 04:39:12PM -0600, Tom Tromey wrote: > Thiago> I believe it's better to avoid using current_language, right? > Thiago> I don't think there's a way to get a sensible language_defn to > Thiago> use here, so my only idea is to add an element to struct value > Thiago> which holds the language associated with the value. This > Thiago> element would be filled at the moment the value is created. > > My first reaction to this was "no way". But, I couldn't think of a > concrete case where this would have bad results -- especially provided > we restrict use of the language field to stringifying the value. This seems iffy. A value's just a value - how it's printed depends on how it's used, not how it was created. e.g. if two languages had different number formatting, "print $1" should generate different results based on the current language. So what the right language is may depend on the context. > There are some intermediate ideas, too, like allowing the invisible > approach only when the field name is unique; or we could define the > search order. (It is tempting to use the language's rules, but I > suspect this might be too tricky to get right.) Why isn't this the same as for expression evaluation in GDB today? That does follow the language rules (and fail, in some cases). I'm not sure we really need length to work, but I haven't spent much time looking at it. If we don't, I'd rather it failed always than intermittently. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-21 4:27 ` Daniel Jacobowitz @ 2008-09-25 4:33 ` Thiago Jung Bauermann 2008-09-25 11:47 ` Daniel Jacobowitz 0 siblings, 1 reply; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-09-25 4:33 UTC (permalink / raw) To: gdb-patches Daniel Jacobowitz wrote: > On Fri, Sep 19, 2008 at 04:39:12PM -0600, Tom Tromey wrote: >> Thiago> I believe it's better to avoid using current_language, right? >> Thiago> I don't think there's a way to get a sensible language_defn to >> Thiago> use here, so my only idea is to add an element to struct value >> Thiago> which holds the language associated with the value. This >> Thiago> element would be filled at the moment the value is created. >> >> My first reaction to this was "no way". Well, thanks for the sincerity I suppose. :-) >> But, I couldn't think of a >> concrete case where this would have bad results -- especially provided >> we restrict use of the language field to stringifying the value. > > This seems iffy. A value's just a value - how it's printed depends on > how it's used, not how it was created. e.g. if two languages had > different number formatting, "print $1" should generate different > results based on the current language. > > So what the right language is may depend on the context. My reasoning was that if a value comes from a C context (for example), at least at first I'd expect it to always be printed in C syntax. But I see your point. And I have no preference, really. So leaving current_language in valpy_str is acceptable? Then one FIXME can be just dropped. >> There are some intermediate ideas, too, like allowing the invisible >> approach only when the field name is unique; or we could define the >> search order. (It is tempting to use the language's rules, but I >> suspect this might be too tricky to get right.) > > Why isn't this the same as for expression evaluation in GDB today? > That does follow the language rules (and fail, in some cases). From what I understood of the expression evaluator, this means just calling value_struct_elt to find the element. If that's the case, it's what this patch implements. Is it useful to provide a casting mechanism, to enable for instance access to elements from a specific type in the inheritance hierarchy? With that, a python script will have the same capability as the user at the GDB prompt to access any struct/class element, right? But I'd leave type casting to the (still to be written) patch exposing the type system to python. > I'm not sure we really need length to work, but I haven't spent much > time looking at it. If we don't, I'd rather it failed always than > intermittently. A function which always fails? I can write that. :-) -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-25 4:33 ` Thiago Jung Bauermann @ 2008-09-25 11:47 ` Daniel Jacobowitz 2008-09-26 2:00 ` Thiago Jung Bauermann 0 siblings, 1 reply; 28+ messages in thread From: Daniel Jacobowitz @ 2008-09-25 11:47 UTC (permalink / raw) To: gdb-patches On Thu, Sep 25, 2008 at 01:32:57AM -0300, Thiago Jung Bauermann wrote: > My reasoning was that if a value comes from a C context (for example), > at least at first I'd expect it to always be printed in C syntax. But > I see your point. And I have no preference, really. > > So leaving current_language in valpy_str is acceptable? Then one FIXME > can be just dropped. IMO, yes - or else we can pass it the language somehow, later. > Is it useful to provide a casting mechanism, to enable for instance access > to elements from a specific type in the inheritance hierarchy? I think so. This reminds me of another reason why length is a peculiar concept here: GDB should (though I think does not, at present) support "v->D::x" and "v->C::x". So v['C::x'] would make sense, but you can't iterate over things like that. So maybe length should be the number of direct fields? Or the number of fields + base classes? Either way, I suggest it match what iterators do if you support iteration. > But I'd leave type casting to the (still to be written) patch exposing the > type system to python. Sure. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-25 11:47 ` Daniel Jacobowitz @ 2008-09-26 2:00 ` Thiago Jung Bauermann 2008-09-26 9:30 ` Eli Zaretskii ` (2 more replies) 0 siblings, 3 replies; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-09-26 2:00 UTC (permalink / raw) To: gdb-patches [-- Attachment #1: Type: text/plain, Size: 931 bytes --] Daniel Jacobowitz wrote: > On Thu, Sep 25, 2008 at 01:32:57AM -0300, Thiago Jung Bauermann wrote: >> Is it useful to provide a casting mechanism, to enable for instance >> access to elements from a specific type in the inheritance hierarchy? > > I think so. This reminds me of another reason why length is a > peculiar concept here: GDB should (though I think does not, at > present) support "v->D::x" and "v->C::x". So v['C::x'] would make > sense, but you can't iterate over things like that. So maybe length > should be the number of direct fields? Or the number of fields + base > classes? Either way, I suggest it match what iterators do if you > support iteration. There's no iterator support. Not yet at least. This version has no FIXMEs. It uses current_language for value printing, and its length operation always fails with NotImplemented. Ok to commit? -- []'s Thiago Jung Bauermann IBM Linux Technology Center [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: python-values-2008-09-25.diff --] [-- Type: text/x-diff; name="python-values-2008-09-25.diff", Size: 41510 bytes --] 2008-09-25 Thiago Jung Bauermann <bauerman@br.ibm.com> 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) + \f # 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 <http://www.gnu.org/licenses/>. */ + +#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 <http://www.gnu.org/licenses/>. */ + +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 <http://www.gnu.org/licenses/>. + +# 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__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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 *); ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-26 2:00 ` Thiago Jung Bauermann @ 2008-09-26 9:30 ` Eli Zaretskii 2008-09-28 1:19 ` Thiago Jung Bauermann 2008-09-26 20:57 ` Daniel Jacobowitz 2008-10-01 5:39 ` Joel Brobecker 2 siblings, 1 reply; 28+ messages in thread From: Eli Zaretskii @ 2008-09-26 9:30 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches > From: Thiago Jung Bauermann <bauerman@br.ibm.com> > Date: Thu, 25 Sep 2008 22:59:26 -0300 > > gdb/doc/ > * gdb.texinfo. (Values From Inferior): New subsubsection. This part is approved with a few comments, see below. > +@node Values From Inferior > +@subsubsection Values From Inferior Please add some @cindex entry/entries here, which would be useful for someone who will look for this stuff via the index. > +@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. My assumption in reviewing Python-related manual sections is that the reader will generally _not_ be a proficient Python programmer, but rather someone who programs in another language, like C++ or Ada, and whose use of Python in GDB is just so to get away with whatever scripting the reader needs to craft for her current needs. With that in mind, I think we should have as much examples and specific concrete information in these sections as possible, to make it easier for the reader to understand how to use these features without too many detours into Python manuals. Abstract information, such as references to Python types, will not alone reach that goal. For example, in the above paragraph, would it help to describe the specific elements and methods of `gdb.Value', or give a couple of examples of their use in real life? You mention the type and address of the inferior's value, but are there other useful members that the reader would want to know about? And even for these 2, what kind of data types are they? > +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 Again, a practical example of using this would be good to have. Thanks. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-26 9:30 ` Eli Zaretskii @ 2008-09-28 1:19 ` Thiago Jung Bauermann 2008-09-28 18:19 ` Eli Zaretskii 0 siblings, 1 reply; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-09-28 1:19 UTC (permalink / raw) To: gdb-patches [-- Attachment #1: Type: text/plain, Size: 3524 bytes --] Eli Zaretskii wrote: >> +@node Values From Inferior >> +@subsubsection Values From Inferior > > Please add some @cindex entry/entries here, which would be useful for > someone who will look for this stuff via the index. Right. Added a couple. >> +@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. > > My assumption in reviewing Python-related manual sections is that the > reader will generally _not_ be a proficient Python programmer, but > rather someone who programs in another language, like C++ or Ada, and > whose use of Python in GDB is just so to get away with whatever > scripting the reader needs to craft for her current needs. Agreed. It's a useful assumption. > With that in mind, I think we should have as much examples and > specific concrete information in these sections as possible, to make > it easier for the reader to understand how to use these features > without too many detours into Python manuals. Abstract information, > such as references to Python types, will not alone reach that goal. I mostly agree, but in some cases I believe examples are not necessary because IMHO some basic programming skills can be assumed, and a Python integer is the same as an integer in any other language (I believe you are referencing to my Python integer example). Examples for such cases will most likely be obvious code snippets. In any case, I added an example for that. If you think it's helpful, it can stay. > For example, in the above paragraph, would it help to describe the > specific elements and methods of `gdb.Value', or give a couple of > examples of their use in real life? You mention the type and address > of the inferior's value, but are there other useful members that the > reader would want to know about? And even for these 2, what kind of > data types are they? The objective of that paragraph was not to describe features of gdb.Value available for direct use, but rather to give to the user a rationale of the existence of the gdb.Value object (as opposed to having GDB return native Python values). There's not much to describe regarding specific elements and methods of gdb.Value since it has no element, and just one method which I explicitly list and describe. From your comment above, I see that my text was not clear so I tried to rephrase it. What do you think of this version? >> +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 > > Again, a practical example of using this would be good to have. I didn't provide an example originally because I thought that it was clear enough from the description what the method does and how to use it. I'm providing an example in this version, even though I think it's kind of redundant. If you think it helps, it can stay. This patch also fixes the comments from Daniel. Unfortunately, I just found out that it won't compile with current CVS since it uses some builtin_type_* variables which don't exist anymore. :-( I post it as it is to get the documentation part reviewed, and will work to fix this problem... -- []'s Thiago Jung Bauermann IBM Linux Technology Center [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: python-value-2008-09-27.diff --] [-- Type: text/x-diff; name="python-value-2008-09-27.diff", Size: 43053 bytes --] 2008-09-27 Thiago Jung Bauermann <bauerman@br.ibm.com> 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 * configure: Regenerate. * 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-value.c: New file. * gdb.python/python-value.exp: New file. Index: gdb.git/gdb/Makefile.in =================================================================== --- gdb.git.orig/gdb/Makefile.in 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/Makefile.in 2008-09-27 21:28:44.000000000 -0300 @@ -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= @@ -1849,6 +1851,10 @@ python-utils.o: $(srcdir)/python/python- $(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) + # # Dependency tracking. Most of this is conditional on GNU Make being # found by configure; if GNU Make is not found, we fall back to a Index: gdb.git/gdb/configure =================================================================== --- gdb.git.orig/gdb/configure 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/configure 2008-09-27 21:26:28.000000000 -0300 @@ -11704,10 +11704,10 @@ rm -f conftest.err conftest.$ac_objext c echo "${ECHO_T}${PYTHON_CFLAGS}" >&6 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 Index: gdb.git/gdb/configure.ac =================================================================== --- gdb.git.orig/gdb/configure.ac 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/configure.ac 2008-09-27 21:26:28.000000000 -0300 @@ -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) Index: gdb.git/gdb/doc/gdb.texinfo =================================================================== --- gdb.git.orig/gdb/doc/gdb.texinfo 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/doc/gdb.texinfo 2008-09-27 21:26:28.000000000 -0300 @@ -17674,6 +17674,7 @@ situation, a Python @code{KeyboardInterr @menu * Basic Python:: Basic Python Functions. * Exception Handling:: +* Values From Inferior:: @end menu @node Basic Python @@ -17752,6 +17753,61 @@ message as its value, and the Python cal Python statement closest to where the @value{GDBN} error occured as the traceback. +@node Values From Inferior +@subsubsection Values From Inferior +@cindex values from inferior +@cindex python, working with values from inferior in + +@cindex @code{gdb.Value} +@value{GDBN} provides values it obtains from the inferior program in an +object of type @code{gdb.Value}. This object is used so that @value{GDBN} +can keep track of information related to the value such as its type and +location in the inferior, and also to enable @value{GDBN} to fetch its +contents from the inferior only when necessary. + +You don't need to be aware of such ancillary information in order to use +@code{gdb.Value} objects. You can directly use them 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 was a Python +integer variable. For example, if @code{some_val} is a @code{gdb.Value} +instance holding an integer (or even a floating point value), the following +usage is valid: + +@smallexample +bar = some_val + 2 +@end smallexample + +@code{bar} will also be a @code{gdb.Value} object. + +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 + +Again, @code{bar} will also be a @code{gdb.Value} object. + +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. For example, if +@code{some_val} is a @code{gdb.Value} instance holding a pointer to an +integer (in C, that would be @code{int *}), the statement + +@smallexample +bar = some_val.dereference () +@end smallexample + +will make @code{bar} a @code{gdb.Value} object holding the integer pointed +to by @code{some_val}. @code{bar} will be of integer type (in C, that +would be @code{int}). +@end defmethod + @node Interpreters @chapter Command Interpreters @cindex command interpreters Index: gdb.git/gdb/python/python-internal.h =================================================================== --- gdb.git.orig/gdb/python/python-internal.h 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/python/python-internal.h 2008-09-27 21:26:28.000000000 -0300 @@ -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); Index: gdb.git/gdb/python/python-value.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb.git/gdb/python/python-value.c 2008-09-27 21:26:28.000000000 -0300 @@ -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 <http://www.gnu.org/licenses/>. */ + +#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) || TYPE_CODE (type) == TYPE_CODE_PTR) + 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 */ Index: gdb.git/gdb/python/python.c =================================================================== --- gdb.git.orig/gdb/python/python.c 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/python/python.c 2008-09-27 21:26:28.000000000 -0300 @@ -52,6 +52,8 @@ static PyObject *gdbpy_flush (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, @@ -398,6 +400,8 @@ Enables or disables printing of Python s 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 Index: gdb.git/gdb/python/python.h =================================================================== --- gdb.git.orig/gdb/python/python.h 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/python/python.h 2008-09-27 21:26:28.000000000 -0300 @@ -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 */ Index: gdb.git/gdb/testsuite/gdb.python/python-value.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb.git/gdb/testsuite/gdb.python/python-value.c 2008-09-27 21:26:28.000000000 -0300 @@ -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 <http://www.gnu.org/licenses/>. */ + +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 */ +} Index: gdb.git/gdb/testsuite/gdb.python/python-value.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb.git/gdb/testsuite/gdb.python/python-value.exp 2008-09-27 21:26:28.000000000 -0300 @@ -0,0 +1,236 @@ +# 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 <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing values to Python. + +if $tracelevel then { + strace $tracelevel +} + +set testfile "python-value" +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 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 } } + } +} + +proc test_value_creation {} { + global gdb_prompt + + gdb_py_test_silent_cmd "python i = gdb.Value (True)" "create boolean value" 1 + gdb_py_test_silent_cmd "python i = gdb.Value (5)" "create integer value" 1 + gdb_py_test_silent_cmd "python i = gdb.Value (5L)" "create long value" 1 + gdb_py_test_silent_cmd "python f = gdb.Value (1.25)" "create double value" 1 + gdb_py_test_silent_cmd "python a = gdb.Value ('string test')" "create 8-bit string value" 1 + gdb_test "python print a" "\"string test\"" "print 8-bit string" + gdb_test "python print a.__class__" "<type 'gdb.Value'>" "verify type of 8-bit string" + gdb_py_test_silent_cmd "python a = gdb.Value (u'unicode test')" "create unicode value" 1 + gdb_test "python print a" "\"unicode test\"" "print Unicode string" + gdb_test "python print a.__class__" "<type 'gdb.Value'>" "verify type of unicode string" +} + +proc test_value_numeric_ops {} { + global gdb_prompt + + gdb_py_test_silent_cmd "python i = gdb.Value (5)" "create first integer value" 0 + gdb_py_test_silent_cmd "python j = gdb.Value (2)" "create second integer value" 0 + gdb_py_test_silent_cmd "python f = gdb.Value (1.25)" "create first double value" 0 + gdb_py_test_silent_cmd "python g = gdb.Value (2.5)" "create second double value" 0 + gdb_test "python print 'result = ' + str(i+j)" " = 7" "add two integer values" + gdb_test "python print (i+j).__class__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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_py_test_silent_cmd "python s = gdb.get_value_from_history (0)" "get value from history" 1 + + 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_py_test_silent_cmd "python argv = gdb.get_value_from_history (0)" "" 0 + gdb_py_test_silent_cmd "python arg0 = argv.dereference ()" "dereference value" 1 + + # 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 Index: gdb.git/gdb/value.c =================================================================== --- gdb.git.orig/gdb/value.c 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/value.c 2008-09-27 21:26:28.000000000 -0300 @@ -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 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); } Index: gdb.git/gdb/value.h =================================================================== --- gdb.git.orig/gdb/value.h 2008-09-27 21:26:26.000000000 -0300 +++ gdb.git/gdb/value.h 2008-09-27 21:26:28.000000000 -0300 @@ -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 *); ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-28 1:19 ` Thiago Jung Bauermann @ 2008-09-28 18:19 ` Eli Zaretskii 2008-09-29 16:16 ` Thiago Jung Bauermann 0 siblings, 1 reply; 28+ messages in thread From: Eli Zaretskii @ 2008-09-28 18:19 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches > From: Thiago Jung Bauermann <bauerman@br.ibm.com> > Date: Sat, 27 Sep 2008 22:18:15 -0300 > > > With that in mind, I think we should have as much examples and > > specific concrete information in these sections as possible, to make > > it easier for the reader to understand how to use these features > > without too many detours into Python manuals. Abstract information, > > such as references to Python types, will not alone reach that goal. > > I mostly agree, but in some cases I believe examples are not necessary > because IMHO some basic programming skills can be assumed, and a Python > integer is the same as an integer in any other language (I believe you are > referencing to my Python integer example). Examples for such cases will > most likely be obvious code snippets. > > In any case, I added an example for that. If you think it's helpful, it can > stay. > > > For example, in the above paragraph, would it help to describe the > > specific elements and methods of `gdb.Value', or give a couple of > > examples of their use in real life? You mention the type and address > > of the inferior's value, but are there other useful members that the > > reader would want to know about? And even for these 2, what kind of > > data types are they? > > The objective of that paragraph was not to describe features of gdb.Value > available for direct use, but rather to give to the user a rationale of the > existence of the gdb.Value object (as opposed to having GDB return native > Python values). There's not much to describe regarding specific elements > and methods of gdb.Value since it has no element, and just one method which > I explicitly list and describe. > > >From your comment above, I see that my text was not clear so I tried to > rephrase it. What do you think of this version? > > >> +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 > > > > Again, a practical example of using this would be good to have. > > I didn't provide an example originally because I thought that it was clear > enough from the description what the method does and how to use it. I'm > providing an example in this version, even though I think it's kind of > redundant. If you think it helps, it can stay. Thanks for working on this, the new version is an improvement. Rather than responding with another set of comments, I decided to rephrase your text, interspersed with comments about noteworthy issues. > +@node Values From Inferior > +@subsubsection Values From Inferior > +@cindex values from inferior @cindex values from inferior, with Python (Without the Python qualifier, this index entry is too general.) > +@cindex python, working with values from inferior in @cindex python, working with values from inferior ("in" is redundant.) > +@value{GDBN} provides values it obtains from the inferior program in an > +object of type @code{gdb.Value}. This object is used so that @value{GDBN} > +can keep track of information related to the value such as its type and > +location in the inferior, and also to enable @value{GDBN} to fetch its > +contents from the inferior only when necessary. @value{GDBN} provides values it obtains from the inferior program in an object of type @code{gdb.Value}. @value{GDBN} uses this object for its internal bookkeeping of the inferior's values, and for fetching values when necessary. (this avoids using passive voice -- "object is used ..." -- and also makes details of no user importance opaque, to avoid questions that have no useful answers.) > +You don't need to be aware of such ancillary information in order to use > +@code{gdb.Value} objects. You can directly use them 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 was a Python > +integer variable. For example, if @code{some_val} is a @code{gdb.Value} > +instance holding an integer (or even a floating point value), the following > +usage is valid: Inferior values that are simple scalars can be used directly in Python expressions that are valid for the value's data type Here's an example for an integer or floating-point value @code{some_val}: (point out explicitly that this describes how to work with scalar values.) > +@smallexample > +bar = some_val + 2 > +@end smallexample > + > +@code{bar} will also be a @code{gdb.Value} object. @noindent As result of this, @code{bar} will also be a @code{gdb.Value} object whose values are of the same type as those of @code{some_val}. > +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. Inferior values that are structures or instances of some class can be accessed using the Python @dfn{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 > + > +Again, @code{bar} will also be a @code{gdb.Value} object. This is okay. > +The following method is provided: For pointer data types, @code{gdb.Value} provides a method for dereferencing the pointer to obtain the object it points to. (avoid passive tense again, and make it clear how this is connected to the rest of the section.) > +@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. For example, if > +@code{some_val} is a @code{gdb.Value} instance holding a pointer to an > +integer (in C, that would be @code{int *}), the statement > + > +@smallexample > +bar = some_val.dereference () > +@end smallexample > + > +will make @code{bar} a @code{gdb.Value} object holding the integer pointed > +to by @code{some_val}. @code{bar} will be of integer type (in C, that > +would be @code{int}). > +@end defmethod @defmethod Value dereference This method returns a new @code{gdb.Value} object whose contents is the object pointed to by the pointer. For example, if @code{foo} is a C pointer to an @code{int}, declared in your C program as @smallexample int *foo; @end smallexample @noindent then you can use the corresponding @code{gdb.Value} to access what @code{foo} points too like this: @smallexample bar = foo.dereference () @end smallexample The result @code{bar} will be a @code{gdb.Value} object holding the value pointed to by @code{foo}. Note how the slightly modified text imposes a more clear structure on the description: it describes 3 possible categories of data types: scalars, structs/classes, and pointers, and gives useful information on how to handle each one of them. Btw, now that I've written this, I see that this begs a few questions about other kinds of data types: arrays, strings, and C/C++ unions, just for starters. Would it be useful to cover them as well? Or maybe they are already covered by one of the above, in which case the text should mention them as well. Also, I don't see where we explain how to come by the gdb.Value object in the first place. For example, given a variable `foo', how do I create a gdb.Value object for it? Do I just use `foo'? i.e., is the example above (that says "foo.dereference ()" for `foo' that is a C pointer) valid? This is also something non-trivial that should be explained. Thanks. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-28 18:19 ` Eli Zaretskii @ 2008-09-29 16:16 ` Thiago Jung Bauermann 2008-09-29 17:00 ` Daniel Jacobowitz 2008-09-29 18:52 ` Eli Zaretskii 0 siblings, 2 replies; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-09-29 16:16 UTC (permalink / raw) To: Eli Zaretskii; +Cc: gdb-patches, Daniel Jacobowitz El dom, 28-09-2008 a las 21:18 +0300, Eli Zaretskii escribió: > Thanks for working on this, the new version is an improvement. Rather > than responding with another set of comments, I decided to rephrase > your text, interspersed with comments about noteworthy issues. Thanks for your version! It is certainly better than mine. I adopted all your changes. Thanks as well for your tips. I'll try to follow them next time I write a doc patch. > > +The following method is provided: > > For pointer data types, @code{gdb.Value} provides a method for > dereferencing the pointer to obtain the object it points to. > > (avoid passive tense again, and make it clear how this is connected to > the rest of the section.) In future patches, more methods will probably be added to gdb.Value, so we may (or may not) return to the "list of methods" format. But I am currently adopting your version. > Btw, now that I've written this, I see that this begs a few questions > about other kinds of data types: arrays, strings, and C/C++ unions, > just for starters. Would it be useful to cover them as well? Or > maybe they are already covered by one of the above, in which case the > text should mention them as well. Good observation. Unions work just like structs, so they are covered. Arrays and strings are not implemented yet. I will cover them in a future patch. > Also, I don't see where we explain how to come by the gdb.Value object > in the first place. For example, given a variable `foo', how do I > create a gdb.Value object for it? Do I just use `foo'? i.e., is the > example above (that says "foo.dereference ()" for `foo' that is a C > pointer) valid? This is also something non-trivial that should be > explained. Good observation, again. :-) This patch is not an end on itself, it is more of an infrastructure for other python patches. The user won't want to directly produce a gdb.Value for its own sake. He'll end up with one when trying to accomplish other things. For instance, if the user wants GDB to evaluate a C expression and get the result, GDB will return a gdb.Value object. Or when implementing a pretty-printer in Python, he'll be given a gdb.Value to examine and pretty-print. Or if he is examining a stack frame and requests the value of a register or a local variable, he'll be given a gdb.Value. That's talking about features which are not in CVS yet. For now, the only way to get a gdb.Value is using gdb.get_value_from_history. So, with your example you could do: (gdb) print foo $3 = 2 (gdb) python foo = gdb.get_value_from_history (3) I am attaching the latest version of this patch, which incorporates all documentation and code review comments, and is adapted to yesterday's CVS HEAD version. It applies cleanly, passes all included tests and introduces no regression. Tested with x86_64-linux. Daniel, There are two differences from what you reviewed: I had to change python-value.c to use only arch-independent builtin_type_*. And I had to use value_binop instead of value_add and value_sub. This made gdb.Value unable to do pointer math. I'll post a follow up patch explicitly adding pointer math. Is this ok? -- []'s Thiago Jung Bauermann IBM Linux Technology Center 2008-09-28 Thiago Jung Bauermann <bauerman@br.ibm.com> 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 * configure: Regenerate. * 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-value.c: New file. * gdb.python/python-value.exp: New file. Index: gdb.git/gdb/Makefile.in =================================================================== --- gdb.git.orig/gdb/Makefile.in 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/Makefile.in 2008-09-28 23:18:56.000000000 -0300 @@ -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= @@ -1849,6 +1851,10 @@ python-utils.o: $(srcdir)/python/python- $(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) + # # Dependency tracking. Most of this is conditional on GNU Make being # found by configure; if GNU Make is not found, we fall back to a Index: gdb.git/gdb/configure =================================================================== --- gdb.git.orig/gdb/configure 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/configure 2008-09-28 23:18:56.000000000 -0300 @@ -11704,10 +11704,10 @@ rm -f conftest.err conftest.$ac_objext c echo "${ECHO_T}${PYTHON_CFLAGS}" >&6 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 Index: gdb.git/gdb/configure.ac =================================================================== --- gdb.git.orig/gdb/configure.ac 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/configure.ac 2008-09-28 23:18:56.000000000 -0300 @@ -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) Index: gdb.git/gdb/doc/gdb.texinfo =================================================================== --- gdb.git.orig/gdb/doc/gdb.texinfo 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/doc/gdb.texinfo 2008-09-28 23:19:46.000000000 -0300 @@ -17674,6 +17674,7 @@ situation, a Python @code{KeyboardInterr @menu * Basic Python:: Basic Python Functions. * Exception Handling:: +* Values From Inferior:: @end menu @node Basic Python @@ -17752,6 +17753,64 @@ message as its value, and the Python cal Python statement closest to where the @value{GDBN} error occured as the traceback. +@node Values From Inferior +@subsubsection Values From Inferior +@cindex values from inferior, with Python +@cindex python, working with values from inferior + +@cindex @code{gdb.Value} +@value{GDBN} provides values it obtains from the inferior program in +an object of type @code{gdb.Value}. @value{GDBN} uses this object +for its internal bookkeeping of the inferior's values, and for +fetching values when necessary. + +Inferior values that are simple scalars can be used directly in +Python expressions that are valid for the value's data type Here's +an example for an integer or floating-point value @code{some_val}: + +@smallexample +bar = some_val + 2 +@end smallexample + +@noindent +As result of this, @code{bar} will also be a @code{gdb.Value} object +whose values are of the same type as those of @code{some_val}. + +Inferior values that are structures or instances of some class can +be accessed using the Python @dfn{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 + +Again, @code{bar} will also be a @code{gdb.Value} object. + +For pointer data types, @code{gdb.Value} provides a method for +dereferencing the pointer to obtain the object it points to. + +@defmethod Value dereference +This method returns a new @code{gdb.Value} object whose contents is +the object pointed to by the pointer. For example, if @code{foo} is +a C pointer to an @code{int}, declared in your C program as + +@smallexample +int *foo; +@end smallexample + +@noindent +then you can use the corresponding @code{gdb.Value} to access what +@code{foo} points to like this: + +@smallexample +bar = foo.dereference () +@end smallexample + +The result @code{bar} will be a @code{gdb.Value} object holding the +value pointed to by @code{foo}. +@end defmethod + @node Interpreters @chapter Command Interpreters @cindex command interpreters Index: gdb.git/gdb/python/python-internal.h =================================================================== --- gdb.git.orig/gdb/python/python-internal.h 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/python/python-internal.h 2008-09-28 23:18:56.000000000 -0300 @@ -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); Index: gdb.git/gdb/python/python-value.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb.git/gdb/python/python-value.c 2008-09-28 23:18:56.000000000 -0300 @@ -0,0 +1,684 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#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" + +/* Python's integer type corresponds to native C's long type. */ +struct type *builtin_type_pyint; + +/* Python's float type corresponds to native C's double type (which is + assumed to use IEEE double format). */ +#define builtin_type_pyfloat builtin_type_ieee_double + +/* Python's long type corresponds to native C's long long type (which is + assumed to be int64_t). */ +#define builtin_type_pylong builtin_type_int64 + +/* The current language may not have a boolean type, so always use an + integer as boolean type. Hopefully any language can deal with integers + as boolean values. */ +#define builtin_type_pybool builtin_type_int32 + +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_pyint, l); + } + else if (PyFloat_Check (other)) + { + d = PyFloat_AsDouble (other); + if (PyErr_Occurred ()) + return Py_NotImplemented; + + other_val = value_from_double (builtin_type_pyfloat, 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_binop (self_value->value, other_val, BINOP_ADD); + break; + case VALPY_SUB: + res_val = value_binop (self_value->value, other_val, BINOP_SUB); + 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_int8, 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) || TYPE_CODE (type) == TYPE_CODE_PTR) + 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_pyint, l); + } + else if (PyFloat_Check (other)) + { + DOUBLEST d; + + d = PyFloat_AsDouble (other); + if (PyErr_Occurred ()) + return NULL; + + value_other = value_from_double (builtin_type_pyfloat, 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_pybool, obj == Py_True); + else if (PyInt_Check (obj)) + value = value_from_longest (builtin_type_pyint, PyInt_AsLong (obj)); + else if (PyLong_Check (obj)) + { + LONGEST l = PyLong_AsLongLong (obj); + if (! PyErr_Occurred ()) + value = value_from_longest (builtin_type_pylong, l); + } + else if (PyFloat_Check (obj)) + { + double d = PyFloat_AsDouble (obj); + if (! PyErr_Occurred ()) + value = value_from_double (builtin_type_pyfloat, 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) +{ + builtin_type_pyint = init_type (TYPE_CODE_INT, sizeof (long), 0, "long", + (struct objfile *) NULL); + + 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 */ Index: gdb.git/gdb/python/python.c =================================================================== --- gdb.git.orig/gdb/python/python.c 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/python/python.c 2008-09-28 23:18:56.000000000 -0300 @@ -52,6 +52,8 @@ static PyObject *gdbpy_flush (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, @@ -398,6 +400,8 @@ Enables or disables printing of Python s 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 Index: gdb.git/gdb/python/python.h =================================================================== --- gdb.git.orig/gdb/python/python.h 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/python/python.h 2008-09-28 23:18:56.000000000 -0300 @@ -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 */ Index: gdb.git/gdb/testsuite/gdb.python/python-value.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb.git/gdb/testsuite/gdb.python/python-value.c 2008-09-28 23:18:56.000000000 -0300 @@ -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 <http://www.gnu.org/licenses/>. */ + +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 */ +} Index: gdb.git/gdb/testsuite/gdb.python/python-value.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb.git/gdb/testsuite/gdb.python/python-value.exp 2008-09-28 23:18:56.000000000 -0300 @@ -0,0 +1,236 @@ +# 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 <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing values to Python. + +if $tracelevel then { + strace $tracelevel +} + +set testfile "python-value" +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 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 } } + } +} + +proc test_value_creation {} { + global gdb_prompt + + gdb_py_test_silent_cmd "python i = gdb.Value (True)" "create boolean value" 1 + gdb_py_test_silent_cmd "python i = gdb.Value (5)" "create integer value" 1 + gdb_py_test_silent_cmd "python i = gdb.Value (5L)" "create long value" 1 + gdb_py_test_silent_cmd "python f = gdb.Value (1.25)" "create double value" 1 + gdb_py_test_silent_cmd "python a = gdb.Value ('string test')" "create 8-bit string value" 1 + gdb_test "python print a" "\"string test\"" "print 8-bit string" + gdb_test "python print a.__class__" "<type 'gdb.Value'>" "verify type of 8-bit string" + gdb_py_test_silent_cmd "python a = gdb.Value (u'unicode test')" "create unicode value" 1 + gdb_test "python print a" "\"unicode test\"" "print Unicode string" + gdb_test "python print a.__class__" "<type 'gdb.Value'>" "verify type of unicode string" +} + +proc test_value_numeric_ops {} { + global gdb_prompt + + gdb_py_test_silent_cmd "python i = gdb.Value (5)" "create first integer value" 0 + gdb_py_test_silent_cmd "python j = gdb.Value (2)" "create second integer value" 0 + gdb_py_test_silent_cmd "python f = gdb.Value (1.25)" "create first double value" 0 + gdb_py_test_silent_cmd "python g = gdb.Value (2.5)" "create second double value" 0 + gdb_test "python print 'result = ' + str(i+j)" " = 7" "add two integer values" + gdb_test "python print (i+j).__class__" "<type 'gdb.Value'>" "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__" "<type 'gdb.Value'>" "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_py_test_silent_cmd "python s = gdb.get_value_from_history (0)" "get value from history" 1 + + 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_py_test_silent_cmd "python argv = gdb.get_value_from_history (0)" "" 0 + gdb_py_test_silent_cmd "python arg0 = argv.dereference ()" "dereference value" 1 + + # 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 Index: gdb.git/gdb/value.c =================================================================== --- gdb.git.orig/gdb/value.c 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/value.c 2008-09-28 23:18:56.000000000 -0300 @@ -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 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); } Index: gdb.git/gdb/value.h =================================================================== --- gdb.git.orig/gdb/value.h 2008-09-28 23:17:19.000000000 -0300 +++ gdb.git/gdb/value.h 2008-09-28 23:18:56.000000000 -0300 @@ -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 *); ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-29 16:16 ` Thiago Jung Bauermann @ 2008-09-29 17:00 ` Daniel Jacobowitz 2008-09-30 4:07 ` Thiago Jung Bauermann 2008-09-29 18:52 ` Eli Zaretskii 1 sibling, 1 reply; 28+ messages in thread From: Daniel Jacobowitz @ 2008-09-29 17:00 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: Eli Zaretskii, gdb-patches On Mon, Sep 29, 2008 at 01:15:05PM -0300, Thiago Jung Bauermann wrote: > +/* Python's float type corresponds to native C's double type (which is > + assumed to use IEEE double format). */ > +#define builtin_type_pyfloat builtin_type_ieee_double > + > +/* Python's long type corresponds to native C's long long type (which is > + assumed to be int64_t). */ > +#define builtin_type_pylong builtin_type_int64 > + > +/* The current language may not have a boolean type, so always use an > + integer as boolean type. Hopefully any language can deal with integers > + as boolean values. */ > +#define builtin_type_pybool builtin_type_int32 Can't you get an architecture to use when you need these? I'm scared the double change, in particular, will break any non-ieee target. This is supposed to be target arithmetic, we should use the target formats. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-29 17:00 ` Daniel Jacobowitz @ 2008-09-30 4:07 ` Thiago Jung Bauermann 2008-09-30 12:41 ` Daniel Jacobowitz 0 siblings, 1 reply; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-09-30 4:07 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: Eli Zaretskii, gdb-patches El lun, 29-09-2008 a las 12:59 -0400, Daniel Jacobowitz escribió: > On Mon, Sep 29, 2008 at 01:15:05PM -0300, Thiago Jung Bauermann wrote: > > +/* Python's float type corresponds to native C's double type (which is > > + assumed to use IEEE double format). */ > > +#define builtin_type_pyfloat builtin_type_ieee_double > > + > > +/* Python's long type corresponds to native C's long long type (which is > > + assumed to be int64_t). */ > > +#define builtin_type_pylong builtin_type_int64 > > + > > +/* The current language may not have a boolean type, so always use an > > + integer as boolean type. Hopefully any language can deal with integers > > + as boolean values. */ > > +#define builtin_type_pybool builtin_type_int32 > > Can't you get an architecture to use when you need these? I'm scared > the double change, in particular, will break any non-ieee target. > This is supposed to be target arithmetic, we should use the target > formats. I did it this way having in mind the case when a value needs to be created at a moment when no file is loaded (and thus an architecture/target is not yet determined). This is the same problem faced by the expression evaluator, when the user types (say): (gdb) print 1.2 Right after starting GDB, before any file has been loaded. It uses current_gdbarch for now. I had a look again at the places where builtin_type_* are used, and I found out in several cases that we can find a gdbarch when needed, but sometimes I can't see how to do that, and then we'd have to resort to something like the above. Some of the uses of builtin_type_* are in convert_value_from_python, it has two callers: the gdb.Value constructor and gdb.Function, which registers a Python function that can be called from the GDB expression evaluator. The constructor can be removed as the user won't need to create new gdb.Value objects himself. I kept it because it was very convenient for the testsuite. I'll rewrite it to use the get_value_from_history trick. For gdb.Function, I think it's possible to get gdbarch from the expression where it appears. I'll have to dig more into this, but since that code is not in this patch, I can just remove convert_value_from_python for now, and resubmit it later. The other uses of builtin_type_* are in valpy_binop and valpy_richcompare. They have a struct value handy, which is the second argument to the binary operation. From its type->main_type->objfile I can get a gdbarch. Sometimes a type will not have an objfile though (if it is a builtin_type itself), and in those cases I'd have to resort to some generic builtin_type. I thought this could happen if a gdb.Function was called before an architecture is determined, but now that I wrote all this, I believe since it would get its gdbarch from the expression, I guess we can just punt if value->type->main_type->objfile is NULL. Or should I expect types which are not associated with any objfile? -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-30 4:07 ` Thiago Jung Bauermann @ 2008-09-30 12:41 ` Daniel Jacobowitz 2008-10-01 3:18 ` Thiago Jung Bauermann 0 siblings, 1 reply; 28+ messages in thread From: Daniel Jacobowitz @ 2008-09-30 12:41 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: Eli Zaretskii, gdb-patches On Tue, Sep 30, 2008 at 01:06:02AM -0300, Thiago Jung Bauermann wrote: > I did it this way having in mind the case when a value needs to be > created at a moment when no file is loaded (and thus an > architecture/target is not yet determined). This is the same problem > faced by the expression evaluator, when the user types (say): > > (gdb) print 1.2 And I think it should have the same solution. There's a default architecture for these cases (depending on how GDB was built). If, in the future, we find another way to handle the print command... then we can find another way to handle this, too. I suggest that rather than bending over backwards to avoid the gdbarch. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-30 12:41 ` Daniel Jacobowitz @ 2008-10-01 3:18 ` Thiago Jung Bauermann 2008-10-01 11:40 ` Daniel Jacobowitz 0 siblings, 1 reply; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-10-01 3:18 UTC (permalink / raw) To: gdb-patches Daniel Jacobowitz wrote: > And I think it should have the same solution. There's a default > architecture for these cases (depending on how GDB was built). Which is current_gdbarch, as far as I can tell. I'll provide a new version of the patch which uses it when gdb.Value cannot get one from a nearby type's objfile. -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-01 3:18 ` Thiago Jung Bauermann @ 2008-10-01 11:40 ` Daniel Jacobowitz 0 siblings, 0 replies; 28+ messages in thread From: Daniel Jacobowitz @ 2008-10-01 11:40 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches On Wed, Oct 01, 2008 at 12:18:29AM -0300, Thiago Jung Bauermann wrote: > Daniel Jacobowitz wrote: > > And I think it should have the same solution. There's a default > > architecture for these cases (depending on how GDB was built). > > Which is current_gdbarch, as far as I can tell. Right. > I'll provide a new version of the patch which uses it when gdb.Value > cannot get one from a nearby type's objfile. How about we use it consistently, instead? I'd be very confused if what other types in the expression changed which gdbarch to use... -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-29 16:16 ` Thiago Jung Bauermann 2008-09-29 17:00 ` Daniel Jacobowitz @ 2008-09-29 18:52 ` Eli Zaretskii 1 sibling, 0 replies; 28+ messages in thread From: Eli Zaretskii @ 2008-09-29 18:52 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches, drow > From: Thiago Jung Bauermann <bauerman@br.ibm.com> > Cc: gdb-patches@sources.redhat.com, Daniel Jacobowitz <drow@false.org> > Date: Mon, 29 Sep 2008 13:15:05 -0300 > > Is this ok? The documentation part is approved. Thanks. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-26 2:00 ` Thiago Jung Bauermann 2008-09-26 9:30 ` Eli Zaretskii @ 2008-09-26 20:57 ` Daniel Jacobowitz 2008-10-01 5:39 ` Joel Brobecker 2 siblings, 0 replies; 28+ messages in thread From: Daniel Jacobowitz @ 2008-09-26 20:57 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches On Thu, Sep 25, 2008 at 10:59:26PM -0300, Thiago Jung Bauermann wrote: > This version has no FIXMEs. It uses current_language for value printing, and > its length operation always fails with NotImplemented. Ok to commit? Once Eli is happy with the documentation this is OK. Two coding notes: > + PyErr_SetString (PyExc_MemoryError, _("Could not allocate memory to " \ > + "create Value object.")); No need for the backslash here. > + gdb_test_multiple "python argv = gdb.get_value_from_history (0)" "" { > + -re "Traceback.*$gdb_prompt $" {fail "get value from history"} > + -re "$gdb_prompt $" {} > + } You really want a helper function for tests expected not to produce output, they're the bulk of this file. Another way to do it is by matching the command as it is sent; that's how the MI tests work. I think there's some non-MI examples too but I'm not sure where. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-26 2:00 ` Thiago Jung Bauermann 2008-09-26 9:30 ` Eli Zaretskii 2008-09-26 20:57 ` Daniel Jacobowitz @ 2008-10-01 5:39 ` Joel Brobecker 2008-10-04 22:14 ` Thiago Jung Bauermann 2 siblings, 1 reply; 28+ messages in thread From: Joel Brobecker @ 2008-10-01 5:39 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches > + 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" (Humpf, it's getting really hard sometimes to read these tests :-(). Anyway, this forced me to have a heada..., I mean focus, and I noticed a typo: "acess" -> "access". -- Joel ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-01 5:39 ` Joel Brobecker @ 2008-10-04 22:14 ` Thiago Jung Bauermann 0 siblings, 0 replies; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-10-04 22:14 UTC (permalink / raw) To: gdb-patches Joel Brobecker wrote: >> + 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" > > (Humpf, it's getting really hard sometimes to read these tests :-(). Yes, there are a few levels of quoting involved in that. :-/ > Anyway, this forced me to have a heada..., I mean focus, and I noticed > a typo: "acess" -> "access". Fixed, thanks! -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-20 21:29 ` Tom Tromey 2008-09-21 4:27 ` Daniel Jacobowitz @ 2008-09-25 4:49 ` Thiago Jung Bauermann 2008-09-26 23:08 ` Tom Tromey 2008-10-01 5:48 ` Joel Brobecker 2 siblings, 1 reply; 28+ messages in thread From: Thiago Jung Bauermann @ 2008-09-25 4:49 UTC (permalink / raw) To: gdb-patches Tom Tromey wrote: >>>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes: > There are some tradeoffs here. The "invisible access" approach is > convenient. However, it runs into issues with odd programs -- say, > multiple inheritance where a given name refers to multiple fields. Adding a way to do casting in python solves this, right? > I tend to like something toward the raw side, partly because any > cooked approach will still need some second way to deal with the > underlying explicit types. Sorry, I didn't understand "deal with the underlying explicit types". > I propose we decide these questions and implement this before checking > in this patch. The semantics of Value are critical. Right. I suggest people to take a look at the testcase to see what the syntax looks like, and speak up if there are any concerns or preferences. -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-25 4:49 ` Thiago Jung Bauermann @ 2008-09-26 23:08 ` Tom Tromey 0 siblings, 0 replies; 28+ messages in thread From: Tom Tromey @ 2008-09-26 23:08 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches Tom> There are some tradeoffs here. The "invisible access" approach is Tom> convenient. However, it runs into issues with odd programs -- say, Tom> multiple inheritance where a given name refers to multiple fields. Thiago> Adding a way to do casting in python solves this, right? Yes. Tom> I tend to like something toward the raw side, partly because any Tom> cooked approach will still need some second way to deal with the Tom> underlying explicit types. Thiago> Sorry, I didn't understand "deal with the underlying explicit types". That is just a funny way of saying, we'll still need the casting, and of course some way to get the list of superclasses. Tom> I propose we decide these questions and implement this before checking Tom> in this patch. The semantics of Value are critical. Thiago> Right. I suggest people to take a look at the testcase to see Thiago> what the syntax looks like, and speak up if there are any Thiago> concerns or preferences. The tricky cases all occur when using inheritance and having conflicting names. Tom ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-09-20 21:29 ` Tom Tromey 2008-09-21 4:27 ` Daniel Jacobowitz 2008-09-25 4:49 ` Thiago Jung Bauermann @ 2008-10-01 5:48 ` Joel Brobecker 2008-10-01 15:12 ` Tom Tromey 2 siblings, 1 reply; 28+ messages in thread From: Joel Brobecker @ 2008-10-01 5:48 UTC (permalink / raw) To: Tom Tromey; +Cc: Thiago Jung Bauermann, gdb-patches ml (Sorry I am participating so late in the discussion, but I am still recovering from my summer, and only recently managed to learn some Python) > The underlying question is what type model Value presents. If a Value > has a derived type, should we be able to access fields of the base > class using v["f"]? Or should we need v["Base"]["f"]? My initial reaction to this question is that the Value object should follow the same semantics as the debugger. For instance, if class Foo inherits from class Bar, then any component of class Bar should be directly visible from class Foo. So if X is Value of type class Foo, and class Bar has a component named "Baz", then I should be able to access that component with X["Baz"]. Better yet, I would love for the object to have one attribute for each component that I could simply access using X.baz. But I suspect that there is no way we can implement that without having to compute the value of each component, which would be quite wastful the vast majority of the time. Sigh, is it not possible to lazy-initialize attributes? -- Joel ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-01 5:48 ` Joel Brobecker @ 2008-10-01 15:12 ` Tom Tromey 2008-10-01 16:04 ` Joel Brobecker 2008-10-04 22:21 ` Daniel Jacobowitz 0 siblings, 2 replies; 28+ messages in thread From: Tom Tromey @ 2008-10-01 15:12 UTC (permalink / raw) To: Joel Brobecker; +Cc: Thiago Jung Bauermann, gdb-patches ml >>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes: >> The underlying question is what type model Value presents. If a Value >> has a derived type, should we be able to access fields of the base >> class using v["f"]? Or should we need v["Base"]["f"]? Joel> My initial reaction to this question is that the Value object Joel> should follow the same semantics as the debugger. By this I take you to mean the semantics of the currently selected language...? Joel> For instance, if class Foo inherits from class Bar, then any Joel> component of class Bar should be directly visible from class Joel> Foo. So if X is Value of type class Foo, and class Bar has a Joel> component named "Baz", then I should be able to access that Joel> component with X["Baz"]. This is the bad case: class B1 { int x; }; class B2 { int x; }; class D : B1, B2 { }; Suppose you have a Value v whose type is "D". An extension author needs a way to access each 'x'. I think the current proposal is to try to follow the current language, and then have the user cast 'v' to B1 or B2 if needed. The reason I prefer the other, more explicit, approach is that the meaning of python code does not change depending on a user's language setting. However, it is more typing, which is a drawback. Joel> Better yet, I would love for the object to have one attribute Joel> for each component that I could simply access using X.baz. Joel> But I suspect that there is no way we can implement that without Joel> having to compute the value of each component, which would be Joel> quite wastful the vast majority of the time. Sigh, is it not Joel> possible to lazy-initialize attributes? I think we can do this, but there is a cost, namely conflicts between methods on Value and field names in the inferior will have to be resolved in favor of the method. So, robust programs will always have to use the [] syntax anyway. Tom ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-01 15:12 ` Tom Tromey @ 2008-10-01 16:04 ` Joel Brobecker 2008-10-04 22:21 ` Daniel Jacobowitz 1 sibling, 0 replies; 28+ messages in thread From: Joel Brobecker @ 2008-10-01 16:04 UTC (permalink / raw) To: Tom Tromey; +Cc: Thiago Jung Bauermann, gdb-patches ml > Joel> My initial reaction to this question is that the Value object > Joel> should follow the same semantics as the debugger. > > By this I take you to mean the semantics of the currently selected > language...? Yes. I think it should mean the currently-selected language when the value is created? > This is the bad case: > > class B1 { int x; }; > class B2 { int x; }; > class D : B1, B2 { }; > > I think the current proposal is to try to follow the current language, > and then have the user cast 'v' to B1 or B2 if needed. Argh! Multiple inheritance. I see the problem, now. The proposal makes sense. > I think we can do this, but there is a cost, namely conflicts between > methods on Value and field names in the inferior will have to be > resolved in favor of the method. So, robust programs will always have > to use the [] syntax anyway. In that case, I don't see much value in my request. Oh well! -- Joel ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-01 15:12 ` Tom Tromey 2008-10-01 16:04 ` Joel Brobecker @ 2008-10-04 22:21 ` Daniel Jacobowitz 2008-10-05 0:00 ` Tom Tromey 1 sibling, 1 reply; 28+ messages in thread From: Daniel Jacobowitz @ 2008-10-04 22:21 UTC (permalink / raw) To: Tom Tromey; +Cc: Joel Brobecker, Thiago Jung Bauermann, gdb-patches ml On Wed, Oct 01, 2008 at 09:09:36AM -0600, Tom Tromey wrote: > Joel> Better yet, I would love for the object to have one attribute > Joel> for each component that I could simply access using X.baz. > Joel> But I suspect that there is no way we can implement that without > Joel> having to compute the value of each component, which would be > Joel> quite wastful the vast majority of the time. Sigh, is it not > Joel> possible to lazy-initialize attributes? > > I think we can do this, but there is a cost, namely conflicts between > methods on Value and field names in the inferior will have to be > resolved in favor of the method. So, robust programs will always have > to use the [] syntax anyway. WDYT about making this work anyway - and using a documented namespace for any methods we add? Then the common case will be able to use the attributes safely. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-04 22:21 ` Daniel Jacobowitz @ 2008-10-05 0:00 ` Tom Tromey 2008-10-06 18:49 ` Joel Brobecker 2008-10-06 21:15 ` Daniel Jacobowitz 0 siblings, 2 replies; 28+ messages in thread From: Tom Tromey @ 2008-10-05 0:00 UTC (permalink / raw) To: Joel Brobecker; +Cc: Thiago Jung Bauermann, gdb-patches ml >>>>> "Daniel" == Daniel Jacobowitz <drow@false.org> writes: Daniel> WDYT about making this work anyway - and using a documented namespace Daniel> for any methods we add? Then the common case will be able to use the Daniel> attributes safely. In practice, I don't mind how it works now. I wrote a few pretty-printers (appended for your viewing pleasure) and I found that the array syntax is not a real impediment. If anything, the dereference method is more of a pain. FYI -- on the current python branch, Value already has a few more methods: address, cast, and type. I guess I wouldn't mind a mangling scheme if it were unobtrusive enough. Tom # Pretty-printers for libstc++. def printstdlist(val): "Print a std::list" itype = val.type().template_argument(0) nodetype = gdb.Type('std::_List_node<%s>' % itype).pointer() head = val['_M_impl']['_M_node'] base = head['_M_next'] head = head.address() max = gdb.get_parameter('print elements') n = 0 result = [] while base != head and (max == 0 or n < max): elt = base.cast(nodetype).dereference() datum = elt['_M_data'] result.append ('[%d] = %s' % (n, str (datum))) n = n + 1 base = elt['_M_next'] if len (result) == 0: result.append('<<empty list>>') return "\n".join(result) def printstdvector(val): "Print a std::vector" start = val['_M_impl']['_M_start'] finish = val['_M_impl']['_M_finish'] end = val['_M_impl']['_M_end_of_storage'] max = gdb.get_parameter('print elements') result = [] result.append('std::vector of length %d, capacity %d' % (int (finish - start), int (end - start))) i = 0 while start < finish and (max == 0 or i < max): result.append('[%d] = %s' % (i, str(start.dereference()))) start = start + 1 i = i + 1 return "\n".join(result) def printstdstack(val): "Print a std::stack or std::queue" # Maybe we should print stack and queue in opposite orders? cur = val['c']['_M_impl']['_M_start']['_M_cur'] finish = val['c']['_M_impl']['_M_finish']['_M_cur'] i = 0 max = gdb.get_parameter('print elements') result = ['std::stack or std::queue of size %d' % int (finish -cur)] while cur != finish and (max == 0 or i < max): result.append('[%d] = %s' % (i, str (cur.dereference()))) cur = cur + 1 i = i + 1 return "\n".join(result) def rbtree_to_list(val): "Turn an rbtree into a size and a list of elements." maxprint = gdb.get_parameter('print elements') size = val['_M_t']['_M_impl']['_M_node_count'] if maxprint == 0 or maxprint > size: maxprint = size node = val['_M_t']['_M_impl']['_M_header']['_M_left'] i = 0 result = [] while i < maxprint: result.append(node) if node.dereference()['_M_right']: node = node.dereference()['_M_right'] while node.dereference()['_M_left']: node = node.dereference()['_M_left'] else: parent = node.dereference()['_M_parent'] while node == parent.dereference()['_M_right']: node = parent parent = parent.dereference()['_M_parent'] if node.dereference()['_M_right'] != parent: node = parent i = i + 1 return (size, result) def printstdmap(val): "Print a std::map" keytype = val.type().template_argument(0) valuetype = val.type().template_argument(1) nodetype = gdb.Type('std::_Rb_tree_node< std::pair< const %s, %s > >' % (keytype, valuetype)) nodetype = nodetype.pointer() (size, nodes) = rbtree_to_list (val) result = ['std::map with %d elements' % int (size)] for node in nodes: pair = node.cast(nodetype).dereference()['_M_value_field'] result.append('[%s] = %s' % (str (pair['first']), str (pair['second']))) return "\n".join(result) def printstdset(val): "Print a std::set" keytype = val.type().template_argument(0) nodetype = gdb.Type("std::_Rb_tree_node< %s >" % keytype).pointer() (size, nodes) = rbtree_to_list (val) result = ['std::set with %d elements' % int (size)] for node in nodes: elt = node.cast(nodetype).dereference()['_M_value_field'] result.append(' %s' % str (elt)) return "\n".join(result) def printstdstring(val): "Print a std::string" return val['_M_dataplus']['_M_p'] gdb.pretty_printers['^std::list<.*>$'] = printstdlist gdb.pretty_printers['^std::vector<.*>$'] = printstdvector gdb.pretty_printers['^std::map<.*>$'] = printstdmap gdb.pretty_printers['^std::set<.*>$'] = printstdset gdb.pretty_printers['^std::basic_string<char.*>$'] = printstdstring gdb.pretty_printers['^std::stack<.*>$'] = printstdstack gdb.pretty_printers['^std::queue<.*>$'] = printstdstack ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-05 0:00 ` Tom Tromey @ 2008-10-06 18:49 ` Joel Brobecker 2008-10-06 21:15 ` Daniel Jacobowitz 1 sibling, 0 replies; 28+ messages in thread From: Joel Brobecker @ 2008-10-06 18:49 UTC (permalink / raw) To: Tom Tromey; +Cc: Thiago Jung Bauermann, gdb-patches ml > In practice, I don't mind how it works now. I'm find with the array notation too, and I don't necessarily want us to implement more than one way to access the fields anyway. The script looks nice! -- Joel ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [rfc] expose gdb values to python 2008-10-05 0:00 ` Tom Tromey 2008-10-06 18:49 ` Joel Brobecker @ 2008-10-06 21:15 ` Daniel Jacobowitz 1 sibling, 0 replies; 28+ messages in thread From: Daniel Jacobowitz @ 2008-10-06 21:15 UTC (permalink / raw) To: Tom Tromey; +Cc: Joel Brobecker, Thiago Jung Bauermann, gdb-patches ml On Sat, Oct 04, 2008 at 05:58:16PM -0600, Tom Tromey wrote: > I guess I wouldn't mind a mangling scheme if it were unobtrusive enough. I had in mind something like this: itype = val.gdb_type() # Invoke method field = val.type # Fetch member value That saves on quoting, but will make other code more verbose. You're probably right that it's not worthwhile. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2008-10-06 21:15 UTC | newest] Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-09-12 6:05 [rfc] expose gdb values to python Thiago Jung Bauermann 2008-09-20 21:29 ` Tom Tromey 2008-09-21 4:27 ` Daniel Jacobowitz 2008-09-25 4:33 ` Thiago Jung Bauermann 2008-09-25 11:47 ` Daniel Jacobowitz 2008-09-26 2:00 ` Thiago Jung Bauermann 2008-09-26 9:30 ` Eli Zaretskii 2008-09-28 1:19 ` Thiago Jung Bauermann 2008-09-28 18:19 ` Eli Zaretskii 2008-09-29 16:16 ` Thiago Jung Bauermann 2008-09-29 17:00 ` Daniel Jacobowitz 2008-09-30 4:07 ` Thiago Jung Bauermann 2008-09-30 12:41 ` Daniel Jacobowitz 2008-10-01 3:18 ` Thiago Jung Bauermann 2008-10-01 11:40 ` Daniel Jacobowitz 2008-09-29 18:52 ` Eli Zaretskii 2008-09-26 20:57 ` Daniel Jacobowitz 2008-10-01 5:39 ` Joel Brobecker 2008-10-04 22:14 ` Thiago Jung Bauermann 2008-09-25 4:49 ` Thiago Jung Bauermann 2008-09-26 23:08 ` Tom Tromey 2008-10-01 5:48 ` Joel Brobecker 2008-10-01 15:12 ` Tom Tromey 2008-10-01 16:04 ` Joel Brobecker 2008-10-04 22:21 ` Daniel Jacobowitz 2008-10-05 0:00 ` Tom Tromey 2008-10-06 18:49 ` Joel Brobecker 2008-10-06 21:15 ` Daniel Jacobowitz
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox