patch_tester.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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/libgomp/testsuite/libgomp.sum
  162. $TARGET/libmudflap/testsuite/libmudflap.sum"
  163. COMPILERS="gcc/cc1
  164. gcc/cc1obj
  165. gcc/cc1plus
  166. gcc/f951
  167. gcc/jc1
  168. gcc/gnat1
  169. gcc/tree1"
  170. now () {
  171. echo `TZ=UTC date +"%Y_%m_%d_%H_%M_%S"`
  172. }
  173. report () {
  174. echo "$@" >> $REPORT
  175. }
  176. freport () {
  177. if [ -s $1 ]; then
  178. report "(cat $1"
  179. cat $1 >> $REPORT
  180. report "tac)"
  181. fi
  182. }
  183. cleanup () {
  184. cd $SOURCE
  185. svn cleanup && svn revert -R . && svn st | cut -d' ' -f5- | xargs rm -v
  186. }
  187. selfexec () {
  188. exec ${CONFIG_SHELL-/bin/sh} $0 $args
  189. }
  190. update () {
  191. svn_branch=`grep "^branch:" $PATCH | sed -e "s/^branch://g" -e "s/ //g"`
  192. if [ x$svn_branch = x ]; then
  193. svn_branch=trunk
  194. fi
  195. svn_revision=`grep "^revision:" $PATCH | sed -e "s/^revision://g" -e "s/ //g"`
  196. if [ x$svn_revision = x ]; then
  197. svn_revision=HEAD
  198. fi
  199. cleanup
  200. cd $SOURCE
  201. case $svn_branch in
  202. trunk)
  203. if ! svn switch -r $svn_revision $svnpath/trunk &> $TESTING/svn ; then
  204. report "failed to update svn sources with"
  205. report "svn switch -r $svn_revision $svnpath/trunk"
  206. freport $TESTING/svn
  207. return 1
  208. fi
  209. ;;
  210. ${svnpath}*)
  211. if ! svn switch -r $svn_revision $svn_branch &> $TESTING/svn ; then
  212. report "failed to update svn sources with"
  213. report "svn switch -r $svn_revision $svn_branch"
  214. freport $TESTING/svn
  215. return 1
  216. fi
  217. ;;
  218. *)
  219. if ! svn switch -r $svn_revision $svnpath/branches/$svn_branch &> $TESTING/svn ; then
  220. report "failed to update svn sources with"
  221. report "svn switch -r $svn_revision $svnpath/branches/$svn_branch"
  222. freport $TESTING/svn
  223. return 1
  224. fi
  225. ;;
  226. esac
  227. contrib/gcc_update --touch
  228. current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
  229. if [[ $VERSION < $current_version ]]; then
  230. if [ -f $SOURCE/contrib/patch_tester.sh ]; then
  231. selfexec
  232. fi
  233. fi
  234. return 0
  235. }
  236. apply_patch () {
  237. if [ $nogpg = false ]; then
  238. if ! gpg --batch --verify $PATCH &> $TESTING/gpgverify ; then
  239. report "your patch failed to verify:"
  240. freport $TESTING/gpgverify
  241. return 1
  242. fi
  243. fi
  244. cd $SOURCE
  245. if ! patch -p0 < $PATCH &> $TESTING/patching ; then
  246. report "your patch failed to apply:"
  247. report "(check that the patch was created at the top level)"
  248. freport $TESTING/patching
  249. return 1
  250. fi
  251. # Just assume indexes for now -- not really great, but svn always
  252. # makes them.
  253. grep "^Index: " $PATCH | sed -e 's/Index: //' | while read file; do
  254. # If the patch resulted in an empty file, delete it.
  255. # This is how svn reports deletions.
  256. if [ ! -s $file ]; then
  257. rm -f $file
  258. report "Deleting empty file $file"
  259. fi
  260. done
  261. }
  262. save_compilers () {
  263. for COMPILER in $COMPILERS ; do
  264. if [ -f $BUILD/$COMPILER ]; then
  265. cp $BUILD/$COMPILER $PRISTINE
  266. fi
  267. done
  268. }
  269. bootntest () {
  270. rm -rf $BUILD
  271. mkdir $BUILD
  272. cd $BUILD
  273. CONFIG_OPTIONS=`grep "^configure:" $PATCH | sed -e "s/^configure://g"`
  274. CONFIG_OPTIONS="$default_config $CONFIG_OPTIONS"
  275. if ! eval $SOURCE/configure $CONFIG_OPTIONS &> $1/configure ; then
  276. report "configure with `basename $1` version failed with:"
  277. freport $1/configure
  278. return 1
  279. fi
  280. MAKE_ARGS=`grep "^make:" $PATCH | sed -e "s/^make://g"`
  281. MAKE_ARGS="$default_make $MAKE_ARGS"
  282. if ! eval make $dashj $MAKE_ARGS &> $1/bootstrap ; then
  283. report "bootstrap with `basename $1` version failed with last lines:"
  284. tail -30 $1/bootstrap > $1/last_bootstrap
  285. freport $1/last_bootstrap
  286. report "grep --context=20 Error bootstrap:"
  287. grep --context=20 Error $1/bootstrap > $1/bootstrap_error
  288. freport $1/bootstrap_error
  289. return 1
  290. fi
  291. CHECK_OPTIONS=`grep "^check:" $PATCH | sed -e "s/^check://g"`
  292. CHECK_OPTIONS="$default_check $CHECK_OPTIONS"
  293. eval make $dashj $CHECK_OPTIONS -k check &> $1/check
  294. SUITESRUN="`grep 'Summary ===' $1/check | cut -d' ' -f 2 | sort`"
  295. if [ x$SUITESRUN = x ]; then
  296. report "check with `basename $1` version failed, no testsuites were run"
  297. return 1
  298. fi
  299. for LOG in $TESTLOGS ; do
  300. if [ -f $BUILD/$LOG ]; then
  301. mv $BUILD/$LOG $1
  302. mv `echo "$BUILD/$LOG" | sed -e "s/\.sum/\.log/g"` $1
  303. fi
  304. done
  305. return 0
  306. }
  307. bootntest_patched () {
  308. cleanup
  309. mkdir -p $PATCHED
  310. apply_patch && bootntest $PATCHED
  311. return $?
  312. }
  313. # Build the pristine tree with exactly the same options as the patch under test.
  314. bootntest_pristine () {
  315. cleanup
  316. current_branch=`svn info $SOURCE | grep "^URL:" | sed -e "s/URL: //g" -e "s,${svnpath},,g"`
  317. current_version=`svn info $SOURCE | grep "^Revision:" | sed -e "s/^Revision://g" -e "s/ //g"`
  318. PRISTINE=$STATE/$current_branch/$current_version
  319. if [ $nopristinecache = true ]; then
  320. rm -rf $PRISTINE
  321. fi
  322. if [ -d $PRISTINE ]; then
  323. ln -s $PRISTINE $TESTING/pristine
  324. return 0
  325. else
  326. mkdir -p $PRISTINE
  327. ln -s $PRISTINE $TESTING/pristine
  328. bootntest $PRISTINE
  329. RETVAL=$?
  330. if [ $RETVAL = 0 -a $savecompilers = true ]; then
  331. save_compilers
  332. fi
  333. return $RETVAL
  334. fi
  335. }
  336. regtest () {
  337. touch $1/report
  338. touch $1/passes
  339. touch $1/failed
  340. touch $1/regress
  341. for LOG in $TESTLOGS ; do
  342. NLOG=`basename $LOG`
  343. if [ -f $1/$NLOG ]; then
  344. awk '/^FAIL: / { print "'$NLOG'",$2; }' $1/$NLOG
  345. fi
  346. done | sort | uniq > $1/failed
  347. comm -12 $1/failed $1/passes >> $1/regress
  348. NUMREGRESS=`wc -l < $1/regress | tr -d ' '`
  349. if [ $NUMREGRESS -eq 0 ] ; then
  350. for LOG in $TESTLOGS ; do
  351. NLOG=`basename $LOG`
  352. if [ -f $1/$NLOG ] ; then
  353. awk '/^PASS: / { print "'$NLOG'",$2; }' $1/$NLOG
  354. fi
  355. done | sort | uniq | comm -23 - $1/failed > $1/passes
  356. echo "there are no regressions with your patch." >> $1/report
  357. else
  358. echo "with your patch there are $NUMREGRESS regressions." >> $1/report
  359. echo "list of regressions with your patch:" >> $1/report
  360. cat $1/regress >> $1/report
  361. fi
  362. }
  363. contrib_compare_tests () {
  364. report "comparing logs with contrib/compare_tests:"
  365. for LOG in $TESTLOGS ; do
  366. NLOG=`basename $LOG`
  367. if [ -f $PRISTINE/$NLOG -a -f $PATCHED/$NLOG ]; then
  368. $SOURCE/contrib/compare_tests $PRISTINE/$NLOG $PATCHED/$NLOG > $TESTING/compare_$NLOG
  369. freport $TESTING/compare_$NLOG
  370. fi
  371. done
  372. }
  373. compare_passes () {
  374. regtest $PRISTINE
  375. cp $PRISTINE/passes $PATCHED
  376. regtest $PATCHED
  377. freport $PATCHED/report
  378. report "FAILs with patched version:"
  379. freport $PATCHED/failed
  380. report "FAILs with pristine version:"
  381. freport $PRISTINE/failed
  382. # contrib_compare_tests
  383. }
  384. write_report () {
  385. backup_patched=$STATE/patched/`now`
  386. report "The files used for the validation of your patch are stored in $backup_patched on the tester machine."
  387. EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
  388. if [ x$EMAIL != x ]; then
  389. mutt -s "[regtest] Results for `basename $PATCH` on $TARGET" -i $REPORT -a $PATCH $EMAIL
  390. fi
  391. mv $TESTING $backup_patched
  392. }
  393. announce () {
  394. EMAIL=`grep "^email:" $PATCH | sed -e "s/^email://g" -e "s/ //g"`
  395. if [ x$EMAIL != x ]; then
  396. START_REPORT=$TESTING/start_report
  397. echo "Hi, " >> $START_REPORT
  398. echo "I'm the automatic tester running on $TARGET." >> $START_REPORT
  399. echo "I just started to look at your patch `basename $PATCH`." >> $START_REPORT
  400. echo "Bye, your automatic tester." >> $START_REPORT
  401. mutt -s "[regtest] Starting bootstrap for `basename $PATCH` on $TARGET" -i $START_REPORT $EMAIL
  402. fi
  403. }
  404. # After selfexec, $TESTING is already set up.
  405. if [ -d $TESTING ]; then
  406. # The only file in $TESTING is the patch.
  407. PATCH=`ls -rt -1 $TESTING | head -1`
  408. PATCH=$TESTING/$PATCH
  409. if [ -f $PATCH ]; then
  410. bootntest_patched && bootntest_pristine && compare_passes
  411. write_report
  412. fi
  413. fi
  414. firstpatch=true
  415. while true; do
  416. PATCH=`ls -rt -1 $PATCHES | head -1`
  417. if [ x$PATCH = x ]; then
  418. if [ $stop = true ]; then
  419. if [ $firstpatch = true ]; then
  420. echo "No patches ready to test, quitting."
  421. exit 1
  422. else
  423. echo "No more patches to test."
  424. exit 0
  425. fi
  426. fi
  427. sleep ${standby}m
  428. else
  429. firstpatch=false
  430. sysload=`uptime | cut -d, -f 5`
  431. if [[ $sysload > $watermark ]]; then
  432. # Wait a bit when system load is too high.
  433. sleep ${standby}m
  434. else
  435. mkdir -p $TESTING
  436. mv $PATCHES/$PATCH $TESTING/
  437. PATCH=$TESTING/$PATCH
  438. announce
  439. update && bootntest_patched && bootntest_pristine && compare_passes
  440. write_report
  441. fi
  442. fi
  443. done