diff mbox

[2/4] Fix some aspects of python lazy string handling: python

Message ID 94eb2c0339f25be7660540abbfab@google.com
State New
Headers show

Commit Message

Doug Evans Nov. 7, 2016, 1:14 a.m. UTC
Hi. This is the python side of the patch set.

2016-11-06  Doug Evans  <dje@google.com>

	PR python/17728, python/18439, python/18779
	* python/py-lazy-string.c (lazy_string_object): Clarify use of LENGTH
	member.  Change type of TYPE member to PyObject *.  All uses updated.
	(stpy_convert_to_value): Fix handling of TYPE_CODE_PTR.
	(gdbpy_create_lazy_string_object): Flag bad length values.
	Handle TYPE_CODE_ARRAY with possibly different user-provided length.
	Handle typedefs in incoming type.
	(stpy_lazy_string_elt_type): New function.
	(gdbpy_extract_lazy_string): Call it.
	* python/py-value.c (valpy_lazy_string): Flag bad length values.
	Fix handling of TYPE_CODE_PTR.  Handle TYPE_CODE_ARRAY.  Handle
	typedefs in incoming type.

-  gdb_test "python print (snstr.address)" "0" "Test lazy string address"
-}
-
-
  proc test_inferior_function_call {} {
      global gdb_prompt hex decimal

@@ -530,7 +507,6 @@ if ![runto_main] then {

  test_value_in_inferior
  test_inferior_function_call
-test_lazy_strings
  test_value_after_death

  # Test either C or C++ values.

Comments

Doug Evans via gdb-patches March 16, 2017, 4:57 p.m. UTC | #1
On Sun, Nov 6, 2016 at 5:14 PM, Doug Evans <dje@google.com> wrote:
> Hi. This is the python side of the patch set.
>
> 2016-11-06  Doug Evans  <dje@google.com>
>
>         PR python/17728, python/18439, python/18779
>         * python/py-lazy-string.c (lazy_string_object): Clarify use of
> LENGTH
>         member.  Change type of TYPE member to PyObject *.  All uses
> updated.
>         (stpy_convert_to_value): Fix handling of TYPE_CODE_PTR.
>         (gdbpy_create_lazy_string_object): Flag bad length values.
>         Handle TYPE_CODE_ARRAY with possibly different user-provided length.
>         Handle typedefs in incoming type.
>         (stpy_lazy_string_elt_type): New function.
>         (gdbpy_extract_lazy_string): Call it.
>         * python/py-value.c (valpy_lazy_string): Flag bad length values.
>         Fix handling of TYPE_CODE_PTR.  Handle TYPE_CODE_ARRAY.  Handle
>         typedefs in incoming type.

Rebased, re-regression tested on amd64-linux, and committed.
[I'll post the committed patch as soon as I can get sendmail on my box
working again, bleah. We still don't allow attachments do we?]
diff mbox

Patch

diff --git a/gdb/python/py-lazy-string.c b/gdb/python/py-lazy-string.c
index d4b40df..57e94d9 100644
--- a/gdb/python/py-lazy-string.c
+++ b/gdb/python/py-lazy-string.c
@@ -26,6 +26,7 @@ 

  typedef struct {
    PyObject_HEAD
+
    /*  Holds the address of the lazy string.  */
    CORE_ADDR address;

@@ -35,14 +36,21 @@  typedef struct {
        encoding when the sting is printed.  */
    char *encoding;

-  /* Holds the length of the string in characters.  If the
-     length is -1, then the string will be fetched and encoded up to
-     the first null of appropriate width.  */
+  /* If TYPE is an array: If the length is known, then this value is the
+     array's length, otherwise it is -1.
+     If TYPE is not an array: Then this value represents the string's  
length.
+     In either case, if the value is -1 then the string will be fetched and
+     encoded up to the first null of appropriate width.  */
    long length;

-  /*  This attribute holds the type that is represented by the lazy
-      string's type.  */
-  struct type *type;
+  /* This attribute holds the type of the string.
+     For example if the lazy string was created from a C "char*" then TYPE
+     represents a C "char*".
+     To get the type of the character in the string call
+     stpy_lazy_string_elt_type.
+     This is recorded as a PyObject so that we take advantage of support  
for
+     preserving the type should its owning objfile go away.  */
+  PyObject *type;
  } lazy_string_object;

  extern PyTypeObject lazy_string_object_type
@@ -88,11 +96,12 @@  stpy_get_type (PyObject *self, void *closure)
  {
    lazy_string_object *str_obj = (lazy_string_object *) self;

-  return type_to_type_object (str_obj->type);
+  Py_INCREF (str_obj->type);
+  return str_obj->type;
  }

  static PyObject *
-stpy_convert_to_value  (PyObject *self, PyObject *args)
+stpy_convert_to_value (PyObject *self, PyObject *args)
  {
    lazy_string_object *self_string = (lazy_string_object *) self;
    struct value *val = NULL;
@@ -106,7 +115,32 @@  stpy_convert_to_value  (PyObject *self, PyObject *args)

    TRY
      {
-      val = value_at_lazy (self_string->type, self_string->address);
+      struct type *type = type_object_to_type (self_string->type);
+      struct type *realtype;
+
+      gdb_assert (type != NULL);
+      realtype = check_typedef (type);
+      switch (TYPE_CODE (realtype))
+	{
+	case TYPE_CODE_PTR:
+	  /* If a length is specified we need to convert this to an array
+	     of the specified size.  */
+	  if (self_string->length != -1)
+	    {
+	      /* PR 20786: There's no way to specify an array of length zero.
+		 Record a length of [0,-1] which is how Ada does it.  Anything
+		 we do is broken, but this one possible solution.  */
+	      type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype),
+					      0, self_string->length - 1);
+	      val = value_at_lazy (type, self_string->address);
+	    }
+	  else
+	    val = value_from_pointer (type, self_string->address);
+	  break;
+	default:
+	  val = value_at_lazy (type, self_string->address);
+	  break;
+	}
      }
    CATCH (except, RETURN_MASK_ALL)
      {
@@ -125,11 +159,24 @@  stpy_dealloc (PyObject *self)
    xfree (self_string->encoding);
  }

+/* Low level routine to create a <gdb.LazyString> object.
+
+   Note: If TYPE is an array, LENGTH either must be -1 (meaning to use the
+   size of the array, which may itself be unknown in which case a length of
+   -1 is still used) or must be the length of the array.  */
+
  PyObject *
  gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
-			   const char *encoding, struct type *type)
+				 const char *encoding, struct type *type)
  {
    lazy_string_object *str_obj = NULL;
+  struct type *realtype;
+
+  if (length < -1)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Invalid length."));
+      return NULL;
+    }

    if (address == 0 && length != 0)
      {
@@ -146,6 +193,27 @@  gdbpy_create_lazy_string_object (CORE_ADDR address,  
long length,
        return NULL;
      }

+  realtype = check_typedef (type);
+  switch (TYPE_CODE (realtype))
+    {
+    case TYPE_CODE_ARRAY:
+      {
+	LONGEST array_length = -1;
+	LONGEST low_bound, high_bound;
+
+	if (get_array_bounds (realtype, &low_bound, &high_bound))
+	  array_length = high_bound - low_bound + 1;
+	if (length == -1)
+	  length = array_length;
+	else if (length != array_length)
+	  {
+	    PyErr_SetString (PyExc_ValueError, _("Invalid length."));
+	    return NULL;
+	  }
+	break;
+      }
+    }
+
    str_obj = PyObject_New (lazy_string_object, &lazy_string_object_type);
    if (!str_obj)
      return NULL;
@@ -156,7 +224,7 @@  gdbpy_create_lazy_string_object (CORE_ADDR address,  
long length,
      str_obj->encoding = NULL;
    else
      str_obj->encoding = xstrdup (encoding);
-  str_obj->type = type;
+  str_obj->type = type_to_type_object (type);

    return (PyObject *) str_obj;
  }
@@ -179,6 +247,29 @@  gdbpy_is_lazy_string (PyObject *result)
    return PyObject_TypeCheck (result, &lazy_string_object_type);
  }

+/* Return the type of a character in lazy string LAZY.  */
+
+static struct type *
+stpy_lazy_string_elt_type (lazy_string_object *lazy)
+{
+  struct type *type = type_object_to_type (lazy->type);
+  struct type *realtype;
+
+  gdb_assert (type != NULL);
+  realtype = check_typedef (type);
+
+  switch (TYPE_CODE (realtype))
+    {
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_ARRAY:
+      return TYPE_TARGET_TYPE (realtype);
+    default:
+      /* This is done to preserve existing behaviour.  PR 20769.
+	 E.g., gdb.parse_and_eval("my_int_variable").lazy_string().type.  */
+      return realtype;
+    }
+}
+
  /* Extract the parameters from the lazy string object STRING.
     ENCODING will either be set to NULL, or will be allocated with
     xmalloc, in which case the callers is responsible for freeing
@@ -186,7 +277,7 @@  gdbpy_is_lazy_string (PyObject *result)

  void
  gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR *addr,
-			   struct type **str_type,
+			   struct type **str_elt_type,
  			   long *length, char **encoding)
  {
    lazy_string_object *lazy;
@@ -196,7 +287,7 @@  gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR  
*addr,
    lazy = (lazy_string_object *) string;

    *addr = lazy->address;
-  *str_type = lazy->type;
+  *str_elt_type = stpy_lazy_string_elt_type (lazy);
    *length = lazy->length;
    *encoding = lazy->encoding ? xstrdup (lazy->encoding) : NULL;
  }
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index 46683b8..6703d21 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -409,9 +409,19 @@  valpy_get_dynamic_type (PyObject *self, void *closure)
     A lazy string is a pointer to a string with an optional encoding and
     length.  If ENCODING is not given, encoding is set to None.  If an
     ENCODING is provided the encoding parameter is set to ENCODING, but
-   the string is not encoded.  If LENGTH is provided then the length
-   parameter is set to LENGTH, otherwise length will be set to -1 (first
-   null of appropriate with).  */
+   the string is not encoded.
+   If LENGTH is provided then the length parameter is set to LENGTH.
+   Otherwise if the value is an array of known length then the array's  
length
+   is used.  Otherwise the length will be set to -1 (meaning first null of
+   appropriate with).
+
+   Note: In order to not break any existing uses this allows creating
+   lazy strings from anything.  PR 20769.  E.g.,
+   gdb.parse_and_eval("my_int_variable").lazy_string().
+   "It's easier to relax restrictions than it is to impose them after the
+   fact."  So we should be flagging any unintended uses as errors, but it's
+   perhaps too late for that.  */
+
  static PyObject *
  valpy_lazy_string (PyObject *self, PyObject *args, PyObject *kw)
  {
@@ -425,16 +435,66 @@  valpy_lazy_string (PyObject *self, PyObject *args,  
PyObject *kw)
  				    &user_encoding, &length))
      return NULL;

