From: Tom de Vries Date: Wed, 25 Sep 2024 17:29:57 +0000 (+0200) Subject: [gdb/python] Make sure python sys.exit makes gdb exit X-Git-Tag: gdb-16-branchpoint~807 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dfba4847f6a22eef700917e2718ea847d9f5cc9d;p=thirdparty%2Fbinutils-gdb.git [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 : 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 Approved-By: Tom Tromey PR python/31946 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31946 --- 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 . + +# 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 (). +do_test 0 +do_test 1 +do_test 2 + +# Test sys.exit (None). +do_test None 0 + +# Test sys.exit (). +do_test {"Error Message"} 1 "Error Message" +do_test {""} 1