[PATCH v4] [gdb/python] Make sure python sys.exit makes gdb exit

Tom de Vries tdevries@suse.de
Wed Sep 25 15:12:29 GMT 2024


With gdb 15.1, python sys.exit no longer makes gdb exit:
...
$ gdb -q -batch -ex "python sys.exit(2)" -ex "print 123"; echo $?
Python Exception <class 'SystemExit'>: 2
Error occurred in Python: 2
$1 = 123
0
...

This is a change in behaviour since commit a207f6b3a38 ("Rewrite "python"
command exception handling"), first available in gdb 15.1.

This patch reverts to the old behaviour by handling PyExc_SystemExit in
gdbpy_handle_exception, such what we have instead:
...
$ gdb -q -batch -ex "python sys.exit(2)" -ex "print 123"; echo $?
2
...

Tested on x86_64-linux, with python 3.6 and 3.13.

Tested-By: Guinevere Larsen <blarsen@redhat.com>

PR python/31946
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31946
---
 gdb/python/py-utils.c                 | 29 +++++++++++
 gdb/testsuite/gdb.python/sys-exit.exp | 69 +++++++++++++++++++++++++++
 2 files changed, 98 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/sys-exit.exp

diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 8064cec73a7..0bc18a668b7 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -390,6 +390,35 @@ gdbpy_handle_exception ()
 
   if (fetched_error.type_matches (PyExc_KeyboardInterrupt))
     throw_quit ("Quit");
+  else if (fetched_error.type_matches (PyExc_SystemExit))
+    {
+      gdbpy_ref<> value = fetched_error.value ();
+      gdbpy_ref<> code (PyObject_GetAttrString (value.get (), "code"));
+      int exit_arg;
+
+      if (code.get () == Py_None)
+	{
+	  /* CODE == None: exit status is 0.  */
+	  exit_arg = 0;
+	}
+      else if (code.get () != nullptr && PyLong_Check (code.get ()))
+	{
+	  /* CODE == integer: exit status is aforementioned integer.  */
+	  exit_arg = PyLong_AsLong (code.get ());
+	}
+      else
+	{
+	  if (code.get () == nullptr)
+	    gdbpy_print_stack ();
+
+	  /* Otherwise: exit status is 1, print code to stderr.  */
+	  if (msg != nullptr)
+	    gdb_printf (gdb_stderr, "%s\n", msg.get ());
+	  exit_arg = 1;
+	}
+
+      quit_force (&exit_arg, 0);
+    }
   else if (! fetched_error.type_matches (gdbpy_gdberror_exc)
 	   || msg == NULL || *msg == '\0')
     {
diff --git a/gdb/testsuite/gdb.python/sys-exit.exp b/gdb/testsuite/gdb.python/sys-exit.exp
new file mode 100644
index 00000000000..3396b8dbb04
--- /dev/null
+++ b/gdb/testsuite/gdb.python/sys-exit.exp
@@ -0,0 +1,69 @@
+# Copyright 2024 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 <http://www.gnu.org/licenses/>.
+
+# Check that python sys.exit makes gdb exit, with the correct exit status.
+
+require allow_python_tests
+
+# Have this code in a proc avoids clashing with runtest variable exit_status.
+
+proc do_test { n {expected_exit_status ""} {msg ""}} {
+    if { $expected_exit_status == "" } {
+	set expected_exit_status $n
+    }
+
+    with_test_prefix $n {
+	clean_restart
+
+	# Regression test for PR python/31946.
+	set seen_message 0
+	gdb_test_multiple "python sys.exit ($n)" "python sys.exit" {
+	    -re "\r\n$msg\r\n" {
+		set seen_message 1
+		exp_continue
+	    }
+	    eof {
+		set wait_status [wait -i $::gdb_spawn_id]
+		clear_gdb_spawn_id
+
+		verbose -log "GDB process exited with wait status $wait_status"
+
+		set os_error [lindex $wait_status 2]
+		set exit_status [lindex $wait_status 3]
+
+		gdb_assert \
+		    { $os_error == 0 \
+			  && $exit_status == $expected_exit_status } \
+		    $gdb_test_name
+
+		if { $msg != "" } {
+		    gdb_assert { $seen_message }
+		}
+	    }
+	}
+    }
+}
+
+# Test sys.exit (<int>).
+do_test 0
+do_test 1
+do_test 2
+
+# Test sys.exit (None).
+do_test None 0
+
+# Test sys.exit (<string>).
+do_test {"Error Message"} 1 "Error Message"
+do_test {""} 1

base-commit: fc14343205d3a68db1fc139e4af9796be208fab4
-- 
2.35.3



More information about the Gdb-patches mailing list