# Copyright (c) 2005 Free Software Foundation, Inc. # # This file 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. # # For a copy of the GNU General Public License, write the the # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # Loosely based on # gcc/testsuite/gcc.test-framework/test-framework.exp. load_lib dg.exp load_lib libgloss.exp load_lib target-libpath.exp # Just the ones we need to decide whether to run these tests. set csibe(major) 2 set csibe(minor) 1 set csibe(micror) 1 set csibe(version) "$csibe(major).$csibe(minor).$csibe(micror)" set csibe(dir) "$srcdir/$subdir/CSiBE" set csibe(verfile) "$csibe(dir)/bin/create-config" # Don't run these tests unless we have the right version of CSiBE # below, the version that we know how to run. # While we use the "remote_xxx" framework for executing host commands, # we don't actually e.g. copy the CSiBE code to a remote host, so bail # out early. if [is_remote host] { verbose "Remote host, skipping." 0 return } if ![file exists $csibe(verfile)] { verbose "No CSiBE-$csibe(version) present at $csibe(dir), skipping." 0 return } if { [grep "$csibe(verfile)" "^VER_M=$csibe(major)\$"] == "" \ || [grep "$csibe(verfile)" "^VER_S=$csibe(minor)\$"] == "" \ || [grep "$csibe(verfile)" "^VER_B=$csibe(micror)\$"] == "" } { verbose "Version CSiBE-$csibe(version) required, skipping." 0 return } # Create a shell-script passable as the compiler, using the same # options as would proc gcc_target_compile. We need a script for # cases when we can't pass all the options we need and for cases where # one and the same command is used for compilation as well as for # linking. proc build_gcc_target_compile_script { scriptname } { global srcdir subdir set glue_files "" set glue_macros "" if { ![isnative] } { # See comment in build_sim_script. set glue_files "$srcdir/$subdir/sim-main-glue.c" set glue_macros "-Dmain=_main_wrapped" } # Find out with what options GCC is called. This is backwards to # dejagnu normal operations; usually a black-box proc # something-or-other-compile is called for a known set of sources. # Here, we have to get the compiler and throw it to an unknown set # of sources. (Well, unknown as in "deliberately kept separate".) # We accomplish this by abusing the possibility of redefining # procs in Tcl to introduce our own version of remote_exec, which # we happen to know is an inner DejaGnu functions that we know # will get all the compiler options that will be passed to the # final exec. (It's not dynamic scoping, or we'd not have to "put # back" local_exec.) proc find_gcc_cmdline { type input output } { # Save the "real" local_exec (and don't depend on a known set of # parameters). set saved_remote_exec_body [info body remote_exec] set saved_remote_exec_args [info args remote_exec] set found_gcc_cmdline "" proc remote_exec { hostname program args } { # We depend on find_gcc_cmdline not being wrapped in # another proc, i.e. that it and its variables is two # scope-levels down from global scope. upvar #2 found_gcc_cmdline found_gcc_cmdline_1 verbose "find_gcc_cmdline called as 1: $hostname 2: $program 3: $args" 3 set commandline $program if { [llength $args] > 0 } { set commandline "$commandline [lindex $args 0]" } set found_gcc_cmdline_1 $commandline return [list 0 ""] } gcc_target_compile $input $output $type "" # Put back remote_exec. proc remote_exec $saved_remote_exec_args $saved_remote_exec_body return $found_gcc_cmdline } set infile "script-compile-foo.c" set outfile "script-compile-outfoo.o" set gcc_objcompile [find_gcc_cmdline "object" $infile $outfile] verbose "gcc_objcompile before: $gcc_objcompile" 2 # Prune "-c" and "-o". regsub -all " -\[co\] " $gcc_objcompile " " gcc_objcompile regsub "$infile" $gcc_objcompile " $glue_macros " gcc_objcompile regsub "$outfile" $gcc_objcompile " " gcc_objcompile verbose "gcc_objcompile after: $gcc_objcompile" 2 set infile "script-link-foo.o" set outfile "script-link-outfoo-exec" set gcc_link [find_gcc_cmdline "executable" $infile $outfile] verbose "gcc_link before: $gcc_link" 2 # As the location matters for object files, we'll replace it with with # a placeholder which happens to be directly usable in a shell-script. regsub "$infile" $gcc_link "$glue_macros $glue_files \$in_options" gcc_link # Prune "-o" and the output file. As dejagnu-1.4.4 doesn't seem # to attach any executable suffix by itself, we don't have to # worry about that. regsub " -o " $gcc_link " " gcc_link regsub "$outfile" $gcc_link "\$out_options" gcc_link set fd [open $scriptname w] # Beware that end-of-line is swallowed if it's the last character of a # concatenated string-part. Only break strings where that doesn't matter. puts $fd "\#! /bin/sh" puts $fd "\# Generated by $srcdir/$subdir/csibe.exp" # We canonicalize on quoting by {} to avoid backslashes when we # don't need variable expansion. # We need to support non-compile, non-link uses, such as # "-dumpversion" or "-print-file-name=libgcc.a", so we assume all # link usage to supply an "-o". puts $fd {maybe_link=true; dasho=false; in_options=; out_options=} puts $fd {while test $# != 0; do} puts $fd { case "$1" in} puts $fd { -o) out_options="$out_options $1 $2";} puts $fd { in_options="$in_options -D_ARGFILE=\"$2.glueargs\"";} puts $fd { dasho=true;} puts $fd { shift;;} puts $fd { -c) maybe_link=false; in_options="$in_options $1";;} puts $fd { *) in_options="$in_options $1";;} puts $fd { esac} puts $fd { shift} puts $fd {done} puts $fd "\$maybe_link && \$dasho && exec $gcc_link" puts $fd "exec $gcc_objcompile \$out_options \$in_options" puts $fd "exit 127" close $fd remote_exec host "chmod a+x $scriptname" } # Build a shell script that calls the simulator and outputs the # execution time into a separate file specified by the environment # variable SIM_TIME_OUTPUT (usually specifying the time in 100s of # cycles, no matter where it normally goes. The script also takes # care of recreating stdout by pruning simulator and status_wrapper # output as applicable. # For some reason, probably related to remote_spawn being called # recursively through the build_sim_script_remote_spawn call, we can't # pass the command-line and arguments the same way as works for gcc # (binding through upvar #2), so we have to pass it through a global # var. set build_sim_script_cmdline "" proc build_sim_script { scriptname timefile } { global srcdir subdir target_triplet # Until information about how to get a board to reveal execution # time, we need out own little database. if ![target_info exists sim_time_method] { switch -glob $target_triplet { "cris-*-*" { set_currtarget_info sim_time_method option set_currtarget_info sim_time_sim_option "--cris-cycles=basic" set_currtarget_info sim_time_output stderr set_currtarget_info sim_time_output_filter_regex_prefix \ {Basic clock cycles, total @: \([0-9][0-9]*\)} } "mmix-knuth-mmixware" { set_currtarget_info sim_time_method option set_currtarget_info sim_time_sim_option "-s" set_currtarget_info sim_time_output stdout # For the time being, we have to choose only one, and # the number of oops seems most appropriate. set_currtarget_info sim_time_output_filter_regex_prefix \ { [0-9][0-9]* instructions, [0-9][0-9]* mems, \([0-9][0-9]*\) oops} } default { set_currtarget_info sim_time_method time } } } set testname "[pwd]/sim-proggie" remote_exec host "touch $testname" # Find out with what options the simulator is called, like for gcc # in build_gcc_target_compile_script. proc find_sim_cmdline { testprog } { # Save the "real" local_exec (and don't depend on a known set of # parameters). set saved_remote_spawn_body [info body remote_spawn] set saved_remote_spawn_args [info args remote_spawn] global build_sim_script_cmdline set build_sim_script_cmdline "" proc build_sim_script_remote_spawn \ $saved_remote_spawn_args $saved_remote_spawn_body proc remote_spawn { dest commandline args } { global build_sim_script_cmdline verbose "find_spawn_cmdline called as 1: $dest 2: $commandline 3: $args" 2 if { $dest != "host" } { return [build_sim_script_remote_spawn $dest $commandline $args] } set build_sim_script_cmdline $commandline return -1 } set result [remote_load target $testprog "" ""] # Put back remote_exec. proc remote_spawn $saved_remote_spawn_args $saved_remote_spawn_body verbose "found_spawn_cmdline: $build_sim_script_cmdline" 2 return $build_sim_script_cmdline } set sim_script_cmdline [find_sim_cmdline $testname] file_on_host delete $testname regsub -all "$testname" $sim_script_cmdline "" sim_script_cmdline set fd [open $scriptname w] puts $fd "\#! /bin/sh" puts $fd "\# Generated by $srcdir/$subdir/csibe.exp" # Now, we'd be interested in how DejaGNU passes *arguments* to the # simulator. Unfortunately, in dejagnu-1.4.4, only a first # parameter is handled, and just as "sim < arg1"! Not to mention # that few simulators handle arguments at all. As most simulators # can do I/O through the host file-system, instead we pass # arguments through a *file*, by wrapping the main function. We # could restrict this hact to simulators where this noargs # inability is marked and otherwise, just pass on the arguments at # the end of the command-line, but it doesn't save us much from # doing it the hard way for all simulators, as long as the PWD # assumption below holds, and the noargs marker isn't always there # anyway. We clear the environment for sake of cycle-result # stability in case the simulator copies the environment. (Which # is different to sim-main-glue.c not passing it on.) We rely on # the program being executed from the same PWD from where it was # linked, and that relative paths are used, to avoid using # absolute file-paths and the related nondeterministic effect # (when the path is varying) on simulation time. puts $fd {file=$1.glueargs; prog=$1} puts $fd {: > $file} puts $fd {while test $# != 0; do echo "$1" >> $file; shift; done} switch [target_info sim_time_method] { option { set tofile "2>" set fromstdout "1>&2" if { "[target_info sim_time_output]" == "stdout" } { set tofile ">" set fromstdout "" } puts $fd \ [concat "env -i $sim_script_cmdline" \ [target_info sim_time_sim_option] \ {$prog} \ "$tofile \$file.tmpout"] puts $fd {exitcode=$?} # The dot is there to get it treated as a floating-point number. puts $fd \ [concat \ "sed -ne 's/[target_info sim_time_output_filter_regex_prefix].*/\\1./p'" \ {< $file.tmpout} \ {>> ${SIM_TIME_OUTPUT-/tmp/missing_SIM_TIME_OUTPUT}}] puts $fd \ [concat \ "sed -e '/[target_info sim_time_output_filter_regex_prefix]/" \ " { s/\(..*\)[target_info sim_time_output_filter_regex_prefix].*/\\1/p; Q;}'" \ {< $file.tmpout} \ "$fromstdout"] puts $fd {exit $exitcode} } default { puts $fd \ [concat "exec /usr/bin/time -a -o" \ {${SIM_TIME_OUTPUT-/tmp/missing_SIM_TIME_OUTPUTy}} \ {-f "%U"} \ "env -i $sim_script_cmdline \$prog"] } } close $fd remote_exec host "chmod a+x $scriptname" } # Build a sim script and a time script suitable for use with CSiBE, # that prepares command-line parameters and makes time output appear # as expected, working together with the sim script. proc build_sim_and_time_scripts { simscriptname timescriptname } { global csibe srcdir subdir build_sim_script $simscriptname $csibe(tmptime) set fd [open $timescriptname w] puts $fd "\#! /bin/sh" puts $fd "\# Generated by $srcdir/$subdir/csibe.exp" if [isnative] { puts $fd {exec /usr/bin/time ${1+"$@"}} } { puts $fd \ [concat \ {if test "$1" != "-a" || test "$2" != "-o"} \ {|| test "$4" != "-f" || test "$5" != "%U" || test "$6"} \ "!= \"$simscriptname\""] puts $fd {then exec /usr/bin/time ${1+"$@"}} puts $fd {else} puts $fd { output=$3; shift; shift; shift; shift; shift} puts $fd { exec env SIM_TIME_OUTPUT=$output ${1+"$@"}} puts $fd {fi} puts $fd {exit 127} } close $fd remote_exec host "chmod a+x $timescriptname" } # Read a file containing baseline descriptions, comments and empty # lines. proc read_csibe_baseline_file { fname } { global csibe verbose -log "Reading baseline from $fname" set fd [open $fname r] set lineno 0 while { [gets $fd line] != -1 } { incr lineno # Ignore empty lines and comment lines. if { ![string match "\#*" $line] && ![string match "" $line] } { if [regexp {^([^ ,]*),([^ ,]*),([^ ,]*),([^ ,]*) ([^ ]*)$} \ $line dummy test_type ccopt subtest baseline result] { set csibe(test,$test_type,$ccopt,$subtest,$baseline) $result } { error "$fname:$lineno: Not a valid baseline description:\n $line" } } } close $fd } # Find out where they are, and read in, baseline files. proc read_csibe_baselines { } { global csibe CSIBE_BASELINES srcdir subdir target_triplet # Start with a global baseline, if it exists. set baseline_paths [list "$srcdir/subdir/baselines/$target_triplet"] # Get user-provided baselines, accept var or env with env taking # precedence. if [info exists CSIBE_BASELINES] { lappend baseline_paths $CSIBE_BASELINES } if [info exists env(CSIBE_BASELINES)] { lappend baseline_paths $env(CSIBE_BASELINES) } foreach df $baseline_paths { # Read baselines from dirs or individual files. if [file isdirectory $df/.] { foreach f [glob $baselines_dir/*] { read_csibe_baseline_file $f } } { if [file exists $df] { read_csibe_baseline_file $df } } } } # Set CSiBE-specific variables that don't depend on the actual tests # we want. proc set_static_csibe_config_variables { } { global csibe target_triplet # The different baselines with which we compare results. set csibe(baselines) {best milestone previous} # ??? Set basedir differently to allow parallel multilib tests? set csibe(basedir) "[pwd]/csibe" set csibe(dumpfile) "$csibe(basedir)/csibe-results" set csibe(ar) "--ar [find_binutils_prog ar]" set csibe(compiler) "$csibe(basedir)/csibe_gcc" # We don't set the branch, cause we don't have the information # handy, and it'll be seen in gcc.{log,sum} anyway. set csibe(compiler_opt) "--compiler $csibe(compiler)" set csibe(basedir_opt) "--basedir $csibe(basedir)" # I can't see any effects of --lib. # We don't set --binary-prefix, since we're not installed. set csibe(sim) "" set csibe(run) "" set csibe(time_opt) "" if ![isnative] { set csibe(timescript) "$csibe(basedir)/csibe_time" # Overwritten for each simulator run. set csibe(tmptime) "$csibe(basedir)/simtime.tmp" set csibe(time_opt) "--time $csibe(timescript)" set csibe(simscript) "$csibe(basedir)/csibe_sim" set csibe(sim) "--simulator $csibe(simscript)" # For a simulator, we'd get the same result every time. # As long as we don't "time" the simulator itself, that is. set csibe(run) "--run-counter 1" } # FIXME: set csibe(run) "--run-counter 1 --object-counter 1" set csibe(size) "--size [find_binutils_prog size]" set csibe(target) "--target $target_triplet" # Don't set --user, that just causes spurious differences. # ??? Set --zip? # No reason to have this other than the compiler. set csibe(as) "--as $csibe(compiler)" # No special timestamp. # No reason to have this other than the compiler. # (For *real* targets, Linux usually requires a separate linker invocation.) set csibe(ld) "--linker $csibe(compiler)" # No cvstag. # There's apparently no option for these; we'll have to pass them # through the environment for them to take effect. set csibe(nm) [find_nm] set csibe(strip) [find_binutils_prog strip] set csibe(objcopy) [find_binutils_prog objcopy] set csibe(objdump) [find_binutils_prog objdump] set csibe(configcmd_static) \ [concat "env" "NM=$csibe(nm)" "STRIP=$csibe(strip)" \ "OBJCOPY=$csibe(objcopy)" "OBJDUMP=$csibe(objdump)" \ "$csibe(verfile)"] if [info exists CSIBE_TIMEOUT] { set csibe(timeout) $CSIBE_TIMEOUT } { # Ad-hoc setting of three hours per iteration. A little less # than two hours are needed for a fast simulator toolchain and # -Os. set csibe(timeout) 10800 } foreach x { ar compiler_opt basedir_opt sim run time_opt size \ target as ld } { set csibe(configcmd_static) "$csibe(configcmd_static) $csibe($x)" } set csibe(test,results) {} read_csibe_baselines } # Set CSiBE variables that depend on the compiler option and type of # results we want. proc set_dynamic_csibe_config_variables { ccopt tests } { global csibe set csibe(configdir) "x$ccopt" # The basenames of these are what CSiBE hands us. set csibe(runtime_result) "$csibe(basedir)/$csibe(configdir)/result-runtime.csv" set csibe(time_result) "$csibe(basedir)/$csibe(configdir)/result-time.csv" set csibe(size_result) "$csibe(basedir)/$csibe(configdir)/result-size.csv" set csibe(ccflags_opt) "--flags $ccopt" set csibe(results_opt) "--results [list $tests]" set csibe(configcmd) "$csibe(configcmd_static)" foreach x { ccflags_opt results_opt configdir } { set csibe(configcmd) "$csibe(configcmd) $csibe($x)" } } # Read test results from files generated by the the CSiBE run. proc read_csibe_results { ccopt tests } { global csibe foreach test $tests { set fname $csibe(${test}_result) verbose -log "Reading results from $fname" set fd [open $fname r] if { $fd == -1 } { error "Couldn't open $fname" } set lineno 3 # Ignore the first three lines, containing redundant or # uninteresting information. gets $fd; gets $fd; gets $fd; while { [gets $fd line] != -1 } { incr lineno verbose "Got $line" 2 # Lines are on the form # testdir,subtest,result1,...resultN. # We replace the first "," with ":" for ease of programming. regsub "," $line ":" line # Compute the average of the numbers at the end of the line. set result 0.0 set nresults 0 # Don't write this as # "while [regexp {^(.*),([0-9]+[^,]*)$} $line dummy line subresult] open-brace" # or you'll notice a supposed bug in Tcl (8.4.9-3): "line" will not get updated. while { [regexp {^(.*),([0-9]+[^,]*)$} $line dummy line subresult] != 0 } { verbose "($nresults,$result): $line : $subresult" 2 incr nresults set result [expr $result + $subresult] } if { $nresults == 0 } { error "$fname:$lineno: can't parse result-line:\n $line" } set result [expr $result / $nresults] if ![info exists csibe(test,$test,$line)] { set csibe(test,$test,$line) "" lappend csibe(test,$test,subtests) $line } set csibe(test,$test,$ccopt,$line,this) $result } verbose "Done reading results from $fname" 2 close $fd } } # Dump test results in a format directly usable as a "previous" # baseline and an updated "best" baseline. proc dump_csibe_results { } { global csibe set fd [open $csibe(dumpfile) w] foreach x $csibe(test,results) { puts $fd "[lindex $x 0],previous [lindex $x 1]" } close $fd } set_static_csibe_config_variables # Clean up after a previous run and create a new directory. (These do # not exist as "remote_file" commands.) remote_exec host "rm -rf $csibe(basedir)" remote_exec host "mkdir $csibe(basedir)" if ![isnative] { build_sim_and_time_scripts $csibe(simscript) $csibe(timescript) } build_gcc_target_compile_script $csibe(compiler) if ![info exists CSIBE_OPTIONS] { set CSIBE_OPTIONS \ [list { -O0 {time} } \ { -O1 {time runtime} } \ { -Os {time size} } \ { -O2 {time runtime} } \ { -O3 {time runtime} } ] } foreach csibe_combo $CSIBE_OPTIONS { set ccopt [lindex $csibe_combo 0] set tests [lindex $csibe_combo 1] set_dynamic_csibe_config_variables $ccopt $tests verbose "remote_exec host $csibe(configcmd)" 2 remote_exec host "$csibe(configcmd)" set runtime_tests { \ { "not slower than $basename" \ { $this <= $baseline } } \ { "not more than .1% slower than $basename" \ { $this <= 1.001 * $baseline } } \ { "not more than 1% slower than $basename" \ { $this <= 1.01 * $baseline } } \ { "not more than 10% slower than $basename" \ { $this <= 1.1 * $baseline } } \ } set size_tests { \ { "not larger than $basename" \ { $this <= $baseline } } \ } set time_tests { \ { "compilation not slower than $basename" \ { $this <= $baseline } } \ { "compilation not more than 1% slower than $basename" \ { $this <= 1.01 * $baseline } } \ { "compilation not more than 10% slower than $basename" \ { $this <= 1.1 * $baseline } } \ } # It doesn't help specifying a separate log; it doesn't stop the # same contents from going into gcc.log. Unfortunately, we have # to silence make output, because dejagnu halts the program when # output size goes beyond 512000. We have to pass # --no-print-directory explicitly, as it'll be turned on by # default as a consequence of the "-C" option. set status [remote_exec host "$env(MAKE) -s --no-print-directory" \ "-C $csibe(basedir)/$csibe(configdir)" \ "" "" $csibe(timeout)] foreach test_type $tests { if ![info exists csibe(test,$test_type,subtests)] { set csibe(test,$test_type,subtests) {} } } set testname "csibe test $ccopt $tests" if { [lindex $status 0] == 0 } { pass $testname read_csibe_results $ccopt $tests set testresults_valid 1 } { fail $testname verbose -log "failure cause: [lindex $status 1]" set testresults_valid 0 } foreach test_type $tests { foreach subtest $csibe(test,$test_type,subtests) { if { $testresults_valid != 0 } { if [info exists csibe(test,$test_type,$ccopt,$subtest,this)] { set this $csibe(test,$test_type,$ccopt,$subtest,this) } { # Or should we error here? set this 99e99 } lappend csibe(test,results) [list "$test_type,$ccopt,$subtest" $this] } foreach basename $csibe(baselines) { if [info exists csibe(test,$test_type,$ccopt,$subtest,$basename)] { set baseline $csibe(test,$test_type,$ccopt,$subtest,$basename) } { set baseline $this } # To account for rounding errors causing numbers not # to compare equal to themselves, we need to add an # insignificant bias to the baseline before comparing # it to the result of this run. We settle for no less # than the below since that seems to somewhat match # the granularity in seconds of host # "/usr/bin/time"-based testing. set baseline [expr $baseline + 0.05] foreach test_criteria [eval concat $${test_type}_tests] { set testname \ [concat "csibe" $ccopt $test_type $subtest \ [eval concat [lindex $test_criteria 0]]] if { $testresults_valid != 0 } { if { [expr [concat [lindex $test_criteria 1]]] } { pass $testname } { fail $testname verbose -log "$basename: $baseline" verbose -log "this: $this" } } { unresolved $testname } } } } } } dump_csibe_results