#!/bin/sh # # vim: ts=4 sw=4 et # # This script finds miscompiled procedures. # # Given a stage2 directory that works (stage2.ok) and one that doesn't # (stage2.bad), both of which must have their object files, 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 everything 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), some initial part of bootstrap checking (making dependencies, # compiling the library), the successful execution of all the test cases in # one or more subdirectories of the tests directory, or the successful execution # of a single command. usage="\ Usage: $0 [options] Options: -b-, --no-bootcheck Do not perform a bootcheck; check only the tests directory or the single command. -c, --compile-only Check the successful creation of the stage3 .c files, but do not compare stage2.ok and stage3. -C, --compare-to-bad Compile stage3 using the parameter settings in the stage2.bad directory, and compare stage3 to stage2.bad, not stage2.ok. -d, --dependency-only Make dependencies for stage3 only. Do not compile stage3. -D , --dir Confine the search to one directory, library, mdbcomp, analysis 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. -l, --library-only Check the successful creation of the stage3 .c files in the library, but do not compile the compiler directory. -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. -s , --single-command Execute the given command using each constructed compiler. -t , --test-dir Execute runtests from the named subdirectory of tests. " # If you change this, you will also need to change the files indicated # in scripts/c2init.in. STD_LIB_NAME=mer_std set -x bootcheck="" compile_only="" dependency_only="" library_only="" jfactor= mmake_opts="" outfile="" copy_runtime=false single_command="" testdirs="" negative=false alldirs="" allmodules="" compare_to_bad="" basis="ok" while [ $# -gt 0 ]; do case "$1" in -b-|--no-bootcheck) bootcheck="-b-" ;; -c|--compile-only) compile_only="-c" ;; -C|--compare-to-bad) compare_to_bad="-C" basis="bad" ;; -d|--dependency-only) dependency_only="-d" ;; -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\(.*\)' `" ;; -l|--library-only) library_only="-l" ;; -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 ;; -s|--single-command) single_command="$2"; shift ;; -s*) single_command=` expr "$1" : '-s\(.*\)' ` ;; --single-command*) single_command=` expr "$1" : '--single-command\(.*\)' ` ;; -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 "$negative" 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 root=`/bin/pwd` trueroot=`echo $root | sed ' s:/mount/munkora/mercury:/home/mercury: s:/mount/munkora/home/mercury:/home/mercury: s:/mount/munkora/clp/mercury:/home/mercury:'` PATH=$root/tools:$PATH export PATH if test "$RMSTAGECMD" = "" then RMSTAGECMD="/bin/rm -fr" fi echo "starting at `date`" for dir in $base $trial do for subdir in library mdbcomp analysis compiler do case $subdir in library) example_o=builtin.o ;; mdbcomp) example_o=mdbcomp.prim_data.o ;; analysis) example_o=analysis.o ;; compiler) example_o=check_hlds.cse_detection.o ;; esac if test -f stage2.$dir/$subdir/$example_o then echo "stage2.$dir/$subdir seems to have its object files" else echo "reconstructing object files in stage2.$dir/$subdir" cd stage2.$dir/$subdir mmake fi done done echo "starting binary at `date`" set +x [ -d stage2 ] || mkdir stage2 $RMSTAGECMD $trueroot/stage2/compiler < /dev/null & $RMSTAGECMD $trueroot/stage2/library < /dev/null & wait $RMSTAGECMD $trueroot/stage2/* < /dev/null echo linking stage 2... 1>&2 cd stage2 ln -s $root/main.c . mkdir compiler cd compiler ln -s $root/compiler/[a-h]*.m . ln -s $root/compiler/[i-o]*.m . ln -s $root/compiler/[p-s]*.m . ln -s $root/compiler/[t-z]*.m . ln -s $root/compiler/*.pp . cp $root/compiler/Mmake* . cp $root/compiler/Mercury* . cp $root/compiler/*FLAGS* . cp $root/compiler/.mgnuc* . cd $root/stage2 mkdir library cd library ln -s $root/library/[a-l]*.m . ln -s $root/library/[m-z]*.m . ln -s $root/library/*.init . cp $root/library/print_extra_inits . cp $root/library/Mmake* . cp $root/library/Mercury* . cp $root/library/*FLAGS* . cp $root/library/.mgnuc* . cd $root/stage2 mkdir mdbcomp cd mdbcomp ln -s $root/mdbcomp/*.m . ln -s $root/mdbcomp/*.init . cp $root/mdbcomp/Mmake* . cp $root/mdbcomp/Mercury* . cp $root/mdbcomp/*FLAGS* . cp $root/mdbcomp/.mgnuc* . cd $root/stage2 mkdir analysis cd analysis ln -s $root/analysis/*.m . ln -s $root/analysis/*.init . cp $root/analysis/Mmake* . cp $root/analysis/Mercury* . cp $root/analysis/*FLAGS* . cp $root/analysis/.mgnuc* . cd $root/stage2 if "$copy_runtime" then mkdir runtime cd runtime ln -s $root/runtime/*.h . ln -s $root/runtime/*.c . ln -s $root/runtime/*.in . ln -s $root/runtime/machdeps . cp $root/runtime/Mmake* . cd $root/stage2 else # $root/runtime may be in a different grade from the stage2 directories. ln -s $root/stage2.ok/runtime . fi ln -s $root/boehm_gc . ln -s $root/browser . ln -s $root/trace . ln -s $root/doc . ln -s $root/scripts . ln -s $root/util . ln -s $root/profiler . ln -s $root/deep_profiler . ln -s $root/tools . ln -s $root/conf* . ln -s $root/aclocal.m4 . ln -s $root/VERSION . ln -s $root/Mercury.options . ln -s $root/.*.in . 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/*.dv stage2/library cp stage2.ok/library/*.int3 stage2/library cp stage2.ok/library/*.date3 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/library/*.opt stage2/library cp stage2.ok/library/*.optdate stage2/library cp stage2.ok/library/*.trans_opt stage2/library cp stage2.ok/library/*.trans_opt_date stage2/library cp stage2.ok/library/*.mh stage2/library # cp stage2.ok/mdbcomp/*.d stage2/mdbcomp cp stage2.ok/mdbcomp/*.dep stage2/mdbcomp cp stage2.ok/mdbcomp/*.dv stage2/mdbcomp cp stage2.ok/mdbcomp/*.int3 stage2/mdbcomp cp stage2.ok/mdbcomp/*.date3 stage2/mdbcomp cp stage2.ok/mdbcomp/*.int stage2/mdbcomp cp stage2.ok/mdbcomp/*.int2 stage2/mdbcomp cp stage2.ok/mdbcomp/*.date stage2/mdbcomp cp stage2.ok/mdbcomp/*.mh stage2/mdbcomp # cp stage2.ok/analysis/*.d stage2/analysis cp stage2.ok/analysis/*.dep stage2/analysis cp stage2.ok/analysis/*.dv stage2/analysis cp stage2.ok/analysis/*.int3 stage2/analysis cp stage2.ok/analysis/*.date3 stage2/analysis cp stage2.ok/analysis/*.int stage2/analysis cp stage2.ok/analysis/*.int2 stage2/analysis cp stage2.ok/analysis/*.date stage2/analysis cp stage2.ok/analysis/*.mh stage2/analysis # cp stage2.ok/compiler/*.d stage2/compiler cp stage2.ok/compiler/*.dep stage2/compiler cp stage2.ok/compiler/*.dv stage2/compiler cp stage2.ok/compiler/*.int3 stage2/compiler cp stage2.ok/compiler/*.date3 stage2/compiler cp stage2.ok/compiler/*.int stage2/compiler cp stage2.ok/compiler/*.int2 stage2/compiler cp stage2.ok/compiler/*.date stage2/compiler cp stage2.ok/compiler/*.mh stage2/compiler if test "$bootcheck" = "" then cd $root [ -d stage3 ] || mkdir stage3 $RMSTAGECMD $trueroot/stage3/compiler < /dev/null & $RMSTAGECMD $trueroot/stage3/library < /dev/null & wait $RMSTAGECMD $trueroot/stage3/* < /dev/null echo linking stage 3... 1>&2 cd stage3 ln -s $root/main.c mkdir compiler cd compiler # Break up the links into several chunks. # This is needed to cope with small limits # on the size of argument vectors. ln -s $root/compiler/[a-h]*.m . ln -s $root/compiler/[i-o]*.m . ln -s $root/compiler/[p-s]*.m . ln -s $root/compiler/[t-z]*.m . ln -s $root/compiler/*.pp . cp $root/compiler/Mmake* . cp $root/compiler/Mercury* . cp $root/compiler/*FLAGS* . cp $root/compiler/.mgnuc* . cd $root/stage3 mkdir library cd library ln -s $root/library/[a-l]*.m . ln -s $root/library/[m-z]*.m . ln -s $root/library/*.init . cp $root/library/Mercury* . cp $root/library/*FLAGS* . cp $root/library/.mgnuc* . cp $root/library/Mmake* . cd $root/stage3 mkdir mdbcomp cd mdbcomp ln -s $root/mdbcomp/*.m . ln -s $root/mdbcomp/*.init . cp $root/mdbcomp/Mercury* . cp $root/mdbcomp/*FLAGS* . cp $root/mdbcomp/.mgnuc* . cp $root/mdbcomp/Mmake* . cd $root/stage3 mkdir analysis cd analysis ln -s $root/analysis/*.m . ln -s $root/analysis/*.init . cp $root/analysis/Mercury* . cp $root/analysis/*FLAGS* . cp $root/analysis/.mgnuc* . cp $root/analysis/Mmake* . cd $root/stage3 ln -s $root/boehm_gc . ln -s $root/browser . ln -s $root/trace . ln -s $root/doc . ln -s $root/scripts . ln -s $root/util . ln -s $root/profiler . ln -s $root/deep_profiler . ln -s $root/runtime . ln -s $root/tools . ln -s $root/conf* . ln -s $root/aclocal.m4 . ln -s $root/VERSION . ln -s $root/Mercury.options . ln -s $root/.*.in . rm -f config*.log /bin/rm -f Mmake* cp $root/stage2.$basis/Mmake* . # cp $root/stage2.ok/so_locations . cd $root fi set -x if "$copy_runtime" 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 cp stage2.ok/main.o stage2 if test "$alldirs" = "" then echo testing which directory the problem is in /bin/rm .stage2_problem found=false for testsubdir in library mdbcomp analysis compiler do if $found then echo skipping test of $testsubdir else for subdir in library mdbcomp analysis compiler do if test "$subdir" = "$testsubdir" then echo linking stage2/$subdir from stage2.bad/$subdir 1>&2 cp stage2.bad/$subdir/*.[co] stage2/$subdir cp stage2.bad/$subdir/*.pic_o stage2/$subdir else echo linking stage2/$subdir from stage2.ok/$subdir 1>&2 cp stage2.ok/$subdir/*.[co] stage2/$subdir cp stage2.ok/$subdir/*.pic_o stage2/$subdir fi done binary_step $bootcheck $compile_only $compare_to_bad \ $dependency_only $library_only $jfactor -m "$mmake_opts" \ $outfile $testdirs -s "$single_command" step_status=$? if test -f .stage2_problem then echo could not build stage 2 to test exit 1 fi if "$negative" then if test "$step_status" == 0 then testeddir=$testsubdir found=true echo "solution seems to be in the $testeddir directory" fi else if test "$step_status" != 0 then testeddir=$testsubdir found=true echo "problem seems to be in the $testeddir directory" fi fi fi done if test "$found" = false then echo "could not find problem" exit 1 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/library/*.pic_o 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 if test $testeddir = library then cp stage2.$trial/library/$module.pic_o stage2/library fi done if "$negative" then if binary_step $bootcheck $compile_only $compare_to_bad $dependency_only $library_only $jfactor -m "$mmake_opts" $outfile $testdirs -s "$single_command" then echo "test succeeded" lasttest=success doubtful="$tested" else echo "test failed" lasttest=failure set +x newdoubtful="" for module in $doubtful do if not appears $module $tested then newdoubtful="$newdoubtful $module" fi done set -x doubtful="$newdoubtful" fi else if binary_step $bootcheck $compile_only $compare_to_bad $dependency_only $library_only $jfactor -m "$mmake_opts" $outfile $testdirs -s "$single_command" then echo "test succeeded" lasttest=success knowngood="$knowngood $tested" set +x newdoubtful="" for module in $doubtful do if not appears $module $tested then newdoubtful="$newdoubtful $module" fi done set -x 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 if test $testeddir = library then cp stage2.$base/library/$module.pic_o stage2/library fi 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 "$negative" then true else echo "the modules known to be ok are: $knowngood" fi echo "there is a problem in $testeddir/$module" echo basecnt=`egrep '^MR_END_MODULE' stage2.$base/$testeddir/$module.c | wc -l` trialcnt=`egrep '^MR_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 set +x doubtful= knowngood= i=0 while test $i -le $basecnt do # If two corresponding parts from stage2.ok and stage.bad are the same, # then the bug cannot be in that part. if cmp -s stage2.$base/$testeddir/$module.c.part.$i stage2.$trial/$testeddir/$module.c.part.$i then knowngood="$knowngood $i" else doubtful="$doubtful $i" fi i=`expr $i + 1` done set -x tested=`half $doubtful` /bin/rm -fr .stage2_problem while test "$tested" != "" do echo "knowngood: $knowngood" 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 "$negative" then if binary_step $bootcheck $compile_only $compare_to_bad $dependency_only $library_only $jfactor -m "$mmake_opts" $outfile $testdirs -s "$single_command" then echo "test succeeded" lasttest=success doubtful="$tested" else echo "test failed" if test -f .stage2_problem then /bin/rm .stage2_problem echo "problem with the creation of stage 2:" echo "search of $testeddir/$module abandoned" exit 1 fi lasttest=failure set +x newdoubtful="" for part in $doubtful do if not appears $part $tested then newdoubtful="$newdoubtful $part" fi done set -x doubtful="$newdoubtful" fi else if binary_step $bootcheck $compile_only $compare_to_bad $dependency_only $library_only $jfactor -m "$mmake_opts" $outfile $testdirs -s "$single_command" then echo "test succeeded" lasttest=success knowngood="$knowngood $tested" set +x newdoubtful="" for part in $doubtful do if not appears $part $tested then newdoubtful="$newdoubtful $part" fi done set -x doubtful="$newdoubtful" else echo "test failed" if test -f .stage2_problem then /bin/rm .stage2_problem echo "problem with the creation of stage 2:" echo "search of $testeddir/$module abandoned" exit 1 fi 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 "$negative" then true else 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