aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog12
-rw-r--r--gdb/frame.c67
-rw-r--r--gdb/frame.h44
-rw-r--r--gdb/stack.c26
4 files changed, 147 insertions, 2 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index da9c8cd..678081b 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,17 @@
2006-10-18 Daniel Jacobowitz <dan@codesourcery.com>
+ * frame.c (struct frame_info): Add stop_reason.
+ (get_prev_frame_1): Set stop_reason. Don't call error for
+ stop reasons.
+ (get_frame_unwind_stop_reason, frame_stop_reason_string): New.
+ * frame.h (enum unwind_stop_reason): New.
+ (get_frame_unwind_stop_reason, frame_stop_reason_string): New
+ prototypes.
+ * stack.c (frame_info): Print the stop reason.
+ (backtrace_command_1): Print the stop reason for errors.
+
+2006-10-18 Daniel Jacobowitz <dan@codesourcery.com>
+
* inferior.h (start_remote): Update prototype.
* infrun.c (start_remote): Take FROM_TTY. Call
post_create_inferior.
diff --git a/gdb/frame.c b/gdb/frame.c
index 13b57ad..917ec45 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -107,6 +107,10 @@ struct frame_info
struct frame_info *next; /* down, inner, younger */
int prev_p;
struct frame_info *prev; /* up, outer, older */
+
+ /* 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;
};
/* Flag to control debugging. */
@@ -1055,6 +1059,7 @@ get_prev_frame_1 (struct frame_info *this_frame)
return this_frame->prev;
}
this_frame->prev_p = 1;
+ this_frame->stop_reason = UNWIND_NO_REASON;
/* 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
@@ -1068,6 +1073,7 @@ get_prev_frame_1 (struct frame_info *this_frame)
fprint_frame (gdb_stdlog, NULL);
fprintf_unfiltered (gdb_stdlog, " // this ID is NULL }\n");
}
+ this_frame->stop_reason = UNWIND_NULL_ID;
return NULL;
}
@@ -1078,14 +1084,32 @@ get_prev_frame_1 (struct frame_info *this_frame)
if (this_frame->next->level >= 0
&& this_frame->next->unwind->type != SIGTRAMP_FRAME
&& frame_id_inner (this_id, get_frame_id (this_frame->next)))
- error (_("Previous frame inner to this frame (corrupt stack?)"));
+ {
+ 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. */
if (this_frame->level > 0
&& frame_id_eq (this_id, get_frame_id (this_frame->next)))
- error (_("Previous frame identical to this frame (corrupt stack?)"));
+ {
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "-> ");
+ fprint_frame (gdb_stdlog, NULL);
+ fprintf_unfiltered (gdb_stdlog, " // this frame has same ID }\n");
+ }
+ this_frame->stop_reason = UNWIND_SAME_ID;
+ return NULL;
+ }
/* 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
@@ -1556,6 +1580,45 @@ frame_sp_unwind (struct frame_info *next_frame)
internal_error (__FILE__, __LINE__, _("Missing unwind SP method"));
}
+/* Return the reason why we can't unwind past FRAME. */
+
+enum unwind_stop_reason
+get_frame_unwind_stop_reason (struct frame_info *frame)
+{
+ /* If we haven't tried to unwind past this point yet, then assume
+ that unwinding would succeed. */
+ if (frame->prev_p == 0)
+ return UNWIND_NO_REASON;
+
+ /* Otherwise, we set a reason when we succeeded (or failed) to
+ unwind. */
+ return frame->stop_reason;
+}
+
+/* Return a string explaining REASON. */
+
+const char *
+frame_stop_reason_string (enum unwind_stop_reason reason)
+{
+ switch (reason)
+ {
+ 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_NO_REASON:
+ case UNWIND_FIRST_ERROR:
+ default:
+ internal_error (__FILE__, __LINE__,
+ "Invalid frame stop reason");
+ }
+}
+
extern initialize_file_ftype _initialize_frame; /* -Wmissing-prototypes */
static struct cmd_list_element *set_backtrace_cmdlist;
diff --git a/gdb/frame.h b/gdb/frame.h
index 800cbfb..1d6ae9d 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -394,6 +394,50 @@ enum frame_type
};
extern enum frame_type get_frame_type (struct frame_info *);
+/* For frames where we can not unwind further, describe why. */
+
+enum unwind_stop_reason
+ {
+ /* No particular reason; either we haven't tried unwinding yet,
+ or we didn't fail. */
+ UNWIND_NO_REASON,
+
+ /* The previous frame's analyzer returns an invalid result
+ from this_id.
+
+ FIXME drow/2006-08-16: This is how GDB used to indicate end of
+ stack. We should migrate to a model where frames always have a
+ valid ID, and this becomes not just an error but an internal
+ error. But that's a project for another day. */
+ UNWIND_NULL_ID,
+
+ /* All the conditions after this point are considered errors;
+ abnormal stack termination. If a backtrace stops for one
+ of these reasons, we'll let the user know. This marker
+ 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
+ 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,
+ };
+
+/* Return the reason why we can't unwind past this frame. */
+
+enum unwind_stop_reason get_frame_unwind_stop_reason (struct frame_info *);
+
+/* Translate a reason code to an informative string. */
+
+const char *frame_stop_reason_string (enum unwind_stop_reason);
+
/* Unwind the stack frame so that the value of REGNUM, in the previous
(up, older) frame is returned. If VALUEP is NULL, don't
fetch/compute the value. Instead just return the location of the
diff --git a/gdb/stack.c b/gdb/stack.c
index 6eb96dc..d58ba31 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -923,6 +923,16 @@ frame_info (char *addr_exp, int from_tty)
deprecated_print_address_numeric (frame_pc_unwind (fi), 1, gdb_stdout);
printf_filtered ("\n");
+ if (calling_frame_info == NULL)
+ {
+ enum unwind_stop_reason reason;
+
+ reason = get_frame_unwind_stop_reason (fi);
+ if (reason != UNWIND_NO_REASON)
+ printf_filtered (_(" Outermost frame: %s\n"),
+ frame_stop_reason_string (reason));
+ }
+
if (calling_frame_info)
{
printf_filtered (" called by frame at ");
@@ -940,6 +950,7 @@ frame_info (char *addr_exp, int from_tty)
}
if (get_next_frame (fi) || calling_frame_info)
puts_filtered ("\n");
+
if (s)
printf_filtered (" source language %s.\n",
language_str (s->language));
@@ -1163,11 +1174,26 @@ backtrace_command_1 (char *count_exp, int show_locals, int from_tty)
print_frame_info (fi, 1, LOCATION, 1);
if (show_locals)
print_frame_local_vars (fi, 1, gdb_stdout);
+
+ /* Save the last frame to check for error conditions. */
+ trailing = fi;
}
/* If we've stopped before the end, mention that. */
if (fi && from_tty)
printf_filtered (_("(More stack frames follow...)\n"));
+
+ /* If we've run out of frames, and the reason appears to be an error
+ condition, print it. */
+ if (fi == NULL && trailing != NULL)
+ {
+ enum unwind_stop_reason reason;
+
+ reason = get_frame_unwind_stop_reason (trailing);
+ if (reason > UNWIND_FIRST_ERROR)
+ printf_filtered (_("Backtrace stopped: %s\n"),
+ frame_stop_reason_string (reason));
+ }
}
struct backtrace_command_args