blob: 3b6764abff7d91b786e8995e6501dfbd675d3554 [file]
# 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 <http://www.gnu.org/licenses/>.
# 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" -lbl {
-re "\r\nEVENT: 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
# Clean up now that GDB has gone away. This prevents the
# generic support code from trying to shut down GDB again.
catch {wait -nowait -i $::gdb_spawn_id}
clean_up_spawn_id host $::gdb_spawn_id
unset ::gdb_spawn_id
}
}
}
# 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
}
return [runto foo]
}
# 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 [host_standard_output_file $::testfile.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 the test program, attach to it, and then exit GDB. Check that
# we see an 'exited' event, and that it is associated with the correct
# gdb.Inferior.
proc_with_prefix check_with_attach {} {
if {![can_spawn_for_attach]} {
return
}
set test_spawn_id [spawn_wait_for_attach $::binfile]
set testpid [spawn_id_get_pid $test_spawn_id]
clean_restart $::testfile
gdb_breakpoint [gdb_get_line_number "Break here."]
if { ![gdb_attach $testpid] } {
kill_wait_spawned_process $test_spawn_id
return
}
gdb_continue_to_breakpoint "continue to b/p in foo"
check_backtrace "backtrace after attaching"
# When GDB detaches on exit, this should ensure the test program
# runs to completion.
gdb_test "set global_var = 1"
source_py_script_and_exit_checking_event
# In case the test program doesn't self-terminate after detach,
# kill it.
kill_wait_spawned_process $test_spawn_id
}
# 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
check_with_attach