* [rfc] Eliminate frame_id_inner comparisons
@ 2008-06-20 4:42 Ulrich Weigand
2008-06-25 13:32 ` Joel Brobecker
2008-06-25 14:15 ` Daniel Jacobowitz
0 siblings, 2 replies; 9+ messages in thread
From: Ulrich Weigand @ 2008-06-20 4:42 UTC (permalink / raw)
To: gdb-patches
Hello,
the frame_id_inner function makes assumptions how to use the
values of the stack pointer to try to figure out whether one
frame is inner-to another one. These assumptions may not in
fact be valid in certain situations today (e.g. when switching
stacks like with sigaltstack), and they will become invalid
with per-frame architecture support.
This patch eliminates the need for performing this type of
frame ID comparison. These were used for:
- dummy_frame_push checks for stale dummy frame IDs. As suggested
in a comment by Andrew, this can simply do a frame_find_by_id check.
- return_command uses frame_id_inner as a safety check while popping
frames one by one. Again as already noted by Andrew, this should
really be just popping to the target frame in one go.
- handle_inferior_event has a call to frame_id_inner that is
actually dead:
struct frame_info *frame = get_current_frame ();
struct frame_id current_frame = get_frame_id (frame);
if (!(frame_id_inner (get_frame_arch (frame), current_frame,
step_frame_id)))
step_frame_id = current_frame;
as step_frame_id was already unconditionally set to
get_frame_id (get_current_frame ())
immediately before that check.
- frame_find_by_id used frame_id_inner as a sanity check to detect
infinite cycles in the frame chain. This is really redundant as
frame_find_by_id calls get_prev_frame which already has a similar
check.
- get_prev_frame_1 also used frame_id_inner as sanity check to
detect cycles. I've replaced this by using Floyd's algorithm
to find cycles, without having to compare frame IDs. (This
keeps a backtrac pass linear in the number of stack frames;
a simple comparison loop would make in quadratic. Not sure
whether this actually matters in practice ...)
What do you think? Is that a reasonable approach?
Tested on i386-linux, s390-linux and s390x-linux with no regressions.
Bye,
Ulrich
ChangeLog:
* frame.h (struct frame_id): Update comments.
(frame_id_inner): Remove prototype.
(enum unwind_stop_reason): Remove UNWIND_INNER_ID and
UNWIND_SAME_ID, add UNWIND_CYCLE.
* frame.c (struct frame_info): New member "cycle".
(frame_id_inner): Delete.
(frame_find_by_id): Remove frame_id_inner check.
(create_sentinel_frame): Initialize frame->cycle.
(get_prev_frame_1): Remove frame_id_inner check. Check for
cycles in the frame chain using Floyd's algorithm.
Initialize prev_frame->cycle.
(frame_stop_reason_string): Handle UNWIND_CYCLE instead of
UNWIND_INNER_ID and UNWIND_SAME_ID.
* dummy-frame.c (dummy_frame_push): Use frame_find_by_id to
detect stale dummy frames.
* stack.c (return_command): Directly pop the selected frame.
* infrun.c (handle_inferior_event): Remove dead code.
* i386-tdep.c (i386_push_dummy_call): Update comment.
diff -urNp gdb-orig/gdb/dummy-frame.c gdb-head/gdb/dummy-frame.c
--- gdb-orig/gdb/dummy-frame.c 2008-05-09 19:20:22.000000000 +0200
+++ gdb-head/gdb/dummy-frame.c 2008-06-19 21:36:37.000000000 +0200
@@ -87,7 +87,6 @@ void
dummy_frame_push (struct regcache *caller_regcache,
const struct frame_id *dummy_id)
{
- struct gdbarch *gdbarch = get_regcache_arch (caller_regcache);
struct dummy_frame *dummy_frame;
/* Check to see if there are stale dummy frames, perhaps left over
@@ -95,8 +94,7 @@ dummy_frame_push (struct regcache *calle
the debugger. */
dummy_frame = dummy_frame_stack;
while (dummy_frame)
- /* FIXME: cagney/2004-08-02: Should just test IDs. */
- if (frame_id_inner (gdbarch, dummy_frame->id, (*dummy_id)))
+ if (frame_find_by_id (dummy_frame->id) == NULL)
/* Stale -- destroy! */
{
dummy_frame_stack = dummy_frame->next;
diff -urNp gdb-orig/gdb/frame.c gdb-head/gdb/frame.c
--- gdb-orig/gdb/frame.c 2008-05-26 17:21:58.000000000 +0200
+++ gdb-head/gdb/frame.c 2008-06-19 21:43:35.000000000 +0200
@@ -106,6 +106,12 @@ struct frame_info
int prev_p;
struct frame_info *prev; /* up, outer, older */
+ /* Cached pointer to frame of level LEVEL/2, where LEVEL is the
+ level of this frame. We use this to implement Floyd's cycle
+ detection algorithm (the frame chain has a cycle iff for some I
+ the frame of level I has the same ID as the frame of level 2*I). */
+ struct frame_info *cycle;
+
/* The reason why we could not set PREV, or UNWIND_NO_REASON if we
could. Only valid when PREV_P is set. */
enum unwind_stop_reason stop_reason;
@@ -367,30 +373,6 @@ frame_id_eq (struct frame_id l, struct f
return eq;
}
-int
-frame_id_inner (struct gdbarch *gdbarch, struct frame_id l, struct frame_id r)
-{
- int inner;
- if (!l.stack_addr_p || !r.stack_addr_p)
- /* Like NaN, any operation involving an invalid ID always fails. */
- inner = 0;
- else
- /* Only return non-zero when strictly inner than. Note that, per
- comment in "frame.h", there is some fuzz here. Frameless
- functions are not strictly inner than (same .stack but
- different .code and/or .special address). */
- inner = gdbarch_inner_than (gdbarch, l.stack_addr, r.stack_addr);
- if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "{ frame_id_inner (l=");
- fprint_frame_id (gdb_stdlog, l);
- fprintf_unfiltered (gdb_stdlog, ",r=");
- fprint_frame_id (gdb_stdlog, r);
- fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", inner);
- }
- return inner;
-}
-
struct frame_info *
frame_find_by_id (struct frame_id id)
{
@@ -409,13 +391,6 @@ frame_find_by_id (struct frame_id id)
if (frame_id_eq (id, this))
/* An exact match. */
return frame;
- if (frame_id_inner (get_frame_arch (frame), id, this))
- /* Gone to far. */
- return NULL;
- /* Either we're not yet gone far enough out along the frame
- chain (inner(this,id)), or we're comparing frameless functions
- (same .base, different .func, no test available). Struggle
- on until we've definitly gone to far. */
}
return NULL;
}
@@ -868,6 +843,7 @@ create_sentinel_frame (struct regcache *
/* Link this frame back to itself. The frame is self referential
(the unwound PC is the same as the pc), so make it so. */
frame->next = frame;
+ frame->cycle = frame;
/* Make the sentinel frame's ID valid, but invalid. That way all
comparisons with it should fail. */
frame->this_id.p = 1;
@@ -1202,38 +1178,25 @@ get_prev_frame_1 (struct frame_info *thi
return NULL;
}
- /* Check that this frame's ID isn't inner to (younger, below, next)
- the next frame. This happens when a frame unwind goes backwards.
- Exclude signal trampolines (due to sigaltstack the frame ID can
- go backwards) and sentinel frames (the test is meaningless). */
- if (this_frame->next->level >= 0
- && this_frame->next->unwind->type != SIGTRAMP_FRAME
- && frame_id_inner (get_frame_arch (this_frame), this_id,
- get_frame_id (this_frame->next)))
- {
- if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "-> ");
- fprint_frame (gdb_stdlog, NULL);
- fprintf_unfiltered (gdb_stdlog, " // this frame ID is inner }\n");
- }
- this_frame->stop_reason = UNWIND_INNER_ID;
- return NULL;
- }
-
- /* Check that this and the next frame are not identical. If they
- are, there is most likely a stack cycle. As with the inner-than
- test above, avoid comparing the inner-most and sentinel frames. */
+ /* Check for cycles in the frame chain. These are an indication
+ of either unwinder failure or stack corruption; we want to detect
+ them to avoid infinite loops in backtrace or frame_find_by_id.
+
+ We detect short cycles of length 1 or 2 (hopefully the most
+ frequent instances) by direct comparison. To detect longer
+ cycles in linear time, we use Floyd's algorithm. */
if (this_frame->level > 0
- && frame_id_eq (this_id, get_frame_id (this_frame->next)))
+ && (frame_id_eq (this_id, get_frame_id (this_frame->next))
+ || frame_id_eq (this_id, get_frame_id (this_frame->next->next))
+ || frame_id_eq (this_id, get_frame_id (this_frame->cycle))))
{
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "-> ");
fprint_frame (gdb_stdlog, NULL);
- fprintf_unfiltered (gdb_stdlog, " // this frame has same ID }\n");
+ fprintf_unfiltered (gdb_stdlog, " // cycle detected }\n");
}
- this_frame->stop_reason = UNWIND_SAME_ID;
+ this_frame->stop_reason = UNWIND_CYCLE;
return NULL;
}
@@ -1319,6 +1282,12 @@ get_prev_frame_1 (struct frame_info *thi
this_frame->prev = prev_frame;
prev_frame->next = this_frame;
+ /* Set the cached link to the LEVEL/2 frame. */
+ if (prev_frame->level & 1)
+ prev_frame->cycle = this_frame->cycle;
+ else
+ prev_frame->cycle = this_frame->cycle->prev;
+
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "-> ");
@@ -1778,11 +1747,8 @@ frame_stop_reason_string (enum unwind_st
case UNWIND_NULL_ID:
return _("unwinder did not report frame ID");
- case UNWIND_INNER_ID:
- return _("previous frame inner to this frame (corrupt stack?)");
-
- case UNWIND_SAME_ID:
- return _("previous frame identical to this frame (corrupt stack?)");
+ case UNWIND_CYCLE:
+ return _("same frame ID as some inner frame (corrupt stack?)");
case UNWIND_NO_SAVED_PC:
return _("frame did not save the PC");
diff -urNp gdb-orig/gdb/frame.h gdb-head/gdb/frame.h
--- gdb-orig/gdb/frame.h 2008-05-26 17:21:58.000000000 +0200
+++ gdb-head/gdb/frame.h 2008-06-19 21:36:37.000000000 +0200
@@ -110,8 +110,7 @@ struct frame_id
lifetime of the frame. This is used for architectures that may have
frames that do not change the stack but are still distinct and have
some form of distinct identifier (e.g. the ia64 which uses a 2nd
- stack for registers). This field is treated as unordered - i.e. will
- not be used in frame ordering comparisons such as frame_id_inner().
+ stack for registers).
This field is valid only if special_addr_p is true. Otherwise, this
frame is considered to have a wildcard special address, i.e. one that
@@ -126,19 +125,10 @@ struct frame_id
/* Methods for constructing and comparing Frame IDs.
- NOTE: Given stackless functions A and B, where A calls B (and hence
- B is inner-to A). The relationships: !eq(A,B); !eq(B,A);
- !inner(A,B); !inner(B,A); all hold.
-
- This is because, while B is inner-to A, B is not strictly inner-to A.
- Being stackless, they have an identical .stack_addr value, and differ
- only by their unordered .code_addr and/or .special_addr values.
-
- Because frame_id_inner is only used as a safety net (e.g.,
- detect a corrupt stack) the lack of strictness is not a problem.
- Code needing to determine an exact relationship between two frames
- must instead use frame_id_eq and frame_id_unwind. For instance,
- in the above, to determine that A stepped-into B, the equation
+ We only support comparing frame IDs for identity, because we cannot
+ in general rely on stack addresses to be linearly ordered. The inner-to
+ relationship can be discovered by unwinding. For example, to discover
+ that a function A has just stepped into another function B, the equation
"A.id != B.id && A.id == id_unwind (B)" can be used. */
/* For convenience. All fields are zero. */
@@ -176,12 +166,6 @@ extern int frame_id_p (struct frame_id l
either L or R have a zero .func, then the same frame base. */
extern int frame_id_eq (struct frame_id l, struct frame_id r);
-/* Returns non-zero when L is strictly inner-than R (they have
- different frame .bases). Neither L, nor R can be `null'. See note
- above about frameless functions. */
-extern int frame_id_inner (struct gdbarch *gdbarch, struct frame_id l,
- struct frame_id r);
-
/* Write the internal representation of a frame ID on the specified
stream. */
extern void fprint_frame_id (struct ui_file *file, struct frame_id id);
@@ -427,17 +411,11 @@ enum unwind_stop_reason
is not a valid stop reason. */
UNWIND_FIRST_ERROR,
- /* This frame ID looks like it ought to belong to a NEXT frame,
- but we got it for a PREV frame. Normally, this is a sign of
+ /* This frame has the same ID some frame inner to it. That means
+ that unwinding further would almost certainly get stuck in an
+ infinite loop, so break the chain. Normally, this is a sign of
unwinder failure. It could also indicate stack corruption. */
- UNWIND_INNER_ID,
-
- /* This frame has the same ID as the previous one. That means
- that unwinding further would almost certainly give us another
- frame with exactly the same ID, so break the chain. Normally,
- this is a sign of unwinder failure. It could also indicate
- stack corruption. */
- UNWIND_SAME_ID,
+ UNWIND_CYCLE,
/* The frame unwinder didn't find any saved PC, but we needed
one to unwind further. */
diff -urNp gdb-orig/gdb/i386-tdep.c gdb-head/gdb/i386-tdep.c
--- gdb-orig/gdb/i386-tdep.c 2008-06-16 00:02:04.000000000 +0200
+++ gdb-head/gdb/i386-tdep.c 2008-06-19 21:36:37.000000000 +0200
@@ -1570,8 +1570,8 @@ i386_push_dummy_call (struct gdbarch *gd
(i386_frame_this_id, i386_sigtramp_frame_this_id,
i386_dummy_id). It's there, since all frame unwinders for
a given target have to agree (within a certain margin) on the
- definition of the stack address of a frame. Otherwise
- frame_id_inner() won't work correctly. Since DWARF2/GCC uses the
+ definition of the stack address of a frame. Otherwise frame id
+ comparison might not work correctly. Since DWARF2/GCC uses the
stack address *before* the function call as a frame's CFA. On
the i386, when %ebp is used as a frame pointer, the offset
between the contents %ebp and the CFA as defined by GCC. */
diff -urNp gdb-orig/gdb/infrun.c gdb-head/gdb/infrun.c
--- gdb-orig/gdb/infrun.c 2008-06-16 00:02:04.000000000 +0200
+++ gdb-head/gdb/infrun.c 2008-06-19 21:36:37.000000000 +0200
@@ -3144,33 +3144,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
ecs->current_line = ecs->sal.line;
ecs->current_symtab = ecs->sal.symtab;
- /* In the case where we just stepped out of a function into the
- middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
-#if 0
- /* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
- generous. It will trigger on things like a step into a frameless
- stackless leaf function. I think the logic should instead look
- at the unwound frame ID has that should give a more robust
- indication of what happened. */
- if (step - ID == current - ID)
- still stepping in same function;
- else if (step - ID == unwind (current - ID))
- stepped into a function;
- else
- stepped out of a function;
- /* Of course this assumes that the frame ID unwind code is robust
- and we're willing to introduce frame unwind logic into this
- function. Fortunately, those days are nearly upon us. */
-#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
-
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
keep_going (ecs);
diff -urNp gdb-orig/gdb/stack.c gdb-head/gdb/stack.c
--- gdb-orig/gdb/stack.c 2008-05-28 19:39:56.000000000 +0200
+++ gdb-head/gdb/stack.c 2008-06-19 21:36:37.000000000 +0200
@@ -1851,29 +1851,8 @@ If you continue, the return value that y
error (_("Not confirmed"));
}
- /* NOTE: cagney/2003-01-18: Is this silly? Rather than pop each
- frame in turn, should this code just go straight to the relevant
- frame and pop that? */
-
- /* First discard all frames inner-to the selected frame (making the
- selected frame current). */
- {
- struct frame_id selected_id = get_frame_id (get_selected_frame (NULL));
- while (!frame_id_eq (selected_id, get_frame_id (get_current_frame ())))
- {
- struct frame_info *frame = get_current_frame ();
- if (frame_id_inner (get_frame_arch (frame), selected_id,
- get_frame_id (frame)))
- /* Caught in the safety net, oops! We've gone way past the
- selected frame. */
- error (_("Problem while popping stack frames (corrupt stack?)"));
- frame_pop (get_current_frame ());
- }
- }
-
- /* Second discard the selected frame (which is now also the current
- frame). */
- frame_pop (get_current_frame ());
+ /* Discard the selected frame and all frames inner-to it. */
+ frame_pop (get_selected_frame (NULL));
/* Store RETURN_VALUE in the just-returned register set. */
if (return_value != NULL)
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [rfc] Eliminate frame_id_inner comparisons
2008-06-20 4:42 [rfc] Eliminate frame_id_inner comparisons Ulrich Weigand
@ 2008-06-25 13:32 ` Joel Brobecker
2008-06-25 14:15 ` Daniel Jacobowitz
1 sibling, 0 replies; 9+ messages in thread
From: Joel Brobecker @ 2008-06-25 13:32 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: gdb-patches
Hi Ulrich,
> ChangeLog:
>
> * frame.h (struct frame_id): Update comments.
> (frame_id_inner): Remove prototype.
> (enum unwind_stop_reason): Remove UNWIND_INNER_ID and
> UNWIND_SAME_ID, add UNWIND_CYCLE.
> * frame.c (struct frame_info): New member "cycle".
> (frame_id_inner): Delete.
> (frame_find_by_id): Remove frame_id_inner check.
> (create_sentinel_frame): Initialize frame->cycle.
> (get_prev_frame_1): Remove frame_id_inner check. Check for
> cycles in the frame chain using Floyd's algorithm.
> Initialize prev_frame->cycle.
> (frame_stop_reason_string): Handle UNWIND_CYCLE instead of
> UNWIND_INNER_ID and UNWIND_SAME_ID.
>
> * dummy-frame.c (dummy_frame_push): Use frame_find_by_id to
> detect stale dummy frames.
> * stack.c (return_command): Directly pop the selected frame.
> * infrun.c (handle_inferior_event): Remove dead code.
> * i386-tdep.c (i386_push_dummy_call): Update comment.
FWIW, I looked at the patch and I don't see any problem with it.
> + /* This frame has the same ID some frame inner to it. That means
the same ID as some frame
--
Joel
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [rfc] Eliminate frame_id_inner comparisons
2008-06-20 4:42 [rfc] Eliminate frame_id_inner comparisons Ulrich Weigand
2008-06-25 13:32 ` Joel Brobecker
@ 2008-06-25 14:15 ` Daniel Jacobowitz
2008-08-21 19:43 ` [rfc, v2] Fix frame_id_inner comparison false positives Ulrich Weigand
1 sibling, 1 reply; 9+ messages in thread
From: Daniel Jacobowitz @ 2008-06-25 14:15 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: gdb-patches
On Thu, Jun 19, 2008 at 10:34:05PM +0200, Ulrich Weigand wrote:
> Hello,
>
> the frame_id_inner function makes assumptions how to use the
> values of the stack pointer to try to figure out whether one
> frame is inner-to another one. These assumptions may not in
> fact be valid in certain situations today (e.g. when switching
> stacks like with sigaltstack), and they will become invalid
> with per-frame architecture support.
I had a concern about this patch, which I couldn't explain well in
person - let me see if I can do better with examples.
> - dummy_frame_push checks for stale dummy frame IDs. As suggested
> in a comment by Andrew, this can simply do a frame_find_by_id check.
frame_find_by_id is a loop calling get_prev_frame until we run out of
stack. By default there is no limit on the number of frames GDB will
unwind. So if the stack is very deep, or if the unwinder is confused
and the resulting stack is essentially infinite (a common failure
mode), frame_find_by_id may take a very long time. frame_find_by_id
is constant time (though, as you've noted, sometimes wrong).
On the other hand, if the user has set a backtrace depth limit, then
frame_find_by_id may fail if a function with a deep stack is called.
We'll discard the dummy frame when it's still valid and get an extra
sigtrap. This behavior should be straightforward to write a test case
for: set the backtrace limit to ten, call a function which recurses
twenty times and then hits a breakpoint, call another function, raise
the backtrace limit and see if we've lost the outer dummy frame.
> - return_command uses frame_id_inner as a safety check while popping
> frames one by one. Again as already noted by Andrew, this should
> really be just popping to the target frame in one go.
>
> - handle_inferior_event has a call to frame_id_inner that is
> actually dead:
>
> struct frame_info *frame = get_current_frame ();
> struct frame_id current_frame = get_frame_id (frame);
> if (!(frame_id_inner (get_frame_arch (frame), current_frame,
> step_frame_id)))
> step_frame_id = current_frame;
>
> as step_frame_id was already unconditionally set to
> get_frame_id (get_current_frame ())
> immediately before that check.
These both look fine. IIRC the inlining patch also removes one or
both of these, I know I've run into them before.
> - frame_find_by_id used frame_id_inner as a sanity check to detect
> infinite cycles in the frame chain. This is really redundant as
> frame_find_by_id calls get_prev_frame which already has a similar
> check.
No, it's not checking for cycles.
- if (frame_id_inner (get_frame_arch (frame), id, this))
- /* Gone to far. */
- return NULL;
- /* Either we're not yet gone far enough out along the frame
- chain (inner(this,id)), or we're comparing frameless functions
- (same .base, different .func, no test available). Struggle
- on until we've definitly gone to far. */
This is a limit on the number of cycles frame_find_by_id will travel
if we're in a new location or if we have a confused unwinder.
Suppose we record a varobj at frame sp==0x1000 (stack grows down).
We continue. Next time we hit a breakpoint sp==0xfc0. The frontend
requests a varobj update. We unwind, find the next frame is
sp==0x1200. The old frame is gone; we stop. Good thing, since if
we'd kept backtracing we'd find the stack went all the way up to
0xff00... it'd take seconds or minutes to get up there.
> - get_prev_frame_1 also used frame_id_inner as sanity check to
> detect cycles. I've replaced this by using Floyd's algorithm
> to find cycles, without having to compare frame IDs. (This
> keeps a backtrac pass linear in the number of stack frames;
> a simple comparison loop would make in quadratic. Not sure
> whether this actually matters in practice ...)
I think the part using frame_id_inner is another corrupt stack check,
and the chances of it forming an exact cycle are slim to none. But
it's clear that there's a problem with the multi-stack or multi-arch
cases that you're fixing.
Is there some other way we can avoid unnecessary backtracing? Maybe a
predicate which determines whether two consecutive frames are
reasonably on the same stack - in the presence of sigaltstack that
could be quite fragile though.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 9+ messages in thread
* [rfc, v2] Fix frame_id_inner comparison false positives
2008-06-25 14:15 ` Daniel Jacobowitz
@ 2008-08-21 19:43 ` Ulrich Weigand
2008-08-21 20:16 ` Daniel Jacobowitz
0 siblings, 1 reply; 9+ messages in thread
From: Ulrich Weigand @ 2008-08-21 19:43 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
Daniel Jacobowitz wrote:
> I had a concern about this patch, which I couldn't explain well in
> person - let me see if I can do better with examples.
Thanks for looking at this, and sorry for the long delay in my
getting back to this patch ...
> > - dummy_frame_push checks for stale dummy frame IDs. As suggested
> > in a comment by Andrew, this can simply do a frame_find_by_id check.
>
> frame_find_by_id is a loop calling get_prev_frame until we run out of
> stack. By default there is no limit on the number of frames GDB will
> unwind. So if the stack is very deep, or if the unwinder is confused
> and the resulting stack is essentially infinite (a common failure
> mode), frame_find_by_id may take a very long time. frame_find_by_id
> is constant time (though, as you've noted, sometimes wrong).
I think the frame_id_inner check in dummy_frame_push is simply wrong
anyway. On the one hand, there are false positives on targets where
the range of valid stack addresses is non-contiguous (e.g. with
sigaltstack, or in a multi-arch setup). On the other hand, even so
there are common situations where there are false negatives: if you
simply call multiple inferior functions one after the other from the
same stack frame, their dummy IDs will all have the same stack address,
and therefore the frame_id_inner check will not recognize the old
dummy frames may be cleaned up.
Overall, I guess I still agree with Andrew's comment that at this point,
we just should do a check whether the frame ID is still valid.
On the other hand, you're probably right that we need to take care to
avoid unnecessary looping/backtracing -- but that should be fixed *in*
frame_find_by_id.
> On the other hand, if the user has set a backtrace depth limit, then
> frame_find_by_id may fail if a function with a deep stack is called.
> We'll discard the dummy frame when it's still valid and get an extra
> sigtrap. This behavior should be straightforward to write a test case
> for: set the backtrace limit to ten, call a function which recurses
> twenty times and then hits a breakpoint, call another function, raise
> the backtrace limit and see if we've lost the outer dummy frame.
This seems simply a bug of frame_find_by_id. This function should be
using get_prev_frame_1 instead of get_prev_frame. There is no reason
why re-identifying a frame you had already selected previously should
suddenly fail just because some user-interface setting was changed ...
> This is a limit on the number of cycles frame_find_by_id will travel
> if we're in a new location or if we have a confused unwinder.
> Suppose we record a varobj at frame sp==0x1000 (stack grows down).
> We continue. Next time we hit a breakpoint sp==0xfc0. The frontend
> requests a varobj update. We unwind, find the next frame is
> sp==0x1200. The old frame is gone; we stop. Good thing, since if
> we'd kept backtracing we'd find the stack went all the way up to
> 0xff00... it'd take seconds or minutes to get up there.
Agreed, we should keep some sanity check here. See below ...
> I think the part using frame_id_inner is another corrupt stack check,
> and the chances of it forming an exact cycle are slim to none. But
> it's clear that there's a problem with the multi-stack or multi-arch
> cases that you're fixing.
This part is actually OK, as long as cross-arch frames are treated just
like signal trampoline frames are today.
> Is there some other way we can avoid unnecessary backtracing? Maybe a
> predicate which determines whether two consecutive frames are
> reasonably on the same stack - in the presence of sigaltstack that
> could be quite fragile though.
Even in the presence of non-contiguous stack ranges, we can still
keep a significant set of safety-net changes because for NORMAL
frames changes in the stack address still follow predictable rules:
* If frame NEXT is the immediate inner frame to THIS, and NEXT
is a NORMAL frame, then the stack address of NEXT must be
inner-than-or-equal to the stack address of THIS.
Therefore, if frame_id_inner (THIS, NEXT) holds, some unwind
error has occurred.
* If frame NEXT is the immediate inner frame to THIS, and NEXT
is a NORMAL frame, and NEXT and THIS have different stack
addresses, no other frame in the frame chain may have a stack
address in between.
Therefore, if frame_id_inner (TEST, THIS) holds, but
frame_id_inner (TEST, NEXT) does not hold, TEST cannot refer
to a valid frame in the frame chain.
This will catch for example the case you mention below, while at
the same time never yielding false positives in non-contiguous
stack situations.
The patch below implements this logic. Note that frame_id_inner
is now only used in frame.c, so I've made it static -- due to the
somewhat fragile logic, it's probably better if is isn't used
outside the core frame code anyway ...
What do you think?
Tested on powerpc-linux, powerpc64-linux, and spu-elf with no
regressions.
Bye,
Ulrich
ChangeLog:
* frame.h (struct frame_id): Update comments.
(frame_id_inner): Remove prototype.
* frame.c (frame_id_inner): Make static. Add comments.
(frame_find_by_id): Update frame_id_inner safety net check to avoid
false positives for targets using non-contiguous stack ranges.
Use get_prev_frame_1 instead of get_prev_frame.
(get_prev_frame_1): Update frame_id_inner safety net check.
* dummy-frame.c (dummy_frame_push): Use frame_find_by_id to
detect stale dummy frames.
* stack.c (return_command): Directly pop the selected frame.
* infrun.c (handle_inferior_event): Remove dead code.
* i386-tdep.c (i386_push_dummy_call): Update comment.
diff -urNp gdb-orig/gdb/dummy-frame.c gdb-head/gdb/dummy-frame.c
--- gdb-orig/gdb/dummy-frame.c 2008-05-01 01:19:59.000000000 +0200
+++ gdb-head/gdb/dummy-frame.c 2008-08-21 19:34:49.716119838 +0200
@@ -87,7 +87,6 @@ void
dummy_frame_push (struct regcache *caller_regcache,
const struct frame_id *dummy_id)
{
- struct gdbarch *gdbarch = get_regcache_arch (caller_regcache);
struct dummy_frame *dummy_frame;
/* Check to see if there are stale dummy frames, perhaps left over
@@ -95,8 +94,7 @@ dummy_frame_push (struct regcache *calle
the debugger. */
dummy_frame = dummy_frame_stack;
while (dummy_frame)
- /* FIXME: cagney/2004-08-02: Should just test IDs. */
- if (frame_id_inner (gdbarch, dummy_frame->id, (*dummy_id)))
+ if (frame_find_by_id (dummy_frame->id) == NULL)
/* Stale -- destroy! */
{
dummy_frame_stack = dummy_frame->next;
diff -urNp gdb-orig/gdb/frame.c gdb-head/gdb/frame.c
--- gdb-orig/gdb/frame.c 2008-08-05 22:16:25.000000000 +0200
+++ gdb-head/gdb/frame.c 2008-08-21 20:39:52.682528854 +0200
@@ -368,7 +368,33 @@ frame_id_eq (struct frame_id l, struct f
return eq;
}
-int
+/* Safety net to check whether frame ID L should be inner to
+ frame ID R, according to their stack addresses.
+
+ This method cannot be used to compare arbitrary frames, as the
+ ranges of valid stack addresses may be discontiguous (e.g. due
+ to sigaltstack).
+
+ However, it can be used as safety net to discover invalid frame
+ IDs in certain circumstances.
+
+ * If frame NEXT is the immediate inner frame to THIS, and NEXT
+ is a NORMAL frame, then the stack address of NEXT must be
+ inner-than-or-equal to the stack address of THIS.
+
+ Therefore, if frame_id_inner (THIS, NEXT) holds, some unwind
+ error has occurred.
+
+ * If frame NEXT is the immediate inner frame to THIS, and NEXT
+ is a NORMAL frame, and NEXT and THIS have different stack
+ addresses, no other frame in the frame chain may have a stack
+ address in between.
+
+ Therefore, if frame_id_inner (TEST, THIS) holds, but
+ frame_id_inner (TEST, NEXT) does not hold, TEST cannot refer
+ to a valid frame in the frame chain. */
+
+static int
frame_id_inner (struct gdbarch *gdbarch, struct frame_id l, struct frame_id r)
{
int inner;
@@ -395,28 +421,34 @@ frame_id_inner (struct gdbarch *gdbarch,
struct frame_info *
frame_find_by_id (struct frame_id id)
{
- struct frame_info *frame;
+ struct frame_info *frame, *prev_frame;
/* ZERO denotes the null frame, let the caller decide what to do
about it. Should it instead return get_current_frame()? */
if (!frame_id_p (id))
return NULL;
- for (frame = get_current_frame ();
- frame != NULL;
- frame = get_prev_frame (frame))
+ for (frame = get_current_frame (); ; frame = prev_frame)
{
struct frame_id this = get_frame_id (frame);
if (frame_id_eq (id, this))
/* An exact match. */
return frame;
- if (frame_id_inner (get_frame_arch (frame), id, this))
- /* Gone to far. */
+
+ prev_frame = get_prev_frame_1 (frame);
+ if (!prev_frame)
+ return NULL;
+
+ /* As a safety net to avoid unnecessary backtracing while trying
+ to find an invalid ID, we check for a common situation where
+ we can detect from comparing stack addresses that no other
+ frame in the current frame chain can have this ID. See the
+ comment at frame_id_inner for details. */
+ if (get_frame_type (frame) == NORMAL_FRAME
+ && !frame_id_inner (get_frame_arch (frame), id, this)
+ && frame_id_inner (get_frame_arch (prev_frame), id,
+ get_frame_id (prev_frame)))
return NULL;
- /* Either we're not yet gone far enough out along the frame
- chain (inner(this,id)), or we're comparing frameless functions
- (same .base, different .func, no test available). Struggle
- on until we've definitly gone to far. */
}
return NULL;
}
@@ -1222,11 +1254,10 @@ get_prev_frame_1 (struct frame_info *thi
/* Check that this frame's ID isn't inner to (younger, below, next)
the next frame. This happens when a frame unwind goes backwards.
- Exclude signal trampolines (due to sigaltstack the frame ID can
- go backwards) and sentinel frames (the test is meaningless). */
- if (this_frame->next->level >= 0
- && this_frame->next->unwind->type != SIGTRAMP_FRAME
- && frame_id_inner (get_frame_arch (this_frame), this_id,
+ This check is valid only if the next frame is NORMAL. See the
+ comment at frame_id_inner for details. */
+ if (this_frame->next->unwind->type == NORMAL_FRAME
+ && frame_id_inner (get_frame_arch (this_frame->next), this_id,
get_frame_id (this_frame->next)))
{
if (frame_debug)
diff -urNp gdb-orig/gdb/frame.h gdb-head/gdb/frame.h
--- gdb-orig/gdb/frame.h 2008-08-05 22:16:25.000000000 +0200
+++ gdb-head/gdb/frame.h 2008-08-21 19:51:46.145791293 +0200
@@ -111,7 +111,7 @@ struct frame_id
frames that do not change the stack but are still distinct and have
some form of distinct identifier (e.g. the ia64 which uses a 2nd
stack for registers). This field is treated as unordered - i.e. will
- not be used in frame ordering comparisons such as frame_id_inner().
+ not be used in frame ordering comparisons.
This field is valid only if special_addr_p is true. Otherwise, this
frame is considered to have a wildcard special address, i.e. one that
@@ -124,22 +124,7 @@ struct frame_id
unsigned int special_addr_p : 1;
};
-/* Methods for constructing and comparing Frame IDs.
-
- NOTE: Given stackless functions A and B, where A calls B (and hence
- B is inner-to A). The relationships: !eq(A,B); !eq(B,A);
- !inner(A,B); !inner(B,A); all hold.
-
- This is because, while B is inner-to A, B is not strictly inner-to A.
- Being stackless, they have an identical .stack_addr value, and differ
- only by their unordered .code_addr and/or .special_addr values.
-
- Because frame_id_inner is only used as a safety net (e.g.,
- detect a corrupt stack) the lack of strictness is not a problem.
- Code needing to determine an exact relationship between two frames
- must instead use frame_id_eq and frame_id_unwind. For instance,
- in the above, to determine that A stepped-into B, the equation
- "A.id != B.id && A.id == id_unwind (B)" can be used. */
+/* Methods for constructing and comparing Frame IDs. */
/* For convenience. All fields are zero. */
extern const struct frame_id null_frame_id;
@@ -176,12 +161,6 @@ extern int frame_id_p (struct frame_id l
either L or R have a zero .func, then the same frame base. */
extern int frame_id_eq (struct frame_id l, struct frame_id r);
-/* Returns non-zero when L is strictly inner-than R (they have
- different frame .bases). Neither L, nor R can be `null'. See note
- above about frameless functions. */
-extern int frame_id_inner (struct gdbarch *gdbarch, struct frame_id l,
- struct frame_id r);
-
/* Write the internal representation of a frame ID on the specified
stream. */
extern void fprint_frame_id (struct ui_file *file, struct frame_id id);
diff -urNp gdb-orig/gdb/i386-tdep.c gdb-head/gdb/i386-tdep.c
--- gdb-orig/gdb/i386-tdep.c 2008-08-12 19:13:15.000000000 +0200
+++ gdb-head/gdb/i386-tdep.c 2008-08-21 19:34:49.736116974 +0200
@@ -1712,8 +1712,8 @@ i386_push_dummy_call (struct gdbarch *gd
(i386_frame_this_id, i386_sigtramp_frame_this_id,
i386_dummy_id). It's there, since all frame unwinders for
a given target have to agree (within a certain margin) on the
- definition of the stack address of a frame. Otherwise
- frame_id_inner() won't work correctly. Since DWARF2/GCC uses the
+ definition of the stack address of a frame. Otherwise frame id
+ comparison might not work correctly. Since DWARF2/GCC uses the
stack address *before* the function call as a frame's CFA. On
the i386, when %ebp is used as a frame pointer, the offset
between the contents %ebp and the CFA as defined by GCC. */
diff -urNp gdb-orig/gdb/infrun.c gdb-head/gdb/infrun.c
--- gdb-orig/gdb/infrun.c 2008-08-08 20:14:08.000000000 +0200
+++ gdb-head/gdb/infrun.c 2008-08-21 19:34:49.796108383 +0200
@@ -3284,33 +3284,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
tss->current_line = stop_pc_sal.line;
tss->current_symtab = stop_pc_sal.symtab;
- /* In the case where we just stepped out of a function into the
- middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
-#if 0
- /* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
- generous. It will trigger on things like a step into a frameless
- stackless leaf function. I think the logic should instead look
- at the unwound frame ID has that should give a more robust
- indication of what happened. */
- if (step - ID == current - ID)
- still stepping in same function;
- else if (step - ID == unwind (current - ID))
- stepped into a function;
- else
- stepped out of a function;
- /* Of course this assumes that the frame ID unwind code is robust
- and we're willing to introduce frame unwind logic into this
- function. Fortunately, those days are nearly upon us. */
-#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
-
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
keep_going (ecs);
diff -urNp gdb-orig/gdb/stack.c gdb-head/gdb/stack.c
--- gdb-orig/gdb/stack.c 2008-08-05 22:16:26.000000000 +0200
+++ gdb-head/gdb/stack.c 2008-08-21 19:34:49.850100651 +0200
@@ -1855,29 +1855,8 @@ If you continue, the return value that y
error (_("Not confirmed"));
}
- /* NOTE: cagney/2003-01-18: Is this silly? Rather than pop each
- frame in turn, should this code just go straight to the relevant
- frame and pop that? */
-
- /* First discard all frames inner-to the selected frame (making the
- selected frame current). */
- {
- struct frame_id selected_id = get_frame_id (get_selected_frame (NULL));
- while (!frame_id_eq (selected_id, get_frame_id (get_current_frame ())))
- {
- struct frame_info *frame = get_current_frame ();
- if (frame_id_inner (get_frame_arch (frame), selected_id,
- get_frame_id (frame)))
- /* Caught in the safety net, oops! We've gone way past the
- selected frame. */
- error (_("Problem while popping stack frames (corrupt stack?)"));
- frame_pop (get_current_frame ());
- }
- }
-
- /* Second discard the selected frame (which is now also the current
- frame). */
- frame_pop (get_current_frame ());
+ /* Discard the selected frame and all frames inner-to it. */
+ frame_pop (get_selected_frame (NULL));
/* Store RETURN_VALUE in the just-returned register set. */
if (return_value != NULL)
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [rfc, v2] Fix frame_id_inner comparison false positives
2008-08-21 19:43 ` [rfc, v2] Fix frame_id_inner comparison false positives Ulrich Weigand
@ 2008-08-21 20:16 ` Daniel Jacobowitz
2008-08-21 21:47 ` [rfc, v3] " Ulrich Weigand
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Jacobowitz @ 2008-08-21 20:16 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: gdb-patches
On Thu, Aug 21, 2008 at 09:42:35PM +0200, Ulrich Weigand wrote:
> > On the other hand, if the user has set a backtrace depth limit, then
> > frame_find_by_id may fail if a function with a deep stack is called.
> > We'll discard the dummy frame when it's still valid and get an extra
> > sigtrap. This behavior should be straightforward to write a test case
> > for: set the backtrace limit to ten, call a function which recurses
> > twenty times and then hits a breakpoint, call another function, raise
> > the backtrace limit and see if we've lost the outer dummy frame.
>
> This seems simply a bug of frame_find_by_id. This function should be
> using get_prev_frame_1 instead of get_prev_frame. There is no reason
> why re-identifying a frame you had already selected previously should
> suddenly fail just because some user-interface setting was changed ...
Well, here I'm not sure about. I don't consider 'set backtrace limit'
to be just a user interface change; we use it as a safety net.
For example. Suppose you have an unterminated M68K stack, and the top
says '0xffffffff'. GDB will see that, assume it's a return address,
check whether there's code there, decide there isn't (memory read
fails), and use the stub unwinder - which adds 4 to the stack pointer,
restores the return address, and goes along its merry recursive way.
This is a bug in the 68k unwinder, of course. But it's a very easy
bug to have and a very hard bug to catch. So I don't like algorithms
that make us unwind past the immediate frame.
The only place your patch switched to using frame_find_by_id instead
of frame_id_inner was the dummy frame check, though. So maybe the
right thing to do is just to remove that stale dummy frame check, and
clear the dummy list when we get a new inferior? And stick to
get_prev_frame, at least for the moment.
Other than that, your logic looks entirely right to me. I like
the clever corruption check.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 9+ messages in thread
* [rfc, v3] Fix frame_id_inner comparison false positives
2008-08-21 20:16 ` Daniel Jacobowitz
@ 2008-08-21 21:47 ` Ulrich Weigand
2008-08-22 1:45 ` Ulrich Weigand
0 siblings, 1 reply; 9+ messages in thread
From: Ulrich Weigand @ 2008-08-21 21:47 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
Daniel Jacobowitz wrote:
> Well, here I'm not sure about. I don't consider 'set backtrace limit'
> to be just a user interface change; we use it as a safety net.
OK, I see.
> The only place your patch switched to using frame_find_by_id instead
> of frame_id_inner was the dummy frame check, though. So maybe the
> right thing to do is just to remove that stale dummy frame check, and
> clear the dummy list when we get a new inferior? And stick to
> get_prev_frame, at least for the moment.
Hmmm, from the comments at the frame_id_inner check it seems like this
is only used in exceptional cases; I'd agree to defer those until we
get a new inferior.
However, in actual fact, this is *only* place that ever deletes dummy
frames, even in the absence of longjmp or other exceptional situations.
That seems simply a bug to me, however. When in the normal course of
execution a dummy frame is popped, it's associated dummy-frame.c data
should really be cleaned up as well.
The patch below implements this in addition to your suggestion.
> Other than that, your logic looks entirely right to me. I like
> the clever corruption check.
Thanks for checking!
New version re-tested on powerpc-linux, powerpc64-linux, and spu-elf.
Bye,
Ulrich
ChangeLog:
* dummy-frame.h (dummy_frame_pop): Add prototype.
* dummy-frame.c: Include "observer.h".
(dummy_frame_push): Do not check for stale frames.
(dummy_frame_pop): New function.
(cleanup_dummy_frames): New function.
(_initialize_dummy_frame): Install it as inferior_created observer.
* frame.h (struct frame_id): Update comments.
(frame_id_inner): Remove prototype.
* frame.c (frame_id_inner): Make static. Add comments.
(frame_find_by_id): Update frame_id_inner safety net check to avoid
false positives for targets using non-contiguous stack ranges.
Use get_prev_frame_1 instead of get_prev_frame.
(get_prev_frame_1): Update frame_id_inner safety net check.
(frame_pop): Call dummy_frame_pop when popping a dummy frame.
* stack.c (return_command): Directly pop the selected frame.
* infrun.c (handle_inferior_event): Remove dead code.
* i386-tdep.c (i386_push_dummy_call): Update comment.
diff -urNp gdb-orig/gdb/dummy-frame.c gdb-head/gdb/dummy-frame.c
--- gdb-orig/gdb/dummy-frame.c 2008-08-21 23:01:33.488803000 +0200
+++ gdb-head/gdb/dummy-frame.c 2008-08-21 23:41:19.968722918 +0200
@@ -30,6 +30,7 @@
#include "command.h"
#include "gdbcmd.h"
#include "gdb_string.h"
+#include "observer.h"
/* Dummy frame. This saves the processor state just prior to setting
up the inferior function call. Older targets save the registers
@@ -87,26 +88,8 @@ void
dummy_frame_push (struct regcache *caller_regcache,
const struct frame_id *dummy_id)
{
- struct gdbarch *gdbarch = get_regcache_arch (caller_regcache);
struct dummy_frame *dummy_frame;
- /* Check to see if there are stale dummy frames, perhaps left over
- from when a longjump took us out of a function that was called by
- the debugger. */
- dummy_frame = dummy_frame_stack;
- while (dummy_frame)
- /* FIXME: cagney/2004-08-02: Should just test IDs. */
- if (frame_id_inner (gdbarch, dummy_frame->id, (*dummy_id)))
- /* Stale -- destroy! */
- {
- dummy_frame_stack = dummy_frame->next;
- regcache_xfree (dummy_frame->regcache);
- xfree (dummy_frame);
- dummy_frame = dummy_frame_stack;
- }
- else
- dummy_frame = dummy_frame->next;
-
dummy_frame = XZALLOC (struct dummy_frame);
dummy_frame->regcache = caller_regcache;
dummy_frame->id = (*dummy_id);
@@ -114,6 +97,47 @@ dummy_frame_push (struct regcache *calle
dummy_frame_stack = dummy_frame;
}
+/* Pop the dummy frame with ID dummy_id from the dummy-frame stack. */
+
+void
+dummy_frame_pop (struct frame_id dummy_id)
+{
+ struct dummy_frame **dummy_ptr;
+
+ for (dummy_ptr = &dummy_frame_stack;
+ (*dummy_ptr) != NULL;
+ dummy_ptr = &(*dummy_ptr)->next)
+ {
+ struct dummy_frame *dummy = *dummy_ptr;
+ if (frame_id_eq (dummy->id, dummy_id))
+ {
+ *dummy_ptr = dummy->next;
+ regcache_xfree (dummy->regcache);
+ xfree (dummy);
+ break;
+ }
+ }
+}
+
+/* There may be stale dummy frames, perhaps left over from when a longjump took us
+ out of a function that was called by the debugger. Clean them up at least once
+ whenever we start a new inferior. */
+
+static void
+cleanup_dummy_frames (struct target_ops *target, int from_tty)
+{
+ struct dummy_frame *dummy, *next;
+
+ for (dummy = dummy_frame_stack; dummy; dummy = next)
+ {
+ next = dummy->next;
+ regcache_xfree (dummy->regcache);
+ xfree (dummy);
+ }
+
+ dummy_frame_stack = NULL;
+}
+
/* Return the dummy frame cache, it contains both the ID, and a
pointer to the regcache. */
struct dummy_frame_cache
@@ -258,4 +282,5 @@ _initialize_dummy_frame (void)
_("Print the contents of the internal dummy-frame stack."),
&maintenanceprintlist);
+ observer_attach_inferior_created (cleanup_dummy_frames);
}
diff -urNp gdb-orig/gdb/dummy-frame.h gdb-head/gdb/dummy-frame.h
--- gdb-orig/gdb/dummy-frame.h 2008-08-21 23:01:33.492803000 +0200
+++ gdb-head/gdb/dummy-frame.h 2008-08-21 23:41:19.972722344 +0200
@@ -41,6 +41,8 @@ struct frame_id;
extern void dummy_frame_push (struct regcache *regcache,
const struct frame_id *dummy_id);
+extern void dummy_frame_pop (struct frame_id dummy_id);
+
/* If the PC falls in a dummy frame, return a dummy frame
unwinder. */
diff -urNp gdb-orig/gdb/frame.c gdb-head/gdb/frame.c
--- gdb-orig/gdb/frame.c 2008-08-21 23:01:33.499802000 +0200
+++ gdb-head/gdb/frame.c 2008-08-21 23:41:19.980721197 +0200
@@ -368,7 +368,33 @@ frame_id_eq (struct frame_id l, struct f
return eq;
}
-int
+/* Safety net to check whether frame ID L should be inner to
+ frame ID R, according to their stack addresses.
+
+ This method cannot be used to compare arbitrary frames, as the
+ ranges of valid stack addresses may be discontiguous (e.g. due
+ to sigaltstack).
+
+ However, it can be used as safety net to discover invalid frame
+ IDs in certain circumstances.
+
+ * If frame NEXT is the immediate inner frame to THIS, and NEXT
+ is a NORMAL frame, then the stack address of NEXT must be
+ inner-than-or-equal to the stack address of THIS.
+
+ Therefore, if frame_id_inner (THIS, NEXT) holds, some unwind
+ error has occurred.
+
+ * If frame NEXT is the immediate inner frame to THIS, and NEXT
+ is a NORMAL frame, and NEXT and THIS have different stack
+ addresses, no other frame in the frame chain may have a stack
+ address in between.
+
+ Therefore, if frame_id_inner (TEST, THIS) holds, but
+ frame_id_inner (TEST, NEXT) does not hold, TEST cannot refer
+ to a valid frame in the frame chain. */
+
+static int
frame_id_inner (struct gdbarch *gdbarch, struct frame_id l, struct frame_id r)
{
int inner;
@@ -395,28 +421,34 @@ frame_id_inner (struct gdbarch *gdbarch,
struct frame_info *
frame_find_by_id (struct frame_id id)
{
- struct frame_info *frame;
+ struct frame_info *frame, *prev_frame;
/* ZERO denotes the null frame, let the caller decide what to do
about it. Should it instead return get_current_frame()? */
if (!frame_id_p (id))
return NULL;
- for (frame = get_current_frame ();
- frame != NULL;
- frame = get_prev_frame (frame))
+ for (frame = get_current_frame (); ; frame = prev_frame)
{
struct frame_id this = get_frame_id (frame);
if (frame_id_eq (id, this))
/* An exact match. */
return frame;
- if (frame_id_inner (get_frame_arch (frame), id, this))
- /* Gone to far. */
+
+ prev_frame = get_prev_frame_1 (frame);
+ if (!prev_frame)
+ return NULL;
+
+ /* As a safety net to avoid unnecessary backtracing while trying
+ to find an invalid ID, we check for a common situation where
+ we can detect from comparing stack addresses that no other
+ frame in the current frame chain can have this ID. See the
+ comment at frame_id_inner for details. */
+ if (get_frame_type (frame) == NORMAL_FRAME
+ && !frame_id_inner (get_frame_arch (frame), id, this)
+ && frame_id_inner (get_frame_arch (prev_frame), id,
+ get_frame_id (prev_frame)))
return NULL;
- /* Either we're not yet gone far enough out along the frame
- chain (inner(this,id)), or we're comparing frameless functions
- (same .base, different .func, no test available). Struggle
- on until we've definitly gone to far. */
}
return NULL;
}
@@ -517,6 +549,11 @@ frame_pop (struct frame_info *this_frame
scratch = frame_save_as_regcache (prev_frame);
cleanups = make_cleanup_regcache_xfree (scratch);
+ /* If we are popping a dummy frame, clean up the associated
+ data as well. */
+ if (get_frame_type (this_frame) == DUMMY_FRAME)
+ dummy_frame_pop (get_frame_id (this_frame));
+
/* FIXME: cagney/2003-03-16: It should be possible to tell the
target's register cache that it is about to be hit with a burst
register transfer and that the sequence of register writes should
@@ -1207,11 +1244,10 @@ get_prev_frame_1 (struct frame_info *thi
/* Check that this frame's ID isn't inner to (younger, below, next)
the next frame. This happens when a frame unwind goes backwards.
- Exclude signal trampolines (due to sigaltstack the frame ID can
- go backwards) and sentinel frames (the test is meaningless). */
- if (this_frame->next->level >= 0
- && this_frame->next->unwind->type != SIGTRAMP_FRAME
- && frame_id_inner (get_frame_arch (this_frame), this_id,
+ This check is valid only if the next frame is NORMAL. See the
+ comment at frame_id_inner for details. */
+ if (this_frame->next->unwind->type == NORMAL_FRAME
+ && frame_id_inner (get_frame_arch (this_frame->next), this_id,
get_frame_id (this_frame->next)))
{
if (frame_debug)
diff -urNp gdb-orig/gdb/frame.h gdb-head/gdb/frame.h
--- gdb-orig/gdb/frame.h 2008-08-21 22:11:47.007062000 +0200
+++ gdb-head/gdb/frame.h 2008-08-21 23:41:20.023715033 +0200
@@ -111,7 +111,7 @@ struct frame_id
frames that do not change the stack but are still distinct and have
some form of distinct identifier (e.g. the ia64 which uses a 2nd
stack for registers). This field is treated as unordered - i.e. will
- not be used in frame ordering comparisons such as frame_id_inner().
+ not be used in frame ordering comparisons.
This field is valid only if special_addr_p is true. Otherwise, this
frame is considered to have a wildcard special address, i.e. one that
@@ -124,22 +124,7 @@ struct frame_id
unsigned int special_addr_p : 1;
};
-/* Methods for constructing and comparing Frame IDs.
-
- NOTE: Given stackless functions A and B, where A calls B (and hence
- B is inner-to A). The relationships: !eq(A,B); !eq(B,A);
- !inner(A,B); !inner(B,A); all hold.
-
- This is because, while B is inner-to A, B is not strictly inner-to A.
- Being stackless, they have an identical .stack_addr value, and differ
- only by their unordered .code_addr and/or .special_addr values.
-
- Because frame_id_inner is only used as a safety net (e.g.,
- detect a corrupt stack) the lack of strictness is not a problem.
- Code needing to determine an exact relationship between two frames
- must instead use frame_id_eq and frame_id_unwind. For instance,
- in the above, to determine that A stepped-into B, the equation
- "A.id != B.id && A.id == id_unwind (B)" can be used. */
+/* Methods for constructing and comparing Frame IDs. */
/* For convenience. All fields are zero. */
extern const struct frame_id null_frame_id;
@@ -176,12 +161,6 @@ extern int frame_id_p (struct frame_id l
either L or R have a zero .func, then the same frame base. */
extern int frame_id_eq (struct frame_id l, struct frame_id r);
-/* Returns non-zero when L is strictly inner-than R (they have
- different frame .bases). Neither L, nor R can be `null'. See note
- above about frameless functions. */
-extern int frame_id_inner (struct gdbarch *gdbarch, struct frame_id l,
- struct frame_id r);
-
/* Write the internal representation of a frame ID on the specified
stream. */
extern void fprint_frame_id (struct ui_file *file, struct frame_id id);
diff -urNp gdb-orig/gdb/i386-tdep.c gdb-head/gdb/i386-tdep.c
--- gdb-orig/gdb/i386-tdep.c 2008-08-21 21:43:48.854745000 +0200
+++ gdb-head/gdb/i386-tdep.c 2008-08-21 23:41:20.032713743 +0200
@@ -1712,8 +1712,8 @@ i386_push_dummy_call (struct gdbarch *gd
(i386_frame_this_id, i386_sigtramp_frame_this_id,
i386_dummy_id). It's there, since all frame unwinders for
a given target have to agree (within a certain margin) on the
- definition of the stack address of a frame. Otherwise
- frame_id_inner() won't work correctly. Since DWARF2/GCC uses the
+ definition of the stack address of a frame. Otherwise frame id
+ comparison might not work correctly. Since DWARF2/GCC uses the
stack address *before* the function call as a frame's CFA. On
the i386, when %ebp is used as a frame pointer, the offset
between the contents %ebp and the CFA as defined by GCC. */
diff -urNp gdb-orig/gdb/infrun.c gdb-head/gdb/infrun.c
--- gdb-orig/gdb/infrun.c 2008-08-21 21:44:32.081066000 +0200
+++ gdb-head/gdb/infrun.c 2008-08-21 23:41:20.082706575 +0200
@@ -3314,33 +3314,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
tss->current_line = stop_pc_sal.line;
tss->current_symtab = stop_pc_sal.symtab;
- /* In the case where we just stepped out of a function into the
- middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
-#if 0
- /* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
- generous. It will trigger on things like a step into a frameless
- stackless leaf function. I think the logic should instead look
- at the unwound frame ID has that should give a more robust
- indication of what happened. */
- if (step - ID == current - ID)
- still stepping in same function;
- else if (step - ID == unwind (current - ID))
- stepped into a function;
- else
- stepped out of a function;
- /* Of course this assumes that the frame ID unwind code is robust
- and we're willing to introduce frame unwind logic into this
- function. Fortunately, those days are nearly upon us. */
-#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
-
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
keep_going (ecs);
diff -urNp gdb-orig/gdb/stack.c gdb-head/gdb/stack.c
--- gdb-orig/gdb/stack.c 2008-08-21 21:44:33.061060000 +0200
+++ gdb-head/gdb/stack.c 2008-08-21 23:41:20.125700410 +0200
@@ -1844,29 +1844,8 @@ If you continue, the return value that y
error (_("Not confirmed"));
}
- /* NOTE: cagney/2003-01-18: Is this silly? Rather than pop each
- frame in turn, should this code just go straight to the relevant
- frame and pop that? */
-
- /* First discard all frames inner-to the selected frame (making the
- selected frame current). */
- {
- struct frame_id selected_id = get_frame_id (get_selected_frame (NULL));
- while (!frame_id_eq (selected_id, get_frame_id (get_current_frame ())))
- {
- struct frame_info *frame = get_current_frame ();
- if (frame_id_inner (get_frame_arch (frame), selected_id,
- get_frame_id (frame)))
- /* Caught in the safety net, oops! We've gone way past the
- selected frame. */
- error (_("Problem while popping stack frames (corrupt stack?)"));
- frame_pop (get_current_frame ());
- }
- }
-
- /* Second discard the selected frame (which is now also the current
- frame). */
- frame_pop (get_current_frame ());
+ /* Discard the selected frame and all frames inner-to it. */
+ frame_pop (get_selected_frame (NULL));
/* Store RETURN_VALUE in the just-returned register set. */
if (return_value != NULL)
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [rfc, v3] Fix frame_id_inner comparison false positives
2008-08-21 21:47 ` [rfc, v3] " Ulrich Weigand
@ 2008-08-22 1:45 ` Ulrich Weigand
2008-08-26 17:46 ` Ulrich Weigand
0 siblings, 1 reply; 9+ messages in thread
From: Ulrich Weigand @ 2008-08-22 1:45 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Daniel Jacobowitz, gdb-patches
> The patch below implements this in addition to your suggestion.
Oops, wrong version -- this still had get_prev_frame_1.
The current version is appended below.
Bye,
Ulrich
ChangeLog:
* dummy-frame.h (dummy_frame_pop): Add prototype.
* dummy-frame.c: Include "observer.h".
(dummy_frame_push): Do not check for stale frames.
(dummy_frame_pop): New function.
(cleanup_dummy_frames): New function.
(_initialize_dummy_frame): Install it as inferior_created observer.
* frame.h (struct frame_id): Update comments.
(frame_id_inner): Remove prototype.
* frame.c (frame_id_inner): Make static. Add comments.
(frame_find_by_id): Update frame_id_inner safety net check to avoid
false positives for targets using non-contiguous stack ranges.
(get_prev_frame_1): Update frame_id_inner safety net check.
(frame_pop): Call dummy_frame_pop when popping a dummy frame.
* stack.c (return_command): Directly pop the selected frame.
* infrun.c (handle_inferior_event): Remove dead code.
* i386-tdep.c (i386_push_dummy_call): Update comment.
diff -urNp gdb-orig/gdb/dummy-frame.c gdb-head/gdb/dummy-frame.c
--- gdb-orig/gdb/dummy-frame.c 2008-08-21 23:01:33.488803000 +0200
+++ gdb-head/gdb/dummy-frame.c 2008-08-21 23:41:19.968722918 +0200
@@ -30,6 +30,7 @@
#include "command.h"
#include "gdbcmd.h"
#include "gdb_string.h"
+#include "observer.h"
/* Dummy frame. This saves the processor state just prior to setting
up the inferior function call. Older targets save the registers
@@ -87,26 +88,8 @@ void
dummy_frame_push (struct regcache *caller_regcache,
const struct frame_id *dummy_id)
{
- struct gdbarch *gdbarch = get_regcache_arch (caller_regcache);
struct dummy_frame *dummy_frame;
- /* Check to see if there are stale dummy frames, perhaps left over
- from when a longjump took us out of a function that was called by
- the debugger. */
- dummy_frame = dummy_frame_stack;
- while (dummy_frame)
- /* FIXME: cagney/2004-08-02: Should just test IDs. */
- if (frame_id_inner (gdbarch, dummy_frame->id, (*dummy_id)))
- /* Stale -- destroy! */
- {
- dummy_frame_stack = dummy_frame->next;
- regcache_xfree (dummy_frame->regcache);
- xfree (dummy_frame);
- dummy_frame = dummy_frame_stack;
- }
- else
- dummy_frame = dummy_frame->next;
-
dummy_frame = XZALLOC (struct dummy_frame);
dummy_frame->regcache = caller_regcache;
dummy_frame->id = (*dummy_id);
@@ -114,6 +97,47 @@ dummy_frame_push (struct regcache *calle
dummy_frame_stack = dummy_frame;
}
+/* Pop the dummy frame with ID dummy_id from the dummy-frame stack. */
+
+void
+dummy_frame_pop (struct frame_id dummy_id)
+{
+ struct dummy_frame **dummy_ptr;
+
+ for (dummy_ptr = &dummy_frame_stack;
+ (*dummy_ptr) != NULL;
+ dummy_ptr = &(*dummy_ptr)->next)
+ {
+ struct dummy_frame *dummy = *dummy_ptr;
+ if (frame_id_eq (dummy->id, dummy_id))
+ {
+ *dummy_ptr = dummy->next;
+ regcache_xfree (dummy->regcache);
+ xfree (dummy);
+ break;
+ }
+ }
+}
+
+/* There may be stale dummy frames, perhaps left over from when a longjump took us
+ out of a function that was called by the debugger. Clean them up at least once
+ whenever we start a new inferior. */
+
+static void
+cleanup_dummy_frames (struct target_ops *target, int from_tty)
+{
+ struct dummy_frame *dummy, *next;
+
+ for (dummy = dummy_frame_stack; dummy; dummy = next)
+ {
+ next = dummy->next;
+ regcache_xfree (dummy->regcache);
+ xfree (dummy);
+ }
+
+ dummy_frame_stack = NULL;
+}
+
/* Return the dummy frame cache, it contains both the ID, and a
pointer to the regcache. */
struct dummy_frame_cache
@@ -258,4 +282,5 @@ _initialize_dummy_frame (void)
_("Print the contents of the internal dummy-frame stack."),
&maintenanceprintlist);
+ observer_attach_inferior_created (cleanup_dummy_frames);
}
diff -urNp gdb-orig/gdb/dummy-frame.h gdb-head/gdb/dummy-frame.h
--- gdb-orig/gdb/dummy-frame.h 2008-08-21 23:01:33.492803000 +0200
+++ gdb-head/gdb/dummy-frame.h 2008-08-21 23:41:19.972722344 +0200
@@ -41,6 +41,8 @@ struct frame_id;
extern void dummy_frame_push (struct regcache *regcache,
const struct frame_id *dummy_id);
+extern void dummy_frame_pop (struct frame_id dummy_id);
+
/* If the PC falls in a dummy frame, return a dummy frame
unwinder. */
diff -urNp gdb-orig/gdb/frame.c gdb-head/gdb/frame.c
--- gdb-orig/gdb/frame.c 2008-08-21 23:01:33.499802000 +0200
+++ gdb-head/gdb/frame.c 2008-08-21 23:41:19.980721197 +0200
@@ -368,7 +368,33 @@ frame_id_eq (struct frame_id l, struct f
return eq;
}
-int
+/* Safety net to check whether frame ID L should be inner to
+ frame ID R, according to their stack addresses.
+
+ This method cannot be used to compare arbitrary frames, as the
+ ranges of valid stack addresses may be discontiguous (e.g. due
+ to sigaltstack).
+
+ However, it can be used as safety net to discover invalid frame
+ IDs in certain circumstances.
+
+ * If frame NEXT is the immediate inner frame to THIS, and NEXT
+ is a NORMAL frame, then the stack address of NEXT must be
+ inner-than-or-equal to the stack address of THIS.
+
+ Therefore, if frame_id_inner (THIS, NEXT) holds, some unwind
+ error has occurred.
+
+ * If frame NEXT is the immediate inner frame to THIS, and NEXT
+ is a NORMAL frame, and NEXT and THIS have different stack
+ addresses, no other frame in the frame chain may have a stack
+ address in between.
+
+ Therefore, if frame_id_inner (TEST, THIS) holds, but
+ frame_id_inner (TEST, NEXT) does not hold, TEST cannot refer
+ to a valid frame in the frame chain. */
+
+static int
frame_id_inner (struct gdbarch *gdbarch, struct frame_id l, struct frame_id r)
{
int inner;
@@ -395,28 +421,34 @@ frame_id_inner (struct gdbarch *gdbarch,
struct frame_info *
frame_find_by_id (struct frame_id id)
{
- struct frame_info *frame;
+ struct frame_info *frame, *prev_frame;
/* ZERO denotes the null frame, let the caller decide what to do
about it. Should it instead return get_current_frame()? */
if (!frame_id_p (id))
return NULL;
- for (frame = get_current_frame ();
- frame != NULL;
- frame = get_prev_frame (frame))
+ for (frame = get_current_frame (); ; frame = prev_frame)
{
struct frame_id this = get_frame_id (frame);
if (frame_id_eq (id, this))
/* An exact match. */
return frame;
- if (frame_id_inner (get_frame_arch (frame), id, this))
- /* Gone to far. */
+
+ prev_frame = get_prev_frame (frame);
+ if (!prev_frame)
+ return NULL;
+
+ /* As a safety net to avoid unnecessary backtracing while trying
+ to find an invalid ID, we check for a common situation where
+ we can detect from comparing stack addresses that no other
+ frame in the current frame chain can have this ID. See the
+ comment at frame_id_inner for details. */
+ if (get_frame_type (frame) == NORMAL_FRAME
+ && !frame_id_inner (get_frame_arch (frame), id, this)
+ && frame_id_inner (get_frame_arch (prev_frame), id,
+ get_frame_id (prev_frame)))
return NULL;
- /* Either we're not yet gone far enough out along the frame
- chain (inner(this,id)), or we're comparing frameless functions
- (same .base, different .func, no test available). Struggle
- on until we've definitly gone to far. */
}
return NULL;
}
@@ -517,6 +549,11 @@ frame_pop (struct frame_info *this_frame
scratch = frame_save_as_regcache (prev_frame);
cleanups = make_cleanup_regcache_xfree (scratch);
+ /* If we are popping a dummy frame, clean up the associated
+ data as well. */
+ if (get_frame_type (this_frame) == DUMMY_FRAME)
+ dummy_frame_pop (get_frame_id (this_frame));
+
/* FIXME: cagney/2003-03-16: It should be possible to tell the
target's register cache that it is about to be hit with a burst
register transfer and that the sequence of register writes should
@@ -1207,11 +1244,10 @@ get_prev_frame_1 (struct frame_info *thi
/* Check that this frame's ID isn't inner to (younger, below, next)
the next frame. This happens when a frame unwind goes backwards.
- Exclude signal trampolines (due to sigaltstack the frame ID can
- go backwards) and sentinel frames (the test is meaningless). */
- if (this_frame->next->level >= 0
- && this_frame->next->unwind->type != SIGTRAMP_FRAME
- && frame_id_inner (get_frame_arch (this_frame), this_id,
+ This check is valid only if the next frame is NORMAL. See the
+ comment at frame_id_inner for details. */
+ if (this_frame->next->unwind->type == NORMAL_FRAME
+ && frame_id_inner (get_frame_arch (this_frame->next), this_id,
get_frame_id (this_frame->next)))
{
if (frame_debug)
diff -urNp gdb-orig/gdb/frame.h gdb-head/gdb/frame.h
--- gdb-orig/gdb/frame.h 2008-08-21 22:11:47.007062000 +0200
+++ gdb-head/gdb/frame.h 2008-08-21 23:41:20.023715033 +0200
@@ -111,7 +111,7 @@ struct frame_id
frames that do not change the stack but are still distinct and have
some form of distinct identifier (e.g. the ia64 which uses a 2nd
stack for registers). This field is treated as unordered - i.e. will
- not be used in frame ordering comparisons such as frame_id_inner().
+ not be used in frame ordering comparisons.
This field is valid only if special_addr_p is true. Otherwise, this
frame is considered to have a wildcard special address, i.e. one that
@@ -124,22 +124,7 @@ struct frame_id
unsigned int special_addr_p : 1;
};
-/* Methods for constructing and comparing Frame IDs.
-
- NOTE: Given stackless functions A and B, where A calls B (and hence
- B is inner-to A). The relationships: !eq(A,B); !eq(B,A);
- !inner(A,B); !inner(B,A); all hold.
-
- This is because, while B is inner-to A, B is not strictly inner-to A.
- Being stackless, they have an identical .stack_addr value, and differ
- only by their unordered .code_addr and/or .special_addr values.
-
- Because frame_id_inner is only used as a safety net (e.g.,
- detect a corrupt stack) the lack of strictness is not a problem.
- Code needing to determine an exact relationship between two frames
- must instead use frame_id_eq and frame_id_unwind. For instance,
- in the above, to determine that A stepped-into B, the equation
- "A.id != B.id && A.id == id_unwind (B)" can be used. */
+/* Methods for constructing and comparing Frame IDs. */
/* For convenience. All fields are zero. */
extern const struct frame_id null_frame_id;
@@ -176,12 +161,6 @@ extern int frame_id_p (struct frame_id l
either L or R have a zero .func, then the same frame base. */
extern int frame_id_eq (struct frame_id l, struct frame_id r);
-/* Returns non-zero when L is strictly inner-than R (they have
- different frame .bases). Neither L, nor R can be `null'. See note
- above about frameless functions. */
-extern int frame_id_inner (struct gdbarch *gdbarch, struct frame_id l,
- struct frame_id r);
-
/* Write the internal representation of a frame ID on the specified
stream. */
extern void fprint_frame_id (struct ui_file *file, struct frame_id id);
diff -urNp gdb-orig/gdb/i386-tdep.c gdb-head/gdb/i386-tdep.c
--- gdb-orig/gdb/i386-tdep.c 2008-08-21 21:43:48.854745000 +0200
+++ gdb-head/gdb/i386-tdep.c 2008-08-21 23:41:20.032713743 +0200
@@ -1712,8 +1712,8 @@ i386_push_dummy_call (struct gdbarch *gd
(i386_frame_this_id, i386_sigtramp_frame_this_id,
i386_dummy_id). It's there, since all frame unwinders for
a given target have to agree (within a certain margin) on the
- definition of the stack address of a frame. Otherwise
- frame_id_inner() won't work correctly. Since DWARF2/GCC uses the
+ definition of the stack address of a frame. Otherwise frame id
+ comparison might not work correctly. Since DWARF2/GCC uses the
stack address *before* the function call as a frame's CFA. On
the i386, when %ebp is used as a frame pointer, the offset
between the contents %ebp and the CFA as defined by GCC. */
diff -urNp gdb-orig/gdb/infrun.c gdb-head/gdb/infrun.c
--- gdb-orig/gdb/infrun.c 2008-08-21 21:44:32.081066000 +0200
+++ gdb-head/gdb/infrun.c 2008-08-21 23:41:20.082706575 +0200
@@ -3314,33 +3314,6 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
tss->current_line = stop_pc_sal.line;
tss->current_symtab = stop_pc_sal.symtab;
- /* In the case where we just stepped out of a function into the
- middle of a line of the caller, continue stepping, but
- step_frame_id must be modified to current frame */
-#if 0
- /* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
- generous. It will trigger on things like a step into a frameless
- stackless leaf function. I think the logic should instead look
- at the unwound frame ID has that should give a more robust
- indication of what happened. */
- if (step - ID == current - ID)
- still stepping in same function;
- else if (step - ID == unwind (current - ID))
- stepped into a function;
- else
- stepped out of a function;
- /* Of course this assumes that the frame ID unwind code is robust
- and we're willing to introduce frame unwind logic into this
- function. Fortunately, those days are nearly upon us. */
-#endif
- {
- struct frame_info *frame = get_current_frame ();
- struct frame_id current_frame = get_frame_id (frame);
- if (!(frame_id_inner (get_frame_arch (frame), current_frame,
- step_frame_id)))
- step_frame_id = current_frame;
- }
-
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
keep_going (ecs);
diff -urNp gdb-orig/gdb/stack.c gdb-head/gdb/stack.c
--- gdb-orig/gdb/stack.c 2008-08-21 21:44:33.061060000 +0200
+++ gdb-head/gdb/stack.c 2008-08-21 23:41:20.125700410 +0200
@@ -1844,29 +1844,8 @@ If you continue, the return value that y
error (_("Not confirmed"));
}
- /* NOTE: cagney/2003-01-18: Is this silly? Rather than pop each
- frame in turn, should this code just go straight to the relevant
- frame and pop that? */
-
- /* First discard all frames inner-to the selected frame (making the
- selected frame current). */
- {
- struct frame_id selected_id = get_frame_id (get_selected_frame (NULL));
- while (!frame_id_eq (selected_id, get_frame_id (get_current_frame ())))
- {
- struct frame_info *frame = get_current_frame ();
- if (frame_id_inner (get_frame_arch (frame), selected_id,
- get_frame_id (frame)))
- /* Caught in the safety net, oops! We've gone way past the
- selected frame. */
- error (_("Problem while popping stack frames (corrupt stack?)"));
- frame_pop (get_current_frame ());
- }
- }
-
- /* Second discard the selected frame (which is now also the current
- frame). */
- frame_pop (get_current_frame ());
+ /* Discard the selected frame and all frames inner-to it. */
+ frame_pop (get_selected_frame (NULL));
/* Store RETURN_VALUE in the just-returned register set. */
if (return_value != NULL)
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [rfc, v3] Fix frame_id_inner comparison false positives
2008-08-22 1:45 ` Ulrich Weigand
@ 2008-08-26 17:46 ` Ulrich Weigand
2008-08-26 22:19 ` Daniel Jacobowitz
0 siblings, 1 reply; 9+ messages in thread
From: Ulrich Weigand @ 2008-08-26 17:46 UTC (permalink / raw)
To: gdb-patches; +Cc: Daniel Jacobowitz
> ChangeLog:
>
> * dummy-frame.h (dummy_frame_pop): Add prototype.
> * dummy-frame.c: Include "observer.h".
> (dummy_frame_push): Do not check for stale frames.
> (dummy_frame_pop): New function.
> (cleanup_dummy_frames): New function.
> (_initialize_dummy_frame): Install it as inferior_created observer.
>
> * frame.h (struct frame_id): Update comments.
> (frame_id_inner): Remove prototype.
> * frame.c (frame_id_inner): Make static. Add comments.
> (frame_find_by_id): Update frame_id_inner safety net check to avoid
> false positives for targets using non-contiguous stack ranges.
> (get_prev_frame_1): Update frame_id_inner safety net check.
> (frame_pop): Call dummy_frame_pop when popping a dummy frame.
>
> * stack.c (return_command): Directly pop the selected frame.
> * infrun.c (handle_inferior_event): Remove dead code.
> * i386-tdep.c (i386_push_dummy_call): Update comment.
This is now checked in too.
Bye,
Ulrich
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [rfc, v3] Fix frame_id_inner comparison false positives
2008-08-26 17:46 ` Ulrich Weigand
@ 2008-08-26 22:19 ` Daniel Jacobowitz
0 siblings, 0 replies; 9+ messages in thread
From: Daniel Jacobowitz @ 2008-08-26 22:19 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: gdb-patches
On Tue, Aug 26, 2008 at 07:41:13PM +0200, Ulrich Weigand wrote:
> This is now checked in too.
Great! Sorry I didn't have time to respond, but it looks good to me.
I'm probably going to be useless for patch review until mid-October.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-08-26 22:19 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-06-20 4:42 [rfc] Eliminate frame_id_inner comparisons Ulrich Weigand
2008-06-25 13:32 ` Joel Brobecker
2008-06-25 14:15 ` Daniel Jacobowitz
2008-08-21 19:43 ` [rfc, v2] Fix frame_id_inner comparison false positives Ulrich Weigand
2008-08-21 20:16 ` Daniel Jacobowitz
2008-08-21 21:47 ` [rfc, v3] " Ulrich Weigand
2008-08-22 1:45 ` Ulrich Weigand
2008-08-26 17:46 ` Ulrich Weigand
2008-08-26 22:19 ` Daniel Jacobowitz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox