[2/4] Define gdb.Value(bufobj, type) constructor

Message ID 20190218080636.02e3847f@f29-4.lan
State New, archived
Headers

Commit Message

Kevin Buettner Feb. 18, 2019, 3:06 p.m. UTC
  Provided a buffer BUFOBJ and a type TYPE, construct a gdb.Value object
with type TYPE, where the value's contents are taken from BUFOBJ.

E.g...

(gdb) python import struct
(gdb) python unsigned_int_type=gdb.lookup_type('unsigned int')
(gdb) python b=struct.pack('=I',0xdeadbeef)
(gdb) python v=gdb.Value(b, unsigned_int_type) ; print("%#x" % v)
0xdeadbeef

This two argument form of the gdb.Value constructor may also be used
to obtain gdb values from selected portions of buffers read with
Inferior.read_memory().  The test case (which is in a separate patch)
demonstrates this use case.

gdb/ChangeLog:
    
            * python/py-value.c (convert_buffer_and_type_to_value): New
            function.
            (valpy_new): Parse arguments via gdb_PyArg_ParseTupleAndKeywords.
            Add support for handling an optional second argument.  Call
            convert_buffer_and_type_to_value as appropriate.
---
 gdb/python/py-value.c | 72 ++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 62 insertions(+), 10 deletions(-)
  

Comments

Tom Tromey Feb. 18, 2019, 10:45 p.m. UTC | #1
>>>>> "Kevin" == Kevin Buettner <kevinb@redhat.com> writes:

FWIW I think this whole series looks quite reasonable and would be a
good addition.

Kevin> +  Py_buffer py_buf;

Elsewhere in the Python code, Py_buffer is only used conditionally on
"#ifdef IS_PY3K".  I don't know exactly why, though.  Py_buffer seems to
be in Python 2.7, maybe it wasn't in some earlier version that gdb still
supports?

I don't know whether anyone still relies on older versions of Python
(2.7 was released in 2010 so it seems plenty old at this point; after
all we require compilers released after this).

However, this seems like a decision to make consciously.
I'm not completely sure how to proceed, though one idea might be to find
the most recent Python 2.[456] compatibility patch and then asking the
author whether this support is still relevant.

thanks,
Tom
  
Kevin Buettner Feb. 19, 2019, 12:57 a.m. UTC | #2
On Mon, 18 Feb 2019 15:45:57 -0700
Tom Tromey <tom@tromey.com> wrote:

> >>>>> "Kevin" == Kevin Buettner <kevinb@redhat.com> writes:  
> 
> FWIW I think this whole series looks quite reasonable and would be a
> good addition.

Thanks for looking it over.

> Kevin> +  Py_buffer py_buf;  
> 
> Elsewhere in the Python code, Py_buffer is only used conditionally on
> "#ifdef IS_PY3K".  I don't know exactly why, though.  Py_buffer seems to
> be in Python 2.7, maybe it wasn't in some earlier version that gdb still
> supports?

FWIW, I've tested my patch using both Python 2.7 and Python 3.7, which
are what I have on my F29 machine.

> I don't know whether anyone still relies on older versions of Python
> (2.7 was released in 2010 so it seems plenty old at this point; after
> all we require compilers released after this).
> 
> However, this seems like a decision to make consciously.
> I'm not completely sure how to proceed, though one idea might be to find
> the most recent Python 2.[456] compatibility patch and then asking the
> author whether this support is still relevant.

I didn't know of that compatibility patch, though I'll try to track it
down.  (A pointer would be appreciated if you have one handy.)

I decided that I had better educate myself about the old buffer
protocol.  I came across this page, which describes Python's old
buffer protocol:

https://docs.python.org/2/c-api/objbuffer.html

The old buffer protocol includes the functions PyObject_CheckReadBuffer()
and PyObject_AsReadBuffer() which are used in the non-IS_PY3K code in
GDB.

According to that page, the old buffer protocol was introduced in
Python 1.6, but is deprecated in the 2.X series.  It also indicates
that support for the new buffer protocol, which is what I used in my
patch, has been backported to Python 2.6.  2.6 was released in Oct
of 2008.  (I'm thinking that indicating that the old buffer protocol is
deprecated in 2.X is a typo.  I think they meant 3.X.)

I'll try removing use of the old buffer protocol from GDB and then
see if anything breaks with Python 2.7 (and maybe even 2.6 if I can
figure out how to make that happen).  If we get rid of the IS_PY3K
ifdefs from GDB, we'll definitely be making this decision consciously.

Kevin
  
