From eb4f72c5f97465009446c097eef2204c6e8573e2 Mon Sep 17 00:00:00 2001 From: Andrew Cagney Date: Tue, 21 Jan 2003 19:32:42 +0000 Subject: [PATCH] 2003-01-21 Andrew Cagney * frame.h (FRAME_OBSTACK_ZALLOC): Define. * blockframe.c (backtrace_below_main): Move to "frame.c". (frame_chain_valid): Delete check for backtrace_below_main. (_initialize_blockframe): Delete initialization, move ``set backtrace-below-main'' command to "frame.c". (do_flush_frames_sfunc): Delete function. * frame.c: Include "command.h" and "gdbcmd.h". (frame_type_from_pc): New function. (create_new_frame): Use frame_type_from_pc. (legacy_get_prev_frame): New function. (get_prev_frame): Rewrite. When an old style frame, call legacy_get_prev_frame. Otherwize, unwind the PC first. (_initialize_frame): Add ``set backtrace-below-main'' command. * Makefile.in (frame.o): Update dependencies. --- gdb/ChangeLog | 17 +++ gdb/Makefile.in | 2 +- gdb/blockframe.c | 48 --------- gdb/frame.c | 268 +++++++++++++++++++++++++++++++++++++---------- gdb/frame.h | 1 + 5 files changed, 230 insertions(+), 106 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2b832ce500..f2df4caa3f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2003-01-21 Andrew Cagney + + * frame.h (FRAME_OBSTACK_ZALLOC): Define. + * blockframe.c (backtrace_below_main): Move to "frame.c". + (frame_chain_valid): Delete check for backtrace_below_main. + (_initialize_blockframe): Delete initialization, move ``set + backtrace-below-main'' command to "frame.c". + (do_flush_frames_sfunc): Delete function. + * frame.c: Include "command.h" and "gdbcmd.h". + (frame_type_from_pc): New function. + (create_new_frame): Use frame_type_from_pc. + (legacy_get_prev_frame): New function. + (get_prev_frame): Rewrite. When an old style frame, call + legacy_get_prev_frame. Otherwize, unwind the PC first. + (_initialize_frame): Add ``set backtrace-below-main'' command. + * Makefile.in (frame.o): Update dependencies. + 2003-01-19 Andrew Cagney * config/pa/tm-hppa.h (DEPRECATED_DO_REGISTERS_INFO): Rename diff --git a/gdb/Makefile.in b/gdb/Makefile.in index d23d48c156..faa6a91fb8 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1690,7 +1690,7 @@ fork-child.o: fork-child.c $(defs_h) $(gdb_string_h) $(frame_h) \ frame.o: frame.c $(defs_h) $(frame_h) $(target_h) $(value_h) $(inferior_h) \ $(regcache_h) $(gdb_assert_h) $(gdb_string_h) $(builtin_regs_h) \ $(gdb_obstack_h) $(dummy_frame_h) $(gdbcore_h) $(annotate_h) \ - $(language_h) $(frame_unwind_h) + $(language_h) $(frame_unwind_h) $(command_h) $(gdbcmd_h) frame-unwind.o: frame-unwind.c $(defs_h) $(frame_h) $(frame_unwind_h) \ $(gdb_assert_h) $(dummy_frame_h) $(legacy_frame_h) frv-tdep.o: frv-tdep.c $(defs_h) $(inferior_h) $(symfile_h) $(gdbcore_h) \ diff --git a/gdb/blockframe.c b/gdb/blockframe.c index 35fc79860b..df94e85913 100644 --- a/gdb/blockframe.c +++ b/gdb/blockframe.c @@ -39,10 +39,6 @@ #include "command.h" #include "gdbcmd.h" -/* Flag to indicate whether backtraces should stop at main. */ - -static int backtrace_below_main; - /* Prototypes for exported functions. */ void _initialize_blockframe (void); @@ -697,53 +693,9 @@ frame_chain_valid (CORE_ADDR fp, struct frame_info *fi) if (inside_entry_file (frame_pc_unwind (fi))) return 0; - /* If we want backtraces to stop at main, and we're inside main, then it - isn't valid. */ - if (!backtrace_below_main && inside_main_func (get_frame_pc (fi))) - return 0; - /* If the architecture has a custom FRAME_CHAIN_VALID, call it now. */ if (FRAME_CHAIN_VALID_P ()) return FRAME_CHAIN_VALID (fp, fi); return 1; } - -void -do_flush_frames_sfunc (char *args, int from_tty, struct cmd_list_element *c) -{ - int saved_level; - struct frame_info *cur_frame; - - if (! target_has_stack) - return; - - saved_level = frame_relative_level (get_selected_frame ()); - - flush_cached_frames (); - - cur_frame = find_relative_frame (get_current_frame (), &saved_level); - select_frame (cur_frame); - - /* If we were below main and backtrace-below-main was turned off, - SAVED_LEVEL will be non-zero. CUR_FRAME will point to main. - Accept this but print the new frame. */ - if (saved_level != 0) - print_stack_frame (get_selected_frame (), -1, 0); -} - -void -_initialize_blockframe (void) -{ - add_setshow_boolean_cmd ("backtrace-below-main", class_obscure, - &backtrace_below_main, - "Set whether backtraces should continue past \"main\".\n" - "Normally the caller of \"main\" is not of interest, so GDB will terminate\n" - "the backtrace at \"main\". Set this variable if you need to see the rest\n" - "of the stack trace.", - "Show whether backtraces should continue past \"main\".\n" - "Normally the caller of \"main\" is not of interest, so GDB will terminate\n" - "the backtrace at \"main\". Set this variable if you need to see the rest\n" - "of the stack trace.", - do_flush_frames_sfunc, NULL, &setlist, &showlist); -} diff --git a/gdb/frame.c b/gdb/frame.c index b00eebc2ae..d252d8a7df 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -35,6 +35,12 @@ #include "annotate.h" #include "language.h" #include "frame-unwind.h" +#include "command.h" +#include "gdbcmd.h" + +/* Flag to indicate whether backtraces should stop at main. */ + +static int backtrace_below_main; /* Return a frame uniq ID that can be used to, later, re-find the frame. */ @@ -843,6 +849,29 @@ deprecated_generic_get_saved_register (char *raw_buffer, int *optimized, deprecated_read_register_gen (regnum, raw_buffer); } +/* Determine the frame's type based on its PC. */ + +static enum frame_type +frame_type_from_pc (CORE_ADDR pc) +{ + /* FIXME: cagney/2002-11-24: Can't yet directly call + pc_in_dummy_frame() as some architectures don't set + PC_IN_CALL_DUMMY() to generic_pc_in_call_dummy() (remember the + latter is implemented by simply calling pc_in_dummy_frame). */ + if (DEPRECATED_USE_GENERIC_DUMMY_FRAMES + && DEPRECATED_PC_IN_CALL_DUMMY (pc, 0, 0)) + return DUMMY_FRAME; + else + { + char *name; + find_pc_partial_function (pc, &name, NULL, NULL); + if (PC_IN_SIGTRAMP (pc, name)) + return SIGTRAMP_FRAME; + else + return NORMAL_FRAME; + } +} + /* Create an arbitrary (i.e. address specified by user) or innermost frame. Always returns a non-NULL value. */ @@ -850,36 +879,12 @@ struct frame_info * create_new_frame (CORE_ADDR addr, CORE_ADDR pc) { struct frame_info *fi; - enum frame_type type; fi = frame_obstack_zalloc (sizeof (struct frame_info)); fi->frame = addr; fi->pc = pc; - /* NOTE: cagney/2002-11-18: The code segments, found in - create_new_frame and get_prev_frame(), that initializes the - frames type is subtly different. The latter only updates ->type - when it encounters a SIGTRAMP_FRAME or DUMMY_FRAME. This stops - get_prev_frame() overriding the frame's type when the INIT code - has previously set it. This is really somewhat bogus. The - initialization, as seen in create_new_frame(), should occur - before the INIT function has been called. */ - if (DEPRECATED_USE_GENERIC_DUMMY_FRAMES - && (DEPRECATED_PC_IN_CALL_DUMMY_P () - ? DEPRECATED_PC_IN_CALL_DUMMY (pc, 0, 0) - : pc_in_dummy_frame (pc))) - /* NOTE: cagney/2002-11-11: Does this even occure? */ - type = DUMMY_FRAME; - else - { - char *name; - find_pc_partial_function (pc, &name, NULL, NULL); - if (PC_IN_SIGTRAMP (fi->pc, name)) - type = SIGTRAMP_FRAME; - else - type = NORMAL_FRAME; - } - fi->type = type; + fi->type = frame_type_from_pc (pc); if (INIT_EXTRA_FRAME_INFO_P ()) INIT_EXTRA_FRAME_INFO (0, fi); @@ -927,43 +932,19 @@ reinit_frame_cache (void) } } -/* Return a structure containing various interesting information - about the frame that called NEXT_FRAME. Returns NULL - if there is no such frame. */ +/* Create the previous frame using the deprecated methods + INIT_EXTRA_INFO, INIT_FRAME_PC and INIT_FRAME_PC_FIRST. */ -struct frame_info * -get_prev_frame (struct frame_info *next_frame) +static struct frame_info * +legacy_get_prev_frame (struct frame_info *next_frame) { CORE_ADDR address = 0; struct frame_info *prev; int fromleaf; - /* Return the inner-most frame, when the caller passes in NULL. */ - /* NOTE: cagney/2002-11-09: Not sure how this would happen. The - caller should have previously obtained a valid frame using - get_selected_frame() and then called this code - only possibility - I can think of is code behaving badly. */ - if (next_frame == NULL) - { - /* NOTE: cagney/2002-11-09: There was a code segment here that - would error out when CURRENT_FRAME was NULL. The comment - that went with it made the claim ... - - ``This screws value_of_variable, which just wants a nice - clean NULL return from block_innermost_frame if there are no - frames. I don't think I've ever seen this message happen - otherwise. And returning NULL here is a perfectly legitimate - thing to do.'' - - Per the above, this code shouldn't even be called with a NULL - NEXT_FRAME. */ - return current_frame; - } - - /* Only try to do the unwind once. */ - if (next_frame->prev_p) - return next_frame->prev; - next_frame->prev_p = 1; + /* This code only works on normal frames. A sentinel frame, where + the level is -1, should never reach this code. */ + gdb_assert (next_frame->level >= 0); /* On some machines it is possible to call a function without setting up a stack frame for it. On these machines, we @@ -974,7 +955,7 @@ get_prev_frame (struct frame_info *next_frame) /* Still don't want to worry about this except on the innermost frame. This macro will set FROMLEAF if NEXT_FRAME is a frameless function invocation. */ - if (next_frame->next == NULL) + if (next_frame->level == 0) /* FIXME: 2002-11-09: Frameless functions can occure anywhere in the frame chain, not just the inner most frame! The generic, per-architecture, frame code should handle this and the below @@ -1166,6 +1147,162 @@ get_prev_frame (struct frame_info *next_frame) return prev; } +/* Return a structure containing various interesting information + about the frame that called NEXT_FRAME. Returns NULL + if there is no such frame. */ + +struct frame_info * +get_prev_frame (struct frame_info *next_frame) +{ + struct frame_info *prev_frame; + + /* Return the inner-most frame, when the caller passes in NULL. */ + /* NOTE: cagney/2002-11-09: Not sure how this would happen. The + caller should have previously obtained a valid frame using + get_selected_frame() and then called this code - only possibility + I can think of is code behaving badly. + + NOTE: cagney/2003-01-10: Talk about code behaving badly. Check + block_innermost_frame(). It does the sequence: frame = NULL; + while (1) { frame = get_prev_frame (frame); .... }. Ulgh! Why + it couldn't be written better, I don't know. + + NOTE: cagney/2003-01-11: I suspect what is happening is + block_innermost_frame() is, when the target has no state + (registers, memory, ...), still calling this function. The + assumption being that this function will return NULL indicating + that a frame isn't possible, rather than checking that the target + has state and then calling get_current_frame() and + get_prev_frame(). This is a guess mind. */ + if (next_frame == NULL) + { + /* NOTE: cagney/2002-11-09: There was a code segment here that + would error out when CURRENT_FRAME was NULL. The comment + that went with it made the claim ... + + ``This screws value_of_variable, which just wants a nice + clean NULL return from block_innermost_frame if there are no + frames. I don't think I've ever seen this message happen + otherwise. And returning NULL here is a perfectly legitimate + thing to do.'' + + Per the above, this code shouldn't even be called with a NULL + NEXT_FRAME. */ + return current_frame; + } + + /* There is always a frame. If this assertion fails, suspect that + something should be calling get_selected_frame() or + get_current_frame(). */ + gdb_assert (next_frame != NULL); + + if (next_frame->level >= 0 + && !backtrace_below_main + && inside_main_func (get_frame_pc (next_frame))) + /* Don't unwind past main(), bug always unwind the sentinel frame. + Note, this is done _before_ the frame has been marked as + previously unwound. That way if the user later decides to + allow unwinds past main(), that just happens. */ + return NULL; + + /* Only try to do the unwind once. */ + if (next_frame->prev_p) + return next_frame->prev; + next_frame->prev_p = 1; + + /* If we're inside the entry file, it isn't valid. */ + /* NOTE: drow/2002-12-25: should there be a way to disable this + check? It assumes a single small entry file, and the way some + debug readers (e.g. dbxread) figure out which object is the + entry file is somewhat hokey. */ + /* NOTE: cagney/2003-01-10: If there is a way of disabling this test + then it should probably be moved to before the ->prev_p test, + above. */ + if (inside_entry_file (get_frame_pc (next_frame))) + return NULL; + + /* If any of the old frame initialization methods are around, use + the legacy get_prev_frame method. Just don't try to unwind a + sentinel frame using that method - it doesn't work. All sentinal + frames use the new unwind code. */ + if ((DEPRECATED_INIT_FRAME_PC_P () + || DEPRECATED_INIT_FRAME_PC_FIRST_P () + || INIT_EXTRA_FRAME_INFO_P ()) + && next_frame->level >= 0) + return legacy_get_prev_frame (next_frame); + + /* Allocate the new frame but do not wire it in to the frame chain. + Some (bad) code in INIT_FRAME_EXTRA_INFO tries to look along + frame->next to pull some fancy tricks (of course such code is, by + definition, recursive). Try to prevent it. + + There is no reason to worry about memory leaks, should the + remainder of the function fail. The allocated memory will be + quickly reclaimed when the frame cache is flushed, and the `we've + been here before' check above will stop repeated memory + allocation calls. */ + prev_frame = FRAME_OBSTACK_ZALLOC (struct frame_info); + prev_frame->level = next_frame->level + 1; + + /* Try to unwind the PC. If that doesn't work, assume we've reached + the oldest frame and simply return. Is there a better sentinal + value? The unwound PC value is then used to initialize the new + previous frame's type. + + Note that the pc-unwind is intentionally performed before the + frame chain. This is ok since, for old targets, both + frame_pc_unwind (nee, FRAME_SAVED_PC) and FRAME_CHAIN()) assume + NEXT_FRAME's data structures have already been initialized (using + INIT_EXTRA_FRAME_INFO) and hence the call order doesn't matter. + + By unwinding the PC first, it becomes possible to, in the case of + a dummy frame, avoid also unwinding the frame ID. This is + because (well ignoring the PPC) a dummy frame can be located + using NEXT_FRAME's frame ID. */ + + prev_frame->pc = frame_pc_unwind (next_frame); + if (prev_frame->pc == 0) + /* The allocated PREV_FRAME will be reclaimed when the frame + obstack is next purged. */ + return NULL; + prev_frame->type = frame_type_from_pc (prev_frame->pc); + + /* Set the unwind functions based on that identified PC. */ + prev_frame->unwind = frame_unwind_find_by_pc (current_gdbarch, + prev_frame->pc); + + /* FIXME: cagney/2003-01-13: A dummy frame doesn't need to unwind + the frame ID because the frame ID comes from the previous frame. + The other frames do though. True? */ + { + /* FIXME: cagney/2002-12-18: Instead of this hack, should just + save the frame ID directly. */ + struct frame_id id = frame_id_unwind (next_frame); + if (!frame_id_p (id)) + return NULL; + prev_frame->frame = id.base; + } + + /* Link it in. */ + next_frame->prev = prev_frame; + prev_frame->next = next_frame; + + /* FIXME: cagney/2002-01-19: This call will go away. Instead of + initializing extra info, all frames will use the frame_cache + (passed to the unwind functions) to store additional frame info. + Unfortunatly legacy targets can't use legacy_get_prev_frame() to + unwind the sentinel frame and, consequently, are forced to take + this code path and rely on the below call to INIT_EXTR_FRAME_INFO + to initialize the inner-most frame. */ + if (INIT_EXTRA_FRAME_INFO_P ()) + { + gdb_assert (prev_frame->level == 0); + INIT_EXTRA_FRAME_INFO (0, prev_frame); + } + + return prev_frame; +} + CORE_ADDR get_frame_pc (struct frame_info *frame) { @@ -1360,4 +1497,21 @@ void _initialize_frame (void) { obstack_init (&frame_cache_obstack); + + /* FIXME: cagney/2003-01-19: This command needs a rename. Suggest + `set backtrace {past,beyond,...}-main'. Also suggest adding `set + backtrace ...-start' to control backtraces past start. The + problem with `below' is that it stops the `up' command. */ + + add_setshow_boolean_cmd ("backtrace-below-main", class_obscure, + &backtrace_below_main, "\ +Set whether backtraces should continue past \"main\".\n\ +Normally the caller of \"main\" is not of interest, so GDB will terminate\n\ +the backtrace at \"main\". Set this variable if you need to see the rest\n\ +of the stack trace.", "\ +Show whether backtraces should continue past \"main\".\n\ +Normally the caller of \"main\" is not of interest, so GDB will terminate\n\ +the backtrace at \"main\". Set this variable if you need to see the rest\n\ +of the stack trace.", + NULL, NULL, &setlist, &showlist); } diff --git a/gdb/frame.h b/gdb/frame.h index 64f932a299..8e0da6df36 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -445,6 +445,7 @@ enum print_what allocate memory using this method. */ extern void *frame_obstack_zalloc (unsigned long size); +#define FRAME_OBSTACK_ZALLOC(TYPE) ((TYPE *) frame_obstack_zalloc (sizeof (TYPE))) /* If FRAME_CHAIN_VALID returns zero it means that the given frame is the outermost one and has no caller. */