* frame.c (struct frame_info): Add stop_reason.
authorDaniel Jacobowitz <drow@false.org>
Wed, 18 Oct 2006 19:52:05 +0000 (19:52 +0000)
committerDaniel Jacobowitz <drow@false.org>
Wed, 18 Oct 2006 19:52:05 +0000 (19:52 +0000)
(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.

gdb/ChangeLog
gdb/frame.c
gdb/frame.h
gdb/stack.c

index da9c8cd..678081b 100644 (file)
@@ -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.
index 13b57ad..917ec45 100644 (file)
@@ -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;
index 800cbfb..1d6ae9d 100644 (file)
@@ -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
index 6eb96dc..d58ba31 100644 (file)
@@ -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