From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by sourceware.org (Postfix) with ESMTPS id 0E93D3893652 for ; Wed, 30 Jun 2021 19:55:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 0E93D3893652 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wr1-x42b.google.com with SMTP id u8so5072779wrq.8 for ; Wed, 30 Jun 2021 12:55:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RPoveFrfkEhUgZ+CbaTlOjNcACYbgjvEdarHstp16Zg=; b=TYA6cI6a5zjELA2p6OVkQOt2qgC0P9qrGuutZZJSLuZppzE7sPsLW4JBh+NghxLm3C 6yzb6FeYCcCRnIbBCgDax668YTx3NS/XZwTJIjQJItbC9HykmbhmBCBzOTBRPzeyzNaV ZG8JLuBlhZ2Vt1Ov2GBJM8Xm5Jp/sBljhkPCvpUB8qWhWfwajt0skNWEpyQj1+0fMLPs BtUA16E/Mxqq+A4Ap+FuCpcoeTH4TEGSA/Re4Cd160XDZ8r82g/2RaZC9NU2BsBXlUKV lOTjSrYXrejyom7+hE2wDCj5TKAtPzzpIsq16kMh1NH5BTNnnvSPbzIweZM9CI2nrosr f9kQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RPoveFrfkEhUgZ+CbaTlOjNcACYbgjvEdarHstp16Zg=; b=TgOO0TSNarQIVy9D+ZRrN7KjTaLFgdj/MQK2yxeNQ9bZeSlk89L3RolPlY0bCqiXG9 XBi66j3PbpinaZo+8hmM4p85r8JE2E932a1JUb4r9BXXIUzY//AFpuUlLw0OQOrslfxS K/uL4Ay3NNGie9mL5Fv45IDbKJhvNcd/Tf/CP+SEiXw65XxBTiVvDusiJf8kUdF+DT93 6Yu337JilyxeZMkzNPihlPZlkUg4ARxjjPWY1YY9quhZlvHEPfp6DJ4uyyWkqtn/M0mp gCVts+2StlsoiMo0rsvrIn+80p2nrWtpz7fhelGQLk5EDw8f0kwQeIEGINYERXy6n6sD AFsg== X-Gm-Message-State: AOAM532b46gRnJtGVvC9vBboBzWyybXzZedMZWY3Q3rtJuPApBPRcTow 2bv+eRt9MrGVkehEeHRhrBJ5sAoR8X3/cA== X-Google-Smtp-Source: ABdhPJy4XfQaFVgo2vFfn9QJaavMJ+DEFM+0KRvX+bYjsklU1NLh3/im7h6SpCK9e2lcQmCpr2QHVg== X-Received: by 2002:a05:6000:511:: with SMTP id a17mr41643211wrf.351.1625082928806; Wed, 30 Jun 2021 12:55:28 -0700 (PDT) Received: from localhost (host86-140-92-85.range86-140.btcentralplus.com. [86.140.92.85]) by smtp.gmail.com with ESMTPSA id n4sm23232287wrw.21.2021.06.30.12.55.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 12:55:28 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Subject: [PATCH 2/3] gdb: support stepping over a clone syscall with displaced stepping off Date: Wed, 30 Jun 2021 20:55:03 +0100 Message-Id: <19133a84065f3c6b4349ca96fb544a02837e14bc.1625081357.git.andrew.burgess@embecosm.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 30 Jun 2021 19:55:33 -0000 Not to be confused with bug gdb/19675, which is stepping over a clone syscall when using displaced stepping, this commit adds support for stepping over a clone syscall when using in-place stepping. Currently, when a new thread is created through a clone syscall, GDB will set the new thread running. With 'continue' this makes sense: - all-stop mode, user issues 'continue', all threads are set running, a newly created thread should also be set running. - non-stop mode, user issues 'continue', other pre-existing threads are not effected, but as the new thread is (sort-of) a child of the thread the user asked to run, it makes sense (to me) that the new threads should be created in the running state. Similarly, if we are stopped at the clone syscall, and there's no software breakpoint at this address, then the current behaviour is fine I think: - all-stop mode, user issues 'stepi', stepping will be done in place (as there's no breakpoint to step over). While stepping the thread of interest all the other threads will be allowed to continue. A newly created thread will be set running, and then stopped once the thread of interest has completed its step. - non-stop mode, user issues 'stepi', stepping will be done in place (as there's no breakpoint to step over). Other threads might be running or stopped, but as with the continue case above, the new thread will be created running. The only possible issue here is that the new thread will be left running after the initial thread has completed its stepi. The user would need to manually select the thread and interrupt it, this might not be what the user expects. However, this is not something I'm trying to change in this commit. The problem then is what happens when we try to step over a clone syscall if there is a software breakpoint at the syscall address. We can ignore the displaced stepping case, this is covered by bug gdb/19675. For this commit we are only interested in the non-displaced stepping case: - For both all-stop and non-stop modes: + user issues 'stepi', + [non-stop mode only] GDB stops all threads. In all-stop mode all threads are already stopped. + GDB removes s/w breakpoint at syscall address, + GDB single steps just the thread of interest, all other threads are left stopped, + New thread is created running, + Initial thread completes its step, + [non-stop mode only] GDB resumes all threads that it previously stopped. There are two problems here: 1. The new thread might pass through the same code that the initial thread is in (i.e. the clone syscall code), in which case it will fail to hit the breakpoint in clone as this was removed so the first thread can single step, 2. The new thread might trigger some other stop event before the initial thread reports its step complete. If this happens we end up triggering an assertion as GDB assumes that only the thread being stepped should stop. The assert looks like this: infrun.c:5899: internal-error: int finish_step_over(execution_control_state*): Assertion `ecs->event_thread->control.trap_expected' failed. What is needed is for GDB to have more control over whether the new thread is created running or not. Issue #1 above requires that the new thread not be allowed to run until the s/w breakpoint has been reinserted, the only way to guarantee this is if the new thread is held in a stopped state until the single step has completed. When looking for solutions to this problem I considered how GDB handles fork/vfork as these seemed like they should have some of the same issues. The main difference I see between fork/vfork and clone is that the thread created events are not, by default, reported back to core GDB, instead the thread created event is handled automatically in the target code and the thread is immediately set running. However, GDB does have a mechanism to request that it be told about new threads, this is target_ops::thread_events. This on/off flag, when on, indicates that the targets should notify GDB about new threads (and also threads exiting). The new threads are still created in a "running" state, that is, the infrun core of GDB sees the thread as running, but the target will not actually start the thread executing, this is perfect for what we need. My proposal is that, before GDB steps a thread (when it is stepping over a software breakpoint), we should first enable thread_events, then, once the step has completed, disable thread_events. What this means is that the new thread will be created in the infrun running state, but not set executing. Once the step has completed then: - In all-stop mode + the stepping thread will stop, + all other threads (except the new one) are already stopped, + the new thread is not executing, but is marked running, however GDB already calls stop_all_threads, which moves the thread into the stopped state, + we're done. - In non-stop mode + the stepping thread will stop, + all other threads (except the new one) are already stopped, + the new thread is not executing, but is marked running, + GDB calls restart_threads, the new thread will be set executing, + we're done. This change worked fine for the native Linux target, but ran into issues with the remote target. The problem is that when enabling thread events for the remote target, the event isn't actively reported to GDB, that is, if thread-events is on, and a new thread is created, then gdbserver does hold the thread in the stopped state, but doesn't report the stop to GDB. This behaviour works fine for the current use of the thread-events mechanism, which is stop_all_threads (infrun.c), in this case (for remote targets) we're going to end up pulling the stop packets using the vStopped mechanism anyway, so having gdbserver actively push the stop to GDB is not important. However, this causes problems for me, we enable thread-events and step the target. The new thread is created but held in a stopped state (with a pending thread-created event), when GDB resumes the target the thread-created stop is immediately reported back to GDB. My proposal in this commit is to have gdbserver discard pending thread-created events if GDB is not interested in being told about these events. This filtering is done earlier, at the point where we pull pending stop events from the threads, prior to resuming any threads. The consequence of this is that if GDB has said it is no longer interested in thread creation events, any pending events are discarded, and the thread is resumed. I've tested this on GNU/Linux using the native target, and with the native-gdbserver and native-extended-gdbserver targets. gdb/ChangeLog: PR gdb/27830 * infrun.c (displaced_step_finish): Disable thread events. (do_target_resume): Enable thread events when stepping. (finish_step_over): Disable thread events. gdb/testsuite/ChangeLog: PR gdb/27830 * gdb.threads/stepi-over-clone.c: New file. * gdb.threads/stepi-over-clone.exp: New file. gdbserver/ChangeLog: PR gdb/27830 * linux-low.cc (linux_process_target::thread_still_has_status_pending): Add support for filtering out unwanted thread created events. --- gdb/ChangeLog | 7 + gdb/infrun.c | 20 ++ gdb/testsuite/ChangeLog | 6 + gdb/testsuite/gdb.threads/stepi-over-clone.c | 90 +++++ .../gdb.threads/stepi-over-clone.exp | 328 ++++++++++++++++++ gdbserver/ChangeLog | 7 + gdbserver/linux-low.cc | 27 +- 7 files changed, 478 insertions(+), 7 deletions(-) create mode 100644 gdb/testsuite/gdb.threads/stepi-over-clone.c create mode 100644 gdb/testsuite/gdb.threads/stepi-over-clone.exp diff --git a/gdb/infrun.c b/gdb/infrun.c index 9469b74af39..98ce8c60b55 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1779,6 +1779,10 @@ displaced_step_finish (thread_info *event_thread, enum gdb_signal signal) if (!displaced->in_progress ()) return DISPLACED_STEP_FINISH_STATUS_OK; + /* We no longer need to be notified about the creation of any new + threads. */ + target_thread_events (false); + gdb_assert (event_thread->inf->displaced_step_state.in_progress_count > 0); event_thread->inf->displaced_step_state.in_progress_count--; @@ -2179,6 +2183,18 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig) else target_pass_signals (signal_pass); + /* If we are performing a step-over (in-place) then we will only be + setting a single thread running. We don't want any new thread + (spawned by the step) to start running, so request that the target + stop the thread. + + Alternatively, if we are doing a displaced step then we will need to + fix the $pc address in any newly created threads, so again, in that + case, request that the target stop any new threads. */ + if (step_over_info_valid_p () + || displaced_step_in_progress (tp->inf)) + target_thread_events (true); + target_resume (resume_ptid, step, sig); if (target_can_async_p ()) @@ -5885,6 +5901,10 @@ finish_step_over (struct execution_control_state *ecs) back an event. */ gdb_assert (ecs->event_thread->control.trap_expected); + /* We no longer need to be notified about any new threads that are + created. */ + target_thread_events (false); + clear_step_over_info (); } diff --git a/gdb/testsuite/gdb.threads/stepi-over-clone.c b/gdb/testsuite/gdb.threads/stepi-over-clone.c new file mode 100644 index 00000000000..0cb7007a8aa --- /dev/null +++ b/gdb/testsuite/gdb.threads/stepi-over-clone.c @@ -0,0 +1,90 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021 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 . */ + +#include +#include +#include +#include +#include + +/* Set this to non-zero from GDB to start a third worker thread. */ +volatile int start_third_thread = 0; + +void * +thread_worker_2 (void *arg) +{ + int i; + + printf ("Hello from the third thread.\n"); + fflush (stdout); + + for (i = 0; i < 600; ++i) + sleep (1); + + return NULL; +} + +void * +thread_worker_1 (void *arg) +{ + int i; + pthread_t thr; + void *val; + + if (start_third_thread) + pthread_create (&thr, NULL, thread_worker_2, NULL); + + printf ("Hello from the first thread.\n"); + fflush (stdout); + + for (i = 0; i < 600; ++i) + sleep (1); + + if (start_third_thread) + pthread_join (thr, &val); + + return NULL; +} + +void * +thread_idle_loop (void *arg) +{ + int i; + + for (i = 0; i < 600; ++i) + sleep (1); + + return NULL; +} + +int +main () +{ + pthread_t thr, thr_idle; + void *val; + + if (getenv ("MAKE_EXTRA_THREAD") != NULL) + pthread_create (&thr_idle, NULL, thread_idle_loop, NULL); + + pthread_create (&thr, NULL, thread_worker_1, NULL); + pthread_join (thr, &val); + + if (getenv ("MAKE_EXTRA_THREAD") != NULL) + pthread_join (thr_idle, &val); + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/stepi-over-clone.exp b/gdb/testsuite/gdb.threads/stepi-over-clone.exp new file mode 100644 index 00000000000..8f0842cbe4f --- /dev/null +++ b/gdb/testsuite/gdb.threads/stepi-over-clone.exp @@ -0,0 +1,328 @@ +# Copyright 2021 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 performing a 'stepi' over a clone syscall instruction. + +# This test relies on us being able to spot syscall instruction in +# disassembly output. For now this is only implemented for x86-64. +if { ![istarget x86_64-*-* ] } { + return +} + +standard_testfile +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable [list debug additional_flags=-static]] != ""} { + return -1 +} + +clean_restart ${binfile} +runto_main + +# Arrange to catch the 'clone' syscall, run until we catch the syscall, and +# try to figure out the address of the actual syscall instruction so we can +# place a breakpoint at this address. + +gdb_test_multiple "catch syscall clone" "" { + -re "The feature \'catch syscall\' is not supported.*\r\n$gdb_prompt $" { + return -1 + } + -re ".*$gdb_prompt $" { + pass $gdb_test_name + } +} + +gdb_test "continue" \ + "Catchpoint $decimal \\(call to syscall clone\\), $hex in clone \\(\\)" + +proc is_syscall_insn { insn } { + if [istarget x86_64-*-* ] { + return { $insn == "syscall" } + } else { + return 0 + } +} + +set syscall_addrs {} +gdb_test_multiple "disassemble" "" { + -re "Dump of assembler code for function \[^\r\n\]+:\r\n" { + exp_continue + } + -re "^(?:=>)?\\s+(${hex})\\s+<\\+${decimal}>:\\s+(\[^\r\n\]+)\r\n" { + set addr $expect_out(1,string) + set insn [string trim $expect_out(2,string)] + if [is_syscall_insn $insn] { + verbose -log "Found a syscall at: $addr" + lappend syscall_addrs $addr + } + exp_continue + } + -re "^End of assembler dump\\.\r\n$gdb_prompt $" { + if { [llength $syscall_addrs] == 0 } { + unsupported "no syscalls found" + return -1 + } + } +} + +# The test proc. NON_STOP and DISPLACED are either 'on' or 'off', and are +# used to configure how GDB starts up. THIRD_THREAD is either True or False, +# and is used to configure the inferior. +proc test {non_stop displaced third_thread} { + global binfile srcfile + global syscall_addrs + global GDBFLAGS + global gdb_prompt hex decimal + + for { set i 0 } { $i < 3 } { incr i } { + with_test_prefix "i=$i" { + + # Arrange to start GDB in the correct mode. + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set non-stop $non_stop\"" + append GDBFLAGS " -ex \"set displaced $displaced\"" + clean_restart $binfile + } + + runto_main + + # Setup breakpoints at all the syscall instructions we + # might hit. We need to do this each time around the loop + # as runto_main deletes all breakpoints. + foreach addr $syscall_addrs { + gdb_test "break *${addr}" ".*" + } + + # Continue until we hit the syscall. + gdb_test "continue" + + if { $third_thread } { + gdb_test_no_output "set start_third_thread=1" + } + + set stepi_error_count 0 + set stepi_new_thread_count 0 + set thread_1_stopped False + set thread_2_stopped False + set seen_prompt False + + gdb_test_multiple "stepi" "" { + -re "^stepi\r\n" { + verbose -log "APB: Consume the initial command" + exp_continue + } + -re "^\\\[New Thread\[^\r\n\]+\\\]\r\n" { + verbose -log "APB: Consume new thread line" + incr stepi_new_thread_count + exp_continue + } + -re "^\\\[Switching to Thread\[^\r\n\]+\\\]\r\n" { + verbose -log "APB: Consume switching to thread line" + exp_continue + } + -re "^\\s*\r\n" { + verbose -log "APB: Consume blank line" + exp_continue + } + -re "^Hello from the first thread\\.\r\n" { + verbose -log "APB: Consume first worker thread message" + if { $third_thread } { + # If we are going to start a third thread then GDB + # should hit the breakpoint in clone before printing + # this message. + incr stepi_error_count + } + exp_continue + } + -re "^Hello from the third thread\\.\r\n" { + # We should never see this message. + verbose -log "APB: Consume third worker thread message" + incr stepi_error_count + exp_continue + } + -re "^$hex in clone \\(\\)\r\n" { + verbose -log "APB: Consume stop location line" + set thread_1_stopped True + if { !$seen_prompt } { + verbose -log "APB: Continuing to look for the prompt" + exp_continue + } + } + -re "^$gdb_prompt $" { + verbose -log "APB: Consume the final prompt" + gdb_assert { $stepi_error_count == 0 } + gdb_assert { $stepi_new_thread_count == 1 } + set seen_prompt True + if { $third_thread } { + if { $non_stop } { + # In non-stop mode if we are trying to start a + # third thread (from the second thread), then the + # second thread should hit the breakpoint in clone + # before actually starting the third thread. And + # so, at this point both thread 1, and thread 2 + # should now be stopped. + if { !$thread_1_stopped || !$thread_2_stopped } { + verbose -log "APB: Continue looking for an additional stop event" + exp_continue + } + } else { + # All stop mode. Something should have stoppped + # by now otherwise we shouldn't have a prompt, but + # we can't know which thread will have stopped as + # that is a race condition. + gdb_assert { $thread_1_stopped || $thread_2_stopped } + } + } + } + -re "^Thread 2\[^\r\n\]+ hit Breakpoint $decimal, $hex in clone \\(\\)\r\n" { + verbose -log "APB: Consume thread 2 hit breakpoint" + set thread_2_stopped True + if { !$seen_prompt } { + verbose -log "APB: Continuing to look for the prompt" + exp_continue + } + } + -re "^PC register is not available\r\n" { + # This is the error we see for remote targets. + verbose -log "APB: Consume error line" + incr stepi_error_count + exp_continue + } + -re "^Couldn't get registers: No such process\\.\r\n" { + # This is the error we see for native linux targets. + verbose -log "APB: Consume error line" + incr stepi_error_count + exp_continue + } + } + + # Ensure we are back at a GDB prompt, resynchronise. + verbose -log "APB: Have completed scanning the 'stepi' output" + gdb_test "p 1 + 2 + 3" " = 6" + + # Check the number of threads we have, it should be exactly two. + set thread_count 0 + set bad_threads 0 + + # Build up our expectations for what the current thread state + # should be. Thread 1 is the easiest, this is the thread we are + # stepping, so this thread should always be stopped, and should + # always still be in clone. + set match_code {} + lappend match_code { + -re "\\*?\\s+1\\s+Thread\[^\r\n\]+clone \\(\\)\r\n" { + incr thread_count + exp_continue + } + } + + # What state should thread 2 be in? + if { $non_stop == "on" } { + if { $third_thread } { + # With non-stop mode on, and creation of a third thread + # having been requested, we expect Thread 2 to exist, and + # be stopped at the breakpoint in clone (just before the + # third thread is actually created). + lappend match_code { + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+$hex in clone \\(\\)\r\n" { + incr thread_count + exp_continue + } + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+\\(running\\)\r\n" { + incr thread_count + incr bad_threads + exp_continue + } + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+\r\n" { + verbose -log "APB: thread 2 is bad, unknown state" + incr thread_count + incr bad_threads + exp_continue + } + } + + } else { + # With non-stop mode on, and no third thread having been + # requested, then we expect Thread 2 to exist, and still + # be running. + lappend match_code { + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+\\(running\\)\r\n" { + incr thread_count + exp_continue + } + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+\r\n" { + verbose -log "APB: thread 2 is bad, unknown state" + incr thread_count + incr bad_threads + exp_continue + } + } + } + } else { + # With non-stop mode off then we expect Thread 2 to exist, and + # be stopped. We don't have any guarantee about where the + # thread will have stopped though, so we need to be vague. + lappend match_code { + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+\\(running\\)\r\n" { + verbose -log "APB: thread 2 is bad, unexpetedly running" + incr thread_count + incr bad_threads + exp_continue + } + -re "\\*?\\s+2\\s+Thread\[^\r\n\]+\r\n" { + incr thread_count + exp_continue + } + } + } + + # We don't expect to ever see a thread 3. Even when we are + # requesting that this third thread be created, thread 2, the + # thread that creates thread 3, should stop before executing the + # clone syscall. So, if we do ever see this then something has + # gone wrong. + lappend match_code { + -re "\\s+3\\s+Thread\[^\r\n\]+\r\n" { + incr thread_count + incr bad_threads + exp_continue + } + } + + lappend match_code { + -re "$gdb_prompt $" { + gdb_assert { $thread_count == 2 } + gdb_assert { $bad_threads == 0 } + } + } + + set match_code [join $match_code] + gdb_test_multiple "info threads" "" $match_code + } + } +} + +# Run the test in all suitable configurations. +foreach_with_prefix third_thread { False True } { + foreach_with_prefix non-stop { "on" "off" } { + foreach_with_prefix displaced { "off" "on" } { + if { $displaced == "on" } { + kfail "gdb/19675" "can't displaced step over clone" + continue + } + # Pass False as the last argument to leave QThreadEvents enabled. + test ${non-stop} ${displaced} ${third_thread} + } + } +} diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index 5c6191d941c..3a44728ff0a 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -1646,13 +1646,14 @@ linux_process_target::thread_still_has_status_pending (thread_info *thread) if (!lp->status_pending_p) return 0; + int discard = 0; + if (thread->last_resume_kind != resume_stop && (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)) { struct thread_info *saved_thread; CORE_ADDR pc; - int discard = 0; gdb_assert (lp->last_status != 0); @@ -1689,16 +1690,28 @@ linux_process_target::thread_still_has_status_pending (thread_info *thread) #endif current_thread = saved_thread; - - if (discard) + } + else if (lp->waitstatus.kind == TARGET_WAITKIND_THREAD_CREATED) + { + /* If this thread stopped due to a thread created event, but GDB has + since told us it no longer cares about these events, then just + ignore this event. */ + client_state &cs = get_client_state (); + if (!cs.report_thread_events) { - if (debug_threads) - debug_printf ("discarding pending breakpoint status\n"); - lp->status_pending_p = 0; - return 0; + lp->waitstatus.kind = TARGET_WAITKIND_IGNORE; + discard = 1; } } + if (discard) + { + if (debug_threads) + debug_printf ("discarding pending thread event\n"); + lp->status_pending_p = 0; + return 0; + } + return 1; } -- 2.25.4