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

Doug Evans
2008-12-03T06:04:34Z

[-- Attachment #1: Type: text/plain, Size: 5281 bytes --]

On Mon, Dec 1, 2008 at 5:20 PM, Doug Evans <dje@google.com> wrote:
> On Mon, Dec 1, 2008 at 1:21 PM, Pedro Alves <pedro@codesourcery.com> wrote:
>> Hi Doug,
>>
>> I'd like to bring a current GDB deficiency to your attention, in
>> case it affects anything related to this patch.
>>
>> If GDB stops due to a signal instead of hitting the dummy frame
>> breakpoint, and you have set GDB to restore the state
>> automatically with "set unwindonsignal on", and the thread
>> that reported the signal (say a SIGSEGV) was *not* the same that was
>> doing the infcall, GDB will currently restore the old context to the
>> wrong thread (seen by inspection, having really tried it).
>>
>> Not having studied the patch yet, I'm just wondering if your changes
>> would make it easier or harder to fix this, or if you could be
>> extending the problem by possibly restoring things in the wrong
>> thread as well.
>
> Heh. My patch serendipitously has the following to catch another case
> I was seeing where the current thread unexpectedly changed.
>
> 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."));
> }
>
> I need fix my patch to save the calling thread's ptid
> (this_thread->ptid) before resuming execution in case the thread dies.
> I think I shouldn't call dummy_frame_discard here too.
> And I also need to change the comment to document why the current
> thread can switch, because it _can_ happen. :-)
> I'll prepare a new version of my patch, and include a testcase to
> handle the situation you mention. Thanks!

Here's an updated version of my patch.
It includes a testcase for the current thread changing in the middle
of a hand-called function.

Ok to check in?

