aboutsummaryrefslogtreecommitdiff
path: root/gdb/frame.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2014-04-18 10:15:21 +0100
committerPedro Alves <palves@redhat.com>2014-04-18 10:34:09 +0100
commit51d481464ec03be1f5479ed648cc38fb944d7fc0 (patch)
tree2bc7d983f2cba1b04de14d68451b79ae87080fcb /gdb/frame.c
parent1bdad2e0421a56e16c0f4623e6320e1225cbe5ee (diff)
downloadgdb-51d481464ec03be1f5479ed648cc38fb944d7fc0.zip
gdb-51d481464ec03be1f5479ed648cc38fb944d7fc0.tar.gz
gdb-51d481464ec03be1f5479ed648cc38fb944d7fc0.tar.bz2
Fix PR backtrace/15558
This PR is about an assertion failure in GDB that can be triggered by setting "backtrace limit" to a value that causes GDB to stop unwinding after an inline frame. In this case, an assertion in inline_frame_this_id will trigger: /* We need a valid frame ID, so we need to be based on a valid frame. (...). */ gdb_assert (frame_id_p (*this_id)); Looking at the function: static void inline_frame_this_id (struct frame_info *this_frame, void **this_cache, struct frame_id *this_id) { struct symbol *func; /* 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)); we see we're computing the frame id for the inline frame. If this is an inline frame, which is a virtual frame constructed based on debug info, on top of a real stack frame, we should _always_ be able to find where the frame was inlined into, as that ultimately just means peeling off the virtual frames on top of the real stack frame. If there ultimately was no prev (real) stack frame, then we wouldn't have been able to construct the inline frame either, by design. That's what the assertion catches. So we have an inline frame, we should _always_ be able to compute its ID, even if that means bypassing the user backtrace limits to get at the real stack frame's info. The problem is that inline_frame_id calls get_prev_frame, and that takes user backtrace limits into account. Code that wants to bypass the limits calls get_prev_frame_1 instead. Note how get_prev_frame_1 already skips all checks for inline frames: /* If we are unwinding from an inline frame, all of the below tests were already performed when we unwound from the next non-inline frame. We must skip them, since we can not get THIS_FRAME's ID until we have unwound all the way down to the previous non-inline frame. */ if (get_frame_type (this_frame) == INLINE_FRAME) return get_prev_frame_if_no_cycle (this_frame); And note how the related frame_unwind_caller_id function also uses get_prev_frame_1: struct frame_id 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. */ next_frame = skip_artificial_frames (next_frame); this_frame = get_prev_frame_1 (next_frame); if (this_frame) return get_frame_id (skip_artificial_frames (this_frame)); else return null_frame_id; } get_prev_frame_1 is currently static in frame.c. As a _1 suffix is not a good name for an extern function, I've renamed it. Tested on x86-64 Fedora 17. gdb/ 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. gdb/testsuite/ 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.
Diffstat (limited to 'gdb/frame.c')
-rw-r--r--gdb/frame.c31
1 files changed, 18 insertions, 13 deletions
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;