New command queue-signal.
If I want to change the signalled state of multiple threads it's a bit cumbersome to do with the "signal" command. What you really want is a way to set the signal state of the desired threads and then just do "continue". This patch adds a new command, queue-signal, to accomplish this. Basically "signal N" == "queue-signal N" + "continue". That's not precisely true in that "signal" can be used to inject any signal, including signals set to "nopass"; whereas "queue-signal" just queues the signal as if the thread stopped because of it. "nopass" handling is done when the thread is resumed which "queue-signal" doesn't do. One could add extra complexity to allow queue-signal to be used to deliver "nopass" signals like the "signal" command. I have no current need for it so in the interests of incremental complexity, I have left such support out and just have the code flag an error if one tries to queue a nopass signal. gdb/ChangeLog: * NEWS: Mention new "queue-signal" command. * infcmd.c (queue_signal_command): New function. (_initialize_infcmd): Add new queue-signal command. gdb/doc/ChangeLog: * gdb.texinfo (Signaling): Document new queue-signal command. gdb/testsuite/ChangeLog: * gdb.threads/queue-signal.c: New file. * gdb.threads/queue-signal.exp: New file.
This commit is contained in:
parent
d4b38d2d05
commit
81219e5358
@ -1,3 +1,9 @@
|
||||
2014-09-13 Doug Evans <xdje42@gmail.com>
|
||||
|
||||
* NEWS: Mention new "queue-signal" command.
|
||||
* infcmd.c (queue_signal_command): New function.
|
||||
(_initialize_infcmd): Add new queue-signal command.
|
||||
|
||||
2014-09-13 Doug Evans <xdje42@gmail.com>
|
||||
|
||||
* linux-nat.c (wait_lwp): Add debugging printf.
|
||||
|
5
gdb/NEWS
5
gdb/NEWS
@ -13,6 +13,11 @@
|
||||
** $_any_caller_is(name [, number_of_frames])
|
||||
** $_any_caller_matches(regexp [, number_of_frames])
|
||||
|
||||
* New commands
|
||||
|
||||
queue-signal signal-name-or-number
|
||||
Queue a signal to be delivered to the thread when it is resumed.
|
||||
|
||||
* 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
|
||||
|
@ -1,3 +1,7 @@
|
||||
2014-09-13 Doug Evans <xdje42@gmail.com>
|
||||
|
||||
* gdb.texinfo (Signaling): Document new queue-signal command.
|
||||
|
||||
2014-09-06 Doug Evans <xdje42@gmail.com>
|
||||
|
||||
PR 15276
|
||||
|
@ -16630,17 +16630,38 @@ same thread before issuing the @samp{signal 0} command. If you issue
|
||||
the @samp{signal 0} command with another thread as the selected one,
|
||||
@value{GDBN} detects that and asks for confirmation.
|
||||
|
||||
@code{signal} does not repeat when you press @key{RET} a second time
|
||||
after executing the command.
|
||||
@end table
|
||||
@c @end group
|
||||
|
||||
Invoking the @code{signal} command is not the same as invoking the
|
||||
@code{kill} utility from the shell. Sending a signal with @code{kill}
|
||||
causes @value{GDBN} to decide what to do with the signal depending on
|
||||
the signal handling tables (@pxref{Signals}). The @code{signal} command
|
||||
passes the signal directly to your program.
|
||||
|
||||
@code{signal} does not repeat when you press @key{RET} a second time
|
||||
after executing the command.
|
||||
|
||||
@kindex queue-signal
|
||||
@item queue-signal @var{signal}
|
||||
Queue @var{signal} to be delivered immediately to the current thread
|
||||
when execution of the thread resumes. 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.
|
||||
The handling of the signal must be set to pass the signal to the program,
|
||||
otherwise @value{GDBN} will report an error.
|
||||
You can control the handling of signals from @value{GDBN} with the
|
||||
@code{handle} command (@pxref{Signals}).
|
||||
|
||||
Alternatively, if @var{signal} is zero, any currently queued signal
|
||||
for the current thread is discarded and when execution resumes no signal
|
||||
will be delivered. This is useful when your program stopped on account
|
||||
of a signal and would ordinarily see the signal when resumed with the
|
||||
@code{continue} command.
|
||||
|
||||
This command differs from the @code{signal} command in that the signal
|
||||
is just queued, execution is not resumed. And @code{queue-signal} cannot
|
||||
be used to pass a signal whose handling state has been set to @code{nopass}
|
||||
(@pxref{Signals}).
|
||||
@end table
|
||||
@c @end group
|
||||
|
||||
@node Returning
|
||||
@section Returning from a Function
|
||||
|
59
gdb/infcmd.c
59
gdb/infcmd.c
@ -1300,6 +1300,46 @@ signal_command (char *signum_exp, int from_tty)
|
||||
proceed ((CORE_ADDR) -1, oursig, 0);
|
||||
}
|
||||
|
||||
/* Queue a signal to be delivered to the current thread. */
|
||||
|
||||
static void
|
||||
queue_signal_command (char *signum_exp, int from_tty)
|
||||
{
|
||||
enum gdb_signal oursig;
|
||||
struct thread_info *tp;
|
||||
|
||||
ERROR_NO_INFERIOR;
|
||||
ensure_not_tfind_mode ();
|
||||
ensure_valid_thread ();
|
||||
ensure_not_running ();
|
||||
|
||||
if (signum_exp == NULL)
|
||||
error_no_arg (_("signal number"));
|
||||
|
||||
/* It would be even slicker to make signal names be valid expressions,
|
||||
(the type could be "enum $signal" or some such), then the user could
|
||||
assign them to convenience variables. */
|
||||
oursig = gdb_signal_from_name (signum_exp);
|
||||
|
||||
if (oursig == GDB_SIGNAL_UNKNOWN)
|
||||
{
|
||||
/* No, try numeric. */
|
||||
int num = parse_and_eval_long (signum_exp);
|
||||
|
||||
if (num == 0)
|
||||
oursig = GDB_SIGNAL_0;
|
||||
else
|
||||
oursig = gdb_signal_from_command (num);
|
||||
}
|
||||
|
||||
if (oursig != GDB_SIGNAL_0
|
||||
&& !signal_pass_state (oursig))
|
||||
error (_("Signal handling set to not pass this signal to the program."));
|
||||
|
||||
tp = inferior_thread ();
|
||||
tp->suspend.stop_signal = oursig;
|
||||
}
|
||||
|
||||
/* Continuation args to be passed to the "until" command
|
||||
continuation. */
|
||||
struct until_next_continuation_args
|
||||
@ -3008,7 +3048,24 @@ The SIGNAL argument is processed the same as the handle command.\n\
|
||||
\n\
|
||||
An argument of \"0\" means continue the program without sending it a signal.\n\
|
||||
This is useful in cases where the program stopped because of a signal,\n\
|
||||
and you want to resume the program while discarding the signal."));
|
||||
and you want to resume the program while discarding the signal.\n\
|
||||
\n\
|
||||
In a multi-threaded program the signal is delivered to, or discarded from,\n\
|
||||
the current thread only."));
|
||||
set_cmd_completer (c, signal_completer);
|
||||
|
||||
c = add_com ("queue-signal", class_run, queue_signal_command, _("\
|
||||
Queue a signal to be delivered to the current thread when it is resumed.\n\
|
||||
Usage: queue-signal SIGNAL\n\
|
||||
The SIGNAL argument is processed the same as the handle command.\n\
|
||||
It is an error if the handling state of SIGNAL is \"nopass\".\n\
|
||||
\n\
|
||||
An argument of \"0\" means remove any currently queued signal from\n\
|
||||
the current thread. This is useful in cases where the program stopped\n\
|
||||
because of a signal, and you want to resume it while discarding the signal.\n\
|
||||
\n\
|
||||
In a multi-threaded program the signal is queued with, or discarded from,\n\
|
||||
the current thread only."));
|
||||
set_cmd_completer (c, signal_completer);
|
||||
|
||||
add_com ("stepi", class_run, stepi_command, _("\
|
||||
|
@ -1,3 +1,8 @@
|
||||
2014-09-13 Doug Evans <xdje42@gmail.com>
|
||||
|
||||
* gdb.threads/queue-signal.c: New file.
|
||||
* gdb.threads/queue-signal.exp: New file.
|
||||
|
||||
2014-09-13 Doug Evans <xdje42@gmail.com>
|
||||
|
||||
* lib/gdb.exp (gdb_prompt): Add comment and change initial value to
|
||||
|
102
gdb/testsuite/gdb.threads/queue-signal.c
Normal file
102
gdb/testsuite/gdb.threads/queue-signal.c
Normal file
@ -0,0 +1,102 @@
|
||||
/* 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 <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Used to individually advance each thread to the desired stopping point. */
|
||||
int ready;
|
||||
|
||||
sig_atomic_t sigusr1_received;
|
||||
sig_atomic_t sigusr2_received;
|
||||
sig_atomic_t sigabrt_received;
|
||||
|
||||
static void
|
||||
sigusr1_handler (int sig)
|
||||
{
|
||||
sigusr1_received = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
sigusr2_handler (int sig)
|
||||
{
|
||||
sigusr2_received = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
sigabrt_handler (int sig)
|
||||
{
|
||||
sigabrt_received = 1;
|
||||
}
|
||||
|
||||
static void *
|
||||
sigusr1_thread_function (void *unused)
|
||||
{
|
||||
while (!ready)
|
||||
usleep (100);
|
||||
pthread_kill (pthread_self (), SIGUSR1);
|
||||
}
|
||||
|
||||
static void *
|
||||
sigusr2_thread_function (void *unused)
|
||||
{
|
||||
while (!ready)
|
||||
usleep (100);
|
||||
/* pthread_kill (pthread_self (), SIGUSR2); - manually injected by gdb */
|
||||
}
|
||||
|
||||
static void
|
||||
all_threads_running (void)
|
||||
{
|
||||
while (!ready)
|
||||
usleep (100);
|
||||
}
|
||||
|
||||
static void
|
||||
all_threads_done (void)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
pthread_t sigusr1_thread, sigusr2_thread;
|
||||
|
||||
/* Protect against running forever. */
|
||||
alarm (60);
|
||||
|
||||
signal (SIGUSR1, sigusr1_handler);
|
||||
signal (SIGUSR2, sigusr2_handler);
|
||||
signal (SIGABRT, sigabrt_handler);
|
||||
|
||||
/* Don't let any thread advance past initialization. */
|
||||
ready = 0;
|
||||
|
||||
pthread_create (&sigusr1_thread, NULL, sigusr1_thread_function, NULL);
|
||||
pthread_create (&sigusr2_thread, NULL, sigusr2_thread_function, NULL);
|
||||
all_threads_running ();
|
||||
|
||||
pthread_kill (pthread_self (), SIGABRT);
|
||||
|
||||
pthread_join (sigusr1_thread, NULL);
|
||||
pthread_join (sigusr2_thread, NULL);
|
||||
all_threads_done ();
|
||||
|
||||
return 0;
|
||||
}
|
91
gdb/testsuite/gdb.threads/queue-signal.exp
Normal file
91
gdb/testsuite/gdb.threads/queue-signal.exp
Normal file
@ -0,0 +1,91 @@
|
||||
# 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/>.
|
||||
|
||||
standard_testfile
|
||||
|
||||
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
|
||||
executable { debug }] != "" } {
|
||||
return -1
|
||||
}
|
||||
|
||||
clean_restart ${binfile}
|
||||
|
||||
if ![runto_main] {
|
||||
fail "Can't run to main"
|
||||
return 0
|
||||
}
|
||||
|
||||
gdb_test "handle SIGUSR1 stop print pass"
|
||||
gdb_test "handle SIGUSR2 stop print pass"
|
||||
gdb_test "handle SIGABRT stop print pass"
|
||||
|
||||
gdb_breakpoint "all_threads_running"
|
||||
gdb_continue_to_breakpoint "all_threads_running"
|
||||
|
||||
# Find out which of threads 2,3 are for sigusr1,2.
|
||||
set sigusr1_thread 0
|
||||
set sigusr2_thread 0
|
||||
gdb_test "thread 2"
|
||||
gdb_test_multiple "bt" "determine thread functions" {
|
||||
-re "sigusr1.*$gdb_prompt $" {
|
||||
set sigusr1_thread 2
|
||||
set sigusr2_thread 3
|
||||
}
|
||||
-re "sigusr2.*$gdb_prompt $" {
|
||||
set sigusr1_thread 3
|
||||
set sigusr2_thread 2
|
||||
}
|
||||
}
|
||||
|
||||
# No point in continuing if we couldn't figure out which thread is which.
|
||||
if { $sigusr1_thread == 0 } {
|
||||
# FAIL already recorded.
|
||||
return 0
|
||||
}
|
||||
|
||||
# Advance each thread to where we want them one at a time.
|
||||
gdb_test_no_output "set scheduler-locking on"
|
||||
gdb_test_no_output "set var ready = 1"
|
||||
|
||||
# Thread sigusr1_thread gets a SIGUSR1 which we leave alone.
|
||||
gdb_test "thread $sigusr1_thread" ""
|
||||
gdb_test "continue" "SIGUSR1.*"
|
||||
|
||||
# Inject SIGUSR2 into thread sigusr2_thread.
|
||||
gdb_test "thread $sigusr2_thread" ""
|
||||
gdb_test_no_output "queue-signal SIGUSR2"
|
||||
|
||||
# The main thread gets SIGABRT which we then throw away.
|
||||
gdb_test "thread 1" ""
|
||||
gdb_test "continue" "SIGABRT.*"
|
||||
gdb_test_no_output "queue-signal 0"
|
||||
|
||||
# Now let every thread run.
|
||||
gdb_test_no_output "set scheduler-locking off"
|
||||
|
||||
gdb_breakpoint "all_threads_done"
|
||||
gdb_continue_to_breakpoint "all_threads_done"
|
||||
|
||||
# Verify SIGUSR1, SIGUSR2 were received, and SIGABRT was discarded.
|
||||
gdb_test "p sigusr1_received" "= 1"
|
||||
gdb_test "p sigusr2_received" "= 1"
|
||||
gdb_test "p sigabrt_received" "= 0"
|
||||
|
||||
# Before we finish up verify the queueing of nopass signals flags an error.
|
||||
gdb_test "queue-signal SIGINT" \
|
||||
"Signal handling set to not pass this signal to the program."
|
||||
|
||||
# Verify program is able to finish.
|
||||
gdb_continue_to_end
|
Loading…
Reference in New Issue
Block a user