[RFA] dummy frame handling cleanup, plus inferior fun call signal handling improvement

Doug Evans dje@google.com
Thu Nov 20 15:06:00 GMT 2008


On Thu, Nov 20, 2008 at 12:03 AM, Doug Evans <dje@google.com> wrote:
> On Tue, Nov 18, 2008 at 4:58 AM, Doug Evans <dje@google.com> wrote:
>> Hi.  This patch was in progress when I wrote
>> http://sourceware.org/ml/gdb-patches/2008-11/msg00391.html
>> This patch subsumes that one.
>>
>> There are two things this patch does:
>> 1) properly pop dummy frames more often
>> 2) make the inferior resumable after an inferior function call gets a
>>   signal without having to remember to do "signal 0"
>>
>> 1) properly pop dummy frames more often
>>
>> Ulrich asked:
>>> I agree that it would be better if call_function_by_hand were to call
>>> dummy_frame_pop in this case.  However, why exactly is this a bug?
>>
>> It's a bug because it's confusing to not pop the frame in the places
>> where one is able to.  Plus it's, well, unclean.
>> I recognize that the dummy frame stack can't be precisely managed because
>> the user can do things like:
>>
>> set $orig_pc = $pc
>> set $orig_sp = $sp
>> break my_fun
>> call my_fun()
>> set $pc = $orig_pc
>> set $sp = $orig_sp
>> continue
>>
>> [This doesn't always work, but you get the idea.]
>> At the "continue", the inferior function call no longer exists and
>> gdb's mechanisms for removing the dummy frame from dummy_frame_stack
>> will never trigger (until the program is resumed and cleanup_dummy_frames
>> is called).  But we can still keep things clean
>> and unconfusing for the common case.
>>
>> 2) make the inferior resumable after an inferior function call gets a
>>   signal without having to remember to do "signal 0"
>>
>> If an inferior function call gets a signal (say SIGSEGV or SIGABRT),
>> one should be able to easily resume program execution after popping the stack.
>> It works today, but after manually popping the stack (e.g. with
>> "frame <dummy> ; return" where <dummy> is the dummy stack frame's number)
>> one has to remember to resume the program with "signal 0".
>> This is because stop_signal doesn't get restored.
>> Maybe there's a reason it shouldn't be, or maybe should be made an option,
>> but the current behaviour seems user-unfriendly for the normal case.
>>
>> Restoring stop_signal also has the benefit that if an inferior function
>> call is made after getting a signal, and the inferior function call
>> doesn't complete (e.g. has a breakpoint and doesn't complete right away),
>> the user can resume the program (after popping the inf fun call off the
>> stack) with "continue" without having to remember what the signal was
>> and remember to use "signal N".
>>
>> [BTW, IWBN if there was a command that did the equivalent of
>> "frame <dummy> ; return", named say "abandon", so that one didn't have
>> to go to the trouble of finding the dummy frame's stack number.
>> "abandon" would have the additional benefit that if the stack
>> was corrupted and one couldn't find the dummy frame, it would still
>> work since everything it needs is in dummy_frame_stack - it doesn't need
>> to examine the inferior's stack, except maybe for some sanity checking.
>> Continuing the inferior may still not be possible, but it does give the
>> user a more straightforward way to abandon an inferior function call
>> than exists today.]
>>
>> The bulk of the patch goes into making (2) work in a clean way.
>> Right now the inferior state that can be restored is a collection of
>> inferior_status, regcache, and misc. things like stop_pc (see the
>> assignment of stop_pc in normal_stop() when stop_stack_dummy).
>> The patch organizes the state into two pieces: inferior program state
>> (registers, stop_pc, stop_signal) and gdb session state
>> (the rest of inferior_status).
>> Program state is recorded on the dummy frame stack and is always
>> restored, either when the inferior function call completes or
>> when the stack frame is manually popped.
>> inferior_status contains the things that only get restored
>> if either the inferior function call successfully completes or
>> it gets a signal and unwindonsignal is set.
>>
>> P.S. I've removed one copy of the regcache.  Hopefully I've structured
>> things in a way that doesn't lose.
>>
>> NOTES:
>> - this adds the unwindonsignal.exp testcase from before, plus a new
>>  testcase that verifies one can resume the inferior after abandoning
>>  an inferior function call that gets a signal
>>
>> It's a big patch so presumably there are issues with it.
>> Comments?
>>
>> [tested on i386-linux and x86_64-linux]
>>
>> 2008-11-18  Doug Evans  <dje@google.com>
>>
>>        * 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,
>>        assert_dummy_present,pop_dummy_frame_below,lookup_dummy_id,
>>        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.  Discard dummy frames below
>>        the one being deleted.
>>        (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.
>>        Ensure dummy frame is popped/discarded for all possible exit paths.
>>        * 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,
>>        make_cleanup_restore_inferior_program_state,
>>        discard_inferior_program_state,
>>        get_inferior_program_state_regcache): New functions.
>>        (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.
>
> ref: http://sourceware.org/ml/gdb-patches/2008-11/msg00454.html
>
> Blech.  I went too far in trying to keep dummy_frame_stack accurate.
> One can (theoretically) have multiple hand-function-calls outstanding
> in multiple threads (and soon in multiple processes presumably).
> Attached is a new version of the patch that goes back to having
> dummy_frame_pop only popping the requested frame (and similarily for
> dummy_frame_discard).
>
> I wrote a testcase that calls functions in multiple threads (with
> scheduler-locking on) by setting a breakpoint on the function being
> called.  I think there's a bug in scheduler-locking support as when I
> make the second call in the second thread, gdb makes the first thread
> step over the breakpoint and then resume, and control returns to
> call_function_by_hand with inferior_ptid changed to the first thread.
> To protect call_function_by_hand from this it now flags an error if
> the thread changes.
> [I'll submit the testcase separately once I can get it to pass, unless
> folks want it to see it.]
>
> How's this?
>
> 2008-11-18  Doug Evans  <dje@google.com>
>
>        * 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.
>

Ummm, this time with the correct patch attached.  Sorry!
-------------- next part --------------
2008-11-18  Doug Evans  <dje@google.com>

	* 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 <http://www.gnu.org/licenses/>.
+
+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\]*) *<function called from gdb>.*$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 <http://www.gnu.org/licenses/>.  */
+
+/* Support program for testing handling of inferior function calls
+   in the presence of signals.  */
+
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+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 <http://www.gnu.org/licenses/>.
+
+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


More information about the Gdb-patches mailing list