#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
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
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
fprintf_unfiltered (gdb_stdlog, ") ");
}
+ /* Only try to do the unwind once. */
+ if (this_frame->prev_p)
+ {
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "-> ");
+ fprint_frame (gdb_stdlog, this_frame->prev);
+ fprintf_unfiltered (gdb_stdlog, " // cached \n");
+ }
+ return this_frame->prev;
+ }
+ this_frame->prev_p = 1;
+
+ /* If any of the old frame initialization methods are around, use
+ the legacy get_prev_frame method. */
+ if (legacy_frame_p (current_gdbarch))
+ {
+ prev_frame = legacy_get_prev_frame (this_frame);
+ return prev_frame;
+ }
+
+ /* Check that this frame's ID was valid. If it wasn't, don't try to
+ unwind to the prev frame. Be careful to not apply this test to
+ the sentinel frame. */
+ if (this_frame->level >= 0 && !frame_id_p (get_frame_id (this_frame)))
+ {
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "-> ");
+ fprint_frame (gdb_stdlog, NULL);
+ fprintf_unfiltered (gdb_stdlog, " // this ID is NULL }\n");
+ }
+ 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.
+ Since the sentinel frame doesn't really exist, don't compare the
+ inner-most against that sentinel. */
+ if (this_frame->level > 0
+ && frame_id_inner (get_frame_id (this_frame),
+ get_frame_id (this_frame->next)))
+ error ("Previous frame inner to this frame (corrupt stack?)");
+
+ /* 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. */
+ if (this_frame->level > 0
+ && frame_id_eq (get_frame_id (this_frame),
+ get_frame_id (this_frame->next)))
+ error ("Previous frame identical to this frame (corrupt stack?)");
+
+ /* Allocate the new frame but do not wire it in to the frame chain.
+ Some (bad) code in INIT_FRAME_EXTRA_INFO tries to look along
+ frame->next to pull some fancy tricks (of course such code is, by
+ definition, recursive). Try to prevent it.
+
+ There is no reason to worry about memory leaks, should the
+ remainder of the function fail. The allocated memory will be
+ quickly reclaimed when the frame cache is flushed, and the `we've
+ been here before' check above will stop repeated memory
+ allocation calls. */
+ prev_frame = FRAME_OBSTACK_ZALLOC (struct frame_info);
+ prev_frame->level = this_frame->level + 1;
+
+ /* Don't yet compute ->unwind (and hence ->type). It is computed
+ on-demand in get_frame_type, frame_register_unwind, and
+ get_frame_id. */
+
+ /* Don't yet compute the frame's ID. It is computed on-demand by
+ get_frame_id(). */
+
+ /* The unwound frame ID is validate at the start of this function,
+ as part of the logic to decide if that frame should be further
+ unwound, and not here while the prev frame is being created.
+ Doing this makes it possible for the user to examine a frame that
+ has an invalid frame ID.
+
+ Some very old VAX code noted: [...] For the sake of argument,
+ suppose that the stack is somewhat trashed (which is one reason
+ that "info frame" exists). So, return 0 (indicating we don't
+ know the address of the arglist) if we don't know what frame this
+ frame calls. */
+
+ /* Link it in. */
+ this_frame->prev = prev_frame;
+ prev_frame->next = this_frame;
+
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "-> ");
+ fprint_frame (gdb_stdlog, prev_frame);
+ fprintf_unfiltered (gdb_stdlog, " }\n");
+ }
+
+ 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
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;
}
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");
+ frame_debug_got_null_frame (gdb_stdlog, this_frame, "inside main func");
return NULL;
}
&& 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");
- }
+ frame_debug_got_null_frame (gdb_stdlog, this_frame, "inside entry func");
return NULL;
}
- /* Only try to do the unwind once. */
- if (this_frame->prev_p)
- {
- if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "-> ");
- fprint_frame (gdb_stdlog, this_frame->prev);
- fprintf_unfiltered (gdb_stdlog, " // cached \n");
- }
- return this_frame->prev;
- }
- 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.
&& 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");
- }
+ frame_debug_got_null_frame (gdb_stdlog, this_frame, "inside entry file");
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))
- {
- prev_frame = legacy_get_prev_frame (this_frame);
- return prev_frame;
- }
-
- /* Check that this frame's ID was valid. If it wasn't, don't try to
- unwind to the prev frame. Be careful to not apply this test to
- the sentinel frame. */
- if (this_frame->level >= 0 && !frame_id_p (get_frame_id (this_frame)))
- {
- if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "-> ");
- fprint_frame (gdb_stdlog, NULL);
- fprintf_unfiltered (gdb_stdlog, " // this ID is NULL }\n");
- }
- 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.
- Since the sentinel frame doesn't really exist, don't compare the
- inner-most against that sentinel. */
- if (this_frame->level > 0
- && frame_id_inner (get_frame_id (this_frame),
- get_frame_id (this_frame->next)))
- error ("Previous frame inner to this frame (corrupt stack?)");
-
- /* 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. */
- if (this_frame->level > 0
- && frame_id_eq (get_frame_id (this_frame),
- get_frame_id (this_frame->next)))
- error ("Previous frame identical to this frame (corrupt stack?)");
-
- /* Allocate the new frame but do not wire it in to the frame chain.
- Some (bad) code in INIT_FRAME_EXTRA_INFO tries to look along
- frame->next to pull some fancy tricks (of course such code is, by
- definition, recursive). Try to prevent it.
-
- There is no reason to worry about memory leaks, should the
- remainder of the function fail. The allocated memory will be
- quickly reclaimed when the frame cache is flushed, and the `we've
- been here before' check above will stop repeated memory
- allocation calls. */
- prev_frame = FRAME_OBSTACK_ZALLOC (struct frame_info);
- prev_frame->level = this_frame->level + 1;
-
- /* Don't yet compute ->unwind (and hence ->type). It is computed
- on-demand in get_frame_type, frame_register_unwind, and
- get_frame_id. */
-
- /* Don't yet compute the frame's ID. It is computed on-demand by
- get_frame_id(). */
-
- /* The unwound frame ID is validate at the start of this function,
- as part of the logic to decide if that frame should be further
- unwound, and not here while the prev frame is being created.
- Doing this makes it possible for the user to examine a frame that
- has an invalid frame ID.
-
- Some very old VAX code noted: [...] For the sake of argument,
- suppose that the stack is somewhat trashed (which is one reason
- that "info frame" exists). So, return 0 (indicating we don't
- know the address of the arglist) if we don't know what frame this
- frame calls. */
-
- /* Link it in. */
- this_frame->prev = prev_frame;
- prev_frame->next = this_frame;
-
- if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "-> ");
- fprint_frame (gdb_stdlog, prev_frame);
- fprintf_unfiltered (gdb_stdlog, " }\n");
- }
-
- return prev_frame;
+ return get_prev_frame_1 (this_frame);
}
CORE_ADDR