From e18b27534c2181c92dc9b685adc05bcf6b685817 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Sun, 9 Oct 2011 19:34:19 +0000 Subject: [PATCH] gdb/ Display @entry parameter values (without references). * dwarf2expr.c (dwarf_block_to_fb_offset, dwarf_block_to_sp_offset): New functions. * dwarf2expr.h (dwarf_block_to_fb_offset, dwarf_block_to_sp_offset): New declarations. * dwarf2loc.c (dwarf2_find_location_expression): Support location list entry record. (dwarf_entry_parameter_to_value, value_of_dwarf_reg_entry) (value_of_dwarf_block_entry, locexpr_read_variable_at_entry): New functions. (dwarf2_locexpr_funcs): Install locexpr_read_variable_at_entry. (loclist_read_variable_at_entry): New function. (dwarf2_loclist_funcs): Install loclist_read_variable_at_entry. * dwarf2read.c (read_call_site_scope): Support also DW_OP_fbreg in DW_AT_location, call dwarf_block_to_sp_offset for it. * frame.h (print_entry_values_no, print_entry_values_only) (print_entry_values_preferred, print_entry_values_if_needed) (print_entry_values_both, print_entry_values_compact) (print_entry_values_default, print_entry_values): New declarations. (struct frame_arg): New field entry_kind. (read_frame_arg): New parameter entryargp. * mi/mi-cmd-stack.c (list_arg_or_local): New gdb_assert for arg->entry_kind. Optionally print the `@entry' suffix. (list_args_or_locals): New variable entryarg, initialize it. Initialize also entry_kind of arg and entryarg. Conditionalize list_arg_or_local for arg, add list_arg_or_local for entryarg. Call xfree for entryarg.error. * stack.c (print_entry_values_no, print_entry_values_only) (print_entry_values_preferred, print_entry_values_if_needed) (print_entry_values_both, print_entry_values_compact) (print_entry_values_default, print_entry_values_choices) (print_entry_values): New variables. (print_frame_arg): New gdb_assert for arg->entry_kind. Optionally print the `@entry' suffix, possibly in combination for print_entry_values_compact. (read_frame_arg): New parameter entryargp, new variables entryval, entryval_error and val_equal. Read in also entryargp, respect print_entry_values, compare the values using val_equal, fill in also argp->entry_kind (together with entryargp->entry_kind). (print_frame_args): New variable entryarg, initialize it. Conditionalize print_frame_arg for arg, add print_frame_arg for entryarg. Call xfree for entryarg.error. (_initialize_stack): Call add_setshow_enum_cmd for `entry-values'. * symtab.h (struct symbol_computed_ops): New field read_variable_at_entry. gdb/doc/ Display @entry parameter values (without references). * gdb.texinfo (Tail Call Frames): Add anchor. Add self tail call example. (Print Settings): New description of set print entry-values and show print entry-values. gdb/testsuite/ Display @entry parameter values (without references). * gdb.arch/amd64-entry-value.cc (locexpr, stacktest, data, data2) (different, validity, invalid): New functions. (main): Call them. * gdb.arch/amd64-entry-value.exp: New breakpoints breakhere_locexpr, stacktest, breakhere_stacktest, different, breakhere_different, breakhere_validity and breakhere_invalid. (entry: bt): Update for @entry. (entry_locexpr: *, entry_stack: *, entry_equal: *, entry_different: *) (entry_validity: *, entry_invalid: *): Many new tests. * gdb.base/break.exp (run until breakpoint set at small function, optimized file): Accept also the @entry suffix. * gdb.mi/Makefile.in (PROGS): Add mi2-amd64-entry-value. * gdb.mi/mi2-amd64-entry-value.c: New files. * gdb.mi/mi2-amd64-entry-value.exp: New files. --- gdb/ChangeLog | 48 ++++++ gdb/doc/ChangeLog | 9 ++ gdb/doc/gdb.texinfo | 141 +++++++++++++++++ gdb/dwarf2expr.c | 62 ++++++++ gdb/dwarf2expr.h | 7 + gdb/dwarf2loc.c | 138 +++++++++++++++++ gdb/dwarf2read.c | 7 +- gdb/frame.h | 23 ++- gdb/mi/mi-cmd-stack.c | 22 ++- gdb/stack.c | 205 +++++++++++++++++++++++-- gdb/symtab.h | 6 + gdb/testsuite/ChangeLog | 19 +++ gdb/testsuite/gdb.arch/amd64-entry-value.cc | 65 ++++++++ gdb/testsuite/gdb.arch/amd64-entry-value.exp | 111 ++++++++++++- gdb/testsuite/gdb.base/break.exp | 6 +- gdb/testsuite/gdb.mi/Makefile.in | 3 +- gdb/testsuite/gdb.mi/mi2-amd64-entry-value.c | 70 +++++++++ gdb/testsuite/gdb.mi/mi2-amd64-entry-value.exp | 171 +++++++++++++++++++++ 18 files changed, 1088 insertions(+), 25 deletions(-) create mode 100644 gdb/testsuite/gdb.mi/mi2-amd64-entry-value.c create mode 100644 gdb/testsuite/gdb.mi/mi2-amd64-entry-value.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 959c4c7..0dc93bf 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,53 @@ 2011-10-09 Jan Kratochvil + Display @entry parameter values (without references). + * dwarf2expr.c (dwarf_block_to_fb_offset, dwarf_block_to_sp_offset): + New functions. + * dwarf2expr.h (dwarf_block_to_fb_offset, dwarf_block_to_sp_offset): + New declarations. + * dwarf2loc.c (dwarf2_find_location_expression): Support location list + entry record. + (dwarf_entry_parameter_to_value, value_of_dwarf_reg_entry) + (value_of_dwarf_block_entry, locexpr_read_variable_at_entry): New + functions. + (dwarf2_locexpr_funcs): Install locexpr_read_variable_at_entry. + (loclist_read_variable_at_entry): New function. + (dwarf2_loclist_funcs): Install loclist_read_variable_at_entry. + * dwarf2read.c (read_call_site_scope): Support also DW_OP_fbreg in + DW_AT_location, call dwarf_block_to_sp_offset for it. + * frame.h (print_entry_values_no, print_entry_values_only) + (print_entry_values_preferred, print_entry_values_if_needed) + (print_entry_values_both, print_entry_values_compact) + (print_entry_values_default, print_entry_values): New declarations. + (struct frame_arg): New field entry_kind. + (read_frame_arg): New parameter entryargp. + * mi/mi-cmd-stack.c (list_arg_or_local): New gdb_assert for + arg->entry_kind. Optionally print the `@entry' suffix. + (list_args_or_locals): New variable entryarg, initialize it. + Initialize also entry_kind of arg and entryarg. Conditionalize + list_arg_or_local for arg, add list_arg_or_local for entryarg. Call + xfree for entryarg.error. + * stack.c (print_entry_values_no, print_entry_values_only) + (print_entry_values_preferred, print_entry_values_if_needed) + (print_entry_values_both, print_entry_values_compact) + (print_entry_values_default, print_entry_values_choices) + (print_entry_values): New variables. + (print_frame_arg): New gdb_assert for arg->entry_kind. Optionally + print the `@entry' suffix, possibly in combination for + print_entry_values_compact. + (read_frame_arg): New parameter entryargp, new variables entryval, + entryval_error and val_equal. Read in also entryargp, respect + print_entry_values, compare the values using val_equal, fill in also + argp->entry_kind (together with entryargp->entry_kind). + (print_frame_args): New variable entryarg, initialize it. + Conditionalize print_frame_arg for arg, add print_frame_arg for + entryarg. Call xfree for entryarg.error. + (_initialize_stack): Call add_setshow_enum_cmd for `entry-values'. + * symtab.h (struct symbol_computed_ops): New field + read_variable_at_entry. + +2011-10-09 Jan Kratochvil + Code reshuffle. * frame.h (struct frame_arg): New definition. (read_frame_arg): New declaration. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 8c9d9b5..7763cce 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,6 +1,15 @@ 2011-10-09 Jan Kratochvil Eli Zaretskii + Display @entry parameter values (without references). + * gdb.texinfo (Tail Call Frames): Add anchor. Add self tail call + example. + (Print Settings): New description of set print entry-values and show + print entry-values. + +2011-10-09 Jan Kratochvil + Eli Zaretskii + Recognize virtual tail call frames. * gdb.texinfo (Optimized Code): Add reference to Tail Call Frames. (Tail Call Frames): New node. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index fdf66c3..9de96c1 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7941,6 +7941,120 @@ thus speeding up the display of each Ada frame. @item show print frame-arguments Show how the value of arguments should be displayed when printing a frame. +@item set print entry-values @var{value} +@kindex set print entry-values +Set printing of frame argument values at function entry. In some cases +@value{GDBN} can determine the value of function argument which was passed by +the function caller, even if the value was modified inside the called function +and therefore is different. With optimized code, the current value could be +unavailable, but the entry value may still be known. + +The default value is @code{default} (see below for its description). Older +@value{GDBN} behaved as with the setting @code{no}. Compilers not supporting +this feature will behave in the @code{default} setting the same way as with the +@code{no} setting. + +This functionality is currently supported only by DWARF 2 debugging format and +the compiler has to produce @samp{DW_TAG_GNU_call_site} tags. With +@value{NGCC}, you need to specify @option{-O -g} during compilation, to get +this information. + +The @var{value} parameter can be one of the following: + +@table @code +@item no +Print only actual parameter values, never print values from function entry +point. +@smallexample +#0 equal (val=5) +#0 different (val=6) +#0 lost (val=) +#0 born (val=10) +#0 invalid (val=) +@end smallexample + +@item only +Print only parameter values from function entry point. The actual parameter +values are never printed. +@smallexample +#0 equal (val@@entry=5) +#0 different (val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val@@entry=) +#0 invalid (val@@entry=) +@end smallexample + +@item preferred +Print only parameter values from function entry point. If value from function +entry point is not known while the actual value is known, print the actual +value for such parameter. +@smallexample +#0 equal (val@@entry=5) +#0 different (val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val=10) +#0 invalid (val@@entry=) +@end smallexample + +@item if-needed +Print actual parameter values. If actual parameter value is not known while +value from function entry point is known, print the entry point value for such +parameter. +@smallexample +#0 equal (val=5) +#0 different (val=6) +#0 lost (val@@entry=5) +#0 born (val=10) +#0 invalid (val=) +@end smallexample + +@item both +Always print both the actual parameter value and its value from function entry +point, even if values of one or both are not available due to compiler +optimizations. +@smallexample +#0 equal (val=5, val@@entry=5) +#0 different (val=6, val@@entry=5) +#0 lost (val=, val@@entry=5) +#0 born (val=10, val@@entry=) +#0 invalid (val=, val@@entry=) +@end smallexample + +@item compact +Print the actual parameter value if it is known and also its value from +function entry point if it is known. If neither is known, print for the actual +value @code{}. If not in MI mode (@pxref{GDB/MI}) and if both +values are known and identical, print the shortened +@code{param=param@@entry=VALUE} notation. +@smallexample +#0 equal (val=val@@entry=5) +#0 different (val=6, val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val=10) +#0 invalid (val=) +@end smallexample + +@item default +Always print the actual parameter value. Print also its value from function +entry point, but only if it is known. If not in MI mode (@pxref{GDB/MI}) and +if both values are known and identical, print the shortened +@code{param=param@@entry=VALUE} notation. +@smallexample +#0 equal (val=val@@entry=5) +#0 different (val=6, val@@entry=5) +#0 lost (val=, val@@entry=5) +#0 born (val=10) +#0 invalid (val=) +@end smallexample +@end table + +For analysis messages on possible failures of frame argument values at function +entry resolution see @ref{set debug entry-values}. + +@item show print entry-values +Show the method being used for printing of frame argument values at function +entry. + @item set print repeats @cindex repeated array elements Set the threshold for suppressing display of repeated array @@ -9600,6 +9714,7 @@ tries to show at least all the unambiguous top tail callers and all the unambiguous bottom tail calees, if any. @table @code +@anchor{set debug entry-values} @item set debug entry-values @kindex set debug entry-values When set to on, enables printing of analysis messages for both frame argument @@ -9675,6 +9790,32 @@ also ambigous. The only non-ambiguous frame is the one for function @code{a}, therefore this one is displayed to the user while the ambiguous frames are omitted. +There can be also reasons why printing of frame argument values at function +entry may fail: + +@smallexample +int v; +static void __attribute__((noinline, noclone)) c (int i) @{ v++; @} +static void __attribute__((noinline, noclone)) a (int i); +static void __attribute__((noinline, noclone)) b (int i) @{ a (i); @} +static void __attribute__((noinline, noclone)) a (int i) +@{ if (i) b (i - 1); else c (0); @} +int main (void) @{ a (5); return 0; @} + +(gdb) bt +#0 c (i=i@@entry=0) at t.c:2 +#1 0x0000000000400428 in a (DW_OP_GNU_entry_value resolving has found +function "a" at 0x400420 can call itself via tail calls +i=) at t.c:6 +#2 0x000000000040036e in main () at t.c:7 +@end smallexample + +@value{GDBN} cannot find out from the inferior state if and how many times did +function @code{a} call itself (via function @code{b}) as these calls would be +tail calls. Such tail calls would modify thue @code{i} variable, therefore +@value{GDBN} cannot be sure the value it knows would be right - @value{GDBN} +prints @code{} instead. + @node Macros @chapter C Preprocessor Macros diff --git a/gdb/dwarf2expr.c b/gdb/dwarf2expr.c index 2eb9d08..4fccc26 100644 --- a/gdb/dwarf2expr.c +++ b/gdb/dwarf2expr.c @@ -518,6 +518,68 @@ dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end) return dwarf_reg; } +/* If = DW_OP_breg0 && *buf <= DW_OP_breg31) + { + dwarf_reg = *buf - DW_OP_breg0; + buf++; + } + else + { + if (*buf != DW_OP_bregx) + return 0; + buf++; + buf = read_uleb128 (buf, buf_end, &dwarf_reg); + } + + if (gdbarch_dwarf2_reg_to_regnum (gdbarch, dwarf_reg) + != gdbarch_sp_regnum (gdbarch)) + return 0; + + buf = read_sleb128 (buf, buf_end, &sp_offset); + *sp_offset_return = sp_offset; + if (buf != buf_end || sp_offset != (LONGEST) *sp_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. */ diff --git a/gdb/dwarf2expr.h b/gdb/dwarf2expr.h index 8518f37..c014ce2 100644 --- a/gdb/dwarf2expr.h +++ b/gdb/dwarf2expr.h @@ -281,4 +281,11 @@ void ctx_no_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end); +int dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end, + CORE_ADDR *fb_offset_return); + +int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf, + const gdb_byte *buf_end, + CORE_ADDR *sp_offset_return); + #endif /* dwarf2expr.h */ diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c index fef0c6b..a44d3d2 100644 --- 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; @@ -892,6 +910,33 @@ dwarf_expr_reg_to_entry_parameter (struct frame_info *frame, int dwarf_reg, return parameter; } +/* Return value for PARAMETER for DW_AT_GNU_call_site_value. + + 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 + NO_ENTRY_VALUE_ERROR if it cannot resolve the value for any reason. */ + +static struct value * +dwarf_entry_parameter_to_value (struct call_site_parameter *parameter, + struct type *type, + struct frame_info *caller_frame, + struct dwarf2_per_cu_data *per_cu) +{ + gdb_byte *data; + + /* DW_AT_GNU_call_site_value is a DWARF expression, not a DWARF + location. Postprocessing of DWARF_VALUE_MEMORY would lose the type from + DWARF block. */ + data = alloca (parameter->value_size + 1); + memcpy (data, parameter->value, parameter->value_size); + data[parameter->value_size] = DW_OP_stack_value; + + return dwarf2_evaluate_loc_desc (type, caller_frame, data, + parameter->value_size + 1, per_cu); +} + /* Execute call_site_parameter's DWARF block for caller of the CTX's frame. CTX must be of dwarf_expr_ctx_funcs kind. See DWARF_REG and FB_OFFSET description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value. @@ -942,6 +987,58 @@ dwarf_expr_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, ctx->baton = saved_ctx.baton; } +/* Read parameter of TYPE at (callee) FRAME's function entry. DWARF_REG and + FB_OFFSET are used to match DW_AT_location at the caller's + DW_TAG_GNU_call_site_parameter. See DWARF_REG and FB_OFFSET description at + struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value. + + Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if it + cannot resolve the parameter 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; + struct dwarf2_per_cu_data *caller_per_cu; + + parameter = dwarf_expr_reg_to_entry_parameter (frame, dwarf_reg, fb_offset, + &caller_per_cu); + + return dwarf_entry_parameter_to_value (parameter, type, caller_frame, + caller_per_cu); +} + +/* Read parameter of TYPE at (callee) FRAME's function entry. DATA and + SIZE are DWARF block used to match DW_AT_location at the caller's + DW_TAG_GNU_call_site_parameter. + + Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if it + cannot resolve the parameter for any reason. */ + +static struct value * +value_of_dwarf_block_entry (struct type *type, struct frame_info *frame, + const gdb_byte *block, size_t block_len) +{ + int dwarf_reg; + CORE_ADDR fb_offset; + + dwarf_reg = dwarf_block_to_dwarf_reg (block, block + block_len); + if (dwarf_reg != -1) + return value_of_dwarf_reg_entry (type, frame, dwarf_reg, 0 /* unused */); + + if (dwarf_block_to_fb_offset (block, block + block_len, &fb_offset)) + return value_of_dwarf_reg_entry (type, frame, -1, fb_offset); + + /* This can normally happen - throw NO_ENTRY_VALUE_ERROR to get the message + suppressed during normal operation. The expression can be arbitrary if + there is no caller-callee entry value binding expected. */ + throw_error (NO_ENTRY_VALUE_ERROR, + _("DWARF-2 expression error: DW_OP_GNU_entry_value is supported " + "only for single DW_OP_reg* or for DW_OP_fbreg(*)")); +} + struct piece_closure { /* Reference count. */ @@ -2853,6 +2950,19 @@ locexpr_read_variable (struct symbol *symbol, struct frame_info *frame) return val; } +/* Return the value of SYMBOL in FRAME at (callee) FRAME's function + entry. SYMBOL should be a function parameter, otherwise NO_ENTRY_VALUE_ERROR + will be thrown. */ + +static struct value * +locexpr_read_variable_at_entry (struct symbol *symbol, struct frame_info *frame) +{ + struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); + + return value_of_dwarf_block_entry (SYMBOL_TYPE (symbol), frame, dlbaton->data, + dlbaton->size); +} + /* Return non-zero iff we need a frame to evaluate SYMBOL. */ static int locexpr_read_needs_frame (struct symbol *symbol) @@ -3494,6 +3604,7 @@ locexpr_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch, evaluator. */ const struct symbol_computed_ops dwarf2_locexpr_funcs = { locexpr_read_variable, + locexpr_read_variable_at_entry, locexpr_read_needs_frame, locexpr_describe_location, locexpr_tracepoint_var_ref @@ -3524,6 +3635,32 @@ loclist_read_variable (struct symbol *symbol, struct frame_info *frame) return val; } +/* Read variable SYMBOL like loclist_read_variable at (callee) FRAME's function + entry. SYMBOL should be a function parameter, otherwise NO_ENTRY_VALUE_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 NO_ENTRY_VALUE_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); + const gdb_byte *data; + size_t size; + CORE_ADDR pc; + + 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)); + + return value_of_dwarf_block_entry (SYMBOL_TYPE (symbol), frame, data, size); +} + /* Return non-zero iff we need a frame to evaluate SYMBOL. */ static int loclist_read_needs_frame (struct symbol *symbol) @@ -3643,6 +3780,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 diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index abc2163..695f341 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -6346,10 +6346,13 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) } parameter->dwarf_reg = dwarf_block_to_dwarf_reg (DW_BLOCK (attr)->data, &DW_BLOCK (attr)->data[DW_BLOCK (attr)->size]); - if (parameter->dwarf_reg == -1) + if (parameter->dwarf_reg == -1 + && !dwarf_block_to_sp_offset (gdbarch, DW_BLOCK (attr)->data, + &DW_BLOCK (attr)->data[DW_BLOCK (attr)->size], + ¶meter->fb_offset)) { complaint (&symfile_complaints, - _("Only single DW_OP_reg is supported " + _("Only single DW_OP_reg or DW_OP_fbreg is supported " "for DW_FORM_block* DW_AT_location for " "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"), child_die->offset, cu->objfile->name); diff --git a/gdb/frame.h b/gdb/frame.h index 3850e11..f5866bd 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -712,6 +712,15 @@ extern int frame_register_read (struct frame_info *frame, int regnum, /* From stack.c. */ +extern const char print_entry_values_no[]; +extern const char print_entry_values_only[]; +extern const char print_entry_values_preferred[]; +extern const char print_entry_values_if_needed[]; +extern const char print_entry_values_both[]; +extern const char print_entry_values_compact[]; +extern const char print_entry_values_default[]; +extern const char *print_entry_values; + /* Inferior function parameter value read in from a frame. */ struct frame_arg @@ -726,10 +735,22 @@ struct frame_arg /* String containing the error message, it is more usually NULL indicating no error occured reading this parameter. */ char *error; + + /* One of the print_entry_values_* entries as appropriate specifically for + this frame_arg. It will be different from print_entry_values. With + print_entry_values_no this frame_arg should be printed as a normal + parameter. print_entry_values_only says it should be printed as entry + value parameter. print_entry_values_compact says it should be printed as + both as a normal parameter and entry values parameter having the same + value - print_entry_values_compact is not permitted fi ui_out_is_mi_like_p + (in such case print_entry_values_no and print_entry_values_only is used + for each parameter kind specifically. */ + const char *entry_kind; }; extern void read_frame_arg (struct symbol *sym, struct frame_info *frame, - struct frame_arg *argp); + struct frame_arg *argp, + struct frame_arg *entryargp); extern void args_info (char *, int); diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 2e32eff..e1393f0 100644 --- a/gdb/mi/mi-cmd-stack.c +++ b/gdb/mi/mi-cmd-stack.c @@ -256,11 +256,17 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what, || values == PRINT_SIMPLE_VALUES || (values == PRINT_ALL_VALUES && (arg->val != NULL || arg->error != NULL))); + gdb_assert (arg->entry_kind == print_entry_values_no + || (arg->entry_kind == print_entry_values_only + && (arg->val || arg->error))); if (values != PRINT_NO_VALUES || what == all) cleanup_tuple = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); - ui_out_field_string (uiout, "name", SYMBOL_PRINT_NAME (arg->sym)); + fputs_filtered (SYMBOL_PRINT_NAME (arg->sym), stb->stream); + if (arg->entry_kind == print_entry_values_only) + fputs_filtered ("@entry", stb->stream); + ui_out_field_stream (uiout, "name", stb); if (what == all && SYMBOL_IS_ARGUMENT (arg->sym)) ui_out_field_int (uiout, "arg", 1); @@ -380,7 +386,7 @@ list_args_or_locals (enum what_to_list what, enum print_values values, if (print_me) { struct symbol *sym2; - struct frame_arg arg; + struct frame_arg arg, entryarg; if (SYMBOL_IS_ARGUMENT (sym)) sym2 = lookup_symbol (SYMBOL_NATURAL_NAME (sym), @@ -391,6 +397,10 @@ list_args_or_locals (enum what_to_list what, enum print_values values, memset (&arg, 0, sizeof (arg)); arg.sym = sym2; + arg.entry_kind = print_entry_values_no; + memset (&entryarg, 0, sizeof (entryarg)); + entryarg.sym = sym2; + entryarg.entry_kind = print_entry_values_no; switch (values) { @@ -401,13 +411,17 @@ list_args_or_locals (enum what_to_list what, enum print_values values, && TYPE_CODE (type) != TYPE_CODE_UNION) { case PRINT_ALL_VALUES: - read_frame_arg (sym2, fi, &arg); + read_frame_arg (sym2, fi, &arg, &entryarg); } break; } - list_arg_or_local (&arg, what, values); + if (arg.entry_kind != print_entry_values_only) + list_arg_or_local (&arg, what, values); + if (entryarg.entry_kind != print_entry_values_no) + list_arg_or_local (&entryarg, what, values); xfree (arg.error); + xfree (entryarg.error); } } if (BLOCK_FUNCTION (block)) diff --git a/gdb/stack.c b/gdb/stack.c index bdc3b01..276dd2b 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -64,6 +64,29 @@ static const char *print_frame_arguments_choices[] = {"all", "scalars", "none", NULL}; static const char *print_frame_arguments = "scalars"; +/* The possible choices of "set print entry-values", and the value + of this setting. */ + +const char print_entry_values_no[] = "no"; +const char print_entry_values_only[] = "only"; +const char print_entry_values_preferred[] = "preferred"; +const char print_entry_values_if_needed[] = "if-needed"; +const char print_entry_values_both[] = "both"; +const char print_entry_values_compact[] = "compact"; +const char print_entry_values_default[] = "default"; +static const char *print_entry_values_choices[] = +{ + print_entry_values_no, + print_entry_values_only, + print_entry_values_preferred, + print_entry_values_if_needed, + print_entry_values_both, + print_entry_values_compact, + print_entry_values_default, + NULL +}; +const char *print_entry_values = print_entry_values_default; + /* Prototypes for local functions. */ static void print_frame_local_vars (struct frame_info *, int, @@ -180,12 +203,29 @@ print_frame_arg (const struct frame_arg *arg) old_chain = make_cleanup_ui_out_stream_delete (stb); gdb_assert (!arg->val || !arg->error); + gdb_assert (arg->entry_kind == print_entry_values_no + || arg->entry_kind == print_entry_values_only + || (!ui_out_is_mi_like_p (uiout) + && arg->entry_kind == print_entry_values_compact)); annotate_arg_begin (); make_cleanup_ui_out_tuple_begin_end (uiout, NULL); fprintf_symbol_filtered (stb->stream, SYMBOL_PRINT_NAME (arg->sym), SYMBOL_LANGUAGE (arg->sym), DMGL_PARAMS | DMGL_ANSI); + if (arg->entry_kind == print_entry_values_compact) + { + /* It is OK to provide invalid MI-like stream as with + PRINT_ENTRY_VALUE_COMPACT we never use MI. */ + fputs_filtered ("=", stb->stream); + + fprintf_symbol_filtered (stb->stream, SYMBOL_PRINT_NAME (arg->sym), + SYMBOL_LANGUAGE (arg->sym), + DMGL_PARAMS | DMGL_ANSI); + } + if (arg->entry_kind == print_entry_values_only + || arg->entry_kind == print_entry_values_compact) + fputs_filtered ("@entry", stb->stream); ui_out_field_stream (uiout, "name", stb); annotate_arg_name_end (); ui_out_text (uiout, "="); @@ -248,25 +288,138 @@ print_frame_arg (const struct frame_arg *arg) void read_frame_arg (struct symbol *sym, struct frame_info *frame, - struct frame_arg *argp) + struct frame_arg *argp, struct frame_arg *entryargp) { - struct value *val = NULL; - char *val_error = NULL; + struct value *val = NULL, *entryval = NULL; + char *val_error = NULL, *entryval_error = NULL; + int val_equal = 0; volatile struct gdb_exception except; - TRY_CATCH (except, RETURN_MASK_ERROR) + if (print_entry_values != print_entry_values_only + && print_entry_values != print_entry_values_preferred) + { + TRY_CATCH (except, RETURN_MASK_ERROR) + { + val = read_var_value (sym, frame); + } + if (!val) + { + val_error = alloca (strlen (except.message) + 1); + strcpy (val_error, except.message); + } + } + + if (SYMBOL_CLASS (sym) == LOC_COMPUTED + && print_entry_values != print_entry_values_no + && (print_entry_values != print_entry_values_if_needed + || !val || value_optimized_out (val))) + { + TRY_CATCH (except, RETURN_MASK_ERROR) + { + const struct symbol_computed_ops *ops; + + ops = SYMBOL_COMPUTED_OPS (sym); + entryval = ops->read_variable_at_entry (sym, frame); + } + if (!entryval) + { + entryval_error = alloca (strlen (except.message) + 1); + strcpy (entryval_error, except.message); + } + + if (except.error == NO_ENTRY_VALUE_ERROR + || (entryval && value_optimized_out (entryval))) + { + entryval = NULL; + entryval_error = NULL; + } + + if (print_entry_values == print_entry_values_compact + || print_entry_values == print_entry_values_default) + { + /* For MI do not try to use print_entry_values_compact for ARGP. */ + + if (val && entryval && !ui_out_is_mi_like_p (current_uiout)) + { + unsigned len = TYPE_LENGTH (value_type (val)); + + 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; + val_equal = 1; + } + } + + /* Try to remove possibly duplicate error message for ENTRYARGP even + in MI mode. */ + + if (val_error && entryval_error + && strcmp (val_error, entryval_error) == 0) + { + entryval_error = NULL; + + /* Do not se VAL_EQUAL as the same error message may be shown for + the entry value even if no entry values are present in the + inferior. */ + } + } + } + + if (entryval == NULL) { - val = read_var_value (sym, frame); + if (print_entry_values == print_entry_values_preferred) + { + TRY_CATCH (except, RETURN_MASK_ERROR) + { + val = read_var_value (sym, frame); + } + if (!val) + { + val_error = alloca (strlen (except.message) + 1); + strcpy (val_error, except.message); + } + } + if (print_entry_values == print_entry_values_only + || print_entry_values == print_entry_values_both + || (print_entry_values == print_entry_values_preferred + && (!val || value_optimized_out (val)))) + entryval = allocate_optimized_out_value (SYMBOL_TYPE (sym)); } - if (!val) + if ((print_entry_values == print_entry_values_compact + || print_entry_values == print_entry_values_if_needed + || print_entry_values == print_entry_values_preferred) + && (!val || value_optimized_out (val)) && entryval != NULL) { - val_error = alloca (strlen (except.message) + 1); - strcpy (val_error, except.message); + val = NULL; + val_error = NULL; } argp->sym = sym; argp->val = val; argp->error = val_error ? xstrdup (val_error) : NULL; + if (!val && !val_error) + argp->entry_kind = print_entry_values_only; + else if ((print_entry_values == print_entry_values_compact + || print_entry_values == print_entry_values_default) && val_equal) + { + argp->entry_kind = print_entry_values_compact; + gdb_assert (!ui_out_is_mi_like_p (current_uiout)); + } + else + argp->entry_kind = print_entry_values_no; + + entryargp->sym = sym; + entryargp->val = entryval; + entryargp->error = entryval_error ? xstrdup (entryval_error) : NULL; + if (!entryval && !entryval_error) + entryargp->entry_kind = print_entry_values_no; + else + entryargp->entry_kind = print_entry_values_only; } /* Print the arguments of frame FRAME on STREAM, given the function @@ -308,7 +461,7 @@ print_frame_args (struct symbol *func, struct frame_info *frame, ALL_BLOCK_SYMBOLS (b, iter, sym) { - struct frame_arg arg; + struct frame_arg arg, entryarg; QUIT; @@ -426,13 +579,30 @@ print_frame_args (struct symbol *func, struct frame_info *frame, { memset (&arg, 0, sizeof (arg)); arg.sym = sym; + arg.entry_kind = print_entry_values_no; + memset (&entryarg, 0, sizeof (entryarg)); + entryarg.sym = sym; + entryarg.entry_kind = print_entry_values_no; } else - read_frame_arg (sym, frame, &arg); + read_frame_arg (sym, frame, &arg, &entryarg); - print_frame_arg (&arg); + if (arg.entry_kind != print_entry_values_only) + print_frame_arg (&arg); + + if (entryarg.entry_kind != print_entry_values_no) + { + if (arg.entry_kind != print_entry_values_only) + { + ui_out_text (uiout, ", "); + ui_out_wrap_hint (uiout, " "); + } + + print_frame_arg (&entryarg); + } xfree (arg.error); + xfree (entryarg.error); first = 0; } @@ -2313,4 +2483,17 @@ source line."), show_disassemble_next_line, &setlist, &showlist); disassemble_next_line = AUTO_BOOLEAN_FALSE; + + add_setshow_enum_cmd ("entry-values", class_stack, + print_entry_values_choices, &print_entry_values, + _("Set printing of function arguments at function " + "entry"), + _("Show printing of function arguments at function " + "entry"), + _("\ +GDB can sometimes determine the values of function arguments at entry,\n\ +in addition to their current values. This option tells GDB whether\n\ +to print the current value, the value at entry (marked as val@entry),\n\ +or both. Note that one or both of these values may be ."), + NULL, NULL, &setprintlist, &showprintlist); } diff --git a/gdb/symtab.h b/gdb/symtab.h index fe8880f..90a6fe4 100644 --- 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 + NO_ENTRY_VALUE_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); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 86fe0e9..74826cf 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,5 +1,24 @@ 2011-10-09 Jan Kratochvil + Display @entry parameter values (without references). + * gdb.arch/amd64-entry-value.cc (locexpr, stacktest, data, data2) + (different, validity, invalid): New functions. + (main): Call them. + * gdb.arch/amd64-entry-value.exp: New breakpoints breakhere_locexpr, + stacktest, breakhere_stacktest, different, breakhere_different, + breakhere_validity and breakhere_invalid. + (entry: bt): Update for @entry. + (entry_locexpr: *, entry_stack: *, entry_equal: *, entry_different: *) + (entry_validity: *, entry_invalid: *): Many new tests. + * gdb.base/break.exp + (run until breakpoint set at small function, optimized file): Accept + also the @entry suffix. + * gdb.mi/Makefile.in (PROGS): Add mi2-amd64-entry-value. + * gdb.mi/mi2-amd64-entry-value.c: New files. + * gdb.mi/mi2-amd64-entry-value.exp: New files. + +2011-10-09 Jan Kratochvil + Protect entry values against self tail calls. * gdb.arch/amd64-entry-value.cc (self2, self): New. (main): Call self. diff --git a/gdb/testsuite/gdb.arch/amd64-entry-value.cc b/gdb/testsuite/gdb.arch/amd64-entry-value.cc index 44b7564..7b7d050 100644 --- a/gdb/testsuite/gdb.arch/amd64-entry-value.cc +++ b/gdb/testsuite/gdb.arch/amd64-entry-value.cc @@ -35,6 +35,13 @@ asm ("breakhere:"); } static void __attribute__((noinline, noclone)) +locexpr (int i) +{ + i = i; +asm ("breakhere_locexpr:"); +} + +static void __attribute__((noinline, noclone)) c (int i, double j) { d (i * 10, j * 10); @@ -114,10 +121,68 @@ 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, + double d1, double d2, double d3, double d4, double d5, double d6, + double d7, double d8, double d9, double da) +{ + s1 = 3; + s2 = 4; + d9 = 3.5; + da = 4.5; + e (v, v); +asm ("breakhere_stacktest:"); + e (v, v); +} + +static int __attribute__((noinline, noclone)) +data (void) +{ + return 10; +} + +static int __attribute__((noinline, noclone)) +data2 (void) +{ + return 20; +} + +static int __attribute__((noinline, noclone)) +different (int val) +{ + val++; + e (val, val); +asm ("breakhere_different:"); + return val; +} + +static int __attribute__((noinline, noclone)) +validity (int lost, int born) +{ + lost = data (); + e (0, 0.0); +asm ("breakhere_validity:"); + return born; +} + +static void __attribute__((noinline, noclone)) +invalid (int inv) +{ + e (0, 0.0); +asm ("breakhere_invalid:"); +} + int main () { d (30, 30.5); + locexpr (30); + stacktest (1, 2, 3, 4, 5, 6, 11, 12, + 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 11.5, 12.5); + different (5); + validity (5, data ()); + invalid (data2 ()); + if (v) a (1, 1.25); else diff --git a/gdb/testsuite/gdb.arch/amd64-entry-value.exp b/gdb/testsuite/gdb.arch/amd64-entry-value.exp index fdf8040..fcccdd8 100644 --- a/gdb/testsuite/gdb.arch/amd64-entry-value.exp +++ b/gdb/testsuite/gdb.arch/amd64-entry-value.exp @@ -35,23 +35,128 @@ if ![runto_main] { } gdb_breakpoint "breakhere" +gdb_breakpoint "breakhere_locexpr" +gdb_breakpoint "stacktest" +gdb_breakpoint "breakhere_stacktest" +gdb_breakpoint "different" +gdb_breakpoint "breakhere_different" +gdb_breakpoint "breakhere_validity" +gdb_breakpoint "breakhere_invalid" # Test @entry values for register passed parameters. gdb_continue_to_breakpoint "entry: breakhere" -gdb_test "bt" "^bt\r\n#0 +d *\\(i=31, j=31\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ +gdb_test "bt" "^bt\r\n#0 +d *\\(i=31, i@entry=30, j=31\\.5, j@entry=30\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ "entry: bt" gdb_test "p i" " = 31" "entry: p i" gdb_test "p j" { = 31\.5} "entry: p j" +# Test @entry values when parameter in function is locexpr (and not loclist). + +gdb_continue_to_breakpoint "entry_locexpr: breakhere_locexpr" +gdb_test "p i" " = 30" "entry_locexpr: p i" +gdb_test_no_output "set variable i = 0" "entry_locexpr: set variable i = 0" +gdb_test "bt" "^bt\r\n#0 +locexpr *\\(i=0, i@entry=30\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry_locexpr: bt" + + +# Test @entry values for stack passed parameters. + +gdb_continue_to_breakpoint "entry_stack: stacktest" + +gdb_test "bt" "^bt\r\n#0 +stacktest *\\(r1=r1@entry=1, r2=r2@entry=2, \[^\r\n\]+, s1=s1@entry=11, s2=s2@entry=12, \[^\r\n\]+, d9=d9@entry=11\\.5, da=da@entry=12\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry_stack: bt at entry" + +gdb_continue_to_breakpoint "entry_stack: breakhere_stacktest" + +gdb_test "bt" "^bt\r\n#0 +stacktest *\\(r1=r1@entry=1, r2=r2@entry=2, \[^\r\n\]+, s1=3, s1@entry=11, s2=4, s2@entry=12, \[^\r\n\]+, d9=3\\.5, d9@entry=11\\.5, da=4\\.5, da@entry=12\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry_stack: bt" + +gdb_test "p s1" " = 3" "entry_stack: p s1" +gdb_test "p s2" " = 4" "entry_stack: p s2" +gdb_test "p d9" " = 3\\.5" "entry_stack: p d9" +gdb_test "p da" " = 4\\.5" "entry_stack: p da" + + +# Test various kinds of `set print entry-values'. + +gdb_continue_to_breakpoint "entry_equal: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_equal: set print entry-values no" +gdb_test "frame" {\(val=5\).*} "entry_equal: frame: no" +gdb_test_no_output "set print entry-values only" "entry_equal: set print entry-values only" +gdb_test "frame" {\(val@entry=5\).*} "entry_equal: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_equal: set print entry-values preferred" +gdb_test "frame" {\(val@entry=5\).*} "entry_equal: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_equal: set print entry-values if-needed" +gdb_test "frame" {\(val=5\).*} "entry_equal: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_equal: set print entry-values both" +gdb_test "frame" {\(val=5, val@entry=5\).*} "entry_equal: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_equal: set print entry-values compact" +gdb_test "frame" {\(val=val@entry=5\).*} "entry_equal: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_equal: set print entry-values default" +gdb_test "frame" {\(val=val@entry=5\).*} "entry_equal: frame: default" + +gdb_continue_to_breakpoint "entry_different: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_different: set print entry-values no" +gdb_test "frame" {\(val=6\).*} "entry_different: frame: no" +gdb_test_no_output "set print entry-values only" "entry_different: set print entry-values only" +gdb_test "frame" {\(val@entry=5\).*} "entry_different: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_different: set print entry-values preferred" +gdb_test "frame" {\(val@entry=5\).*} "entry_different: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_different: set print entry-values if-needed" +gdb_test "frame" {\(val=6\).*} "entry_different: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_different: set print entry-values both" +gdb_test "frame" {\(val=6, val@entry=5\).*} "entry_different: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_different: set print entry-values compact" +gdb_test "frame" {\(val=6, val@entry=5\).*} "entry_different: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_different: set print entry-values default" +gdb_test "frame" {\(val=6, val@entry=5\).*} "entry_different: frame: default" + +gdb_continue_to_breakpoint "entry_validity: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_validity: set print entry-values no" +gdb_test "frame" {\(lost=, born=10\).*} "entry_validity: frame: no" +gdb_test_no_output "set print entry-values only" "entry_validity: set print entry-values only" +gdb_test "frame" {\(lost@entry=5, born@entry=\).*} "entry_validity: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_validity: set print entry-values preferred" +gdb_test "frame" {\(lost@entry=5, born=10\).*} "entry_validity: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_validity: set print entry-values if-needed" +gdb_test "frame" {\(lost@entry=5, born=10\).*} "entry_validity: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_validity: set print entry-values both" +gdb_test "frame" {\(lost=, lost@entry=5, born=10, born@entry=\).*} "entry_validity: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_validity: set print entry-values compact" +gdb_test "frame" {\(lost@entry=5, born=10\).*} "entry_validity: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_validity: set print entry-values default" +gdb_test "frame" {\(lost=, lost@entry=5, born=10\).*} "entry_validity: frame: default" + +gdb_continue_to_breakpoint "entry_invalid: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_invalid: set print entry-values no" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: no" +gdb_test_no_output "set print entry-values only" "entry_invalid: set print entry-values only" +gdb_test "frame" {\(inv@entry=\).*} "entry_invalid: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_invalid: set print entry-values preferred" +gdb_test "frame" {\(inv@entry=\).*} "entry_invalid: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_invalid: set print entry-values if-needed" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_invalid: set print entry-values both" +gdb_test "frame" {\(inv=, inv@entry=\).*} "entry_invalid: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_invalid: set print entry-values compact" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_invalid: set print entry-values default" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: default" + + # Test virtual tail call frames. gdb_continue_to_breakpoint "tailcall: breakhere" -gdb_test "bt" "^bt\r\n#0 +d *\\(i=71, j=73\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in c \\(i=7, j=7\\.25\\) \[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in b \\(i=5, j=5\\.25\\) \[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in main \[^\r\n\]*" \ +gdb_test "bt" "^bt\r\n#0 +d *\\(i=71, i@entry=70, j=73\\.5, j@entry=72\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in c \\(i=i@entry=7, j=j@entry=7\\.25\\) \[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in b \\(i=i@entry=5, j=j@entry=5\\.25\\) \[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in main \[^\r\n\]*" \ "tailcall: bt" gdb_test "p i" " = 71" "tailcall: p i" gdb_test "p j" " = 73\\.5" "tailcall: p j" @@ -71,7 +176,7 @@ gdb_test {p $sp0 + sizeof (void *) == $sp} " = true" gdb_continue_to_breakpoint "ambiguous: breakhere" -gdb_test "bt" "^bt\r\n#0 +d \\(i=, j=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in amb_z \\(i=\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in amb_y \\(i=\\)\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in amb_x \\(i=\\)\[^\r\n\]*\r\n#4 +0x\[0-9a-f\]+ in amb_b \\(i=101\\)\[^\r\n\]*\r\n#5 +0x\[0-9a-f\]+ in amb_a \\(i=100\\)\[^\r\n\]*\r\n#6 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \ +gdb_test "bt" "^bt\r\n#0 +d \\(i=, j=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in amb_z \\(i=\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in amb_y \\(i=\\)\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in amb_x \\(i=\\)\[^\r\n\]*\r\n#4 +0x\[0-9a-f\]+ in amb_b \\(i=i@entry=101\\)\[^\r\n\]*\r\n#5 +0x\[0-9a-f\]+ in amb_a \\(i=i@entry=100\\)\[^\r\n\]*\r\n#6 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \ "ambiguous: bt" diff --git a/gdb/testsuite/gdb.base/break.exp b/gdb/testsuite/gdb.base/break.exp index 15227bc..92fcc69 100644 --- a/gdb/testsuite/gdb.base/break.exp +++ b/gdb/testsuite/gdb.base/break.exp @@ -916,13 +916,13 @@ set bp_location14 [gdb_get_line_number "set breakpoint 14 here" $srcfile1] gdb_test_multiple "continue" \ "run until breakpoint set at small function, optimized file" { - -re "Breakpoint $decimal, marker4 \\(d=177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { + -re "Breakpoint $decimal, marker4 \\(d=(d@entry=)?177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { pass "run until breakpoint set at small function, optimized file" } - -re "Breakpoint $decimal, $hex in marker4 \\(d=177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { + -re "Breakpoint $decimal, $hex in marker4 \\(d=(d@entry=)?177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { pass "run until breakpoint set at small function, optimized file" } - -re "Breakpoint $decimal, marker4 \\(d=177601976\\) at .*$srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void marker4.*" { + -re "Breakpoint $decimal, marker4 \\(d=(d@entry=)?177601976\\) at .*$srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void marker4.*" { # marker4() is defined at line 46 when compiled with -DPROTOTYPES pass "run until breakpoint set at small function, optimized file (line bp_location14)" } diff --git a/gdb/testsuite/gdb.mi/Makefile.in b/gdb/testsuite/gdb.mi/Makefile.in index e8754f4..dc1f1a3 100644 --- a/gdb/testsuite/gdb.mi/Makefile.in +++ b/gdb/testsuite/gdb.mi/Makefile.in @@ -9,7 +9,8 @@ PROGS = basics c_variable cpp_variable var-cmd dw2-ref-missing-frame \ mi-pending mi-pthreads mi-read-memory mi-regs mi-return \ mi-reverse mi-simplerun mi-stack mi-stepi mi-syn-frame \ mi-var-block mi-var-child mi-var-cmd mi-var-cp mi-var-display \ - mi-var-invalidate mi-var-invalidate_bis mi-watch mi2-basics \ + mi-var-invalidate mi-var-invalidate_bis mi-watch \ + mi2-amd64-entry-value mi2-basics \ mi2-break mi2-cli mi2-disassemble mi2-eval mi2-file \ mi2-pthreads mi2-regs mi2-return mi2-simplerun mi2-stepi \ mi2-var-block mi2-var-child mi2-var-cmd mi2-var-display \ diff --git a/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.c b/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.c new file mode 100644 index 0000000..5c73a68 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.c @@ -0,0 +1,70 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 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 . */ + +static volatile int v; + +static void __attribute__((noinline, noclone)) +e (int i, double j) +{ + v = 0; +} + +static int __attribute__((noinline, noclone)) +data (void) +{ + return 10; +} + +static int __attribute__((noinline, noclone)) +data2 (void) +{ + return 20; +} + +static int __attribute__((noinline, noclone)) +different (int val) +{ + val++; + e (val, val); +asm ("breakhere_different:"); + return val; +} + +static int __attribute__((noinline, noclone)) +validity (int lost, int born) +{ + lost = data (); + e (0, 0.0); +asm ("breakhere_validity:"); + return born; +} + +static void __attribute__((noinline, noclone)) +invalid (int inv) +{ + e (0, 0.0); +asm ("breakhere_invalid:"); +} + +int +main () +{ + different (5); + validity (5, data ()); + invalid (data2 ()); + return 0; +} diff --git a/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.exp b/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.exp new file mode 100644 index 0000000..6cfcabf --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.exp @@ -0,0 +1,171 @@ +# Copyright (C) 2011 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 . + +load_lib mi-support.exp +set MIFLAGS "-i=mi2" + +gdb_exit +if [mi_gdb_start] { + continue +} + +set testfile mi2-amd64-entry-value +set srcfile ${testfile}.s +set opts {} + +if [info exists COMPILE] { + # make check RUNTESTFLAGS="gdb.mi/mi2-amd64-entry-value.exp COMPILE=1" + set srcfile ${testfile}.c + lappend opts debug optimize=-O2 +} elseif { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping mi2-amd64-entry-value." + return +} + +set executable ${testfile} +set binfile ${objdir}/${subdir}/${executable} + +if [build_executable ${testfile}.exp ${executable} ${srcfile} $opts] { + return -1 +} + +mi_gdb_reinitialize_dir $srcdir/$subdir +mi_gdb_load ${binfile} + +foreach name {different breakhere_different breakhere_validity breakhere_invalid} { + mi_create_breakpoint $name .* .* .* .* .* .* "break $name" +} + + +# Test various kinds of `set print entry-values'. + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values no" {\^done} "no: set print entry-values" +mi_send_resuming_command "exec-continue" "no: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"}} .* .* {.* disp="keep"} "no: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"}\]} "no: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "no: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"}} .* .* {.* disp="keep"} "no: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"}\]} "no: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "no: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost",value=""},{name="born",value="10"}} .* .* {.* disp="keep"} "no: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost",arg="1",value=""},{name="born",arg="1",value="10"}\]} "no: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "no: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "no: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "no: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values only" {\^done} "only: set print entry-values" +mi_send_resuming_command "exec-continue" "only: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "only: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "only: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "only: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "only: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "only: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "only: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born@entry",value=""}} .* .* {.* disp="keep"} "only: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born@entry",arg="1",value=""}\]} "only: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "only: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv@entry",value=""}} .* .* {.* disp="keep"} "only: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv@entry",arg="1",value=""}\]} "only: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values preferred" {\^done} "preferred: set print entry-values" +mi_send_resuming_command "exec-continue" "preferred: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "preferred: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "preferred: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "preferred: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "preferred: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "preferred: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "preferred: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "preferred: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "preferred: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "preferred: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv@entry",value=""}} .* .* {.* disp="keep"} "preferred: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv@entry",arg="1",value=""}\]} "preferred: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values if-needed" {\^done} "if-needed: set print entry-values" +mi_send_resuming_command "exec-continue" "if-needed: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"}} .* .* {.* disp="keep"} "if-needed: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"}\]} "if-needed: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "if-needed: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"}} .* .* {.* disp="keep"} "if-needed: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"}\]} "if-needed: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "if-needed: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "if-needed: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "if-needed: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "if-needed: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "if-needed: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "if-needed: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values both" {\^done} "both: set print entry-values" +mi_send_resuming_command "exec-continue" "both: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "both: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"},{name="val@entry",arg="1",value="5"}\]} "both: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "both: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "both: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"},{name="val@entry",arg="1",value="5"}\]} "both: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "both: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost",value=""},{name="lost@entry",value="5"},{name="born",value="10"},{name="born@entry",value=""}} .* .* {.* disp="keep"} "both: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost",arg="1",value=""},{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"},{name="born@entry",arg="1",value=""}\]} "both: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "both: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""},{name="inv@entry",value=""}} .* .* {.* disp="keep"} "both: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""},{name="inv@entry",arg="1",value=""}\]} "both: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values compact" {\^done} "compact: set print entry-values" +mi_send_resuming_command "exec-continue" "compact: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "compact: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"},{name="val@entry",arg="1",value="5"}\]} "compact: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "compact: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "compact: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"},{name="val@entry",arg="1",value="5"}\]} "compact: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "compact: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "compact: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "compact: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "compact: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "compact: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "compact: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values default" {\^done} "default: set print entry-values" +mi_send_resuming_command "exec-continue" "default: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "default: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"},{name="val@entry",arg="1",value="5"}\]} "default: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "default: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "default: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"},{name="val@entry",arg="1",value="5"}\]} "default: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "default: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost",value=""},{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "default: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost",arg="1",value=""},{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "default: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "default: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "default: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "default: invalid: -stack-list-variables" -- 2.7.4