2008-12-02 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 (regcache): Delete forward decl.
(inferior_program_state,dummy_frame,cleanup): Add forward decls.
(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_ptid.
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.
* gdb.base/unwindonsignal.c: New file.
* gdb.threads/interrupted-hand-call.exp: New file.
* gdb.threads/interrupted-hand-call.cv: New file.

[-- Attachment #2: gdb-081202-infcall-3.patch.txt --]
[-- Type: text/plain, Size: 51485 bytes --]

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 (regcache): Delete forward decl.
(inferior_program_state,dummy_frame,cleanup): Add forward decls.
(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_ptid.
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.
* gdb.base/unwindonsignal.c: New file.
* gdb.threads/interrupted-hand-call.exp: New file.
* gdb.threads/interrupted-hand-call.cv: 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 3 Dec 2008 05:00:42 -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 3 Dec 2008 05:00:42 -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.256
diff -u -p -r1.256 frame.c
--- frame.c 20 Nov 2008 22:16:16 -0000 1.256
+++ frame.c 3 Dec 2008 05:00:43 -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 3 Dec 2008 05:00:43 -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;
+ ptid_t this_thread_ptid;

if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
@@ -351,15 +354,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 +646,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 +677,12 @@ 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 ();
+ struct thread_info *this_thread;
+
+ this_thread = inferior_thread ();
+ /* Save this thread's ptid, we need it later but the thread
+ may have exited. */
+ this_thread_ptid = this_thread->ptid;

/* 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 +690,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 +701,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 +716,28 @@ 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. This can happen if another thread gets a
+ signal or breakpoint while our thread was running.
+ 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);
+ /* Keep the dummy frame record, if the user switches back to the
+ thread with the hand-call, we'll need it. */
+ error (_("\
+The current thread has changed 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 +774,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 +791,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 +814,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 +846,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 3 Dec 2008 05:00:43 -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.345
diff -u -p -r1.345 infrun.c
--- infrun.c 2 Dec 2008 19:20:23 -0000 1.345
+++ infrun.c 3 Dec 2008 05:00:43 -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"
@@ -4346,15 +4346,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:
@@ -4750,10 +4758,86 @@ signals_info (char *signum_exp, int from
printf_filtered (_("\nUse the \"handle\" command to change these tables.\n"));
}
\f
-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;
@@ -4767,32 +4851,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;
@@ -4810,12 +4885,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;
}

@@ -4840,14 +4913,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;
@@ -4860,24 +4933,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
@@ -4889,7 +4949,6 @@ restore_inferior_status (struct inferior
/* Error in restoring the selected frame. Select the innermost
frame. */
select_frame (get_current_frame ());
-
}

xfree (inf_status);
@@ -4912,10 +4971,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);
}
-
+\f
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 3 Dec 2008 05:00:43 -0000
@@ -0,0 +1,159 @@
+# 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/>.
+
+# Test inferior resumption after discarding a hand-called function.
+# There are two things to test.
+# 1) Inferior stops normally. Upon resumption it should continue normally,
+# regardless of whatever signal the hand-called function got.
+# 2) Inferior is stopped at a signal. Upon resumption it should continue
+# with that signal, regardless of whatever the hand-called function did.
+
+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
+}
+
+gdb_test "break stop_one" "Breakpoint \[0-9\]* at .*"
+gdb_test "continue" "Continuing.*Breakpoint \[0-9\]*, stop_one.*" \
+ "continue to breakpoint at stop_one"
+
+# Call function (causing the program to get a signal), and see if gdb handles
+# it properly.
+gdb_test_multiple "call gen_signal ()" \
+ "inferior function call signaled" {
+ -re "\[\r\n\]*no signal\[\r\n\]+$gdb_prompt $" {
+ unsupported "inferior function call signaled"
+ return 0
+ }
+ -re "\[\r\n\]*The program being debugged was signaled.*\[\r\n\]+$gdb_prompt $" {
+ pass "inferior function call signaled"
+ }
+}
+
+set frame_number [get_dummy_frame_number]
+if { "$frame_number" == "" } {
+ fail "dummy stack frame number"
+ setup_xfail "*-*-*"
+} else {
+ pass "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 continue without any signal.
+
+gdb_test "break stop_two" "Breakpoint \[0-9\]* at .*"
+gdb_test "continue" "Breakpoint \[0-9\]*, stop_two.*" \
+ "continue to breakpoint at stop_two"
+
+# Continue again, we should get a signal.
+
+gdb_test "continue" "Program received signal .*" \
+ "continue to receipt of signal"
+
+# Hand call another function that prematurely stops,
+# then manually pop the dummy stack frame.
+
+gdb_test "break null_hand_call" "Breakpoint \[0-9\]* at .*"
+gdb_test "call null_hand_call ()" "Breakpoint \[0-9\]*, null_hand_call.*" \
+ "null_hand_call"
+
+set frame_number [get_dummy_frame_number]
+if { "$frame_number" == "" } {
+ fail "dummy stack frame number"
+ setup_xfail "*-*-*"
+ # Need something.
+ set frame_number 0
+} else {
+ pass "dummy stack frame number"
+}
+
+# Pop the dummy frame.
+gdb_test "frame $frame_number" ""
+gdb_test "set confirm off" ""
+gdb_test "return" ""
+
+# Continue again, this time we should get to the signal handler.
+
+gdb_test "break handle_signal" "Breakpoint \[0-9\]* at .*"
+gdb_test "continue" "Breakpoint \[0-9\]*, handle_signal.*" \
+ "continue to breakpoint at handle_signal"
+
+# Continue one last time, the program should exit normally.
+
+gdb_test "continue" "Program exited normally." \
+ "continue to program exit"
+
+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 3 Dec 2008 05:00:43 -0000
@@ -0,0 +1,89 @@
+/* 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
+handle_signal (int sig)
+{
+}
+
+void
+gen_signal ()
+{
+ /* According to sigall.exp, SIGABRT is always supported. */
+#ifdef SIGABRT
+ kill (getpid (), SIGABRT);
+#endif
+ /* If we get here we couldn't generate a signal, tell dejagnu. */
+ printf ("no signal\n");
+}
+
+/* Easy place to set a breakpoint. */
+
+void
+stop_one ()
+{
+}
+
+void
+stop_two ()
+{
+}
+
+void
+null_hand_call ()
+{
+}
+
+int
+main ()
+{
+#ifdef usestubs
+ set_debug_traps ();
+ breakpoint ();
+#endif
+
+#ifdef SIG_SETMASK
+ /* Ensure all the signals aren't blocked.
+ The environment in which the testsuite is run may have blocked some
+ for whatever reason. */
+ {
+ sigset_t newset;
+ sigemptyset (&newset);
+ sigprocmask (SIG_SETMASK, &newset, NULL);
+ }
+#endif
+
+ signal (SIGABRT, handle_signal);
+
+ /* Stop here so we can hand-call gen_signal. */
+ stop_one ();
+
+ /* When we're resumed stop here. */
+ stop_two ();
+
+ /* When we're resumed we generate a signal ourselves. */
+ gen_signal ();
+
+ return 0;
+}
Index: testsuite/gdb.base/unwindonsignal.c
===================================================================
RCS file: testsuite/gdb.base/unwindonsignal.c
diff -N testsuite/gdb.base/unwindonsignal.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.base/unwindonsignal.c 3 Dec 2008 05:00:43 -0000
@@ -0,0 +1,65 @@
+/* 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 unwindonsignal. */
+
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+void
+gen_signal ()
+{
+ /* According to sigall.exp, SIGABRT is always supported. */
+#ifdef SIGABRT
+ kill (getpid (), SIGABRT);
+#endif
+ /* If we get here we couldn't generate a signal, tell dejagnu. */
+ printf ("no signal\n");
+}
+
+/* Easy place to set a breakpoint. */
+
+void
+stop_here ()
+{
+}
+
+int
+main ()
+{
+#ifdef usestubs
+ set_debug_traps ();
+ breakpoint ();
+#endif
+
+#ifdef SIG_SETMASK
+ /* Ensure all the signals aren't blocked.
+ The environment in which the testsuite is run may have blocked some
+ for whatever reason. */
+ {
+ sigset_t newset;
+ sigemptyset (&newset);
+ sigprocmask (SIG_SETMASK, &newset, NULL);
+ }
+#endif
+
+ /* Stop here so we can hand-call gen_signal. */
+ stop_here ();
+
+ 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 3 Dec 2008 05:00:43 -0000
@@ -0,0 +1,98 @@
+# 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 "unwindonsignal"
+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
+}
+
+gdb_test "break stop_here" "Breakpoint \[0-9\]* at .*"
+gdb_test "continue" "Continuing.*Breakpoint \[0-9\]*, stop_here.*" \
+ "continue to breakpoint at stop_here"
+
+# 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 *\[x0-9a-f in\]*stop_here \\(.*\\) at .*#1 *\[x0-9a-f in\]*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
Index: testsuite/gdb.threads/interrupted-hand-call.c
===================================================================
RCS file: testsuite/gdb.threads/interrupted-hand-call.c
diff -N testsuite/gdb.threads/interrupted-hand-call.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/interrupted-hand-call.c 3 Dec 2008 05:00:43 -0000
@@ -0,0 +1,144 @@
+/* Test case for hand function calls interrupted by a signal in another thread.
+
+ Copyright 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NR_THREADS
+#define NR_THREADS 4
+#endif
+
+pthread_t threads[NR_THREADS];
+
+/* Number of threads currently running. */
+int thread_count;
+
+pthread_mutex_t thread_count_mutex;
+
+pthread_cond_t thread_count_condvar;
+
+sig_atomic_t sigabrt_received;
+
+void
+incr_thread_count (void)
+{
+ pthread_mutex_lock (&thread_count_mutex);
+ ++thread_count;
+ if (thread_count == NR_THREADS)
+ pthread_cond_signal (&thread_count_condvar);
+ pthread_mutex_unlock (&thread_count_mutex);
+}
+
+void
+cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut)
+{
+ pthread_mutex_lock (mut);
+ pthread_cond_wait (cond, mut);
+ pthread_mutex_unlock (mut);
+}
+
+void
+noreturn (void)
+{
+ pthread_mutex_t mut;
+ pthread_cond_t cond;
+
+ pthread_mutex_init (&mut, NULL);
+ pthread_cond_init (&cond, NULL);
+
+ /* Wait for a condition that will never be signaled, so we effectively
+ block the thread here. */
+ cond_wait (&cond, &mut);
+}
+
+void *
+thread_entry (void *unused)
+{
+ incr_thread_count ();
+ noreturn ();
+}
+
+void
+sigabrt_handler (int signo)
+{
+ sigabrt_received = 1;
+}
+
+/* Helper to test a hand-call being "interrupted" by a signal on another
+ thread. */
+
+void
+hand_call_with_signal (void)
+{
+ const struct timespec ts = { 0, 10000000 }; /* 0.01 sec */
+
+ sigabrt_received = 0;
+ pthread_kill (threads[0], SIGABRT);
+ while (! sigabrt_received)
+ nanosleep (&ts, NULL);
+}
+
+/* Wait until all threads are running. */
+
+void
+wait_all_threads_running (void)
+{
+ pthread_mutex_lock (&thread_count_mutex);
+ pthread_cond_wait (&thread_count_condvar, &thread_count_mutex);
+ if (thread_count == NR_THREADS)
+ {
+ pthread_mutex_unlock (&thread_count_mutex);
+ return;
+ }
+ pthread_mutex_unlock (&thread_count_mutex);
+ printf ("failed waiting for all threads to start\n");
+ abort ();
+}
+
+/* Called when all threads are running.
+ Easy place for a breakpoint. */
+
+void
+all_threads_running (void)
+{
+}
+
+int
+main (void)
+{
+ int i;
+
+ signal (SIGABRT, sigabrt_handler);
+
+ pthread_mutex_init (&thread_count_mutex, NULL);
+ pthread_cond_init (&thread_count_condvar, NULL);
+
+ for (i = 0; i < NR_THREADS; ++i)
+ pthread_create (&threads[i], NULL, thread_entry, NULL);
+
+ wait_all_threads_running ();
+ all_threads_running ();
+
+ return 0;
+}
+
Index: testsuite/gdb.threads/interrupted-hand-call.exp
===================================================================
RCS file: testsuite/gdb.threads/interrupted-hand-call.exp
diff -N testsuite/gdb.threads/interrupted-hand-call.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/interrupted-hand-call.exp 3 Dec 2008 05:00:43 -0000
@@ -0,0 +1,92 @@
+# Copyright (C) 2004, 2007, 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/>.
+
+# Test recovering from a hand function call that gets interrupted
+# by a signal in another thread.
+
+set NR_THREADS 4
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+set testfile "interrupted-hand-call"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "incdir=${objdir}" "additional_flags=-DNR_THREADS=$NR_THREADS"]] != "" } {
+ 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
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if { ![runto_main] } {
+ fail "Can't run to main"
+ return 0
+}
+
+gdb_test "break all_threads_running" \
+ "Breakpoint 2 at .*: file .*${srcfile}, line .*" \
+ "breakpoint on all_threads_running"
+
+# Run the program and make sure GDB reports that we stopped after
+# hitting breakpoint 2 in all_threads_running().
+
+gdb_test "continue" \
+ ".*Breakpoint 2, all_threads_running ().*" \
+ "run to all_threads_running"
+
+# NOTE: Don't turn on scheduler-locking here.
+# We want the main thread (hand_call_with_signal) and
+# thread 1 (sigabrt_handler) to both run.
+
+gdb_test "call hand_call_with_signal()" \
+ ".*current thread has changed.*" \
+ "hand-call interrupted by signal in another thread"
+
+# Verify dummy stack frame is still present.
+
+gdb_test "maint print dummy-frames" ".*stack=.*" "dummy stack frame present"
+
+# Continuing now should exit the hand-call and pop the dummy frame.
+
+gdb_test "continue" "" "finish hand-call"
+
+gdb_test_multiple "maint print dummy-frames" "dummy frame popped" {
+ -re ".*stack=.*$gdb_prompt $" {
+ fail "all dummies popped"
+ }
+ -re ".*$gdb_prompt $" {
+ pass "all dummies popped"
+ }
+}
+
+# Continue one last time, the program should exit normally.
+
+gdb_test "continue" "Program exited normally." \
+ "continue to program exit"
+
+return 0