Andrew Burgess ca89bdf8b2 gdb: remove VALUE_FRAME_ID and fix another frame debug issue
This commit was originally part of this patch series:

  (v1): https://sourceware.org/pipermail/gdb-patches/2021-May/179357.html
  (v2): https://sourceware.org/pipermail/gdb-patches/2021-June/180208.html
  (v3): https://sourceware.org/pipermail/gdb-patches/2021-July/181028.html

However, that series is being held up in review, so I wanted to break
out some of the non-related fixes in order to get these merged.

This commit addresses two semi-related issues, both of which are
problems exposed by using 'set debug frame on'.

The first issue is in frame.c in get_prev_frame_always_1, and was
introduced by this commit:

  commit a05a883fbaba69d0f80806e46a9457727fcbe74c
  Date:   Tue Jun 29 12:03:50 2021 -0400

      gdb: introduce frame_debug_printf

This commit replaced fprint_frame with frame_info::to_string.
However, the former could handle taking a nullptr while the later, a
member function, obviously requires a non-nullptr in order to make the
function call.  In one place we are not-guaranteed to have a
non-nullptr, and so, there is the possibility of triggering undefined
behaviour.

The second issue addressed in this commit has existed for a while in
GDB, and would cause this assertion:

  gdb/frame.c:622: internal-error: frame_id get_frame_id(frame_info*): Assertion `fi->this_id.p != frame_id_status::COMPUTING' failed.

We attempt to get the frame_id for a frame while we are computing the
frame_id for that same frame.

What happens is that when GDB stops we create a frame_info object for
the sentinel frame (frame #-1) and then we attempt to unwind this
frame to create a frame_info object for frame #0.

In the test case used here to expose the issue we have created a
Python frame unwinder.  In the Python unwinder we attemt to read the
program counter register.

Reading this register will initially create a lazy register value.
The frame-id stored in the lazy register value will be for the
sentinel frame (lazy register values hold the frame-id for the frame
from which the register will be unwound).

However, the Python unwinder does actually want to examine the value
of the program counter, and so the lazy register value is resolved
into a non-lazy value.  This sends GDB into value_fetch_lazy_register
in value.c.

Now, inside this function, if 'set debug frame on' is in effect, then
we want to print something like:

  frame=%d, regnum=%d(%s), ....

Where 'frame=%d' will be the relative frame level of the frame for
which the register is being fetched, so, in this case we would expect
to see 'frame=0', i.e. we are reading a register as it would be in
frame #0.  But, remember, the lazy register value actually holds the
frame-id for frame #-1 (the sentinel frame).

So, to get the frame_info for frame #0 we used to call:

  frame = frame_find_by_id (VALUE_FRAME_ID (val));

Where VALUE_FRAME_ID is:

  #define VALUE_FRAME_ID(val) (get_prev_frame_id_by_id (VALUE_NEXT_FRAME_ID (val)))

That is, we start with the frame-id for the next frame as obtained by
VALUE_NEXT_FRAME_ID, then call get_prev_frame_id_by_id to get the
frame-id of the previous frame.

The get_prev_frame_id_by_id function finds the frame_info for the
given frame-id (in this case frame #-1), calls get_prev_frame to get
the previous frame, and then calls get_frame_id.

The problem here is that calling get_frame_id requires that we know
the frame unwinder, so then have to try each frame unwinder in turn,
which would include the Python unwinder.... which is where we started,
and thus we have a loop!

To prevent this loop GDB has an assertion in place, which is what
actually triggers.

Solving the assertion failure is pretty easy, if we consider the code
in value_fetch_lazy_register and get_prev_frame_id_by_id then what we
do is:

  1. Start with a frame_id taken from a value,
  2. Lookup the corresponding frame,
  3. Find the previous frame,
  4. Get the frame_id for that frame, and
  5. Lookup the corresponding frame
  6. Print the frame's level

Notice that steps 3 and 5 give us the exact same result, step 4 is
just wasted effort.  We could shorten this process such that we drop
steps 4 and 5, thus:

  1. Start with a frame_id taken from a value,
  2. Lookup the corresponding frame,
  3. Find the previous frame,
  6. Print the frame's level

This will give the exact same frame as a result, and this is what I
have done in this patch by removing the use of VALUE_FRAME_ID from
value_fetch_lazy_register.

Out of curiosity I looked to see how widely VALUE_FRAME_ID was used,
and saw it was only used in one other place in valops.c:value_assign,
where, once again, we take the result of VALUE_FRAME_ID and pass it to
frame_find_by_id, thus introducing a redundant frame_id lookup.

I don't think the value_assign case risks triggering the assertion
though, as we are unlikely to call value_assign while computing the
frame_id for a frame, however, we could make value_assign slightly
more efficient, with no real additional complexity, by removing the
use of VALUE_FRAME_ID.

So, in this commit, I completely remove VALUE_FRAME_ID, and replace it
with a use of VALUE_NEXT_FRAME_ID, followed by a direct call to
get_prev_frame_always, this should make no difference in either case,
and resolves the assertion issue from value.c.

As I said, this patch was originally part of another series, the
original test relied on the fixes in that original series.  However, I
was able to create an alternative test for this issue by enabling
frame debug within an existing test script.

This commit probably fixes bug PR gdb/27938, though the bug doesn't
have a reproducer attached so it is not possible to know for sure.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=27938
2021-07-27 09:20:39 +01:00
2021-07-27 00:00:23 +00:00
2021-07-21 11:00:35 +09:30
2021-07-03 14:50:57 +01:00
2020-09-25 10:24:44 -04:00
2021-07-03 14:50:57 +01:00
2021-07-26 07:34:37 -06:00
2021-07-03 14:50:57 +01:00
2021-07-03 14:50:57 +01:00
2021-05-29 11:56:43 -04:00
2021-05-29 11:56:43 -04:00
2021-05-18 17:47:27 -04:00
2021-05-18 17:47:27 -04:00
2021-01-12 18:19:20 -05:00

		   README for GNU development tools

This directory contains various GNU compilers, assemblers, linkers, 
debuggers, etc., plus their support routines, definitions, and documentation.

If you are receiving this as part of a GDB release, see the file gdb/README.
If with a binutils release, see binutils/README;  if with a libg++ release,
see libg++/README, etc.  That'll give you info about this
package -- supported targets, how to use it, how to report bugs, etc.

It is now possible to automatically configure and build a variety of
tools with one command.  To build all of the tools contained herein,
run the ``configure'' script here, e.g.:

	./configure 
	make

To install them (by default in /usr/local/bin, /usr/local/lib, etc),
then do:
	make install

(If the configure script can't determine your type of computer, give it
the name as an argument, for instance ``./configure sun4''.  You can
use the script ``config.sub'' to test whether a name is recognized; if
it is, config.sub translates it to a triplet specifying CPU, vendor,
and OS.)

If you have more than one compiler on your system, it is often best to
explicitly set CC in the environment before running configure, and to
also set CC when running make.  For example (assuming sh/bash/ksh):

	CC=gcc ./configure
	make

A similar example using csh:

	setenv CC gcc
	./configure
	make

Much of the code and documentation enclosed is copyright by
the Free Software Foundation, Inc.  See the file COPYING or
COPYING.LIB in the various directories, for a description of the
GNU General Public License terms under which you can copy the files.

REPORTING BUGS: Again, see gdb/README, binutils/README, etc., for info
on where and how to report problems.
Description
Unofficial mirror of sourceware binutils-gdb repository. Updated daily.
Readme 780 MiB
Languages
C 51.8%
Makefile 22.4%
Assembly 12.3%
C++ 6%
Roff 1.4%
Other 5.4%