Simon Marchi Feb. 19, 2019, 2:41 a.m. UTC | #3
On 2019-02-18 10:06, Kevin Buettner wrote:
> +/* Convert a python object OBJ with type TYPE to a gdb value.  The
> +   python object in question must conform to the python buffer
> +   protocol.  On success, return the converted value, otherwise
> +   nullptr.  */
> +
> +static struct value *
> +convert_buffer_and_type_to_value (PyObject *obj, struct type *type)
> +{
> +  Py_buffer_up buffer_up;
> +  Py_buffer py_buf;
> +
> +  if (PyObject_CheckBuffer (obj)
> +      && PyObject_GetBuffer (obj, &py_buf, PyBUF_SIMPLE) == 0)
> +    {
> +      /* Got a buffer, py_buf, out of obj.  Cause it to released
> +         when it goes out of scope.  */

Seems to be missing a "be" in "Cause it to be released".

Simon
  
Tom Tromey Feb. 19, 2019, 3:19 p.m. UTC | #4
>>>>> "Kevin" == Kevin Buettner <kevinb@redhat.com> writes:

>> However, this seems like a decision to make consciously.
>> I'm not completely sure how to proceed, though one idea might be to find
>> the most recent Python 2.[456] compatibility patch and then asking the
>> author whether this support is still relevant.

Kevin> I didn't know of that compatibility patch, though I'll try to track it
Kevin> down.  (A pointer would be appreciated if you have one handy.)

I just recall it happening from time to time.  Using:

    git log --grep 'Python 2\.[4-6]'

... shows a couple maybe uninteresting patches, followed by:

commit 49840f2a6669ae2366c522da41edf615785b3626
Author: Ulrich Weigand <ulrich.weigand@de.ibm.com>
Date:   Sun Mar 16 15:01:24 2014 +0100

    Fix Python 2.4 build break
    
    This fixes a build failure against Python 2.4 by casting away "const"
    on the second argument to PyObject_GetAttrString.  Similar casts to
    support Python 2.4 were already present in a number of other places.
    
    gdb/
    2014-03-16  Ulrich Weigand  <uweigand@de.ibm.com>
    
            * python/py-value.c (get_field_flag): Cast flag_name argument to
            PyObject_GetAttrString to support Python 2.4.

So, I've CC'd Ulrich.

Tom
  

Patch

diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index 20ef5822f8..154b175a21 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -107,22 +107,68 @@  note_value (value_object *value_obj)
   values_in_python = value_obj;
 }
 
+/* Convert a python object OBJ with type TYPE to a gdb value.  The
+   python object in question must conform to the python buffer
+   protocol.  On success, return the converted value, otherwise
+   nullptr.  */
+
+static struct value *
+convert_buffer_and_type_to_value (PyObject *obj, struct type *type)
+{
+  Py_buffer_up buffer_up;
+  Py_buffer py_buf;
+
+  if (PyObject_CheckBuffer (obj) 
+      && PyObject_GetBuffer (obj, &py_buf, PyBUF_SIMPLE) == 0)
+    {
+      /* Got a buffer, py_buf, out of obj.  Cause it to released
+         when it goes out of scope.  */
+      buffer_up.reset (&py_buf);
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Object must support the python buffer protocol."));
+      return nullptr;
+    }
+
+  if (TYPE_LENGTH (type) > py_buf.len)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Size of type is larger than that of buffer object."));
+      return nullptr;
+    }
+
+  return value_from_contents (type, (const gdb_byte *) py_buf.buf);
+}
+
 /* Called when a new gdb.Value object needs to be allocated.  Returns NULL on
    error, with a python exception set.  */
 static PyObject *
-valpy_new (PyTypeObject *subtype, PyObject *args, PyObject *keywords)
+valpy_new (PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
 {
-  struct value *value = NULL;   /* Initialize to appease gcc warning.  */
-  value_object *value_obj;
+  static const char *keywords[] = { "val", "type", NULL };
+  PyObject *val_obj = nullptr;
+  PyObject *type_obj = nullptr;
 
-  if (PyTuple_Size (args) != 1)
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords,
+					&val_obj, &type_obj))
+    return nullptr;
+
+  struct type *type = nullptr;
+
+  if (type_obj != nullptr)
     {
-      PyErr_SetString (PyExc_TypeError, _("Value object creation takes only "
-					  "1 argument"));
-      return NULL;
+      type = type_object_to_type (type_obj);
+      if (type == nullptr)
+        {
+	  PyErr_SetString (PyExc_RuntimeError,
+			   _("type argument must be a gdb.Type."));
+	  return nullptr;
+	}
     }
 
-  value_obj = (value_object *) subtype->tp_alloc (subtype, 1);
+  value_object *value_obj = (value_object *) subtype->tp_alloc (subtype, 1);
   if (value_obj == NULL)
     {
       PyErr_SetString (PyExc_MemoryError, _("Could not allocate memory to "
@@ -130,8 +176,14 @@  valpy_new (PyTypeObject *subtype, PyObject *args, PyObject *keywords)
       return NULL;
     }
 
-  value = convert_value_from_python (PyTuple_GetItem (args, 0));
-  if (value == NULL)
+  struct value *value;
+
+  if (type == nullptr)
+    value = convert_value_from_python (val_obj);
+  else
+    value = convert_buffer_and_type_to_value (val_obj, type);
+
+  if (value == nullptr)
     {
       subtype->tp_free (value_obj);
       return NULL;