+  if (length < -1)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Invalid length."));
+      return NULL;
+    }
+
    TRY
      {
        struct cleanup *cleanup = make_cleanup_value_free_to_mark  
(value_mark ());
+      struct type *type, *realtype;
+      CORE_ADDR addr;

-      if (TYPE_CODE (value_type (value)) == TYPE_CODE_PTR)
-	value = value_ind (value);
+      type = value_type (value);
+      realtype = check_typedef (type);
+
+      switch (TYPE_CODE (realtype))
+	{
+	case TYPE_CODE_ARRAY:
+	  {
+	    LONGEST array_length = -1;
+	    LONGEST low_bound, high_bound;
+
+	    /* PR 20786: There's no way to specify an array of length zero.
+	       Record a length of [0,-1] which is how Ada does it.  Anything
+	       we do is broken, but this one possible solution.  */
+	    if (get_array_bounds (realtype, &low_bound, &high_bound))
+	      array_length = high_bound - low_bound + 1;
+	    if (length == -1)
+	      length = array_length;
+	    else if (array_length == -1)
+	      {
+		type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype),
+						0, length - 1);
+	      }
+	    else if (length != array_length)
+	      {
+		/* We need to create a new array type with the
+		   specified length.  */
+		if (length > array_length)
+		  error (_("Length is larger than array size."));
+		type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype),
+						low_bound,
+						low_bound + length - 1);
+	      }
+	    addr = value_address (value);
+	    break;
+	  }
+	case TYPE_CODE_PTR:
+	  /* If a length is specified we defer creating an array of the
+	     specified width until we need to.  */
+	  addr = value_as_address (value);
+	  break;
+	default:
+	  /* Should flag an error here.  PR 20769.  */
+	  addr = value_address (value);
+	  break;
+	}

