# This testcase is part of GDB, the GNU debugger. # 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 <http://www.gnu.org/licenses/>. # This test case looks at GDB's ability to get correct backtraces for a # crashed inferior, recreating it from a live inferior, a corefile and # a gcore. set have_sleep -1 set have_pthread_kill -1 # Use 'thread apply all backtrace' to check if all expected threads # are present, and stopped in the expected locations. Set the global # TEST_LIST to be the a list of regexps expected to match all the # threads. We generate it now so that the list is in the order that # GDB sees the threads. proc thread_apply_all {} { global have_sleep global have_pthread_kill global test_list set test_list { } set unwind_fail false set eol "(?=\r\n)" set hs "\[^\r\n\]*" set cmd "thread apply all backtrace" gdb_test_multiple $cmd "Get thread information" { -re "^$cmd$eol" { exp_continue } -re "^\r\n#$::decimal\\\?\\\?$hs$eol" { set unwind_fail true exp_continue } -re "^\r\n${hs}syscall_task .location=SIGNAL_ALT_STACK$hs$eol" { lappend test_list 1 exp_continue } -re "^\r\n${hs}syscall_task .location=SIGNAL_HANDLER$hs$eol" { lappend test_list 2 exp_continue } -re "^\r\n${hs}syscall_task .location=NORMAL$hs$eol" { lappend test_list 3 exp_continue } -re "^\r\n${hs}spin_task .location=SIGNAL_ALT_STACK$hs$eol" { lappend test_list 4 exp_continue } -re "^\r\n${hs}spin_task .location=SIGNAL_HANDLER$hs$eol" { lappend test_list 5 exp_continue } -re "^\r\n${hs}spin_task .location=NORMAL$hs$eol" { lappend test_list 6 exp_continue } -re "^\r\n${hs}in main$hs$eol" { lappend test_list 7 exp_continue } -re "^\r\n${hs} in sleep $hs$eol" { set have_sleep 1 exp_continue } -re "^\r\n${hs} in pthread_kill $hs$eol" { set have_pthread_kill 1 exp_continue } -re "^\r\n$hs$eol" { exp_continue } -re "^\r\n$::gdb_prompt $" { pass $gdb_test_name } } gdb_assert {$unwind_fail == false} if { $have_sleep == -1 } { set have_sleep 0 } if { $have_pthread_kill == -1 } { set have_pthread_kill 0 } } # Perform all the tests we're interested in. They are: # * test if we have 7 threads # * Creating the list of backtraces for all threads seen # * testing if GDB recreated the full backtrace we expect for all threads proc do_full_test {} { global have_sleep global have_pthread_kill global test_list set thread_count [get_valueof "" "\$_inferior_thread_count" 0] gdb_assert {$thread_count == 7} thread_apply_all gdb_assert {$thread_count == [llength $test_list]} if { $have_sleep } { set sleep ".*sleep.*" } else { set sleep ".*" } if { $have_pthread_kill } { set pthread_kill ".*pthread_kill.*" } else { set pthread_kill ".*" } for {set i 0} {$i < $thread_count } {incr i} { set thread_num [expr [llength $test_list] - $i] set type [lindex $test_list $i] if { $type == 1 } { set re \ [multi_line \ $sleep \ ".*do_syscall_task .location=SIGNAL_ALT_STACK.*" \ ".*signal_handler.*" \ ".*signal handler called.*" \ $pthread_kill \ ".*thread_function.*"] } elseif { $type == 2 } { set re \ [multi_line \ $sleep \ ".*do_syscall_task .location=SIGNAL_HANDLER.*" \ ".*signal_handler.*" \ ".*signal handler called.*" \ $pthread_kill \ ".*thread_function.*"] } elseif { $type == 3 } { set re \ [multi_line \ $sleep \ ".*do_syscall_task .location=NORMAL.*" \ ".*thread_function.*"] } elseif { $type == 4 } { set re \ [multi_line \ ".*do_spin_task .location=SIGNAL_ALT_STACK.*" \ ".*signal_handler.*" \ ".*signal handler called.*" \ $pthread_kill \ ".*thread_function.*"] } elseif { $type == 5 } { set re \ [multi_line \ ".*do_spin_task .location=SIGNAL_HANDLER.*" \ ".*signal_handler.*" \ ".*signal handler called.*" \ $pthread_kill \ ".*thread_function.*"] } elseif { $type == 6 } { set re \ [multi_line \ ".*do_spin_task .location=NORMAL..*" \ ".*thread_function.*"] } elseif { $type == 7 } { set re ".*main.*" } else { error "invalid type: $type" } gdb_test "thread apply $thread_num backtrace" $re } } # Do all preparation steps for running the corefile tests, then # call do_full_test to actually run the tests. proc_with_prefix test_live_inferior {} { gdb_test "handle SIGUSR1 nostop print pass" \ ".*SIGUSR1.*No.*Yes.*Yes.*User defined signal 1" \ "setup SIGUSR1" gdb_test "handle SIGUSR2 nostop print pass" \ ".*SIGUSR2.*No.*Yes.*Yes.*User defined signal 2" \ "setup SIGUSR2" if {![runto_main]} { return } gdb_breakpoint "breakpt" gdb_continue_to_breakpoint "running to breakpoint" ".*" do_full_test } # Do all preparation steps for running the corefile tests, then # call do_full_test to actually run the tests. proc_with_prefix test_corefile {} { set corefile [core_find $::binfile] if { $corefile == "" } { untested "couldn't generate corefile" return } set corefile [gdb_remote_download host $corefile] gdb_test "core-file $corefile" \ "" \ "loading_corefile" \ "A program is being debugged already\\\. Kill it\\\? \\\(y or n\\\) " \ "y" do_full_test } # Do all preparation steps for running the gcore tests, then # call do_full_test to actually run the tests. proc_with_prefix test_gcore {} { clean_restart "$::binfile" gdb_test "handle SIGUSR1 nostop print pass" \ ".*SIGUSR1.*No.*Yes.*Yes.*User defined signal 1" \ "setup SIGUSR1" gdb_test "handle SIGUSR2 nostop print pass" \ ".*SIGUSR2.*No.*Yes.*Yes.*User defined signal 2" \ "setup SIGUSR2" if {![runto_main]} { return -1 } gdb_test "continue" ".*Segmentation fault.*" "continue to crash" set gcore_host [host_standard_output_file $::testfile.gcore] set gcore_supported [gdb_gcore_cmd "$gcore_host" "saving gcore"] if {!$gcore_supported} { unsupported "couldn't generate gcore file" return } gdb_test "core-file $gcore_host" \ "" \ "loading_corefile" \ "A program is being debugged already\\\. Kill it\\\? \\\(y or n\\\) " \ "y" do_full_test } standard_testfile if [prepare_for_testing "failed to prepare" $testfile $srcfile \ {debug pthreads}] { return -1 } clean_restart ${binfile} gdb_test_no_output "set backtrace limit unlimited" test_live_inferior test_corefile test_gcore