2008-11-18 Doug Evans * dummy-frame.c (dummy_frame): Replace regcache member with caller_state. (dummy_frame_push): Replace caller_regcache arg with caller_state. Return pointer to created dummy frame. All callers updated. (remove_dummy_frame,do_dummy_frame_cleanup,pop_dummy_frame_from_ptr, lookup_dummy,lookup_dummy_id, pop_dummy_frame,dummy_frame_discard, do_pop_dummy_frame_cleanup,make_cleanup_pop_dummy_frame): New functions. (dummy_frame_pop): Rewrite. Verify requested frame is in the dummy frame stack. Restore program state. (cleanup_dummy_frames): Rewrite. (dummy_frame_sniffer): Update. * dummy-frame.h (dummy_frame_push): Update prototype. (dummy_frame_discard,make_cleanup_pop_dummy_frame): Declare. * frame.c (frame_pop): dummy_frame_pop now does all the work for DUMMY_FRAMEs. * infcall.c (call_function_by_hand): Replace caller_regcache, caller_regcache_cleanup with caller_state, caller_state_cleanup. New locals dummy_frame, dummy_frame_cleanup, this_thread. Ensure dummy frame is popped/discarded for all possible exit paths. Detect program stopping in a different thread. * inferior.h (inferior_program_state): Declare (opaque type). (save_inferior_program_state,restore_inferior_program_state, make_cleanup_restore_inferior_program_state, discard_inferior_program_state, get_inferior_program_state_regcache): Declare. (save_inferior_status): Update prototype. * infrun.c: #include "dummy-frame.h" (normal_stop): When stopped for the completion of an inferior function call, verify the expected stack frame kind and use dummy_frame_pop. (inferior_program_state): New struct. (save_inferior_program_state,restore_inferior_program_state, do_restore_inferior_program_state_cleanup, make_cleanup_restore_inferior_program_state, discard_inferior_program_state, get_inferior_program_state_regcache): New functions. (inferior_status): Remove members stop_signal, stop_pc, registers, restore_stack_info. (save_inferior_status): Remove arg restore_stack_info. All callers updated. Remove saving of state now saved by save_inferior_program_state. (restore_inferior_status): Remove restoration of state now done by restore_inferior_program_state. (discard_inferior_status): Remove freeing of registers, now done by discard_inferior_program_state. * gdb.base/call-signal-resume.exp: New file. * gdb.base/call-signals.c: New file. * gdb.base/unwindonsignal.exp: New file. Index: dummy-frame.c =================================================================== RCS file: /cvs/src/src/gdb/dummy-frame.c,v retrieving revision 1.53 diff -u -p -r1.53 dummy-frame.c --- dummy-frame.c 30 Oct 2008 20:35:30 -0000 1.53 +++ dummy-frame.c 20 Nov 2008 07:09:58 -0000 @@ -42,8 +42,8 @@ struct dummy_frame /* This frame's ID. Must match the value returned by gdbarch_dummy_id. */ struct frame_id id; - /* The caller's regcache. */ - struct regcache *regcache; + /* The caller's state prior to the call. */ + struct inferior_program_state *caller_state; }; static struct dummy_frame *dummy_frame_stack = NULL; @@ -81,61 +81,181 @@ deprecated_pc_in_call_dummy (CORE_ADDR p return 0; } -/* Push the caller's state, along with the dummy frame info, onto a +/* Push the caller's state, along with the dummy frame info, onto the dummy-frame stack. */ -void -dummy_frame_push (struct regcache *caller_regcache, +struct dummy_frame * +dummy_frame_push (struct inferior_program_state *caller_state, const struct frame_id *dummy_id) { struct dummy_frame *dummy_frame; dummy_frame = XZALLOC (struct dummy_frame); - dummy_frame->regcache = caller_regcache; + dummy_frame->caller_state = caller_state; dummy_frame->id = (*dummy_id); dummy_frame->next = dummy_frame_stack; dummy_frame_stack = dummy_frame; + + return dummy_frame; } -/* Pop the dummy frame with ID dummy_id from the dummy-frame stack. */ +/* Remove *DUMMY_PTR from the dummy frame stack. */ -void -dummy_frame_pop (struct frame_id dummy_id) +static void +remove_dummy_frame (struct dummy_frame **dummy_ptr) +{ + struct dummy_frame *dummy = *dummy_ptr; + + *dummy_ptr = dummy->next; + discard_inferior_program_state (dummy->caller_state); + xfree (dummy); +} + +/* Cleanup handler for dummy_frame_pop. */ + +static void +do_dummy_frame_cleanup (void *arg) { - struct dummy_frame **dummy_ptr; + struct dummy_frame **dummy_ptr = arg; + + remove_dummy_frame (dummy_ptr); +} + +/* Pop *DUMMY_PTR, restoring program state to that before the + frame was created. */ + +static void +pop_dummy_frame_from_ptr (struct dummy_frame **dummy_ptr) +{ + struct cleanup *cleanups; + struct dummy_frame *dummy; + + cleanups = make_cleanup (do_dummy_frame_cleanup, dummy_ptr); - for (dummy_ptr = &dummy_frame_stack; - (*dummy_ptr) != NULL; - dummy_ptr = &(*dummy_ptr)->next) + restore_inferior_program_state ((*dummy_ptr)->caller_state); + + /* restore_inferior_status frees inf_status, + all that remains is to pop *dummy_ptr */ + discard_cleanups (cleanups); + dummy = *dummy_ptr; + *dummy_ptr = dummy->next; + xfree (dummy); + + /* We've made right mess of GDB's local state, just discard + everything. */ + reinit_frame_cache (); +} + +/* Return a pointer to DUMMY's entry in the dummy frame stack. + Returns NULL if DUMMY is not present. */ + +static struct dummy_frame ** +lookup_dummy (struct dummy_frame *dummy) +{ + struct dummy_frame **dp; + + for (dp = &dummy_frame_stack; *dp != NULL; dp = &(*dp)->next) { - struct dummy_frame *dummy = *dummy_ptr; - if (frame_id_eq (dummy->id, dummy_id)) - { - *dummy_ptr = dummy->next; - regcache_xfree (dummy->regcache); - xfree (dummy); - break; - } + if (*dp == dummy) + return dp; } + + return NULL; } -/* There may be stale dummy frames, perhaps left over from when a longjump took us - out of a function that was called by the debugger. Clean them up at least once - whenever we start a new inferior. */ +/* Look up DUMMY_ID. + Return NULL if not found. */ -static void -cleanup_dummy_frames (struct target_ops *target, int from_tty) +static struct dummy_frame ** +lookup_dummy_id (struct frame_id dummy_id) { - struct dummy_frame *dummy, *next; + struct dummy_frame **dp; - for (dummy = dummy_frame_stack; dummy; dummy = next) + for (dp = &dummy_frame_stack; *dp != NULL; dp = &(*dp)->next) { - next = dummy->next; - regcache_xfree (dummy->regcache); - xfree (dummy); + if (frame_id_eq ((*dp)->id, dummy_id)) + return dp; } - dummy_frame_stack = NULL; + return NULL; +} + +/* Pop DUMMY, restore inferior state. + On return reinit_frame_cache has been called. + If the frame isn't found, flag an internal error. */ + +static void +pop_dummy_frame (struct dummy_frame *dummy) +{ + struct dummy_frame **dp; + + dp = lookup_dummy (dummy); + gdb_assert (dp != NULL); + + pop_dummy_frame_from_ptr (dp); +} + +/* Pop the dummy frame DUMMY_ID, restoring program state to that before the + frame was created. + On return reinit_frame_cache has been called. + If the frame isn't found, flag an internal error. + + NOTE: This can only pop the one frame, even if it is in the middle of the + stack, because the other frames may be for different threads, and there's + currently no way to tell which stack frame is for which thread. */ + +void +dummy_frame_pop (struct frame_id dummy_id) +{ + struct dummy_frame **dp; + + dp = lookup_dummy_id (dummy_id); + gdb_assert (dp != NULL); + + pop_dummy_frame_from_ptr (dp); +} + +/* Discard DUMMY and remove it from the dummy frame stack. + If the frame isn't found, flag an internal error. */ + +void +dummy_frame_discard (struct dummy_frame *dummy) +{ + struct dummy_frame **dp; + + dp = lookup_dummy (dummy); + gdb_assert (dp != NULL); + + remove_dummy_frame (dp); +} + +/* Utility for make_cleanup_pop_dummy_frame. */ + +static void +do_pop_dummy_frame_cleanup (void *dummy) +{ + pop_dummy_frame (dummy); +} + +/* Schedule a cleanup to pop DUMMY_FRAME and restore inferior state. */ + +struct cleanup * +make_cleanup_pop_dummy_frame (struct dummy_frame *dummy) +{ + return make_cleanup (do_pop_dummy_frame_cleanup, dummy); +} + +/* There may be stale dummy frames, perhaps left over from when a longjump took + us out of a function that was called by the debugger. Clean them up at + least once whenever we start a new inferior. */ + +static void +cleanup_dummy_frames (struct target_ops *target, int from_tty) +{ + while (dummy_frame_stack != NULL) + { + remove_dummy_frame (&dummy_frame_stack); + } } /* Return the dummy frame cache, it contains both the ID, and a @@ -178,7 +298,7 @@ dummy_frame_sniffer (const struct frame_ { struct dummy_frame_cache *cache; cache = FRAME_OBSTACK_ZALLOC (struct dummy_frame_cache); - cache->prev_regcache = dummyframe->regcache; + cache->prev_regcache = get_inferior_program_state_regcache (dummyframe->caller_state); cache->this_id = this_id; (*this_prologue_cache) = cache; return 1; @@ -215,7 +335,7 @@ dummy_frame_prev_register (struct frame_ return reg_val; } -/* Assuming that THIS frame is a dummy, return the ID of THIS frame. That ID is +/* Assuming that THIS_FRAME is a dummy, return its ID. That ID is determined by examining the NEXT frame's unwound registers using the method dummy_id(). As a side effect, THIS dummy frame's dummy cache is located and and saved in THIS_PROLOGUE_CACHE. */ Index: dummy-frame.h =================================================================== RCS file: /cvs/src/src/gdb/dummy-frame.h,v retrieving revision 1.23 diff -u -p -r1.23 dummy-frame.h --- dummy-frame.h 8 Sep 2008 15:23:12 -0000 1.23 +++ dummy-frame.h 20 Nov 2008 07:09:58 -0000 @@ -23,8 +23,10 @@ #include "frame.h" struct frame_info; -struct regcache; +struct inferior_program_state; struct frame_unwind; +struct dummy_frame; +struct cleanup; /* Push the information needed to identify, and unwind from, a dummy frame onto the dummy frame stack. */ @@ -39,11 +41,29 @@ struct frame_unwind; be expanded so that it knowns the lower/upper extent of the dummy frame's code. */ -extern void dummy_frame_push (struct regcache *regcache, - const struct frame_id *dummy_id); +extern struct dummy_frame *dummy_frame_push (struct inferior_program_state *caller_state, + const struct frame_id *dummy_id); + +/* Pop the dummy frame DUMMY_ID, restoring program state to that before the + frame was created. + On return reinit_frame_cache has been called. + If the frame isn't found, flag an internal error. + + NOTE: This can only pop the one frame, even if it is in the middle of the + stack, because the other frames may be for different threads, and there's + currently no way to tell which stack frame is for which thread. */ extern void dummy_frame_pop (struct frame_id dummy_id); +/* Discard DUMMY and remove it from the dummy frame stack. + If the frame isn't found, flag an internal error. */ + +extern void dummy_frame_discard (struct dummy_frame *dummy); + +/* Schedule a cleanup to pop DUMMY_FRAME. */ + +extern struct cleanup *make_cleanup_pop_dummy_frame (struct dummy_frame *); + /* If the PC falls in a dummy frame, return a dummy frame unwinder. */ Index: frame.c =================================================================== RCS file: /cvs/src/src/gdb/frame.c,v retrieving revision 1.255 diff -u -p -r1.255 frame.c --- frame.c 24 Sep 2008 12:59:49 -0000 1.255 +++ frame.c 20 Nov 2008 07:09:58 -0000 @@ -536,6 +536,14 @@ frame_pop (struct frame_info *this_frame struct regcache *scratch; struct cleanup *cleanups; + if (get_frame_type (this_frame) == DUMMY_FRAME) + { + /* 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)); + return; + } + /* Ensure that we have a frame to pop to. */ prev_frame = get_prev_frame_1 (this_frame); @@ -549,11 +557,6 @@ frame_pop (struct frame_info *this_frame scratch = frame_save_as_regcache (prev_frame); cleanups = make_cleanup_regcache_xfree (scratch); - /* If we are popping a dummy frame, clean up the associated - data as well. */ - if (get_frame_type (this_frame) == DUMMY_FRAME) - dummy_frame_pop (get_frame_id (this_frame)); - /* FIXME: cagney/2003-03-16: It should be possible to tell the target's register cache that it is about to be hit with a burst register transfer and that the sequence of register writes should Index: infcall.c =================================================================== RCS file: /cvs/src/src/gdb/infcall.c,v retrieving revision 1.106 diff -u -p -r1.106 infcall.c --- infcall.c 18 Nov 2008 00:13:02 -0000 1.106 +++ infcall.c 20 Nov 2008 07:09:58 -0000 @@ -318,16 +318,19 @@ call_function_by_hand (struct value *fun struct cleanup *retbuf_cleanup; struct inferior_status *inf_status; struct cleanup *inf_status_cleanup; + struct inferior_program_state *caller_state; + struct cleanup *caller_state_cleanup; + struct dummy_frame *dummy_frame; + struct cleanup *dummy_frame_cleanup; CORE_ADDR funaddr; CORE_ADDR real_pc; struct type *ftype = check_typedef (value_type (function)); CORE_ADDR bp_addr; - struct regcache *caller_regcache; - struct cleanup *caller_regcache_cleanup; struct frame_id dummy_id; struct cleanup *args_cleanup; struct frame_info *frame; struct gdbarch *gdbarch; + struct thread_info *this_thread; if (TYPE_CODE (ftype) == TYPE_CODE_PTR) ftype = check_typedef (TYPE_TARGET_TYPE (ftype)); @@ -335,6 +338,7 @@ call_function_by_hand (struct value *fun if (!target_has_execution) noprocess (); + this_thread = inferior_thread (); frame = get_current_frame (); gdbarch = get_frame_arch (frame); @@ -351,15 +355,16 @@ call_function_by_hand (struct value *fun /* A cleanup for the inferior status. Create this AFTER the retbuf so that this can be discarded or applied without interfering with the regbuf. */ - inf_status = save_inferior_status (1); + inf_status = save_inferior_status (); inf_status_cleanup = make_cleanup_restore_inferior_status (inf_status); - /* Save the caller's registers so that they can be restored once the + /* Save the caller's registers and other state associated with the + inferior itself so that they can be restored once the callee returns. To allow nested calls the registers are (further down) pushed onto a dummy frame stack. Include a cleanup (which is tossed once the regcache has been pushed). */ - caller_regcache = frame_save_as_regcache (frame); - caller_regcache_cleanup = make_cleanup_regcache_xfree (caller_regcache); + caller_state = save_inferior_program_state (); + caller_state_cleanup = make_cleanup_restore_inferior_program_state (caller_state); /* Ensure that the initial SP is correctly aligned. */ { @@ -642,8 +647,10 @@ call_function_by_hand (struct value *fun /* Everything's ready, push all the info needed to restore the caller (and identify the dummy-frame) onto the dummy-frame stack. */ - dummy_frame_push (caller_regcache, &dummy_id); - discard_cleanups (caller_regcache_cleanup); + dummy_frame = dummy_frame_push (caller_state, &dummy_id); + /* Do this before calling make_cleanup_pop_dummy_frame. */ + discard_cleanups (caller_state_cleanup); + dummy_frame_cleanup = make_cleanup_pop_dummy_frame (dummy_frame); /* - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - If you're looking to implement asynchronous dummy-frames, then @@ -671,7 +678,6 @@ call_function_by_hand (struct value *fun struct cleanup *old_cleanups = make_cleanup (null_cleanup, 0); struct cleanup *old_cleanups2; int saved_async = 0; - struct thread_info *tp = inferior_thread (); /* If all error()s out of proceed ended up calling normal_stop (and perhaps they should; it already does in the special case @@ -679,7 +685,7 @@ call_function_by_hand (struct value *fun make_cleanup (breakpoint_auto_delete_contents, NULL); disable_watchpoints_before_interactive_call_start (); - tp->proceed_to_finish = 1; /* We want stop_registers, please... */ + this_thread->proceed_to_finish = 1; /* We want stop_registers, please... */ if (target_can_async_p ()) saved_async = target_async_mask (0); @@ -690,12 +696,12 @@ call_function_by_hand (struct value *fun suppress_stop_observer = 1; proceed (real_pc, TARGET_SIGNAL_0, 0); do_cleanups (old_cleanups2); - + if (saved_async) target_async_mask (saved_async); - + enable_watchpoints_after_interactive_call_stop (); - + discard_cleanups (old_cleanups); } @@ -705,10 +711,27 @@ call_function_by_hand (struct value *fun we'll crash as the inferior is no longer running. */ discard_cleanups (inf_status_cleanup); discard_inferior_status (inf_status); + dummy_frame_discard (dummy_frame); error (_("\ The program being debugged exited while in a function called from GDB.")); } + if (! ptid_equal (this_thread->ptid, inferior_thread ()->ptid)) + { + /* We've switched threads. Perhaps this shouldn't happen, but we + protect ourselves anyway. + There's no point in restoring the inferior status, + we're in a different thread. */ + discard_cleanups (inf_status_cleanup); + discard_inferior_status (inf_status); + dummy_frame_discard (dummy_frame); + error (_("\ +The current thread has switched while making a function call from GDB.\n\ +The state of the function call has been lost.\n\ +It may be recoverable by changing back to the original thread\n\ +and examining the state.")); + } + if (stopped_by_random_signal || !stop_stack_dummy) { /* Find the name of the function we're about to complain about. */ @@ -745,9 +768,10 @@ The program being debugged exited while { /* The user wants the context restored. */ - /* We must get back to the frame we were before the - dummy call. */ - frame_pop (get_current_frame ()); + /* We must get back to the frame we were before the dummy call. + Plus we need to restore inferior status to that before the + dummy call. This is all handled by cleanups + dummy_frame_cleanup and inf_status_cleanup. */ /* FIXME: Insert a bunch of wrap_here; name can be very long if it's a C++ name with arguments and stuff. */ @@ -761,14 +785,16 @@ Evaluation of the expression containing else { /* The user wants to stay in the frame where we stopped - (default).*/ - /* If we restored the inferior status (via the cleanup), + (default). + If we restored the inferior status (via the cleanup), we would print a spurious error message (Unable to - restore previously selected frame), would write the - registers from the inf_status (which is wrong), and - would do other wrong things. */ + restore previously selected frame), and + would do other wrong things. + Discarding inf_status_cleanup also discards + dummy_frame_cleanup. */ discard_cleanups (inf_status_cleanup); discard_inferior_status (inf_status); + /* FIXME: Insert a bunch of wrap_here; name can be very long if it's a C++ name with arguments and stuff. */ error (_("\ @@ -782,14 +808,17 @@ Evaluation of the expression containing if (!stop_stack_dummy) { - /* We hit a breakpoint inside the FUNCTION. */ - /* If we restored the inferior status (via the cleanup), we + /* We hit a breakpoint inside the FUNCTION. + If we restored the inferior status (via the cleanup), we would print a spurious error message (Unable to restore previously selected frame), would write the registers from the inf_status (which is wrong), and would do other - wrong things. */ + wrong things. + Discarding inf_status_cleanup also discards + dummy_frame_cleanup. */ discard_cleanups (inf_status_cleanup); discard_inferior_status (inf_status); + /* The following error message used to say "The expression which contained the function call has been discarded." It is a hard concept to explain in a few words. Ideally, @@ -811,6 +840,9 @@ the function call)."), name); /* If we get here the called FUNCTION run to completion. */ + /* The dummy frame has been popped so discard its cleanup. */ + discard_cleanups (dummy_frame_cleanup); + /* On normal return, the stack dummy has been popped already. */ regcache_cpy_no_passthrough (retbuf, stop_registers); Index: inferior.h =================================================================== RCS file: /cvs/src/src/gdb/inferior.h,v retrieving revision 1.116 diff -u -p -r1.116 inferior.h --- inferior.h 20 Nov 2008 00:35:23 -0000 1.116 +++ inferior.h 20 Nov 2008 07:09:58 -0000 @@ -40,24 +40,40 @@ struct ui_out; /* For struct frame_id. */ #include "frame.h" -/* Structure in which to save the status of the inferior. Create/Save - through "save_inferior_status", restore through - "restore_inferior_status". - - This pair of routines should be called around any transfer of - control to the inferior which you don't want showing up in your - control variables. */ +/* Two structures are used to record inferior state. + inferior_program_state contains state about the program itself like its + registers and any signal it received when it last stopped. + This state must be restored regardless of how the inferior function call + ends (either successfully, or after it hits a breakpoint or signal) + if the program is to properly continue where it left off. + + inferior_status contains state regarding gdb's control of the inferior + itself like stepping control. It also contains session state like the + user's currently selected frame. + This state is only restored upon successful completion of the + inferior function call. + + Call these routines around hand called functions, including function calls + in conditional breakpoints for example. */ + +struct inferior_program_state; struct inferior_status; -extern struct inferior_status *save_inferior_status (int); +extern struct inferior_program_state *save_inferior_program_state (void); +extern struct inferior_status *save_inferior_status (void); +extern void restore_inferior_program_state (struct inferior_program_state *); extern void restore_inferior_status (struct inferior_status *); +extern struct cleanup *make_cleanup_restore_inferior_program_state (struct inferior_program_state *); extern struct cleanup *make_cleanup_restore_inferior_status (struct inferior_status *); +extern void discard_inferior_program_state (struct inferior_program_state *); extern void discard_inferior_status (struct inferior_status *); +extern struct regcache *get_inferior_program_state_regcache (struct inferior_program_state *); + /* The -1 ptid, often used to indicate either an error condition or a "don't care" condition, i.e, "run all threads." */ extern ptid_t minus_one_ptid; Index: infrun.c =================================================================== RCS file: /cvs/src/src/gdb/infrun.c,v retrieving revision 1.339 diff -u -p -r1.339 infrun.c --- infrun.c 20 Nov 2008 00:35:23 -0000 1.339 +++ infrun.c 20 Nov 2008 07:09:58 -0000 @@ -45,7 +45,7 @@ #include "language.h" #include "solib.h" #include "main.h" - +#include "dummy-frame.h" #include "gdb_assert.h" #include "mi/mi-common.h" #include "event-top.h" @@ -4342,15 +4342,23 @@ Further execution is probably impossible if (stop_stack_dummy) { - /* Pop the empty frame that contains the stack dummy. POP_FRAME - ends with a setting of the current frame, so we can use that - next. */ - frame_pop (get_current_frame ()); - /* Set stop_pc to what it was before we called the function. - Can't rely on restore_inferior_status because that only gets - called if we don't stop in the called function. */ - stop_pc = read_pc (); - select_frame (get_current_frame ()); + /* Pop the empty frame that contains the stack dummy. + This also restores all inferior state prior to the call. + If the current frame is not a stack dummy, do nothing and warn + the user. */ + struct frame_info *frame = get_current_frame (); + if (get_frame_type (frame) == DUMMY_FRAME) + { + dummy_frame_pop (get_frame_id (frame)); + } + else + { + /* We avoid calling the frame a dummy frame as it has little + meaning to the user. */ + printf_filtered (_("\ +Stopped after an inferior function call, but not in the expected stack frame.\n\ +Proceed with caution.\n")); + } } done: @@ -4746,10 +4754,86 @@ signals_info (char *signum_exp, int from printf_filtered (_("\nUse the \"handle\" command to change these tables.\n")); } -struct inferior_status +/* Inferior program state. + These are details related to the inferior itself, and don't include + things like what frame the user had selected or what gdb was doing + with the target at the time. + For inferior function calls these are things we want to restore + regardless of whether the function call successfully completes + or the dummy frame has to be manually popped. */ + +struct inferior_program_state { enum target_signal stop_signal; CORE_ADDR stop_pc; + struct regcache *registers; +}; + +struct inferior_program_state * +save_inferior_program_state () +{ + struct inferior_program_state *inf_state = XMALLOC (struct inferior_program_state); + struct thread_info *tp = inferior_thread (); + struct inferior *inf = current_inferior (); + + inf_state->stop_signal = tp->stop_signal; + inf_state->stop_pc = stop_pc; + + inf_state->registers = regcache_dup (get_current_regcache ()); + + return inf_state; +} + +/* Restore inferior session state to INF_STATE. */ + +void +restore_inferior_program_state (struct inferior_program_state *inf_state) +{ + struct thread_info *tp = inferior_thread (); + struct inferior *inf = current_inferior (); + + tp->stop_signal = inf_state->stop_signal; + stop_pc = inf_state->stop_pc; + + /* The inferior can be gone if the user types "print exit(0)" + (and perhaps other times). */ + if (target_has_execution) + /* NB: The register write goes through to the target. */ + regcache_cpy (get_current_regcache (), inf_state->registers); + regcache_xfree (inf_state->registers); +} + +static void +do_restore_inferior_program_state_cleanup (void *state) +{ + restore_inferior_program_state (state); +} + +struct cleanup * +make_cleanup_restore_inferior_program_state (struct inferior_program_state *inf_state) +{ + return make_cleanup (do_restore_inferior_program_state_cleanup, inf_state); +} + +void +discard_inferior_program_state (struct inferior_program_state *inf_state) +{ + regcache_xfree (inf_state->registers); + xfree (inf_state); +} + +struct regcache * +get_inferior_program_state_regcache (struct inferior_program_state *inf_state) +{ + return inf_state->registers; +} + +/* Session related state for inferior function calls. + These are the additional bits of state that need to be restored + when an inferior function call successfully completes. */ + +struct inferior_status +{ bpstat stop_bpstat; int stop_step; int stop_stack_dummy; @@ -4763,32 +4847,23 @@ struct inferior_status int stop_after_trap; int stop_soon; - /* These are here because if call_function_by_hand has written some - registers and then decides to call error(), we better not have changed - any registers. */ - struct regcache *registers; - - /* A frame unique identifier. */ + /* ID if the selected frame when the inferior function call was made. */ struct frame_id selected_frame_id; int breakpoint_proceeded; - int restore_stack_info; int proceed_to_finish; }; /* Save all of the information associated with the inferior<==>gdb - connection. INF_STATUS is a pointer to a "struct inferior_status" - (defined in inferior.h). */ + connection. */ struct inferior_status * -save_inferior_status (int restore_stack_info) +save_inferior_status () { struct inferior_status *inf_status = XMALLOC (struct inferior_status); struct thread_info *tp = inferior_thread (); struct inferior *inf = current_inferior (); - inf_status->stop_signal = tp->stop_signal; - inf_status->stop_pc = stop_pc; inf_status->stop_step = tp->stop_step; inf_status->stop_stack_dummy = stop_stack_dummy; inf_status->stopped_by_random_signal = stopped_by_random_signal; @@ -4806,12 +4881,10 @@ save_inferior_status (int restore_stack_ inf_status->stop_bpstat = tp->stop_bpstat; tp->stop_bpstat = bpstat_copy (tp->stop_bpstat); inf_status->breakpoint_proceeded = breakpoint_proceeded; - inf_status->restore_stack_info = restore_stack_info; inf_status->proceed_to_finish = tp->proceed_to_finish; - inf_status->registers = regcache_dup (get_current_regcache ()); - inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL)); + return inf_status; } @@ -4836,14 +4909,14 @@ restore_selected_frame (void *args) return (1); } +/* Restore inferior session state to INF_STATUS. */ + void restore_inferior_status (struct inferior_status *inf_status) { struct thread_info *tp = inferior_thread (); struct inferior *inf = current_inferior (); - tp->stop_signal = inf_status->stop_signal; - stop_pc = inf_status->stop_pc; tp->stop_step = inf_status->stop_step; stop_stack_dummy = inf_status->stop_stack_dummy; stopped_by_random_signal = inf_status->stopped_by_random_signal; @@ -4856,24 +4929,11 @@ restore_inferior_status (struct inferior inf->stop_soon = inf_status->stop_soon; bpstat_clear (&tp->stop_bpstat); tp->stop_bpstat = inf_status->stop_bpstat; + inf_status->stop_bpstat = NULL; breakpoint_proceeded = inf_status->breakpoint_proceeded; tp->proceed_to_finish = inf_status->proceed_to_finish; - /* The inferior can be gone if the user types "print exit(0)" - (and perhaps other times). */ - if (target_has_execution) - /* NB: The register write goes through to the target. */ - regcache_cpy (get_current_regcache (), inf_status->registers); - regcache_xfree (inf_status->registers); - - /* FIXME: If we are being called after stopping in a function which - is called from gdb, we should not be trying to restore the - selected frame; it just prints a spurious error message (The - message is useful, however, in detecting bugs in gdb (like if gdb - clobbers the stack)). In fact, should we be restoring the - inferior status at all in that case? . */ - - if (target_has_stack && inf_status->restore_stack_info) + if (target_has_stack) { /* The point of catch_errors is that if the stack is clobbered, walking the stack might encounter a garbage pointer and @@ -4885,7 +4945,6 @@ restore_inferior_status (struct inferior /* Error in restoring the selected frame. Select the innermost frame. */ select_frame (get_current_frame ()); - } xfree (inf_status); @@ -4908,10 +4967,9 @@ discard_inferior_status (struct inferior { /* See save_inferior_status for info on stop_bpstat. */ bpstat_clear (&inf_status->stop_bpstat); - regcache_xfree (inf_status->registers); xfree (inf_status); } - + int inferior_has_forked (ptid_t pid, ptid_t *child_pid) { Index: testsuite/gdb.base/call-signal-resume.exp =================================================================== RCS file: testsuite/gdb.base/call-signal-resume.exp diff -N testsuite/gdb.base/call-signal-resume.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.base/call-signal-resume.exp 20 Nov 2008 07:09:59 -0000 @@ -0,0 +1,107 @@ +# Copyright 2008 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if $tracelevel then { + strace $tracelevel +} + +if [target_info exists gdb,noinferiorio] { + verbose "Skipping call-signal-resume.exp because of no fileio capabilities." + continue +} + +set prms_id 0 +set bug_id 0 + +set testfile "call-signals" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested call-signal-resume.exp + return -1 +} + +# Some targets can't do function calls, so don't even bother with this +# test. +if [target_info exists gdb,cannot_call_functions] { + setup_xfail "*-*-*" 2416 + fail "This target can not call functions" + continue +} + +proc get_dummy_frame_number { } { + global gdb_prompt + + send_gdb "bt\n" + gdb_expect { + -re "#(\[0-9\]*) *.*$gdb_prompt $" + { + return $expect_out(1,string) + } + -re "$gdb_prompt $" + { + return "" + } + timeout + { + return "" + } + } + return "" +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if { ![runto_main] } { + fail "Can't run to main" + return 0 +} + +# Call function (causing the program to get a signal), and see if gdb handles +# it properly. +gdb_test_multiple "call gen_signal ()" \ + "call-signal-resume, inferior function call signaled" { + -re "\[\r\n\]*no signal\[\r\n\]+$gdb_prompt $" { + unsupported "call-signal-resume, inferior function call signaled" + return 0 + } + -re "\[\r\n\]*The program being debugged was signaled.*\[\r\n\]+$gdb_prompt $" { + pass "call-signal-resume, inferior function call signaled" + } +} + +set frame_number [get_dummy_frame_number] +if { "$frame_number" == "" } { + fail "call-signal-resume, dummy stack frame number" + setup_xfail "*-*-*" +} else { + pass "call-signal-resume, dummy stack frame number" +} + +# Pop the dummy frame. +gdb_test "frame $frame_number" "" +gdb_test "set confirm off" "" +gdb_test "return" "" + +# Resume execution, the program should successfully complete. +gdb_test "continue" "Program exited normally." + +return 0 Index: testsuite/gdb.base/call-signals.c =================================================================== RCS file: testsuite/gdb.base/call-signals.c diff -N testsuite/gdb.base/call-signals.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.base/call-signals.c 20 Nov 2008 07:09:59 -0000 @@ -0,0 +1,48 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Support program for testing handling of inferior function calls + in the presence of signals. */ + +#include +#include +#include + +void +gen_signal () +{ + /* According to sigall.exp, SIGABRT is always supported, + so try that first. */ +#ifdef SIGABRT + kill (getpid (), SIGABRT); +#endif +#ifdef SIGSEGV + kill (getpid (), SIGSEGV); +#endif + /* If we get here we couldn't generate a signal, tell dejagnu. */ + printf ("no signal\n"); +} + +int +main () +{ +#ifdef usestubs + set_debug_traps (); + breakpoint (); +#endif + return 0; +} Index: testsuite/gdb.base/unwindonsignal.exp =================================================================== RCS file: testsuite/gdb.base/unwindonsignal.exp diff -N testsuite/gdb.base/unwindonsignal.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.base/unwindonsignal.exp 20 Nov 2008 07:09:59 -0000 @@ -0,0 +1,94 @@ +# Copyright 2008 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if $tracelevel then { + strace $tracelevel +} + +if [target_info exists gdb,noinferiorio] { + verbose "Skipping unwindonsignal.exp because of no fileio capabilities." + continue +} + +set prms_id 0 +set bug_id 0 + +set testfile "call-signals" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested unwindonsignal.exp + return -1 +} + +# Some targets can't do function calls, so don't even bother with this +# test. +if [target_info exists gdb,cannot_call_functions] { + setup_xfail "*-*-*" 2416 + fail "This target can not call functions" + continue +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if { ![runto_main] } { + fail "Can't run to main" + return 0 +} + +# Turn on unwindonsignal. +gdb_test "set unwindonsignal on" \ + "" \ + "setting unwindonsignal" +gdb_test "show unwindonsignal" \ + "Unwinding of stack .* is on." \ + "showing unwindonsignal" + +# Call function (causing the program to get a signal), and see if gdb handles +# it properly. +gdb_test_multiple "call gen_signal ()" \ + "unwindonsignal, inferior function call signaled" { + -re "\[\r\n\]*no signal\[\r\n\]+$gdb_prompt $" { + unsupported "unwindonsignal, inferior function call signaled" + return 0 + } + -re "\[\r\n\]*The program being debugged was signaled.*\[\r\n\]+$gdb_prompt $" { + pass "unwindonsignal, inferior function call signaled" + } +} + +# Verify the stack got unwound. +gdb_test "bt" \ + "#0 *main \\(.*\\) at .*" \ + "unwindonsignal, stack unwound" + +# Verify the dummy frame got removed from dummy_frame_stack. +gdb_test_multiple "maint print dummy-frames" \ + "unwindonsignal, dummy frame removed" { + -re "\[\r\n\]*.*stack=.*code=.*\[\r\n\]+$gdb_prompt $" { + fail "unwindonsignal, dummy frame removed" + } + -re "\[\r\n\]+$gdb_prompt $" { + pass "unwindonsignal, dummy frame removed" + } +} + +return 0