From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id SxzGL92XyGrwWB0AWB0awg (envelope-from ) for ; Fri, 09 Oct 2026 03:29:33 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=qq.com header.i=@qq.com header.a=rsa-sha256 header.s=s201512 header.b=mcI2TAbA; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id AC10F1E051; Fri, 09 Oct 2026 03:29:33 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 43F361E047 for ; Fri, 09 Oct 2026 03:29:29 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 301CA4BA2E04 for ; Fri, 9 Oct 2026 07:29:28 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 301CA4BA2E04 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=qq.com header.i=@qq.com header.a=rsa-sha256 header.s=s201512 header.b=mcI2TAbA Received: from out203-205-221-155.mail.qq.com (out203-205-221-155.mail.qq.com [203.205.221.155]) by sourceware.org (Postfix) with ESMTPS id 190674BA2E03 for ; Fri, 9 Oct 2026 07:28:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 190674BA2E03 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=qq.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=qq.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 190674BA2E03 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=203.205.221.155 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1791530940; cv=none; b=qvnlcAJ1h/Zy3Vk+3RKtsdZEb6R9nGFrhFmpYUtKEH+d+vEMh06LuZBgdwU3zXu5g9a4VC9K3stcQht3vf23O6u09augv3Z2Mkm1hzbYFwFybkozPsPlFuFiI4Owf8l/OFYBf4KKd9g89BGB34St4Ns7j+3uYRnA0XKlWsAdAnA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1791530940; c=relaxed/simple; bh=Nd1UA7pP+CJqnLeMdEE/X5xL5R134m1XtLP1nuYCXGY=; h=DKIM-Signature:Message-ID:From:To:Subject:Date:MIME-Version; b=vJ5F3xnxgyDFMwU9+S374n8i/yEqqS7AMJ2i1NArskwN9KEzkv9Iqo4d9Y9SRKOqc377igsI2eihC+az368VDOZHcJyVW+KJmWCPjhQPMHo8Cbyd0frpQv/KtCpNhhderuMcPQaHvc5RqOVepG+TuDHqfrCVvJSPL7A1cExPM0c= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=qq.com header.i=@qq.com header.a=rsa-sha256 header.s=s201512 header.b=mcI2TAbA DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 190674BA2E03 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s201512; t=1791530937; bh=Tzye4xkKNHoR28FQdtuoo6+1v8MbeoyofeMnBWmNq6I=; h=From:To:Cc:Subject:Date; b=mcI2TAbAfC2HA6z7GCoZQrer/3h6XjBIPRjDPHrd7bYi7mf0KCOK/SfMRuHl0N1sR fTS+zPHNmIzT8x+sDIoGbh7B9HL0ubPNYCO3gr8muVG5CSTVi9mvgQrKXmhVHcB0/t YvjVsILr4XDO0tfMN/vXZPp6RfxWKtaE/2nTDyjA= Received: from odot4-YangTianM4000q-28IRB.. ([171.212.113.59]) by newxmesmtplogicsvrszb51-1.qq.com (NewEsmtp) with SMTP id 7373AE24; Fri, 09 Oct 2026 15:28:55 +0800 X-QQ-mid: xmsmtpt1791530935t6fvxu2s1 Message-ID: X-QQ-XMAILINFO: MyIXMys/8kCtg2KYjTnqQRFbuxYQo4/2tP3aT87aRkFzFSRScmotavsdEhylv+ LYWSnBn0U+jATtBFU53an8eNVhCA9asBhwyoAD4INE53e39YTQSBb7RMQGU9rLZkezwKRrBDu0ZD lTuDORRyi/srBsm+PTfVsZqxKNW7jOdJO9zpppy3Sc0n3zKlq3GYvzaKh41zBJUE+ZjmBF3BAjQV cvyugSWfd45RWDukC1Wwegbu1LCymFWKGT18lO3Y/FZCRbvRL69SpaxFPLOCSMQrIFBsHKx2mPpe xDDKh5y//OSCOt1dLgLmEpXttppC2QEcfz4OI6bDVG3T+aY/VzxUFZ8N2LRs9FxUiuA/UMsSYC4Z 232dJCjXR2GsWOO8j5oGOkwaUQgkOmiMNOeUHTGDzMoqFSkCKbEazUzLUoISr6bdWebXGVXand7V 2B4k8lu2WCx2ZpseUOWDIPFSDQ2rUvp5yNrgoL1pbSBde2xWAjbVbPSGo87VfQyl3oVS2326pnvo 3XadgPXEg6HFkVX5Q6R1JR3d9B8jHa56toJ7RQDJtIGALBv0VijOaeSulG4GfVsd4bIAiJ+AAO2a K38LnZeCJjeXfvaQqRQkKk2CooRKqBceoUBeHRmoao8vOXtVCjpR/l6WYHkVqnA6g0x1QZIqLgbf cGsSB7mOlL9hi7/KZZ7TqXXiQmoBm286/AL8jxujRK22IUYZfyKeE8lGDiYHbLXxnGscZV4sJ0cM x7Kr4uhkIgqL7r2MzZDq7sR6ByetNGuxpalgRtgLX3dSVTjTGGnPTPp20wAW5teT6mY8YYw2jT/P E0MpZ1OK+233Ojq2JnfbWMFKgCiYnFHCsR28TSCwc5hImNm3uRuyqLiZD8AnjxbUGZ5etvxOlWil pwAEBXyKvAYIqk6CYGxK9uZz0VsH6ULpnTcXGloN8/r9tSp4KOI4A8SS4ywLtB5lym/egUcQLxlc UOa+HSiJbXOGxVzIjZHBb4ZqHJ4z/FkWnCttvICHSYeW9BW7fU1wNVshrrasA1NFl2hmZAyEkAPo 8TSNp59017iK9Z/n4RwwZhnCSiE9TOLr578cKUm6yJvn8uU9RM X-QQ-XMRINFO: NS+P29fieYNwqS3WCnRCOn9D1NpZuCnCRA== From: LiRvs-Miria <1753531985@qq.com> To: gdb-patches@sourceware.org Cc: Tom de Vries , Pedro Alves , Tankut Baris Aktemur Subject: [PATCH] gdb: fix non-stop attach leaving non-main threads stopped forever Date: Fri, 9 Oct 2026 15:28:55 +0800 X-OQ-MSGID: <20261009072855.640267-1-1753531985@qq.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org When attaching natively to a multi-threaded process in non-stop mode, linux-nat collects the leader's stop synchronously but leaves every other LWP's attach SIGSTOP pending ("the next time we wait for this LWP we'll see a SIGSTOP"), while the worker threads are reported as running. A synchronous -target-attach reports ^done as soon as attach_command returns -- before those pending attach stops are consumed. An MI consumer that then sends "-exec-continue --all" (as DAP adapters and session services do) hits that window: proceed_one_thread skips every thread whose state is not THREAD_STOPPED, so only the leader gets resumed. The workers' attach stops are consumed later, but nothing ever resumes those threads: they remain stranded in ptrace-stop forever, with no error reported. A synchronous CLI attach is not affected, because a foreground attach removes stdin from the event loop until the attach continuation has really finished. Fix by draining the pending stops with stop_all_threads in the non-stop path of attach_post_wait (ATTACH_POST_WAIT_STOP), scoped to the inferior being attached, mirroring what the target-always-non-stop path right below already does, and then syncing the user-visible thread states of that inferior with finish_thread_state, so the attach command only completes when the whole process is really stopped, with consistent thread states. This also stops the pending attach stops from leaking later to the UI as bogus "*stopped,reason="signal-received",signal-name="0"" events. If the target does not actually run in non-stop mode (e.g. "maint set target-async off"), keep the previous plain target_stop request, as stop_all_threads asserts on exists_non_stop_target. The async "attach&" path (ATTACH_POST_WAIT_RESUME) is unaffected; it already behaves correctly. Testing: the previous revision of this fix was validated on gdb 18.1 (x86_64-linux): the reproducer went from 4/10 to 0/10 failures, and a regression subset (gdb.base/attach.exp, gdb.mi/mi-nonstop.exp, gdb.threads/non-stop-fair-events.exp, gdb.threads/attach-into-signal.exp) showed no differences (534 passes, 2 unsupported both before and after). The testsuite changes in this revision are syntax-checked and dry-run only; full testsuite runs against master will follow (my main build machine is temporarily down with a hardware issue). Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28270 --- gdb/infcmd.c | 40 +++- .../gdb.mi/mi-nonstop-attach-continue.exp | 172 ++++++++++++++++++ gdb/testsuite/gdb.threads/attach-non-stop.exp | 33 ++-- 3 files changed, 226 insertions(+), 19 deletions(-) create mode 100644 gdb/testsuite/gdb.mi/mi-nonstop-attach-continue.exp diff --git a/gdb/infcmd.c b/gdb/infcmd.c index d754416..ce33eb1 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -2818,11 +2818,43 @@ attach_post_wait (int from_tty, enum attach_post_wait_mode mode) /* In all-stop, by definition, all threads have to be already stopped at this point. In non-stop, however, although the - selected thread is stopped, others may still be executing. - Be sure to explicitly stop all threads of the process. This - should have no effect on already stopped threads. */ + selected thread is stopped, other threads may still be + executing -- or, right after a native attach, may be waiting + for their attach SIGSTOP to be collected while being reported + as running. Stop all threads of this inferior, and wait until + every stop has been collected, so that this command only + completes when the whole process is really stopped. Otherwise, + an MI "-exec-continue --all" issued right after + -target-attach's ^done would only resume the thread(s) that + reported a stop so far, silently leaving the remaining threads + stopped forever (the attach stops are consumed later, but + nothing resumes those threads in non-stop mode). This should + have no effect on already stopped threads. */ if (non_stop) - target_stop (ptid_t (inferior->pid)); + { + if (target_is_non_stop_p ()) + { + stop_all_threads ("attaching", inferior); + + /* Sync the user-visible thread states of this inferior: + the attach stops consumed above flipped the internal + state of threads that had been reported as running + during the attach, and nothing else will publish those + states. Without this, the user-visible states of those + threads stay stale (running), even though the threads + are stopped, which e.g. makes a subsequent + "-exec-continue --all" skip them. Syncing is silent: + no thread becomes running here. */ + finish_thread_state (inferior->process_target (), + ptid_t (inferior->pid)); + } + else + /* The target is not actually operating in non-stop mode + (e.g. "maint set target-async off"); just request a stop + like before -- stop_all_threads asserts on + exists_non_stop_target. */ + target_stop (ptid_t (inferior->pid)); + } else if (target_is_non_stop_p ()) { struct thread_info *lowest = inferior_thread (); diff --git a/gdb/testsuite/gdb.mi/mi-nonstop-attach-continue.exp b/gdb/testsuite/gdb.mi/mi-nonstop-attach-continue.exp new file mode 100644 index 0000000..3e7827d --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-nonstop-attach-continue.exp @@ -0,0 +1,172 @@ +# Copyright 2026 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 . + +# Test attaching to a multi-threaded process in non-stop mode via MI, +# and then resuming all threads with "-exec-continue --all". +# +# This is a regression test for a bug where a synchronous native +# attach in non-stop mode returned before all the pending attach stops +# had been processed: the stops of the non-main threads dribbled in +# after the prompt as bogus '*stopped,reason="signal-received", +# signal-name="0"' events, and a following "-exec-continue --all" could +# leave those threads stranded. +# +# The attach logic now drains all the pending attach stops with +# stop_all_threads before the attach command completes, so when +# "-target-attach" reports "^done", all threads of the attached process +# are already stopped, and no stop notification leaks after the prompt. +# That makes the checks below deterministic. (On builds without the +# fix, the same checks are racy.) + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +require can_spawn_for_attach + +if {![istarget "*-*-linux*"]} { + unsupported "native non-stop attach test" + return +} + +standard_testfile + +# Number of worker threads started by the program. +set n_workers 6 + +# Number of threads GDB should see: the main thread plus the workers. +set n_threads [expr {$n_workers + 1}] + +# The debuggee: the main thread creates the workers and then blocks in +# pthread_join, while the workers loop in nanosleep. The process is +# thus quiescent (no signals, no thread exits) when we attach to it. +set src { +#include +#include +#include + +#define N_WORKERS 6 + +static void * +worker_function (void *arg) +{ + struct timespec ts; + + for (;;) + { + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + + nanosleep (&ts, NULL); + } + + return NULL; /* Not reached. */ +} + +int +main (void) +{ + pthread_t workers[N_WORKERS]; + int i; + + alarm (300); + + for (i = 0; i < N_WORKERS; i++) + pthread_create (&workers[i], NULL, worker_function, NULL); + + for (i = 0; i < N_WORKERS; i++) + pthread_join (workers[i], NULL); + + return 0; +} +} + +set srcfile [standard_output_file ${testfile}.c] +set binfile [standard_output_file $testfile] + +set fd [open $srcfile w] +puts $fd $src +close $fd + +set options [list debug] +if {[gdb_compile_pthreads $srcfile $binfile executable $options] != ""} { + return +} + +set spawn_id [spawn_wait_for_attach $::binfile] +set prog_pid [spawn_id_get_pid $spawn_id] + +# Give slow machines some head room. +with_timeout_factor 4 { + + # Start GDB with non-stop mode on. + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set non-stop on\"" + mi_clean_restart $::testfile + } + + # Attach to the spawned process, synchronously. All the pending + # attach stops are drained before the attach command completes, so + # any "*stopped" notifications appear before "^done"; consume them + # here. + mi_gdb_test "-target-attach $prog_pid" ".*\\^done.*" \ + "attach to process $prog_pid" + + # No stop notification should leak out after the attach command + # completed. Builds without the fix dribble in bogus + # '*stopped,reason="signal-received",signal-name="0"' + # notifications here, after the prompt. + set test "no stops after attach prompt" + gdb_expect 2 { + -re "\\*stopped" { + fail $test + } + timeout { + pass $test + } + } + + # When the attach command completed, all threads of the attached + # process were already stopped. + set states {} + for {set i 0} {$i < $n_threads} {incr i} { + lappend states "stopped" + } + mi_check_thread_states $states "all threads stopped after attach" + + # Resume all threads, and expect one "*running" notification per + # thread. Don't require a specific thread-id ordering in the + # notifications; the all-running state check below covers that. + set running_re "" + for {set i 1} {$i <= $n_threads} {incr i} { + append running_re "\\*running,thread-id=\"\[0-9\]+\"\r\n" + } + set running_re [string range $running_re 0 end-2] + + mi_gdb_test "-exec-continue --all" ".*$running_re.*" \ + "resume all threads" + + # Every thread should now be reported as running. + set states {} + for {set i 0} {$i < $n_threads} {incr i} { + lappend states "running" + } + mi_check_thread_states $states "all threads running after continue" + + mi_gdb_test "-target-detach" "\\^done" "detach from process $prog_pid" +} + +kill_wait_spawned_process $spawn_id + +mi_gdb_exit diff --git a/gdb/testsuite/gdb.threads/attach-non-stop.exp b/gdb/testsuite/gdb.threads/attach-non-stop.exp index 3d36339..131b458 100644 --- a/gdb/testsuite/gdb.threads/attach-non-stop.exp +++ b/gdb/testsuite/gdb.threads/attach-non-stop.exp @@ -48,7 +48,12 @@ proc test {target_non_stop non_stop cmd} { set any "\[^\r\n\]*" if {$cmd == "attach"} { + set stops 0 gdb_test_multiple "attach $testpid" $test { + -re "Thread $::decimal ${any} stopped" { + incr stops + exp_continue + } -re "Attaching to program:${any}process $testpid\r\n.*$gdb_prompt " { pass $test set attached 1 @@ -61,22 +66,20 @@ proc test {target_non_stop non_stop cmd} { } if {$non_stop} { - # In non-stop, we will see one stop per thread after - # the prompt. - set stops 0 - set test "seen all stops" - for {set thread 1} { $thread <= $n_threads } { incr thread } { - gdb_test_multiple "" $test { - -re "Thread $::decimal ${any} stopped" { - incr stops - } + # In non-stop, all the pending attach stops are drained + # before the attach command completes, so we should have + # seen one stop per thread before the prompt. + gdb_assert {$stops == $n_threads} "seen all stops" + + # No stop should dribble in after the prompt, either. + set test "no stops after the prompt" + gdb_expect 2 { + -re "Thread $::decimal ${any} stopped" { + fail $test + } + timeout { + pass $test } - } - - # If we haven't seen all stops, then the gdb_test_multiple - # in the loop above will have already issued a FAIL. - if {$stops == $n_threads} { - pass $test } } -- 2.34.1