From 901461f8eb407af99c049a381fc985baefce179c Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Wed, 2 Oct 2013 16:15:46 +0000 Subject: [PATCH] Print registers not saved in the frame as "" instead of "". Currently, in some scenarios, GDB prints when printing outer frame registers. An register is a confusing concept. What this really means is that the register is call-clobbered, or IOW, not saved by the callee. This patch makes GDB say that instead. Before patch: (gdb) p/x $rax $1 = (gdb) info registers rax rax After patch: (gdb) p/x $rax $1 = (gdb) info registers rax rax However, if for some reason the debug info describes a variable as being in such a register (**), we still want to print when printing the variable. IOW, is reserved for inspecting registers at the machine level. The patch uses lval_register+optimized_out to encode the not saved registers, and makes it so that optimized out variables always end up in !lval_register values. ** See . Current/recent enough GCC doesn't mark variables/arguments as being in call-clobbered registers in the ranges corresponding to function calls, while older GCCs did. Newer GCCs will just not say where the variable is, so GDB will end up realizing the variable is optimized out. frame_unwind_got_optimized creates not_lval optimized out registers, so by default, in most cases, we'll see . value_of_register is the function eval.c uses for evaluating OP_REGISTER (again, $pc, etc.), and related bits. It isn't used for anything else. This function makes sure to return lval_register values. The patch makes "info registers" and the MI equivalent use it too. I think it just makes a lot of sense, as this makes it so that when printing machine registers ($pc, etc.), we go through a central function. We're likely to need a different encoding at some point, if/when we support partially saved registers. Even then, I think value_of_register will still be the spot to tag the intention to print machine register values differently. value_from_register however may also return optimized out lval_register values, so at a couple places where we're computing a variable's location from a dwarf expression, we convert the resulting value away from lval_register to a regular optimized out value. Tested on x86_64 Fedora 17 gdb/ 2013-10-02 Pedro Alves * cp-valprint.c (cp_print_value_fields): Adjust calls to val_print_optimized_out. * jv-valprint.c (java_print_value_fields): Likewise. * p-valprint.c (pascal_object_print_value_fields): Likewise. * dwarf2loc.c (dwarf2_evaluate_loc_desc_full) : If the register was not saved, return a new optimized out value. * findvar.c (address_from_register): Likewise. * frame.c (put_frame_register): Tweak error string to say the register was not saved, rather than optimized out. * infcmd.c (default_print_one_register_info): Adjust call to val_print_optimized_out. Use value_of_register instead of get_frame_register_value. * mi/mi-main.c (output_register): Use value_of_register instead of get_frame_register_value. * valprint.c (valprint_check_validity): Likewise. (val_print_optimized_out): New value parameter. If the value is lval_register, print instead. (value_check_printable, val_print_scalar_formatted): Adjust calls to val_print_optimized_out. * valprint.h (val_print_optimized_out): New value parameter. * value.c (struct value) : Extend comment. (error_value_optimized_out): New function. (require_not_optimized_out): Use it. Use a different string for lval_register values. * value.h (error_value_optimized_out): New declaration. * NEWS: Mention . gdb/testsuite/ 2013-10-02 Pedro Alves * gdb.dwarf2/dw2-reg-undefined.exp : Set to "". * gdb.mi/mi-reg-undefined.exp (opt_out_pattern): Delete. (not_saved_pattern): New. Replace use of the former with the latter. gdb/doc/ 2013-10-02 Pedro Alves * gdb.texinfo (Registers): Expand description of saved registers in frames. Explain . --- gdb/ChangeLog | 30 ++++++++++++++++++ gdb/NEWS | 14 +++++++++ gdb/cp-valprint.c | 4 +-- gdb/doc/ChangeLog | 5 +++ gdb/doc/gdb.texinfo | 31 ++++++++++++++++--- gdb/dwarf2loc.c | 16 ++++++++-- gdb/findvar.c | 9 ++++++ gdb/frame.c | 2 +- gdb/infcmd.c | 4 +-- gdb/jv-valprint.c | 4 +-- gdb/mi/mi-main.c | 2 +- gdb/p-valprint.c | 4 +-- gdb/testsuite/ChangeLog | 8 +++++ .../gdb.dwarf2/dw2-reg-undefined.exp | 4 +-- gdb/testsuite/gdb.mi/mi-reg-undefined.exp | 4 +-- gdb/valprint.c | 13 +++++--- gdb/valprint.h | 3 +- gdb/value.c | 22 +++++++++++-- gdb/value.h | 5 +++ 19 files changed, 154 insertions(+), 30 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 69c72c52b2..10840a5b17 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,33 @@ +2013-10-02 Pedro Alves + + * cp-valprint.c (cp_print_value_fields): Adjust calls to + val_print_optimized_out. + * jv-valprint.c (java_print_value_fields): Likewise. + * p-valprint.c (pascal_object_print_value_fields): Likewise. + * dwarf2loc.c (dwarf2_evaluate_loc_desc_full) + : If the register was not saved, return a + new optimized out value. + * findvar.c (address_from_register): Likewise. + * frame.c (put_frame_register): Tweak error string to say the + register was not saved, rather than optimized out. + * infcmd.c (default_print_one_register_info): Adjust call to + val_print_optimized_out. Use value_of_register instead of + get_frame_register_value. + * mi/mi-main.c (output_register): Use value_of_register instead of + get_frame_register_value. + * valprint.c (valprint_check_validity): Likewise. + (val_print_optimized_out): New value parameter. If the value is + lval_register, print instead. + (value_check_printable, val_print_scalar_formatted): Adjust calls + to val_print_optimized_out. + * valprint.h (val_print_optimized_out): New value parameter. + * value.c (struct value) : Extend comment. + (error_value_optimized_out): New function. + (require_not_optimized_out): Use it. Use a different string for + lval_register values. + * value.h (error_value_optimized_out): New declaration. + * NEWS: Mention . + 2013-10-02 Joel Brobecker * symtab.c (compare_search_syms): Use FILENAME_CMP instead of diff --git a/gdb/NEWS b/gdb/NEWS index b44eb256bc..5a9f8db69a 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -15,6 +15,20 @@ * The "catch syscall" command now works on arm*-linux* targets. +* GDB now consistently shows "" when printing values of + registers the debug info indicates have not been saved in the frame + and there's nowhere to retrieve them from + (callee-saved/call-clobbered registers): + + (gdb) p $rax + $1 = + + (gdb) info registers rax + rax + + Before, the former would print "", and the latter + "*value not available*". + * Python scripting ** Frame filters and frame decorators have been added. diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c index e83d9791ee..1d7147cdd2 100644 --- a/gdb/cp-valprint.c +++ b/gdb/cp-valprint.c @@ -298,7 +298,7 @@ cp_print_value_fields (struct type *type, struct type *real_type, TYPE_FIELD_BITPOS (type, i), TYPE_FIELD_BITSIZE (type, i))) { - val_print_optimized_out (stream); + val_print_optimized_out (val, stream); } else { @@ -334,7 +334,7 @@ cp_print_value_fields (struct type *type, struct type *real_type, _(""), ex.message); else if (v == NULL) - val_print_optimized_out (stream); + val_print_optimized_out (NULL, stream); else cp_print_static_field (TYPE_FIELD_TYPE (type, i), v, stream, recurse + 1, diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index f5a80d3e78..705af69599 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2013-10-02 Pedro Alves + + * gdb.texinfo (Registers): Expand description of saved registers + in frames. Explain . + 2013-09-25 Doug Evans * gdb.texinfo (Debugging Output): Document set/show debug symfile. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 07d5068797..cf6f7d30a8 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -10031,10 +10031,33 @@ were exited and their saved registers restored. In order to see the true contents of hardware registers, you must select the innermost frame (with @samp{frame 0}). -However, @value{GDBN} must deduce where registers are saved, from the machine -code generated by your compiler. If some registers are not saved, or if -@value{GDBN} is unable to locate the saved registers, the selected stack -frame makes no difference. +@cindex caller-saved registers +@cindex call-clobbered registers +@cindex volatile registers +@cindex values +Usually ABIs reserve some registers as not needed to be saved by the +callee (a.k.a.: ``caller-saved'', ``call-clobbered'' or ``volatile'' +registers). It may therefore not be possible for @value{GDBN} to know +the value a register had before the call (in other words, in the outer +frame), if the register value has since been changed by the callee. +@value{GDBN} tries to deduce where the inner frame saved +(``callee-saved'') registers, from the debug info, unwind info, or the +machine code generated by your compiler. If some register is not +saved, and @value{GDBN} knows the register is ``caller-saved'' (via +its own knowledge of the ABI, or because the debug/unwind info +explicitly says the register's value is undefined), @value{GDBN} +displays @w{@samp{}} as the register's value. With targets +that @value{GDBN} has no knowledge of the register saving convention, +if a register was not saved by the callee, then its value and location +in the outer frame are assumed to be the same of the inner frame. +This is usually harmless, because if the register is call-clobbered, +the caller either does not care what is in the register after the +call, or has code to restore the value that it does care about. Note, +however, that if you change such a register in the outer frame, you +may also be affecting the inner frame. Also, the more ``outer'' the +frame is you're looking at, the more likely a call-clobbered +register's value is to be wrong, in the sense that it doesn't actually +represent the value the register had just before the call. @node Floating Point Hardware @section Floating Point Hardware diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c index c875d3a4af..8242dca93b 100644 --- a/gdb/dwarf2loc.c +++ b/gdb/dwarf2loc.c @@ -2290,11 +2290,21 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame, if (byte_offset != 0) error (_("cannot use offset on synthetic pointer to register")); do_cleanups (value_chain); - if (gdb_regnum != -1) - retval = value_from_register (type, gdb_regnum, frame); - else + if (gdb_regnum == -1) error (_("Unable to access DWARF register number %d"), dwarf_regnum); + retval = value_from_register (type, gdb_regnum, frame); + if (value_optimized_out (retval)) + { + /* This means the register has undefined value / was + not saved. As we're computing the location of some + variable etc. in the program, not a value for + inspecting a register ($pc, $sp, etc.), return a + generic optimized out value instead, so that we show + instead of . */ + do_cleanups (value_chain); + retval = allocate_optimized_out_value (type); + } } break; diff --git a/gdb/findvar.c b/gdb/findvar.c index 25242bea71..c3550b43f8 100644 --- a/gdb/findvar.c +++ b/gdb/findvar.c @@ -757,6 +757,15 @@ address_from_register (struct type *type, int regnum, struct frame_info *frame) value = value_from_register (type, regnum, frame); gdb_assert (value); + if (value_optimized_out (value)) + { + /* This function is used while computing a location expression. + Complain about the value being optimized out, rather than + letting value_as_address complain about some random register + the expression depends on not being saved. */ + error_value_optimized_out (); + } + result = value_as_address (value); release_value (value); value_free (value); diff --git a/gdb/frame.c b/gdb/frame.c index eace738903..59e31db692 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -1143,7 +1143,7 @@ put_frame_register (struct frame_info *frame, int regnum, frame_register (frame, regnum, &optim, &unavail, &lval, &addr, &realnum, NULL); if (optim) - error (_("Attempt to assign to a value that was optimized out.")); + error (_("Attempt to assign to a register that was not saved.")); switch (lval) { case lval_memory: diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 0659605281..20f8857069 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -2035,7 +2035,7 @@ default_print_one_register_info (struct ui_file *file, } else if (value_optimized_out (val)) { - val_print_optimized_out (file); + val_print_optimized_out (val, file); fprintf_filtered (file, "\n"); return; } @@ -2142,7 +2142,7 @@ default_print_registers_info (struct gdbarch *gdbarch, default_print_one_register_info (file, gdbarch_register_name (gdbarch, i), - get_frame_register_value (frame, i)); + value_of_register (i, frame)); } } diff --git a/gdb/jv-valprint.c b/gdb/jv-valprint.c index 3b90e54fec..cb89a85400 100644 --- a/gdb/jv-valprint.c +++ b/gdb/jv-valprint.c @@ -395,7 +395,7 @@ java_print_value_fields (struct type *type, const gdb_byte *valaddr, else if (!value_bits_valid (val, TYPE_FIELD_BITPOS (type, i), TYPE_FIELD_BITSIZE (type, i))) { - val_print_optimized_out (stream); + val_print_optimized_out (val, stream); } else { @@ -420,7 +420,7 @@ java_print_value_fields (struct type *type, const gdb_byte *valaddr, struct value *v = value_static_field (type, i); if (v == NULL) - val_print_optimized_out (stream); + val_print_optimized_out (NULL, stream); else { struct value_print_options opts; diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index a8405799d9..ea31367a82 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -1161,7 +1161,7 @@ output_register (struct frame_info *frame, int regnum, int format, { struct gdbarch *gdbarch = get_frame_arch (frame); struct ui_out *uiout = current_uiout; - struct value *val = get_frame_register_value (frame, regnum); + struct value *val = value_of_register (regnum, frame); struct cleanup *tuple_cleanup; struct value_print_options opts; struct ui_file *stb; diff --git a/gdb/p-valprint.c b/gdb/p-valprint.c index 05d4c6f821..e6d4b91c3a 100644 --- a/gdb/p-valprint.c +++ b/gdb/p-valprint.c @@ -629,7 +629,7 @@ pascal_object_print_value_fields (struct type *type, const gdb_byte *valaddr, else if (!value_bits_valid (val, TYPE_FIELD_BITPOS (type, i), TYPE_FIELD_BITSIZE (type, i))) { - val_print_optimized_out (stream); + val_print_optimized_out (val, stream); } else { @@ -657,7 +657,7 @@ pascal_object_print_value_fields (struct type *type, const gdb_byte *valaddr, v = value_field_bitfield (type, i, valaddr, offset, val); if (v == NULL) - val_print_optimized_out (stream); + val_print_optimized_out (NULL, stream); else pascal_object_print_static_field (v, stream, recurse + 1, options); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 3acc52180c..1eef53025e 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2013-10-02 Pedro Alves + + * gdb.dwarf2/dw2-reg-undefined.exp : Set to "". + * gdb.mi/mi-reg-undefined.exp (opt_out_pattern): Delete. + (not_saved_pattern): New. + Replace use of the former with the latter. + 2013-10-02 Pedro Alves * README (Board Settings): Document "exit_is_reliable". diff --git a/gdb/testsuite/gdb.dwarf2/dw2-reg-undefined.exp b/gdb/testsuite/gdb.dwarf2/dw2-reg-undefined.exp index 468664827a..44bf8e9e19 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-reg-undefined.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-reg-undefined.exp @@ -45,8 +45,8 @@ for {set f 0} {$f < 3} {incr f} { set pattern_r8_r9_print "$hex" set pattern_r8_r9_info "$hex\\s+$decimal" } else { - set pattern_rax_rbx_rcx_print "" - set pattern_rax_rbx_rcx_info "" + set pattern_rax_rbx_rcx_print "" + set pattern_rax_rbx_rcx_info "" set pattern_r8_r9_print "$hex" set pattern_r8_r9_info "$hex\\s+$decimal" } diff --git a/gdb/testsuite/gdb.mi/mi-reg-undefined.exp b/gdb/testsuite/gdb.mi/mi-reg-undefined.exp index 8bcbb21dbf..cb3a71615b 100644 --- a/gdb/testsuite/gdb.mi/mi-reg-undefined.exp +++ b/gdb/testsuite/gdb.mi/mi-reg-undefined.exp @@ -52,13 +52,13 @@ mi_gdb_test "111-stack-list-frames" \ "111\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"stop_frame\",.*\},frame=\{level=\"1\",addr=\"$hex\",func=\"first_frame\",.*\},frame=\{level=\"2\",addr=\"$hex\",func=\"main\",.*\}\\\]" \ "stack frame listing" -set opt_out_pattern "" +set not_saved_pattern "" for {set f 0} {$f < 3} {incr f} { if {${f} == 0} { set pattern_0_1_2 ${hex} } else { - set pattern_0_1_2 ${opt_out_pattern} + set pattern_0_1_2 ${not_saved_pattern} } mi_gdb_test "22${f}-data-list-register-values --thread 1 --frame ${f} x 0 1 2 8 9" \ diff --git a/gdb/valprint.c b/gdb/valprint.c index 0f6d65ef36..61492ccba9 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -314,7 +314,7 @@ valprint_check_validity (struct ui_file *stream, if (!value_bits_valid (val, TARGET_CHAR_BIT * embedded_offset, TARGET_CHAR_BIT * TYPE_LENGTH (type))) { - val_print_optimized_out (stream); + val_print_optimized_out (val, stream); return 0; } @@ -336,9 +336,12 @@ valprint_check_validity (struct ui_file *stream, } void -val_print_optimized_out (struct ui_file *stream) +val_print_optimized_out (const struct value *val, struct ui_file *stream) { - fprintf_filtered (stream, _("")); + if (val != NULL && value_lval_const (val) == lval_register) + fprintf_filtered (stream, _("")); + else + fprintf_filtered (stream, _("")); } void @@ -805,7 +808,7 @@ value_check_printable (struct value *val, struct ui_file *stream, if (options->summary && !val_print_scalar_type_p (value_type (val))) fprintf_filtered (stream, "..."); else - val_print_optimized_out (stream); + val_print_optimized_out (val, stream); return 0; } @@ -966,7 +969,7 @@ val_print_scalar_formatted (struct type *type, printed, because all bits contribute to its representation. */ if (!value_bits_valid (val, TARGET_CHAR_BIT * embedded_offset, TARGET_CHAR_BIT * TYPE_LENGTH (type))) - val_print_optimized_out (stream); + val_print_optimized_out (val, stream); else if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type))) val_print_unavailable (stream); else diff --git a/gdb/valprint.h b/gdb/valprint.h index e7073b6dfb..1d86ed73da 100644 --- a/gdb/valprint.h +++ b/gdb/valprint.h @@ -160,7 +160,8 @@ extern int read_string (CORE_ADDR addr, int len, int width, enum bfd_endian byte_order, gdb_byte **buffer, int *bytes_read); -extern void val_print_optimized_out (struct ui_file *stream); +extern void val_print_optimized_out (const struct value *val, + struct ui_file *stream); extern void val_print_unavailable (struct ui_file *stream); diff --git a/gdb/value.c b/gdb/value.c index bc1239d05a..d96d285cbd 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -197,8 +197,13 @@ struct value reset, be sure to consider this use as well! */ unsigned int lazy : 1; - /* If nonzero, this is the value of a variable which does not - actually exist in the program. */ + /* If nonzero, this is the value of a variable that does not + actually exist in the program. If nonzero, and LVAL is + lval_register, this is a register ($pc, $sp, etc., never a + program variable) that has not been saved in the frame. All + optimized-out values are treated pretty much the same, except + registers have a different string representation and related + error strings. */ unsigned int optimized_out : 1; /* If value is a variable, is it initialized or not. */ @@ -902,11 +907,22 @@ value_actual_type (struct value *value, int resolve_simple_types, return result; } +void +error_value_optimized_out (void) +{ + error (_("value has been optimized out")); +} + static void require_not_optimized_out (const struct value *value) { if (value->optimized_out) - error (_("value has been optimized out")); + { + if (value->lval == lval_register) + error (_("register has not been saved in frame")); + else + error_value_optimized_out (); + } } static void diff --git a/gdb/value.h b/gdb/value.h index 98dbadf368..db964e36e8 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -273,6 +273,11 @@ extern void set_value_lazy (struct value *value, int val); extern int value_stack (struct value *); extern void set_value_stack (struct value *value, int val); +/* Throw an error complaining that the value has been optimized + out. */ + +extern void error_value_optimized_out (void); + /* value_contents() and value_contents_raw() both return the address of the gdb buffer used to hold a copy of the contents of the lval. value_contents() is used when the contents of the buffer are needed -- 2.34.1