summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cagney <cagney@redhat.com>2002-06-01 20:44:21 +0000
committerAndrew Cagney <cagney@redhat.com>2002-06-01 20:44:21 +0000
commitba944fbb321cf4fac89b7b58de118865680616c4 (patch)
treec747321ba216ad08fcf65d73bc3181c24c97f23e
parentb057c94ddeb8141fcb1742d9f074260840d902c4 (diff)
downloadgdb-ba944fbb321cf4fac89b7b58de118865680616c4.tar.gz
* stack.c (frame_info): Use frame_register_unwind instead of
saved_regs. Mention when the SP is on the stack or in a register. * frame.h (frame_register_unwind_ftype): Define. Document. (struct frame_info): Add field register_unwind and register_unwind_cache. (frame_register_unwind): Declare. (generic_unwind_get_saved_register): Declare. * frame.c (frame_register_unwind): New function. (generic_unwind_get_saved_register): New function. * blockframe.c (generic_call_dummy_register_unwind): New function. (frame_saved_regs_register_unwind): New function. (set_unwind_by_pc): New function. (create_new_frame): New function. (get_prev_frame): New function.
-rw-r--r--gdb/ChangeLog20
-rw-r--r--gdb/blockframe.c184
-rw-r--r--gdb/frame.c92
-rw-r--r--gdb/frame.h44
-rw-r--r--gdb/stack.c105
5 files changed, 414 insertions, 31 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 634eb356021..0c1e7fc0b26 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,23 @@
+2002-06-01 Andrew Cagney <ac131313@redhat.com>
+
+ * stack.c (frame_info): Use frame_register_unwind instead of
+ saved_regs. Mention when the SP is on the stack or in a register.
+
+ * frame.h (frame_register_unwind_ftype): Define. Document.
+ (struct frame_info): Add field register_unwind and
+ register_unwind_cache.
+ (frame_register_unwind): Declare.
+ (generic_unwind_get_saved_register): Declare.
+
+ * frame.c (frame_register_unwind): New function.
+ (generic_unwind_get_saved_register): New function.
+
+ * blockframe.c (generic_call_dummy_register_unwind): New function.
+ (frame_saved_regs_register_unwind): New function.
+ (set_unwind_by_pc): New function.
+ (create_new_frame): New function.
+ (get_prev_frame): New function.
+
2002-05-30 Andrew Cagney <ac131313@redhat.com>
* a29k-share/: Delete directory.
diff --git a/gdb/blockframe.c b/gdb/blockframe.c
index 706d0283cf1..deeda4c7df6 100644
--- a/gdb/blockframe.c
+++ b/gdb/blockframe.c
@@ -34,9 +34,28 @@
#include "inferior.h" /* for read_pc */
#include "annotate.h"
#include "regcache.h"
+#include "gdb_assert.h"
/* Prototypes for exported functions. */
+static void generic_call_dummy_register_unwind (struct frame_info *frame,
+ void **cache,
+ int regnum,
+ int *optimized,
+ enum lval_type *lval,
+ CORE_ADDR *addrp,
+ int *realnum,
+ void *raw_buffer);
+static void frame_saved_regs_register_unwind (struct frame_info *frame,
+ void **cache,
+ int regnum,
+ int *optimized,
+ enum lval_type *lval,
+ CORE_ADDR *addrp,
+ int *realnum,
+ void *buffer);
+
+
void _initialize_blockframe (void);
/* A default FRAME_CHAIN_VALID, in the form that is suitable for most
@@ -208,6 +227,27 @@ set_current_frame (struct frame_info *frame)
current_frame = frame;
}
+
+/* Using the PC, select a mechanism for unwinding a frame returning
+ the previous frame. The register unwind function should, on
+ demand, initialize the ->context object. */
+
+static void
+set_unwind_by_pc (CORE_ADDR pc, CORE_ADDR fp,
+ frame_register_unwind_ftype **unwind)
+{
+ if (!USE_GENERIC_DUMMY_FRAMES)
+ /* Still need to set this to something. The ``info frame'' code
+ calls this function to find out where the saved registers are.
+ Hopefully this is robust enough to stop any core dumps and
+ return vaguely correct values.. */
+ *unwind = frame_saved_regs_register_unwind;
+ else if (PC_IN_CALL_DUMMY (pc, fp, fp))
+ *unwind = generic_call_dummy_register_unwind;
+ else
+ *unwind = frame_saved_regs_register_unwind;
+}
+
/* Create an arbitrary (i.e. address specified by user) or innermost frame.
Always returns a non-NULL value. */
@@ -232,6 +272,9 @@ create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
if (INIT_EXTRA_FRAME_INFO_P ())
INIT_EXTRA_FRAME_INFO (0, fi);
+ /* Select/initialize an unwind function. */
+ set_unwind_by_pc (fi->pc, fi->frame, &fi->register_unwind);
+
return fi;
}
@@ -456,6 +499,12 @@ get_prev_frame (struct frame_info *next_frame)
}
}
+ /* Initialize the code used to unwind the frame PREV based on the PC
+ (and probably other architectural information). The PC lets you
+ check things like the debug info at that point (dwarf2cfi?) and
+ use that to decide how the frame should be unwound. */
+ set_unwind_by_pc (prev->pc, prev->frame, &prev->register_unwind);
+
find_pc_partial_function (prev->pc, &name,
(CORE_ADDR *) NULL, (CORE_ADDR *) NULL);
if (PC_IN_SIGTRAMP (prev->pc, name))
@@ -1269,6 +1318,141 @@ generic_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs,
return;
}
+/* Given a call-dummy dummy-frame, return the registers. Here the
+ register value is taken from the local copy of the register buffer. */
+
+static void
+generic_call_dummy_register_unwind (struct frame_info *frame, void **cache,
+ int regnum, int *optimized,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnum, void *bufferp)
+{
+ gdb_assert (frame != NULL);
+ gdb_assert (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame));
+
+ /* Describe the register's location. Generic dummy frames always
+ have the register value in an ``expression''. */
+ *optimized = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnum = -1;
+
+ /* If needed, find and return the value of the register. */
+ if (bufferp != NULL)
+ {
+ char *registers;
+#if 1
+ /* Get the address of the register buffer that contains all the
+ saved registers for this dummy frame. Cache that address. */
+ registers = (*cache);
+ if (registers == NULL)
+ {
+ registers = generic_find_dummy_frame (frame->pc, frame->frame);
+ (*cache) = registers;
+ }
+#else
+ /* Get the address of the register buffer that contains the
+ saved registers and then extract the value from that. */
+ registers = generic_find_dummy_frame (frame->pc, frame->frame);
+#endif
+ gdb_assert (registers != NULL);
+ /* Return the actual value. */
+ memcpy (bufferp, registers + REGISTER_BYTE (regnum),
+ REGISTER_RAW_SIZE (regnum));
+ }
+}
+
+/* Return the register saved in the simplistic ``saved_regs'' cache.
+ If the value isn't here AND a value is needed, try the next inner
+ most frame. */
+
+static void
+frame_saved_regs_register_unwind (struct frame_info *frame, void **cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *bufferp)
+{
+ /* There is always a frame at this point. And THIS is the frame
+ we're interested in. */
+ gdb_assert (frame != NULL);
+ gdb_assert (!PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame));
+
+ /* Load the saved_regs register cache. */
+ if (frame->saved_regs == NULL)
+ FRAME_INIT_SAVED_REGS (frame);
+
+ if (frame->saved_regs != NULL
+ && frame->saved_regs[regnum] != 0)
+ {
+ if (regnum == SP_REGNUM)
+ {
+ /* SP register treated specially. */
+ *optimizedp = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnump = -1;
+ if (bufferp != NULL)
+ store_address (bufferp, REGISTER_RAW_SIZE (regnum),
+ frame->saved_regs[regnum]);
+ }
+ else
+ {
+ /* Any other register is saved in memory, fetch it but cache
+ a local copy of its value. */
+ *optimizedp = 0;
+ *lvalp = lval_memory;
+ *addrp = frame->saved_regs[regnum];
+ *realnump = -1;
+ if (bufferp != NULL)
+ {
+#if 1
+ /* Save each register value, as it is read in, in a
+ frame based cache. */
+ void **regs = (*cache);
+ if (regs == NULL)
+ {
+ int sizeof_cache = ((NUM_REGS + NUM_PSEUDO_REGS)
+ * sizeof (void *));
+ regs = frame_obstack_alloc (sizeof_cache);
+ memset (regs, 0, sizeof_cache);
+ (*cache) = regs;
+ }
+ if (regs[regnum] == NULL)
+ {
+ regs[regnum]
+ = frame_obstack_alloc (REGISTER_RAW_SIZE (regnum));
+ read_memory (frame->saved_regs[regnum], regs[regnum],
+ REGISTER_RAW_SIZE (regnum));
+ }
+ memcpy (bufferp, regs[regnum], REGISTER_RAW_SIZE (regnum));
+#else
+ /* Read the value in from memory. */
+ read_memory (frame->saved_regs[regnum], bufferp,
+ REGISTER_RAW_SIZE (regnum));
+#endif
+ }
+ }
+ return;
+ }
+
+ /* No luck, assume this and the next frame have the same register
+ value. If a value is needed, pass the request on down the chain;
+ otherwise just return an indication that the value is in the same
+ register as the next frame. */
+ if (bufferp == NULL)
+ {
+ *optimizedp = 0;
+ *lvalp = lval_register;
+ *addrp = 0;
+ *realnump = regnum;
+ }
+ else
+ {
+ frame_register_unwind (frame->next, regnum, optimizedp, lvalp, addrp,
+ realnump, bufferp);
+ }
+}
+
/* Function: get_saved_register
Find register number REGNUM relative to FRAME and put its (raw,
target format) contents in *RAW_BUFFER.
diff --git a/gdb/frame.c b/gdb/frame.c
index 24cd9071437..1fd35c22065 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1,4 +1,4 @@
-/* Cache and manage the values of registers for GDB, the GNU debugger.
+/* Cache and manage frames for GDB, the GNU debugger.
Copyright 1986, 1987, 1989, 1991, 1994, 1995, 1996, 1998, 2000,
2001, 2002 Free Software Foundation, Inc.
@@ -26,6 +26,7 @@
#include "value.h"
#include "inferior.h" /* for inferior_ptid */
#include "regcache.h"
+#include "gdb_assert.h"
/* FIND_SAVED_REGISTER ()
@@ -127,6 +128,95 @@ default_get_saved_register (char *raw_buffer,
*addrp = addr;
}
+void
+frame_register_unwind (struct frame_info *frame, int regnum,
+ int *optimizedp, enum lval_type *lvalp,
+ CORE_ADDR *addrp, int *realnump, void *bufferp)
+{
+ struct frame_unwind_cache *cache;
+
+ /* Require all but BUFFERP to be valid. A NULL BUFFERP indicates
+ that the value proper does not need to be fetched. */
+ gdb_assert (optimizedp != NULL);
+ gdb_assert (lvalp != NULL);
+ gdb_assert (addrp != NULL);
+ gdb_assert (realnump != NULL);
+ /* gdb_assert (bufferp != NULL); */
+
+ /* NOTE: cagney/2002-04-14: It would be nice if, instead of a
+ special case, there was always an inner frame dedicated to the
+ hardware registers. Unfortunatly, there is too much unwind code
+ around that looks up/down the frame chain while making the
+ assumption that each frame level is using the same unwind code. */
+
+ if (frame == NULL)
+ {
+ /* We're in the inner-most frame, get the value direct from the
+ register cache. */
+ *optimizedp = 0;
+ *lvalp = lval_register;
+ *addrp = 0;
+ /* Should this code test ``register_cached (regnum) < 0'' and do
+ something like set realnum to -1 when the register isn't
+ available? */
+ *realnump = regnum;
+ if (bufferp)
+ read_register_gen (regnum, bufferp);
+ return;
+ }
+
+ /* Ask this frame to unwind its register. */
+ frame->register_unwind (frame, &frame->register_unwind_cache, regnum,
+ optimizedp, lvalp, addrp, realnump, bufferp);
+}
+
+
+void
+generic_unwind_get_saved_register (char *raw_buffer,
+ int *optimizedp,
+ CORE_ADDR *addrp,
+ struct frame_info *frame,
+ int regnum,
+ enum lval_type *lvalp)
+{
+ int optimizedx;
+ CORE_ADDR addrx;
+ int realnumx;
+ enum lval_type lvalx;
+
+ if (!target_has_registers)
+ error ("No registers.");
+
+ /* Keep things simple, ensure that all the pointers (except valuep)
+ are non NULL. */
+ if (optimizedp == NULL)
+ optimizedp = &optimizedx;
+ if (lvalp == NULL)
+ lvalp = &lvalx;
+ if (addrp == NULL)
+ addrp = &addrx;
+
+ /* Reached the the bottom (youngest, inner most) of the frame chain
+ (youngest, inner most) frame, go direct to the hardware register
+ cache (do not pass go, do not try to cache the value, ...). The
+ unwound value would have been cached in frame->next but that
+ doesn't exist. This doesn't matter as the hardware register
+ cache is stopping any unnecessary accesses to the target. */
+
+ /* NOTE: cagney/2002-04-14: It would be nice if, instead of a
+ special case, there was always an inner frame dedicated to the
+ hardware registers. Unfortunatly, there is too much unwind code
+ around that looks up/down the frame chain while making the
+ assumption that each frame level is using the same unwind code. */
+
+ if (frame == NULL)
+ frame_register_unwind (NULL, regnum, optimizedp, lvalp, addrp, &realnumx,
+ raw_buffer);
+ else
+ frame_register_unwind (frame->next, regnum, optimizedp, lvalp, addrp,
+ &realnumx, raw_buffer);
+}
+
#if !defined (GET_SAVED_REGISTER)
#define GET_SAVED_REGISTER(raw_buffer, optimized, addrp, frame, regnum, lval) \
default_get_saved_register(raw_buffer, optimized, addrp, frame, regnum, lval)
diff --git a/gdb/frame.h b/gdb/frame.h
index f0631b01555..cdbcd481764 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -23,6 +23,29 @@
#if !defined (FRAME_H)
#define FRAME_H 1
+/* Return the location (and possibly value) of REGNUM for the previous
+ (older, up) frame. All parameters except VALUEP can be assumed to
+ be non NULL. When VALUEP is NULL, just the location of the
+ register should be returned.
+
+ UNWIND_CACHE is provided as mechanism for implementing a per-frame
+ local cache. It's initial value being NULL. Memory for that cache
+ should be allocated using frame_obstack_alloc().
+
+ Register window architectures (eg SPARC) should note that REGNUM
+ identifies the register for the previous frame. For instance, a
+ request for the value of "o1" for the previous frame would be found
+ in the register "i1" in this FRAME. */
+
+typedef void (frame_register_unwind_ftype) (struct frame_info *frame,
+ void **unwind_cache,
+ int regnum,
+ int *optimized,
+ enum lval_type *lvalp,
+ CORE_ADDR *addrp,
+ int *realnump,
+ void *valuep);
+
/* Describe the saved registers of a frame. */
#if defined (EXTRA_FRAME_INFO) || defined (FRAME_FIND_SAVED_REGS)
@@ -112,6 +135,11 @@ struct frame_info
related unwind data. */
struct unwind_contect *context;
+ /* See description above. Return the register value for the
+ previous frame. */
+ frame_register_unwind_ftype *register_unwind;
+ void *register_unwind_cache;
+
/* Pointers to the next (down, inner) and previous (up, outer)
frame_info's in the frame cache. */
struct frame_info *next; /* down, inner */
@@ -278,6 +306,22 @@ extern void generic_get_saved_register (char *, int *, CORE_ADDR *,
struct frame_info *, int,
enum lval_type *);
+extern void generic_unwind_get_saved_register (char *raw_buffer,
+ int *optimized,
+ CORE_ADDR * addrp,
+ struct frame_info *frame,
+ int regnum,
+ enum lval_type *lval);
+
+/* Unwind the stack frame so that the value of REGNUM, in the previous
+ frame is returned. If VALUEP is NULL, don't fetch/compute the
+ value. Instead just return the location of the value. */
+
+extern void frame_register_unwind (struct frame_info *frame, int regnum,
+ int *optimizedp, enum lval_type *lvalp,
+ CORE_ADDR *addrp, int *realnump,
+ void *valuep);
+
extern void generic_save_call_dummy_addr (CORE_ADDR lo, CORE_ADDR hi);
extern void get_saved_register (char *raw_buffer, int *optimized,
diff --git a/gdb/stack.c b/gdb/stack.c
index 2dab0deb016..509883b4070 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -929,39 +929,84 @@ frame_info (char *addr_exp, int from_tty)
}
}
- FRAME_INIT_SAVED_REGS (fi);
- if (fi->saved_regs != NULL)
- {
- /* The sp is special; what's returned isn't the save address, but
- actually the value of the previous frame's sp. */
- printf_filtered (" Previous frame's sp is ");
- print_address_numeric (fi->saved_regs[SP_REGNUM], 1, gdb_stdout);
- printf_filtered ("\n");
- count = 0;
- numregs = NUM_REGS + NUM_PSEUDO_REGS;
- for (i = 0; i < numregs; i++)
- if (fi->saved_regs[i] && i != SP_REGNUM)
+ if (fi->saved_regs == NULL)
+ FRAME_INIT_SAVED_REGS (fi);
+ /* Print as much information as possible on the location of all the
+ registers. */
+ {
+ enum lval_type lval;
+ int optimized;
+ CORE_ADDR addr;
+ int realnum;
+ int count;
+ int i;
+ int need_nl = 1;
+
+ /* The sp is special; what's displayed isn't the save address, but
+ the value of the previous frame's sp. This is a legacy thing,
+ at one stage the frame cached the previous frame's SP instead
+ of its address, hence it was easiest to just display the cached
+ value. */
+ if (SP_REGNUM >= 0)
+ {
+ /* Find out the location of the saved stack pointer with out
+ actually evaluating it. */
+ frame_register_unwind (fi, SP_REGNUM, &optimized, &lval, &addr,
+ &realnum, NULL);
+ if (!optimized && lval == not_lval)
{
- if (count == 0)
- puts_filtered (" Saved registers:\n ");
- else
- puts_filtered (",");
- wrap_here (" ");
- printf_filtered (" %s at ", REGISTER_NAME (i));
- print_address_numeric (fi->saved_regs[i], 1, gdb_stdout);
- count++;
+ void *value = alloca (MAX_REGISTER_RAW_SIZE);
+ CORE_ADDR sp;
+ frame_register_unwind (fi, SP_REGNUM, &optimized, &lval, &addr,
+ &realnum, value);
+ sp = extract_address (value, REGISTER_RAW_SIZE (SP_REGNUM));
+ printf_filtered (" Previous frame's sp is ");
+ print_address_numeric (sp, 1, gdb_stdout);
+ printf_filtered ("\n");
+ need_nl = 0;
}
- if (count)
- puts_filtered ("\n");
- }
- else
- {
- /* We could get some information about saved registers by
- calling get_saved_register on each register. Which info goes
- with which frame is necessarily lost, however, and I suspect
- that the users don't care whether they get the info. */
+ else if (!optimized && lval == lval_memory)
+ {
+ printf_filtered (" Previous frame's sp at ");
+ print_address_numeric (addr, 1, gdb_stdout);
+ printf_filtered ("\n");
+ need_nl = 0;
+ }
+ else if (!optimized && lval == lval_register)
+ {
+ printf_filtered (" Previous frame's sp in %s\n",
+ REGISTER_NAME (realnum));
+ need_nl = 0;
+ }
+ /* else keep quiet. */
+ }
+
+ count = 0;
+ numregs = NUM_REGS + NUM_PSEUDO_REGS;
+ for (i = 0; i < numregs; i++)
+ if (i != SP_REGNUM)
+ {
+ /* Find out the location of the saved register without
+ fetching the corresponding value. */
+ frame_register_unwind (fi, i, &optimized, &lval, &addr, &realnum,
+ NULL);
+ /* For moment, only display registers that were saved on the
+ stack. */
+ if (!optimized && lval == lval_memory)
+ {
+ if (count == 0)
+ puts_filtered (" Saved registers:\n ");
+ else
+ puts_filtered (",");
+ wrap_here (" ");
+ printf_filtered (" %s at ", REGISTER_NAME (i));
+ print_address_numeric (addr, 1, gdb_stdout);
+ count++;
+ }
+ }
+ if (count || need_nl)
puts_filtered ("\n");
- }
+ }
}
#if 0