| # 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 |