From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id SgJHC/ZZJGrtkzgAWB0awg (envelope-from ) for ; Sat, 06 Jun 2026 13:33:42 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=V8f4bURz; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 1B5471E062; Sat, 06 Jun 2026 13:33:42 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=no 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 413D91E062 for ; Sat, 06 Jun 2026 13:33:40 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 304474C31839 for ; Sat, 6 Jun 2026 17:33:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 304474C31839 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=V8f4bURz 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 7EA434BA5439 for ; Sat, 6 Jun 2026 17:33:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7EA434BA5439 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 7EA434BA5439 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=1780767185; cv=none; b=gm8svWP36XY1WMVq4GKjZeCVr4C22wpZj9RN4h2CxaigC7GM+F/aEI01CHNNJp48O/bQkkBvCQyALUXfxKLJaoor8LMEoJVPMDrCeBzmBzW6rm8LhY6PbAsKPrNnYNHjjvuaXx4cFhLKOGCGtpZDKoyoYw+f1BM6BI+NOiKPjOQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1780767185; c=relaxed/simple; bh=1TvVzPdvaQIBfbH+tuN5Yh5I18craXGT7EhdL4t+Kog=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=n6P3Cub5oXK3MRlpD56plFGiQX60EXJBPBEyjXrG9bG4r/hMd5qxMWaWndzQCTHFZk1MUZdxa5i7D87Q0VL9uqid32TQqiprLPqn8bSx0bVp7jdtDtzEJbOiRY1s/rbQmFaZAaLH26RBM+nkg1mfzug0abKBo3cF8Gg7aqvdr3Y= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=V8f4bURz DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7EA434BA5439 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780767185; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Zu4Qm15S6dmhc4nVA09k3rRp3PT4oCMywfCIjmMuKhk=; b=V8f4bURzuN0MzDQNq/T8w5+8cXpyxUVkbslJrKNCkEJiGUkHJCPqlk06ZrEyOF9IGLPJPU zQyL81G9Lmhw0fu0QsXZo12XVSrB4541OucjCzurngP9p0WedIeDI6Pukgi/eKCijJEqFv LD1T1Ye3TB0F+caj6jgzXguS3xI8Mus= Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-347-oZGl4AX-P46GZhv7LFclQg-1; Sat, 06 Jun 2026 13:33:03 -0400 X-MC-Unique: oZGl4AX-P46GZhv7LFclQg-1 X-Mimecast-MFC-AGG-ID: oZGl4AX-P46GZhv7LFclQg_1780767182 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-45ef63d1214so1927066f8f.3 for ; Sat, 06 Jun 2026 10:33:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780767182; x=1781371982; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Zu4Qm15S6dmhc4nVA09k3rRp3PT4oCMywfCIjmMuKhk=; b=aEDRsLbPKvUtPKX1L8xyGFgr7/KOkIcGxTeuG0v0mb5uGaQmPrN95TQbquQfJIkWpG Bikfk4z5yWwPwI0LhIdQVs6aMmkjkftIxruFReTJCE9N1tz8X+4h1Sq3aIG06gXGowI9 hvAslFLs6KlETr7VDiZc1IWHcDEPTMzY86rz3vKNyRjPVl0ibogZffuF0vijjUuiKBf9 jNSxVUCTpCW+R04Zw2rs3n5SW5vDQkB4yGrCkjHOblpfhSrpP9+VPLPKXKuA/Qk9RrTV mYqn4xcWqzOHKOTeo6rwA4R3ptukxiIkZlUNTazQX5LdovsRXps+fttE6ime3h3+j85x FQ7Q== X-Gm-Message-State: AOJu0YxbJhDVSafdx3Jn3/sVAehHtmQGMSZB8ugZgUlfLaNvpoLImoeg SlcRKb0EaCyuBFNU3rN8hUgO9nft7P/83hvEfkYkXTdAWKmPf2BqVn+Lm2uS9dn9HOLAVcLh5A7 4TIioBmZQvOgKKLS6WrLqr+9eJZyVmjD4gZnhjgYEz4nMLR7IZzDBh3VwhmrceXwiuMHbLXbCkk OKgvZyyOUbIma0zKkYGTsT1lHXxV+7xQ5E9IThePIc7PPDogk= X-Gm-Gg: Acq92OEKO7T8e1qDAhqJiYfS+bA5NoRBcwUfSaNX2fDC505QG8c/tV0mbrVRFPKHtTY Wa+gmZjGw1SHYdkC6kvTsNN61RZtpqKwd9zpEDwQ6Ku//7WKj+uqXnERiMlcUvruZKJS65LKsx7 qemzTlPCnC2F3Y94xr3RT4EqjU5wh6xh78j/0fEI9WWb1Kwfd95xJozVBQu4cVQeibZ0VB9XkAc DN/vUTLESo0ZnSQiCJPMTc2qJ9kS/7pwdurkeKz2F9ZAzjCYKzrBbQBG/M7EyoNYWeH/kgHztNF jkXzOaFx2Flp0sd+9jAQqqWSUeR+fPCewThbFBQcH0RAwzTxOYr4lYyq+pINQWF2U7VABgN3VK6 Mya0XeKjTbTu1yo0cGSQ8SGH5ow== X-Received: by 2002:a05:600c:6994:b0:490:3f7a:108b with SMTP id 5b1f17b1804b1-490c25de6f1mr136253175e9.16.1780767181700; Sat, 06 Jun 2026 10:33:01 -0700 (PDT) X-Received: by 2002:a05:600c:6994:b0:490:3f7a:108b with SMTP id 5b1f17b1804b1-490c25de6f1mr136252825e9.16.1780767181103; Sat, 06 Jun 2026 10:33:01 -0700 (PDT) Received: from localhost ([213.31.44.97]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-490bc3d663csm275698475e9.11.2026.06.06.10.33.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 06 Jun 2026 10:33:00 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Lancelot SIX Subject: [PATCHv2] gdb/python: fix 'exited' event when GDB exits from core file debugging Date: Sat, 6 Jun 2026 18:32:49 +0100 Message-Id: <7db5bc5d0e3c7189881540ab2c775ea20fc03b5f.1780767159.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: aRFX3V-CB3QASdSvtNoXOLwuf7Ki9VBUUq2uIMX3IlY_1780767182 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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 This fixes an issue that was reported here: https://inbox.sourceware.org/gdb-patches/v3x4md2dg6rflq35ymzwrmmqf5uaem5exrnlbsp5dmhph2vihy@lq22ncu774yu After commit: commit 3780b9993c973a2b68b496b80eddb820c0932cc0 Date: Fri Mar 27 11:29:07 2026 +0000 gdb: refactor core_target ::close and ::detach functions it was observed that the Python 'exited' event was no longer being emitted when debugging a core file, and then exiting GDB. The problem is that, when GDB is exiting we eventually end up in quit_force (in top.c), which calls kill_or_detach for every inferior. In kill_or_detach we call either target_detach or target_kill, but only for non-core file targets. For core file targets, neither of these is called and kill_or_detach does nothing of interest. After the call to kill_or_detach, we call inferior::pop_all_targets, which calls inferior::pop_all_targets_above the dummy_stratum target, which means popping all targets. In inferior::pop_all_targets_above (in inferior.c), we call switch_to_inferior_no_thread, which ensures the correct inferior is selected, but makes it so that no thread is selected. Switching to no thread sets inferior_ptid to null_ptid. Now popping the core_target calls core_target::close, and within core_target::close we currently check inferior_ptid in order to determine if exit_core_file_inferior has already been called or not. We only call exit_core_file_inferior if inferior_ptid is not null_ptid, so in this case we will not call exit_core_file_inferior. The only other place that exit_core_file_inferior can be called from is core_target::detach, but remember we specifically avoided calling target_detach earlier in kill_or_detach. This means that exit_core_file_inferior ends up never being called. It is exit_core_file_inferior that calls exit_inferior, and it is from here that the Python 'exited' event is emitted. I don't see any reason why kill_or_detach couldn't call target_detach for a core file target, but I don't propose making that change in this commit. The check against inferior_ptid in core_target::close is clearly incorrect, checking this requires that a suitable thread within the inferior be selected, and that is not really a requirement for closing a core_target. Instead, we can just check the inferior::pid field. When we open a core_target we always set inferior::pid, even if we just assign a fake CORELOW_PID value, so checking inferior::pid against zero will tell us if the inferior has already been exited. Fixing this check is enough to resolve the reported bug and ensure that the 'exited' event is always emitted, which is why I don't propose changing kill_or_detach in this commit. An assert in core_target::exit_core_file_inferior has to go too for the same reason, the assert is checking that a thread is currently selected, and as discussed above, this is not always the case. There's a new test which checks that the 'exited' event is emitted for both a core file debug session, and a live inferior debug session. Only the core file case was broken before this commit, but more testing is always a good thing. --- gdb/corelow.c | 13 +- .../gdb.python/py-inf-exited-at-exit.c | 29 ++++ .../gdb.python/py-inf-exited-at-exit.exp | 125 ++++++++++++++++++ .../gdb.python/py-inf-exited-at-exit.py | 20 +++ 4 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 gdb/testsuite/gdb.python/py-inf-exited-at-exit.c create mode 100644 gdb/testsuite/gdb.python/py-inf-exited-at-exit.exp create mode 100644 gdb/testsuite/gdb.python/py-inf-exited-at-exit.py diff --git a/gdb/corelow.c b/gdb/corelow.c index 819e7cae6f9..185b8da90de 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -629,10 +629,6 @@ core_target::build_file_mappings () void core_target::exit_core_file_inferior () { - /* Opening a core file ensures that some thread, even if it's just a - "fake" thread, will have been selected. */ - gdb_assert (inferior_ptid != null_ptid); - /* Avoid confusion from thread stuff. */ switch_to_no_thread (); @@ -665,10 +661,11 @@ core_target::close () mostly harmless except it causes two 'exited' events to be emitted in the Python API, which isn't ideal. - As opening a core_target always ensures that some thread is selected, - then we can tell if exit_core_file_inferior has already been called by - checking if no thread is now selected. */ - if (inferior_ptid != null_ptid) + As opening a core_target always ensures that a pid is assigned to the + core file inferior, even if it is the fake CORELOW_PID, then we can + tell if exit_core_file_inferior has already been called by checking if + the inferior has a non-zero pid or not. */ + if (current_inferior ()->pid != 0) exit_core_file_inferior (); /* Core targets are heap-allocated (see core_target_open), so here diff --git a/gdb/testsuite/gdb.python/py-inf-exited-at-exit.c b/gdb/testsuite/gdb.python/py-inf-exited-at-exit.c new file mode 100644 index 00000000000..068e2e6a509 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-inf-exited-at-exit.c @@ -0,0 +1,29 @@ +/* Copyright 2026 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 . */ + +void +foo (void) +{ + /* Nothing. */ +} + +int +main (void) +{ + foo (); + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-inf-exited-at-exit.exp b/gdb/testsuite/gdb.python/py-inf-exited-at-exit.exp new file mode 100644 index 00000000000..53e2a2b782c --- /dev/null +++ b/gdb/testsuite/gdb.python/py-inf-exited-at-exit.exp @@ -0,0 +1,125 @@ +# 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 . + +# Check that the 'exited' event triggers when GDB exits. Test for +# both live inferiors, and for core files. + +require allow_python_tests + +load_lib gdb-python.exp + +standard_testfile + +if {[build_executable "build executable" $testfile $srcfile] == -1} { + return +} + +set remote_python_file \ + [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +# Load the Python script for this test. Record the string +# representation of the current inferior. Then exit GDB. Ensure that +# during the exit we see a single Python 'exited' event associated +# with the expected inferior. +proc source_py_script_and_exit_checking_event {} { + gdb_test_no_output "source $::remote_python_file" \ + "load python script" + + set expected_inferior_string \ + [capture_command_output \ + "python print(str(gdb.selected_inferior()))" ""] + + set inferior_string "" + set event_count 0 + gdb_test_multiple "with confirm off -- exit" "exit gdb" { + -re "^EVENT: inferior exited event\\. Inferior is (\[^\r\n\]+)\r\n" { + set inferior_string $expect_out(1,string) + incr event_count + exp_continue + } + + eof { + verbose -log "GDB has now exited" + gdb_assert { $expected_inferior_string eq $inferior_string \ + && $event_count == 1 } $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } +} + +# Clean restart using global TESTFILE as the executable, then run to +# 'foo'. Return true on success, otherwise, return false. +proc clean_restart_and_runto_foo {} { + if {[clean_restart $::testfile] == -1} { + return false + } + + if {![runto foo]} { + return false + } + + return true +} + +# Check that the current inferior's backtrace is 'main -> foo'. +proc check_backtrace { testname } { + gdb_test "bt" \ + [multi_line \ + "#0 (?:$::hex in )?foo \\(\\) at \[^\r\n\]+" \ + "#1 (?:$::hex in )?main \\(\\) at \[^\r\n\]+"] \ + $testname +} + +# Create a core file. Start GDB and load the core file. Exit GDB. +# Check that we see an 'exited' event, and that it is associated with +# the correct gdb.Inferior. +proc_with_prefix check_with_corefile {} { + if {![clean_restart_and_runto_foo]} { + return + } + + check_backtrace "backtrace before generating core file" + + set corefile "$::binfile.core" + if {![gdb_gcore_cmd $corefile "dump core file"]} { + return + } + + clean_restart $::testfile + + gdb_core_cmd $corefile "load corefile" + + check_backtrace "backtrace after loading core file" + + source_py_script_and_exit_checking_event +} + +# Start a running inferior. Exit GDB. Check that we see an 'exited' +# event, and that it is associated with the correct gdb.Inferior. +proc_with_prefix check_with_live {} { + if {![clean_restart_and_runto_foo]} { + return + } + + check_backtrace "backtrace before exiting" + + source_py_script_and_exit_checking_event +} + +check_with_live +check_with_corefile diff --git a/gdb/testsuite/gdb.python/py-inf-exited-at-exit.py b/gdb/testsuite/gdb.python/py-inf-exited-at-exit.py new file mode 100644 index 00000000000..b6fe39e4061 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-inf-exited-at-exit.py @@ -0,0 +1,20 @@ +# 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 . + +def exit_event_handler(event): + inf = event.inferior + print("EVENT: inferior exited event. Inferior is " + str(inf)) + +gdb.events.exited.connect(exit_event_handler) base-commit: bd64797371d27c766d551d0bf115d9090f1d0594 -- 2.25.4