binutils-gdb/gdb/testsuite/gdb.dwarf2/dw2-reg-undefined.exp

88 lines
3 KiB
Text
Raw Normal View History

# Copyright 2013-2023 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
load_lib dwarf.exp
# This test can only be run on targets which support DWARF-2 and use gas.
require dwarf2_support
# This test can only be run on x86_64 targets.
require is_x86_64_m64_target
standard_testfile .S
if { [prepare_for_testing "failed to prepare" $testfile $srcfile {nodebug nopie}] } {
return -1
}
if ![runto stop_frame] {
perror "Failed to stop in stop_frame"
return -1
}
gdb_test "bt" "#0 (0x\[0-9a-f\]+ in )?stop_frame \[^\r\n\]*\r\n#1 \[^\r\n\]*first_frame \[^\r\n\]*\r\n#2 \[^\r\n\]*main\[^\r\n\]*" \
"backtrace from stop_frame"
for {set f 0} {$f < 3} {incr f} {
if {${f} == 0} {
set pattern_rax_rbx_rcx_print "$hex"
set pattern_rax_rbx_rcx_info "$hex\\s+$decimal"
set pattern_r8_r9_print "$hex"
set pattern_r8_r9_info "$hex\\s+$decimal"
} else {
Print registers not saved in the frame as "<not saved>" instead of "<optimized out>". Currently, in some scenarios, GDB prints <optimized out> when printing outer frame registers. An <optimized out> 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 = <optimized out> (gdb) info registers rax rax <optimized out> After patch: (gdb) p/x $rax $1 = <not saved> (gdb) info registers rax rax <not saved> However, if for some reason the debug info describes a variable as being in such a register (**), we still want to print <optimized out> when printing the variable. IOW, <not saved> 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 <https://sourceware.org/ml/gdb-patches/2012-08/msg00787.html>. 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 <optimized out>. 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 <palves@redhat.com> * 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) <DWARF_VALUE_REGISTER>: 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 <not saved> 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) <optimized_out>: 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 <not saved>. gdb/testsuite/ 2013-10-02 Pedro Alves <palves@redhat.com> * gdb.dwarf2/dw2-reg-undefined.exp <pattern_rax_rbx_rcx_print, pattern_rax_rbx_rcx_info>: Set to "<not saved>". * 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 <palves@redhat.com> * gdb.texinfo (Registers): Expand description of saved registers in frames. Explain <not saved>.
2013-10-02 16:15:46 +00:00
set pattern_rax_rbx_rcx_print "<not saved>"
set pattern_rax_rbx_rcx_info "<not saved>"
set pattern_r8_r9_print "$hex"
set pattern_r8_r9_info "$hex\\s+$decimal"
}
# Select frame.
Fix test names starting with uppercase using gdb_test on a single line. Changes in v3: Fixed incorrect substitutions. This fixes offender testcases that have test names starting with uppercase when using gdb_test in a single line construct. gdb/testsuite/ChangeLog 2016-12-01 Luis Machado <lgustavo@codesourcery.com> Fix test names starting with uppercase throughout the files. * gdb.arch/i386-mpx-simple_segv.exp * gdb.arch/i386-mpx.exp * gdb.arch/i386-permbkpt.exp * gdb.arch/pa-nullify.exp * gdb.arch/powerpc-d128-regs.exp * gdb.arch/vsx-regs.exp * gdb.base/bfp-test.exp * gdb.base/break.exp * gdb.base/breakpoint-shadow.exp * gdb.base/callfuncs.exp * gdb.base/charset.exp * gdb.base/commands.exp * gdb.base/completion.exp * gdb.base/dfp-test.exp * gdb.base/echo.exp * gdb.base/ending-run.exp * gdb.base/eval.exp * gdb.base/expand-psymtabs.exp * gdb.base/float128.exp * gdb.base/floatn.exp * gdb.base/foll-exec-mode.exp * gdb.base/gdb1056.exp * gdb.base/gdb11531.exp * gdb.base/kill-after-signal.exp * gdb.base/multi-forks.exp * gdb.base/overlays.exp * gdb.base/pending.exp * gdb.base/sepdebug.exp * gdb.base/testenv.exp * gdb.base/valgrind-db-attach.exp * gdb.base/watch_thread_num.exp * gdb.base/watchpoint-cond-gone.exp * gdb.base/watchpoint.exp * gdb.base/watchpoints.exp * gdb.cp/arg-reference.exp * gdb.cp/baseenum.exp * gdb.cp/operator.exp * gdb.cp/shadow.exp * gdb.dwarf2/dw2-op-out-param.exp * gdb.dwarf2/dw2-reg-undefined.exp * gdb.go/chan.exp * gdb.go/hello.exp * gdb.go/integers.exp * gdb.go/methods.exp * gdb.go/package.exp * gdb.guile/scm-parameter.exp * gdb.guile/scm-progspace.exp * gdb.guile/scm-value.exp * gdb.mi/mi-pending.exp * gdb.mi/user-selected-context-sync.exp * gdb.multi/multi-attach.exp * gdb.multi/tids.exp * gdb.opt/clobbered-registers-O2.exp * gdb.pascal/floats.exp * gdb.pascal/integers.exp * gdb.python/py-block.exp * gdb.python/py-events.exp * gdb.python/py-parameter.exp * gdb.python/py-symbol.exp * gdb.python/py-symtab.exp * gdb.python/py-type.exp * gdb.python/py-value.exp * gdb.python/py-xmethods.exp * gdb.python/python.exp * gdb.reverse/break-precsave.exp * gdb.reverse/consecutive-precsave.exp * gdb.reverse/finish-precsave.exp * gdb.reverse/i386-precsave.exp * gdb.reverse/machinestate-precsave.exp * gdb.reverse/sigall-precsave.exp * gdb.reverse/solib-precsave.exp * gdb.reverse/step-precsave.exp * gdb.reverse/until-precsave.exp * gdb.reverse/watch-precsave.exp * gdb.server/ext-attach.exp * gdb.server/ext-restart.exp * gdb.server/ext-run.exp * gdb.server/ext-wrapper.exp * gdb.stabs/gdb11479.exp * gdb.stabs/weird.exp * gdb.threads/attach-many-short-lived-threads.exp * gdb.threads/kill.exp * gdb.threads/watchpoint-fork.exp
2016-12-01 14:41:23 -06:00
gdb_test "frame ${f}" "#${f}.*" "switch to frame ${f}"
gdb_test "p/x \$rax" ".*$pattern_rax_rbx_rcx_print.*" \
"print \$rax in frame ${f}"
gdb_test "p/x \$rbx" "$pattern_rax_rbx_rcx_print" \
"print \$rbx in frame ${f}"
gdb_test "p/x \$rcx" "$pattern_rax_rbx_rcx_print" \
"print \$rcx in frame ${f}"
gdb_test "p/x \$r8" "$pattern_r8_r9_print" "print \$r8 in frame ${f}"
gdb_test "p/x \$r9" "$pattern_r8_r9_print" "print \$r9 in frame ${f}"
# Display register values.
gdb_test "info registers rax rbx rcx r8 r9" \
[multi_line "rax\\s+${pattern_rax_rbx_rcx_info}\\s*" \
"rbx\\s+${pattern_rax_rbx_rcx_info}\\s*" \
"rcx\\s+${pattern_rax_rbx_rcx_info}\\s*" \
"r8\\s+${pattern_r8_r9_info}\\s*" \
"r9\\s+${pattern_r8_r9_info}\\s*"] \
"Check values of rax, rbx, rcx, r8, r9 in frame ${f}"
}
gdb: make frame_unwind_got_optimized return a not_lval value TLDR: frame_unwind_got_optimized uses wrong frame id value, trying to fix it makes GDB sad, return not_lval value and don't use frame id value instead. Longer version: The `prev_register` method of the `frame_unwind` interface corresponds to asking the question: "where did this frame - passed as a parameter - save the value this register had in its caller frame?". When "this frame" did not save that register value (DW_CFA_undefined in DWARF), the implementation can use the `frame_unwind_got_optimized` function to create a struct value that represents the optimized out / not saved register. `frame_unwind_got_optimized` marks the value as fully optimized out, sets the lval field to lval_register and assigns the required data for lval_register: the next frame id and the register number. The problem is that it uses the frame id from the wrong frame (see below for in depth explanation). In practice, this is not problematic because the frame id is never used: the value is already not lazy (and is marked as optimized out), so the value is never fetched from the target. When trying to change it to put the right next frame id in the value, we bump into problems: computing the frame id for some frame requires unwinding some register, if that register is not saved / optimized out, we try to get the frame id that we are currently computing. This patch addresses the problem by changing `frame_unwind_got_optimized` to return a not_lval value instead. Doing so, we don't need to put a frame id, so we don't hit that problem. It may seem like an unnecessary change today, because it looks like we're fixing something that is not broken (from the user point of view). However, the bug becomes user visible with the following patches, where inline frames are involved. I put this change in its own patch to keep it logically separate. Let's now illustrate how we are putting the wrong frame id in the value returned by `frame_unwind_got_optimized`. Let's assume this stack: frame #0 frame #1 frame #2 frame #3 Let's suppose that we are calling `frame_unwind_register_value` with frame #2 as the "next_frame" parameter and some register number X as the regnum parameter. That is like asking the question "where did frame #2 save frame #3's value for register X". `frame_unwind_register_value` calls the frame unwinder's `prev_register` method, which in our case is `dwarf2_frame_prev_register`. Note that in `dwarf2_frame_prev_register`, the parameter is now called `this_frame`, but its value is still frame #2, and we are still looking for where frame #2 saved frame #3's value of register X. Let's now suppose that frame #2's CFI explicitly indicates that the register X is was not saved (DW_CFA_undefined). We go into `frame_unwind_got_optimized`. In `frame_unwind_got_optimized`, the intent is to create a value that represents register X in frame #3. An lval_register value requires that we specify the id of the _next_ frame, that is the frame from which we would need to unwind in order to get the value. Therefore, we would want to put the id of frame #2 in there. However, `frame_unwind_got_optimized` does: VALUE_NEXT_FRAME_ID (val) = get_frame_id (get_next_frame_sentinel_okay (frame)); where `frame` is frame #2. The get_next_frame_sentinel_okay call returns frame #1, so we end up putting frame #1's id in the value. Let's now pretend that we try to "fix" it by placing the right frame id, in other words doing this change: --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -260,8 +260,7 @@ frame_unwind_got_optimized (struct frame_info *frame, int regnum) mark_value_bytes_optimized_out (val, 0, TYPE_LENGTH (type)); VALUE_LVAL (val) = lval_register; VALUE_REGNUM (val) = regnum; - VALUE_NEXT_FRAME_ID (val) - = get_frame_id (get_next_frame_sentinel_okay (frame)); + VALUE_NEXT_FRAME_ID (val) = get_frame_id (frame); return val; } This makes some tests fails, such as gdb.dwarf2/dw2-undefined-ret-addr.exp, like so: ... #9 0x0000557a8ab15a5d in internal_error (file=0x557a8b31ef80 "/home/simark/src/binutils-gdb/gdb/frame.c", line=623, fmt=0x557a8b31efe0 "%s: Assertion `%s' failed.") at /home/simark/src/binutils-gdb/gdbsupport/errors.cc:55 #10 0x0000557a87f816d6 in get_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:623 #11 0x0000557a87f7cac7 in frame_unwind_got_optimized (frame=0x62100034bde0, regnum=16) at /home/simark/src/binutils-gdb/gdb/frame-unwind.c:264 #12 0x0000557a87a71a76 in dwarf2_frame_prev_register (this_frame=0x62100034bde0, this_cache=0x62100034bdf8, regnum=16) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1267 #13 0x0000557a87f86621 in frame_unwind_register_value (next_frame=0x62100034bde0, regnum=16) at /home/simark/src/binutils-gdb/gdb/frame.c:1288 #14 0x0000557a87f855d5 in frame_register_unwind (next_frame=0x62100034bde0, regnum=16, optimizedp=0x7fff5f459070, unavailablep=0x7fff5f459080, lvalp=0x7fff5f4590a0, addrp=0x7fff5f4590b0, realnump=0x7fff5f459090, bufferp=0x7fff5f459150 "") at /home/simark/src/binutils-gdb/gdb/frame.c:1191 #15 0x0000557a87f860ef in frame_unwind_register (next_frame=0x62100034bde0, regnum=16, buf=0x7fff5f459150 "") at /home/simark/src/binutils-gdb/gdb/frame.c:1247 #16 0x0000557a881875f9 in i386_unwind_pc (gdbarch=0x621000190110, next_frame=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/i386-tdep.c:1971 #17 0x0000557a87fe58a5 in gdbarch_unwind_pc (gdbarch=0x621000190110, next_frame=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/gdbarch.c:3062 #18 0x0000557a87a6267b in dwarf2_tailcall_sniffer_first (this_frame=0x62100034bde0, tailcall_cachep=0x62100034bee0, entry_cfa_sp_offsetp=0x7fff5f4593f0) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame-tailcall.c:387 #19 0x0000557a87a70cdf in dwarf2_frame_cache (this_frame=0x62100034bde0, this_cache=0x62100034bdf8) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1198 #20 0x0000557a87a711c2 in dwarf2_frame_this_id (this_frame=0x62100034bde0, this_cache=0x62100034bdf8, this_id=0x62100034be40) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1226 #21 0x0000557a87f81167 in compute_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:587 #22 0x0000557a87f81803 in get_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:635 #23 0x0000557a87f7efef in scoped_restore_selected_frame::scoped_restore_selected_frame (this=0x7fff5f459920) at /home/simark/src/binutils-gdb/gdb/frame.c:320 #24 0x0000557a891488ae in print_frame_args (fp_opts=..., func=0x621000183b90, frame=0x62100034bde0, num=-1, stream=0x6030000caa20) at /home/simark/src/binutils-gdb/gdb/stack.c:750 #25 0x0000557a8914e87a in print_frame (fp_opts=..., frame=0x62100034bde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, sal=...) at /home/simark/src/binutils-gdb/gdb/stack.c:1394 #26 0x0000557a8914c2ae in print_frame_info (fp_opts=..., frame=0x62100034bde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, set_current_sal=1) at /home/simark/src/binutils-gdb/gdb/stack.c:1119 ... We end up calling get_frame_id (in the hunk above, frame #10) while we are computing it (frame #21), and that's not good. Now, the question is how do we fix this. I suggest making the unwinder return a not_lval value in this case. The reason why we return an lval_register here is to make sure that this is printed as "not saved" and not "optimized out" down the line. See these two commits: 1. 901461f8eb40 ("Print registers not saved in the frame as "<not saved>" instead of "<optimized out>"."). 2. 6bd273ae450b ("Make "set debug frame 1" output print <not saved> instead of <optimized out>.") The current design (introduced by the first commit) is to check the value's lval to choose which one to print (see val_print_optimized_out). Making the unwinder return not_lval instead of lval_register doesn't break "not saved" when doing "print $rax" or "info registers", because value_fetch_lazy_register only consumes the contents and optimized-out property from the value the unwinder returned. The value being un-lazified stays an lval_register. I believe that this is a correct technical solution (and not just papering over the problem), because what we expect of unwinders is to tell us where a given register's value is saved. If the value is saved in memory, -> lval_memory. If the value is saved in some other register of the next frame, -> lval_register. If the value is not saved, it doesn't really make sense to return an lval_register value. not_lval would be more appropriate. If the code then wants to represent an optimized out register value (like value_fetch_lazy_register does), then it's a separate concern which shouldn't involve the unwinder. This change breaks the output of "set debug frame 1" though (introduced by the second commit), since that logging statement consumes the return value of the unwinder directly. To keep the correct behavior, just make `frame_unwind_register_value` call `val_print_not_saved` directly, instead of `val_print_optimized_out`. This is fine because we know in this context that we are always talking about a register value, and that we want to show "not saved" for those. I augmented the gdb.dwarf2/dw2-reg-undefined.exp test case to test some cases I stumbled on while working on this, which I think are not tested anywhere: - the "set debug frame 1" debug output mentioned above. It's just debug output, but if we want to make sure it doesn't change, it should be tested - printing not-saved register values from the history (should print not saved) - copying a not-saved register value in a convenience variable. In this case, we expect that printing the convenience variable shows "optimized out", because we copied the value, not the property of where the value came from. gdb/ChangeLog: * frame-unwind.c (frame_unwind_got_optimized): Don't set regnum/frame in value. Call allocate_value_lazy. * frame.c (frame_unwind_register_value): Use val_print_not_saved. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-reg-undefined.exp: Test "set debug frame 1" output, printing a "not saved" value from history and printing a convenience variable created from a "not saved" value. Change-Id: If451739a3ef7a5b453b1f50707e21ce16d74807e
2020-08-31 13:22:54 -04:00
# Test that the debug log statement in frame_unwind_register_value produces
# "not saved" and not "optimized out".
gdb: make frame_unwind_got_optimized return a not_lval value TLDR: frame_unwind_got_optimized uses wrong frame id value, trying to fix it makes GDB sad, return not_lval value and don't use frame id value instead. Longer version: The `prev_register` method of the `frame_unwind` interface corresponds to asking the question: "where did this frame - passed as a parameter - save the value this register had in its caller frame?". When "this frame" did not save that register value (DW_CFA_undefined in DWARF), the implementation can use the `frame_unwind_got_optimized` function to create a struct value that represents the optimized out / not saved register. `frame_unwind_got_optimized` marks the value as fully optimized out, sets the lval field to lval_register and assigns the required data for lval_register: the next frame id and the register number. The problem is that it uses the frame id from the wrong frame (see below for in depth explanation). In practice, this is not problematic because the frame id is never used: the value is already not lazy (and is marked as optimized out), so the value is never fetched from the target. When trying to change it to put the right next frame id in the value, we bump into problems: computing the frame id for some frame requires unwinding some register, if that register is not saved / optimized out, we try to get the frame id that we are currently computing. This patch addresses the problem by changing `frame_unwind_got_optimized` to return a not_lval value instead. Doing so, we don't need to put a frame id, so we don't hit that problem. It may seem like an unnecessary change today, because it looks like we're fixing something that is not broken (from the user point of view). However, the bug becomes user visible with the following patches, where inline frames are involved. I put this change in its own patch to keep it logically separate. Let's now illustrate how we are putting the wrong frame id in the value returned by `frame_unwind_got_optimized`. Let's assume this stack: frame #0 frame #1 frame #2 frame #3 Let's suppose that we are calling `frame_unwind_register_value` with frame #2 as the "next_frame" parameter and some register number X as the regnum parameter. That is like asking the question "where did frame #2 save frame #3's value for register X". `frame_unwind_register_value` calls the frame unwinder's `prev_register` method, which in our case is `dwarf2_frame_prev_register`. Note that in `dwarf2_frame_prev_register`, the parameter is now called `this_frame`, but its value is still frame #2, and we are still looking for where frame #2 saved frame #3's value of register X. Let's now suppose that frame #2's CFI explicitly indicates that the register X is was not saved (DW_CFA_undefined). We go into `frame_unwind_got_optimized`. In `frame_unwind_got_optimized`, the intent is to create a value that represents register X in frame #3. An lval_register value requires that we specify the id of the _next_ frame, that is the frame from which we would need to unwind in order to get the value. Therefore, we would want to put the id of frame #2 in there. However, `frame_unwind_got_optimized` does: VALUE_NEXT_FRAME_ID (val) = get_frame_id (get_next_frame_sentinel_okay (frame)); where `frame` is frame #2. The get_next_frame_sentinel_okay call returns frame #1, so we end up putting frame #1's id in the value. Let's now pretend that we try to "fix" it by placing the right frame id, in other words doing this change: --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -260,8 +260,7 @@ frame_unwind_got_optimized (struct frame_info *frame, int regnum) mark_value_bytes_optimized_out (val, 0, TYPE_LENGTH (type)); VALUE_LVAL (val) = lval_register; VALUE_REGNUM (val) = regnum; - VALUE_NEXT_FRAME_ID (val) - = get_frame_id (get_next_frame_sentinel_okay (frame)); + VALUE_NEXT_FRAME_ID (val) = get_frame_id (frame); return val; } This makes some tests fails, such as gdb.dwarf2/dw2-undefined-ret-addr.exp, like so: ... #9 0x0000557a8ab15a5d in internal_error (file=0x557a8b31ef80 "/home/simark/src/binutils-gdb/gdb/frame.c", line=623, fmt=0x557a8b31efe0 "%s: Assertion `%s' failed.") at /home/simark/src/binutils-gdb/gdbsupport/errors.cc:55 #10 0x0000557a87f816d6 in get_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:623 #11 0x0000557a87f7cac7 in frame_unwind_got_optimized (frame=0x62100034bde0, regnum=16) at /home/simark/src/binutils-gdb/gdb/frame-unwind.c:264 #12 0x0000557a87a71a76 in dwarf2_frame_prev_register (this_frame=0x62100034bde0, this_cache=0x62100034bdf8, regnum=16) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1267 #13 0x0000557a87f86621 in frame_unwind_register_value (next_frame=0x62100034bde0, regnum=16) at /home/simark/src/binutils-gdb/gdb/frame.c:1288 #14 0x0000557a87f855d5 in frame_register_unwind (next_frame=0x62100034bde0, regnum=16, optimizedp=0x7fff5f459070, unavailablep=0x7fff5f459080, lvalp=0x7fff5f4590a0, addrp=0x7fff5f4590b0, realnump=0x7fff5f459090, bufferp=0x7fff5f459150 "") at /home/simark/src/binutils-gdb/gdb/frame.c:1191 #15 0x0000557a87f860ef in frame_unwind_register (next_frame=0x62100034bde0, regnum=16, buf=0x7fff5f459150 "") at /home/simark/src/binutils-gdb/gdb/frame.c:1247 #16 0x0000557a881875f9 in i386_unwind_pc (gdbarch=0x621000190110, next_frame=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/i386-tdep.c:1971 #17 0x0000557a87fe58a5 in gdbarch_unwind_pc (gdbarch=0x621000190110, next_frame=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/gdbarch.c:3062 #18 0x0000557a87a6267b in dwarf2_tailcall_sniffer_first (this_frame=0x62100034bde0, tailcall_cachep=0x62100034bee0, entry_cfa_sp_offsetp=0x7fff5f4593f0) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame-tailcall.c:387 #19 0x0000557a87a70cdf in dwarf2_frame_cache (this_frame=0x62100034bde0, this_cache=0x62100034bdf8) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1198 #20 0x0000557a87a711c2 in dwarf2_frame_this_id (this_frame=0x62100034bde0, this_cache=0x62100034bdf8, this_id=0x62100034be40) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1226 #21 0x0000557a87f81167 in compute_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:587 #22 0x0000557a87f81803 in get_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:635 #23 0x0000557a87f7efef in scoped_restore_selected_frame::scoped_restore_selected_frame (this=0x7fff5f459920) at /home/simark/src/binutils-gdb/gdb/frame.c:320 #24 0x0000557a891488ae in print_frame_args (fp_opts=..., func=0x621000183b90, frame=0x62100034bde0, num=-1, stream=0x6030000caa20) at /home/simark/src/binutils-gdb/gdb/stack.c:750 #25 0x0000557a8914e87a in print_frame (fp_opts=..., frame=0x62100034bde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, sal=...) at /home/simark/src/binutils-gdb/gdb/stack.c:1394 #26 0x0000557a8914c2ae in print_frame_info (fp_opts=..., frame=0x62100034bde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, set_current_sal=1) at /home/simark/src/binutils-gdb/gdb/stack.c:1119 ... We end up calling get_frame_id (in the hunk above, frame #10) while we are computing it (frame #21), and that's not good. Now, the question is how do we fix this. I suggest making the unwinder return a not_lval value in this case. The reason why we return an lval_register here is to make sure that this is printed as "not saved" and not "optimized out" down the line. See these two commits: 1. 901461f8eb40 ("Print registers not saved in the frame as "<not saved>" instead of "<optimized out>"."). 2. 6bd273ae450b ("Make "set debug frame 1" output print <not saved> instead of <optimized out>.") The current design (introduced by the first commit) is to check the value's lval to choose which one to print (see val_print_optimized_out). Making the unwinder return not_lval instead of lval_register doesn't break "not saved" when doing "print $rax" or "info registers", because value_fetch_lazy_register only consumes the contents and optimized-out property from the value the unwinder returned. The value being un-lazified stays an lval_register. I believe that this is a correct technical solution (and not just papering over the problem), because what we expect of unwinders is to tell us where a given register's value is saved. If the value is saved in memory, -> lval_memory. If the value is saved in some other register of the next frame, -> lval_register. If the value is not saved, it doesn't really make sense to return an lval_register value. not_lval would be more appropriate. If the code then wants to represent an optimized out register value (like value_fetch_lazy_register does), then it's a separate concern which shouldn't involve the unwinder. This change breaks the output of "set debug frame 1" though (introduced by the second commit), since that logging statement consumes the return value of the unwinder directly. To keep the correct behavior, just make `frame_unwind_register_value` call `val_print_not_saved` directly, instead of `val_print_optimized_out`. This is fine because we know in this context that we are always talking about a register value, and that we want to show "not saved" for those. I augmented the gdb.dwarf2/dw2-reg-undefined.exp test case to test some cases I stumbled on while working on this, which I think are not tested anywhere: - the "set debug frame 1" debug output mentioned above. It's just debug output, but if we want to make sure it doesn't change, it should be tested - printing not-saved register values from the history (should print not saved) - copying a not-saved register value in a convenience variable. In this case, we expect that printing the convenience variable shows "optimized out", because we copied the value, not the property of where the value came from. gdb/ChangeLog: * frame-unwind.c (frame_unwind_got_optimized): Don't set regnum/frame in value. Call allocate_value_lazy. * frame.c (frame_unwind_register_value): Use val_print_not_saved. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-reg-undefined.exp: Test "set debug frame 1" output, printing a "not saved" value from history and printing a convenience variable created from a "not saved" value. Change-Id: If451739a3ef7a5b453b1f50707e21ce16d74807e
2020-08-31 13:22:54 -04:00
gdb_test "set debug frame 1"
gdb_test {print $rax} [multi_line \
gdb: introduce FRAME_SCOPED_DEBUG_ENTER_EXIT Introduce FRAME_SCOPED_DEBUG_ENTER_EXIT and use it to print enter/exit messages in important frame-related functions. I think this helps understand which lower-level operations are done as part of which higher-level operation. And it helps visually skip over a higher-level operation you are not interested in. Here's an example, combined with some py-unwind messages: [frame] frame_unwind_find_by_frame: enter [frame] frame_unwind_find_by_frame: this_frame=0 [frame] frame_unwind_try_unwinder: trying unwinder "dummy" [frame] frame_unwind_try_unwinder: no [frame] frame_unwind_try_unwinder: trying unwinder "dwarf2 tailcall" [frame] frame_unwind_try_unwinder: no [frame] frame_unwind_try_unwinder: trying unwinder "inline" [frame] frame_unwind_try_unwinder: no [frame] frame_unwind_try_unwinder: trying unwinder "jit" [frame] frame_unwind_try_unwinder: no [frame] frame_unwind_try_unwinder: trying unwinder "python" [py-unwind] pyuw_sniffer: enter [frame] frame_unwind_register_value: enter [frame] frame_unwind_register_value: frame=-1, regnum=7(rsp) [frame] frame_unwind_register_value: -> register=7 bytes=[40ddffffff7f0000] [frame] frame_unwind_register_value: exit [py-unwind] pyuw_sniffer: frame=0, sp=0x7fffffffdd40, pc=0x5555555551ec [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_eq: l={stack=<sentinel>,!code,special=0x0000000000000000}, r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_unwind_register_value: enter [frame] frame_unwind_register_value: frame=-1, regnum=6(rbp) [frame] frame_unwind_register_value: -> register=6 bytes=[50ddffffff7f0000] [frame] frame_unwind_register_value: exit [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_eq: l={stack=<sentinel>,!code,special=0x0000000000000000}, r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] get_prev_frame: enter [frame] get_prev_frame_always_1: enter [frame] get_prev_frame_always_1: this_frame=-1 [frame] get_prev_frame_always_1: -> {level=0,type=NORMAL_FRAME,unwind=0x5588ee3d17c0,pc=0x5555555551ec,id=<not computed>,func=<unknown>} // cached [frame] get_prev_frame_always_1: exit [frame] get_prev_frame: exit [frame] value_fetch_lazy_register: (frame=0, regnum=6(rbp), ...) -> register=6 bytes=[50ddffffff7f0000] [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_eq: l={stack=<sentinel>,!code,special=0x0000000000000000}, r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_unwind_register_value: enter [frame] frame_unwind_register_value: frame=-1, regnum=7(rsp) [frame] frame_unwind_register_value: -> register=7 bytes=[40ddffffff7f0000] [frame] frame_unwind_register_value: exit [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_eq: l={stack=<sentinel>,!code,special=0x0000000000000000}, r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] get_prev_frame: enter [frame] get_prev_frame_always_1: enter [frame] get_prev_frame_always_1: this_frame=-1 [frame] get_prev_frame_always_1: -> {level=0,type=NORMAL_FRAME,unwind=0x5588ee3d1824,pc=0x5555555551ec,id=<not computed>,func=<unknown>} // cached [frame] get_prev_frame_always_1: exit [frame] get_prev_frame: exit [frame] value_fetch_lazy_register: (frame=0, regnum=7(rsp), ...) -> register=7 bytes=[40ddffffff7f0000] [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_eq: l={stack=<sentinel>,!code,special=0x0000000000000000}, r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_unwind_register_value: enter [frame] frame_unwind_register_value: frame=-1, regnum=16(rip) [frame] frame_unwind_register_value: -> register=16 bytes=[ec51555555550000] [frame] frame_unwind_register_value: exit [frame] frame_id_p: l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] frame_id_eq: l={stack=<sentinel>,!code,special=0x0000000000000000}, r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1 [frame] get_prev_frame: enter [frame] get_prev_frame_always_1: enter [frame] get_prev_frame_always_1: this_frame=-1 [frame] get_prev_frame_always_1: -> {level=0,type=NORMAL_FRAME,unwind=0x5588ee3d1888,pc=0x5555555551ec,id=<not computed>,func=<unknown>} // cached [frame] get_prev_frame_always_1: exit [frame] get_prev_frame: exit [frame] value_fetch_lazy_register: (frame=0, regnum=16(rip), ...) -> register=16 bytes=[ec51555555550000] [py-unwind] pyuw_sniffer: frame claimed by unwinder test unwinder [py-unwind] pyuw_sniffer: exit [frame] frame_unwind_try_unwinder: yes [frame] frame_unwind_find_by_frame: exit gdb/ChangeLog: * frame.h (FRAME_SCOPED_DEBUG_ENTER_EXIT): New. * frame.c (compute_frame_id, get_prev_frame_always_1, get_prev_frame): Use FRAME_SCOPED_DEBUG_ENTER_EXIT. * frame-unwind.c (frame_unwind_find_by_frame): Likewise. (frame_unwind_register_value): Likewise. Change-Id: I45b69b4ed962e70572bc55b8adfb211483c1eeed
2021-06-29 12:05:14 -04:00
{ \[frame\] frame_unwind_register_value: frame=0, regnum=0\(rax\)} \
{ \[frame\] frame_unwind_register_value: -> <not saved>} \
{.*}]
gdb: make frame_unwind_got_optimized return a not_lval value TLDR: frame_unwind_got_optimized uses wrong frame id value, trying to fix it makes GDB sad, return not_lval value and don't use frame id value instead. Longer version: The `prev_register` method of the `frame_unwind` interface corresponds to asking the question: "where did this frame - passed as a parameter - save the value this register had in its caller frame?". When "this frame" did not save that register value (DW_CFA_undefined in DWARF), the implementation can use the `frame_unwind_got_optimized` function to create a struct value that represents the optimized out / not saved register. `frame_unwind_got_optimized` marks the value as fully optimized out, sets the lval field to lval_register and assigns the required data for lval_register: the next frame id and the register number. The problem is that it uses the frame id from the wrong frame (see below for in depth explanation). In practice, this is not problematic because the frame id is never used: the value is already not lazy (and is marked as optimized out), so the value is never fetched from the target. When trying to change it to put the right next frame id in the value, we bump into problems: computing the frame id for some frame requires unwinding some register, if that register is not saved / optimized out, we try to get the frame id that we are currently computing. This patch addresses the problem by changing `frame_unwind_got_optimized` to return a not_lval value instead. Doing so, we don't need to put a frame id, so we don't hit that problem. It may seem like an unnecessary change today, because it looks like we're fixing something that is not broken (from the user point of view). However, the bug becomes user visible with the following patches, where inline frames are involved. I put this change in its own patch to keep it logically separate. Let's now illustrate how we are putting the wrong frame id in the value returned by `frame_unwind_got_optimized`. Let's assume this stack: frame #0 frame #1 frame #2 frame #3 Let's suppose that we are calling `frame_unwind_register_value` with frame #2 as the "next_frame" parameter and some register number X as the regnum parameter. That is like asking the question "where did frame #2 save frame #3's value for register X". `frame_unwind_register_value` calls the frame unwinder's `prev_register` method, which in our case is `dwarf2_frame_prev_register`. Note that in `dwarf2_frame_prev_register`, the parameter is now called `this_frame`, but its value is still frame #2, and we are still looking for where frame #2 saved frame #3's value of register X. Let's now suppose that frame #2's CFI explicitly indicates that the register X is was not saved (DW_CFA_undefined). We go into `frame_unwind_got_optimized`. In `frame_unwind_got_optimized`, the intent is to create a value that represents register X in frame #3. An lval_register value requires that we specify the id of the _next_ frame, that is the frame from which we would need to unwind in order to get the value. Therefore, we would want to put the id of frame #2 in there. However, `frame_unwind_got_optimized` does: VALUE_NEXT_FRAME_ID (val) = get_frame_id (get_next_frame_sentinel_okay (frame)); where `frame` is frame #2. The get_next_frame_sentinel_okay call returns frame #1, so we end up putting frame #1's id in the value. Let's now pretend that we try to "fix" it by placing the right frame id, in other words doing this change: --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -260,8 +260,7 @@ frame_unwind_got_optimized (struct frame_info *frame, int regnum) mark_value_bytes_optimized_out (val, 0, TYPE_LENGTH (type)); VALUE_LVAL (val) = lval_register; VALUE_REGNUM (val) = regnum; - VALUE_NEXT_FRAME_ID (val) - = get_frame_id (get_next_frame_sentinel_okay (frame)); + VALUE_NEXT_FRAME_ID (val) = get_frame_id (frame); return val; } This makes some tests fails, such as gdb.dwarf2/dw2-undefined-ret-addr.exp, like so: ... #9 0x0000557a8ab15a5d in internal_error (file=0x557a8b31ef80 "/home/simark/src/binutils-gdb/gdb/frame.c", line=623, fmt=0x557a8b31efe0 "%s: Assertion `%s' failed.") at /home/simark/src/binutils-gdb/gdbsupport/errors.cc:55 #10 0x0000557a87f816d6 in get_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:623 #11 0x0000557a87f7cac7 in frame_unwind_got_optimized (frame=0x62100034bde0, regnum=16) at /home/simark/src/binutils-gdb/gdb/frame-unwind.c:264 #12 0x0000557a87a71a76 in dwarf2_frame_prev_register (this_frame=0x62100034bde0, this_cache=0x62100034bdf8, regnum=16) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1267 #13 0x0000557a87f86621 in frame_unwind_register_value (next_frame=0x62100034bde0, regnum=16) at /home/simark/src/binutils-gdb/gdb/frame.c:1288 #14 0x0000557a87f855d5 in frame_register_unwind (next_frame=0x62100034bde0, regnum=16, optimizedp=0x7fff5f459070, unavailablep=0x7fff5f459080, lvalp=0x7fff5f4590a0, addrp=0x7fff5f4590b0, realnump=0x7fff5f459090, bufferp=0x7fff5f459150 "") at /home/simark/src/binutils-gdb/gdb/frame.c:1191 #15 0x0000557a87f860ef in frame_unwind_register (next_frame=0x62100034bde0, regnum=16, buf=0x7fff5f459150 "") at /home/simark/src/binutils-gdb/gdb/frame.c:1247 #16 0x0000557a881875f9 in i386_unwind_pc (gdbarch=0x621000190110, next_frame=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/i386-tdep.c:1971 #17 0x0000557a87fe58a5 in gdbarch_unwind_pc (gdbarch=0x621000190110, next_frame=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/gdbarch.c:3062 #18 0x0000557a87a6267b in dwarf2_tailcall_sniffer_first (this_frame=0x62100034bde0, tailcall_cachep=0x62100034bee0, entry_cfa_sp_offsetp=0x7fff5f4593f0) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame-tailcall.c:387 #19 0x0000557a87a70cdf in dwarf2_frame_cache (this_frame=0x62100034bde0, this_cache=0x62100034bdf8) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1198 #20 0x0000557a87a711c2 in dwarf2_frame_this_id (this_frame=0x62100034bde0, this_cache=0x62100034bdf8, this_id=0x62100034be40) at /home/simark/src/binutils-gdb/gdb/dwarf2/frame.c:1226 #21 0x0000557a87f81167 in compute_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:587 #22 0x0000557a87f81803 in get_frame_id (fi=0x62100034bde0) at /home/simark/src/binutils-gdb/gdb/frame.c:635 #23 0x0000557a87f7efef in scoped_restore_selected_frame::scoped_restore_selected_frame (this=0x7fff5f459920) at /home/simark/src/binutils-gdb/gdb/frame.c:320 #24 0x0000557a891488ae in print_frame_args (fp_opts=..., func=0x621000183b90, frame=0x62100034bde0, num=-1, stream=0x6030000caa20) at /home/simark/src/binutils-gdb/gdb/stack.c:750 #25 0x0000557a8914e87a in print_frame (fp_opts=..., frame=0x62100034bde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, sal=...) at /home/simark/src/binutils-gdb/gdb/stack.c:1394 #26 0x0000557a8914c2ae in print_frame_info (fp_opts=..., frame=0x62100034bde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, set_current_sal=1) at /home/simark/src/binutils-gdb/gdb/stack.c:1119 ... We end up calling get_frame_id (in the hunk above, frame #10) while we are computing it (frame #21), and that's not good. Now, the question is how do we fix this. I suggest making the unwinder return a not_lval value in this case. The reason why we return an lval_register here is to make sure that this is printed as "not saved" and not "optimized out" down the line. See these two commits: 1. 901461f8eb40 ("Print registers not saved in the frame as "<not saved>" instead of "<optimized out>"."). 2. 6bd273ae450b ("Make "set debug frame 1" output print <not saved> instead of <optimized out>.") The current design (introduced by the first commit) is to check the value's lval to choose which one to print (see val_print_optimized_out). Making the unwinder return not_lval instead of lval_register doesn't break "not saved" when doing "print $rax" or "info registers", because value_fetch_lazy_register only consumes the contents and optimized-out property from the value the unwinder returned. The value being un-lazified stays an lval_register. I believe that this is a correct technical solution (and not just papering over the problem), because what we expect of unwinders is to tell us where a given register's value is saved. If the value is saved in memory, -> lval_memory. If the value is saved in some other register of the next frame, -> lval_register. If the value is not saved, it doesn't really make sense to return an lval_register value. not_lval would be more appropriate. If the code then wants to represent an optimized out register value (like value_fetch_lazy_register does), then it's a separate concern which shouldn't involve the unwinder. This change breaks the output of "set debug frame 1" though (introduced by the second commit), since that logging statement consumes the return value of the unwinder directly. To keep the correct behavior, just make `frame_unwind_register_value` call `val_print_not_saved` directly, instead of `val_print_optimized_out`. This is fine because we know in this context that we are always talking about a register value, and that we want to show "not saved" for those. I augmented the gdb.dwarf2/dw2-reg-undefined.exp test case to test some cases I stumbled on while working on this, which I think are not tested anywhere: - the "set debug frame 1" debug output mentioned above. It's just debug output, but if we want to make sure it doesn't change, it should be tested - printing not-saved register values from the history (should print not saved) - copying a not-saved register value in a convenience variable. In this case, we expect that printing the convenience variable shows "optimized out", because we copied the value, not the property of where the value came from. gdb/ChangeLog: * frame-unwind.c (frame_unwind_got_optimized): Don't set regnum/frame in value. Call allocate_value_lazy. * frame.c (frame_unwind_register_value): Use val_print_not_saved. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-reg-undefined.exp: Test "set debug frame 1" output, printing a "not saved" value from history and printing a convenience variable created from a "not saved" value. Change-Id: If451739a3ef7a5b453b1f50707e21ce16d74807e
2020-08-31 13:22:54 -04:00
gdb_test "set debug frame 0"
# Test that history values show "not saved" and not "optimized out".
gdb_test "print" " = <not saved>"
# Test that convenience variables _don't_ show "not saved".
gdb_test {print $foo = $rax} " = <optimized out>"