-      str_obj = gdbpy_create_lazy_string_object (value_address (value),  
length,
-						 user_encoding,
-						 value_type (value));
+      str_obj = gdbpy_create_lazy_string_object (addr, length,  
user_encoding,
+						 type);

        do_cleanups (cleanup);
      }
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 52038e3..5b6d339 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,13 @@ 
+2016-11-06  Doug Evans  <dje@google.com>
+
+	PR python/17728, python/18439, python/18779
+	* gdb.python/py-value.c (main) Delete locals sptr, sn.
+	* gdb.python/py-lazy-string.c (pointer): New typedef.
+	(main): New locals ptr, array, typedef_ptr.
+	* gdb.python/py-value.exp: Move lazy string tests to ...
+	* gdb.python/py-lazy-string.exp: ... here.  Add more tests for pointer,
+	array, typedef lazy strings.
+
  2016-10-28  Pedro Alves  <palves@redhat.com>

  	* gdb.base/maint.exp <maint info line-table w/o a file name>: Use
diff --git a/gdb/testsuite/gdb.python/py-lazy-string.c  
b/gdb/testsuite/gdb.python/py-lazy-string.c
index a6e1ee8..936b4e2 100644
--- a/gdb/testsuite/gdb.python/py-lazy-string.c
+++ b/gdb/testsuite/gdb.python/py-lazy-string.c
@@ -15,9 +15,14 @@ 
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.   
*/

