Remove the frame_object::frame_id_is_next field. This has been part
of how Python handles frames since this code was first added in commit
f8f6f20b6e back in 2009. The motivation
for this field can be found in a couple of comments, there's this one
on frame_id_is_next:
/* Marks that the FRAME_ID member actually holds the ID of the frame next
to this, and not this frames' ID itself. This is a hack to permit Python
frame objects which represent invalid frames (i.e., the last frame_info
in a corrupt stack). The problem arises from the fact that this code
relies on FRAME_ID to uniquely identify a frame, which is not always true
for the last "frame" in a corrupt stack (it can have a null ID, or the same
ID as the previous frame). Whenever get_prev_frame returns NULL, we
record the frame_id of the next frame and set FRAME_ID_IS_NEXT to 1. */
And this one in frame_info_to_frame_object:
/* Try to get the previous frame, to determine if this is the last frame
in a corrupt stack. If so, we need to store the frame_id of the next
frame and not of this one (which is possibly invalid). */
This field is dealing with a problem that was present in older
versions of GDB where not every stack frame had a valid frame-id, if
the stack was corrupted in some way then the last frame might have an
invalid frame-id. However, that is no longer the case. With current
GDB there is a promise that every frame has a valid frame-id. I don't
have a single commit to point to where this became the reality, but it
is my understanding of current GDB.
As an example, the frame-id of every frame (except #0) is computed as
the frame is created, any frames with a duplicate frame-id, or any
errors during computation of the frame-id, and the new frame is
discarded.
Additionally, our frame_info_ptr::reinflate mechanism relies on unique
and valid frame-ids.
The problem with the existing code is that the last frame in a
corrupted stack will hold the frame-id of the next frame, that is, the
more inner frame. This means we have two frames holding the same
frame-id, and the only difference is the frame_id_is_next flag.
However, the frapy_str function, which prints a string representation
of the frame, doesn't take frame_id_is_next into account, so printing
the last two frames in a corrupted stack, will print the same
frame-id.
We could fix this in frapy_str and also frapy_repr by checking the
frame_id_is_next flag and then fetching the previous frame-id, but
this would still assume that the previous frame has a valid-id, so we
might as well just drop the frame_id_is_next flag and make everything
simpler.
There's a new test which exposes the incorrectly printed frame-id
problem.
While testing I needed to "fix" the results for two existing tests.
In frame_info_to_frame_object, in order to figure out if we should use
the next frame, we called get_prev_frame. This would cause GDB to
always unwind 1 extra level of the stack.
What this means is that, if there is an error, or some diagnostic
output, when unwinding frame #1, then this will show up when trying to
access frame #0 via the Python API as frame_info_to_frame_object on
frame #0 would call get_prev_frame, which would then unwind frame #1.
After this commit this is no longer the case. We only see
output (either errors, or diagnostic output) associated with frame #1
when we actually try to unwind to frame #1. The two tests that needed
updating were expecting output associated with frame #1 while printing
frame #0. This is now fixed and the output for frame #1 occurs later
on. I think this is an improvement.