diff options
author | Doug Evans <xdje42@gmail.com> | 2014-09-13 21:44:00 -0700 |
---|---|---|
committer | Doug Evans <xdje42@gmail.com> | 2014-09-13 21:44:00 -0700 |
commit | 81219e5358e6238d3810136690a0c0b2cd20955e (patch) | |
tree | 19573767c0e0915e7e0fd670a0e544dcf26dfc3a /gdb/testsuite/gdb.threads | |
parent | d4b38d2d057a5b8a35bc052e4f43b02b53c40f89 (diff) | |
download | binutils-81219e5358e6238d3810136690a0c0b2cd20955e.zip binutils-81219e5358e6238d3810136690a0c0b2cd20955e.tar.gz binutils-81219e5358e6238d3810136690a0c0b2cd20955e.tar.bz2 |
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.
Diffstat (limited to 'gdb/testsuite/gdb.threads')
-rw-r--r-- | gdb/testsuite/gdb.threads/queue-signal.c | 102 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/queue-signal.exp | 91 |
2 files changed, 193 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.threads/queue-signal.c b/gdb/testsuite/gdb.threads/queue-signal.c new file mode 100644 index 0000000..147f2f7 --- /dev/null +++ b/gdb/testsuite/gdb.threads/queue-signal.c @@ -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; +} diff --git a/gdb/testsuite/gdb.threads/queue-signal.exp b/gdb/testsuite/gdb.threads/queue-signal.exp new file mode 100644 index 0000000..207073d --- /dev/null +++ b/gdb/testsuite/gdb.threads/queue-signal.exp @@ -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 |