2004-03-31 Andrew Cagney <cagney@redhat.com>

* frame.h (frame_unwind_id): Declare.
	* frame.c (frame_unwind_id): New function.
	(get_prev_frame_1): New function.
	(frame_debug_got_null_frame): New function.
	(get_prev_frame): Use frame_debug_got_null_frame.  Move unwind
	code proper to prev_frame, update description.
	* infrun.c (step_over_function): Use frame_unwind_id.
This commit is contained in:
Andrew Cagney 2004-03-31 19:40:28 +00:00
parent c4557624e5
commit 5613d8d3a3
4 changed files with 193 additions and 146 deletions

View File

@ -1,3 +1,13 @@
2004-03-31 Andrew Cagney <cagney@redhat.com>
* frame.h (frame_unwind_id): Declare.
* frame.c (frame_unwind_id): New function.
(get_prev_frame_1): New function.
(frame_debug_got_null_frame): New function.
(get_prev_frame): Use frame_debug_got_null_frame. Move unwind
code proper to prev_frame, update description.
* infrun.c (step_over_function): Use frame_unwind_id.
2004-04-31 J. Brobecker <brobecker@gnat.com>
* hppa-tdep.c (hppa32_push_dummy_call): Set the Stack Pointer.

View File

@ -40,6 +40,8 @@
#include "command.h"
#include "gdbcmd.h"
static struct frame_info *get_prev_frame_1 (struct frame_info *this_frame);
/* We keep a cache of stack frames, each of which is a "struct
frame_info". The innermost one gets allocated (in
wait_for_inferior) each time the inferior stops; current_frame
@ -250,6 +252,16 @@ get_frame_id (struct frame_info *fi)
return fi->this_id.value;
}
struct frame_id
frame_unwind_id (struct frame_info *next_frame)
{
/* Use prev_frame, and not get_prev_frame. The latter will truncate
the frame chain, leading to this function unintentionally
returning a null_frame_id (e.g., when a caller requests the frame
ID of "main()"s caller. */
return get_frame_id (get_prev_frame_1 (next_frame));
}
const struct frame_id null_frame_id; /* All zeros. */
struct frame_id
@ -1720,23 +1732,22 @@ legacy_get_prev_frame (struct frame_info *this_frame)
return prev;
}
/* Return a structure containing various interesting information
about the frame that called THIS_FRAME. Returns NULL
if there is no such frame.
/* Return a "struct frame_info" corresponding to the frame that called
THIS_FRAME. Returns NULL if there is no such frame.
This function tests some target-independent conditions that should
terminate the frame chain, such as unwinding past main(). It
should not contain any target-dependent tests, such as checking
whether the program-counter is zero. */
Unlike get_prev_frame, this function always tries to unwind the
frame. */
struct frame_info *
get_prev_frame (struct frame_info *this_frame)
static struct frame_info *
get_prev_frame_1 (struct frame_info *this_frame)
{
struct frame_info *prev_frame;
gdb_assert (this_frame != NULL);
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "{ get_prev_frame (this_frame=");
fprintf_unfiltered (gdb_stdlog, "{ get_prev_frame_1 (this_frame=");
if (this_frame != NULL)
fprintf_unfiltered (gdb_stdlog, "%d", this_frame->level);
else
@ -1744,107 +1755,6 @@ get_prev_frame (struct frame_info *this_frame)
fprintf_unfiltered (gdb_stdlog, ") ");
}
/* Return the inner-most frame, when the caller passes in NULL. */
/* NOTE: cagney/2002-11-09: Not sure how this would happen. The
caller should have previously obtained a valid frame using
get_selected_frame() and then called this code - only possibility
I can think of is code behaving badly.
NOTE: cagney/2003-01-10: Talk about code behaving badly. Check
block_innermost_frame(). It does the sequence: frame = NULL;
while (1) { frame = get_prev_frame (frame); .... }. Ulgh! Why
it couldn't be written better, I don't know.
NOTE: cagney/2003-01-11: I suspect what is happening is
block_innermost_frame() is, when the target has no state
(registers, memory, ...), still calling this function. The
assumption being that this function will return NULL indicating
that a frame isn't possible, rather than checking that the target
has state and then calling get_current_frame() and
get_prev_frame(). This is a guess mind. */
if (this_frame == NULL)
{
/* NOTE: cagney/2002-11-09: There was a code segment here that
would error out when CURRENT_FRAME was NULL. The comment
that went with it made the claim ...
``This screws value_of_variable, which just wants a nice
clean NULL return from block_innermost_frame if there are no
frames. I don't think I've ever seen this message happen
otherwise. And returning NULL here is a perfectly legitimate
thing to do.''
Per the above, this code shouldn't even be called with a NULL
THIS_FRAME. */
return current_frame;
}
/* There is always a frame. If this assertion fails, suspect that
something should be calling get_selected_frame() or
get_current_frame(). */
gdb_assert (this_frame != NULL);
/* Make sure we pass an address within THIS_FRAME's code block to
inside_main_func. Otherwise, we might stop unwinding at a
function which has a call instruction as its last instruction if
that function immediately precedes main(). */
if (this_frame->level >= 0
&& !backtrace_past_main
&& inside_main_func (get_frame_address_in_block (this_frame)))
/* Don't unwind past main(), bug always unwind the sentinel frame.
Note, this is done _before_ the frame has been marked as
previously unwound. That way if the user later decides to
allow unwinds past main(), that just happens. */
{
if (frame_debug)
fprintf_unfiltered (gdb_stdlog, "-> NULL // inside main func }\n");
return NULL;
}
if (this_frame->level > backtrace_limit)
{
error ("Backtrace limit of %d exceeded", backtrace_limit);
}
/* If we're already inside the entry function for the main objfile,
then it isn't valid. Don't apply this test to a dummy frame -
dummy frame PC's typically land in the entry func. Don't apply
this test to the sentinel frame. Sentinel frames should always
be allowed to unwind. */
/* NOTE: cagney/2003-02-25: Don't enable until someone has found
hard evidence that this is needed. */
/* NOTE: cagney/2003-07-07: Fixed a bug in inside_main_func - wasn't
checking for "main" in the minimal symbols. With that fixed
asm-source tests now stop in "main" instead of halting the
backtrace in wierd and wonderful ways somewhere inside the entry
file. Suspect that deprecated_inside_entry_file and
inside_entry_func tests were added to work around that (now
fixed) case. */
/* NOTE: cagney/2003-07-15: danielj (if I'm reading it right)
suggested having the inside_entry_func test use the
inside_main_func msymbol trick (along with entry_point_address I
guess) to determine the address range of the start function.
That should provide a far better stopper than the current
heuristics. */
/* NOTE: cagney/2003-07-15: Need to add a "set backtrace
beyond-entry-func" command so that this can be selectively
disabled. */
if (0
#if 0
&& backtrace_beyond_entry_func
#endif
&& this_frame->type != DUMMY_FRAME && this_frame->level >= 0
&& inside_entry_func (this_frame))
{
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "-> ");
fprint_frame (gdb_stdlog, NULL);
fprintf_unfiltered (gdb_stdlog, "// inside entry func }\n");
}
return NULL;
}
/* Only try to do the unwind once. */
if (this_frame->prev_p)
{
@ -1858,40 +1768,6 @@ get_prev_frame (struct frame_info *this_frame)
}
this_frame->prev_p = 1;
/* If we're inside the entry file, it isn't valid. Don't apply this
test to a dummy frame - dummy frame PC's typically land in the
entry file. Don't apply this test to the sentinel frame.
Sentinel frames should always be allowed to unwind. */
/* NOTE: drow/2002-12-25: should there be a way to disable this
check? It assumes a single small entry file, and the way some
debug readers (e.g. dbxread) figure out which object is the
entry file is somewhat hokey. */
/* NOTE: cagney/2003-01-10: If there is a way of disabling this test
then it should probably be moved to before the ->prev_p test,
above. */
/* NOTE: vinschen/2003-04-01: Disabled. It turns out that the call
to deprecated_inside_entry_file destroys a meaningful backtrace
under some conditions. E. g. the backtrace tests in the
asm-source testcase are broken for some targets. In this test
the functions are all implemented as part of one file and the
testcase is not necessarily linked with a start file (depending
on the target). What happens is, that the first frame is printed
normaly and following frames are treated as being inside the
enttry file then. This way, only the #0 frame is printed in the
backtrace output. */
if (0
&& this_frame->type != DUMMY_FRAME && this_frame->level >= 0
&& deprecated_inside_entry_file (get_frame_pc (this_frame)))
{
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "-> ");
fprint_frame (gdb_stdlog, NULL);
fprintf_unfiltered (gdb_stdlog, " // inside entry file }\n");
}
return NULL;
}
/* If any of the old frame initialization methods are around, use
the legacy get_prev_frame method. */
if (legacy_frame_p (current_gdbarch))
@ -1977,6 +1853,166 @@ get_prev_frame (struct frame_info *this_frame)
return prev_frame;
}
/* Debug routine to print a NULL frame being returned. */
static void
frame_debug_got_null_frame (struct ui_file *file,
struct frame_info *this_frame,
const char *reason)
{
if (frame_debug)
{
fprintf_unfiltered (gdb_stdlog, "{ get_prev_frame (this_frame=");
if (this_frame != NULL)
fprintf_unfiltered (gdb_stdlog, "%d", this_frame->level);
else
fprintf_unfiltered (gdb_stdlog, "<NULL>");
fprintf_unfiltered (gdb_stdlog, ") -> // %s}\n", reason);
}
}
/* Return a structure containing various interesting information about
the frame that called THIS_FRAME. Returns NULL if there is entier
no such frame or the frame fails any of a set of target-independent
condition that should terminate the frame chain (e.g., as unwinding
past main()).
This function should not contain target-dependent tests, such as
checking whether the program-counter is zero. */
struct frame_info *
get_prev_frame (struct frame_info *this_frame)
{
struct frame_info *prev_frame;
/* Return the inner-most frame, when the caller passes in NULL. */
/* NOTE: cagney/2002-11-09: Not sure how this would happen. The
caller should have previously obtained a valid frame using
get_selected_frame() and then called this code - only possibility
I can think of is code behaving badly.
NOTE: cagney/2003-01-10: Talk about code behaving badly. Check
block_innermost_frame(). It does the sequence: frame = NULL;
while (1) { frame = get_prev_frame (frame); .... }. Ulgh! Why
it couldn't be written better, I don't know.
NOTE: cagney/2003-01-11: I suspect what is happening is
block_innermost_frame() is, when the target has no state
(registers, memory, ...), still calling this function. The
assumption being that this function will return NULL indicating
that a frame isn't possible, rather than checking that the target
has state and then calling get_current_frame() and
get_prev_frame(). This is a guess mind. */
if (this_frame == NULL)
{
/* NOTE: cagney/2002-11-09: There was a code segment here that
would error out when CURRENT_FRAME was NULL. The comment
that went with it made the claim ...
``This screws value_of_variable, which just wants a nice
clean NULL return from block_innermost_frame if there are no
frames. I don't think I've ever seen this message happen
otherwise. And returning NULL here is a perfectly legitimate
thing to do.''
Per the above, this code shouldn't even be called with a NULL
THIS_FRAME. */
frame_debug_got_null_frame (gdb_stdlog, this_frame, "this_frame NULL");
return current_frame;
}
/* There is always a frame. If this assertion fails, suspect that
something should be calling get_selected_frame() or
get_current_frame(). */
gdb_assert (this_frame != NULL);
/* Make sure we pass an address within THIS_FRAME's code block to
inside_main_func. Otherwise, we might stop unwinding at a
function which has a call instruction as its last instruction if
that function immediately precedes main(). */
if (this_frame->level >= 0
&& !backtrace_past_main
&& inside_main_func (get_frame_address_in_block (this_frame)))
/* Don't unwind past main(), bug always unwind the sentinel frame.
Note, this is done _before_ the frame has been marked as
previously unwound. That way if the user later decides to
allow unwinds past main(), that just happens. */
{
frame_debug_got_null_frame (gdb_stdlog, this_frame, "inside main func");
return NULL;
}
if (this_frame->level > backtrace_limit)
{
error ("Backtrace limit of %d exceeded", backtrace_limit);
}
/* If we're already inside the entry function for the main objfile,
then it isn't valid. Don't apply this test to a dummy frame -
dummy frame PC's typically land in the entry func. Don't apply
this test to the sentinel frame. Sentinel frames should always
be allowed to unwind. */
/* NOTE: cagney/2003-02-25: Don't enable until someone has found
hard evidence that this is needed. */
/* NOTE: cagney/2003-07-07: Fixed a bug in inside_main_func - wasn't
checking for "main" in the minimal symbols. With that fixed
asm-source tests now stop in "main" instead of halting the
backtrace in wierd and wonderful ways somewhere inside the entry
file. Suspect that deprecated_inside_entry_file and
inside_entry_func tests were added to work around that (now
fixed) case. */
/* NOTE: cagney/2003-07-15: danielj (if I'm reading it right)
suggested having the inside_entry_func test use the
inside_main_func msymbol trick (along with entry_point_address I
guess) to determine the address range of the start function.
That should provide a far better stopper than the current
heuristics. */
/* NOTE: cagney/2003-07-15: Need to add a "set backtrace
beyond-entry-func" command so that this can be selectively
disabled. */
if (0
#if 0
&& backtrace_beyond_entry_func
#endif
&& this_frame->type != DUMMY_FRAME && this_frame->level >= 0
&& inside_entry_func (this_frame))
{
frame_debug_got_null_frame (gdb_stdlog, this_frame, "inside entry func");
return NULL;
}
/* If we're inside the entry file, it isn't valid. Don't apply this
test to a dummy frame - dummy frame PC's typically land in the
entry file. Don't apply this test to the sentinel frame.
Sentinel frames should always be allowed to unwind. */
/* NOTE: drow/2002-12-25: should there be a way to disable this
check? It assumes a single small entry file, and the way some
debug readers (e.g. dbxread) figure out which object is the
entry file is somewhat hokey. */
/* NOTE: cagney/2003-01-10: If there is a way of disabling this test
then it should probably be moved to before the ->prev_p test,
above. */
/* NOTE: vinschen/2003-04-01: Disabled. It turns out that the call
to deprecated_inside_entry_file destroys a meaningful backtrace
under some conditions. E. g. the backtrace tests in the
asm-source testcase are broken for some targets. In this test
the functions are all implemented as part of one file and the
testcase is not necessarily linked with a start file (depending
on the target). What happens is, that the first frame is printed
normaly and following frames are treated as being inside the
enttry file then. This way, only the #0 frame is printed in the
backtrace output. */
if (0
&& this_frame->type != DUMMY_FRAME && this_frame->level >= 0
&& deprecated_inside_entry_file (get_frame_pc (this_frame)))
{
frame_debug_got_null_frame (gdb_stdlog, this_frame, "inside entry file");
return NULL;
}
return get_prev_frame_1 (this_frame);
}
CORE_ADDR
get_frame_pc (struct frame_info *frame)
{

View File

@ -312,6 +312,7 @@ extern CORE_ADDR get_frame_base (struct frame_info *);
frame after a frame cache flush (and other similar operations). If
FI is NULL, return the null_frame_id. */
extern struct frame_id get_frame_id (struct frame_info *fi);
extern struct frame_id frame_unwind_id (struct frame_info *next_frame);
/* Assuming that a frame is `normal', return its base-address, or 0 if
the information isn't available. NOTE: This address is really only

View File

@ -2959,7 +2959,7 @@ step_over_function (struct execution_control_state *ecs)
sr_id = get_frame_id (get_current_frame ());
}
else
sr_id = get_frame_id (get_prev_frame (get_current_frame ()));
sr_id = frame_unwind_id (get_current_frame ());
step_resume_breakpoint = set_momentary_breakpoint (sr_sal, sr_id, bp_step_resume);