# Simulator dejagnu utilities. # TODO: Switch to using dg-xxx helpers rather than parsing the files directly. # Communicate simulator path from sim_init to sim_version. # For some reason [board_info target sim] doesn't work in sim_version. # [Presumubly because the target has been "popped" by then. Odd though.] set sim_path "unknown-run" # Find the simulator arch. proc sim_arch {} { global subdir set arch "$subdir" while { [file dirname $arch] != "." } { set arch [file dirname $arch] } return "$arch" } # Initialize the testrun. # # Normally dejagnu will execute ${tool}_init automatically, but since we set # --tool '' (for a simpler testsuite/ layout), we have each test call this # itself. proc sim_init { args } { global builddir global subdir global sim_path # Find the path to the simulator for executing. set sim_path "$builddir/[sim_arch]/run" # As gross as it is, we unset the linker script specified by the target # board. The simulator board file mips-sim.exp, sets ldscript to the # MIPS libgloss linker scripts which include libgcc (and possibly other # libraries), which the linker (used to link these tests rather than the # compiler) can't necessarily find. Similarly iq2000-sim.exp and # m68hc11-sim.exp. So, we make it a common rule to clear the slate for # all simulators. unset_currtarget_info ldscript sim_init_toolchain # Need to return an empty string. This tells dejagnu to *not* re-run us # with the exact test that we're about to run. return "" } # Initialize the toolchain settings for this port. # Needs to be called once per-port. proc sim_init_toolchain {} { global objdir global srcdir global cpu_option global cpu_option_sep global ASFLAGS_FOR_TARGET global CFLAGS_FOR_TARGET global LDFLAGS_FOR_TARGET global SIMFLAGS_FOR_TARGET global global_as_works global global_cpp_works global global_cc_works global global_cc_os global CFLAGS_FOR_TARGET_init # Reset all the toolchain settings. This provides a clean slate when # starting the next set of tests. set ASFLAGS_FOR_TARGET "" set CFLAGS_FOR_TARGET "" set LDFLAGS_FOR_TARGET "" set SIMFLAGS_FOR_TARGET "" unset -nocomplain cpu_option cpu_option_sep # The configure script created XXX_FOR_TARGET_$ARCH for us, so merge those # into plain XXX_FOR_TARGET for this particular arch run. global SIM_PRIMARY_TARGET set arch [sim_arch] set ARCH [string map {- _} [string toupper $arch]] foreach var {AS LD CC} { set var_for_target "${var}_FOR_TARGET" global $var_for_target set var_for_target_arch "${var_for_target}_${ARCH}" global $var_for_target_arch if [info exists $var_for_target_arch] { set $var_for_target [set $var_for_target_arch] } else { set $var_for_target "" } if { [set $var_for_target] == "" } { # If building for the primary target, use the default settings. if { $arch == $SIM_PRIMARY_TARGET } { unset -nocomplain $var_for_target } { set $var_for_target false } } } # See if an assembler is available. if { $arch != $SIM_PRIMARY_TARGET && $AS_FOR_TARGET == "false" } { verbose -log "Can't find a compatible assembler" set global_as_works 0 } { verbose -log "Found a compatible assembler" set global_as_works 1 } # Merge per-test settings if available. if ![info exists CFLAGS_FOR_TARGET_init] { set CFLAGS_FOR_TARGET_init "" } set cc_options [list "additional_flags=$CFLAGS_FOR_TARGET_init"] # See if we have a preprocessor available. set result [target_compile $srcdir/lib/compilercheck.c \ $objdir/compilercheck.x "preprocess" $cc_options] set global_cpp_works [string equal "" "$result"] # See if we have a compiler available, and which environment it's targeting. set global_cc_os "" set global_cc_works 0 if { $arch != $SIM_PRIMARY_TARGET && $CC_FOR_TARGET == "false" } { verbose -log "Can't find a compatible C compiler" } elseif { [target_compile $srcdir/lib/newlibcheck.c \ $objdir/compilercheck.x "executable" $cc_options] == "" } { verbose -log "Found newlib C compiler" set global_cc_works 1 set global_cc_os "newlib" } elseif { [target_compile $srcdir/lib/linuxcheck.c \ $objdir/compilercheck.x "executable" $cc_options] == "" } { verbose -log "Found Linux C compiler" set global_cc_works 1 set global_cc_os "linux" } elseif { [target_compile $srcdir/lib/compilercheck.c \ $objdir/compilercheck.x "executable" $cc_options] == "" } { verbose -log "Found C compiler, but unknown OS" set global_cc_works 1 } { verbose -log "Can't execute C compiler" } file delete $objdir/compilercheck.x unset CFLAGS_FOR_TARGET_init } # Print the version of the simulator being tested. # Required by dejagnu. proc sim_version {} { global sim_path set version 0.5 clone_output "$sim_path $version\n" } # Run a program on the simulator. # Required by dejagnu (at least ${tool}_run used to be). # # SIM_OPTS are options for the simulator. # PROG_OPTS are options passed to the simulated program. # At present REDIR must be "" or "> foo". # OPTIONS is a list of options internal to this routine. # This is modelled after target_compile. We want to be able to add new # options without having to update all our users. # Currently: # env(foo)=val - set environment variable foo to val for this run # timeout=val - set the timeout to val for this run # # The result is a list of two elements. # The first is the program's exit status (0/1/etc...). # The second is the program's output. # # This is different than the sim_load routine provided by # dejagnu/config/sim.exp. It's not clear how to pass arguments to the # simulator (not the simulated program, the simulator) with sim_load. proc sim_run { prog sim_opts prog_opts redir options } { global sim_path # Set the default value of the timeout. # FIXME: The timeout value we actually want is a function of # host, target, and testcase. set testcase_timeout [board_info target sim_time_limit] if { "$testcase_timeout" == "" } { set testcase_timeout [board_info host testcase_timeout] } if { "$testcase_timeout" == "" } { set testcase_timeout 240 ;# 240 same as in dejagnu/config/sim.exp. } # Initial the environment we pass to the testcase. set testcase_env "" # Process OPTIONS ... foreach o $options { if [regexp {^env\((.*)\)=(.*)} $o full var val] { set testcase_env "$testcase_env $var=$val" } elseif [regexp {^timeout=(.*)} $o full val] { set testcase_timeout $val } } verbose "testcase timeout is set to $testcase_timeout" 1 set sim $sim_path if [is_remote host] { set prog [remote_download host $prog] if { $prog == "" } { error "download failed" return -1 } } set board [target_info name] if [board_info $board exists sim,options] { set always_opts [board_info $board sim,options] } else { set always_opts "" } # FIXME: this works for UNIX only if { "$testcase_env" != "" } { set sim "env $testcase_env $sim" } if { [board_info target sim,protocol] == "sid" } { set cmd "" set sim_opts "$sim_opts -e \"set cpu-loader file [list ${prog}]\"" } else { set cmd "$prog" } send_log "$sim $always_opts $sim_opts $cmd $prog_opts\n" if { "$redir" == "" } { remote_spawn host "$sim $always_opts $sim_opts $cmd $prog_opts" } else { remote_spawn host "$sim $always_opts $sim_opts $cmd $prog_opts $redir" writeonly } set result [remote_wait host $testcase_timeout] set return_code [lindex $result 0] set output [lindex $result 1] # Remove the \r part of "\r\n" so we don't break all the patterns # we want to match. regsub -all -- "\r" $output "" output if [is_remote host] { # clean up after ourselves. remote_file host delete $prog } return [list $return_code $output] } # Support function for "#requires: simoption ": # Looks in "run --help" output for , returns 1 iff is mentioned # there and looks like an option name, otherwise 0. proc sim_check_requires_simoption { optname } { global sim_path set testrun "$sim_path --help" verbose -log "Checking for simoption `$optname'" 3 remote_spawn host $testrun set result [remote_wait host 240] set return_code [lindex $result 0] if { $return_code != 0 } { perror "Can't execute `$testrun' to check for `$optname'" return 0 } set output [lindex $result 1] # Remove \r as for regular runs. regsub -all -- "\r" $output "" output # The option output format for --help for each line where an # option name is mentioned, is assumed to be two space followed # by the option name followed by a space or left square bracket, # like in (optname=--foo): " --foo " or " --foo[this|that]". # Beware not to match " --foo-bar" nor " --foobar". if [string match "*\n $optname\[\[ \]*" $output] { verbose -log "Found `$optname'" 3 return 1 } verbose -log "Did not find `$optname'" 3 return 0 } # Run testcase NAME. # NAME is either a fully specified file name, or just the file name in which # case $srcdir/$subdir will be prepended. # REQUESTED_MACHS is a list of machines to run the testcase on. If NAME isn't # for the specified machine(s), it is ignored. # Typically REQUESTED_MACHS contains just one element, it is up to the caller # to iterate over the desired machine variants. # # The file can contain options in the form "# option(mach list): value". # Possibilities: # mach: [all | machine names] # as[(mach-list)]: # ld[(mach-list)]: # cc[(mach-list)]: # sim[(mach-list)]: # progopts: # progos: OS required for the test # status: program exit status to treat as "pass" # output: program output pattern to match with string-match # xerror: program is expected to return with a "failure" exit code # xfail: # kfail: # If `output' is not specified, the program must output "pass" if !xerror or # "fail" if xerror. # The parens in "optname()" are optional if the specification is for all machs. # Multiple "output", "xfail" and "kfail" options concatenate. # The xfail and kfail arguments are space-separated target triplets and PRIDs. # There must be a PRMS (bug report ID) specified for kfail, while it's # optional for xfail. proc run_sim_test { name requested_machs } { global subdir srcdir objdir global sim_path global opts global cpu_option global cpu_option_sep global SIMFLAGS_FOR_TARGET global global_as_works global global_cpp_works global global_cc_works global global_cc_os if ![file exists $sim_path] { unsupported "$name: missing simulator $sim_path" return } if [string match "*/*" $name] { set file $name set name [file tail $name] } else { set file "$srcdir/$subdir/$name" } set opt_array [slurp_options "${file}"] if { $opt_array == -1 } { unresolved $subdir/$name return } # Clear default options set opts(as) "" set opts(ld) "" set opts(cc) "" set opts(progopts) "" set opts(progos) "" set opts(requires) {} set opts(sim) "" set opts(status) "0" set opts(output) "" set opts(mach) "" set opts(timeout) "" set opts(xerror) "no" set opts(xfail) "" set opts(kfail) "" set seen_output 0 if ![info exists SIMFLAGS_FOR_TARGET] { set SIMFLAGS_FOR_TARGET "" } # Clear any machine specific options specified in a previous test case foreach m $requested_machs { if [info exists opts(as,$m)] { unset opts(as,$m) } if [info exists opts(ld,$m)] { unset opts(ld,$m) } if [info exists opts(cc,$m)] { unset opts(cc,$m) } if [info exists opts(sim,$m)] { unset opts(sim,$m) } } foreach i $opt_array { set opt_name [lindex $i 0] set opt_machs [lindex $i 1] set opt_val [lindex $i 2] if ![info exists opts($opt_name)] { perror "unknown option $opt_name in file $file" unresolved $subdir/$name return } # Multiple "output" specifications concatenate, they don't override. if { $opt_name == "output" } { set opt_val "$opts(output)$opt_val" set seen_output 1 } # Similar with "xfail" and "kfail", but arguments are space-separated. if { $opt_name == "xfail" || $opt_name == "kfail" } { set opt_val "$opts($opt_name) $opt_val" } # Similar for "requires", except we append a pair to a list, and # that doesn't match the processing in the rest of the loop, so we # "continue" early. if { $opt_name == "requires" } { lappend opts($opt_name) [split $opt_val " "] continue } foreach m $opt_machs { set opts($opt_name,$m) $opt_val } if { "$opt_machs" == "" } { set opts($opt_name) $opt_val } } if { $opts(progos) != "" && $opts(progos) != $global_cc_os } { untested $subdir/$name return } set testname $name set sourcefile $file if { $seen_output == 0 } { if { "$opts(xerror)" == "no" } { set opts(output) "pass\n" } else { set opts(output) "fail\n" } } # Change \n sequences to newline chars. regsub -all "\\\\n" $opts(output) "\n" opts(output) set testcase_machs $opts(mach) if { "$testcase_machs" == "all" } { set testcase_machs $requested_machs } foreach mach $testcase_machs { if { [lsearch $requested_machs $mach] < 0 } { verbose -log "Skipping $mach version of $name, not requested." continue } verbose -log "Testing $name on machine $mach." # Time to setup xfailures and kfailures. if { "$opts(xfail)" != "" } { verbose -log "xfail: $opts(xfail)" # Using eval to make $opts(xfail) appear as individual # arguments. eval setup_xfail $opts(xfail) } if { "$opts(kfail)" != "" } { verbose -log "kfail: $opts(kfail)" eval setup_kfail $opts(kfail) } if ![info exists opts(as,$mach)] { set opts(as,$mach) $opts(as) } set as_options "$opts(as,$mach) -I$srcdir/$subdir" if [info exists cpu_option] { if ![info exists cpu_option_sep] { set sep "=" } { set sep $cpu_option_sep } set as_options "$as_options $cpu_option$sep$mach" } regsub {(^ *| +)([^ ]+)} "$as_options" { -Wa,\2} c_as_options if ![info exists opts(ld,$mach)] { set opts(ld,$mach) $opts(ld) } regsub {(^ *| +)([^ ]+)} "$opts(ld,$mach)" { -Wl,\2} c_ld_options if ![info exists opts(cc,$mach)] { set opts(cc,$mach) $opts(cc) } foreach req $opts(requires) { set what [lindex $req 0] set what_opt [lindex $req 1] verbose -log "requires: <$what> <$what_opt>" if { [info procs sim_check_requires_${what}] != [list] } { if ![sim_check_requires_${what} $what_opt] { untested $subdir/$name return } } { perror "unknown requirement `requires: $what' in file $file" unresolved $subdir/$name return } } if [string match "*.c" $sourcefile] { # If we don't have a compiler available, skip tests :(. if { $global_cc_works == 0 } { untested $subdir/$name return } set comp_output [target_compile $sourcefile $objdir/${name}.x "executable" \ [list "incdir=$srcdir/$subdir" "additional_flags=$c_as_options $c_ld_options $opts(cc,$mach)"]] set method "compiling/linking" } else { # If we don't have an assembler available, skip tests :(. if { $global_as_works == 0 } { untested $subdir/$name return } if [string match "*.S" $sourcefile] { # If we don't have a preprocessor available, skip tests :(. if { $global_cpp_works == 0 } { untested $subdir/$name return } set comp_output [target_compile $sourcefile $objdir/${name}.o "object" \ [list "incdir=$srcdir/$subdir" "additional_flags=$c_as_options"]] set method "compiling" } else { set comp_output [target_assemble $sourcefile $objdir/${name}.o "$as_options"] set method "assembling" } if ![string match "" $comp_output] { verbose -log "$comp_output" 3 fail "$mach $testname (${method})" continue } set comp_output [target_link $objdir/${name}.o $objdir/${name}.x "$opts(ld,$mach)"] set method "linking" } if ![string match "" $comp_output] { verbose -log "$comp_output" 3 fail "$mach $testname (${method})" continue } # If no machine specific options, default to the general version. if ![info exists opts(sim,$mach)] { set opts(sim,$mach) $opts(sim) } # Build the options argument. set options "" if { "$opts(timeout)" != "" } { set options "$options timeout=$opts(timeout)" } set result [sim_run $objdir/${name}.x "$opts(sim,$mach) $SIMFLAGS_FOR_TARGET" "$opts(progopts)" "" "$options"] set return_code [lindex $result 0] set output [lindex $result 1] set status fail if { $return_code == 77 } { set status unsupported } elseif { $return_code == $opts(status) } { set status pass } if { "$status" == "pass" } { if { "$opts(xerror)" == "no" } { if [string match $opts(output) $output] { pass "$mach $testname" file delete $objdir/${name}.o $objdir/${name}.x } else { verbose -log "status: $return_code" 3 verbose -log "output: $output" 3 verbose -log "pattern: $opts(output)" 3 fail "$mach $testname (execution)" } } else { verbose -log "`pass' return code when expecting failure" 3 fail "$mach $testname (execution)" } } elseif { "$status" == "fail" } { if { "$opts(xerror)" == "no" } { fail "$mach $testname (execution)" } else { if [string match $opts(output) $output] { pass "$mach $testname" file delete $objdir/${name}.o $objdir/${name}.x } else { verbose -log "status: $return_code" 3 verbose -log "output: $output" 3 verbose -log "pattern: $opts(output)" 3 fail "$mach $testname (execution)" } } } else { $status "$mach $testname" } } } # Subroutine of run_sim_test to process options in FILE. proc slurp_options { file } { global subdir srcdir if [catch { set f [open $file r] } x] { #perror "couldn't open `$file': $x" perror "$x" return -1 } set opt_array {} # whitespace expression set ws {[ ]*} set nws {[^ ]*} # whitespace is ignored anywhere except within the options list; # option names are alphabetic only set pat "^#${ws}(\[a-zA-Z\]*)\\(?(\[^):\]*)\\)?$ws:${ws}(.*)$ws\$" # Allow arbitrary lines until the first option is seen. set seen_opt 0 while { [gets $f line] != -1 } { set line [string trim $line] # Whitespace here is space-tab. if [regexp $pat $line xxx opt_name opt_machs opt_val] { # match! set opt_val [string map [list \ {$pwd} [pwd] \ {$srcdir} "$srcdir" \ {$subdir} "$subdir" \ ] "$opt_val"] lappend opt_array [list $opt_name $opt_machs $opt_val] set seen_opt 1 } else { if { $seen_opt } { break } } } close $f return $opt_array }