aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Evans <dje@google.com>2015-12-10 12:00:30 -0800
committerDoug Evans <dje@google.com>2015-12-10 12:00:30 -0800
commitd41bc77ef18d738827e67e9becd3acf406f23e8e (patch)
treee722ccb0893fd4a0540b4168f6aafa9c943903a3
parent9f70033d635ff2b0fdf87b6bebc5be5e18afbcd4 (diff)
downloadfsf-binutils-gdb-d41bc77ef18d738827e67e9becd3acf406f23e8e.zip
fsf-binutils-gdb-d41bc77ef18d738827e67e9becd3acf406f23e8e.tar.gz
fsf-binutils-gdb-d41bc77ef18d738827e67e9becd3acf406f23e8e.tar.bz2
patch ../102427192.patch
-rw-r--r--README.google29
-rw-r--r--gdb/NEWS5
-rw-r--r--gdb/doc/gdb.texinfo9
-rw-r--r--gdb/gdbthread.h3
-rw-r--r--gdb/infcmd.c179
-rw-r--r--gdb/testsuite/gdb.base/async-shell.exp4
-rw-r--r--gdb/testsuite/gdb.base/dprintf-non-stop.exp4
-rw-r--r--gdb/testsuite/gdb.base/interrupt-noterm.exp4
-rw-r--r--gdb/testsuite/gdb.threads/interrupt-command.c63
-rw-r--r--gdb/testsuite/gdb.threads/interrupt-command.exp465
-rw-r--r--gdb/thread.c12
11 files changed, 753 insertions, 24 deletions
diff --git a/README.google b/README.google
index 95f505c..2ac5ac8 100644
--- a/README.google
+++ b/README.google
@@ -129,3 +129,32 @@ they are an ongoing maintenance burden.
+ Ref# 2180247
+ * infcmd.c (attach_command_post_wait): Pass zero to
+ post_create_inferior, same as run_command_1.
+--- README.google 2015-09-05 16:24:43.000000000 -0700
++++ README.google 2015-09-05 16:33:32.000000000 -0700
++
++2015-09-05 Doug Evans <google.com>
++
++ Ref# 13734692, submitted upstream
++ PR gdb/14236
++ * NEWS: Mention change in default behaviour of "interrupt" command.
++ * infcmd.c (struct interrupt_command_continuation_args): New struct.
++ (interrupt_command_continuation_free_args): New function.
++ (wait_thread_stopped): New function.
++ (interrupt_command_post_wait): New function.
++ (interrupt_command_continuation): New function.
++ (add_interrupt_continuation): New function.
++ (interrupt_command): Rewrite.
++
++ doc/
++ * gdb.texinfo (Background Execution): Document new option "&" for
++ "interrupt" command.
++
++ testsuite/
++ * gdb.base/async-shell.exp: Add "&" to "interrupt" command.
++ * gdb.base/dprintf-non-stop.exp: Ditto.
++ * gdb.base/interrupt-noterm.exp: Ditto.
++ * gdb.threads/interrupt-command.c: New file.
++ * gdb.threads/interrupt-command.exp: New file.
++
++ * gdbthread.h (any_running): Declare.
++ * thread.c (any_running): New function.
diff --git a/gdb/NEWS b/gdb/NEWS
index 5689def..26f328d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -417,6 +417,11 @@ VAX running Ultrix vax-*-ultrix*
and "assf"), have been removed. Use the "sharedlibrary" command, or
its alias "share", instead.
+*** Changes since GDB 7.8
+
+* The "interrupt" command now waits for the inferior to stop by default.
+ To avoid waiting for the inferior to stop pass "&".
+
*** Changes in GDB 7.8
* New command line options
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4477048..b073152 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6040,13 +6040,14 @@ using the @code{interrupt} command.
@table @code
@kindex interrupt
-@item interrupt
-@itemx interrupt -a
-
+@item interrupt @r{[}-a@r{]} @r{[}&@r{]}
Suspend execution of the running program. In all-stop mode,
@code{interrupt} stops the whole process, but in non-stop mode, it stops
only the current thread. To stop the whole program in non-stop mode,
-use @code{interrupt -a}.
+use @code{interrupt -a}. The @code{-a} option is ignored in all-stop mode.
+By default, the @code{interrupt} command waits for the thread to stop,
+or if the @code{-a} option is specified all threads.
+To not wait for the thread to stop pass @code{&}.
@end table
@node Thread-Specific Breakpoints
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index fefad48..488f1cd 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -426,6 +426,9 @@ extern int is_exited (ptid_t ptid);
/* In the frontend's perpective, is this thread stopped? */
extern int is_stopped (ptid_t ptid);
+/* In the frontend's perpective is there any thread running? */
+extern int any_running (void);
+
/* Marks thread PTID as executing, or not. If PTID is minus_one_ptid,
marks all threads.
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 76ed5c2..276aefc 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2848,29 +2848,180 @@ interrupt_target_1 (int all_threads)
set_stop_requested (ptid, 1);
}
-/* interrupt [-a]
- Stop the execution of the target while running in async mode, in
- the backgound. In all-stop, stop the whole process. In non-stop
- mode, stop the current thread only by default, or stop all threads
- if the `-a' switch is used. */
+/* Data associated with an "interrupt" command continuation. */
+
+struct interrupt_command_continuation_args
+{
+ /* This is the thread to wait for or minus_one_ptid to wait for all
+ threads. */
+ ptid_t wait_for_ptid;
+};
+
+/* continuation_free_arg_ftype function for the interrupt command. */
+
+static void
+interrupt_command_continuation_free_args (void *args)
+{
+ struct interrupt_command_continuation_args *a = args;
+
+ xfree (a);
+}
+
+/* Wait for thread PTID to stop, or all threads if it is minus_one_ptid.
+ This is used in non-stop mode to implement "interrupt -a" without
+ complicating the infrun state machine more than it already is. */
+
+static void
+wait_thread_stopped (ptid_t ptid)
+{
+ struct cleanup* cleanups = make_cleanup_restore_current_thread ();
+ int all_threads = ptid_equal (ptid, minus_one_ptid);
+
+ gdb_assert (non_stop);
+
+ while (all_threads
+ ? any_running ()
+ : is_running (ptid))
+ {
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ QUIT;
+ wait_for_inferior ();
+ get_last_target_status (&last_ptid, &last);
+ finish_thread_state (last_ptid);
+ /* Leave printing of the stop event of PTID to normal_stop. */
+ if (!all_threads && !ptid_equal (last_ptid, ptid))
+ print_stop_event (&last);
+ }
+
+ do_cleanups (cleanups);
+
+ if (!all_threads && !target_can_async_p ())
+ normal_stop ();
+}
+
+/* Finish up waiting for thread WAIT_FOR_PTID to stop, or continue waiting
+ for all threads to stop if it is minus_one_ptid.
+ Note: This function is named the way it is as it's derived from
+ attach_command_post_wait. */
+
+static void
+interrupt_command_post_wait (ptid_t wait_for_ptid)
+{
+ struct inferior *inferior;
+
+ inferior = current_inferior ();
+ inferior->control.stop_soon = NO_STOP_QUIETLY;
+
+ /* If this is all-stop we don't have to do anything special to wait for all
+ threads to stop. */
+ if (non_stop)
+ wait_thread_stopped (wait_for_ptid);
+}
+
+/* continuation_ftype function for the interrupt command. */
+
+static void
+interrupt_command_continuation (void *args, int err)
+{
+ struct interrupt_command_continuation_args *a = args;
+
+ if (err)
+ return;
+
+ interrupt_command_post_wait (a->wait_for_ptid);
+}
+
+/* Queue a continuation to complete the interrupt command.
+ WAIT_FOR_PTID is minus_one_ptid if waiting for all threads to stop. */
+
+static void
+add_interrupt_continuation (ptid_t wait_for_ptid)
+{
+ struct interrupt_command_continuation_args *a;
+
+ a = xmalloc (sizeof (*a));
+ a->wait_for_ptid = wait_for_ptid;
+
+ /* We add the continuation to the inferior, even if we're only waiting for
+ a single thread, because we don't know which thread the event loop will
+ see first. */
+ add_inferior_continuation (interrupt_command_continuation, a,
+ interrupt_command_continuation_free_args);
+}
+
+/* interrupt [-a] [&]
+ Stop the execution of the target.
+ In all-stop, stop the whole process. In non-stop mode, stop only
+ the current thread by default, or stop all threads if the "-a"
+ switch is used. "-a" is ignored in all-stop mode. */
static void
interrupt_command (char *args, int from_tty)
{
- if (target_can_async_p ())
+ int async_exec;
+ int all_threads = 0;
+
+ /* Otherwise subsequent invocations will just throw an error. */
+ dont_repeat ();
+
+ ERROR_NO_INFERIOR;
+ ensure_not_tfind_mode ();
+
+ args = strip_bg_char (args, &async_exec);
+
+ if (args != NULL
+ && startswith (args, "-a"))
+ all_threads = 1;
+
+ /* Done with ARGS. */
+ xfree (args);
+
+ if (all_threads)
+ {
+ if (!any_running ())
+ error (_("All threads are already stopped."));
+ }
+ else
{
- int all_threads = 0;
+ if (!is_running (inferior_ptid))
+ error (_("Current thread is already stopped."));
+ }
+
+ /* If we're in synchronous all-stop mode, the above error checking for
+ whether threads are already stopped should have fired. */
+ gdb_assert (non_stop || target_can_async_p ());
- dont_repeat (); /* Not for the faint of heart. */
+ /* This will flag an error if & is given in non-async mode.
+ This isn't an execution command per se, but this performs what we
+ need. */
+ prepare_execution_command (&current_target, async_exec);
- if (args != NULL
- && startswith (args, "-a"))
- all_threads = 1;
+ interrupt_target_1 (all_threads);
+
+ if (async_exec)
+ return;
- if (!non_stop && all_threads)
- error (_("-a is meaningless in all-stop mode."));
+ /* Set the terminal to the inferior while we're waiting for it to stop.
+ One reason to do this is because if we don't in target-async mode,
+ after we return rl_linefunc will get set to NULL. If we leave the
+ terminal as ours then if the user types a command while gdb is
+ waiting for the inferior, readline will get called to process the
+ command and will abort because rl_linefunc is NULL. This is
+ fragile, but apparently exists as a workaround for a readline
+ issue. Grep for "trick readline" in display_gdb_prompt.
+ This must be done after calling async_disable_stdin (which is called
+ by prepare_execution_command), or it will "early exit" without doing
+ anything. */
+ target_terminal_inferior ();
- interrupt_target_1 (all_threads);
+ if (target_can_async_p ())
+ add_interrupt_continuation (all_threads ? minus_one_ptid : inferior_ptid);
+ else
+ {
+ gdb_assert (non_stop);
+ wait_thread_stopped (all_threads ? minus_one_ptid : inferior_ptid);
}
}
diff --git a/gdb/testsuite/gdb.base/async-shell.exp b/gdb/testsuite/gdb.base/async-shell.exp
index 718dccc..585943a 100644
--- a/gdb/testsuite/gdb.base/async-shell.exp
+++ b/gdb/testsuite/gdb.base/async-shell.exp
@@ -39,9 +39,9 @@ gdb_test "run &" "Starting program: \[^\r\n\]*(\r\n$gdbindex_warning_re)?"
gdb_test "shell echo foo" "foo"
-set test "interrupt"
+set test "interrupt &"
gdb_test_multiple $test $test {
- -re "interrupt\r\n$gdb_prompt " {
+ -re "interrupt &\r\n$gdb_prompt " {
pass $test
}
}
diff --git a/gdb/testsuite/gdb.base/dprintf-non-stop.exp b/gdb/testsuite/gdb.base/dprintf-non-stop.exp
index 4e3e5bc..401c94e 100644
--- a/gdb/testsuite/gdb.base/dprintf-non-stop.exp
+++ b/gdb/testsuite/gdb.base/dprintf-non-stop.exp
@@ -51,9 +51,9 @@ gdb_expect {
# Now test that we're still able to issue commands. GDB used to
# implement re-resuming from dprintfs with a synchronous "continue" in
# the dprintf's command list, which stole the prompt from the user.
-set test "interrupt"
+set test "interrupt &"
gdb_test_multiple $test $test {
- -re "interrupt\r\n$gdb_prompt " {
+ -re "interrupt &\r\n$gdb_prompt " {
pass $test
}
}
diff --git a/gdb/testsuite/gdb.base/interrupt-noterm.exp b/gdb/testsuite/gdb.base/interrupt-noterm.exp
index ee55d33..7e1e158 100644
--- a/gdb/testsuite/gdb.base/interrupt-noterm.exp
+++ b/gdb/testsuite/gdb.base/interrupt-noterm.exp
@@ -58,9 +58,9 @@ if { $async_supported < 0 } {
# With native debugging, and no terminal (emulated by interactive-mode
# off, above), GDB had a bug where "interrupt" would send SIGINT to
# its own process group, instead of the inferior's.
-set test "interrupt"
+set test "interrupt &"
gdb_test_multiple $test $test {
- -re "interrupt\r\n$gdb_prompt " {
+ -re "interrupt &\r\n$gdb_prompt " {
pass $test
}
}
diff --git a/gdb/testsuite/gdb.threads/interrupt-command.c b/gdb/testsuite/gdb.threads/interrupt-command.c
new file mode 100644
index 0000000..506142e
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/interrupt-command.c
@@ -0,0 +1,63 @@
+/* Test the "interrupt" command.
+
+ Copyright 2014 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 <stdlib.h>
+#include <unistd.h>
+
+#ifndef NR_THREADS
+#define NR_THREADS 4
+#endif
+
+pthread_t threads[NR_THREADS];
+
+static void *
+thread_entry (void *unused)
+{
+ while (1)
+ sleep (1);
+}
+
+static void
+all_threads_running (void)
+{
+}
+
+static void
+do_something (void)
+{
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+
+ alarm (60);
+
+ for (i = 0; i < NR_THREADS; ++i)
+ pthread_create (&threads[i], NULL, thread_entry, NULL);
+
+ all_threads_running ();
+
+ while (1)
+ do_something ();
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/interrupt-command.exp b/gdb/testsuite/gdb.threads/interrupt-command.exp
new file mode 100644
index 0000000..c97a99c
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/interrupt-command.exp
@@ -0,0 +1,465 @@
+# 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/>.
+
+set NR_THREADS 4
+
+standard_testfile
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "additional_flags=-DNR_THREADS=$NR_THREADS"]] != "" } {
+ return -1
+}
+
+proc prepare_interrupt_test { target_async non_stop } {
+ global binfile
+ clean_restart ${binfile}
+
+ gdb_test_no_output "set target-async ${target_async}" "set async mode"
+ gdb_test_no_output "set non-stop ${non_stop}" "set non-stop mode"
+
+ if ![runto_main] {
+ untested "could not run to main"
+ return -1
+ }
+
+ gdb_breakpoint "all_threads_running"
+ gdb_continue_to_breakpoint "all_threads_running"
+}
+
+proc interrupt_all_stop { command } {
+ global gdb_prompt
+
+ set test_name $command
+ gdb_test_multiple $command "" {
+ -re "Program received signal SIGINT.*$gdb_prompt $" {
+ pass $test_name
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+}
+
+# Apparently the state of a thread isn't necessarily "running" immediately
+# after doing "c &". This feels like a bug, but for robustness sake it's a
+# good thing to verify anyway.
+# THREAD_NR is either a thread number or "all".
+
+proc wait_thread_running { thread_nr } {
+ global gdb_prompt
+ set nr_attempts 4
+
+ set test_name "wait thread $thread_nr running"
+ for { set i 0 } { $i < $nr_attempts } { incr i } {
+ if { "$thread_nr" == "all" } {
+ set running -1
+ } else {
+ set running -1
+ send_gdb "info thread $thread_nr\n"
+ gdb_expect {
+ -re "\n\[ \]*$thread_nr +Thread \[^\r\]+\\(running\\)\[\r\n\]+$gdb_prompt $" {
+ set running 1
+ }
+ -re "\n\[ \]*$thread_nr +Thread \[^\r\]+\[\r\n\]+$gdb_prompt $" {
+ set running 0
+ }
+ -re "$gdb_prompt $" {
+ verbose -log "unexpected output processing info thread output"
+ }
+ timeout {
+ verbose -log "unexpected timeout processing info thread output"
+ }
+ }
+ if { $running < 0 } {
+ fail "$test_name (error processing info threads output)"
+ return
+ }
+ if { $running } {
+ pass $test_name
+ return
+ }
+ }
+ }
+
+ fail "$test_name (thread still not running after $nr_attempts attempts)"
+}
+
+proc verify_current_thread { thread_nr } {
+ global gdb_prompt
+
+ set test_name "verify thread $thread_nr current"
+ set correct_current_thread 0
+ gdb_test_multiple "info threads" $test_name {
+ -re "\n\[*\]\[ \]+$thread_nr +Thread \[^\r\]+\r" {
+ set correct_current_thread 1
+ exp_continue
+ }
+ -re "\n\[*\]?\[ \]+\[\[:digit:\]\]+ +Thread \[^\r\]+\r" {
+ exp_continue
+ }
+ -re "$gdb_prompt $" {
+ if { $correct_current_thread } {
+ pass $test_name
+ } else {
+ fail $test_name
+ }
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+}
+
+proc interrupt_all_threads_all_stop_background { command } {
+ global gdb_prompt
+
+ set command "$command &"
+ set test_name $command
+ # This has to watch for gdb_prompt separately from SIGINT because
+ # other cases added will catch gdb_prompt before we recognize SIGINT.
+ gdb_test_multiple $command $test_name {
+ -re "$gdb_prompt " {
+ exp_continue
+ }
+ -re "\[\r\n\]+Program received signal SIGINT" {
+ pass $test_name
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+}
+
+proc interrupt_and_verify_one_thread_non_stop { ampersand } {
+ global gdb_prompt NR_THREADS
+
+ for { set i 0 } { $i < $NR_THREADS } { incr i } {
+ set thread_nr [expr 2 + $i]
+
+ gdb_test "thread $thread_nr" \
+ "Switching to thread $thread_nr.*running.*"
+
+ set test_name [string trim "interrupt $ampersand"]
+ set test_name "$test_name, thread $thread_nr"
+ gdb_test_multiple "interrupt $ampersand" $test_name {
+ -re "$gdb_prompt " {
+ if { "$ampersand" == "&" } {
+ exp_continue
+ }
+ pass $test_name
+ }
+ -re "\n\[^\r\]+Thread \[^\r\]+ stopped\[^\r\]+\r" {
+ if { "$ampersand" != "&" } {
+ exp_continue
+ }
+ pass $test_name
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+
+ set test_name "thread $thread_nr stopped"
+ gdb_test_multiple "info thread $thread_nr" $test_name {
+ -re "running.*$gdb_prompt $" {
+ fail $test_name
+ }
+ -re "$gdb_prompt $" {
+ pass $test_name
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ set i $NR_THREADS
+ }
+ }
+ }
+}
+
+proc interrupt_all_threads_non_stop { command ampersand nr_threads_running } {
+ global gdb_prompt NR_THREADS
+
+ set thread_stopped_count 0
+ set test_name [string trim "$command $ampersand"]
+
+ gdb_test_multiple "$command $ampersand" $test_name {
+ -re "\n\[^\r\]+Thread \[^\r\]+ stopped\[^\r\]+\r" {
+ verbose -log "got stopped thread"
+ incr thread_stopped_count
+ if { "$ampersand" == "&" } {
+ if { $thread_stopped_count == $nr_threads_running } {
+ pass $test_name
+ } else {
+ exp_continue
+ }
+ } else {
+ exp_continue
+ }
+ }
+ -re "\nNo unwaited-for children left.\[\r\n\]+$gdb_prompt $" {
+ # This appeared during development. Ensure it doesn't come back.
+ fail "$test_name (No unwaited-for children left)"
+ }
+ -re "$gdb_prompt " {
+ if { "$ampersand" == "&" } {
+ exp_continue
+ }
+ if { $thread_stopped_count != $nr_threads_running } {
+ fail "$test_name (missing thread)"
+ } else {
+ pass $test_name
+ }
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+}
+
+proc verify_all_threads_stopped { } {
+ global gdb_prompt NR_THREADS
+
+ set test_name "info threads"
+ set running_count 0
+ set thread_count 0
+
+ gdb_test_multiple "info threads" $test_name {
+ -re "\n\[ \]*\[\[:digit:\]\]+ +Thread \[^\r\]+\\(running\\)\r" {
+ verbose -log "got running thread"
+ incr running_count
+ incr thread_count
+ exp_continue
+ }
+ -re "\n\[*\]?\[ \]*\[\[:digit:\]\]+ +Thread \[^\r\]+\r" {
+ verbose -log "got stopped thread"
+ incr thread_count
+ exp_continue
+ }
+ -re "$gdb_prompt $" {
+ if { $thread_count != [expr $NR_THREADS + 1] } {
+ fail "$test_name (missing thread)"
+ } elseif { $running_count != 0 } {
+ fail "$test_name (running thread)"
+ } else {
+ pass $test_name
+ }
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+}
+
+proc interrupt_and_verify_all_threads_non_stop { ampersand } {
+ global gdb_prompt NR_THREADS
+
+ # These tests are repeated just for stress testing purposes.
+
+ for { set i 0 } { $i < $NR_THREADS } { incr i } {
+ set thread_nr [expr 2 + $i]
+ with_test_prefix "thread ${thread_nr}" {
+ gdb_test "c -a &" "Continuing."
+ gdb_test "thread $thread_nr" \
+ "Switching to thread $thread_nr.*running.*"
+ interrupt_all_threads_non_stop "interrupt -a" $ampersand [expr $NR_THREADS + 1]
+ verify_all_threads_stopped
+ }
+ }
+}
+
+proc test_sync_all_stop { } {
+ with_test_prefix "sync, all-stop" {
+ prepare_interrupt_test off off
+
+ gdb_test "interrupt" "Current thread is already stopped."
+ gdb_test "interrupt -a" "All threads are already stopped."
+ }
+}
+
+proc test_async_all_stop { } {
+ global NR_THREADS
+
+ with_test_prefix "async, all-stop" {
+ prepare_interrupt_test on off
+
+ # Do these tests several times because there was a bug during
+ # development when doing two in a row.
+
+ for { set i 0 } { $i < $NR_THREADS } { incr i } {
+ set thread_nr [expr 2 + $i]
+ with_test_prefix "interrupt, thread ${thread_nr}" {
+ gdb_test "c &" "Continuing."
+ wait_thread_running $thread_nr
+ gdb_test "thread ${thread_nr}" \
+ "Switching to thread ${thread_nr}.*running.*"
+ interrupt_all_stop "interrupt"
+ verify_all_threads_stopped
+ }
+ }
+
+ for { set i 0 } { $i < $NR_THREADS } { incr i } {
+ set thread_nr [expr 2 + $i]
+ with_test_prefix "interrupt -a, thread ${thread_nr}" {
+ gdb_test "c &" "Continuing."
+ wait_thread_running $thread_nr
+ gdb_test "thread ${thread_nr}" \
+ "Switching to thread ${thread_nr}.*running.*"
+ interrupt_all_stop "interrupt -a"
+ verify_all_threads_stopped
+ }
+ }
+ }
+
+ with_test_prefix "async, all-stop, &" {
+ prepare_interrupt_test on off
+
+ # These tests are repeated just for stress testing purposes.
+
+ for { set i 0 } { $i < $NR_THREADS } { incr i } {
+ set thread_nr [expr 2 + $i]
+ with_test_prefix "interrupt, thread ${thread_nr}" {
+ gdb_test "c &" "Continuing."
+ wait_thread_running $thread_nr
+ gdb_test "thread ${thread_nr}" \
+ "Switching to thread ${thread_nr}.*running.*"
+ interrupt_all_threads_all_stop_background "interrupt"
+ verify_all_threads_stopped
+ }
+ }
+
+ for { set i 0 } { $i < $NR_THREADS } { incr i } {
+ set thread_nr [expr 2 + $i]
+ with_test_prefix "interrupt -a, thread ${thread_nr}" {
+ gdb_test "c &" "Continuing."
+ wait_thread_running $thread_nr
+ gdb_test "thread ${thread_nr}" \
+ "Switching to thread ${thread_nr}.*running.*"
+ interrupt_all_threads_all_stop_background "interrupt -a"
+ verify_all_threads_stopped
+ }
+ }
+ }
+}
+
+proc test_sync_non_stop { } {
+ global NR_THREADS
+
+ with_test_prefix "sync, non-stop, one thread at a time" {
+ prepare_interrupt_test off on
+
+ interrupt_and_verify_one_thread_non_stop ""
+ }
+
+ with_test_prefix "sync, non-stop, all threads" {
+ prepare_interrupt_test off on
+
+ # IWBN to use interrupt_and_verify_all_threads_non_stop here,
+ # but it uses & to continually resume the threads, which we can't do.
+ # So we just test once, until we know the extra coverage is needed
+ # here. One suggestions is to put a breakpoint on do_something.
+ interrupt_all_threads_non_stop "interrupt -a" "" $NR_THREADS
+ verify_all_threads_stopped
+ }
+}
+
+proc test_async_non_stop { } {
+ global NR_THREADS
+
+ with_test_prefix "async, non-stop, one thread at a time" {
+ prepare_interrupt_test on on
+
+ interrupt_and_verify_one_thread_non_stop ""
+ }
+
+ with_test_prefix "async, non-stop, one thread at a time, &" {
+ prepare_interrupt_test on on
+
+ interrupt_and_verify_one_thread_non_stop "&"
+ }
+
+ with_test_prefix "async, non-stop, all threads" {
+ prepare_interrupt_test on on
+
+ interrupt_and_verify_all_threads_non_stop ""
+ }
+
+ with_test_prefix "async, non-stop, all threads, &" {
+ prepare_interrupt_test on on
+
+ interrupt_and_verify_all_threads_non_stop "&"
+ }
+}
+
+test_sync_all_stop
+test_async_all_stop
+test_sync_non_stop
+test_async_non_stop
+
+# That's it for standard testing.
+# The rest of the file is specialized testing.
+
+# Verify "interrupt" waits for the right thread in non-stop.
+# This test is inheritantly racy, even if it succeeds we're not testing
+# the right things to verify success wasn't a fluke. But it has been
+# quite useful in practice.
+
+proc test_right_thread_stopped { } {
+ global gdb_prompt
+
+ with_test_prefix "test_right_thread_stopped" {
+ prepare_interrupt_test on on
+ # At this point thread 1 is stopped, threads 2-5 are running.
+ gdb_test_multiple "define interrupt2" "define user command: interrupt2" {
+ -re "Type commands for definition of \"interrupt2\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+ gdb_test "thread 2\ninterrupt &\nthread 3\ninterrupt\nend" "" \
+ "define user command: interrupt2"
+ }
+ }
+ set test_name "interrupt2"
+ set prompt_seen 0
+ set thread2_stopped 0
+ set thread3_stopped 0
+ # Turn on command tracing, makes it easier to match output to the
+ # commands in the interrupt2 macro.
+ gdb_test_no_output "set trace-commands on"
+ gdb_test_multiple "interrupt2" $test_name {
+ -re "\[\r\n\]+\[^\r\]+Thread \[^\r\]+ #3 stopped\[^\r\]+\[\r\n\]+" {
+ # Note: There may also be a notification that thread 2 stopped
+ # before we see the gdb prompt. However, it can also occur
+ # after the prompt: be careful not to add a regexp for thread 2
+ # here lest it swallow the prompt!
+ verbose -log "saw thread 3"
+ set thread3_stopped 1
+ exp_continue
+ }
+ -re "$gdb_prompt " {
+ # Don't put a trailing $ on the regexp here.
+ # If expect buffers the thread 2 stopped output, we lose.
+ if { $thread3_stopped } {
+ pass $test_name
+ } else {
+ fail "$test_name (thread 3 not stopped before prompt)"
+ }
+ }
+ timeout {
+ fail "$test_name (timeout)"
+ }
+ }
+ # We can't use gdb_test_no_output here because the command is traced.
+ gdb_test "set trace-commands off" ".*"
+ # One last thing, verify thread 3 is still the current thread.
+ verify_current_thread 3
+ }
+}
+
+test_right_thread_stopped
diff --git a/gdb/thread.c b/gdb/thread.c
index 23dfcc9..4c957db 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -837,6 +837,18 @@ is_running (ptid_t ptid)
}
int
+any_running (void)
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->state == THREAD_RUNNING)
+ return 1;
+
+ return 0;
+}
+
+int
is_executing (ptid_t ptid)
{
struct thread_info *tp;