[RFA] Python: PR/13327: expose laziness to Python

Paul Koning
2011-10-25T17:38:54Z

The attached implements a solution for PR/13327 by introducing the gdb.Value attribute "is_lazy" and method "fetch_lazy()". It also reverts an earlier change I made that always immediately fetches values for gdb.Value, as discussed in http://sourceware.org/ml/gdb-patches/2011-10/msg00541.html .

The documentation briefly touches on how lazy fetching works in the discussion of the is_lazy attribute, but it doesn't more fully discuss it as a topic of its own. I could try to do so, in the introductory material for gdb.Value. Should I do that?

Ok to commit?

paul

ChangeLog:

2011-10-25 Paul Koning <paul_koning@dell.com>

PR python/13327

* python/py-value.c (value_to_value_object): Remove fetching of
the value if it was lazy.
(valpy_get_is_lazy): New function.
(valpy_fetch_lazy): New function.

doc/Changelog:

2011-10-25 Paul Koning <paul_koning@dell.com>

PR python/13327

* gdb.texinfo (Values From Inferior): Add is_lazy attribute,
fetch_lazy method.

testsuite/ChangeLog:

2011-10-25 Paul Koning <paul_koning@dell.com>

PR python/13327

* gdb.python/py-value.exp: Add testcases for is_lazy attribute,
fetch_lazy method.

Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.880
diff -u -r1.880 gdb.texinfo
--- doc/gdb.texinfo 20 Oct 2011 12:31:29 -0000 1.880
+++ doc/gdb.texinfo 25 Oct 2011 17:24:18 -0000
@@ -21681,6 +21681,20 @@
it will just return the static type of the value as in @kbd{ptype foo}
(@pxref{Symbols, ptype}).
@end defvar
+
+@defvar Value.is_lazy
+This read-only boolean attribute is true if the value has not yet
+been fetched from the inferior. GDB does not fetch values until
+necessary, for efficiency. For example:
+
+@smallexample
+myval = gdb.parse_and_eval ('somevar')
+@end smallexample
+
+The value of @var{somevar} is not fetched at this time. It will be
+fetched when the value is needed, or when the @code{fetch_lazy ()}
+method is invoked.
+@end defvar
@end table

The following methods are provided:
@@ -21814,6 +21828,19 @@
the @var{length} argument is not provided, the string will be fetched
and encoded until a null of appropriate width is found.
@end defun
+
+@defun Value.fetch_lazy ()
+If the @code{gdb.Value} object is currently a lazy value
+(@code{gdb.Value.is_lazy} is @code{True}) then the value is
+fetched from the inferior. Any errors that occur in the process
+will produce a Python exception.
+
+If the @code{gdb.Value} object is not a lazy value, this method
+has no effect.
+
+This method does not return a value.
+@end defun
+
@end table

@node Types In Python
Index: python/py-value.c
===================================================================
RCS file: /cvs/src/src/gdb/python/py-value.c,v
retrieving revision 1.28
diff -u -r1.28 py-value.c
--- python/py-value.c 7 Oct 2011 22:02:42 -0000 1.28
+++ python/py-value.c 25 Oct 2011 17:24:18 -0000
@@ -617,6 +617,43 @@
Py_RETURN_FALSE;
}

+/* Implements gdb.Value.is_lazy. */
+static PyObject *
+valpy_get_is_lazy (PyObject *self, void *closure)
+{
+ struct value *value = ((value_object *) self)->value;
+ int opt = 0;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ opt = value_lazy (value);
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ if (opt)
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
+/* Implements gdb.Value.fetch_lazy (). */
+static PyObject *
+valpy_fetch_lazy (PyObject *self, PyObject *args)
+{
+ struct value *value = ((value_object *) self)->value;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (value_lazy (value))
+ value_fetch_lazy (value);
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ Py_RETURN_NONE;
+}
+
/* Calculate and return the address of the PyObject as the value of
the builtin __hash__ call. */
static long
@@ -1081,15 +1118,7 @@
value_to_value_object (struct value *val)
{
value_object *val_obj;
- volatile struct gdb_exception except;

- TRY_CATCH (except, RETURN_MASK_ALL)
- {
- if (value_lazy (val))
- value_fetch_lazy (val);
- }
- GDB_PY_HANDLE_EXCEPTION (except);
-
val_obj = PyObject_New (value_object, &value_object_type);
if (val_obj != NULL)
{
@@ -1276,6 +1305,10 @@
{ "type", valpy_get_type, NULL, "Type of the value.", NULL },
{ "dynamic_type", valpy_get_dynamic_type, NULL,
"Dynamic type of the value.", NULL },
+ { "is_lazy", valpy_get_is_lazy, NULL,
+ "Boolean telling whether the value is lazy (not fetched yet\n\
+from the inferior). A lazy value is fetched when needed, or when\n\
+the \"fetch_lazy()\" method is called.", NULL },
{NULL} /* Sentinel */
};

@@ -1298,6 +1331,8 @@
{ "string", (PyCFunction) valpy_string, METH_VARARGS | METH_KEYWORDS,
"string ([encoding] [, errors] [, length]) -> string\n\
Return Unicode string representation of the value." },
+ { "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
+ "Fetches the value from the inferior, if it was lazy." },
{NULL} /* Sentinel */
};

Index: testsuite/gdb.python/py-value.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-value.exp,v
retrieving revision 1.24
diff -u -r1.24 py-value.exp
--- testsuite/gdb.python/py-value.exp 3 Oct 2011 16:15:26 -0000 1.24
+++ testsuite/gdb.python/py-value.exp 25 Oct 2011 17:24:19 -0000
@@ -236,17 +236,27 @@
gdb_test "python print gdb.parse_and_eval('*(int*)0')" "gdb.MemoryError: Cannot access memory at address 0x0.*" $test
}

- # Test Python values are not lazy.
- set test "memory error occurs even for possibly lazy values"
+ # Test Python lazy value handling
+ set test "memory error and lazy values"
if {$can_read_0} {
untested $test
} else {
- gdb_test "python inval = gdb.parse_and_eval('*(int*)0')" "gdb.MemoryError: Cannot access memory at address 0x0.*" $test
+ gdb_test "python inval = gdb.parse_and_eval('*(int*)0')"
+ gdb_test "python print inval.is_lazy" "True"
+ gdb_test "python inval2 = inval+1" "gdb.MemoryError: Cannot access memory at address 0x0.*" $test
+ gdb_test "python inval.fetch_lazy ()" "gdb.MemoryError: Cannot access memory at address 0x0.*" $test
}
gdb_test "python argc_lazy = gdb.parse_and_eval('argc')"
+ gdb_test "python argc_notlazy = gdb.parse_and_eval('argc')"
+ gdb_test "python argc_notlazy.fetch_lazy()"
+ gdb_test "python print argc_lazy.is_lazy" "True"
+ gdb_test "python print argc_notlazy.is_lazy" "False"
gdb_test "print argc" " = 1" "sanity check argc"
+ gdb_test "python print argc_lazy.is_lazy" "\r\nTrue"
gdb_test_no_output "set argc=2"
- gdb_test "python print argc_lazy" "\r\n1"
+ gdb_test "python print argc_notlazy" "\r\n1"
+ gdb_test "python print argc_lazy" "\r\n2"
+ gdb_test "python print argc_lazy.is_lazy" "False"

# Test string fetches, both partial and whole.
gdb_test "print st" "\"divide et impera\""