# Copyright 2023 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 . # This test checks that GDB correctly handles several cases that can # occur when GDB attempts to detach an inferior process. The process # can exit or be terminated (e.g. via SIGKILL) prior to GDB's event # loop getting a chance to remove it from GDB's internal data # structures. To complicate things even more, detach works differently # when a checkpoint (created via GDB's "checkpoint" command) exists for # the inferior. This test checks all four possibilities: process exit # with no checkpoint, process termination with no checkpoint, process # exit with a checkpoint, and process termination with a checkpoint. standard_testfile # This test requires python. require allow_python_tests # This test attempts to kill a process on the host running GDB, so # disallow remote targets. (Setting --target_board to # native-gdbserver or native-extended-gdbserver should still work.) require {!is_remote target} # Checkpoint support only works on native Linux: if { [istarget "*-*-linux*"] && [target_info gdb_protocol] == ""} { set has_checkpoint true } else { set has_checkpoint false } set flags {} lappend flags debug lappend flags additional_flags=-DBINFILE=$binfile if {[build_executable "failed to prepare" $testfile $srcfile $flags] == -1} { return -1 } set checkpoint_line [gdb_get_line_number "Checkpoint here"] # Start an inferior, which blocks in a spin loop. Setup a Python # function that performs an action based on EXIT_P that will cause the # inferior to exit, and then, within the same Python function, ask GDB # to detach from the inferior. Use 'continue&' to run the inferior in # the background, and then invoke the Python function. Note, too, that # non-stop mode is enabled during the restart; if this is not done, # remote_target::putpkt_binary in remote.c will disallow some of the # operations necessary for this test. # # The idea is that GDB's event loop will not get a chance to handle # the inferior exiting, so it will only be at the point that we try to # detach that we notice that the inferior has exited. # # When EXIT_P is true the action we perform to terminate the inferior # is to set a flag in the inferior, which allows the inferior to break # out of its spin loop. # # When EXIT_P is false the action we perform is to send SIGKILL to the # inferior. # # When CHECKPOINT_P is true, before issuing 'continue&' we use the # 'checkpoint' command to create a checkpoint of GDB. # # When CHECKPOINT_P is false we don't use the 'checkpoint' command. proc run_test { exit_p checkpoint_p } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"set non-stop on\"" clean_restart $::binfile } if {![runto_main]} { return -1 } if { $checkpoint_p } { # Active checkpoint-specific code in $srcfile. gdb_test_no_output "set var with_checkpoint=1" # Run to line where we want to set the checkpoint. gdb_breakpoint "$::srcfile:$::checkpoint_line" gdb_continue_to_breakpoint "checkpoint line" # Set the checkpoint. gdb_test "checkpoint" \ "checkpoint 1: fork returned pid $::decimal\\." } # Must get the PID before we resume the inferior. set inf_pid [get_inferior_pid] # Put the PID in a python variable so that a numerical PID won't # appear in the PASS/FAIL output. gdb_test_no_output "python inf_pid=$inf_pid" "assign inf_pid" gdb_test "continue &" if { $exit_p } { set action_line "gdb.execute(\"set variable dont_exit_just_yet=0\")" } else { set action_line "os.kill(inf_pid, signal.SIGKILL)" } gdb_test_multiline "Create worker function" \ "python" "" \ "import time" "" \ "import os" "" \ "import signal" "" \ "def kill_and_detach():" "" \ " $action_line" "" \ " time.sleep(1)" "" \ " gdb.execute(\"detach\")" "" \ "end" "" if { $checkpoint_p } { # NOTE: The 'checkpoint' system in GDB appears to be a little # iffy. This detach does seem to restore the checkpoint, but # it leaves the inferior stuck in a running state. gdb_test_no_output "python kill_and_detach()" } else { gdb_test "python kill_and_detach()" \ "\\\[Inferior $::decimal \[^\r\n\]+ detached\\\]" } } if { $has_checkpoint } { set checkpoint_iters { true false } } else { set checkpoint_iters { false } } foreach_with_prefix exit_p { true false } { foreach_with_prefix checkpoint_p $checkpoint_iters { run_test $exit_p $checkpoint_p } }