This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[patch] [VLA 2/2] Interpret FIELD_LOC_KIND_DWARF_BLOCK fields
- From: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- To: gdb-patches at sourceware dot org
- Cc: Joel Brobecker <brobecker at adacore dot com>, Ulrich Weigand <uweigand at de dot ibm dot com>, Jim Blandy <jimb at red-bean dot com>, Tobias Burnus <burnus at net-b dot de>
- Date: Tue, 13 Jan 2009 02:41:22 +0100
- Subject: [patch] [VLA 2/2] Interpret FIELD_LOC_KIND_DWARF_BLOCK fields
Hi,
this patch provides the interpreter for simple C variable length arrays (VLA).
check_typedef() will contain a memory leak with this patch. This has been
fixed by Tom Tromey's + mine <type> garbage collector to be posted later from:
http://sourceware.org/gdb/wiki/ArcherBranchManagement
archer-jankratochvil-type-refcount
No regressions for both patches on x86_64-unknown-linux-gnu, Fedora gcc-4.3.2-7.
Thanks,
Jan
gdb/
2009-01-13 Jan Kratochvil <jan.kratochvil@redhat.com>
Interpret FIELD_LOC_KIND_DWARF_BLOCK fields.
* c-typeprint.c (c_type_print_varspec_suffix): Display a marker for the
variable length arrays.
* dwarf2loc.c (dwarf2_evaluate_loc_desc): Wrap the function body by
a cleanup block. Move the BATON variable with the DATA and SIZE
evaluation to ...
(dwarf_expr_prep_ctx): ... a new function here.
(dwarf_locexpr_baton_eval): New function.
* dwarf2loc.h (dwarf_locexpr_baton_eval): New declaration.
* dwarf2read.c (read_subrange_type <dwarf2_attr_block>): Set
TYPE_DYNAMIC for RANGE_TYPE.
* gdbtypes.c: New include dwarf2loc.h.
(create_array_type): Do not call get_discrete_bounds for TYPE_DYNAMIC
types. Fix/remove TYPE_TARGET_STUB which was set for static zero length
arrays. Call complaint for negative length static arrays. Set
TYPE_DYNAMIC appropriately for new RESULT_TYPE.
(check_typedef): Make TYPE_DYNAMIC types parameters static.
(create_copied_types_hash): Permit NULL OBJFILE. Move the comment.
(copy_type_recursive <FIELD_LOC_KIND_DWARF_BLOCK>): New.
(copy_type_recursive): Clear TYPE_DYNAMIC for NEW_TYPE.
* gdbtypes.h (TYPE_DYNAMIC): New define.
(struct main_type): New field flag_dynamic.
(TYPE_LENGTH): Update the comment for array types.
gdb/testsuite/
2009-01-13 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.base/vla.c, gdb.base/vla.exp: New.
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/c-typeprint.c 2009-01-11 17:09:13.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/c-typeprint.c 2009-01-12 14:26:54.000000000 +0100
@@ -559,8 +559,13 @@ c_type_print_varspec_suffix (struct type
fprintf_filtered (stream, ")");
fprintf_filtered (stream, "[");
- if (TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0
- && !TYPE_ARRAY_UPPER_BOUND_IS_UNDEFINED (type))
+ if (TYPE_DYNAMIC (TYPE_INDEX_TYPE (type)))
+ {
+ /* Do not translate it as it is a part of the printed source. */
+ fprintf_filtered (stream, "variable");
+ }
+ else if (TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0
+ && !TYPE_ARRAY_UPPER_BOUND_IS_UNDEFINED (type))
fprintf_filtered (stream, "%d",
(TYPE_LENGTH (type)
/ TYPE_LENGTH (TYPE_TARGET_TYPE (type))));
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/dwarf2loc.c 2009-01-11 17:09:14.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/dwarf2loc.c 2009-01-12 22:43:25.000000000 +0100
@@ -191,6 +191,67 @@ dwarf_expr_tls_address (void *baton, COR
return target_translate_tls_address (debaton->objfile, offset);
}
+/* Evaluate DWARF expression at DATA ... DATA + SIZE with its result readable
+ by dwarf_expr_fetch (RETVAL, 0). FRAME parameter can be left NULL to call
+ get_selected_frame to get its value. Freeing of returned dwarf_expr_context
+ is remembered on the current cleanup chain. */
+
+static struct dwarf_expr_context *
+dwarf_expr_prep_ctx (struct frame_info *frame, gdb_byte *data,
+ unsigned short size, struct dwarf2_per_cu_data *per_cu)
+{
+ struct dwarf_expr_context *ctx;
+ struct dwarf_expr_baton baton;
+
+ if (!frame)
+ frame = get_selected_frame (NULL);
+
+ baton.frame = frame;
+ baton.objfile = dwarf2_per_cu_objfile (per_cu);
+
+ ctx = new_dwarf_expr_context ();
+ ctx->gdbarch = get_objfile_arch (baton.objfile);
+ ctx->addr_size = dwarf2_per_cu_addr_size (per_cu);
+ ctx->baton = &baton;
+ ctx->read_reg = dwarf_expr_read_reg;
+ ctx->read_mem = dwarf_expr_read_mem;
+ ctx->get_frame_base = dwarf_expr_frame_base;
+ ctx->get_tls_address = dwarf_expr_tls_address;
+
+ make_cleanup ((make_cleanup_ftype *) free_dwarf_expr_context, ctx);
+
+ dwarf_expr_eval (ctx, data, size);
+
+ /* The field has been used only above during dwarf_expr_eval. */
+ ctx->baton = NULL;
+
+ return ctx;
+}
+
+/* Evaluate DWARF expression at DLBATON expecting it produces exactly one
+ CORE_ADDR result on the DWARF stack stack. */
+
+CORE_ADDR
+dwarf_locexpr_baton_eval (struct dwarf2_locexpr_baton *dlbaton)
+{
+ struct dwarf_expr_context *ctx;
+ CORE_ADDR retval;
+ struct cleanup *back_to = make_cleanup (null_cleanup, 0);
+
+ ctx = dwarf_expr_prep_ctx (NULL, dlbaton->data, dlbaton->size,
+ dlbaton->per_cu);
+ if (ctx->num_pieces > 0)
+ error (_("DW_OP_*piece is unsupported for DW_FORM_block"));
+ else if (ctx->in_reg)
+ error (_("Register result is unsupported for DW_FORM_block"));
+
+ retval = dwarf_expr_fetch (ctx, 0);
+
+ do_cleanups (back_to);
+
+ return retval;
+}
+
/* Evaluate a location description, starting at DATA and with length
SIZE, to find the current location of variable VAR in the context
of FRAME. */
@@ -201,8 +262,8 @@ dwarf2_evaluate_loc_desc (struct symbol
{
struct gdbarch *arch = get_frame_arch (frame);
struct value *retval;
- struct dwarf_expr_baton baton;
struct dwarf_expr_context *ctx;
+ struct cleanup *back_to = make_cleanup (null_cleanup, 0);
if (size == 0)
{
@@ -212,19 +273,8 @@ dwarf2_evaluate_loc_desc (struct symbol
return retval;
}
- baton.frame = frame;
- baton.objfile = dwarf2_per_cu_objfile (per_cu);
-
- ctx = new_dwarf_expr_context ();
- ctx->gdbarch = get_objfile_arch (baton.objfile);
- ctx->addr_size = dwarf2_per_cu_addr_size (per_cu);
- ctx->baton = &baton;
- ctx->read_reg = dwarf_expr_read_reg;
- ctx->read_mem = dwarf_expr_read_mem;
- ctx->get_frame_base = dwarf_expr_frame_base;
- ctx->get_tls_address = dwarf_expr_tls_address;
+ ctx = dwarf_expr_prep_ctx (frame, data, size, per_cu);
- dwarf_expr_eval (ctx, data, size);
if (ctx->num_pieces > 0)
{
int i;
@@ -270,7 +320,7 @@ dwarf2_evaluate_loc_desc (struct symbol
set_value_initialized (retval, ctx->initialized);
- free_dwarf_expr_context (ctx);
+ do_cleanups (back_to);
return retval;
}
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/dwarf2loc.h 2009-01-11 17:09:14.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/dwarf2loc.h 2009-01-12 10:37:22.000000000 +0100
@@ -72,4 +72,7 @@ struct dwarf2_loclist_baton
extern const struct symbol_ops dwarf2_locexpr_funcs;
extern const struct symbol_ops dwarf2_loclist_funcs;
+extern CORE_ADDR dwarf_locexpr_baton_eval
+ (struct dwarf2_locexpr_baton *dlbaton);
+
#endif /* dwarf2loc.h */
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/dwarf2read.c 2009-01-12 22:57:30.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/dwarf2read.c 2009-01-12 22:57:29.000000000 +0100
@@ -5171,6 +5171,7 @@ read_subrange_type (struct die_info *die
SET_FIELD_DWARF_BLOCK (TYPE_FIELD (range_type, 0),
dwarf_block_to_locexpr_baton (DW_BLOCK (attr),
cu));
+ TYPE_DYNAMIC (range_type) = 1;
break;
}
@@ -5188,6 +5189,7 @@ read_subrange_type (struct die_info *die
SET_FIELD_DWARF_BLOCK (TYPE_FIELD (range_type, 1),
dwarf_block_to_locexpr_baton (DW_BLOCK (attr),
cu));
+ TYPE_DYNAMIC (range_type) = 1;
break;
}
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/gdbtypes.c 2009-01-11 17:09:15.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/gdbtypes.c 2009-01-13 00:54:24.000000000 +0100
@@ -38,6 +38,7 @@
#include "cp-abi.h"
#include "gdb_assert.h"
#include "hashtab.h"
+#include "dwarf2loc.h"
/* These variables point to the objects
representing the predefined C data types. */
@@ -822,17 +823,6 @@ create_array_type (struct type *result_t
}
TYPE_CODE (result_type) = TYPE_CODE_ARRAY;
TYPE_TARGET_TYPE (result_type) = element_type;
- if (get_discrete_bounds (range_type, &low_bound, &high_bound) < 0)
- low_bound = high_bound = 0;
- CHECK_TYPEDEF (element_type);
- /* Be careful when setting the array length. Ada arrays can be
- empty arrays with the high_bound being smaller than the low_bound.
- In such cases, the array length should be zero. */
- if (high_bound < low_bound)
- TYPE_LENGTH (result_type) = 0;
- else
- TYPE_LENGTH (result_type) =
- TYPE_LENGTH (element_type) * (high_bound - low_bound + 1);
TYPE_NFIELDS (result_type) = 1;
TYPE_FIELDS (result_type) =
(struct field *) TYPE_ALLOC (result_type, sizeof (struct field));
@@ -840,11 +830,44 @@ create_array_type (struct type *result_t
TYPE_INDEX_TYPE (result_type) = range_type;
TYPE_VPTR_FIELDNO (result_type) = -1;
- /* TYPE_FLAG_TARGET_STUB will take care of zero length arrays */
- if (TYPE_LENGTH (result_type) == 0)
- TYPE_TARGET_STUB (result_type) = 1;
+ /* The resulting array type needs to have evaluated the original TYPE_DYNAMIC
+ RANGE_TYPE expressions on each check_typedef call. Therefore we mark
+ RESULT_TYPE by TYPE_TARGET_STUB and TYPE_LENGTH can be left unset for the
+ later check_typedef call by the RESULT_TYPE consumer.
+ TYPE_RANGE_LOWER_BOUND_IS_UNDEFINED or TYPE_RANGE_UPPER_BOUND_IS_UNDEFINED
+ will result in a non-TYPE_TARGET_STUB array with TYPE_LENGTH set to 0. */
+ if (TYPE_TARGET_STUB (element_type)
+ || TYPE_DYNAMIC (range_type)
+ || get_discrete_bounds (range_type, &low_bound, &high_bound) < 0)
+ {
+ TYPE_TARGET_STUB (result_type) = 1;
+ TYPE_LENGTH (result_type) = 0;
+ }
+ else if (high_bound + 1 < low_bound)
+ {
+ complaint (&symfile_complaints, _("Range type %s is invalid"),
+ TYPE_NAME (range_type) ? TYPE_NAME (range_type) : "<NULL>");
+ TYPE_LENGTH (result_type) = 0;
+ }
+ else
+ {
+ CHECK_TYPEDEF (element_type);
+ TYPE_LENGTH (result_type) = TYPE_LENGTH (element_type)
+ * (high_bound - low_bound + 1);
+ }
- return (result_type);
+ /* Propagate FIELD_LOC_KIND_DWARF_BLOCK bounds. */
+ if (TYPE_DYNAMIC (range_type))
+ TYPE_DYNAMIC (result_type) = 1;
+
+ /* Multidimensional TYPE_DYNAMIC arrays need to have all the outer dimensions
+ also TYPE_DYNAMIC. check_typedef will have to update the TYPE_TARGET_TYPE
+ pointer in the outer dimension with the new type (+main_type) copy
+ containing the constant evaluated parameters. */
+ if (TYPE_DYNAMIC (element_type))
+ TYPE_DYNAMIC (result_type) = 1;
+
+ return result_type;
}
/* Create a string type using either a blank type supplied in
@@ -1512,6 +1535,21 @@ check_typedef (struct type *type)
}
}
+ /* copy_type_recursive automatically makes the resulting type containing only
+ constant values expected by the callers of this function.
+
+ FIXME: New TYPE memory leaks here on each check_typedef call. */
+ if (TYPE_DYNAMIC (type))
+ {
+ htab_t copied_types;
+
+ copied_types = create_copied_types_hash (NULL);
+ type = copy_type_recursive (TYPE_OBJFILE (type), type, copied_types);
+ htab_delete (copied_types);
+
+ gdb_assert (TYPE_DYNAMIC (type) == 0);
+ }
+
if (TYPE_TARGET_STUB (type))
{
struct type *range_type;
@@ -2915,16 +2953,21 @@ type_pair_eq (const void *item_lhs, cons
}
/* Allocate the hash table used by copy_type_recursive to walk
- types without duplicates. We use OBJFILE's obstack, because
- OBJFILE is about to be deleted. */
+ types without duplicates. */
htab_t
create_copied_types_hash (struct objfile *objfile)
{
- return htab_create_alloc_ex (1, type_pair_hash, type_pair_eq,
- NULL, &objfile->objfile_obstack,
- hashtab_obstack_allocate,
- dummy_obstack_deallocate);
+ if (objfile == NULL)
+ return htab_create (1, type_pair_hash, type_pair_eq, NULL);
+ else
+ {
+ /* Use OBJFILE's obstack, because OBJFILE is about to be deleted. */
+ return htab_create_alloc_ex (1, type_pair_hash, type_pair_eq,
+ NULL, &objfile->objfile_obstack,
+ hashtab_obstack_allocate,
+ dummy_obstack_deallocate);
+ }
}
/* Recursively copy (deep copy) TYPE, if it is associated with
@@ -3010,6 +3053,12 @@ copy_type_recursive (struct objfile *obj
xstrdup (TYPE_FIELD_STATIC_PHYSNAME (type,
i)));
break;
+ case FIELD_LOC_KIND_DWARF_BLOCK:
+ /* `struct dwarf2_locexpr_baton' is too bound to its objfile so
+ it is expected to be made constant by check_typedef. */
+ SET_FIELD_BITPOS (TYPE_FIELD (new_type, i),
+ dwarf_locexpr_baton_eval (TYPE_FIELD_DWARF_BLOCK (type, i)));
+ break;
default:
internal_error (__FILE__, __LINE__,
_("Unexpected type field location kind: %d"),
@@ -3018,6 +3067,9 @@ copy_type_recursive (struct objfile *obj
}
}
+ /* FIELD_LOC_KIND_DWARF_BLOCK fields have been possibly converted. */
+ TYPE_DYNAMIC (new_type) = 0;
+
/* Copy pointers to other types. */
if (TYPE_TARGET_TYPE (type))
TYPE_TARGET_TYPE (new_type) =
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/gdbtypes.h 2009-01-11 19:37:36.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/gdbtypes.h 2009-01-13 00:25:22.000000000 +0100
@@ -209,6 +209,14 @@ enum type_instance_flag_value
#define TYPE_TARGET_STUB(t) (TYPE_MAIN_TYPE (t)->flag_target_stub)
+/* Type needs to be made static by check_typedef creating a new copy no longer
+ being TYPE_DYNAMIC. Difference to TYPE_STUB or TYPE_TARGET_STUB is that the
+ original type must be preserved intact. Check_typedef must return its copy
+ (of main_type, not just type). For example FIELD_LOC_KIND_DWARF_BLOCK
+ fields will get resolved to the static form FIELD_LOC_KIND_BITPOS. */
+
+#define TYPE_DYNAMIC(t) (TYPE_MAIN_TYPE (t)->flag_dynamic)
+
/* Static type. If this is set, the corresponding type had
* a static modifier.
* Note: This may be unnecessary, since static data members
@@ -352,6 +360,7 @@ struct main_type
unsigned int flag_stub_supported : 1;
unsigned int flag_nottext : 1;
unsigned int flag_fixed_instance : 1;
+ unsigned int flag_dynamic : 1;
/* Number of fields described for this type. This field appears at
this location because it packs nicely here. */
@@ -795,9 +804,9 @@ extern void allocate_cplus_struct_type (
#define TYPE_POINTER_TYPE(thistype) (thistype)->pointer_type
#define TYPE_REFERENCE_TYPE(thistype) (thistype)->reference_type
#define TYPE_CHAIN(thistype) (thistype)->chain
-/* Note that if thistype is a TYPEDEF type, you have to call check_typedef.
- But check_typedef does set the TYPE_LENGTH of the TYPEDEF type,
- so you only have to call check_typedef once. Since allocate_value
+/* Note that if thistype is a TYPEDEF or ARRAY type, you have to call
+ check_typedef. But check_typedef does set the TYPE_LENGTH of the TYPEDEF
+ type, so you only have to call check_typedef once. Since allocate_value
calls check_typedef, TYPE_LENGTH (VALUE_TYPE (X)) is safe. */
#define TYPE_LENGTH(thistype) (thistype)->length
#define TYPE_OBJFILE(thistype) TYPE_MAIN_TYPE(thistype)->objfile
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/testsuite/gdb.base/vla.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/testsuite/gdb.base/vla.c 2009-01-13 00:14:34.000000000 +0100
@@ -0,0 +1,55 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 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/>. */
+
+#include <string.h>
+
+void
+marker (void)
+{
+}
+
+void
+bar (char *a, char *b, char *c, int size)
+{
+ memset (a, '1', size);
+ memset (b, '2', size);
+ memset (c, '3', 48);
+}
+
+void
+foo (int size)
+{
+ char temp1[size];
+ char temp3[48];
+
+ temp1[size - 1] = '\0';
+ {
+ char temp2[size];
+
+ bar (temp1, temp2, temp3, size);
+
+ marker (); /* break-here */
+ }
+}
+
+int
+main (void)
+{
+ foo (26);
+ foo (78);
+ return 0;
+}
--- gdb-cvs-dwarf2_get_attr_constant_value/gdb/testsuite/gdb.base/vla.exp 1970-01-01 01:00:00.000000000 +0100
+++ gdb-cvs-dwarf_block-eval/gdb/testsuite/gdb.base/vla.exp 2009-01-13 01:29:08.000000000 +0100
@@ -0,0 +1,81 @@
+# Copyright 2009 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/>.
+
+# Test C variable length arrays.
+
+set srcfile vla.c
+
+if [prepare_for_testing vla.exp "vla"] {
+ return -1
+}
+if ![runto_main] {
+ untested vla.exp
+ return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here"
+
+# Set if the compiler has produced no DW_FORM_block*.
+set xfail 0
+
+set test "first: whatis temp1"
+gdb_test_multiple "whatis temp1" $test {
+ -re "type = char \\\[\\\]\r\n$gdb_prompt $" {
+ # Compiler has produced no DW_FORM_block*?
+ setup_xfail "*-*-*"
+ fail $test
+
+ set xfail 1
+ }
+ -re "type = char \\\[variable\\\]\r\n$gdb_prompt $" {
+ pass $test
+ }
+}
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "whatis temp2" "type = char \\\[variable\\\]" "first: whatis temp2"
+gdb_test "whatis temp3" "type = char \\\[48\\\]" "first: whatis temp3"
+
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "ptype temp1" "type = char \\\[26\\\]" "first: ptype temp1"
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "ptype temp2" "type = char \\\[26\\\]" "first: ptype temp2"
+gdb_test "ptype temp3" "type = char \\\[48\\\]" "first: ptype temp3"
+
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "p temp1" "\\$\[0-9\]+ = '1' <repeats 26 times>" "first: print temp1"
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "p temp2" "\\$\[0-9\]+ = '2' <repeats 26 times>" "first: print temp2"
+gdb_test "p temp3" "\\$\[0-9\]+ = '3' <repeats 48 times>" "first: print temp3"
+
+gdb_continue_to_breakpoint "break-here"
+
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "whatis temp1" "type = char \\\[variable\\\]" "second: whatis temp1"
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "whatis temp2" "type = char \\\[variable\\\]" "second: whatis temp2"
+gdb_test "whatis temp3" "type = char \\\[48\\\]" "second: whatis temp3"
+
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "ptype temp1" "type = char \\\[78\\\]" "second: ptype temp1"
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "ptype temp2" "type = char \\\[78\\\]" "second: ptype temp2"
+gdb_test "ptype temp3" "type = char \\\[48\\\]" "second: ptype temp3"
+
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "p temp1" "\\$\[0-9\]+ = '1' <repeats 78 times>" "second: print temp1"
+if $xfail { setup_xfail "*-*-*" }
+gdb_test "p temp2" "\\$\[0-9\]+ = '2' <repeats 78 times>" "second: print temp2"
+gdb_test "p temp3" "\\$\[0-9\]+ = '3' <repeats 48 times>" "second: print temp3"