[RFC 06/12] entryval: Display @entry parameters in bt full

Jan Kratochvil
2011-07-18T20:19:42Z

Hi,

this is a first kind of access to the entry values. A (non-testcase) demo is:

#8 0x000000000048c50d in execute_command (p=0x22b573b "", from_tty=1) at top.c:438
p@entry = 0x22b5720 "maintenance internal-error "
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
arg = <optimized out>
cleanup = 0x28333b0
c = 0x24996d0
flang = <optimized out>
warned = 0
line = 0x22b5720 "maintenance internal-error "

p@entry is not particularly useful here because the `line' local variable
contains the same. One can imagine in other real world applications it can be
useful - for a backtrace sent by user where you do not have a reproducer and/or
the core file (the case of ABRT bugreports).

The parameter@entry lines are not displayed if they are not useful. Therefore
if they would display the same value as the current parameter value or if GDB
cannot successfully determine the entry value.

It is displayed by `bt full' and `info args'. RFC is whether it is not too
verbose and/or if it should not be displayed some way even with normal `bt'.
Also whether there should be some `set' variable to forget about @entry values.

Thanks,
Jan

gdb/
2011-07-18 Jan Kratochvil <jan.kratochvil@redhat.com>

Display @entry parameter values (without references).
* doc/gdb.texinfo (backtrace full, info args): Mention @entry suffix.
* dwarf2expr.c (dwarf_block_to_fb_offset): New function.
* dwarf2expr.h (dwarf_block_to_fb_offset): New declaration.
* dwarf2loc.c (dwarf2_find_location_expression): Support location list
entry record.
(dwarf_entry_parameter_to_value): New function.
(dwarf2_locexpr_funcs): New NULL for read_variable_at_entry.
(value_of_dwarf_reg_entry, loclist_read_variable_at_entry): New
functions.
(dwarf2_loclist_funcs): Install loclist_read_variable_at_entry.
* f-valprint.c (info_common_command): Add PVAVD_IS_NOT_ARGUMENT to the
print_variable_and_value call.
* printcmd.c (print_variable_and_value): New parameter print_argument,
describe it. Print @entry parameter value when appropriate.
* stack.c (struct print_variable_and_value_data): New field
print_argument.
(do_print_variable_and_value): Pass the print_argument parameter.
(print_frame_local_vars): Change the parameter num_tabs to from_frame.
Print @entry parameters if FROM_FRAME.
(print_frame_arg_vars): Specify PVAVD_ARGUMENT_PRINT_BOTH.
* symtab.h (struct symbol_computed_ops): New field
read_variable_at_entry.
* value.h (enum print_argument): New.
(print_variable_and_value): New parameter print_argument.

gdb/testsuite/
2011-07-18 Jan Kratochvil <jan.kratochvil@redhat.com>

Display @entry parameter values (without references).
* gdb.arch/amd64-entry-value.cc (stacktest, reference): New functions.
(main): New variable refvar, call stacktest and reference.
* gdb.arch/amd64-entry-value.exp: New breakpoints stacktest,
breakhere_stacktest, reference and breakhere_reference.
(entry: bt full): Update for @entry.
(entry_stack: stacktest, entry_stack: bt full at entry)
(entry_stack: breakhere_stacktest, entry_stack: bt full)
(entry_stack: p s1, entry_stack: p s2, entry_reference: reference)
(entry_reference: bt full at entry)
(entry_reference: breakhere_reference, entry_reference: bt full)
(entry_reference: ptype refparam, entry_reference: p refparam)
(entry_reference: p refcopy): New tests.

--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -5947,8 +5947,13 @@ Similar, but print only the outermost @var{n} frames.
@itemx bt full
@itemx bt full @var{n}
@itemx bt full -@var{n}
-Print the values of the local variables also. @var{n} specifies the
-number of frames to print, as described above.
+Print the values of the local variables also.
+
+Names with appended @code{@@entry} show values of function parameters at the
+time the function got called. The @code{@@entry} parameter line is not shown
+when the entry value is not available.
+
+@var{n} specifies the number of frames to print, as described above.
@end table

@kindex where
@@ -6223,6 +6228,10 @@ architectures) that you specify in the @code{frame} command.
@item info args
Print the arguments of the selected frame, each on a separate line.

