# process exec tests.

# value gets set later
set test_exepath ""

# "load" generation function for stap_run.  It spawns our test
# executable, which execs our copy of /bin/sleep.
proc run_test_exe {} {
    global test_exepath
    global exepath

    spawn $test_exepath $exepath
    set exe_id $spawn_id
    wait_n_secs 5
    catch {close -i $exe_id}
    catch {wait -i $exe_id}
    return 0;
}

# Set up our own copy of /bin/sleep, to make testing for a particular
# executable easy.  We can't use 'ln' here, since we might be creating
# a cross-device link.  We can't use 'ln -s' here, since the kernel
# resolves the symbolic link and reports that /bin/sleep is being
# exec'ed (instead of our local copy).
set exepath "[pwd]/sleep_[pid]"
if {[catch {exec cp /bin/sleep $exepath} res]} {
    fail "unable to copy /bin/sleep: $res"
    return
}

# Note use of '%1$s' - the 'N$' is the 'argpos', the argument position
# passed to 'format'.
set test_script {
    global output
    global sleep_start, sleep_end
    probe begin { printf("systemtap starting probe\n") }
    probe process("%1$s").begin {
	output .= sprintf("exe(%%d) begin\n", tid())
    }
    probe process("%1$s").thread.begin {
	output .= sprintf("exe(%%d) thread begin\n", tid())
    }
    probe process("%1$s").end {
	output .= sprintf("exe(%%d) end\n", tid())
    }
    probe process("%1$s").thread.end {
	output .= sprintf("exe(%%d) thread end\n", tid())
    }
    probe process("%2$s").begin {
	sleep_start = gettimeofday_s()
	output .= sprintf("sleep(%%d) begin\n", tid())
    }
    probe process("%2$s").end {
	output .= sprintf("sleep(%%d) end\n", tid())
	sleep_end = gettimeofday_s()
    }
    probe end {
	printf("systemtap ending probe\n%%s", output)
	time_slept = sleep_end - sleep_start
	if (time_slept >= 2 && time_slept <= 4)
	    printf("time slept: OK\n")
	else
	    printf("time slept: ERROR (%%d)\n", time_slept)
    }
}

#######
#
# Test a normal fork/exec pair.
#

set fork_exec_srcpath "$srcdir/systemtap.base/proc_fork_exec.c"
set fork_exec_exepath "[pwd]/proc_fork_exec_[pid]"
set fork_exec_flags ""

# We make sure the outputs looks like the following using regexp
# subexpressions:
#	exe(PARENT_PID) begin
#	exe(CHILD_PID) begin
#	exe(CHILD_PID) end
#	sleep(CHILD_PID) begin
#	sleep(CHILD_PID) end
#	exe(PARENT_PID) end
#	time slept: OK
#
# Accept the 'sleep(CHILD_PID)' end and 'exe(PARENT_PID) end' lines in
# any order.
set fork_exec_script_output "exe\\((\\d+)\\) begin\r\nexe\\((\\d+)\\) begin\r\nexe\\(\\2\\) end\r\nsleep\\(\\2\\) begin\r\n(sleep\\(\\2\\)|exe\\(\\1\\)) end\r\n(sleep\\(\\2\\)|exe\\(\\1\\)) end\r\ntime slept: OK\r\n"

# Compile our program that forks/exec's /bin/sleep.
set test_exepath $fork_exec_exepath
set res [target_compile $fork_exec_srcpath $fork_exec_exepath executable $fork_exec_flags]
if { $res != "" } {
    verbose "target_compile failed: $res" 2
    fail "unable to compile $fork_exec_srcpath"
    return
}

set TEST_NAME "PROC_EXEC_01"
if {![utrace_p]} {
    untested "$TEST_NAME : no kernel utrace support found"
} elseif {![installtest_p]} {
    untested "$TEST_NAME"
} else {
    set script [format $test_script $fork_exec_exepath $exepath]
    stap_run $TEST_NAME run_test_exe $fork_exec_script_output -e $script
}

#######
#
# Test a straight exec (no fork).
#

set exec_srcpath "$srcdir/systemtap.base/proc_exec.c"
set exec_exepath "[pwd]/proc_exec_[pid]"
set exec_flags ""

# We make sure the outputs looks like the following using regexp
# subexpressions:
#	exe(PID) begin
#	exe(PID) end
#	sleep(PID) begin
#	sleep(PID) end
#	time slept: OK
set exec_script_output "exe\\((\\d+)\\) begin\r\nexe\\(\\1\\) end\r\nsleep\\(\\1\\) begin\r\nsleep\\(\\1\\) end\r\ntime slept: OK\r\n"

# Compile our program that exec's /bin/sleep.
set test_exepath $exec_exepath
set res [target_compile $exec_srcpath $exec_exepath executable $exec_flags]
if { $res != "" } {
    verbose "target_compile failed: $res" 2
    fail "unable to compile $exec_srcpath"
    return
}

set TEST_NAME "PROC_EXEC_02"
if {![utrace_p]} {
    untested "$TEST_NAME : no kernel utrace support found"
} elseif {![installtest_p]} {
    untested "$TEST_NAME"
} else {
    set script [format $test_script $exec_exepath $exepath]
    stap_run $TEST_NAME run_test_exe $exec_script_output -e $script
}

#######
#
# Test a multi-threaded exec, which programs aren't supposed to do,
# but...
#

set thread_exec_srcpath "$srcdir/systemtap.base/proc_thread_exec.c"
set thread_exec_exepath "[pwd]/proc_thread_exec_[pid]"
set thread_exec_flags "libs=-lpthread"

# We make sure the outputs looks like the following using regexp
# subexpressions.
#	exe(PID1) begin
#	exe(PID2) thread begin
#	exe(PID1) thread end
#	exe(PID1) end
#	sleep(PID1) begin
#	sleep(PID1) end
#	time slept: OK
#
# Why no PID2 thread end and why isn't sleep using PID2?  When the
# thread calls exec (which it isn't supposed to do), the kernel has to
# clean up the best it can.  It really kills the original task,
# promotes the thread to be the process group leader (and changes the
# thread's pid to the original process group leader's pid).
set thread_exec_script_output "exe\\((\\d+)\\) begin\r\nexe\\(\\d+\\) thread begin\r\nexe\\(\\1\\)( thread)? end\r\nexe\\(\\1\\) end\r\nsleep\\(\\1\\) begin\r\nsleep\\(\\1\\) end\r\ntime slept: OK\r\n"

# Compile our multi-threaded program that exec's /bin/sleep.
set test_exepath $thread_exec_exepath
set res [target_compile $thread_exec_srcpath $thread_exec_exepath executable $thread_exec_flags]
if { $res != "" } {
    verbose "target_compile failed: $res" 2
    fail "unable to compile $exec_srcpath"
    return
}

set TEST_NAME "PROC_EXEC_03"
if {![utrace_p]} {
    untested "$TEST_NAME : no kernel utrace support found"
} elseif {![installtest_p]} {
    untested "$TEST_NAME"
} else {
    set script [format $test_script $thread_exec_exepath $exepath]
    stap_run $TEST_NAME run_test_exe $thread_exec_script_output -e $script
}

# Cleanup
exec rm -f $exepath $fork_exec_exepath $exec_exepath $thread_exec_exepath
