aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog13
-rw-r--r--gdb/frame.c31
-rw-r--r--gdb/frame.h7
-rw-r--r--gdb/inline-frame.c11
-rw-r--r--gdb/testsuite/ChangeLog10
-rw-r--r--gdb/testsuite/gdb.opt/inline-bt.exp16
-rw-r--r--gdb/testsuite/gdb.python/py-frame-inline.c4
-rw-r--r--gdb/testsuite/gdb.python/py-frame-inline.exp14
8 files changed, 87 insertions, 19 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index c9b914a..cad4602 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,16 @@
+2014-04-18 Pedro alves <palves@redhat.com>
+ Tom Tromey <tromey@redhat.com>
+
+ 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 <gingold@adacore.com>
* solib-darwin.c (darwin_solib_create_inferior_hook): Simplify
diff --git a/gdb/frame.c b/gdb/frame.c
index 97d54e9..013d602 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 e451a93..b88bd28 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 05ba9ff..eb82143 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 6f5f9aa..08a3a61 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,13 @@
+2014-04-18 Tom Tromey <palves@redhat.com>
+ Pedro alves <tromey@redhat.com>
+
+ 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 <marcus.shawcroft@arm.com>
* 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 c437383..ce73623 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 a3669bc..f08e84b 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 f5cf33e..8851d87 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())" ".*"