+Names with appended @code{@@entry} show values of function parameters at the
+time the function got called. The @code{@@entry} parameter line is not shown
+when the entry value is not available.
+
@item info locals
@kindex info locals
Print the local variables of the selected frame, each on a separate
--- a/gdb/dwarf2expr.c
+++ b/gdb/dwarf2expr.c
@@ -602,6 +602,30 @@ dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
return 1;
}

+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_fbreg(X) fill
+ in FB_OFFSET_RETURN with the X offset and return 1. Otherwise return 0. */
+
+int
+dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end,
+ CORE_ADDR *fb_offset_return)
+{
+ LONGEST fb_offset;
+
+ if (buf_end <= buf)
+ return 0;
+
+ if (*buf != DW_OP_fbreg)
+ return 0;
+ buf++;
+
+ buf = read_sleb128 (buf, buf_end, &fb_offset);
+ *fb_offset_return = fb_offset;
+ if (buf != buf_end || fb_offset != (LONGEST) *fb_offset_return)
+ return 0;
+
+ return 1;
+}
+
/* The engine for the expression evaluator. Using the context in CTX,
evaluate the expression between OP_PTR and OP_END. */

--- a/gdb/dwarf2expr.h
+++ b/gdb/dwarf2expr.h
@@ -270,6 +270,9 @@ int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
const gdb_byte *buf_end,
CORE_ADDR *sp_offset_return);

