From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17387 invoked by alias); 22 Aug 2006 20:41:44 -0000 Received: (qmail 17367 invoked by uid 22791); 22 Aug 2006 20:41:42 -0000 X-Spam-Check-By: sourceware.org Received: from nevyn.them.org (HELO nevyn.them.org) (66.93.172.17) by sourceware.org (qpsmtpd/0.31.1) with ESMTP; Tue, 22 Aug 2006 20:41:33 +0000 Received: from drow by nevyn.them.org with local (Exim 4.54) id 1GFd3z-0008Hs-OC for gdb-patches@sourceware.org; Tue, 22 Aug 2006 16:41:31 -0400 Date: Tue, 22 Aug 2006 20:58:00 -0000 From: Daniel Jacobowitz To: gdb-patches@sourceware.org Subject: Re: [rfc, frame] Add backtrace stop reasons Message-ID: <20060822204131.GA31802@nevyn.them.org> Mail-Followup-To: gdb-patches@sourceware.org References: <20060819154646.GA25238@nevyn.them.org> <200608201438.k7KEcJho022644@elgar.sibelius.xs4all.nl> <20060820162821.GA20987@nevyn.them.org> <200608222009.k7MK9Vmu002926@elgar.sibelius.xs4all.nl> <20060822202425.GA30970@nevyn.them.org> <200608222037.k7MKbr0V014016@elgar.sibelius.xs4all.nl> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <200608222037.k7MKbr0V014016@elgar.sibelius.xs4all.nl> User-Agent: Mutt/1.5.11+cvs20060403 X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2006-08/txt/msg00169.txt.bz2 On Tue, Aug 22, 2006 at 10:37:53PM +0200, Mark Kettenis wrote: > Perhaps you should post that updated patch! Sure; here it is. -- Daniel Jacobowitz CodeSourcery 2006-08-19 Daniel Jacobowitz * 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. --- gdb/frame.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- gdb/frame.h | 44 +++++++++++++++++++++++++++++++++++++++ gdb/stack.c | 25 ++++++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) Index: src/gdb/frame.c =================================================================== --- src.orig/gdb/frame.c 2006-08-19 11:28:50.000000000 -0400 +++ src/gdb/frame.c 2006-08-19 11:32:17.000000000 -0400 @@ -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 *thi 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 *thi 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 *thi 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 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: src/gdb/frame.h =================================================================== --- src.orig/gdb/frame.h 2006-08-19 11:28:50.000000000 -0400 +++ src/gdb/frame.h 2006-08-19 11:29:33.000000000 -0400 @@ -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: src/gdb/stack.c =================================================================== --- src.orig/gdb/stack.c 2006-08-19 11:28:50.000000000 -0400 +++ src/gdb/stack.c 2006-08-19 11:28:59.000000000 -0400 @@ -923,6 +923,15 @@ 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); + printf_filtered (_(" Outermost frame: %s\n"), + frame_stop_reason_string (reason)); + } + if (calling_frame_info) { printf_filtered (" called by frame at "); @@ -940,6 +949,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 +1173,26 @@ backtrace_command_1 (char *count_exp, in 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