reg-hunt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. #! /bin/bash
  2. #set -x
  3. ########################################################################
  4. #
  5. # File: reg-hunt
  6. # Author: Janis Johnson <janis187@us.ibm.com>
  7. # Date: 2003/08/19
  8. #
  9. # Search for the patch identifier for which results for a test changed,
  10. # using a binary search. The functionality for getting sources,
  11. # building the component to test, and running the test are in other
  12. # scripts that are run from here. Before the search begins, we verify
  13. # that we get the expected behavior for the first and last patch
  14. # identifiers.
  15. #
  16. # Define these in a file whose name is the argument to this script:
  17. # LOW_PATCH: Patch identifier.
  18. # HIGH_PATCH: Patch identifier.
  19. # REG_UPDATE: Pathname of script to update your source tree; returns
  20. # zero for success, nonzero for failure.
  21. # REG_BUILD: Pathname of script to build enough of the product to run
  22. # the test; returns zero for success, nonzero for failure.
  23. # REG_TEST: Pathname of script to run the test; returns 1 if we
  24. # should search later patches, 0 if we should search
  25. # earlier patches, and something else if there was an
  26. # unexpected failure.
  27. # Optional:
  28. # REG_REPORT Pathname of script to call at the end with the id of the
  29. # patch that caused the change in behavior.
  30. # REG_FINISH Pathname of script to call at the end with the two final
  31. # patch identifiers as arguments.
  32. # REG_NEWMID Pathname of script to call when a build has failed, with
  33. # arguments of the failed id and the current low and high
  34. # SKIP_LOW If 1, skip verifying the low patch identifier of the
  35. # range; define this only if you're restarting and have
  36. # already tested the low patch.
  37. # SKIP_HIGH If 1, skip verifying the high patch identifier of the
  38. # range; define this only if you're restarting and have
  39. # already tested the high patch.
  40. # FIRST_MID Use this as the first midpoint, to avoid a midpoint that
  41. # is known not to build.
  42. # VERBOSITY Default is 0, to print only errors and final message.
  43. # DATE_IN_MSG If set to anything but 0, include the time and date in
  44. # messages.
  45. #
  46. #
  47. #
  48. # Copyright (c) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
  49. #
  50. # This file is free software; you can redistribute it and/or modify
  51. # it under the terms of the GNU General Public License as published by
  52. # the Free Software Foundation; either version 3 of the License, or
  53. # (at your option) any later version.
  54. #
  55. # This program is distributed in the hope that it will be useful,
  56. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  57. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  58. # GNU General Public License for more details.
  59. #
  60. # For a copy of the GNU General Public License, write the the
  61. # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  62. # Boston, MA 02111-1301, USA.
  63. #
  64. ########################################################################
  65. ########################################################################
  66. # Functions
  67. ########################################################################
  68. # Issue a message if its verbosity level is high enough.
  69. msg() {
  70. test ${1} -gt ${VERBOSITY} && return
  71. if [ "x${DATE_IN_MSG}" = "x" ]; then
  72. echo "${2}"
  73. else
  74. echo "`date` ${2}"
  75. fi
  76. }
  77. # Issue an error message and exit with a non-zero status. If there
  78. # is a valid current range whose end points have been tested, report
  79. # it so the user can start again from there.
  80. error() {
  81. msg 0 "error: ${1}"
  82. test ${VALID_RANGE} -eq 1 && \
  83. echo "current range:"
  84. echo "LOW_PATCH=${LATER_THAN}"
  85. echo "HIGH_PATCH=${EARLIER_THAN}"
  86. exit 1
  87. }
  88. # Build the components to test using sources as of a particular patch
  89. # and run a test case. Pass each of the scripts the patch identifier
  90. # that we're testing; the first one needs it, the others can ignore it
  91. # if they want.
  92. process_patch () {
  93. TEST_ID=${1}
  94. # If we're keeping track of known failures, see if TEST_ID is one and
  95. # if so, don't bother updating sources and trying to build.
  96. FAILS=0
  97. SKIP=0
  98. if [ ${SKIP_FAILURES} -eq 1 ]; then
  99. ${REG_CHECKFAIL} ${TEST_ID}
  100. if [ $? -eq 0 ]; then
  101. msg 1 "skipping ${TEST_ID}; it is a known build failure"
  102. FAILS=1
  103. SKIP=1
  104. fi
  105. fi
  106. if [ ${FAILS} -eq 0 ]; then
  107. ${REG_UPDATE} ${TEST_ID} || error "source update failed for ${TEST_ID}"
  108. ${REG_BUILD} ${TEST_ID}
  109. if [ $? -ne 0 ]; then
  110. FAILS=1
  111. msg 1 "build failed for ${TEST_ID}"
  112. if [ ${SKIP_FAILURES} -eq 1 ]; then
  113. ${REG_RECORDFAIL} ${TEST_ID}
  114. fi
  115. fi
  116. fi
  117. if [ ${FAILS} -eq 0 ]; then
  118. ${REG_TEST} ${TEST_ID}
  119. LATER=$?
  120. if [ $LATER -ne 0 -a $LATER -ne 1 ]; then
  121. msg 0 "unexpected test failure for ${TEST_ID}"
  122. exit 1
  123. fi
  124. else
  125. # The build failed, or this patch is already known to fail to build.
  126. # If it's an endpoint, or if we don't have a way to recover from
  127. # build failures, quit now.
  128. if [ ${SKIP} -eq 0 ]; then
  129. if [ "x${REG_NEWMID}" == "x" \
  130. -o ${TEST_ID} -eq ${LATER_THAN} \
  131. -o ${TEST_ID} -eq ${EARLIER_THAN} ]; then
  132. error "build failed for ${TEST_ID}"
  133. fi
  134. fi
  135. # Try to find a new patch to try within the current range.
  136. FIRST_MID=`${REG_NEWMID} ${LATER_THAN} ${EARLIER_THAN}`
  137. if [ ${FIRST_MID} -eq 0 ]; then
  138. # The heuristics in the tool ran out of patches to try next;
  139. # let the user handle it from here.+
  140. error "build failed for ${TEST_ID}, could not find new candidate"
  141. fi
  142. msg 1 "using ${FIRST_MID}, between ${LATER_THAN} and ${EARLIER_THAN}"
  143. fi
  144. # Return with a valid LATER value or a new ID to try in FIRST_MID.
  145. }
  146. # Get the number of a patch within the range. It's not actually the
  147. # middle one, but the one that might minimize the number of checks.
  148. get_mid_special() {
  149. LOW=$1
  150. HIGH=$2
  151. let DIFF=HIGH-LOW
  152. M=1
  153. POWER2=1
  154. while
  155. [ $POWER2 -lt $DIFF ]
  156. do
  157. let M=POWER2
  158. let POWER2=POWER2*2
  159. done
  160. let MID=LOW+M
  161. }
  162. # Get the number of the patch in the middle of the range.
  163. get_mid () {
  164. LOW=$1
  165. HIGH=$2
  166. let DIFF=HIGH-LOW
  167. let M=DIFF/2
  168. let MID=LOW+M
  169. }
  170. # Perform a binary search on patch identifiers within the range
  171. # specified by the arguments.
  172. search_patches () {
  173. LOW=$1
  174. HIGH=$2
  175. # Get an identifier within the range. The user can override the
  176. # initial mid patch if it is known to have problems, e.g., if a
  177. # build fails for that patch.
  178. if [ ${FIRST_MID} -ne 0 ]; then
  179. MID=${FIRST_MID}
  180. FIRST_MID=0
  181. let DIFF=HIGH-LOW
  182. else
  183. get_mid $LOW $HIGH
  184. fi
  185. while [ ${DIFF} -gt 1 ]; do
  186. TEST_ID="${MID}"
  187. # Test it.
  188. process_patch ${TEST_ID}
  189. # FIRST_MID being set is a signal that the build failed and we
  190. # should start over again.
  191. test ${FIRST_MID} -ne 0 && return
  192. # Narrow the search based on the outcome of testing TEST_ID.
  193. if [ ${LATER} -eq 1 ]; then
  194. msg 1 "search patches later than ${TEST_ID}"
  195. LATER_THAN=${TEST_ID}
  196. let LOW=MID
  197. else
  198. msg 1 "search patches earlier than ${TEST_ID}"
  199. EARLIER_THAN=${TEST_ID}
  200. let HIGH=MID
  201. fi
  202. get_mid $LOW $HIGH
  203. done
  204. }
  205. ########################################################################
  206. # Main program (so to speak)
  207. ########################################################################
  208. # The error function uses this.
  209. VALID_RANGE=0
  210. # Process the configuration file.
  211. if [ $# != 1 ]; then
  212. echo Usage: $0 config_file
  213. exit 1
  214. fi
  215. CONFIG=${1}
  216. if [ ! -f ${CONFIG} ]; then
  217. error "configuration file ${CONFIG} does not exist"
  218. fi
  219. # OK, the config file exists. Source it, make sure required parameters
  220. # are defined and their files exist, and give default values to optional
  221. # parameters.
  222. . ${CONFIG}
  223. test "x${REG_UPDATE}" = "x" && error "REG_UPDATE is not defined"
  224. test "x${REG_BUILD}" = "x" && error "REG_BUILD is not defined"
  225. test "x${REG_TEST}" = "x" && error "REG_TEST is not defined"
  226. test -x ${REG_TEST} || error "REG_TEST is not an executable file"
  227. test "x${SKIP_LOW}" = "x" && SKIP_LOW=0
  228. test "x${SKIP_HIGH}" = "x" && SKIP_HIGH=0
  229. test "x${VERBOSITY}" = "x" && VERBOSITY=0
  230. test "x${REG_FINISH}" = "x" && REG_FINISH=true
  231. test "x${REG_REPORT}" = "x" && REG_REPORT=true
  232. msg 2 "LOW_PATCH = ${LOW_PATCH}"
  233. msg 2 "HIGH_PATCH = ${HIGH_PATCH}"
  234. msg 2 "REG_UPDATE = ${REG_UPDATE}"
  235. msg 2 "REG_BUILD = ${REG_BUILD}"
  236. msg 2 "REG_TEST = ${REG_TEST}"
  237. msg 2 "REG_NEWMID = ${REG_NEWMID}"
  238. msg 2 "SKIP_LOW = ${SKIP_LOW}"
  239. msg 2 "SKIP_HIGH = ${SKIP_HIGH}"
  240. msg 2 "FIRST_MID = ${FIRST_MID}"
  241. msg 2 "VERBOSITY = ${VERBOSITY}"
  242. # If REG_NEWMID was defined, assume that we're skipping known failures
  243. # and adding to the list for new failures. If the list of failures
  244. # doesn't exist, create it. We use a different flag, SKIP_FAILURES,
  245. # to make it easier to separate the flag from REG_NEWMID if we want
  246. # to change the usage later.
  247. if [ "x${REG_NEWMID}" != "x" ]; then
  248. touch ${REG_FAILLIST}
  249. SKIP_FAILURES=1
  250. else
  251. SKIP_FAILURES=0
  252. fi
  253. # If FIRST_MID was defined, make sure it's in the range.
  254. if [ "x${FIRST_MID}" != "x" ]; then
  255. test ${FIRST_MID} -le ${LOW_PATCH} && \
  256. error "FIRST_MID id is lower than LOW_PATCH"
  257. test ${FIRST_MID} -ge ${HIGH_PATCH} && \
  258. error "FIRST_MID is higher than HIGH_PATCH"
  259. else
  260. FIRST_MID=0
  261. fi
  262. # Keep track of the bounds of the range where the test behavior changes.
  263. LATER_THAN=${LOW_PATCH}
  264. EARLIER_THAN=${HIGH_PATCH}
  265. LATER=1
  266. msg 1 "LATER_THAN = ${LATER_THAN}"
  267. msg 1 "EARLIER_THAN = ${EARLIER_THAN}"
  268. # Verify that the range isn't backwards.
  269. test ${LOW_PATCH} -lt ${HIGH_PATCH} || \
  270. error "patch identifier range is backwards"
  271. # Verify that the first and last patches in the range get the results we
  272. # expect. If not, quit, because any of several things could be wrong.
  273. if [ ${SKIP_HIGH} -eq 0 ]; then
  274. process_patch ${EARLIER_THAN}
  275. test ${LATER} -ne 0 && \
  276. error "unexpected result for high patch ${EARLIER_THAN}"
  277. msg 1 "result for high patch ${EARLIER_THAN} is as expected"
  278. fi
  279. if [ ${SKIP_LOW} -eq 0 ]; then
  280. process_patch ${LATER_THAN}
  281. test ${LATER} -ne 1 && \
  282. error "unexpected result for low patch ${LATER_THAN}"
  283. msg 1 "result for low patch ${LATER_THAN} is as expected"
  284. fi
  285. # Search within the range, now that we know that the end points are valid.
  286. # If the build failed then FIRST_MID is set to a new patch to try.
  287. VALID_RANGE=1
  288. while true; do
  289. search_patches ${LATER_THAN} ${EARLIER_THAN}
  290. test ${FIRST_MID} -eq 0 && break
  291. done
  292. # Report where the test behavior changes.
  293. echo "Test result changes with id ${EARLIER_THAN}"
  294. ${REG_REPORT} ${EARLIER_THAN}
  295. # Invoke the optional script to verify the result and report additional
  296. # information about changes between the two patches.
  297. ${REG_FINISH} ${LATER_THAN} ${EARLIER_THAN}