From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id UXioL/jzBmpt+jwAWB0awg (envelope-from ) for ; Fri, 15 May 2026 06:22:48 -0400 Authentication-Results: simark.ca; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=O6pArrcZ; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id B4F561E0B1; Fri, 15 May 2026 06:22:48 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.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 1AF8B1E093 for ; Fri, 15 May 2026 06:22:45 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8D21551A4323 for ; Fri, 15 May 2026 10:22:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8D21551A4323 Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=O6pArrcZ Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 429C84BBC0EE for ; Fri, 15 May 2026 10:22:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 429C84BBC0EE Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 429C84BBC0EE Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778840532; cv=none; b=UsK0U/E3rrWrNdFAkNLG2oDefC0AtWWth6h6wlbqY4Exq1I/YTVgtpsXYzCWtbhtcITzIDQ82793T36yVaGr58G9u96ed1Z9iUiAZR+iu5ydQn1Dbxnn/IOlnukl7a60qr8wkT52rBOcpgCsxRJQ6AwTFVOH9CPVNO5VebRUgDE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778840532; c=relaxed/simple; bh=6uEudLmVgWMviHix0UFwW+q5J40WFBT9v3QAggHEWDA=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Ig7ItMc173mzQkTUSclJ37VOmCP6P81GlWfFz8YzSTGzBxxarPNOS2ChcbiC+BCqeLYuSvlclWO+T2tttqFCsMDAsJEmBm8BeyeRh2/LNUJbkXe0zQQxwP96rTVxuFz7tM+kz2KQkqFkYXrRZI5PnhWa6LOvl2OL2/Mx49gptgg= ARC-Authentication-Results: i=1; sourceware.org; dkim=fail (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=O6pArrcZ reason="signature verification failed" DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 429C84BBC0EE DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1778840531; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=FulToHtuXLCFO19sWnkl9xcq8h8SZEf+7tNh7BMbPoU=; b=O6pArrcZLCmRbwJMLo9BC6UCC5++CAk6olRoCOsAH+jvsCoD8k5KOpo3f9Pq+sJbcISb5W e3KPVM6duscFa9HUKxoUqi8uJ2CJboPTB0IJ56Uo+I5pVxl6QJ0gaiIo9qaNmcAKOyBjhT NwVzQWozTIC3LkbqVETP1ZMG+qB3iaU= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-187-ZpFS2ep-M6anpj4D0gzLig-1; Fri, 15 May 2026 06:22:10 -0400 X-MC-Unique: ZpFS2ep-M6anpj4D0gzLig-1 X-Mimecast-MFC-AGG-ID: ZpFS2ep-M6anpj4D0gzLig_1778840529 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-48e89faa62eso27608355e9.1 for ; Fri, 15 May 2026 03:22:10 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778840529; x=1779445329; h=mime-version:message-id:date:references:in-reply-to:subject:to:from :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=vuUwDtR7uwWq8VQo9njL/CujlTjJm5SGKrPGIT0UNIU=; b=rczqbE2puvslJrNRugfcqmpUoQIpeaAjzDr1ih9skgOqj/5sL8fLZWM3VJVR5DeG9Y iFYNtbRB+DmBlzwRBB4oQIfY1DZO82ItWe+Xc3dSV5WYswaqDj3lh7eTW4zB2AP0MYMs MqS+C1/oSQOkNb90VTfF3u2MjWvInHi0dWSQXI8qxR2HNFv5xqAmUUmzsYESzATZUbTB M7j+5L3xyXX+3WHTVY62UJuuurClTUiaUzck5swDqvhw424GBjwll+hcd7ECcgtsI5TN Uuwb9JL8WLkspPSu7ayvFfUd+qSu/OBniVg3L7sEh4K0aASdwVMfKufCBsf6yClwEQx/ GjJg== X-Forwarded-Encrypted: i=1; AFNElJ/FDF46UT3/DYQetGKZYrhMO8/aE3SEpAwj5IyrIhLyepbml5Tdt/cm6SfkxnTmPO8xkCrkSPzSPfqaqw==@sourceware.org X-Gm-Message-State: AOJu0YzJX6k3R40JUiAes1bx+Y9cqilwTiyeItSOdflVL0znDYIOsVzY OABwquZY0WM8bXdTSh+OEYS0Mfu7E8Tijxa2F9on2kXS/Gm43tgByKUMm+dbs8gTz7D3YzSUhdg Rv7GePD/9AErh6BEmQMtME0DKPNMPLTBzIVJf6asIhQLY1dQ3sa7gG6brgSWtoZJLLv0EDQA= X-Gm-Gg: Acq92OE3umO5Zra6BJrDZgzdOxyvSSNvpaPsNG65zl33T9nsVB1gfvYLm9MAPfkrb3k t2eLIDvClIhAD4NVUf+QJMOiYPDe52WajWUN7Czhff0vwXPRDBYZ8nLc/MxzBPVHj844lcCjZc9 9y0tJQV7kxmujDWcsqciyluL8SsxFg2/xDch82PnB8HCYISC3T7eacCzmWYeo3ZIDRxxFFIQZG4 zoFUfvJXzq6NfHX/1uq4Ry3GDK9WzYbyvzj9LzjpgFJYZ14MaC1jCvmoGYSgD2V7kocAadqH3vQ Qvf5WET66tFqxCbOq3xGzg44Y8xVY5RCFEjHFiw+NMiwb6PZ6pVzi4jC2LLiguLygegL2yTf7+/ Rv+wsDGz6Nf5VJ5qB X-Received: by 2002:a05:600c:34d6:b0:48f:d1b8:9aa0 with SMTP id 5b1f17b1804b1-48fe60e3cf4mr44368905e9.7.1778840528757; Fri, 15 May 2026 03:22:08 -0700 (PDT) X-Received: by 2002:a05:600c:34d6:b0:48f:d1b8:9aa0 with SMTP id 5b1f17b1804b1-48fe60e3cf4mr44368225e9.7.1778840528041; Fri, 15 May 2026 03:22:08 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48feb029180sm19493045e9.4.2026.05.15.03.22.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 03:22:07 -0700 (PDT) From: Andrew Burgess To: Simon Marchi , gdb-patches@sourceware.org Subject: Re: [PATCH] gdb: pass inferior argument to inf_child_target::maybe_unpush_target In-Reply-To: <1f7b28b1-6929-47f0-b183-abaabc94e46b@simark.ca> References: <4a06ca5986e36021a3ca4c47020cd3f8daee16a7.1778600715.git.aburgess@redhat.com> <1f7b28b1-6929-47f0-b183-abaabc94e46b@simark.ca> Date: Fri, 15 May 2026 11:22:06 +0100 Message-ID: <871pfddlbl.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: LjjsAcSBqKUTQ2LstWtNmT9LNscMDSwjRZHfhDpGN4k_1778840529 X-Mimecast-Originator: redhat.com Content-Type: text/plain 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 Simon Marchi writes: > On 2026-05-12 11:45, Andrew Burgess wrote: >> @@ -2594,9 +2594,9 @@ procfs_target::mourn_inferior () >> destroy_procinfo (pi); >> } >> >> + inferior *inf = current_inferior (); >> generic_mourn_inferior (); >> - >> - maybe_unpush_target (); >> + maybe_unpush_target (inf); > > You wrote it this particular way because generic_mourn_inferior could > switch inferior, as you explained in your commit message. I think it > would deserve a comment at all these spots to explain that we capture > the current inferior before "generic_mourn_inferior", because that > function can switch the current inferior. Otherwise, it would be very > easy for someone (like me) to "optimize out" the temporary variable > without realizing the behavior change. The updated patch is below, the only change is I've added a comment in the 3 places where I capture current_inferior before calling generic_mourn_inferior, here's one example, the other 2 are identically worded: void inf_child_target::mourn_inferior () { + /* Capture the inferior before calling generic_mourn_inferior, as + generic_mourn_inferior can trigger a change of the current inferior + via an extension language inferior exited event hook. */ + inferior *inf = current_inferior (); generic_mourn_inferior (); - maybe_unpush_target (); + maybe_unpush_target (inf); } If I don't hear anything I'll push this next week. > Otherwise I think it's fine, one little baby steps towards using less > "current inferior" everywhere. Indeed. I'm tempted to change generic_mourn_inferior to take an inferior next, which would open the door to target_ops::mourn_inferior taking an inferior, which would be a nice cleanup I think. Time permitting of course. Thanks, Andrew --- commit c634d7b80ad6a7aa96b6d930f354cadf8a60db68 Author: Andrew Burgess Date: Tue May 12 11:51:44 2026 +0100 gdb: pass inferior argument to inf_child_target::maybe_unpush_target Bug PR gdb/29944 highlights an issue where this assertion can trigger: thread-iter.c:109: internal-error: all_matching_threads_iterator: Assertion `filter_target != nullptr' failed. The problem occurs when GDB is configured with these settings: set detach-on-fork off set schedule-multiple on set non-stop on There is a Python exit event listener registered like this: def exit_handler(_): gdb.execute('inferior 1') gdb.events.exited.connect(exit_handler) Then the user runs an inferior which forks a child process, the child (at some point) exits while the parent process continues running. Then, at some future time, the parent process also exits. Initially we only have inferior 1, but when this inferior forks we now have inferior's 1 and 2. Due to the settings GDB follows both, and resumes both inferiors. On GNU/Linux, when the child, inferior 2, exits, we eventually end up in inf_child_target::mourn_inferior. This then calls generic_mourn_inferior which operates on the current inferior. This includes calling exit_inferior, which is where the inferior_exit observer is notified, and it is this that runs the Python 'exit' event handlers. In our case the Python exit handler runs 'inferior 1', which changes the current inferior. Back in inf_child_target::mourn_inferior we now call maybe_unpush_target which potentially unpushes the inf_child_target from the target stack of the current inferior. But notice, we already switched the current inferior to inferior 1. This means that we just unpushed the inf_child_target from inferior 1, which is still running, not inferior 2, which has exited. Later, when inferior 1 exits we end up in normal_stop and, because we are in non-stop mode, we try to find the finish_ptid and finish_target based on the inferior which just exited. In this case inferior 1. But remember, inferior 1 no longer has a process stratum target, we incorrectly unpushed it earlier when inferior 2 exited. We now initialise maybe_finish_thread_state, a scoped_finish_thread_state object, using the finish_ptid, which is the ptid of inferior 1, and finish_target, which is NULL. Eventually, later in normal_stop, the scoped_finish_thread_state runs, which calls finish_thread_state, which calls all_non_exited_threads. This eventually calls all_matching_threads_iterator::all_matching_threads_iterator to iterate over the applicable threads, and, as the filter_ptid is that of inferior 1 (i.e. not minus_one_ptid), and filter_target is NULL, the assert triggers. The solution is simple enough, have inf_child_target::maybe_unpush_target take the inferior to operate on, rather than relying on the current_inferior being correct. It looks like we might have run into something like this before as in inf_child_target::follow_exec we temporarily change the current inferior back prior to calling maybe_unpush_target. In that case it is GDB's follow-exec-mode that was causing the problem. See: commit 737358ba1ed8b28820cc965f62027bb7417b132b Date: Thu May 13 15:28:42 2021 -0400 gdb: maybe unpush target from old inferior in inf_child_target::follow_exec I did consider having inf_child_target::maybe_unpush_target perform the inferior switch for us, but after looking at what maybe_unpush_target actually does, I don't think it is really necessary for the current inferior to be set "correctly", given an inferior pointer, we can just operate on that. So that's what I've done. maybe_unpush_target takes an 'inferior *' argument, and it is that inferior from which we unpush the target. It is no longer necessary to switch inferiors in follow_exec. There's a test that exposes the original assertion failure, which passes with this patch. The gdb.base/foll-exec-mode.exp test, which was added in commit 737358ba1ed8b288 also still passes after this change. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29944 Approved-By: Kevin Buettner diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index 985aa14debc..39e02f42f40 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -2226,7 +2226,7 @@ gnu_nat_target::detach (inferior *inf, int from_tty) switch_to_no_thread (); detach_inferior (inf); - maybe_unpush_target (); + maybe_unpush_target (inf); } diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c index b3ea5c1fd5c..ebf6be34eaf 100644 --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -773,8 +773,12 @@ go32_nat_target::mourn_inferior () prog_has_started = 0; + /* Capture the inferior before calling generic_mourn_inferior, as + generic_mourn_inferior can trigger a change of the current inferior + via an extension language inferior exited event hook. */ + inferior *inf = current_inferior (); generic_mourn_inferior (); - maybe_unpush_target (); + maybe_unpush_target (inf); } /* Hardware watchpoint support. */ diff --git a/gdb/inf-child.c b/gdb/inf-child.c index a87aa925133..c19121eb9fe 100644 --- a/gdb/inf-child.c +++ b/gdb/inf-child.c @@ -191,17 +191,21 @@ inf_child_target::close () void inf_child_target::mourn_inferior () { + /* Capture the inferior before calling generic_mourn_inferior, as + generic_mourn_inferior can trigger a change of the current inferior + via an extension language inferior exited event hook. */ + inferior *inf = current_inferior (); generic_mourn_inferior (); - maybe_unpush_target (); + maybe_unpush_target (inf); } /* See inf-child.h. */ void -inf_child_target::maybe_unpush_target () +inf_child_target::maybe_unpush_target (inferior *inf) { if (!inf_child_explicitly_opened) - current_inferior ()->unpush_target (this); + inf->unpush_target (this); } bool @@ -420,13 +424,7 @@ inf_child_target::follow_exec (inferior *follow_inf, ptid_t ptid, process_stratum_target::follow_exec (follow_inf, ptid, execd_pathname); if (orig_inf != follow_inf) - { - /* If the target was implicitly push in the original inferior, unpush - it. */ - scoped_restore_current_thread restore_thread; - switch_to_inferior_no_thread (orig_inf); - maybe_unpush_target (); - } + maybe_unpush_target (orig_inf); } /* See inf-child.h. */ diff --git a/gdb/inf-child.h b/gdb/inf-child.h index 9fa62664b36..4392b119b40 100644 --- a/gdb/inf-child.h +++ b/gdb/inf-child.h @@ -95,13 +95,14 @@ class inf_child_target bool can_use_agent () override; protected: - /* Unpush the target if it wasn't explicitly open with "target native" - and there are no live inferiors left. Note: if calling this as a - result of a mourn or detach, the current inferior shall already - have its PID cleared, so it isn't counted as live. That's usually - done by calling either generic_mourn_inferior or + /* Unpush the target from inferior INF if it wasn't explicitly open with + "target native" and there are no live inferiors left. + + Note: if calling this as a result of a mourn or detach, INF shall + already have its PID cleared, so it isn't counted as live. + That's usually done by calling either generic_mourn_inferior or detach_inferior. */ - void maybe_unpush_target (); + void maybe_unpush_target (inferior *inf); }; /* Convert the host wait(2) status to a target_waitstatus. */ diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c index 5363717208e..aed0759dddf 100644 --- a/gdb/inf-ptrace.c +++ b/gdb/inf-ptrace.c @@ -213,7 +213,7 @@ inf_ptrace_target::detach_success (inferior *inf) switch_to_no_thread (); detach_inferior (inf); - maybe_unpush_target (); + maybe_unpush_target (inf); } /* Kill the inferior. */ diff --git a/gdb/procfs.c b/gdb/procfs.c index 7472c10616e..3c10c81297b 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -1784,7 +1784,7 @@ procfs_target::detach (inferior *inf, int from_tty) switch_to_no_thread (); detach_inferior (inf); - maybe_unpush_target (); + maybe_unpush_target (inf); } static void @@ -2594,9 +2594,12 @@ procfs_target::mourn_inferior () destroy_procinfo (pi); } + /* Capture the inferior before calling generic_mourn_inferior, as + generic_mourn_inferior can trigger a change of the current inferior + via an extension language inferior exited event hook. */ + inferior *inf = current_inferior (); generic_mourn_inferior (); - - maybe_unpush_target (); + maybe_unpush_target (inf); } /* When GDB forks to create a runnable inferior process, this function diff --git a/gdb/testsuite/gdb.python/py-switch-inferior-in-exit-event.c b/gdb/testsuite/gdb.python/py-switch-inferior-in-exit-event.c new file mode 100644 index 00000000000..f329e17f8b8 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-switch-inferior-in-exit-event.c @@ -0,0 +1,62 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 . */ + +#include +#include +#include +#include +#include + +volatile int global_var = 0; + +void +breakpt () +{ + global_var = 42; /* Break here. */ +} + +int +main () +{ + pid_t pid; + + alarm (300); + + /* Create the child process. */ + pid = fork (); + assert (pid >= 0); + + if (pid == 0) + { + /* Child process. */ + exit (0); + } + else + { + /* Parent process. */ + + /* Wait for the child process to finish. */ + wait (NULL); + + while (global_var == 0) + sleep (1); + + breakpt (); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-switch-inferior-in-exit-event.exp b/gdb/testsuite/gdb.python/py-switch-inferior-in-exit-event.exp new file mode 100644 index 00000000000..2f23a5529c4 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-switch-inferior-in-exit-event.exp @@ -0,0 +1,195 @@ +# Copyright (C) 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 for PR gdb/29944. In GDB set 'non-stop on', set +# 'detach-on-fork off', and set 'schedule-multiple on'. +# +# Register a Python event handler for the exit event, when an exit +# event arrives, switch to inferior 1. +# +# Run an inferior which forks, the child process exits, and then +# parent process exits some time later. +# +# The bug was that GDB would unpush the target from inferior 1 instead +# of inferior 2 (the one that actually exited). When inferior 1 exits +# later it has no process stratum target, and this triggers an +# assertion. + +require allow_fork_tests +require allow_python_tests + +load_lib gdb-python.exp + +standard_testfile + +if { [build_executable "build executable" ${testfile} ${srcfile}] } { + return +} + +foreach_with_prefix non_stop { on off } { + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set detach-on-fork off\"" + append GDBFLAGS " -ex \"set schedule-multiple on\"" + append GDBFLAGS " -ex \"set non-stop $non_stop\"" + clean_restart $testfile + } + + if {![runto_main]} { + return + } + + set before_output [capture_command_output "info inferiors" ""] + + gdb_test_multiline "add exited listener" \ + "python" "" \ + "handler_ran = False" "" \ + "def exit_handler(_):" "" \ + " global handler_ran" "" \ + " handler_ran = True" "" \ + " gdb.execute('inferior 1')" "" \ + "gdb.events.exited.connect(exit_handler)" "" \ + "end" "" + + set lineno [gdb_get_line_number "/* Break here. */"] + gdb_breakpoint $lineno + + # Initial part of the test. Resume inferior 1, this will fork a child + # process, which immediately exits. The Python exit event will trigger, + # and GDB will switch back to inferior 1. At this point we land back at + # a GDB prompt. + set saw_inferior_created false + set saw_inferior_exited false + set saw_inferior_selected false + set saw_thread_selected false + gdb_test_multiple "continue" "continue to inferior exit event" { + -re "^\\\[New inferior 2\[^\r\n\]*\\\]\r\n" { + set saw_inferior_created true + exp_continue + } + + -re "^\\\[Inferior 2 \[^\r\n\]+exited normally\\\]\r\n" { + set saw_inferior_exited true + exp_continue + } + + -re "^\\\[Switching to inferior 1\[^\r\n\]*\\\]\r\n" { + set saw_inferior_selected true + exp_continue + } + + -re "^\\\[Switching to thread 1\\.1\[^\r\n\]*\\\]\\(running\\)\r\n" { + # In all-stop (non-stop off) mode, this line is possibly a + # little confusing; the inferior is marked as '(running)', but + # by the time the prompt is displayed, the inferior will be + # stopped. This is a consequence of when the exit observer for + # inferior 2 triggers. If this is ever changed / fixed then the + # above regexp will need changing, at least for all-stop mode. + set saw_thread_selected true + exp_continue + } + + -re "^$gdb_prompt $" { + gdb_assert { $saw_inferior_created && $saw_inferior_exited \ + && $saw_inferior_selected && $saw_thread_selected } \ + $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Confirm the event handler ran. + gdb_test "python print(handler_ran)" "^True" + + if { $non_stop eq "on" } { + set resume_cmd "set global_var = 1" + set re "\r\n\\*\\s+1\\s+\[^\r\n\]+\\(running\\)" + } else { + gdb_test_no_output "set global_var = 1" + set resume_cmd "continue" + set re "\r\n\\*\\s+1\\s+\[^\r\n\]+$hex in \[^\r\n\]+" + } + + # This validates that the expected thread is selected. In non-stop mode + # this also checks that the thread is still running. + gdb_test "info threads" $re + + # Check that 'info inferiors' output is unchanged. We are mostly + # checking that the 'connection' is unchanged. + set midway_output [capture_command_output "info inferiors" ""] + gdb_assert {$before_output eq $midway_output} \ + "info inferiors output is unchanged at midway point" + + # The second part of the test. Send RESUME_CMD which will allow + # inferior 1 to progress to the 'breakpt' function, where a breakpoint + # will be hit and the inferior will stop again. + set saw_breakpoint_hit false + set saw_source_line false + set saw_prompt false + gdb_test_multiple $resume_cmd "continue to breakpoint" { + -re "^Thread $::decimal \[^\r\n\]*hit Breakpoint $::decimal, breakpt \\(\\) at \[^\r\n\]+\r\n" { + set saw_breakpoint_hit true + exp_continue + } + + -re "^$lineno\\s+\[^\r\n\]+Break here\\. \\*/\r\n" { + set saw_source_line true + + if { $non_stop eq "on" } { + gdb_assert { $saw_inferior_created && $saw_inferior_exited \ + && $saw_inferior_selected && $saw_thread_selected \ + && $saw_breakpoint_hit && $saw_source_line } \ + $gdb_test_name + } else { + exp_continue + } + } + + -re "^$gdb_prompt $" { + set saw_prompt true + # In all-stop (non-stop off) mode we expect to see a prompt + # after hitting the breakpoint. + # + # In non-stop mode, the prompt is displayed immediately after + # sending the RESUME_CMD (above). Hitting the breakpoint is an + # async background event, which is announced to the console. + # The prompt is not redisplayed afterwards, that's why for + # non-stop mode the test's exit point is after seeing the source + # line. + if { $non_stop eq "off" } { + gdb_assert { $saw_breakpoint_hit && $saw_source_line \ + && $saw_prompt } \ + $gdb_test_name + } else { + exp_continue + } + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Check GDB is still alive, plus in non-stop mode, get us back to a GDB + # prompt. + gdb_test "print 1" " = 1" + + # Check that 'info inferiors' output is unchanged. We are mostly + # checking that the 'connection' is unchanged. + set after_output [capture_command_output "info inferiors" ""] + gdb_assert {$before_output eq $after_output} \ + "info inferiors output is unchanged at end of test" +} diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index d506b42fbda..bff670a3652 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -2427,7 +2427,7 @@ windows_nat_target::detach (inferior *inf, int from_tty) windows_process->process_id = 0; - maybe_unpush_target (); + maybe_unpush_target (inf); } /* The pid_to_exec_file target_ops method for this platform. */