# 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