From patchwork Mon Nov 7 01:14:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 17240 Received: (qmail 77825 invoked by alias); 7 Nov 2016 01:14:52 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 77771 invoked by uid 89); 7 Nov 2016 01:14:49 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-4.5 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 spammy=xfree, recorded, divide, impose X-HELO: mail-pf0-f201.google.com Received: from mail-pf0-f201.google.com (HELO mail-pf0-f201.google.com) (209.85.192.201) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 07 Nov 2016 01:14:39 +0000 Received: by mail-pf0-f201.google.com with SMTP id a8so143061pfg.1 for ; Sun, 06 Nov 2016 17:14:38 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:message-id:date:subject:from:to; bh=vB8Nu8ipWRJ8QSqHaO6pC1Z9d1sIaagLSQd8Dxz/+y0=; b=Ej0bc6uhhtipufLk34a2uqcsNqhnJP1L7W+dnmazcqJa03P6I8W/KEz22C9vrT9Ywh qpK8y+CufBcSfnx0rj9Wtp1yW0y+UH50dohf9qqrVj/3QipjMynqj5F7z+UTKzheQTep jsLL6AcRtyfwJPUsF9CJLC77TOaqjNb+068WiaULh8yez+wSuLA1m1wRJejrUVYrqR1Q CJFfdWtNXPR9ycaYDzak+XIIYY+CkqkQrwICh7mVnOCf7nPkc6TsXWQnPjy2GKZ8Ys1L P2uHdH9azK1kPnvT3w+dAtvYs3bdljc8DSswtSQJJjUAYdBrO4I2sfEgZ2eZRM5zd5Gc AdaQ== X-Gm-Message-State: ABUngvciK061WdVSNOPWiJgRWRtjWzk4iotVNacyxGH4IJw0p2vX9m6vDERAwvBbpya1MSuu9ZvueOXqGY5MO4pEwUvevQBeWlhj1Ytyoco7Kn6Z55pceBt4Mkb5Z/9nR7b0YPd4+gWltautFbAPRhuWUpC4mh4uRQopcrPEtErvYfpMITJsyQ== MIME-Version: 1.0 X-Received: by 10.98.26.139 with SMTP id a133mr1437582pfa.18.1478481277580; Sun, 06 Nov 2016 17:14:37 -0800 (PST) Message-ID: <94eb2c0339f25be7660540abbfab@google.com> Date: Mon, 07 Nov 2016 01:14:37 +0000 Subject: [PATCH 2/4] Fix some aspects of python lazy string handling: python From: Doug Evans To: gdb-patches@sourceware.org, eliz@gnu.org, pmuldoon@redhat.com X-IsSubscribed: yes Hi. This is the python side of the patch set. 2016-11-06 Doug Evans 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. 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 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 + + 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 * gdb.base/maint.exp : 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 . */ +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"