+typedef const char *pointer;
+
  int
  main ()
  {
+  const char *ptr = "pointer";
+  const char array[] = "array";
+  pointer typedef_ptr = "typedef pointer";
    const char *null = 0;

    return 0; /* break here */
diff --git a/gdb/testsuite/gdb.python/py-lazy-string.exp  
b/gdb/testsuite/gdb.python/py-lazy-string.exp
index f57a482..e799376 100644
--- a/gdb/testsuite/gdb.python/py-lazy-string.exp
+++ b/gdb/testsuite/gdb.python/py-lazy-string.exp
@@ -34,9 +34,43 @@  if ![runto_main ] {
  gdb_breakpoint [gdb_get_line_number "break here"]
  gdb_continue_to_breakpoint "break here"

-gdb_test_no_output "python null = gdb.parse_and_eval(\"null\")"
+gdb_py_test_silent_cmd "python null = gdb.parse_and_eval(\"null\")" "get  
null value" 1

-gdb_test "python print(null.lazy_string(length=0).value())" \
-    "gdb.MemoryError: Cannot create a value from NULL.*Error while  
executing Python code."
+gdb_py_test_silent_cmd "python nullstr =  
null.lazy_string(length=0)" "create a null lazy string" 1
+gdb_test "python print (nullstr.length)" "0" "null lazy string length"
+gdb_test "python print (nullstr.address)" "0" "null lazy string address"
+gdb_test "python print (nullstr.type)" "const char \\*" "null lazy string  
type"
+gdb_test "python print(nullstr.value())" \
+    "gdb.MemoryError: Cannot create a value from NULL.*Error while  
executing Python code." \
+    "create value from NULL"
  gdb_test "python print(null.lazy_string(length=3).value())" \
-    "gdb.MemoryError: Cannot create a lazy string with address 0x0, and a  
non-zero length.*Error while executing Python code."
+    "gdb.MemoryError: Cannot create a lazy string with address 0x0, and a  
non-zero length.*Error while executing Python code." \
+    "null lazy string with non-zero length"
+gdb_test "python print(null.lazy_string(length=-2))" \
+    "ValueError: Invalid length.*Error while executing Python code." \
+    "bad length"
+
+foreach var_spec { { "ptr" "pointer" "const char \\*" -1 } \
+		   { "array" "array" "const char \\[6\\]" 6 } \
+		   { "typedef_ptr" "typedef pointer" "pointer" -1 } } {
+    set var [lindex $var_spec 0]
+    set value [lindex $var_spec 1]
+    set type [lindex $var_spec 2]
+    set length [lindex $var_spec 3]
+    with_test_prefix $var {
+	gdb_test "print $var" "\"$value\""
+	gdb_py_test_silent_cmd "python $var = gdb.history (0)" "get value from  
history" 1
+	gdb_py_test_silent_cmd "python l$var = $var.lazy_string()" "acquire lazy  
string" 1
+	gdb_test "python print ($var.type)" "$type" "string type name equality"
+	gdb_test "python print (l$var.type)" "$type" "lazy-string type name  
equality"
+	gdb_test "python print (l$var.length)" "$length" "lazy string length"
+	gdb_test "python print (l$var.value())" "\"$value\"" "lazy string value"
+	gdb_py_test_silent_cmd "python l2$var =  
$var.lazy_string(length=2)" "acquire lazy string, length 2" 1
+	gdb_test "python print (l2$var.length)" "2" "lazy string length 2"
+	gdb_test "python print (l2$var.value())" "\"[string range $value 0  
1]\"" "lazy string length 2 value"
+	# This test will have to wait until gdb can handle it. There's no way,
+	# currently, to internally specify an array of length zero in the C
+	# language support.  PR 20786
+	#gdb_test "python print  
($var.lazy_string(length=0).value())" "\"\"" "empty lazy string value"
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-value.c  
b/gdb/testsuite/gdb.python/py-value.c
index 8a8ace6..77c3610 100644
--- a/gdb/testsuite/gdb.python/py-value.c
+++ b/gdb/testsuite/gdb.python/py-value.c
@@ -90,13 +90,11 @@  main (int argc, char *argv[])
    char nullst[17] = "divide\0et\0impera";
    void (*fp1) (void)  = &func1;
    int  (*fp2) (int, int) = &func2;
-  const char *sptr = "pointer";
    const char *embed = "embedded x\201\202\203\204";
    int a[3] = {1,2,3};
    int *p = a;
    int i = 2;
    int *ptr_i = &i;
-  const char *sn = 0;
    struct str *xstr;

    /* Prevent gcc from optimizing argv[] out.  */
diff --git a/gdb/testsuite/gdb.python/py-value.exp  
b/gdb/testsuite/gdb.python/py-value.exp
index 89be659..68ad76d 100644
--- a/gdb/testsuite/gdb.python/py-value.exp
+++ b/gdb/testsuite/gdb.python/py-value.exp
@@ -313,29 +313,6 @@  proc test_value_in_inferior {} {
      "read string beyond declared size"
  }

-proc test_lazy_strings {} {
-
-  global hex
-
-  gdb_test "print sptr" "\"pointer\""
-  gdb_py_test_silent_cmd "python sptr = gdb.history (0)" "Get value from  
history" 1
-
-  gdb_py_test_silent_cmd "python lstr = sptr.lazy_string()" "Aquire lazy  
string" 1
-  gdb_test "python print (lstr.type)" "const char \*." "Test lazy-string  
type name equality"
-  gdb_test "python print (sptr.type)" "const char \*." "Test string type  
name equality"
-
-  # Prevent symbol on address 0x0 being printed.
-  gdb_test_no_output "set print symbol off"
-  gdb_test "print sn" "0x0"
-
-  gdb_py_test_silent_cmd "python snptr = gdb.history (0)" "Get value from  
history" 1
-  gdb_test "python snstr = snptr.lazy_string(length=5)" ".*Cannot create a  
lazy string with address.*" "Test lazy string"
-  gdb_py_test_silent_cmd "python snstr =  
snptr.lazy_string(length=0)" "Succesfully create a lazy string" 1
-  gdb_test "python print (snstr.length)" "0" "Test lazy string length"