diff options
author | Andrew Cagney <cagney@redhat.com> | 2002-06-01 20:44:21 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 2002-06-01 20:44:21 +0000 |
commit | ba944fbb321cf4fac89b7b58de118865680616c4 (patch) | |
tree | c747321ba216ad08fcf65d73bc3181c24c97f23e | |
parent | b057c94ddeb8141fcb1742d9f074260840d902c4 (diff) | |
download | gdb-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/ChangeLog | 20 | ||||
-rw-r--r-- | gdb/blockframe.c | 184 | ||||
-rw-r--r-- | gdb/frame.c | 92 | ||||
-rw-r--r-- | gdb/frame.h | 44 | ||||
-rw-r--r-- | gdb/stack.c | 105 |
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 |