+int dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end,
+ CORE_ADDR *fb_offset_return);
+
int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf,
const gdb_byte *buf_end,
CORE_ADDR *deref_size_return);
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -121,6 +121,24 @@ dwarf2_find_location_expression (struct dwarf2_loclist_baton *baton,
length = extract_unsigned_integer (loc_ptr, 2, byte_order);
loc_ptr += 2;

+ if (low == high && pc == low)
+ {
+ /* This is entry PC record present only at entry point
+ of a function. Verify it is really the function entry point. */
+
+ struct block *pc_block = block_for_pc (pc);
+ struct symbol *pc_func = NULL;
+
+ if (pc_block)
+ pc_func = block_linkage_function (pc_block);
+
+ if (pc_func && pc == BLOCK_START (SYMBOL_BLOCK_VALUE (pc_func)))
+ {
+ *locexpr_length = length;
+ return loc_ptr;
+ }
+ }
+
if (pc >= low && pc < high)
{
*locexpr_length = length;
@@ -913,6 +931,35 @@ dwarf_entry_parameter_to_block (struct call_site_parameter *parameter,
return parameter->call_site_data_value;
}

+/* Return value for PARAMETER matching DEREF_SIZE, see
+ dwarf_entry_parameter_to_block for the description of these parameters.
+
+ TYPE and CALLER_FRAME specify how to evaluate the DWARF block into returned
+ struct value.
+
+ Function always returns non-NULL, non-optimized out value. It throws
+ NOT_FOUND_ERROR if it cannot resolve the value for any reason. */
+
+static struct value *
+dwarf_entry_parameter_to_value (struct call_site_parameter *parameter,
+ CORE_ADDR deref_size, struct type *type,
+ struct frame_info *caller_frame)
+{
+ struct dwarf2_locexpr_baton *dwarf_block;
+ gdb_byte *data;
+
+ dwarf_block = dwarf_entry_parameter_to_block (parameter, deref_size);
+
+ /* DW_AT_GNU_call_site_value is a DWARF expression, not a DWARF
+ location. */
+ data = alloca (dwarf_block->size + 1);
+ memcpy (data, dwarf_block->data, dwarf_block->size);
+ data[dwarf_block->size] = DW_OP_stack_value;
+
+ return dwarf2_evaluate_loc_desc (type, caller_frame, data,
+ dwarf_block->size + 1, dwarf_block->per_cu);
+}
+
/* Execute DWARF_BLOCK for caller of the CTX's frame. CTX must be of
dwarf_expr_ctx_funcs kind. See DWARF_REG, FB_OFFSET and DEREF_SIZE
description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
@@ -3500,6 +3547,7 @@ locexpr_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch,
evaluator. */
const struct symbol_computed_ops dwarf2_locexpr_funcs = {
locexpr_read_variable,
+ NULL, /* read_variable_at_entry */
locexpr_read_needs_frame,
locexpr_describe_location,
locexpr_tracepoint_var_ref
@@ -3530,6 +3578,64 @@ loclist_read_variable (struct symbol *symbol, struct frame_info *frame)
return val;
}

+/* Return value of parameter of TYPE at (callee) FRAME which at function entry
+ point. Parameter has been passed in DWARF_REG or FB_OFFSET, see their
+ description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
+
+ Function always returns non-NULL, non-optimized out value. It throws
+ NOT_FOUND_ERROR if it cannot resolve the value for any reason. */
+
+static struct value *
+value_of_dwarf_reg_entry (struct type *type, struct frame_info *frame,
+ int dwarf_reg, CORE_ADDR fb_offset)
+{
+ struct frame_info *caller_frame = get_prev_frame (frame);
+ struct call_site_parameter *parameter;
+
+ parameter = dwarf_expr_dwarf_reg_entry_value (frame, dwarf_reg, fb_offset);
+
+ return dwarf_entry_parameter_to_value (parameter, -1 /* deref_size */, type,
+ caller_frame);
+}
+
+/* Read variable SYMBOL like loclist_read_variable at (callee) FRAME's function
+ entry. SYMBOL should be a function parameter, otherwise NOT_FOUND_ERROR
+ will be thrown.
+
+ Function always returns non-NULL value, it may be marked optimized out if
+ inferior frame information is not available. It throws NOT_FOUND_ERROR if
+ it cannot resolve the parameter for any reason. */
+
+static struct value *
+loclist_read_variable_at_entry (struct symbol *symbol, struct frame_info *frame)
+{
+ struct dwarf2_loclist_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol);
+ struct value *val;
+ const gdb_byte *data;
+ size_t size;
+ int dwarf_reg;
+ CORE_ADDR deref_size, pc, fb_offset;
+
+ if (frame == NULL || !get_frame_func_if_available (frame, &pc))
+ return allocate_optimized_out_value (SYMBOL_TYPE (symbol));
+
+ data = dwarf2_find_location_expression (dlbaton, &size, pc);
+ if (data == NULL)
+ return allocate_optimized_out_value (SYMBOL_TYPE (symbol));
+
+ dwarf_reg = dwarf_block_to_dwarf_reg (data, data + size);
+ if (dwarf_reg != -1)
+ return value_of_dwarf_reg_entry (SYMBOL_TYPE (symbol), frame, dwarf_reg,
+ 0 /* unused */);
+
+ if (dwarf_block_to_fb_offset (data, data + size, &fb_offset))
+ return value_of_dwarf_reg_entry (SYMBOL_TYPE (symbol), frame, -1,
+ fb_offset);
+
+ error (_("DWARF-2 expression error: DW_OP_GNU_entry_value is supported only "
+ "for single DW_OP_reg* or for DW_OP_fbreg(*)"));
+}
+
/* Return non-zero iff we need a frame to evaluate SYMBOL. */
static int
loclist_read_needs_frame (struct symbol *symbol)
@@ -3649,6 +3755,7 @@ loclist_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch,
evaluator and location lists. */
const struct symbol_computed_ops dwarf2_loclist_funcs = {
loclist_read_variable,
+ loclist_read_variable_at_entry,
loclist_read_needs_frame,
loclist_describe_location,
loclist_tracepoint_var_ref
--- a/gdb/f-valprint.c
+++ b/gdb/f-valprint.c
@@ -610,7 +610,8 @@ info_common_command (char *comname, int from_tty)

while (entry != NULL)
{
- print_variable_and_value (NULL, entry->symbol, fi, gdb_stdout, 0);
+ print_variable_and_value (NULL, entry->symbol, fi, gdb_stdout, 0,
+ PVAVD_IS_NOT_ARGUMENT);
entry = entry->next;
}
}
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -1939,32 +1939,74 @@ clear_dangling_display_expressions (struct so_list *solib)
struct symbol. NAME is the name to print; if NULL then VAR's print
name will be used. STREAM is the ui_file on which to print the
value. INDENT specifies the number of indent levels to print
- before printing the variable name. */
+ before printing the variable name. PRINT_ARGUMENT specifies whether @entry
+ kind of function parameters should be printed. */

void
print_variable_and_value (const char *name, struct symbol *var,
struct frame_info *frame,
- struct ui_file *stream, int indent)
+ struct ui_file *stream, int indent,
+ enum print_argument print_argument)
{
volatile struct gdb_exception except;

if (!name)
name = SYMBOL_PRINT_NAME (var);

- fprintf_filtered (stream, "%s%s = ", n_spaces (2 * indent), name);
TRY_CATCH (except, RETURN_MASK_ERROR)
{
- struct value *val;
+ struct value *val, *entryval = NULL;
struct value_print_options opts;

val = read_var_value (var, frame);
get_user_print_options (&opts);
- common_val_print (val, stream, indent, &opts, current_language);
+
+ if (print_argument != PVAVD_IS_NOT_ARGUMENT
+ && SYMBOL_CLASS (var) == LOC_COMPUTED
+ && SYMBOL_COMPUTED_OPS (var)->read_variable_at_entry != NULL)
+ {
+ const struct symbol_computed_ops *ops;
+ unsigned len = TYPE_LENGTH (value_type (val));
+ volatile struct gdb_exception entryval_ex;
+
+ ops = SYMBOL_COMPUTED_OPS (var);
+
+ TRY_CATCH (entryval_ex, RETURN_MASK_ERROR)
+ {
+ entryval = ops->read_variable_at_entry (var, frame);
+ }
+
+ if (entryval_ex.reason < 0 || value_optimized_out (entryval))
+ entryval = NULL;
+ else
+ {
+ if (!value_optimized_out (val) && value_lazy (val))
+ value_fetch_lazy (val);
+ if (!value_optimized_out (val) && value_lazy (entryval))
+ value_fetch_lazy (entryval);
+ if (!value_optimized_out (val)
+ && value_available_contents_eq (val, 0, entryval, 0, len))
+ entryval = NULL;
+ }
+ }
+
+ if (print_argument != PVAVD_ARGUMENT_PRINT_ENTRYVAL_ONLY)
+ {
+ fprintf_filtered (stream, "%s%s = ", n_spaces (2 * indent), name);
+ common_val_print (val, stream, indent, &opts, current_language);
+ fputc_filtered ('\n', stream);
+ }
+ if (entryval)
+ {
+ fprintf_filtered (stream, "%s%s@entry = ", n_spaces (2 * indent),
+ name);
+ common_val_print (entryval, stream, indent, &opts, current_language);
+ fputc_filtered ('\n', stream);
+ }
}
if (except.reason < 0)
- fprintf_filtered(stream, "<error reading variable %s (%s)>", name,
- except.message);
- fprintf_filtered (stream, "\n");
+ fprintf_filtered (stream, "%s%s = <error reading variable %s (%s)>\n",
+ n_spaces (2 * indent), name, name, except.message);
}

