This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH v2] Always pass signals to the right thread.


Hi Eli,

Could you take a look at the documentation bits of this patch?

Thanks!

Pedro Alves

On 06/24/2014 12:44 PM, Pedro Alves wrote:
> On 06/23/2014 03:28 AM, Yao Qi wrote:
>> On 06/19/2014 09:09 PM, Pedro Alves wrote:
>>>   (gdb) thread 1
>>>   [Switching to thread 1 (Thread 10979)]
>>>   (gdb) signal SIGUSR2
>>>   Note:
>>>     Thread 3 previously stopped with signal SIGUSR2, User defined signal 2.
>>>     Thread 2 previously stopped with signal SIGUSR1, User defined signal 1.
>>>   Continuing thread 1 (the current thread) with specified signal will
>>>   still deliver the signals noted above to their respective threads.
>>>   Continue anyway? (y or n)
>>
>> This message isn't clear to me, because user has to read this sentence
>> and think what GDB will do for these threads.
> 
> Well, stop and think is the point of the warning/query.  :-)
> 
>> IMO, it is better to say
>> explicitly what GDB will do for each thread, like
>>
>> (gdb) signal SIGUSR2
>>   Continuing Thread 3 with signal SIGUSR2, User defined signal 2.
>>   Continuing Thread 2 with signal SIGUSR1, User defined signal 1.
>>   Continuing Thread 1 with signal SIGUSR2, User defined signal 2.
>> Continue anyway? (y or n)
> 
> Thanks.  Even though it first appeared promising, I've been playing
> with this since yesterday and in the end I can't seem to find it
> really better.  :-/
> (I had already contemplated lots of different options before
> posting the original patch.)
> 
> That kind of reads to me like only those three threads
> are continued.  With my user hat on, that then makes me wonder what
> will happen to all the other threads not listed.  And why is GDB
> really telling about these signal that I didn't specify?  And which
> is the one that is getting the signal because I'm overriding
> it?  Thread 3 or thread 1?  And that output would seem to make more
> sense to me if we always asked for confirmation even if no thread
> other than the current would get a signal, otherwise, the user has
> no clue really why he's being asked for confirmation), but I'm not sure
> that'd be a good idea.  (Also, I think the output should still
> make sense if we ever find a need to add a cap on the number of
> threads listed and say "many threads" instead of showing a list.)
> 
> In any case, I still tried this direction, by doing two passes
> over the threads, to determine whether we need to ask for confirmation,
> and added an intro text:
> 
> (gdb) signal SIGUSR1
> Some of the threads other than the current that are also about to be resumed had previously stopped for signals.
> If you proceed, those signals will be delivered to the respective threads.
>   Continuing thread 3 with signal SIGUSR2.
>   Continuing thread 1 with signal SIGUSR1.
> Continue anyway? (y or n)
> 
> But, what about "signal 0" ?  That results in:
> 
> (gdb) signal 0
> Some of the threads other than the current that are also about to be resumed had previously stopped for signals.
> If you proceed, those signals will be delivered to the respective threads.
>   Continuing thread 3 with signal SIGUSR2.
>   Continuing thread 1 with no signal.
> Continue anyway? (y or n)
> 
> And again, that makes me wonder why "no signal" is mentioned for only
> one thread and not any other that will be resumed with no signal
> either (because it hadn't stopped with one).
> 
> We could then list what will happen to _all_ threads that will be
> resumed, with or without signal, but I _really_ don't think it's
> a good idea -- that set will usually be _much_ larger than the
> signalled threads, which will normally be just one.
> 
> So all in all, in the end, I still prefer my original output.
> 
>> We may need a NEWS entry, as this is a user-visible change.
> 
> Done.
> 
>>> +      ALL_NON_EXITED_THREADS (tp)
>>> +        {
>>
>> Replace eight spaces with a tab.
> 
> Thanks, fixed.
> 
>>
>>> @@ -5190,6 +5161,10 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
>>>  	 what keep_going does as well, if we call it.  */
>>>        ecs->event_thread->control.trap_expected = 0;
>>>  
>>> +      /* Likewise, clear the signal if it should not be passed.  */
>>> +      if (!signal_program[ecs->event_thread->suspend.stop_signal])
>>> +	ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
>>> +
>>
>> I can understand this patch except this part.  Why do we need to set
>> stop_signal here?
> 
> We can get here with the stop signal set to GDB_SIGNAL_TRAP due
> to a breakpoint of a finished step.  If we don't clear it now,
> the next time we'll resume the target, and this thread isn't
> current, we'd pass the trap to the thread.  See below for further
> explanation.
> 
>>
>>> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
>>> index c9677ca..8f2600d 100644
>>> --- a/gdb/linux-nat.c
>>> +++ b/gdb/linux-nat.c
>>> @@ -1694,8 +1694,7 @@ linux_nat_resume_callback (struct lwp_info *lp, void *except)
>>>        thread = find_thread_ptid (lp->ptid);
>>>        if (thread != NULL)
>>>  	{
>>> -	  if (signal_pass_state (thread->suspend.stop_signal))
>>> -	    signo = thread->suspend.stop_signal;
>>> +	  signo = thread->suspend.stop_signal;
>>>  	  thread->suspend.stop_signal = GDB_SIGNAL_0;
>>>  	}
> 
> 
>>
>> You didn't say much about these two changes.  Is it because we've
>> already set thread->suspend.stop_signal to GDB_SIGNAL_0 if
>> signal_pass_state (tp->suspend.stop_signal) is false,
> 
> ...
> 
>>
>> +  /* If this signal should not be seen by program, give it zero.
>> +     Used for debugging signals.  */
>> +  if (!signal_pass_state (tp->suspend.stop_signal))
>> +    tp->suspend.stop_signal = GDB_SIGNAL_0;
>> +
>>
>> that means (tp->suspend.stop_signal == GDB_SIGNAL_0 ||
>> signal_pass_state (tp->suspend.stop_signal)) is constantly true.
>> so in the callees of target_resume, signal_pass_state
>> (thread->suspend.stop_signal) is always true if
>> thread->suspend.stop_signal isn't GDB_SIGNAL_0?
> 
> Nope, "signal SIGFOO" should still pass SIGFOO even if
> "handle SIGFOO nopass".  So in that case, 
> signal_pass_state (thread->suspend.stop_signal) is false,
> but we should still pass stop_signal.  We assume the thread
> that was current at "signal SIGFOO" time will be resumed immediately,
> because GDB may need to e.g., step-over a breakpoint for another
> thread first.  So we store the intent to pass SIGFOO once the
> step over is finished in stop_signal.
> 
> I've added this comment:
> 
>  --- c/gdb/gdbthread.h
>  +++ w/gdb/gdbthread.h
>  @@ -135,7 +135,13 @@ struct thread_control_state
>  
>   struct thread_suspend_state
>   {
>  -  /* Last signal that the inferior received (why it stopped).  */
>  +  /* Last signal that the inferior received (why it stopped).  When
>  +     the thread is resumed, this signal is delivered.  Note: the
>  +     target should not check whether the signal is in pass state,
>  +     because the signal may have been explicitly passed with the
>  +     "signal" command, which overrides "handle nopass".  If the signal
>  +     should be suppressed, the core will take care of clearing this
>  +     before the target is resumed.  */
>     enum gdb_signal stop_signal;
>   };
> 
> Does that make things clearer ?
> 
> 
>>> +
>>> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
>>> +	 executable { debug }] != "" } {
>>> +    return -1
>>> +}
>>
>> Need [target_info exists gdb,nosignals] check?
> 
> Yeah, thanks.
> 
>>
>>> +
>>> +# Run the test proper.  STEP_OVER indicates whether we leave in place
>>> +# a breakpoint that needs to be stepped over when we explicitly
>>> +# request a signal be delivered with the "signal" command.
>>> +
>>> +proc test { step_over } {
>>> +    global srcfile binfile
>>> +
>>> +    with_test_prefix "step-over $step_over" {
>>> +	clean_restart ${binfile}
>>> +
>>> +	if ![runto_main] then {
>>> +	    fail "Can't run to main"
>>> +	    return 0
>>> +	}
>>> +
>>> +	gdb_test "handle SIGUSR1 stop print nopass"
>>> +
>>> +	gdb_test "b thread_function" "Breakpoint .* at .*$srcfile.*"
>>> +	gdb_test "continue" "thread_function.*" "stopped in thread"
>>> +
>>> +	# Thread 2 is stopped at a breakpoint.  If we leave the
>>> +	# breakpoint in place, GDB needs to move thread 2 past the
>>> +	# breakpoint before delivering the signal to thread 1.  We
>>> +	# want to be sure that GDB doesn't mistakenly deliver the
>>> +	# signal to thread 1 while doing that.
>>> +	if { $step_over == "no" } {
>>> +	    delete_breakpoints
>>> +	}
>>> +
>>> +	gdb_test "break handler" "Breakpoint .* at .*$srcfile.*"
>>> +
>>> +	gdb_test "thread 1" "Switching to thread 1.*"
>>> +
>>> +	set ws "\[ \t\]+"
>>> +	set line "\[^\r\n\]+"
>>> +
>>> +	set pattern ""
>>> +	append pattern "${ws}2${ws}Thread ${line}\r\n"
>>> +	append pattern "\\\* 1${ws}Thread ${line}.*"
>>> +
>>> +	gdb_test "info threads" $pattern "thread 1 selected"
>>
>> We want to know whether thread 1 is selected, so using "info threads 1"
>> is simpler in the regexp pattern, like
>>
>>  gdb_test "info threads 1" "\\\* 1${ws}Thread .*" "thread 1 selected"
> 
> The reason I don't like "info threads 1" this is that if the test fails,
> when you go look at the logs, you don't see which thread was actually
> current, which tends to make debugging things a little easier.   But we
> can still simplify the patterns.  I was aiming at having no wildcards in
> the middle of the output, but that's really not necessary.
> I've simplified the patterns now.
> 
> Here's the updated patch.
> 
> 8<-------------------------------
> From 5834f1fea7d303e3964751d6af505cac44fd1754 Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Tue, 24 Jun 2014 12:12:57 +0100
> Subject: [PATCH] Always pass signals to the right thread.
> 
> Currently, GDB can pass a signal to the wrong thread in several
> different but related scenarios.
> 
> E.g., if thread 1 stops for signal SIGFOO, the user switches to thread
> 2, and then issues "continue", SIGFOO is actually delivered to thread
> 2, not thread 1.  This obviously messes up programs that use
> pthread_kill to send signals to specific threads.
> 
> This has been a known issue for a long while.  Back in 2008 when I
> made stop_signal be per-thread (2020b7ab), I kept the behavior -- see
> code in 'proceed' being removed -- wanting to come back to it later.
> The time has finally come now.
> 
> The patch fixes this -- on resumption, intercepted signals are always
> delivered to the thread that had intercepted them.
> 
> Another example: if thread 1 stops for a breakpoint, the user switches
> to thread 2, and then issues "signal SIGFOO", SIGFOO is actually
> delivered to thread 1, not thread 2, because 'proceed' first switches
> to thread 1 to step over its breakpoint...  If the user deletes the
> breakpoint before issuing "signal FOO", then the signal is delivered
> to thread 2 (the current thread).
> 
> "signal SIGFOO" can be used for two things: inject a signal in the
> program while the program/thread had stopped for none, bypassing
> "handle nopass"; or changing/suppressing a signal the program had
> stopped for.  These scenarios are really two faces of the same coin,
> and GDB can't really guess what the user is trying to do.  GDB might
> have intercepted signals in more than one thread even (see the new
> signal-command-multiple-signals-pending.exp test).  At least in the
> inject case, it's obviously clear to me that the user means to deliver
> the signal to the currently selected thread, so best is to make the
> command's behavior consistent and easy to explain.
> 
> Then, if the user is trying to suppress/change a signal the program
> had stopped for instead of injecting a new signal, but, the user had
> changed threads meanwhile, then she will be surprised that with:
> 
>   (gdb) continue
>   Thread 1 stopped for signal SIGFOO.
>   (gdb) thread 2
>   (gdb) signal SIGBAR
> 
> ... GDB actually delivers SIGFOO to thread 1, and SIGBAR to thread 2
> (with scheduler-locking off, which is the default, because then
> "signal" or any other resumption command resumes all threads).
> 
> So the patch makes GDB detect that, and ask for confirmation:
> 
>   (gdb) thread 1
>   [Switching to thread 1 (Thread 10979)]
>   (gdb) signal SIGUSR2
>   Note:
>     Thread 3 previously stopped with signal SIGUSR2, User defined signal 2.
>     Thread 2 previously stopped with signal SIGUSR1, User defined signal 1.
>   Continuing thread 1 (the current thread) with specified signal will
>   still deliver the signals noted above to their respective threads.
>   Continue anyway? (y or n)
> 
> All these scenarios are covered by the new tests.
> 
> Tested on x86_64 Fedora 20, native and gdbserver.
> 
> gdb/
> 2014-06-24  Pedro Alves  <palves@redhat.com>
> 
> 	* NEWS: Mention signal passing and "signal" command changes.
> 
> 	* gdbthread.h (struct thread_suspend_state) <stop_signal>: Extend
> 	comment.
> 	* breakpoint.c (until_break_command): Adjust clear_proceed_status
> 	call.
> 	* infcall.c (run_inferior_call): Adjust clear_proceed_status call.
> 	* infcmd.c (proceed_thread_callback, continue_1, step_once)
> 	(jump_command): Adjust clear_proceed_status call.
> 	(signal_command): Warn if other thread that are resumed have
> 	signals that will be delivered.  Adjust clear_proceed_status call.
> 	(until_next_command, finish_command)
> 	(proceed_after_attach_callback, attach_command_post_wait)
> 	(attach_command): Adjust clear_proceed_status call.
> 	* infrun.c (proceed_after_vfork_done): Likewise.
> 	(proceed_after_attach_callback): Adjust comment.
> 	(clear_proceed_status_thread): Clear stop_signal if not in pass
> 	state.
> 	(clear_proceed_status_callback): Delete.
> 	(clear_proceed_status): New 'step' parameter.  Only clear the
> 	proceed status of threads the command being prepared is about to
> 	resume.
> 	(proceed): If passed in an explicit signal, override stop_signal
> 	with it.  Don't pass the last stop signal to the thread we're
> 	resuming.
> 	(init_wait_for_inferior): Adjust clear_proceed_status call.
> 	(switch_back_to_stepped_thread): Clear the signal if it should not
> 	be passed.
> 	* infrun.h (clear_proceed_status): New 'step' parameter.
> 	(user_visible_resume_ptid): Add comment.
> 	* linux-nat.c (linux_nat_resume_callback): Don't check whether the
> 	signal is in pass state.
> 	* remote.c (append_pending_thread_resumptions): Likewise.
> 	* mi/mi-main.c (proceed_thread): Adjust clear_proceed_status call.
> 
> 
> gdb/doc/
> 2014-06-24  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.texinfo (Signaling) <signal command>: Explain what happens
> 	with multi-threaded programs.
> 
> gdb/testsuite/
> 2014-06-24  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.threads/signal-command-handle-nopass.c: New file.
> 	* gdb.threads/signal-command-handle-nopass.exp: New file.
> 	* gdb.threads/signal-command-multiple-signals-pending.c: New file.
> 	* gdb.threads/signal-command-multiple-signals-pending.exp: New file.
> 	* gdb.threads/signal-delivered-right-thread.c: New file.
> 	* gdb.threads/signal-delivered-right-thread.exp: New file.
> ---
>  gdb/NEWS                                           |  11 ++
>  gdb/breakpoint.c                                   |   2 +-
>  gdb/doc/gdb.texinfo                                |  10 +-
>  gdb/gdbthread.h                                    |   8 +-
>  gdb/infcall.c                                      |   2 +-
>  gdb/infcmd.c                                       |  64 ++++++--
>  gdb/infrun.c                                       |  93 +++++-------
>  gdb/infrun.h                                       |   8 +-
>  gdb/linux-nat.c                                    |   3 +-
>  gdb/mi/mi-main.c                                   |   2 +-
>  gdb/remote.c                                       |   3 +-
>  .../gdb.threads/signal-command-handle-nopass.c     |  49 ++++++
>  .../gdb.threads/signal-command-handle-nopass.exp   |  78 ++++++++++
>  .../signal-command-multiple-signals-pending.c      |  98 ++++++++++++
>  .../signal-command-multiple-signals-pending.exp    | 166 +++++++++++++++++++++
>  .../gdb.threads/signal-delivered-right-thread.c    |  61 ++++++++
>  .../gdb.threads/signal-delivered-right-thread.exp  |  85 +++++++++++
>  17 files changed, 664 insertions(+), 79 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
>  create mode 100644 gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp
>  create mode 100644 gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c
>  create mode 100644 gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp
>  create mode 100644 gdb/testsuite/gdb.threads/signal-delivered-right-thread.c
>  create mode 100644 gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d9a19ae..2b61ff4 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,17 @@
>  
>  *** Changes since GDB 7.8
>  
> +* On resume, GDB now always passes the signal the program had stopped
> +  for to the thread the signal was sent to, even if the user changed
> +  threads before resuming.  Previously GDB would often (but not
> +  always) deliver the signal to the thread that happens to be current
> +  at resume time.
> +
> +* Conversely, the "signal" command now consistently delivers the
> +  requested signal to the current thread.  GDB now asks for
> +  confirmation if the program had stopped for a signal and the user
> +  switched threads meanwhile.
> +
>  *** Changes in GDB 7.8
>  
>  * New command line options
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index b04f3b7..d75e8c7 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -11670,7 +11670,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
>    int thread;
>    struct thread_info *tp;
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>  
>    /* Set a breakpoint where the user wants it and at return from
>       this function.  */
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 8588f73..d0afc3d 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -16546,7 +16546,7 @@ detail.
>  @table @code
>  @kindex signal
>  @item signal @var{signal}
> -Resume execution where your program stopped, but immediately give it the
> +Resume execution where your program is stopped, but immediately give it the
>  signal @var{signal}.  The @var{signal} can be the name or the number of a
>  signal.  For example, on many systems @code{signal 2} and @code{signal
>  SIGINT} are both ways of sending an interrupt signal.
> @@ -16557,6 +16557,14 @@ a signal and would ordinarily see the signal when resumed with the
>  @code{continue} command; @samp{signal 0} causes it to resume without a
>  signal.
>  
> +@emph{Note:} When debugging multi-threaded programs, @var{signal} is
> +delivered to the currently selected thread, not the thread that last
> +reported a stop.  If for example the program last stopped on account
> +of a signal in thread 2 and you want to continue execution suppressing
> +that signal, you must have thread 2 selected as current before issuing
> +the @samp{signal 0} command.  @value{GDBN} detects the scenario and
> +asks for confirmation if you had switched threads meanwhile.
> +
>  @code{signal} does not repeat when you press @key{RET} a second time
>  after executing the command.
>  @end table
> diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
> index 9ef74cd..522b674 100644
> --- a/gdb/gdbthread.h
> +++ b/gdb/gdbthread.h
> @@ -135,7 +135,13 @@ struct thread_control_state
>  
>  struct thread_suspend_state
>  {
> -  /* Last signal that the inferior received (why it stopped).  */
> +  /* Last signal that the inferior received (why it stopped).  When
> +     the thread is resumed, this signal is delivered.  Note: the
> +     target should not check whether the signal is in pass state,
> +     because the signal may have been explicitly passed with the
> +     "signal" command, which overrides "handle nopass".  If the signal
> +     should be suppressed, the core will take care of clearing this
> +     before the target is resumed.  */
>    enum gdb_signal stop_signal;
>  };
>  
> diff --git a/gdb/infcall.c b/gdb/infcall.c
> index 685b8a4..2aaac85 100644
> --- a/gdb/infcall.c
> +++ b/gdb/infcall.c
> @@ -391,7 +391,7 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
>  
>    call_thread->control.in_infcall = 1;
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>  
>    disable_watchpoints_before_interactive_call_start ();
>  
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index c4bb401..5c32c5c 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -679,7 +679,7 @@ proceed_thread_callback (struct thread_info *thread, void *arg)
>      return 0;
>  
>    switch_to_thread (thread->ptid);
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>    proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
>    return 0;
>  }
> @@ -745,7 +745,7 @@ continue_1 (int all_threads)
>      {
>        ensure_valid_thread ();
>        ensure_not_running ();
> -      clear_proceed_status ();
> +      clear_proceed_status (0);
>        proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
>      }
>  }
> @@ -1013,7 +1013,7 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
>  	 THREAD is set.  */
>        struct thread_info *tp = inferior_thread ();
>  
> -      clear_proceed_status ();
> +      clear_proceed_status (!skip_subroutines);
>        set_step_frame ();
>  
>        if (!single_inst)
> @@ -1186,7 +1186,7 @@ jump_command (char *arg, int from_tty)
>        printf_filtered (".\n");
>      }
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>    proceed (addr, GDB_SIGNAL_0, 0);
>  }
>  
> @@ -1245,6 +1245,50 @@ signal_command (char *signum_exp, int from_tty)
>  	oursig = gdb_signal_from_command (num);
>      }
>  
> +  /* Look for threads other than the current that this command ends up
> +     resuming too (due to schedlock off), and warn if they'll get a
> +     signal delivered.  "signal 0" is used to suppress a previous
> +     signal, but if the current thread is no longer the one that got
> +     the signal, then the user is potentially suppressing the signal
> +     of the wrong thread.  */
> +  if (!non_stop)
> +    {
> +      struct thread_info *tp;
> +      ptid_t resume_ptid;
> +      int must_confirm = 0;
> +
> +      /* This indicates what will be resumed.  Either a single thread,
> +	 a whole process, or all threads of all processes.  */
> +      resume_ptid = user_visible_resume_ptid (0);
> +
> +      ALL_NON_EXITED_THREADS (tp)
> +	{
> +	  if (ptid_equal (tp->ptid, inferior_ptid))
> +	    continue;
> +	  if (!ptid_match (tp->ptid, resume_ptid))
> +	    continue;
> +
> +	  if (tp->suspend.stop_signal != GDB_SIGNAL_0
> +	      && signal_pass_state (tp->suspend.stop_signal))
> +	    {
> +	      if (!must_confirm)
> +		printf_unfiltered (_("Note:\n"));
> +	      printf_unfiltered (_("  Thread %d previously stopped with signal %s, %s.\n"),
> +				 tp->num,
> +				 gdb_signal_to_name (tp->suspend.stop_signal),
> +				 gdb_signal_to_string (tp->suspend.stop_signal));
> +	      must_confirm = 1;
> +	    }
> +	}
> +
> +      if (must_confirm
> +	  && !query (_("Continuing thread %d (the current thread) with specified signal will\n"
> +		       "still deliver the signals noted above to their respective threads.\n"
> +		       "Continue anyway? "),
> +		     inferior_thread ()->num))
> +	error (_("Not confirmed."));
> +    }
> +
>    if (from_tty)
>      {
>        if (oursig == GDB_SIGNAL_0)
> @@ -1254,7 +1298,7 @@ signal_command (char *signum_exp, int from_tty)
>  			 gdb_signal_to_name (oursig));
>      }
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>    proceed ((CORE_ADDR) -1, oursig, 0);
>  }
>  
> @@ -1295,7 +1339,7 @@ until_next_command (int from_tty)
>    int thread = tp->num;
>    struct cleanup *old_chain;
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>    set_step_frame ();
>  
>    frame = get_current_frame ();
> @@ -1687,7 +1731,7 @@ finish_command (char *arg, int from_tty)
>    if (frame == 0)
>      error (_("\"finish\" not meaningful in the outermost frame."));
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>  
>    /* Finishing from an inline frame is completely different.  We don't
>       try to show the "return value" - no way to locate it.  So we do
> @@ -2299,7 +2343,7 @@ proceed_after_attach_callback (struct thread_info *thread,
>        && thread->suspend.stop_signal == GDB_SIGNAL_0)
>      {
>        switch_to_thread (thread->ptid);
> -      clear_proceed_status ();
> +      clear_proceed_status (0);
>        proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
>      }
>  
> @@ -2399,7 +2443,7 @@ attach_command_post_wait (char *args, int from_tty, int async_exec)
>  	{
>  	  if (inferior_thread ()->suspend.stop_signal == GDB_SIGNAL_0)
>  	    {
> -	      clear_proceed_status ();
> +	      clear_proceed_status (0);
>  	      proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
>  	    }
>  	}
> @@ -2502,7 +2546,7 @@ attach_command (char *args, int from_tty)
>    /* Set up execution context to know that we should return from
>       wait_for_inferior as soon as the target reports a stop.  */
>    init_wait_for_inferior ();
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>  
>    if (non_stop)
>      {
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index bef69a8..2626233 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -623,7 +623,7 @@ proceed_after_vfork_done (struct thread_info *thread,
>  			    target_pid_to_str (thread->ptid));
>  
>        switch_to_thread (thread->ptid);
> -      clear_proceed_status ();
> +      clear_proceed_status (0);
>        proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
>      }
>  
> @@ -1721,15 +1721,6 @@ maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc)
>    return hw_step;
>  }
>  
> -/* Return a ptid representing the set of threads that we will proceed,
> -   in the perspective of the user/frontend.  We may actually resume
> -   fewer threads at first, e.g., if a thread is stopped at a
> -   breakpoint that needs stepping-off, but that should not be visible
> -   to the user/frontend, and neither should the frontend/user be
> -   allowed to proceed any of the threads that happen to be stopped for
> -   internal run control handling, if a previous command wanted them
> -   resumed.  */
> -
>  ptid_t
>  user_visible_resume_ptid (int step)
>  {
> @@ -1757,6 +1748,12 @@ user_visible_resume_ptid (int step)
>        resume_ptid = inferior_ptid;
>      }
>  
> +  /* We may actually resume fewer threads at first, e.g., if a thread
> +     is stopped at a breakpoint that needs stepping-off, but that
> +     should not be visible to the user/frontend, and neither should
> +     the frontend/user be allowed to proceed any of the threads that
> +     happen to be stopped for internal run control handling, if a
> +     previous command wanted them resumed.  */
>    return resume_ptid;
>  }
>  
> @@ -2032,6 +2029,11 @@ clear_proceed_status_thread (struct thread_info *tp)
>  			"infrun: clear_proceed_status_thread (%s)\n",
>  			target_pid_to_str (tp->ptid));
>  
> +  /* If this signal should not be seen by program, give it zero.
> +     Used for debugging signals.  */
> +  if (!signal_pass_state (tp->suspend.stop_signal))
> +    tp->suspend.stop_signal = GDB_SIGNAL_0;
> +
>    tp->control.trap_expected = 0;
>    tp->control.step_range_start = 0;
>    tp->control.step_range_end = 0;
> @@ -2051,26 +2053,24 @@ clear_proceed_status_thread (struct thread_info *tp)
>    bpstat_clear (&tp->control.stop_bpstat);
>  }
>  
> -static int
> -clear_proceed_status_callback (struct thread_info *tp, void *data)
> -{
> -  if (is_exited (tp->ptid))
> -    return 0;
> -
> -  clear_proceed_status_thread (tp);
> -  return 0;
> -}
> -
>  void
> -clear_proceed_status (void)
> +clear_proceed_status (int step)
>  {
>    if (!non_stop)
>      {
> -      /* In all-stop mode, delete the per-thread status of all
> -	 threads, even if inferior_ptid is null_ptid, there may be
> -	 threads on the list.  E.g., we may be launching a new
> -	 process, while selecting the executable.  */
> -      iterate_over_threads (clear_proceed_status_callback, NULL);
> +      struct thread_info *tp;
> +      ptid_t resume_ptid;
> +
> +      resume_ptid = user_visible_resume_ptid (step);
> +
> +      /* In all-stop mode, delete the per-thread status of all threads
> +	 we're about to resume, implicitly and explicitly.  */
> +      ALL_NON_EXITED_THREADS (tp)
> +        {
> +	  if (!ptid_match (tp->ptid, resume_ptid))
> +	    continue;
> +	  clear_proceed_status_thread (tp);
> +	}
>      }
>  
>    if (!ptid_equal (inferior_ptid, null_ptid))
> @@ -2252,6 +2252,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
>        regcache_write_pc (regcache, addr);
>      }
>  
> +  if (siggnal != GDB_SIGNAL_DEFAULT)
> +    tp->suspend.stop_signal = siggnal;
> +
>    /* Record the interpreter that issued the execution command that
>       caused this thread to resume.  If the top level interpreter is
>       MI/async, and the execution command was a CLI command
> @@ -2318,38 +2321,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
>  
>    tp->control.trap_expected = tp->stepping_over_breakpoint;
>  
> -  if (!non_stop)
> -    {
> -      /* Pass the last stop signal to the thread we're resuming,
> -	 irrespective of whether the current thread is the thread that
> -	 got the last event or not.  This was historically GDB's
> -	 behaviour before keeping a stop_signal per thread.  */
> -
> -      struct thread_info *last_thread;
> -      ptid_t last_ptid;
> -      struct target_waitstatus last_status;
> -
> -      get_last_target_status (&last_ptid, &last_status);
> -      if (!ptid_equal (inferior_ptid, last_ptid)
> -	  && !ptid_equal (last_ptid, null_ptid)
> -	  && !ptid_equal (last_ptid, minus_one_ptid))
> -	{
> -	  last_thread = find_thread_ptid (last_ptid);
> -	  if (last_thread)
> -	    {
> -	      tp->suspend.stop_signal = last_thread->suspend.stop_signal;
> -	      last_thread->suspend.stop_signal = GDB_SIGNAL_0;
> -	    }
> -	}
> -    }
> -
> -  if (siggnal != GDB_SIGNAL_DEFAULT)
> -    tp->suspend.stop_signal = siggnal;
> -  /* If this signal should not be seen by program,
> -     give it zero.  Used for debugging signals.  */
> -  else if (!signal_program[tp->suspend.stop_signal])
> -    tp->suspend.stop_signal = GDB_SIGNAL_0;
> -
>    annotate_starting ();
>  
>    /* Make sure that output from GDB appears before output from the
> @@ -2443,7 +2414,7 @@ init_wait_for_inferior (void)
>  
>    breakpoint_init_inferior (inf_starting);
>  
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>  
>    target_last_wait_ptid = minus_one_ptid;
>  
> @@ -5190,6 +5161,10 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
>  	 what keep_going does as well, if we call it.  */
>        ecs->event_thread->control.trap_expected = 0;
>  
> +      /* Likewise, clear the signal if it should not be passed.  */
> +      if (!signal_program[ecs->event_thread->suspend.stop_signal])
> +	ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
> +
>        /* If scheduler locking applies even if not stepping, there's no
>  	 need to walk over threads.  Above we've checked whether the
>  	 current thread is stepping.  If some other thread not the
> diff --git a/gdb/infrun.h b/gdb/infrun.h
> index 66612a8..35b2246 100644
> --- a/gdb/infrun.h
> +++ b/gdb/infrun.h
> @@ -83,7 +83,11 @@ extern struct regcache *stop_registers;
>  
>  extern void start_remote (int from_tty);
>  
> -extern void clear_proceed_status (void);
> +/* Clear out all variables saying what to do when inferior is
> +   continued or stepped.  First do this, then set the ones you want,
> +   then call `proceed'.  STEP indicates whether we're preparing for a
> +   step/stepi command.  */
> +extern void clear_proceed_status (int step);
>  
>  extern void proceed (CORE_ADDR, enum gdb_signal, int);
>  
> @@ -91,6 +95,8 @@ extern void proceed (CORE_ADDR, enum gdb_signal, int);
>     Normally, use `proceed', which handles a lot of bookkeeping.  */
>  extern void resume (int, enum gdb_signal);
>  
> +/* Return a ptid representing the set of threads that we will proceed,
> +   in the perspective of the user/frontend.  */
>  extern ptid_t user_visible_resume_ptid (int step);
>  
>  extern void wait_for_inferior (void);
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index c9677ca..8f2600d 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -1694,8 +1694,7 @@ linux_nat_resume_callback (struct lwp_info *lp, void *except)
>        thread = find_thread_ptid (lp->ptid);
>        if (thread != NULL)
>  	{
> -	  if (signal_pass_state (thread->suspend.stop_signal))
> -	    signo = thread->suspend.stop_signal;
> +	  signo = thread->suspend.stop_signal;
>  	  thread->suspend.stop_signal = GDB_SIGNAL_0;
>  	}
>      }
> diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
> index 96dfc71..26389f1 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -253,7 +253,7 @@ proceed_thread (struct thread_info *thread, int pid)
>      return;
>  
>    switch_to_thread (thread->ptid);
> -  clear_proceed_status ();
> +  clear_proceed_status (0);
>    proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
>  }
>  
> diff --git a/gdb/remote.c b/gdb/remote.c
> index b5318f1..e56cfea 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -4629,8 +4629,7 @@ append_pending_thread_resumptions (char *p, char *endp, ptid_t ptid)
>    ALL_NON_EXITED_THREADS (thread)
>      if (ptid_match (thread->ptid, ptid)
>  	&& !ptid_equal (inferior_ptid, thread->ptid)
> -	&& thread->suspend.stop_signal != GDB_SIGNAL_0
> -	&& signal_pass_state (thread->suspend.stop_signal))
> +	&& thread->suspend.stop_signal != GDB_SIGNAL_0)
>        {
>  	p = append_resumption (p, endp, thread->ptid,
>  			       0, thread->suspend.stop_signal);
> diff --git a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
> new file mode 100644
> index 0000000..abca2f9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
> @@ -0,0 +1,49 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2014 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/>.  */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <signal.h>
> +
> +void
> +handler (int sig)
> +{
> +}
> +
> +void *
> +thread_function (void *arg)
> +{
> +  volatile unsigned int i = 1;
> +
> +  while (i != 0)
> +    usleep (1);
> +}
> +
> +int
> +main (void)
> +{
> +  pthread_t child_thread;
> +  int i;
> +
> +  signal (SIGUSR1, handler);
> +  pthread_create (&child_thread, NULL, thread_function, NULL);
> +  pthread_join (child_thread, NULL);
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp
> new file mode 100644
> index 0000000..3ea9cf8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp
> @@ -0,0 +1,78 @@
> +# Copyright (C) 2014 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 that an explicit "signal FOO" delivers FOO even if "handle" for
> +# that same signal is set to "nopass".  Also make sure the signal is
> +# delivered to the right thread, even if GDB has to step over a
> +# breakpoint in some other thread first.
> +
> +if [target_info exists gdb,nosignals] {
> +    verbose "Skipping ${testfile}.exp because of nosignals."
> +    return -1
> +}
> +
> +standard_testfile
> +
> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
> +	 executable { debug }] != "" } {
> +    return -1
> +}
> +
> +# Run the test proper.  STEP_OVER indicates whether we leave in place
> +# a breakpoint that needs to be stepped over when we explicitly
> +# request a signal be delivered with the "signal" command.
> +
> +proc test { step_over } {
> +    global srcfile binfile
> +
> +    with_test_prefix "step-over $step_over" {
> +	clean_restart ${binfile}
> +
> +	if ![runto_main] then {
> +	    fail "Can't run to main"
> +	    return 0
> +	}
> +
> +	gdb_test "handle SIGUSR1 stop print nopass"
> +
> +	gdb_test "b thread_function" "Breakpoint .* at .*$srcfile.*"
> +	gdb_test "continue" "thread_function.*" "stopped in thread"
> +
> +	# Thread 2 is stopped at a breakpoint.  If we leave the
> +	# breakpoint in place, GDB needs to move thread 2 past the
> +	# breakpoint before delivering the signal to thread 1.  We
> +	# want to be sure that GDB doesn't mistakenly deliver the
> +	# signal to thread 1 while doing that.
> +	if { $step_over == "no" } {
> +	    delete_breakpoints
> +	}
> +
> +	gdb_test "break handler" "Breakpoint .* at .*$srcfile.*"
> +
> +	gdb_test "thread 1" "Switching to thread 1.*"
> +
> +	set pattern "\\\* 1\[ \t\]+Thread.*"
> +
> +	gdb_test "info threads" $pattern "thread 1 selected"
> +
> +	gdb_test "signal SIGUSR1" "handler .*"
> +
> +	gdb_test "info threads" $pattern "thread 1 got the signal"
> +    }
> +}
> +
> +foreach stepover {"yes" "no"} {
> +    test $stepover
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c
> new file mode 100644
> index 0000000..2fc5f53
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c
> @@ -0,0 +1,98 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2014 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/>.  */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <signal.h>
> +
> +pthread_barrier_t barrier;
> +sig_atomic_t got_sigusr1;
> +sig_atomic_t got_sigusr2;
> +
> +void
> +handler_sigusr1 (int sig)
> +{
> +  got_sigusr1 = 1;
> +}
> +
> +void
> +handler_sigusr2 (int sig)
> +{
> +  got_sigusr2 = 1;
> +}
> +
> +void *
> +thread_function (void *arg)
> +{
> +  volatile unsigned int count = 1;
> +
> +  pthread_barrier_wait (&barrier);
> +
> +  while (count++ != 0)
> +    {
> +      if (got_sigusr1 && got_sigusr2)
> +	break;
> +      usleep (1);
> +    }
> +}
> +
> +void
> +all_threads_started (void)
> +{
> +}
> +
> +void
> +all_threads_signalled (void)
> +{
> +}
> +
> +void
> +end (void)
> +{
> +}
> +
> +int
> +main (void)
> +{
> +  pthread_t child_thread[2];
> +  int i;
> +
> +  signal (SIGUSR1, handler_sigusr1);
> +  signal (SIGUSR2, handler_sigusr2);
> +
> +  pthread_barrier_init (&barrier, NULL, 3);
> +
> +  for (i = 0; i < 2; i++)
> +    pthread_create (&child_thread[i], NULL, thread_function, NULL);
> +
> +  pthread_barrier_wait (&barrier);
> +
> +  all_threads_started ();
> +
> +  pthread_kill (child_thread[0], SIGUSR1);
> +  pthread_kill (child_thread[1], SIGUSR2);
> +
> +  all_threads_signalled ();
> +
> +  for (i = 0; i < 2; i++)
> +    pthread_join (child_thread[i], NULL);
> +
> +  end ();
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp
> new file mode 100644
> index 0000000..b5ec00a
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp
> @@ -0,0 +1,166 @@
> +# Copyright (C) 2014 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 that "signal FOO" behaves correctly when we have multiple
> +# threads that have stopped for a signal.
> +
> +if [target_info exists gdb,nosignals] {
> +    verbose "Skipping ${testfile}.exp because of nosignals."
> +    return -1
> +}
> +
> +standard_testfile
> +
> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
> +	 executable { debug }] != "" } {
> +    return -1
> +}
> +
> +# Run the test proper.  SCHEDLOCK indicates which variant (around
> +# scheduler-locking) of the test to perform.
> +
> +proc test { schedlock } {
> +    global srcfile binfile
> +
> +    with_test_prefix "schedlock $schedlock" {
> +	clean_restart ${binfile}
> +
> +	if ![runto_main] then {
> +	    fail "Can't run to main"
> +	    return 0
> +	}
> +
> +	gdb_test "handle SIGUSR1 stop print pass"
> +	gdb_test "handle SIGUSR2 stop print pass"
> +
> +	gdb_test "break all_threads_started" "Breakpoint .* at .*$srcfile.*"
> +	gdb_test "continue" "all_threads_started.*"
> +
> +	# Using schedlock, let the main thread queue a signal for each
> +	# non-main thread.
> +	gdb_test_no_output "set scheduler-locking on"
> +
> +	gdb_test "break all_threads_signalled" "Breakpoint .* at .*$srcfile.*"
> +	gdb_test "continue" "all_threads_signalled.*"
> +
> +	gdb_test "info threads" "\\\* 1\[ \t\]+Thread.*" "thread 1 selected"
> +
> +	# With schedlock still enabled, let each thread report its
> +	# signal.
> +
> +	gdb_test "thread 3" "Switching to thread 3.*"
> +	gdb_test "continue" "Program received signal SIGUSR2.*" "stop with SIGUSR2"
> +	gdb_test "thread 2" "Switching to thread 2.*"
> +	gdb_test "continue" "Program received signal SIGUSR1.*" "stop with SIGUSR1"
> +
> +	gdb_test "break handler_sigusr1" "Breakpoint .* at .*$srcfile.*"
> +	gdb_test "break handler_sigusr2" "Breakpoint .* at .*$srcfile.*"
> +
> +	set handler_re "Breakpoint .*, handler_sigusr. \\(sig=.*\\) at .*"
> +
> +	# Now test the "signal" command with either scheduler locking
> +	# enabled or disabled.
> +
> +	if { $schedlock == "off" } {
> +	    # With scheduler locking off, switch to the main thread
> +	    # and issue "signal 0".  "signal 0" should then warn that
> +	    # two threads have signals that will be delivered.  When
> +	    # we let the command proceed, a signal should be
> +	    # delivered, and thus the corresponding breakpoint in the
> +	    # signal handler should trigger.
> +
> +	    gdb_test_no_output "set scheduler-locking off"
> +	    gdb_test "thread 1" "Switching to thread 1.*"
> +
> +	    set queried 0
> +	    set test "signal command queries"
> +	    gdb_test_multiple "signal 0" $test {
> +		-re "stopped with.*stopped with.*stopped with.*Continue anyway.*y or n. $" {
> +		    fail "$test (too many threads noted)"
> +		    set queried 1
> +		}
> +		-re "stopped with signal SIGUSR.*\r\nContinuing .*still deliver .*Continue anyway.*y or n. $" {
> +		    pass $test
> +		    set queried 1
> +		}
> +		-re "Continue anyway.*y or n. $" {
> +		    fail "$test (no threads noted)"
> +		    set queried 1
> +		}
> +	    }
> +
> +	    # Continuing should stop in one of the signal handlers.
> +	    # Which thread runs first is not determinate.
> +	    if {$queried} {
> +		gdb_test "y" "$handler_re" "one signal delivered"
> +	    }
> +
> +	    # Continuing a second time should stop in the other
> +	    # handler.
> +	    with_test_prefix "second signal" {
> +		gdb_test "continue" "$handler_re" "signal delivered"
> +	    }
> +	} else {
> +	    # With scheduler locking on, stay with thread 2 selected,
> +	    # and try to deliver its signal explicitly.  The "signal"
> +	    # command should then warn that one other thread has a
> +	    # signal that will be delivered.  When we let the command
> +	    # proceed, the current thread's signal should be
> +	    # delivered, and thus the corresponding breakpoint in the
> +	    # signal handler should trigger.
> +	    gdb_test "signal SIGUSR1" \
> +		"Breakpoint .*, handler_sigusr1 \\(sig=.*\\) at .*" \
> +		"signal command does not query, signal delivered"
> +
> +	    with_test_prefix "second signal" {
> +		# The other thread had stopped for a signal too, and
> +		# it wasn't resumed yet.  Disabling schedlock and
> +		# trying "signal 0" from the main thread should warn
> +		# again.
> +		gdb_test_no_output "set scheduler-locking off"
> +
> +		set queried 0
> +		set test "signal command queries"
> +		gdb_test_multiple "signal 0" $test {
> +		    -re "stopped with.*stopped with.*Continue anyway.*y or n. $" {
> +			fail "$test (too many threads noted)"
> +			set queried 1
> +		    }
> +		    -re "stopped with signal SIGUSR.*\r\nContinuing .*still deliver .*Continue anyway.*y or n. $" {
> +			pass $test
> +			set queried 1
> +		    }
> +		    -re "Continue anyway.*y or n. $" {
> +			fail "$test (no threads noted)"
> +			set queried 1
> +		    }
> +		}
> +
> +		if {$queried} {
> +		    gdb_test "y" "Breakpoint .*, handler_sigusr2 \\(sig=.*\\) at .*" "signal delivered"
> +		}
> +	    }
> +	}
> +
> +	# Both threads got their signal.  Continuing again should
> +	# neither intercept nor deliver any other signal.
> +	gdb_test "b end" "Breakpoint .* at .*$srcfile.*"
> +	gdb_test "continue" "end .*" "no more signals"
> +    }
> +}
> +
> +foreach schedlock {"off" "on"} {
> +    test $schedlock
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-delivered-right-thread.c b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.c
> new file mode 100644
> index 0000000..66b68a8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.c
> @@ -0,0 +1,61 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2014 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/>.  */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <signal.h>
> +
> +pthread_barrier_t barrier;
> +
> +void
> +handler (int sig)
> +{
> +}
> +
> +void *
> +thread_function (void *arg)
> +{
> +  pthread_barrier_wait (&barrier);
> +
> +  while (1)
> +    usleep (1);
> +}
> +
> +int
> +main (void)
> +{
> +  pthread_t child_thread[2];
> +  int i;
> +
> +  signal (SIGUSR1, handler);
> +
> +  pthread_barrier_init (&barrier, NULL, 3);
> +
> +  for (i = 0; i < 2; i++)
> +    pthread_create (&child_thread[i], NULL, thread_function, NULL);
> +
> +  pthread_barrier_wait (&barrier);
> +
> +  pthread_kill (child_thread[0], SIGUSR1);
> +
> +  for (i = 0; i < 2; i++)
> +    pthread_join (child_thread[i], NULL);
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp
> new file mode 100644
> index 0000000..4243495
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp
> @@ -0,0 +1,85 @@
> +# Copyright (C) 2014 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 [target_info exists gdb,nosignals] {
> +    verbose "Skipping ${testfile}.exp because of nosignals."
> +    return -1
> +}
> +
> +standard_testfile
> +
> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
> +	 executable { debug }] != "" } {
> +    return -1
> +}
> +
> +# Run test proper.  COMMAND indicates whether to resume the inferior
> +# with "signal 0" or "continue".
> +
> +proc test { command } {
> +    global srcfile binfile
> +
> +    with_test_prefix "$command" {
> +	clean_restart ${binfile}
> +
> +	if ![runto_main] then {
> +	    fail "Can't run to main"
> +	    return 0
> +	}
> +
> +	gdb_test "handle SIGUSR1 stop print pass"
> +
> +	gdb_test "continue" "Program received signal SIGUSR1.*" "stop with SIGUSR1"
> +
> +	set pattern "\\\* 2\[ \t\]+Thread.*"
> +
> +	gdb_test "info threads" $pattern "thread 2 intercepted signal"
> +
> +	gdb_test "break handler" "Breakpoint .* at .*$srcfile.*"
> +
> +	gdb_test "thread 1" "Switching to thread 1.*"
> +
> +	if { $command == "continue" } {
> +	    gdb_test "continue" "handler .*"
> +	} elseif { $command == "signal 0" } {
> +	    set queried 0
> +	    set test "signal 0 queries"
> +	    gdb_test_multiple "signal 0" $test {
> +		-re "stopped with.*stopped with.*Continue anyway.*y or n. $" {
> +		    fail "$test (multiple threads noted)"
> +		    set queried 1
> +		}
> +		-re "stopped with signal SIGUSR1.*\r\nContinuing .*still deliver .*Continue anyway.*y or n. $" {
> +		    pass $test
> +		    set queried 1
> +		}
> +		-re "Continue anyway.*y or n. $" {
> +		    fail "$test (no threads noted)"
> +		    set queried 1
> +		}
> +	    }
> +
> +	    if {$queried} {
> +		gdb_test "y" "handler .*" "signal is delivered"
> +	    }
> +	}
> +
> +	gdb_test "info threads" $pattern "thread 2 got the signal"
> +    }
> +}
> +
> +foreach command {"continue" "signal 0"} {
> +    test $command
> +}
> 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]