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