binutils-gdb/gdb/testsuite/gdb.base/corefile.exp
Pedro Alves 5d067f3d41 Fix core-file -> detach -> crash (corefiles/29275)
After loading a core file, you're supposed to be able to use "detach"
to unload the core file.  That unfortunately regressed starting with
GDB 11, with these commits:

 1192f124a3 - gdb: generalize commit_resume, avoid commit-resuming when threads have pending statuses
 408f66864a - detach in all-stop with threads running

resulting in a GDB crash:

 ...
 Thread 1 "gdb" received signal SIGSEGV, Segmentation fault.
 0x0000555555e842bf in maybe_set_commit_resumed_all_targets () at ../../src/gdb/infrun.c:2899
 2899          if (proc_target->commit_resumed_state)
 (top-gdb) bt
 #0  0x0000555555e842bf in maybe_set_commit_resumed_all_targets () at ../../src/gdb/infrun.c:2899
 #1  0x0000555555e848bf in scoped_disable_commit_resumed::reset (this=0x7fffffffd440) at ../../src/gdb/infrun.c:3023
 #2  0x0000555555e84a0c in scoped_disable_commit_resumed::reset_and_commit (this=0x7fffffffd440) at ../../src/gdb/infrun.c:3049
 #3  0x0000555555e739cd in detach_command (args=0x0, from_tty=1) at ../../src/gdb/infcmd.c:2791
 #4  0x0000555555c0ba46 in do_simple_func (args=0x0, from_tty=1, c=0x55555662a600) at ../../src/gdb/cli/cli-decode.c:95
 #5  0x0000555555c112b0 in cmd_func (cmd=0x55555662a600, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:2514
 #6  0x0000555556173b1f in execute_command (p=0x5555565c5916 "", from_tty=1) at ../../src/gdb/top.c:699

