patch_tester.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. #!/bin/sh
  2. # Tests a set of patches from a directory.
  3. # Copyright (C) 2007, 2008, 2011 Free Software Foundation, Inc.
  4. # Contributed by Sebastian Pop <sebastian.pop@amd.com>
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 3 of the License, or
  8. # (at your option) any later version.
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. cat <<EOF
  17. WARNING: This script should only be fed with patches from known
  18. authorized and trusted sources. Don't even think about
  19. hooking it up to a raw feed from the gcc-patches list or
  20. you'll regret it.
  21. EOF
  22. args=$@
  23. svnpath=svn://gcc.gnu.org/svn/gcc
  24. dashj=
  25. default_standby=1
  26. standby=$default_standby
  27. default_watermark=0.60
  28. watermark=$default_watermark
  29. savecompilers=false
  30. nopristinecache=false
  31. nogpg=false
  32. stop=false
  33. usage() {
  34. cat <<EOF
  35. patch_tester.sh [-j<N>] [-standby N] [-watermark N] [-savecompilers] [-nogpg]
  36. [-svnpath URL] [-stop] [-nopristinecache]
  37. <source_dir> [patches_dir [state_dir [build_dir]]]
  38. J is the flag passed to make. Default is empty string.
  39. STANDBY is the number of minutes between checks for new patches in
  40. PATCHES_DIR. Default is ${default_standby} minutes.
  41. WATERMARK is the 5 minute average system charge under which a new
  42. compile can start. Default is ${default_watermark}.
  43. SAVECOMPILERS copies the compilers in the same directory as the
  44. test results for the non patched version. Default is not copy.
  45. NOPRISTINECACHE prevents use of cached test results from any earlier
  46. test runs on the pristine version of the branch and revision under
  47. test (the default behaviour). This should be used when testing the
  48. same revision and patch with multiple sets of configure options, as
  49. these may affect the set of baseline failures.
  50. NOGPG can be used to avoid checking the GPG signature of patches.
  51. URL is the location of the GCC SVN repository. The default is
  52. ${svnpath}.
  53. STOP exits when PATCHES_DIR is empty.
  54. SOURCE_DIR is the directory containing GCC's toplevel configure.
  55. PATCHES_DIR is the directory containing the patches to be tested.
  56. Default is SOURCE_DIR/patches.
  57. STATE_DIR is where the tester maintains its internal state.
  58. Default is SOURCE_DIR/state.
  59. BUILD_DIR is the build tree, a temporary directory that this
  60. script will delete and recreate. Default is SOURCE_DIR/obj.
  61. EOF
  62. exit 1
  63. }
  64. makedir () {
  65. DIRNAME=$1
  66. mkdir -p $DIRNAME
  67. if [ $? -ne 0 ]; then
  68. echo "ERROR: could not make directory $DIRNAME"
  69. exit 1
  70. fi
  71. }
  72. while [ $# -ne 0 ]; do
  73. case $1 in
  74. -j*)
  75. dashj=$1; shift
  76. ;;
  77. -standby)
  78. [[ $# > 2 ]] || usage
  79. standby=$2; shift; shift
  80. ;;
  81. -watermark)
  82. [[ $# > 2 ]] || usage
  83. watermark=$2; shift; shift
  84. ;;
  85. -savecompilers)
  86. savecompilers=true; shift
  87. ;;
  88. -nopristinecache)
  89. nopristinecache=true; shift
  90. ;;
  91. -nogpg)
  92. nogpg=true; shift
  93. ;;
  94. -stop)
  95. stop=true; shift
  96. ;;
  97. -svnpath)
  98. svnpath=$2; shift; shift
  99. ;;
  100. -*)
  101. echo "Invalid option: $1"
  102. usage
  103. ;;
  104. *)
  105. break
  106. ;;
  107. esac
  108. done
  109. test $# -eq 0 && usage
  110. SOURCE=$1
  111. PATCHES=
  112. STATE=
  113. BUILD=
  114. if [[ $# < 2 ]]; then
  115. PATCHES=$SOURCE/patches
  116. else
  117. PATCHES=$2
  118. fi
  119. if [[ $# < 3 ]]; then
  120. STATE=$SOURCE/state
  121. else
  122. STATE=$3
  123. fi
  124. if [[ $# < 4 ]]; then
  125. BUILD=$SOURCE/obj
  126. else
  127. BUILD=$4
  128. fi
  129. [ -d $PATCHES ] || makedir $PATCHES
  130. [ -d $STATE ] || makedir $STATE
  131. [ -d $STATE/patched ] || makedir $STATE/patched
  132. [ -d $SOURCE ] || makedir $SOURCE
  133. [ -f $SOURCE/config.guess ] || {
  134. cd $SOURCE
  135. svn -q co $svnpath/trunk .
  136. if [ $? -ne 0 ]; then
  137. echo "ERROR: initial svn checkout failed"
  138. exit 1
  139. fi
  140. }
  141. # This can contain required local settings:
  142. # default_config configure options, always passed
  143. # default_make make bootstrap options, always passed
  144. # default_check make check options, always passed
  145. [ -f $STATE/defaults ] && . $STATE/defaults
  146. VERSION=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
  147. exec >> $STATE/tester.log 2>&1 || exit 1
  148. set -x
  149. TESTING=$STATE/testing
  150. REPORT=$TESTING/report
  151. PRISTINE=$TESTING/pristine
  152. PATCHED=$TESTING/patched
  153. PATCH=
  154. TARGET=`$SOURCE/config.guess || exit 1`
  155. TESTLOGS="gcc/testsuite/gcc/gcc.sum
  156. gcc/testsuite/gfortran/gfortran.sum
  157. gcc/testsuite/g++/g++.sum
  158. gcc/testsuite/objc/objc.sum
  159. $TARGET/libstdc++-v3/testsuite/libstdc++.sum
  160. $TARGET/libffi/testsuite/libffi.sum
  161. $TARGET/libjava/testsuite/libjava.sum
  162. $TARGET/libgomp/testsuite/libgomp.sum
  163. $TARGET/libmudflap/testsuite/libmudflap.sum"
  164. COMPILERS="gcc/cc1
  165. gcc/cc1obj
  166. gcc/cc1plus
  167. gcc/f951
  168. gcc/jc1
  169. gcc/gnat1
  170. gcc/tree1"
  171. now () {
  172. echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
  173. }
  174. report () {
  175. echo "$@" >> $REPORT
  176. }
  177. freport () {
  178. if [ -s $1 ]; then
  179. report "(cat $1"
  180. cat $1 >> $REPORT
  181. report "tac)"
  182. fi
  183. }
  184. cleanup () {
  185. cd $SOURCE
  186. svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
  187. }
  188. selfexec () {
  189. exec ${CONFIG_SHELL-/bin/sh} $0 $args
  190. }
  191. update () {
  192. svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
  193. if [ x$svn_branch = x ]; then
  194. svn_branch=trunk
  195. fi
  196. svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
  197. if [ x$svn_revision = x ]; then
  198. svn_revision=HEAD
  199. fi
  200. cleanup
  201. cd $SOURCE
  202. case $svn_branch in
  203. trunk)
  204. if ! svn switch -r $svn_revision $svnpath/trunk &> $TESTING/svn ; then
  205. report "failed to update svn sources with"
  206. report "svn switch -r $svn_revision $svnpath/trunk"
  207. freport $TESTING/svn
  208. return 1
  209. fi
  210. ;;
  211. ${svnpath}*)
  212. if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
  213. report "failed to update svn sources with"
  214. report "svn switch -r $svn_revision $svn_branch"
  215. freport $TESTING/svn
  216. return 1
  217. fi
  218. ;;
  219. *)
  220. if ! svn switch -r $svn_revision $svnpath/branches/$svn_branch &> $TESTING/svn ; then
  221. report "failed to update svn sources with"
  222. report "svn switch -r $svn_revision $svnpath/branches/$svn_branch"
  223. freport $TESTING/svn
  224. return 1
  225. fi
  226. ;;
  227. esac
  228. contrib/gcc_update --touch
  229. current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
  230. if [[ $VERSION < $current_version ]]; then
  231. if [ -f $SOURCE/contrib/patch_tester.sh ]; then
  232. selfexec
  233. fi
  234. fi
  235. return 0
  236. }
  237. apply_patch () {
  238. if [ $nogpg = false ]; then
  239. if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
  240. report "your patch failed to verify:"
  241. freport $TESTING/gpgverify
  242. return 1
  243. fi
  244. fi
  245. cd $SOURCE
  246. if ! patch -p0 < $PATCH &> $TESTING/patching ; then
  247. report "your patch failed to apply:"
  248. report "(check that the patch was created at the top level)"
  249. freport $TESTING/patching
  250. return 1
  251. fi
  252. # Just assume indexes for now -- not really great, but svn always
  253. # makes them.
  254. grep "^Index: " $PATCH | sed -e 's/Index: //' | while read file; do
  255. # If the patch resulted in an empty file, delete it.
  256. # This is how svn reports deletions.
  257. if [ ! -s $file ]; then
  258. rm -f $file
  259. report "Deleting empty file $file"
  260. fi
  261. done
  262. }
  263. save_compilers () {
  264. for COMPILER in $COMPILERS ; do
  265. if [ -f $BUILD/$COMPILER ]; then
  266. cp $BUILD/$COMPILER $PRISTINE
  267. fi
  268. done
  269. }
  270. bootntest () {
  271. rm -rf $BUILD
  272. mkdir $BUILD
  273. cd $BUILD
  274. CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
  275. CONFIG_OPTIONS="$default_config $CONFIG_OPTIONS"
  276. if ! eval $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
  277. report "configure with `basename $1` version failed with:"
  278. freport $1/configure
  279. return 1
  280. fi
  281. MAKE_ARGS=`grep "^make:" $PATCH | sed -e "s/^make://g"`
  282. MAKE_ARGS="$default_make $MAKE_ARGS"
  283. if ! eval make $dashj $MAKE_ARGS &> $1/bootstrap ; then
  284. report "bootstrap with `basename $1` version failed with last lines:"
  285. tail -30 $1/bootstrap > $1/last_bootstrap
  286. freport $1/last_bootstrap
  287. report "grep --context=20 Error bootstrap:"
  288. grep --context=20 Error $1/bootstrap > $1/bootstrap_error
  289. freport $1/bootstrap_error
  290. return 1
  291. fi
  292. CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
  293. CHECK_OPTIONS="$default_check $CHECK_OPTIONS"
  294. eval make $dashj $CHECK_OPTIONS -k check &> $1/check
  295. SUITESRUN="`grep 'Summary ===' $1/check | cut -d' ' -f 2 | sort`"
  296. if [ x$SUITESRUN = x ]; then
  297. report "check with `basename $1` version failed, no testsuites were run"
  298. return 1
  299. fi
  300. for LOG in $TESTLOGS ; do
  301. if [ -f $BUILD/$LOG ]; then
  302. mv $BUILD/$LOG $1
  303. mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
  304. fi
  305. done
  306. return 0
  307. }
  308. bootntest_patched () {
  309. cleanup
  310. mkdir -p $PATCHED
  311. apply_patch && bootntest $PATCHED
  312. return $?
  313. }
  314. # Build the pristine tree with exactly the same options as the patch under test.
  315. bootntest_pristine () {
  316. cleanup
  317. current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s,${svnpath},,g"`
  318. current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
  319. PRISTINE=$STATE/$current_branch/$current_version
  320. if [ $nopristinecache = true ]; then
  321. rm -rf $PRISTINE
  322. fi
  323. if [ -d $PRISTINE ]; then
  324. ln -s $PRISTINE $TESTING/pristine
  325. return 0
  326. else
  327. mkdir -p $PRISTINE
  328. ln -s $PRISTINE $TESTING/pristine
  329. bootntest $PRISTINE
  330. RETVAL=$?
  331. if [ $RETVAL = 0 -a $savecompilers = true ]; then
  332. save_compilers
  333. fi
  334. return $RETVAL
  335. fi
  336. }
  337. regtest () {
  338. touch $1/report
  339. touch $1/passes
  340. touch $1/failed
  341. touch $1/regress
  342. for LOG in $TESTLOGS ; do
  343. NLOG=`basename $LOG`
  344. if [ -f $1/$NLOG ]; then
  345. awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
  346. fi
  347. done | sort | uniq > $1/failed
  348. comm -12 $1/failed $1/passes >> $1/regress
  349. NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
  350. if [ $NUMREGRESS -eq 0 ] ; then
  351. for LOG in $TESTLOGS ; do
  352. NLOG=`basename $LOG`
  353. if [ -f $1/$NLOG ] ; then
  354. awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
  355. fi
  356. done | sort | uniq | comm -23 - $1/failed > $1/passes
  357. echo "there are no regressions with your patch." >> $1/report
  358. else
  359. echo "with your patch there are $NUMREGRESS regressions." >> $1/report
  360. echo "list of regressions with your patch:" >> $1/report
  361. cat $1/regress >> $1/report
  362. fi
  363. }
  364. contrib_compare_tests () {
  365. report "comparing logs with contrib/compare_tests:"
  366. for LOG in $TESTLOGS ; do
  367. NLOG=`basename $LOG`
  368. if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
  369. $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
  370. freport $TESTING/compare_$NLOG
  371. fi
  372. done
  373. }
  374. compare_passes () {
  375. regtest $PRISTINE
  376. cp $PRISTINE/passes $PATCHED
  377. regtest $PATCHED
  378. freport $PATCHED/report
  379. report "FAILs with patched version:"
  380. freport $PATCHED/failed
  381. report "FAILs with pristine version:"
  382. freport $PRISTINE/failed
  383. # contrib_compare_tests
  384. }
  385. write_report () {
  386. backup_patched=$STATE/patched/`now`
  387. report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
  388. EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
  389. if [ x$EMAIL != x ]; then
  390. mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
  391. fi
  392. mv $TESTING $backup_patched
  393. }
  394. announce () {
  395. EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
  396. if [ x$EMAIL != x ]; then
  397. START_REPORT=$TESTING/start_report
  398. echo "Hi, " >> $START_REPORT
  399. echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
  400. echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
  401. echo "Bye, your automatic tester." >> $START_REPORT
  402. mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
  403. fi
  404. }
  405. # After selfexec, $TESTING is already set up.
  406. if [ -d $TESTING ]; then
  407. # The only file in $TESTING is the patch.
  408. PATCH=`ls -rt -1 $TESTING | head -1`
  409. PATCH=$TESTING/$PATCH
  410. if [ -f $PATCH ]; then
  411. bootntest_patched && bootntest_pristine && compare_passes
  412. write_report
  413. fi
  414. fi
  415. firstpatch=true
  416. while true; do
  417. PATCH=`ls -rt -1 $PATCHES | head -1`
  418. if [ x$PATCH = x ]; then
  419. if [ $stop = true ]; then
  420. if [ $firstpatch = true ]; then
  421. echo "No patches ready to test, quitting."
  422. exit 1
  423. else
  424. echo "No more patches to test."
  425. exit 0
  426. fi
  427. fi
  428. sleep ${standby}m
  429. else
  430. firstpatch=false
  431. sysload=`uptime | cut -d, -f 5`
  432. if [[ $sysload > $watermark ]]; then
  433. # Wait a bit when system load is too high.
  434. sleep ${standby}m
  435. else
  436. mkdir -p $TESTING
  437. mv $PATCHES/$PATCH $TESTING/
  438. PATCH=$TESTING/$PATCH
  439. announce
  440. update && bootntest_patched && bootntest_pristine && compare_passes
  441. write_report
  442. fi
  443. fi
  444. done