From patchwork Sun Mar 30 22:05:16 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 345 Return-Path: X-Original-To: siddhesh@wilcox.dreamhost.com Delivered-To: siddhesh@wilcox.dreamhost.com Received: from homiemail-mx22.g.dreamhost.com (mx2.sub5.homie.mail.dreamhost.com [208.113.200.128]) by wilcox.dreamhost.com (Postfix) with ESMTP id 0209336047B for ; Sun, 30 Mar 2014 15:05:25 -0700 (PDT) Received: by homiemail-mx22.g.dreamhost.com (Postfix, from userid 14314964) id A6DA44FABE25; Sun, 30 Mar 2014 15:05:25 -0700 (PDT) X-Original-To: gdb@patchwork.siddhesh.in Delivered-To: x14314964@homiemail-mx22.g.dreamhost.com Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by homiemail-mx22.g.dreamhost.com (Postfix) with ESMTPS id 674094FABE5D for ; Sun, 30 Mar 2014 15:05:25 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:mime-version :content-type; q=dns; s=default; b=Wf6L0bOYXJ1QWcIgrW7+/QZhfUX3i wc70YAu5cYjxLHU6KaYhkBlXSmAkCKL8e1apzp9NM5Pzm1UdZuBbQXiL1P0svGRB 6r56WEj6Lt+SGAnWpy8QRAv4/CKtKRAblyubTczpYod/tH1rtWYPXrKRwNZV2klL mvlJ6PInjQYFj0= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:mime-version :content-type; s=default; bh=3zmamNrd1YF+R8aukfdZcLpGJ/o=; b=FxY vzVnvH7ciba/XLSHgKeU4EgAP4zDT2fYMw2ne2W+tdDJVupv+4q3mvcUkzjU3jlE WN+Xy2bbUnZiFoHCKu0/XwFZdRSUUN2VEz/u5/6vcyT3uxPQfGZGOxbsK2XL/KE1 zTz8PMapTPNwvvnbbYD4ZvQvF4ZaIJuYrEkUQDnA= Received: (qmail 25628 invoked by alias); 30 Mar 2014 22:05:22 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 25600 invoked by uid 89); 30 Mar 2014 22:05:21 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.2 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-oa0-f74.google.com Received: from mail-oa0-f74.google.com (HELO mail-oa0-f74.google.com) (209.85.219.74) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Sun, 30 Mar 2014 22:05:19 +0000 Received: by mail-oa0-f74.google.com with SMTP id i7so1475242oag.1 for ; Sun, 30 Mar 2014 15:05:17 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-type; bh=Frnj0Dz65WQYlSHLoHRM2o5RiLHWLKfPQBIxeIV7wwI=; b=aRmfOYYA5+g2N2aSYYwNHKTrY1CwncRHJB8QxvQidkpPV0Nh51wFnT2/fuYkMSVIb8 KJD90mAp664IPrgJsf3Mpns9YG1ol4XiL1vqyq74t++uqesX+TKuzbbl0CX8IkUfLKCE tTIGNtk+zFlPlIPcZ8NrZGa5lic+QeX0qfYAF4jOvsCJzeyxn6aJ7z88Nhh4V3BYx8ny 9GRMU/GYAupNw2LeBYwAlR5F12a+h7tyKI/lpcjjy+elhyt2C8DeTYhHZAWIkhiiaI47 PBds5bTGefqJ2xl7+0pNL3R4X22AyBJln7Wy6HQHHEC10DNrYv6U6GH83UgE//A9R9jY ZY6g== X-Gm-Message-State: ALoCoQlS4eFJmgu4TpYTYTniRSOCrZy6sfpBQEe/yZiYFrvfa0hvIcawmbekXHCVmmBRoygsTI4x7pgwaA59ISm7Hzh0op76+4FUA+7KDcrw/VDhfJJ8jkeVcFSPBqoqHeluUFa0qR2I4qJT62KVKCRpeJVmzAONg27ZRuDZjvqOahpw4xOQ6anBRrGnH1qMiKr3HyFVQmBhH3M5+mP16MAdAVepl18ZmIJ1ywdG9O/sF0h6g7erwuQ= X-Received: by 10.182.186.105 with SMTP id fj9mr8720667obc.5.1396217117169; Sun, 30 Mar 2014 15:05:17 -0700 (PDT) Received: from corp2gmr1-2.hot.corp.google.com (corp2gmr1-2.hot.corp.google.com [172.24.189.93]) by gmr-mx.google.com with ESMTPS id x22si2304299yhd.5.2014.03.30.15.05.17 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 30 Mar 2014 15:05:17 -0700 (PDT) Received: from ruffy.mtv.corp.google.com (ruffy.mtv.corp.google.com [172.17.128.44]) by corp2gmr1-2.hot.corp.google.com (Postfix) with ESMTP id B03655A421E for ; Sun, 30 Mar 2014 15:05:16 -0700 (PDT) From: Doug Evans To: gdb-patches@sourceware.org Subject: [PATCH, doc RFA]: Fix pr 14236 Date: Sun, 30 Mar 2014 15:05:16 -0700 Message-ID: MIME-Version: 1.0 X-IsSubscribed: yes X-DH-Original-To: gdb@patchwork.siddhesh.in Hi. This patch is a proposed(!) fix to bug 14236. The problem here is that the "interrupt" command (figuratively speaking) takes an implicit & as an argument (it is always asynchronous) and users aren't expecting this. This patch fixes the problem by changing the behaviour by making "interrupt" synchronous by default and supporting a new option &, to be consistent with every other gdb command (which is what I think every user would expect, including this user). Alas, while I think this is the correct fix (*1), if one could set aside breaking existing code, the latter, obviously, is important too. I had to update a few testcases that expected the old behaviour, but I don't, in this particular case, have a feel for whether the change in behaviour would break anything outside of gdb's testsuite. Without a "wait" command the current behaviour is completely useless in scripts, for example. I have patches in the works to fix this in other ways. E.g., regardless of how "interrupt" is fixed I think we also want a "wait" command. But for now I'd like to know what the community thinks of this approach. (*1): gdb's infrun state machine doesn't support waiting for all threads to stop in non-stop mode, AFAICT, and I'm loathe to make it more complicated just to handle this. Therefore the continuation that handles completing the interrupt command waits for any remaining threads itself. Alas, I'm not sure if there's a technical reason why this patch can't work. It is derived, in part, from the attach support, and it too waits for threads outside of the main event loop. 2014-03-30 Doug Evans 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_all_threads_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. diff --git a/gdb/NEWS b/gdb/NEWS index 2a384ba..01d8094 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,9 @@ *** Changes since GDB 7.7 +* The "interrupt" command now waits for the inferior to stop by default. + To avoid waiting for the inferior to stop pass "&". + * Guile scripting GDB now has support for scripting using Guile. Whether this is diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 0bf33b7..f3747fd 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -5871,13 +5871,15 @@ 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/infcmd.c b/gdb/infcmd.c index 920169d..242a436 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -2700,29 +2700,182 @@ 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 +{ + int all_threads; +}; + +/* 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 all threads to stopped. + 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_all_threads_stopped (void) +{ + struct cleanup* cleanups = make_cleanup_restore_current_thread (); + + gdb_assert (non_stop); + + while (any_running ()) + { + struct target_waitstatus last; + ptid_t last_ptid; + + QUIT; + wait_for_inferior (); + get_last_target_status (&last_ptid, &last); + finish_thread_state (last_ptid); + print_stop_event (&last); + } + + do_cleanups (cleanups); +} + +/* Finish up waiting for the current thread to stop, or continue waiting + for all threads to stop ALL_THREADS is non-zero. + Note: This function is named the way it is as it's derived from + attach_command_post_wait. */ + +static void +interrupt_command_post_wait (int all_threads) +{ + 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 (all_threads && non_stop) + wait_all_threads_stopped (); +} + +/* 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->all_threads); +} + +/* Queue a continuation to run when a thread has stopped. + This is used by the interrupt command in target-async mode where we need to + leave waiting for threads to stop to the main loop. */ + +static void +add_interrupt_continuation (int all_threads) +{ + struct interrupt_command_continuation_args *a; + + a = xmalloc (sizeof (*a)); + a->all_threads = all_threads; + + if (all_threads) + { + add_inferior_continuation (interrupt_command_continuation, a, + interrupt_command_continuation_free_args); + } + else + { + struct thread_info *tp = inferior_thread (); + + add_continuation (tp, 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 = 0; + int all_threads = 0; + + /* Subsequent invocations will just throw an error. */ + dont_repeat (); + + ERROR_NO_INFERIOR; + ensure_not_tfind_mode (); + + if (args != NULL) + async_exec = strip_bg_char (&args); + + if (args != NULL + && strncmp (args, "-a", sizeof ("-a") - 1) == 0) + all_threads = 1; + + if (all_threads) { - int all_threads = 0; + if (!any_running ()) + error (_("All threads are already stopped.")); + } + else + { + if (!is_running (inferior_ptid)) + error (_("Current thread is already stopped.")); + } - dont_repeat (); /* Not for the faint of heart. */ + /* 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 ()); - if (args != NULL - && strncmp (args, "-a", sizeof ("-a") - 1) == 0) - all_threads = 1; + /* 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 (¤t_target, async_exec); - if (!non_stop && all_threads) - error (_("-a is meaningless in all-stop mode.")); + interrupt_target_1 (all_threads); - interrupt_target_1 (all_threads); + if (async_exec) + return; + + /* 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 (); + + if (target_can_async_p ()) + add_interrupt_continuation (all_threads); + else + { + gdb_assert (non_stop); + if (all_threads) + wait_all_threads_stopped (); + else + { + wait_for_inferior (); + normal_stop (); + } } } diff --git a/gdb/testsuite/gdb.base/async-shell.exp b/gdb/testsuite/gdb.base/async-shell.exp index 4890a59..d23eb4e 100644 --- a/gdb/testsuite/gdb.base/async-shell.exp +++ b/gdb/testsuite/gdb.base/async-shell.exp @@ -40,9 +40,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 fdaa5c1..5aa4463 100644 --- a/gdb/testsuite/gdb.base/dprintf-non-stop.exp +++ b/gdb/testsuite/gdb.base/dprintf-non-stop.exp @@ -52,9 +52,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 a22acd2..323f5cc 100644 --- a/gdb/testsuite/gdb.base/interrupt-noterm.exp +++ b/gdb/testsuite/gdb.base/interrupt-noterm.exp @@ -59,9 +59,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..fdeea09 --- /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 . */ + +#include +#include +#include + +#ifndef NR_THREADS +#define NR_THREADS 4 +#endif + +pthread_t threads[NR_THREADS]; + +static void * +thread_entry (void *unused) +{ + while (1) + sleep (10); +} + +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..5ca7e0f --- /dev/null +++ b/gdb/testsuite/gdb.threads/interrupt-command.exp @@ -0,0 +1,332 @@ +# 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 . + +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)" + } + } +} + +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" "" { + -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" "" { + -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." + 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." + 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." + 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." + 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