/* Cache and manage frames for GDB, the GNU debugger.
- Copyright (C) 1986-1987, 1989, 1991, 1994-1996, 1998, 2000-2004,
- 2007-2012 Free Software Foundation, Inc.
+ Copyright (C) 1986-2014 Free Software Foundation, Inc.
This file is part of GDB.
#include "value.h"
#include "inferior.h" /* for inferior_ptid */
#include "regcache.h"
-#include "gdb_assert.h"
-#include "gdb_string.h"
#include "user-regs.h"
#include "gdb_obstack.h"
#include "dummy-frame.h"
#include "gdbcmd.h"
#include "observer.h"
#include "objfiles.h"
-#include "exceptions.h"
#include "gdbthread.h"
#include "block.h"
#include "inline-frame.h"
-#include "tracepoint.h"
+#include "tracepoint.h"
+#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);
+
+/* Status of some values cached in the frame_info object. */
+
+enum cached_copy_status
+{
+ /* Value is unknown. */
+ CC_UNKNOWN,
+
+ /* We have a value. */
+ CC_VALUE,
+
+ /* Value was not saved. */
+ CC_NOT_SAVED,
+
+ /* Value is unavailable. */
+ CC_UNAVAILABLE
+};
/* We keep a cache of stack frames, each of which is a "struct
frame_info". The innermost one gets allocated (in
/* Cached copy of the previous frame's resume address. */
struct {
- int p;
+ enum cached_copy_status status;
CORE_ADDR value;
} prev_pc;
/* 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;
+
+ /* A frame specific string describing the STOP_REASON in more detail.
+ Only valid when PREV_P is set, but even then may still be NULL. */
+ const char *stop_string;
};
-/* A frame stash used to speed up frame lookups. */
+/* A frame stash used to speed up frame lookups. Create a hash table
+ to stash frames previously accessed from the frame cache for
+ quicker subsequent retrieval. The hash table is emptied whenever
+ the frame cache is invalidated. */
+
+static htab_t frame_stash;
+
+/* Internal function to calculate a hash from the frame_id addresses,
+ using as many valid addresses as possible. Frames below level 0
+ are not stored in the hash table. */
+
+static hashval_t
+frame_addr_hash (const void *ap)
+{
+ const struct frame_info *frame = ap;
+ const struct frame_id f_id = frame->this_id.value;
+ hashval_t hash = 0;
+
+ gdb_assert (f_id.stack_status != FID_STACK_INVALID
+ || f_id.code_addr_p
+ || f_id.special_addr_p);
-/* We currently only stash one frame at a time, as this seems to be
- sufficient for now. */
-static struct frame_info *frame_stash = NULL;
+ if (f_id.stack_status == FID_STACK_VALID)
+ hash = iterative_hash (&f_id.stack_addr,
+ sizeof (f_id.stack_addr), hash);
+ if (f_id.code_addr_p)
+ hash = iterative_hash (&f_id.code_addr,
+ sizeof (f_id.code_addr), hash);
+ if (f_id.special_addr_p)
+ hash = iterative_hash (&f_id.special_addr,
+ sizeof (f_id.special_addr), hash);
-/* Add the following FRAME to the frame stash. */
+ return hash;
+}
+
+/* Internal equality function for the hash table. This function
+ defers equality operations to frame_id_eq. */
+
+static int
+frame_addr_hash_eq (const void *a, const void *b)
+{
+ const struct frame_info *f_entry = a;
+ const struct frame_info *f_element = b;
+
+ return frame_id_eq (f_entry->this_id.value,
+ f_element->this_id.value);
+}
+
+/* Internal function to create the frame_stash hash table. 100 seems
+ to be a good compromise to start the hash table at. */
static void
+frame_stash_create (void)
+{
+ frame_stash = htab_create (100,
+ frame_addr_hash,
+ frame_addr_hash_eq,
+ NULL);
+}
+
+/* Internal function to add a frame to the frame_stash hash table.
+ Returns false if a frame with the same ID was already stashed, true
+ otherwise. */
+
+static int
frame_stash_add (struct frame_info *frame)
{
- frame_stash = frame;
+ struct frame_info **slot;
+
+ /* Do not try to stash the sentinel frame. */
+ gdb_assert (frame->level >= 0);
+
+ slot = (struct frame_info **) htab_find_slot (frame_stash,
+ frame,
+ INSERT);
+
+ /* If we already have a frame in the stack with the same id, we
+ either have a stack cycle (corrupted stack?), or some bug
+ elsewhere in GDB. In any case, ignore the duplicate and return
+ an indication to the caller. */
+ if (*slot != NULL)
+ return 0;
+
+ *slot = frame;
+ return 1;
}
-/* Search the frame stash for an entry with the given frame ID.
- If found, return that frame. Otherwise return NULL. */
+/* Internal function to search the frame stash for an entry with the
+ given frame ID. If found, return that frame. Otherwise return
+ NULL. */
static struct frame_info *
frame_stash_find (struct frame_id id)
{
- if (frame_stash && frame_id_eq (frame_stash->this_id.value, id))
- return frame_stash;
+ struct frame_info dummy;
+ struct frame_info *frame;
- return NULL;
+ dummy.this_id.value = id;
+ frame = htab_find (frame_stash, &dummy);
+ return frame;
}
-/* Invalidate the frame stash by removing all entries in it. */
+/* Internal function to invalidate the frame stash by removing all
+ entries in it. This only occurs when the frame cache is
+ invalidated. */
static void
frame_stash_invalidate (void)
{
- frame_stash = NULL;
+ htab_empty (frame_stash);
}
/* Flag to control debugging. */
fprint_frame_id (struct ui_file *file, struct frame_id id)
{
fprintf_unfiltered (file, "{");
- fprint_field (file, "stack", id.stack_addr_p, id.stack_addr);
+
+ if (id.stack_status == FID_STACK_INVALID)
+ fprintf_unfiltered (file, "!stack");
+ else if (id.stack_status == FID_STACK_UNAVAILABLE)
+ fprintf_unfiltered (file, "stack=<unavailable>");
+ else
+ fprintf_unfiltered (file, "stack=%s", hex_string (id.stack_addr));
fprintf_unfiltered (file, ",");
+
fprint_field (file, "code", id.code_addr_p, id.code_addr);
fprintf_unfiltered (file, ",");
+
fprint_field (file, "special", id.special_addr_p, id.special_addr);
+
if (id.artificial_depth)
fprintf_unfiltered (file, ",artificial=%d", id.artificial_depth);
+
fprintf_unfiltered (file, "}");
}
case INLINE_FRAME:
fprintf_unfiltered (file, "INLINE_FRAME");
return;
- case SENTINEL_FRAME:
- fprintf_unfiltered (file, "SENTINEL_FRAME");
+ case TAILCALL_FRAME:
+ fprintf_unfiltered (file, "TAILCALL_FRAME");
return;
case SIGTRAMP_FRAME:
fprintf_unfiltered (file, "SIGTRAMP_FRAME");
case ARCH_FRAME:
fprintf_unfiltered (file, "ARCH_FRAME");
return;
+ case SENTINEL_FRAME:
+ fprintf_unfiltered (file, "SENTINEL_FRAME");
+ return;
default:
fprintf_unfiltered (file, "<unknown type>");
return;
fprintf_unfiltered (file, "<unknown>");
fprintf_unfiltered (file, ",");
fprintf_unfiltered (file, "pc=");
- if (fi->next != NULL && fi->next->prev_pc.p)
- fprintf_unfiltered (file, "%s", hex_string (fi->next->prev_pc.value));
- else
+ if (fi->next == NULL || fi->next->prev_pc.status == CC_UNKNOWN)
fprintf_unfiltered (file, "<unknown>");
+ else if (fi->next->prev_pc.status == CC_VALUE)
+ fprintf_unfiltered (file, "%s",
+ hex_string (fi->next->prev_pc.value));
+ else if (fi->next->prev_pc.status == CC_NOT_SAVED)
+ val_print_not_saved (file);
+ else if (fi->next->prev_pc.status == CC_UNAVAILABLE)
+ val_print_unavailable (file);
fprintf_unfiltered (file, ",");
fprintf_unfiltered (file, "id=");
if (fi->this_id.p)
static struct frame_info *
skip_artificial_frames (struct frame_info *frame)
{
- while (get_frame_type (frame) == INLINE_FRAME)
- frame = get_prev_frame (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_always (frame);
return frame;
}
+/* Compute the frame's uniq ID that can be used to, later, re-find the
+ frame. */
+
+static void
+compute_frame_id (struct frame_info *fi)
+{
+ gdb_assert (!fi->this_id.p);
+
+ if (frame_debug)
+ fprintf_unfiltered (gdb_stdlog, "{ compute_frame_id (fi=%d) ",
+ fi->level);
+ /* Find the unwinder. */
+ if (fi->unwind == NULL)
+ frame_unwind_find_by_frame (fi, &fi->prologue_cache);
+ /* Find THIS frame's ID. */
+ /* Default to outermost if no ID is found. */
+ fi->this_id.value = outer_frame_id;
+ fi->unwind->this_id (fi, &fi->prologue_cache, &fi->this_id.value);
+ gdb_assert (frame_id_p (fi->this_id.value));
+ fi->this_id.p = 1;
+ if (frame_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "-> ");
+ fprint_frame_id (gdb_stdlog, fi->this_id.value);
+ fprintf_unfiltered (gdb_stdlog, " }\n");
+ }
+}
+
/* Return a frame uniq ID that can be used to, later, re-find the
frame. */
if (fi == NULL)
return null_frame_id;
- if (!fi->this_id.p)
- {
- if (frame_debug)
- fprintf_unfiltered (gdb_stdlog, "{ get_frame_id (fi=%d) ",
- fi->level);
- /* Find the unwinder. */
- if (fi->unwind == NULL)
- frame_unwind_find_by_frame (fi, &fi->prologue_cache);
- /* Find THIS frame's ID. */
- /* Default to outermost if no ID is found. */
- fi->this_id.value = outer_frame_id;
- fi->unwind->this_id (fi, &fi->prologue_cache, &fi->this_id.value);
- gdb_assert (frame_id_p (fi->this_id.value));
- fi->this_id.p = 1;
- if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "-> ");
- fprint_frame_id (gdb_stdlog, fi->this_id.value);
- fprintf_unfiltered (gdb_stdlog, " }\n");
- }
- }
-
- frame_stash_add (fi);
-
+ gdb_assert (fi->this_id.p);
return fi->this_id.value;
}
{
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
}
const struct frame_id null_frame_id; /* All zeros. */
-const struct frame_id outer_frame_id = { 0, 0, 0, 0, 0, 1, 0 };
+const struct frame_id outer_frame_id = { 0, 0, 0, FID_STACK_INVALID, 0, 1, 0 };
struct frame_id
frame_id_build_special (CORE_ADDR stack_addr, CORE_ADDR code_addr,
struct frame_id id = null_frame_id;
id.stack_addr = stack_addr;
- id.stack_addr_p = 1;
+ id.stack_status = FID_STACK_VALID;
+ id.code_addr = code_addr;
+ id.code_addr_p = 1;
+ id.special_addr = special_addr;
+ id.special_addr_p = 1;
+ return id;
+}
+
+/* See frame.h. */
+
+struct frame_id
+frame_id_build_unavailable_stack (CORE_ADDR code_addr)
+{
+ struct frame_id id = null_frame_id;
+
+ id.stack_status = FID_STACK_UNAVAILABLE;
+ id.code_addr = code_addr;
+ id.code_addr_p = 1;
+ return id;
+}
+
+/* See frame.h. */
+
+struct frame_id
+frame_id_build_unavailable_stack_special (CORE_ADDR code_addr,
+ CORE_ADDR special_addr)
+{
+ struct frame_id id = null_frame_id;
+
+ id.stack_status = FID_STACK_UNAVAILABLE;
id.code_addr = code_addr;
id.code_addr_p = 1;
id.special_addr = special_addr;
struct frame_id id = null_frame_id;
id.stack_addr = stack_addr;
- id.stack_addr_p = 1;
+ id.stack_status = FID_STACK_VALID;
id.code_addr = code_addr;
id.code_addr_p = 1;
return id;
struct frame_id id = null_frame_id;
id.stack_addr = stack_addr;
- id.stack_addr_p = 1;
+ id.stack_status = FID_STACK_VALID;
return id;
}
int p;
/* The frame is valid iff it has a valid stack address. */
- p = l.stack_addr_p;
+ p = l.stack_status != FID_STACK_INVALID;
/* outer_frame_id is also valid. */
if (!p && memcmp (&l, &outer_frame_id, sizeof (l)) == 0)
p = 1;
{
int eq;
- if (!l.stack_addr_p && l.special_addr_p
- && !r.stack_addr_p && r.special_addr_p)
+ if (l.stack_status == FID_STACK_INVALID && l.special_addr_p
+ && r.stack_status == FID_STACK_INVALID && r.special_addr_p)
/* The outermost frame marker is equal to itself. This is the
dodgy thing about outer_frame_id, since between execution steps
we might step into another function - from which we can't
unwind either. More thought required to get rid of
outer_frame_id. */
eq = 1;
- else if (!l.stack_addr_p || !r.stack_addr_p)
+ else if (l.stack_status == FID_STACK_INVALID
+ || l.stack_status == FID_STACK_INVALID)
/* Like a NaN, if either ID is invalid, the result is false.
Note that a frame ID is invalid iff it is the null frame ID. */
eq = 0;
- else if (l.stack_addr != r.stack_addr)
+ else if (l.stack_status != r.stack_status || l.stack_addr != r.stack_addr)
/* If .stack addresses are different, the frames are different. */
eq = 0;
else if (l.code_addr_p && r.code_addr_p && l.code_addr != r.code_addr)
{
int inner;
- if (!l.stack_addr_p || !r.stack_addr_p)
- /* Like NaN, any operation involving an invalid ID always fails. */
+ if (l.stack_status != FID_STACK_VALID || r.stack_status != FID_STACK_VALID)
+ /* Like NaN, any operation involving an invalid ID always fails.
+ Likewise if either ID has an unavailable stack address. */
inner = 0;
else if (l.artificial_depth > r.artificial_depth
&& l.stack_addr == r.stack_addr
&& l.special_addr == r.special_addr)
{
/* Same function, different inlined functions. */
- struct block *lb, *rb;
+ const struct block *lb, *rb;
gdb_assert (l.code_addr_p && r.code_addr_p);
return NULL;
}
-static int
-frame_unwind_pc_if_available (struct frame_info *this_frame, CORE_ADDR *pc)
+static CORE_ADDR
+frame_unwind_pc (struct frame_info *this_frame)
{
- if (!this_frame->prev_pc.p)
+ if (this_frame->prev_pc.status == CC_UNKNOWN)
{
if (gdbarch_unwind_pc_p (frame_unwind_arch (this_frame)))
{
{
pc = gdbarch_unwind_pc (prev_gdbarch, this_frame);
}
- if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
- {
- this_frame->prev_pc.p = -1;
-
- if (frame_debug)
- fprintf_unfiltered (gdb_stdlog,
- "{ frame_unwind_pc (this_frame=%d)"
- " -> <unavailable> }\n",
- this_frame->level);
- }
- else if (ex.reason < 0)
+ if (ex.reason < 0)
{
- throw_exception (ex);
+ if (ex.error == NOT_AVAILABLE_ERROR)
+ {
+ this_frame->prev_pc.status = CC_UNAVAILABLE;
+
+ if (frame_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "{ frame_unwind_pc (this_frame=%d)"
+ " -> <unavailable> }\n",
+ this_frame->level);
+ }
+ else if (ex.error == OPTIMIZED_OUT_ERROR)
+ {
+ this_frame->prev_pc.status = CC_NOT_SAVED;
+
+ if (frame_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "{ frame_unwind_pc (this_frame=%d)"
+ " -> <not saved> }\n",
+ this_frame->level);
+ }
+ else
+ throw_exception (ex);
}
else
{
this_frame->prev_pc.value = pc;
- this_frame->prev_pc.p = 1;
+ this_frame->prev_pc.status = CC_VALUE;
if (frame_debug)
fprintf_unfiltered (gdb_stdlog,
"{ frame_unwind_pc (this_frame=%d) "
else
internal_error (__FILE__, __LINE__, _("No unwind_pc method"));
}
- if (this_frame->prev_pc.p < 0)
- {
- *pc = -1;
- return 0;
- }
- else
- {
- *pc = this_frame->prev_pc.value;
- return 1;
- }
-}
-
-static CORE_ADDR
-frame_unwind_pc (struct frame_info *this_frame)
-{
- CORE_ADDR pc;
- if (!frame_unwind_pc_if_available (this_frame, &pc))
+ if (this_frame->prev_pc.status == CC_VALUE)
+ return this_frame->prev_pc.value;
+ else if (this_frame->prev_pc.status == CC_UNAVAILABLE)
throw_error (NOT_AVAILABLE_ERROR, _("PC not available"));
+ else if (this_frame->prev_pc.status == CC_NOT_SAVED)
+ throw_error (OPTIMIZED_OUT_ERROR, _("PC not saved"));
else
- return pc;
+ internal_error (__FILE__, __LINE__,
+ "unexpected prev_pc status: %d",
+ (int) this_frame->prev_pc.status);
}
CORE_ADDR
}
int
-frame_unwind_caller_pc_if_available (struct frame_info *this_frame,
- CORE_ADDR *pc)
-{
- return frame_unwind_pc_if_available (skip_artificial_frames (this_frame), pc);
-}
-
-int
get_frame_func_if_available (struct frame_info *this_frame, CORE_ADDR *pc)
{
struct frame_info *next_frame = this_frame->next;
static enum register_status
do_frame_register_read (void *src, int regnum, gdb_byte *buf)
{
- if (!frame_register_read (src, regnum, buf))
+ if (!deprecated_frame_register_read (src, regnum, buf))
return REG_UNAVAILABLE;
else
return REG_VALID;
{
/* Popping a dummy frame involves restoring more than just registers.
dummy_frame_pop does all the work. */
- dummy_frame_pop (get_frame_id (this_frame));
+ dummy_frame_pop (get_frame_id (this_frame), inferior_ptid);
return;
}
/* 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."));
+ /* Ignore TAILCALL_FRAME type frames, they were executed already before
+ entering THISFRAME. */
+ while (get_frame_type (prev_frame) == TAILCALL_FRAME)
+ prev_frame = get_prev_frame (prev_frame);
+
/* Make a copy of all the register values unwound from this frame.
Save them in a scratch buffer so that there isn't a race between
trying to extract the old values from the current regcache while
&lval, &addr, &realnum, buf);
if (optimized)
- error (_("Register %d was optimized out"), regnum);
+ throw_error (OPTIMIZED_OUT_ERROR,
+ _("Register %d was not saved"), regnum);
if (unavailable)
throw_error (NOT_AVAILABLE_ERROR,
_("Register %d is not available"), regnum);
{
fprintf_unfiltered (gdb_stdlog, "->");
if (value_optimized_out (value))
- fprintf_unfiltered (gdb_stdlog, " optimized out");
+ {
+ fprintf_unfiltered (gdb_stdlog, " ");
+ val_print_optimized_out (value, gdb_stdlog);
+ }
else
{
if (VALUE_LVAL (value) == lval_register)
frame_register (frame, regnum, &optim, &unavail,
&lval, &addr, &realnum, NULL);
if (optim)
- error (_("Attempt to assign to a value that was optimized out."));
+ error (_("Attempt to assign to a register that was not saved."));
switch (lval)
{
case lval_memory:
{
- /* FIXME: write_memory doesn't yet take constant buffers.
- Arrrg! */
- gdb_byte tmp[MAX_REGISTER_SIZE];
-
- memcpy (tmp, buf, register_size (gdbarch, regnum));
- write_memory (addr, tmp, register_size (gdbarch, regnum));
+ write_memory (addr, buf, register_size (gdbarch, regnum));
break;
}
case lval_register:
}
}
-/* frame_register_read ()
+/* This function is deprecated. Use get_frame_register_value instead,
+ which provides more accurate information.
Find and return the value of REGNUM for the specified stack frame.
The number of bytes copied is REGISTER_SIZE (REGNUM).
Returns 0 if the register value could not be found. */
int
-frame_register_read (struct frame_info *frame, int regnum,
+deprecated_frame_register_read (struct frame_info *frame, int regnum,
gdb_byte *myaddr)
{
int optimized;
{
gdb_byte buf[MAX_REGISTER_SIZE];
- frame_register_read (frame, regnum, buf);
+ deprecated_frame_register_read (frame, regnum, buf);
memcpy (buf + offset, myaddr, curr_len);
put_frame_register (frame, regnum, buf);
}
very likely to read this, and the corresponding unwinder is
entitled to rely that the PC doesn't magically change. */
fi->next->prev_pc.value = pc;
- fi->next->prev_pc.p = 1;
+ fi->next->prev_pc.status = CC_VALUE;
/* We currently assume that frame chain's can't cross spaces. */
fi->pspace = fi->next->pspace;
}
}
-/* Return a "struct frame_info" corresponding to the frame that called
- THIS_FRAME. Returns NULL if there is no such frame.
+/* Called during frame unwinding to remove a previous frame pointer from a
+ frame passed in ARG. */
- Unlike get_prev_frame, this function always tries to unwind the
- frame. */
+static void
+remove_prev_frame (void *arg)
+{
+ struct frame_info *this_frame, *prev_frame;
+
+ this_frame = (struct frame_info *) arg;
+ prev_frame = this_frame->prev;
+ gdb_assert (prev_frame != NULL);
+
+ prev_frame->next = NULL;
+ this_frame->prev = NULL;
+}
+
+/* Get the previous raw frame, and check that it is not identical to
+ same other frame frame already in the chain. If it is, there is
+ most likely a stack cycle, so we discard it, and mark THIS_FRAME as
+ outermost, with UNWIND_SAME_ID stop reason. Unlike the other
+ validity tests, that compare THIS_FRAME and the next frame, we do
+ this right after creating the previous frame, to avoid ever ending
+ up with two frames with the same id in the frame chain. */
static struct frame_info *
-get_prev_frame_1 (struct frame_info *this_frame)
+get_prev_frame_if_no_cycle (struct frame_info *this_frame)
+{
+ struct frame_info *prev_frame;
+ struct cleanup *prev_frame_cleanup;
+
+ prev_frame = get_prev_frame_raw (this_frame);
+ if (prev_frame == NULL)
+ return NULL;
+
+ /* The cleanup will remove the previous frame that get_prev_frame_raw
+ linked onto THIS_FRAME. */
+ prev_frame_cleanup = make_cleanup (remove_prev_frame, this_frame);
+
+ compute_frame_id (prev_frame);
+ if (!frame_stash_add (prev_frame))
+ {
+ /* Another frame with the same id was already in the stash. We just
+ detected a cycle. */
+ 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;
+ /* Unlink. */
+ prev_frame->next = NULL;
+ this_frame->prev = NULL;
+ prev_frame = NULL;
+ }
+
+ discard_cleanups (prev_frame_cleanup);
+ return prev_frame;
+}
+
+/* Helper function for get_prev_frame_always, this is called inside a
+ TRY_CATCH block. Return the frame that called THIS_FRAME or NULL if
+ there is no such frame. This may throw an exception. */
+
+static struct frame_info *
+get_prev_frame_always_1 (struct frame_info *this_frame)
{
- struct frame_id this_id;
struct gdbarch *gdbarch;
gdb_assert (this_frame != NULL);
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
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_raw (this_frame);
+ return get_prev_frame_if_no_cycle (this_frame);
/* Check that this frame is unwindable. If it isn't, don't try to
unwind to the prev frame. */
&this_frame->prologue_cache);
if (this_frame->stop_reason != UNWIND_NO_REASON)
- return NULL;
-
- /* 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
- the sentinel frame. */
- this_id = get_frame_id (this_frame);
- if (this_frame->level >= 0 && frame_id_eq (this_id, outer_frame_id))
{
if (frame_debug)
{
+ enum unwind_stop_reason reason = this_frame->stop_reason;
+
fprintf_unfiltered (gdb_stdlog, "-> ");
fprint_frame (gdb_stdlog, NULL);
- fprintf_unfiltered (gdb_stdlog, " // this ID is NULL }\n");
+ fprintf_unfiltered (gdb_stdlog, " // %s }\n",
+ frame_stop_reason_symbol_string (reason));
}
- this_frame->stop_reason = UNWIND_NULL_ID;
return NULL;
}
See the comment at frame_id_inner for details. */
if (get_frame_type (this_frame) == NORMAL_FRAME
&& this_frame->next->unwind->type == NORMAL_FRAME
- && frame_id_inner (get_frame_arch (this_frame->next), this_id,
+ && frame_id_inner (get_frame_arch (this_frame->next),
+ get_frame_id (this_frame),
get_frame_id (this_frame->next)))
{
CORE_ADDR this_pc_in_block;
/* gcc -fsplit-stack __morestack can continue the stack anywhere. */
this_pc_in_block = get_frame_address_in_block (this_frame);
- morestack_msym = lookup_minimal_symbol_by_pc (this_pc_in_block);
+ morestack_msym = lookup_minimal_symbol_by_pc (this_pc_in_block).minsym;
if (morestack_msym)
- morestack_name = SYMBOL_LINKAGE_NAME (morestack_msym);
+ morestack_name = MSYMBOL_LINKAGE_NAME (morestack_msym);
if (!morestack_name || strcmp (morestack_name, "__morestack") != 0)
{
if (frame_debug)
}
}
- /* 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)))
- {
- 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;
- }
-
/* Check that this and the next frame do not unwind the PC register
to the same memory location. If they do, then even though they
have different frame IDs, the new frame will be bogus; two
}
}
- return get_prev_frame_raw (this_frame);
+ return get_prev_frame_if_no_cycle (this_frame);
+}
+
+/* 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. */
+
+struct frame_info *
+get_prev_frame_always (struct frame_info *this_frame)
+{
+ volatile struct gdb_exception ex;
+ struct frame_info *prev_frame = NULL;
+
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ prev_frame = get_prev_frame_always_1 (this_frame);
+ }
+ if (ex.reason < 0)
+ {
+ if (ex.error == MEMORY_ERROR)
+ {
+ this_frame->stop_reason = UNWIND_MEMORY_ERROR;
+ if (ex.message != NULL)
+ {
+ char *stop_string;
+ size_t size;
+
+ /* The error needs to live as long as the frame does.
+ Allocate using stack local STOP_STRING then assign the
+ pointer to the frame, this allows the STOP_STRING on the
+ frame to be of type 'const char *'. */
+ size = strlen (ex.message) + 1;
+ stop_string = frame_obstack_zalloc (size);
+ memcpy (stop_string, ex.message, size);
+ this_frame->stop_string = stop_string;
+ }
+ prev_frame = NULL;
+ }
+ else
+ throw_exception (ex);
+ }
+
+ return prev_frame;
}
/* Construct a new "struct frame_info" and link it previous to
static int
inside_main_func (struct frame_info *this_frame)
{
- struct minimal_symbol *msymbol;
+ struct bound_minimal_symbol msymbol;
CORE_ADDR maddr;
if (symfile_objfile == 0)
return 0;
msymbol = lookup_minimal_symbol (main_name (), NULL, symfile_objfile);
- if (msymbol == NULL)
+ if (msymbol.minsym == NULL)
return 0;
/* Make certain that the code, and not descriptor, address is
returned. */
maddr = gdbarch_convert_from_func_ptr_addr (get_frame_arch (this_frame),
- SYMBOL_VALUE_ADDRESS (msymbol),
+ BMSYMBOL_VALUE_ADDRESS (msymbol),
¤t_target);
return maddr == get_frame_func (this_frame);
}
return NULL;
}
- return get_prev_frame_1 (this_frame);
+ return get_prev_frame_always (this_frame);
}
CORE_ADDR
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;
+ /* Fill-in STOP_REASON. */
+ get_prev_frame_always (frame);
+ gdb_assert (frame->prev_p);
- /* 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)
+unwind_stop_reason_to_string (enum unwind_stop_reason reason)
{
switch (reason)
{
}
}
+const char *
+frame_stop_reason_string (struct frame_info *fi)
+{
+ gdb_assert (fi->prev_p);
+ gdb_assert (fi->prev == NULL);
+
+ /* Return the specific string if we have one. */
+ if (fi->stop_string != NULL)
+ return fi->stop_string;
+
+ /* Return the generic string if we have nothing better. */
+ return unwind_stop_reason_to_string (fi->stop_reason);
+}
+
+/* Return the enum symbol name of REASON as a string, to use in debug
+ output. */
+
+static const char *
+frame_stop_reason_symbol_string (enum unwind_stop_reason reason)
+{
+ switch (reason)
+ {
+#define SET(name, description) \
+ case name: return #name;
+#include "unwind_stop_reasons.def"
+#undef SET
+
+ default:
+ internal_error (__FILE__, __LINE__,
+ "Invalid frame stop reason");
+ }
+}
+
/* Clean up after a failed (wrong unwinder) attempt to unwind past
FRAME. */
static void
set_backtrace_cmd (char *args, int from_tty)
{
- help_list (set_backtrace_cmdlist, "set backtrace ", -1, gdb_stdout);
+ help_list (set_backtrace_cmdlist, "set backtrace ", all_commands,
+ gdb_stdout);
}
static void
{
obstack_init (&frame_cache_obstack);
+ frame_stash_create ();
+
observer_attach_target_changed (frame_observer_target_changed);
add_prefix_cmd ("backtrace", class_maintenance, set_backtrace_cmd, _("\
Set an upper bound on the number of backtrace levels."), _("\
Show the upper bound on the number of backtrace levels."), _("\
No more than the specified number of frames can be displayed or examined.\n\
-Zero is unlimited."),
+Literal \"unlimited\" or zero means no limit."),
NULL,
show_backtrace_limit,
&set_backtrace_cmdlist,