#   Copyright (C) 1988, 1990, 1991, 1992, 1994 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

# Please email any bugs, comments, and/or additions to this file to:
# bug-gdb@prep.ai.mit.edu

# This file was written by Rob Savoye. (rob@cygnus.com)

if $tracelevel then {
	strace $tracelevel
	}

set prms_id 0
set bug_id 0

# are we on a target board
if ![isnative] then {
    continue
}
if [istarget "m68k*-*-hpux*"] then {
    # The top-level makefile passes CFLAGS= (no -g) for hp300.  This probably
    # should be fixed (it is only needed for gcc bootstrapping, not gdb),
    # but until then.....
    setup_xfail "*-*-*"
    fail "cannot test self if compiled without debug info"
    continue
}

proc test_with_self {} {
    global prompt
    global tool
    global det_file
    global decimal

    # load yourself into the debugger
    # This can take a relatively long time, particularly for testing where
    # the executable is being accessed over a network, or where gdb does not
    # support partial symbols for a particular target and has to load the
    # entire symbol table.  Set the timeout to 10 minutes, which should be
    # adequate for most environments (it *has* timed out with 5 min on a
    # SPARCstation SLC under moderate load, so this isn't unreasonable).
    # After gdb is started, set the timeout to 30 seconds for the duration
    # of this test, and then back to the original value.

    set timeout 600
    if {[gdb_load "./x$tool"] <0} then {
	return -1
    }

    # disassemble yourself
    send "x/10i main\n"
    expect {
	-re "x/10i.*main.*main.$decimal.*main.$decimal.*$prompt $"\
	    { pass "Disassemble main" }
	-re ".*$prompt $"	{ fail "Disassemble main" }
	timeout		{ fail "(timeout) Disassemble main" }
    }

    set description "set breakpoint at main"
    send "break main\n"
    expect {
	-re "Breakpoint.*at.* file.*, line.*$prompt $" {
	    pass "$description"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    # We'll need this when we send a ^C to GDB.  Need to do it before we
    # run the program and gdb starts saving and restoring tty states.
    # On Ultrix, we don't need it and it is really slow (because shell_escape
    # doesn't use vfork).
    if ![istarget "*-*-ultrix*"] then {
	gdb_test "shell stty intr '^C'" "" \
	    "set interrupt character in test_with_self"
    }

    # run yourself
    set description "run until breakpoint at main"
    send "run\n"
    expect {
	-re "Starting program.*Breakpoint \[0-9\]+,.*main .argc.*argv.* at .*main.c:.*$prompt $" {
	    pass "$description"
	}
	-re "Starting program.*Breakpoint \[0-9\]+,.*main .argc.*argv.*$prompt $" {
	    xfail "$description (line numbers scrambled?)"
	}
	-re "vfork: No more processes.*$prompt $" {
	    fail "$description (out of virtual memory)"
	    return -1
	}
	-re ".*$prompt $" {
	    fail "$description"
	    return -1
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    # It may take a very long time for the inferior gdb to start (lynx),
    # so we wait until after it's started to reset the timeout value.
    set timeout 30

    # do we have a version number ?
    send "print version\n"
    expect {
	-re ".\[0-9\]+ = +0x.*\[0-9.\]+.*$prompt $" {
	    pass "printed version"
	}
	-re ".\[0-9\]+ = +.+ +0x.*\[0-9.\]+.*$prompt $" {
	    pass "printed version with cast"
	}
	-re ".*$prompt $"	{ fail "printed version" }
	timeout		{ fail "(timeout) printed version" }
    }

    # lets do a few single steps

    set description "step over symarg initialization"
    send "step\n"
    expect {
	-re "char \[*\]+execarg = NULL;.*$prompt $" {
	    pass "$description"
	}
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    set description "step over execarg initialization"
    send "step\n"
    expect {
	-re "char \[*\]corearg = NULL;.*$prompt $" {
	    pass "$description"
	}
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    set description "step over corearg initialization"
    send "step\n"
    expect {
	-re "char \[*\]cdarg = NULL;.*$prompt $" {
	    pass "$description"
	}
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }
    
    set description "step over cdarg initialization"
    send "step\n"
    expect {
	-re "char \[*\]ttyarg = NULL;.*$prompt $" {
	    pass "$description"
	}
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }
    
    set description "step over ttyarg initialization"
    send "step\n"
    expect {
	-re ".*time_at_startup = get_run_time .*$prompt $" {
	    pass "$description"
	}
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    # now jump over a few functions

    set description "next over get_run_time and everything it calls"
    set got_over_get_run_time 0
    send "next\n"
    expect {
	-re ".*init_malloc.*$prompt $" {
	    pass "$description"
	    set got_over_get_run_time 1
       }
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	    set got_over_get_run_time 1
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	    set got_over_get_run_time 1
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }
    #    -re "if \(setmp \(to_top_level\)\).*$prompt $" { pass "first next" }

    if $got_over_get_run_time then {
	set description "next over init_malloc and everything it calls"
	send "next\n"
	expect {
	    -re "if.*SET_TOP_LEVEL.*$prompt $" {
	        pass "$description"
	    }
	    -re "i = .*count.*$prompt $" {
	      pass "$description"
	      set description "next over ALIGN_STACK_ON_STARTUP code"
	      send "next\n"
	      expect {
		-re "if.*i != 0.*$prompt $" {
		    pass "$description"
		    send "next\n"
		    expect {
			-re "if.*SET_TOP_LEVEL.*$prompt $" {
			    pass "$description"
			}
			-re "alloca.*$prompt $" {
			    send "next\n"
			    expect {
				-re "if.*level.*$prompt $" {
				    pass "$description"
				}
			    }
			}
		    }
		}
	      }
	    }
	    -re ".*No such file or directory.\r\n$prompt $" {
		pass "$description (no source available)"
	    }
	    -re ".*A file or directory .* does not exist..\r\n$prompt $" {
		pass "$description (no source available)"
	    }
	    -re ".*$prompt $" {
		fail "$description"
	    }
	    default {
		fail "$description (timeout or eof)"
	    }
	}
    }

    set next_ok 1
    set need_to_step_over_cmdsize 1
    set description "next over SET_TOP_LEVEL call"
    send "next\n"
    expect {
	-re "cmdsize = 1;.*$prompt $" {
	    pass "$description"
	}
	-re "cmdarg = .*xmalloc.*$prompt $" {
	    # This can happen in optimized code where the "cmdsize = 1" line
	    # is basically removed.
	    pass "$description"
	    set need_to_step_over_cmdsize 0
	}
	-re ".*No such file or directory.\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*A file or directory .* does not exist..\r\n$prompt $" {
	    pass "$description (no source available)"
	}
	-re ".*$prompt $" {
	    fail "$description"
	    set next_ok 0
	}
	timeout {
	    fail "$description (timeout)"
	    set next_ok 0
	}
    }
    
    set description "print a string"
    send "print \"foo\"\n"
    expect {
	-re ".\[0-9\]+ = \"foo\".*$prompt $" {
	    pass "$description"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    if $need_to_step_over_cmdsize {    
	set description "step over cmdsize initialization"
	send "step\n"
	expect {
	    -re "cmdarg = .*xmalloc.*$prompt $" {
		pass "$description"
	    }
	    -re ".*No such file or directory.\r\n$prompt $" {
		pass "$description (no source available)"
	    }
	    -re ".*A file or directory .* does not exist..\r\n$prompt $" {
		pass "$description (no source available)"
	    }
	    -re ".*$prompt $" {
		fail "$description"
	    }
	    timeout {
		fail "$description (timeout)"
	    }
	}
    }

    # and another step for good luck
    if $next_ok {
	set description "step into xmalloc call"
        send "step\n"
        expect {
	    -re "xmalloc.*size=.*at.*utils.c.*$prompt $" {
		pass "$description"
	    }
	    -re ".*No such file or directory.\r\n$prompt $" {
	        pass "$description (no source available)"
	    }
	    -re "A file or directory .* does not exist..\r\n$prompt $" {
		pass "$description (no source available)"
	    }
	    -re ".*$prompt $" {
		fail "$description"
	    }
	    timeout {
		fail "$description (timeout)"
	    }
	}
    }

    # start the "xgdb" process
    send "continue\n"
    expect {
	-re "GDB is free software and you are welcome to distribute copies of it.*
 under certain conditions; type \"show copying\" to see the conditions..*
There is absolutely no warranty for GDB; type \"show warranty\" for details..*
GDB.*Copyright \[0-9\]+ Free Software Foundation, Inc..*$prompt $"\
	    { pass "xgdb is at prompt" }
	-re ".*$prompt $"       { fail "xgdb is at prompt" }
	timeout	            { fail "(timeout) xgdb is at prompt" }
    }
    
    # set xgdb prompt so we can tell which is which
    send "set prompt (xgdb) \n"
    expect {
	-re "\[(\]xgdb\[)\].*\[(\]xgdb\[)\] $"	{ pass "Set xgdb prompt" }
	-re ".*$prompt $"		{ fail "Set xgdb prompt" }
	default				{ fail "(timeout) Set xgdb prompt" }
    }
    
    # kill the xgdb process
    set description "send ^C to child process"
    send "\003"
    expect {
	-re "Program received signal SIGINT.*$prompt $" {
	    pass "$description"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }
    
    set description "send SIGINT signal to child process"
    send "signal SIGINT\n"
    expect {
	-re "Continuing with signal SIGINT.*$prompt $" {
	    pass "$description"
	}
	-re ".*$prompt $" {
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }
    
    # get a stack trace
    setup_xfail "i*86-*-linux*" "i*86-*-sysv4*"
    set description "backtrace through signal handler"
    send "backtrace\n"
    expect {
	-re "#0.*read.*in main \\(.*\\) at .*main\\.c.*$prompt $" {
	    pass "$description"
	}
	-re ".*$prompt $" {
	    # On the alpha, we hit the infamous problem about gdb
	    # being unable to get the frame pointer (mentioned in
	    # gdb/README).  As it is intermittent, there is no way to
	    # XFAIL it which will give us an XPASS if the problem goes
	    # away.
	    setup_xfail "alpha*-*-osf*"
	    fail "$description"
	}
	timeout {
	    fail "$description (timeout)"
	}
    }

    gdb_exit

    # Set the timeout back to the value it had when we were called.

    # Restart gdb in case next test expects it to be started already.
    gdb_start
    return 0
}

# Find a pathname to a file that we would execute if the shell was asked
# to run $arg using the current PATH.

proc find_gdb { arg } {

    # If the arg directly specifies an existing executable file, then
    # simply use it.

    if [file executable $arg] then {
	return $arg
    }

    set result [which $arg]
    if [string match "/" [ string range $result 0 0 ]] then {
	return $result
    }

    # If everything fails, just return the unqualified pathname as default
    # and hope for best.

    return $arg
}

# Run the test with self.
# Copy the file executable file in case this OS doesn't like to edit it's own
# text space.

set GDB_FULLPATH [find_gdb $GDB]

# Remove any old copy lying around.
catch "exec rm -f ./x$tool"

if ![file executable $GDB_FULLPATH] then {
    fail "couldn't convert $GDB to absolute pathname to make local copy"
} else {
    if [catch "exec cp $GDB_FULLPATH ./x$tool"] then {
	fail "couldn't copy $GDB_FULLPATH to current directory"
    } else {
	verbose "\t\tCopied $GDB_FULLPATH to ./x$tool"
	if {[test_with_self] <0} then {
	    warning "Couldn't test self"
	    catch "exec rm -f ./x$tool"
	    return -1
	}
	catch "exec rm -f ./x$tool"
    }
}
