[binutils-gdb/gdb-15-branch] [gdb/python] Make sure python sys.exit makes gdb exit

Tom de Vries vries@sourceware.org
Wed Sep 25 22:36:10 GMT 2024


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4ff12ed8a6c4e29724139cb7f7684c895b8ae8c2

commit 4ff12ed8a6c4e29724139cb7f7684c895b8ae8c2
Author: Tom de Vries <tdevries@suse.de>
Date:   Thu Sep 26 00:36:21 2024 +0200

    [gdb/python] Make sure python sys.exit makes gdb exit
    
    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>
    Approved-By: Tom Tromey <tom@tromey.com>
    
    PR python/31946
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31946

Diff:
---
 gdb/python/py-utils.c                 | 29 +++++++++++++++
 gdb/testsuite/gdb.python/sys-exit.exp | 69 +++++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+)

diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 47f65f4fd44..39ae9b5a7b6 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


More information about the Gdb-cvs mailing list