diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c9b914ae54..cad4602b0c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,16 @@ +2014-04-18 Pedro alves + Tom Tromey + + PR backtrace/15558 + * frame.c (get_prev_frame_1): Rename to ... + (get_prev_frame_always): ... this, and make extern. Adjust. + (skip_artificial_frames): Use get_prev_frame_always. + (frame_unwind_caller_id, frame_pop, get_prev_frame) + (get_frame_unwind_stop_reason): Adjust to rename. + * frame.h (get_prev_frame_always): Declare. + * inline-frame.c: Include frame.h. + (inline_frame_this_id): Use get_prev_frame_always. + 2014-04-18 Tristan Gingold * solib-darwin.c (darwin_solib_create_inferior_hook): Simplify diff --git a/gdb/frame.c b/gdb/frame.c index 97d54e91a9..013d602fec 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -46,7 +46,6 @@ #include "hashtab.h" #include "valprint.h" -static struct frame_info *get_prev_frame_1 (struct frame_info *this_frame); static struct frame_info *get_prev_frame_raw (struct frame_info *this_frame); static const char *frame_stop_reason_symbol_string (enum unwind_stop_reason reason); @@ -425,9 +424,15 @@ fprint_frame (struct ui_file *file, struct frame_info *fi) static struct frame_info * skip_artificial_frames (struct frame_info *frame) { + /* Note we use get_prev_frame_always, 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 the user + sets a backtrace limit). This is safe, because as these frames + are made up by GDB, there must be a real frame in the chain + below. */ while (get_frame_type (frame) == INLINE_FRAME || get_frame_type (frame) == TAILCALL_FRAME) - frame = get_prev_frame (frame); + frame = get_prev_frame_always (frame); return frame; } @@ -484,13 +489,13 @@ frame_unwind_caller_id (struct frame_info *next_frame) { struct frame_info *this_frame; - /* Use get_prev_frame_1, 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. */ + /* Use get_prev_frame_always, 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. */ next_frame = skip_artificial_frames (next_frame); - this_frame = get_prev_frame_1 (next_frame); + this_frame = get_prev_frame_always (next_frame); if (this_frame) return get_frame_id (skip_artificial_frames (this_frame)); else @@ -956,7 +961,7 @@ frame_pop (struct frame_info *this_frame) } /* Ensure that we have a frame to pop to. */ - prev_frame = get_prev_frame_1 (this_frame); + prev_frame = get_prev_frame_always (this_frame); if (!prev_frame) error (_("Cannot pop the initial frame.")); @@ -1775,8 +1780,8 @@ get_prev_frame_if_no_cycle (struct frame_info *this_frame) Unlike get_prev_frame, this function always tries to unwind the frame. */ -static struct frame_info * -get_prev_frame_1 (struct frame_info *this_frame) +struct frame_info * +get_prev_frame_always (struct frame_info *this_frame) { struct gdbarch *gdbarch; @@ -1785,7 +1790,7 @@ get_prev_frame_1 (struct frame_info *this_frame) if (frame_debug) { - fprintf_unfiltered (gdb_stdlog, "{ get_prev_frame_1 (this_frame="); + fprintf_unfiltered (gdb_stdlog, "{ get_prev_frame_always (this_frame="); if (this_frame != NULL) fprintf_unfiltered (gdb_stdlog, "%d", this_frame->level); else @@ -2137,7 +2142,7 @@ get_prev_frame (struct frame_info *this_frame) return NULL; } - return get_prev_frame_1 (this_frame); + return get_prev_frame_always (this_frame); } CORE_ADDR @@ -2523,7 +2528,7 @@ enum unwind_stop_reason get_frame_unwind_stop_reason (struct frame_info *frame) { /* Fill-in STOP_REASON. */ - get_prev_frame_1 (frame); + get_prev_frame_always (frame); gdb_assert (frame->prev_p); return frame->stop_reason; diff --git a/gdb/frame.h b/gdb/frame.h index e451a9395e..b88bd281cb 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -307,6 +307,13 @@ extern void select_frame (struct frame_info *); extern struct frame_info *get_prev_frame (struct frame_info *); extern struct frame_info *get_next_frame (struct frame_info *); +/* Return a "struct frame_info" corresponding to the frame that called + THIS_FRAME. Returns NULL if there is no such frame. + + Unlike get_prev_frame, this function always tries to unwind the + frame. */ +extern struct frame_info *get_prev_frame_always (struct frame_info *); + /* Given a frame's ID, relocate the frame. Returns NULL if the frame is not found. */ extern struct frame_info *frame_find_by_id (struct frame_id id); diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c index 05ba9ff8db..eb821435cf 100644 --- a/gdb/inline-frame.c +++ b/gdb/inline-frame.c @@ -26,6 +26,7 @@ #include "regcache.h" #include "symtab.h" #include "vec.h" +#include "frame.h" #include "gdb_assert.h" @@ -154,11 +155,11 @@ inline_frame_this_id (struct frame_info *this_frame, /* In order to have a stable frame ID for a given inline function, we must get the stack / special addresses from the underlying - real frame's this_id method. So we must call get_prev_frame. - Because we are inlined into some function, there must be previous - frames, so this is safe - as long as we're careful not to - create any cycles. */ - *this_id = get_frame_id (get_prev_frame (this_frame)); + real frame's this_id method. So we must call + get_prev_frame_always. Because we are inlined into some + function, there must be previous frames, so this is safe - as + long as we're careful not to create any cycles. */ + *this_id = get_frame_id (get_prev_frame_always (this_frame)); /* We need a valid frame ID, so we need to be based on a valid frame. FSF submission NOTE: this would be a good assertion to diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 6f5f9aadfb..08a3a61bf4 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2014-04-18 Tom Tromey + Pedro alves + + PR backtrace/15558 + * gdb.opt/inline-bt.exp: Test backtracing from an inline function + with a backtrace limit. + * gdb.python/py-frame-inline.exp: Test running to an inline + function with a backtrace limit, and printing the newest frame. + * gdb.python/py-frame-inline.c (main): Call f. + 2014-04-17 Marcus Shawcroft * gdb.java/jnpe.exp: Drop srcdir from untested path. diff --git a/gdb/testsuite/gdb.opt/inline-bt.exp b/gdb/testsuite/gdb.opt/inline-bt.exp index c437383555..ce7362352d 100644 --- a/gdb/testsuite/gdb.opt/inline-bt.exp +++ b/gdb/testsuite/gdb.opt/inline-bt.exp @@ -50,3 +50,19 @@ gdb_test "up" "#1 .*func1.*" "up from bar (3)" gdb_test "info frame" ".*inlined into frame.*" "func1 inlined (3)" gdb_test "up" "#2 .*func2.*" "up from func1 (3)" gdb_test "info frame" ".*inlined into frame.*" "func2 inlined (3)" + +# A regression test for having a backtrace limit that forces unwinding +# to stop after an inline frame. GDB needs to compute the frame_id of +# the inline frame, which requires unwinding past all the inline +# frames to the real stack frame, even if that means bypassing the +# user visible backtrace limit. See PR backtrace/15558. +# +# Set a backtrace limit that forces an unwind stop after an inline +# function. +gdb_test_no_output "set backtrace limit 2" +# Force flushing the frame cache. +gdb_test "flushregs" "Register cache flushed." +gdb_test "up" "#1 .*func1.*" "up from bar (4)" +gdb_test "info frame" ".*in func1.*" "info frame still works" +# Verify the user visible limit works as expected. +gdb_test "up" "Initial frame selected; you cannot go up." "up hits limit" diff --git a/gdb/testsuite/gdb.python/py-frame-inline.c b/gdb/testsuite/gdb.python/py-frame-inline.c index a3669bc105..f08e84b6e4 100644 --- a/gdb/testsuite/gdb.python/py-frame-inline.c +++ b/gdb/testsuite/gdb.python/py-frame-inline.c @@ -39,5 +39,7 @@ g (void) int main (void) { - return g (); + int x = g (); + x += f (); + return x; } diff --git a/gdb/testsuite/gdb.python/py-frame-inline.exp b/gdb/testsuite/gdb.python/py-frame-inline.exp index f5cf33e7f5..8851d878df 100644 --- a/gdb/testsuite/gdb.python/py-frame-inline.exp +++ b/gdb/testsuite/gdb.python/py-frame-inline.exp @@ -37,3 +37,17 @@ gdb_test "info frame" "inlined into frame 1\r\n.*" gdb_test "up" "#1 g .*" gdb_test "python print (gdb.selected_frame().read_var('l'))" "\r\n42" + +# A regression test for having a backtrace limit that forces unwinding +# to stop after an inline frame. GDB needs to compute the frame_id of +# the inline frame, which requires unwinding past all the inline +# frames to the real stack frame, even if that means bypassing the +# user visible backtrace limit. See PR backtrace/15558. +# +# Set the limit, and run to an inline function. It's important that +# the frame cache is flushed somehow after setting the limit, to force +# frame id recomputation. +gdb_test_no_output "set backtrace limit 1" +gdb_continue_to_breakpoint "Block break here." + +gdb_test "python print (gdb.newest_frame())" ".*"