#!/bin/sh # This script finds miscompiled procedures. # # Given a stage2 directory that works (stage2.ok) and one that doesn't # (stage2.bad), this script uses binary search to try to find in stage2.bad # first the C source file and then the module within that C source file that # when put together with everthing else from the stage2.ok directory, still # causes the compiler to fail. # # If the bad C source file has different numbers of modules in the bad and ok # versions, then the script stops after identifying only the file. # # The test for the composite stage2 compiler is either bootstrap checking # (the default, or the successful execution of the all the test cases in # one or more dubdirectories of the tests directory. usage="\ Usage: $0 [options] Options: -b, --no-bootcheck Do not perform a bootcheck; check only the tests directory. -c, --compile-only Compile only. Do not compare stage2.ok and stage3. -d , --dir Confine the search to one directory, library or compiler. (Usually useful only after a previous search.) -f , --file Confine the search to the named file(s). (Usually useful only after a previous search.) -h, --help Display this usage message. -j , --jobs Run using different parallel processes. -m , --mmake-args Pass as options to \`mmake'. -n, --negative-search Look for the module that suppresses the bug, not causes it. -o , --output-file Output results to . -r, --copy-runtime Copy the runtime directory instead of linking it. This is necessary if the test uses a compilation model option that differs from the one used in the main runtime directory. -t , --test-dir Execute runtests from the named subdirectory of tests. " set -x bootcheck="" compile_only="" jfactor= mmake_opts="" outfile="" copy_runtime=false testdirs="" negative=false alldirs="" allmodules="" while [ $# -gt 0 ]; do case "$1" in -b|--no-bootcheck) bootcheck="-b" ;; -c|--compile-only) compile_only="-c" ;; -d|--dir) alldirs="$2"; shift ;; -d*) alldirs="` expr $1 : '-d\(.*\)' `"; ;; -f|--file) allmodules="$allmodules $2"; shift ;; -f*) allmodules="$allmodules ` expr $1 : '-f\(.*\)' `"; ;; -h|--help) echo "$usage" exit 0 ;; -j|--jobs) jfactor="-j$2"; shift ;; -j*) jfactor="-j` expr $1 : '-j\(.*\)' `" ;; --jobs*) jfactor="--jobs` expr $1 : '--jobs\(.*\)' `" ;; -m|--mmake) mmake_opts="$mmake_opts $2"; shift ;; -n|--negative-search) negative=true ;; -o|--output-file) outfile="-o $2"; shift ;; -o*) outfile="-o ` expr $1 : '-o\(.*\)' `"; ;; -r|--copy-runtime) copy_runtime=true ;; -t|--test-dir) testdirs="$testdirs -t$2"; shift ;; -t*) testdirs="$testdirs ` expr $1 : '-t\(.*\)' `" ;; -*) echo "$0: unknown option \`$1'" 1>&2 echo "$usage" 1>&2 exit 1 ;; *) echo "$usage" 1>&2 exit 1 ;; esac shift done if test "$negative" = true then base=bad trial=ok expected=failure else base=ok trial=bad expected=success fi if test -d stage2.ok -a -d stage2.bad then echo "stage2.ok and stage2.bad both present" else echo "at least one of stage2.ok and stage2.bad is missing" exit 1 fi echo "starting at `date`" root=`/bin/pwd` PATH=$root/tools:$PATH export PATH [ -d stage2 ] || mkdir stage2 /bin/rm -fr stage2/* set +x echo linking stage 2... 1>&2 cd stage2 mkdir compiler cd compiler ln -s $root/compiler/*.m . cp $root/compiler/Mmake* . cd $root/stage2 mkdir library cd library ln -s $root/library/*.m . # ln -s $root/library/*.nl . ln -s $root/library/library.init . cp $root/library/Mmake* . cd $root/stage2 if test "$copy_runtime" = "true" then mkdir runtime cd runtime ln -s $root/runtime/*.h . ln -s $root/runtime/*.c . ln -s $root/runtime/*.mod . ln -s $root/runtime/*.in . ln -s $root/runtime/machdeps . cp $root/runtime/Mmake* . cd $root/stage2 else ln -s $root/runtime . fi ln -s $root/boehm_gc . ln -s $root/doc . ln -s $root/scripts . ln -s $root/util . ln -s $root/profiler . ln -s $root/conf* . rm -f config*.log cp $root/stage2.ok/Mmake* . cd $root # We don't copy the .d files. This prevents mmake from trying to remake any # of the .c and .o files, which we provide in the form they should be used. # cp stage2.ok/library/*.d stage2/library cp stage2.ok/library/*.dep stage2/library cp stage2.ok/library/*.int stage2/library cp stage2.ok/library/*.int2 stage2/library cp stage2.ok/library/*.date stage2/library # cp stage2.ok/compiler/*.d stage2/compiler cp stage2.ok/compiler/*.dep stage2/compiler cp stage2.ok/compiler/*.int stage2/compiler cp stage2.ok/compiler/*.int2 stage2/compiler cp stage2.ok/compiler/*.date stage2/compiler set -x if test "$copy_runtime" = "true" then if (cd stage2 ; mmake $mmake_opts $jfactor runtime) then echo "building of stage 2 runtime successful" else echo "building of stage 2 runtime not successful" exit 1 fi fi /bin/rm -f stage3/Mmake* cp $root/stage2.ok/Mmake* stage3 if test "$alldirs" = "" then echo testing whether the stage2.bad library works set +x echo linking stage2/library from stage2.bad/library 1>&2 cp stage2.bad/library/*.[co] stage2/library echo linking stage2/compiler from stage2.ok/compiler 1>&2 cp stage2.ok/compiler/*.[co] stage2/compiler set -x if test "$negative" = true then if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs then testeddir=library else testeddir=compiler fi echo "solution seems to be in the $testeddir directory" else if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs then testeddir=compiler else testeddir=library fi echo "problem seems to be in the $testeddir directory" fi else testeddir=$alldirs if test ! -d stage2/$testeddir then echo $stage2/$testeddir does not exist exit 1 fi fi # start out with all files in stage2 coming from stage2.$base set +x echo linking stage2 from stage2.$base 1>&2 cp stage2.$base/library/*.[co] stage2/library cp stage2.$base/compiler/*.[co] stage2/compiler set -x # find the set of candidate modules if test "$allmodules" = "" then cd stage2/$testeddir allmodules=`sub X.c X *.c` cd $root else for module in $allmodules do if test ! -f stage2/$testeddir/$module.c then echo $stage2/$testeddir/$module.c does not exist exit 1 fi done fi doubtful="$allmodules" tested=`half $doubtful` knowngood= while test "$tested" != "" do # at this point, all the files in stage2 should be from stage2.$base echo "doubtful modules: $doubtful" echo "testing modules: $tested" for module in $tested do cp stage2.$trial/$testeddir/$module.[co] stage2/$testeddir done if test "$negative" = true then if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs then echo "test succeeded" lasttest=success doubtful="$tested" else echo "test failed" lasttest=failure newdoubtful="" for module in $doubtful do if not appears $module $tested then newdoubtful="$newdoubtful $module" fi done doubtful="$newdoubtful" fi else if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs then echo "test succeeded" lasttest=success knowngood="$knowngood $tested" newdoubtful="" for module in $doubtful do if not appears $module $tested then newdoubtful="$newdoubtful $module" fi done doubtful="$newdoubtful" else echo "test failed" lasttest=failure doubtful="$tested" fi fi for module in $tested do cp stage2.$base/$testeddir/$module.[co] stage2/$testeddir done tested=`half $doubtful` if test "$tested" = "" -a "$lasttest" = "$expected" then tested="$doubtful" fi done if test "$doubtful" = "" then echo "cannot find the problem; all modules behave as expected" exit 1 fi module=`echo $doubtful | tr -d ' '` if test "$negative" = false then echo "the modules known to be ok are: $knowngood" fi echo "there is a problem in $testeddir/$module" echo basecnt=`egrep '^END_MODULE' stage2.$base/$testeddir/$module.c | wc -l` trialcnt=`egrep '^END_MODULE' stage2.$trial/$testeddir/$module.c | wc -l` if test $basecnt -ne $trialcnt then basecnt=`echo $basecnt | tr -d ' '` trialcnt=`echo $trialcnt | tr -d ' '` echo "the two versions of the problem module" echo "differ in the number of C modules they have" echo "$base version: $basecnt vs $trial version: $trialcnt" exit 1 fi for dir in $base $trial do cd stage2.$dir/$testeddir divide $module.c $basecnt cd $root done doubtful= i=0 while test $i -le $basecnt do doubtful="$doubtful $i" i=`expr $i + 1` done tested=`half $doubtful` knowngood= while test "$tested" != "" do echo "doubtful: $doubtful" echo "testing: $tested" assemble $base $trial $testeddir $module $basecnt $tested cd stage2/$testeddir /bin/rm $module.o mmake $module.o cd $root if test "$negative" = true then if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs then echo "test succeeded" lasttest=success doubtful="$tested" else echo "test failed" lasttest=failure newdoubtful="" for part in $doubtful do if not appears $part $tested then newdoubtful="$newdoubtful $part" fi done doubtful="$newdoubtful" fi else if binary_step $bootcheck $compile_only $jfactor -m "$mmake_opts" $outfile $testdirs then echo "test succeeded" lasttest=success knowngood="$knowngood $tested" newdoubtful="" for part in $doubtful do if not appears $part $tested then newdoubtful="$newdoubtful $part" fi done doubtful="$newdoubtful" else echo "test failed" lasttest=failure doubtful="$tested" fi fi tested=`half $doubtful` if test "$tested" = "" -a "$lasttest" = "$expected" then tested="$doubtful" fi done if test "$doubtful" = "" then echo "cannot find the problem; all parts behave as expected" exit 1 fi doubtful=`echo $doubtful | tr -d ' '` if test "$negative" = false then echo "the parts known to be ok are: $knowngood" fi echo "there is a problem in $testeddir/$module.c.part.$doubtful" echo "the difference is:" echo diff -u stage2.$base/$testeddir/$module.c.part.$doubtful stage2.$trial/$testeddir/$module.c.part.$doubtful echo echo "finishing at `date`" exit 0