The code that crashes looks like:

 static void
 maybe_set_commit_resumed_all_targets ()
 {
   scoped_restore_current_thread restore_thread;

   for (inferior *inf : all_non_exited_inferiors ())
     {
       process_stratum_target *proc_target = inf->process_target ();

       if (proc_target->commit_resumed_state)
           ^^^^^^^^^^^

With 'proc_target' above being null.  all_non_exited_inferiors filters
out inferiors that have pid==0.  We get here at the end of
detach_command, after core_target::detach has already run, at which
point the inferior _should_ have pid==0 and no process target.  It is
clear it no longer has a process target, but, it still has a pid!=0
somehow.

The reason the inferior still has pid!=0, is that core_target::detach
just unpushes, and relies on core_target::close to actually do the
getting rid of the core and exiting the inferior.  The problem with
that is that detach_command grabs an extra strong reference to the
process stratum target, so the unpush_target inside
core_target::detach doesn't actually result in a call to
core_target::close.

Fix this my moving the cleaning up the core inferior to a shared
routine called by both core_target::close and core_target::detach.  We
still need to cleanup the inferior from within core_file::close
because there are paths to it that want to get rid of the core without
going through detach.  E.g., "core-file" -> "run".

This commit includes a new test added to gdb.base/corefile.exp to
cover the "core-file core" -> "detach" scenario.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29275

Change-Id: Ic42bdd03182166b19f598428b0dbc2ce6f67c893
2022-07-11 19:21:33 +01:00

352 lines
11 KiB
Plaintext

# Copyright 1992-2022 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/>.
# This file was written by Fred Fish. (fnf@cygnus.com)
# are we on a target board
if ![isnative] then {
return
}
standard_testfile coremaker.c
if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} {
untested "failed to compile"
return -1
}
# Do not delete coremap.data when calling core_find. This file is
# required for GDB to find mmap'd data in the "accessing read-only
# mmapped data in core file" test.
set corefile [core_find $binfile {}]
if {$corefile == ""} {
return 0
}
# Test that we can simply startup with a "-core=$corefile" command line arg
# and recognize that the core file is a valid, usable core file.
# To do this, we must shutdown the currently running gdb and restart
# with the -core args. We can't use gdb_start because it looks for
# the first gdb prompt, and the message we are looking for occurs
# before the first prompt.
#
# Another problem is that on some systems (solaris for example), there
# is apparently a limit on the length of a fully specified path to
# the corefile executable, at about 80 chars. For this case, consider
# it a pass, but note that the program name is bad.
gdb_exit
if $verbose>1 then {
send_user "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS -core=$corefile\n"
}
set oldtimeout $timeout
set timeout [expr "$timeout + 60"]
verbose "Timeout is now $timeout seconds" 2
eval "spawn $GDB $INTERNAL_GDBFLAGS $GDBFLAGS -core=$corefile"
expect {
-re "Couldn't find .* registers in core file.*$gdb_prompt $" {
fail "args: -core=[file tail $corefile] (couldn't find regs)"
}
-re "Core was generated by .*corefile.*\r\n\#0 .*\(\).*\r\n$gdb_prompt $" {
pass "args: -core=[file tail $corefile]"
}
-re "Core was generated by .*\r\n\#0 .*\(\).*\r\n$gdb_prompt $" {
pass "args: -core=[file tail $corefile] (with bad program name)"
}
-re ".*registers from core file: File in wrong format.* $" {
fail "args: -core=[file tail $corefile] (could not read registers from core file)"
}
-re ".*$gdb_prompt $" { fail "args: -core=[file tail $corefile]" }
timeout { fail "(timeout) starting with -core" }
}
#
# Test that startup with both an executable file and -core argument.
# See previous comments above, they are still applicable.
#
close
if $verbose>1 then {
send_user "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS $binfile -core=$corefile\n"
}
eval "spawn $GDB $INTERNAL_GDBFLAGS $GDBFLAGS $binfile -core=$corefile"
expect {
-re "Core was generated by .*corefile.*\r\n\#0 .*\(\).*\r\n$gdb_prompt $" {
pass "args: execfile -core=[file tail $corefile]"
}
-re "Core was generated by .*\r\n\#0 .*\(\).*\r\n$gdb_prompt $" {
pass "args: execfile -core=[file tail $corefile] (with bad program name)"
}
-re ".*registers from core file: File in wrong format.* $" {
fail "args: execfile -core=[file tail $corefile] (could not read registers from core file)"
}
-re ".*$gdb_prompt $" { fail "args: execfile -core=[file tail $corefile]" }
timeout { fail "(timeout) starting with -core" }
}
set timeout $oldtimeout
verbose "Timeout is now $timeout seconds" 2
close
# Now restart normally.
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
# Test basic corefile recognition via core-file command.
gdb_test_multiple "core-file $corefile" "core-file command" {
-re ".* program is being debugged already.*y or n. $" {
# gdb_load may connect us to a gdbserver.
send_gdb "y\n"
exp_continue
}
-re "Core was generated by .*corefile.*\r\n\#0 .*\(\).*\r\n$gdb_prompt $" {
pass "core-file command"
}
-re "Core was generated by .*\r\n\#0 .*\(\).*\r\n$gdb_prompt $" {
pass "core-file command (with bad program name)"
}
-re ".*registers from core file: File in wrong format.* $" {
fail "core-file command (could not read registers from core file)"
}
}
# Test correct mapping of corefile sections by printing some variables.
gdb_test "print coremaker_data" "\\\$$decimal = 202"
gdb_test "print coremaker_bss" "\\\$$decimal = 10"
gdb_test "print coremaker_ro" "\\\$$decimal = 201"
gdb_test "print func2::coremaker_local" "\\\$$decimal = \\{0, 1, 2, 3, 4\\}"
# Test the presence and the correct values of $_exitsignal and
# $_exitcode variables. The corefile is generated with a SIGABRT,
# which is "6" in the Linux kernel.
gdb_test "print \$_exitsignal" " = 6" \
"\$_exitsignal prints SIGABRT (6)"
gdb_test "print \$_exitcode" " = void" \
"\$_exitcode is void"
# Somehow we better test the ability to read the registers out of the core
# file correctly. I don't think the other tests do this.
gdb_test "bt" "abort.*func2.*func1.*main.*" "backtrace in corefile.exp"
gdb_test "up" "#\[0-9\]* *\[0-9xa-fH'\]* in .* \\(.*\\).*" "up in corefile.exp"
# Test ability to read mmap'd data
gdb_test "x/8bd buf1" ".*:.*0.*1.*2.*3.*4.*5.*6.*7" "accessing original mmap data in core file"
setup_xfail "*-*-sunos*" "*-*-aix*"
set test "accessing mmapped data in core file"
gdb_test_multiple "x/8bd buf2" "$test" {
-re ".*:.*0.*1.*2.*3.*4.*5.*6.*7.*$gdb_prompt $" {
pass "$test"
}
-re "0x\[f\]*:.*Cannot access memory at address 0x\[f\]*.*$gdb_prompt $" {
fail "$test (mapping failed at runtime)"
}
-re "0x.*:.*Cannot access memory at address 0x.*$gdb_prompt $" {
fail "$test (mapping address not found in core file)"
}
}
set test "accessing read-only mmapped data in core file"
gdb_test_multiple "x/8bd buf2ro" "$test" {
-re ".*:.*0.*1.*2.*3.*4.*5.*6.*7.*$gdb_prompt $" {
pass "$test"
}
-re "0x\[f\]*:.*Cannot access memory at address 0x\[f\]*.*$gdb_prompt $" {
fail "$test (mapping failed at runtime)"
}
-re "0x.*:.*Cannot access memory at address 0x.*$gdb_prompt $" {
fail "$test (mapping address not found in core file)"
}
}
# Test ability to read anonymous and, more importantly, unwritten-to
# mmap'd data.
if { ![istarget *-linux*] } {
setup_xfail "*-*-*"
}
gdb_test "x/wx buf3" "$hex:\[ \t\]+0x00000000" \
"accessing anonymous, unwritten-to mmap data"
# test reinit_frame_cache
gdb_load ${binfile}
gdb_test "up" "#\[0-9\]* *\[0-9xa-fH'\]* in .* \\(.*\\).*" "up in corefile.exp (reinit)"
gdb_test "core" "No core file now."
# Test that we can unload the core with the "detach" command.
proc_with_prefix corefile_detach {} {
clean_restart $::binfile
gdb_test "core-file $::corefile" "Core was generated by .*" "load core"
gdb_test "detach" "No core file now\\." "detach core"
}
corefile_detach
# Test a run (start) command will clear any loaded core file.
proc corefile_test_run {} {
global corefile gdb_prompt
# This test is trying to check whether the "run" command finds the
# default run target when already debugging a core, so it would
# fail on boards that set auto-connect-native-target off. Since
# there's no real point in running the test but with the native
# target, it's easier to just skip elsewhere.
if {[target_info gdb_protocol] != ""} {
return
}
clean_restart $::binfile
gdb_test "core-file $corefile" "Core was generated by .*" "run: load core again"
gdb_test "info files" "\r\nLocal core dump file:\r\n.*" "run: sanity check we see the core file"
set test "run: with core"
if [runto_main] {
pass $test
} else {
fail $test
}
set test "run: core file is cleared"
gdb_test_multiple "info files" $test {
-re "\r\nLocal core dump file:\r\n.*\r\n$gdb_prompt $" {
fail $test
}
-re "\r\n$gdb_prompt $" {
pass $test
}
}
set test "quit with a process"
gdb_test_multiple "quit" $test {
-re "A debugging session is active.\r\n.*\r\nQuit anyway\\? \\(y or n\\) $" {
pass $test
gdb_test "n" {Not confirmed\.} "quit with processes: n"
}
}
gdb_exit
}
corefile_test_run
# Verify there is no question if only a core file is loaded.
gdb_start
gdb_test "core-file $corefile" "Core was generated by .*" "no question: load core"
set test "quit with a core file"
gdb_test_multiple "quit" $test {
-re "A debugging session is active.\r\n.*\r\nQuit anyway\\? \\(y or n\\) $" {
fail $test
gdb_test "n" {Not confirmed\.} "quit with processes: n"
}
eof {
pass $test
}
}
gdb_exit
# Test an attach command will clear any loaded core file.
proc corefile_test_attach {} {
global binfile corefile gdb_prompt
# This test is checking whether the "attach" command finds the
# default run target when already debugging a core, so it would
# fail on boards that set auto-connect-native-target off. Since
# there's no real point in running the test but with the native
# target, it's easier to just skip elsewhere.
if {[target_info gdb_protocol] != ""} {
return
}
if [can_spawn_for_attach] {
set test "attach: spawn sleep"
set res [remote_spawn host "$binfile sleep"]
if { $res < 0 || $res == "" } {
fail $test
return
}
set pid [exp_pid -i $res]
# We don't care whether the program is still in the startup phase when we
# attach.
gdb_start
gdb_test "core-file $corefile" "Core was generated by .*" "attach: load core again"
gdb_test "info files" "\r\nLocal core dump file:\r\n.*" "attach: sanity check we see the core file"
gdb_test "attach $pid" "Attaching to process $pid\r\n.*" "attach: with core"
set test "attach: core file is cleared"
gdb_test_multiple "info files" $test {
-re "\r\nLocal core dump file:\r\n.*\r\n$gdb_prompt $" {
fail $test
}
-re "\r\n$gdb_prompt $" {
pass $test
}
}
gdb_exit
}
}
corefile_test_attach
# Test warning-free core file load. E.g., a Linux vDSO used to
# trigger this warning:
# warning: Can't read pathname for load map: Input/output error.
#
# When testing in a docker container using the AUFS storage driver,
# the kernel places host paths in the core file's NT_FILE note. XFAIL
# this case since these paths make no sense in the container.
clean_restart ${testfile}
set test "core-file warning-free"
gdb_test_multiple "core-file $corefile" $test {
-re "warning: Can\'t open file.*\/docker\/aufs\/.*\r\n$gdb_prompt $" {
xfail $test
}
-re "warning: .*\r\n.*\r\n$gdb_prompt $" {
fail $test
}
-re "\r\n$gdb_prompt $" {
pass $test
}
}