aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.threads
diff options
context:
space:
mode:
authorDoug Evans <xdje42@gmail.com>2014-09-13 21:44:00 -0700
committerDoug Evans <xdje42@gmail.com>2014-09-13 21:44:00 -0700
commit81219e5358e6238d3810136690a0c0b2cd20955e (patch)
tree19573767c0e0915e7e0fd670a0e544dcf26dfc3a /gdb/testsuite/gdb.threads
parentd4b38d2d057a5b8a35bc052e4f43b02b53c40f89 (diff)
downloadbinutils-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.c102
-rw-r--r--gdb/testsuite/gdb.threads/queue-signal.exp91
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