/* printf "printf format string" ARG to STREAM. */
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1609,6 +1609,7 @@ struct print_variable_and_value_data
int num_tabs;
struct ui_file *stream;
int values_printed;
+ enum print_argument print_argument;
};

/* The callback for the locals and args iterators. */
@@ -1620,13 +1621,17 @@ do_print_variable_and_value (const char *print_name,
{
struct print_variable_and_value_data *p = cb_data;

- print_variable_and_value (print_name, sym,
- p->frame, p->stream, p->num_tabs);
+ print_variable_and_value (print_name, sym, p->frame, p->stream, p->num_tabs,
+ p->print_argument);
p->values_printed = 1;
}

+/* Show function local variables at FRAME. FROM_FRAME is 1 if the local
+ variables are printed after the function frame (parameter values) have been
+ printed, it is 0 otherwise. Print them to STREAM. */
+
static void
-print_frame_local_vars (struct frame_info *frame, int num_tabs,
+print_frame_local_vars (struct frame_info *frame, int from_frame,
struct ui_file *stream)
{
struct print_variable_and_value_data cb_data;
@@ -1648,10 +1653,24 @@ print_frame_local_vars (struct frame_info *frame, int num_tabs,
}

cb_data.frame = frame;
- cb_data.num_tabs = 4 * num_tabs;
+ cb_data.num_tabs = from_frame ? 4 : 0;
cb_data.stream = stream;
cb_data.values_printed = 0;

+ if (from_frame)
+ {
+ /* For the variables we display them from the innermost block. But for
+ parameters we need to fetch the outermost block still in the same
+ function. Stop at the first inlined function boundary, if any. */
+ struct symbol *func = get_frame_function (frame);
+
+ cb_data.print_argument = PVAVD_ARGUMENT_PRINT_ENTRYVAL_ONLY;
+ if (func)
+ iterate_over_block_arg_vars (SYMBOL_BLOCK_VALUE (func),
+ do_print_variable_and_value, &cb_data);
+ }
+
+ cb_data.print_argument = PVAVD_IS_NOT_ARGUMENT;
iterate_over_block_local_vars (block,
do_print_variable_and_value,
&cb_data);
@@ -1811,6 +1830,7 @@ print_frame_arg_vars (struct frame_info *frame, struct ui_file *stream)
cb_data.stream = gdb_stdout;
cb_data.values_printed = 0;

+ cb_data.print_argument = PVAVD_ARGUMENT_PRINT_BOTH;
iterate_over_block_arg_vars (SYMBOL_BLOCK_VALUE (func),
do_print_variable_and_value, &cb_data);

--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -533,6 +533,12 @@ struct symbol_computed_ops
struct value *(*read_variable) (struct symbol * symbol,
struct frame_info * frame);

+ /* Read variable SYMBOL like read_variable at (callee) FRAME's function
+ entry. SYMBOL should be a function parameter, otherwise NOT_FOUND_ERROR
+ will be thrown. */
+ struct value *(*read_variable_at_entry) (struct symbol *symbol,
+ struct frame_info *frame);
+
/* Return non-zero if we need a frame to find the value of the SYMBOL. */
int (*read_needs_frame) (struct symbol * symbol);

--- a/gdb/testsuite/gdb.arch/amd64-entry-value.cc
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.cc
@@ -113,10 +113,37 @@ self (int i)
}
}

+static void __attribute__((noinline, noclone))
+stacktest (int r1, int r2, int r3, int r4, int r5, int r6, int s1, int s2)
+{
+ s1 = 3;
+ s2 = 4;
+ e (v);
+asm ("breakhere_stacktest:");
+ e (v);
+}
+
+static void __attribute__((noinline, noclone))
+reference (int &refparam)
+{
+ int refcopy = refparam;
+
+ refparam = 10;
+asm ("breakhere_reference:");
+ e (v);
+}
+
int
main ()
{
+ int refvar;
+
d (30);
+ stacktest (1, 2, 3, 4, 5, 6, 11, 12);
+
+ refvar = 5;
+ reference (refvar);
+
if (v)
a (1);
else
--- a/gdb/testsuite/gdb.arch/amd64-entry-value.exp
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.exp
@@ -35,6 +35,10 @@ if ![runto_main] {
}

gdb_breakpoint "breakhere"
+gdb_breakpoint "stacktest"
+gdb_breakpoint "breakhere_stacktest"
+gdb_breakpoint "reference"
+gdb_breakpoint "breakhere_reference"

# Test @entry values for register passed parameters.
@@ -43,12 +47,68 @@ gdb_continue_to_breakpoint "entry: breakhere"

# (gdb) bt full
# #0 d (i=31) at gdb.arch/amd64-entry-value.cc:33
+# i@entry = 30
# #1 0x00000000004003da in main () at gdb.arch/amd64-entry-value.cc:56
-gdb_test "bt full" "^bt full\r\n#0 +d *\\(i=31\\) \[^\r\n\]*\r\nNo locals\\.\r\n#1 +0x\[0-9a-f\]+ in main .*" \
+gdb_test "bt full" "^bt full\r\n#0 +d *\\(i=31\\) \[^\r\n\]*\r\n\[ \t\]*i@entry = 30\r\n#1 +0x\[0-9a-f\]+ in main .*" \
"entry: bt full"
gdb_test "p i" " = 31" "entry: p i"

+# Test @entry values for stack passed parameters.
+
+gdb_continue_to_breakpoint "entry_stack: stacktest"
+
+# (gdb) bt full
+# #0 stacktest (r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=11, s2=12) at gdb.arch/amd64-entry-value.cc:121
+# #1 0x0000000000400412 in main () at gdb.arch/amd64-entry-value.cc:142
+# Check s1 and s2 are suppressed:
+gdb_test "bt full" "^bt full\r\n#0 +stacktest *\\(r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=11, s2=12\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \
+ "entry_stack: bt full at entry"
+
+gdb_continue_to_breakpoint "entry_stack: breakhere_stacktest"
+
+# (gdb) bt full
+# #0 stacktest (r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=3, s2=4) at gdb.arch/amd64-entry-value.cc:123
+# s1@entry = 11
+# s2@entry = 12
+# #1 0x0000000000400412 in main () at gdb.arch/amd64-entry-value.cc:130
+# Check s1 and s2 are present:
+gdb_test "bt full" "^bt full\r\n#0 +stacktest *\\(r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=3, s2=4\\) \[^\r\n\]*\r\n\[ \t\]*s1@entry = 11\r\n\[ \t\]*s2@entry = 12\r\n#1 +0x\[0-9a-f\]+ in main .*" \
+ "entry_stack: bt full"
+
+gdb_test "p s1" " = 3" "entry_stack: p s1"
+gdb_test "p s2" " = 4" "entry_stack: p s2"
+
+
+# Test @entry values for DW_AT_GNU_call_site_data_value parameters.
+
+gdb_continue_to_breakpoint "entry_reference: reference"
+
+# (gdb) bt full
+# #0 reference (refparam=@0x7fffffffdc3c) at gdb.arch/amd64-entry-value.cc:131
+# refcopy = 5
+# #1 0x0000000000400424 in main () at gdb.arch/amd64-entry-value.cc:145
+# refvar = 5
+# Check refparam@entry is suppressed:
+gdb_test "bt full" "#0 +reference \\(refparam=@0x\[0-9a-f\]+\\) \[^\r\n\]*\r\n\[ \t\]*refcopy = 5\r\n#1 +0x\[0-9a-f\]+ in main \\(\\) \[^\r\n\]*\r\n\[ \t\]*refvar = 5" \
+ "entry_reference: bt full at entry"
+
+gdb_continue_to_breakpoint "entry_reference: breakhere_reference"
+
+# (gdb) bt full
+# #0 reference (refparam=@0x7fffffffdc3c) at gdb.arch/amd64-entry-value.cc:133
+# refcopy = 5
+# #1 0x0000000000400424 in main () at gdb.arch/amd64-entry-value.cc:145
+# refvar = 10
+# Check refparam@entry is present:
+gdb_test "bt full" "#0 +reference \\(refparam=@0x\[0-9a-f\]+\\) \[^\r\n\]*\r\n\[ \t\]*refcopy = 5\r\n#1 +0x\[0-9a-f\]+ in main \\(\\) \[^\r\n\]*\r\n\[ \t\]*refvar = 10" \
+ "entry_reference: bt full"
+gdb_test "ptype refparam" " = int &" "entry_reference: ptype refparam"
+
+gdb_test "p refparam" { = \(int &\) @0x[0-9a-f]+: 10} "entry_reference: p refparam"
+gdb_test "p refcopy" " = 5" "entry_reference: p refcopy"
+
+
# Test virtual tail call frames.

gdb_continue_to_breakpoint "tailcall: breakhere"
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -801,11 +801,28 @@ extern int val_print_string (struct type *elttype, const char *encoding,
struct ui_file *stream,
const struct value_print_options *options);

+/* Specify how the @entry kind of function parameters should be printed. */
+enum print_argument
+{
+ /* Symbol is not a function parameter - it is a variable. */
+ PVAVD_IS_NOT_ARGUMENT,
+
+ /* Symbol is a function parameter, print only its @entry value if it is not
+ redundant together with the normal symbol printed value. */
+ PVAVD_ARGUMENT_PRINT_ENTRYVAL_ONLY,
+
+ /* Symbol is a function parameter, print its normal value. Print also its
+ @entry value if it is not redundant together with its normal printed
+ value. */
+ PVAVD_ARGUMENT_PRINT_BOTH
+};
+
extern void print_variable_and_value (const char *name,
struct symbol *var,
struct frame_info *frame,
struct ui_file *stream,
- int indent);
+ int indent,
+ enum print_argument print_argument);

extern int check_field (struct type *, const char *);