#!/bin/bash set -ef -o pipefail scripts=$(dirname $0) . $scripts/jenkins-helpers.sh # Relative artifacts are used for generation of manifests and reproduction # instructions. rel_artifacts=artifacts artifacts=$(pwd)/$rel_artifacts fresh_dir $artifacts "$artifacts/manifests/*" "$artifacts/jenkins/*" # Process bisect-only args convert_args_to_variables "$@" shift "$SHIFT_CONVERTED_ARGS" obligatory_variables bad_branch baseline_branch build_script current_project BUILD_URL="${BUILD_URL:-$(pwd)}" reproduce_bisect="${reproduce_bisect:-false}" # Process build args and record them in build-parameters.sh convert_args_to_variables ^^ $reproduce_bisect %% $artifacts/manifests/build-parameters.sh "$@" $reproduce_bisect || manifest_pop obligatory_variables rr[ci_project] rr[ci_config] verbose="${verbose:-true}" set -u if $verbose; then set -x; fi trap "eval \"echo ERROR at \${FUNCNAME[0]}:\${BASH_LINENO[0]}\" > $artifacts/failures" EXIT rebase_workaround=false case "${rr[ci_project]}/${rr[ci_config]}:$current_project" in tcwg_kernel/*-next-*:linux) # Workaround linux-next/master rebasing on top of linux-next/stable. # Search for regressions against linux-mainline:master (aka linux-next:stable). clone_or_update_repo $current_project stable ${rr[linux_url]} # Just in case linux-next:stable has advanced between the build and bisect jobs, # use merge base between linux-next:stable and $bad_branch. bad_rev="${bad_rev-$(git_rev_parse_long $current_project $bad_branch)}" linux_next_stable="${linux_next_stable-$(git -C $current_project merge-base HEAD $bad_rev)}" cat <> $artifacts/good_revs # Bisect script. # # With this script we find the first commit that has regressed compared # to baseline, but not, necessarily, the commit that caused regression in # $bad_rev. Consider the scenario: # - rev_10 produced good result "2000" -- this is current baseline # - rev_20 completely broke the build (say, result "10") # - rev_22 fixed the build # - rev_30 regressed the build to result "1000" -- this is the regression we # detected vs "2000" baseline. # # The script will identify rev_20 as the first failing commit, which will # cause the baseline to be reset to rev_20 with metric "10". When we then # rebuild master (at rev_30) we will see a /progression/ from "10" to "1000", # thus missing the regression of "2000" to "1000". # # To catch the "2000" to "1000" regression someone would need to manually # trigger bisect between rev_22 and rev_30. # # TODO: We could skip revisions (exit 125) that are worse than metric # for $bad_rev (result metric <1000 in the above scenario), so we would # skip revisions between rev_20 and rev_22. This might cause other edge # cases to be handled sub-optimally, though. cat > $artifacts/test.sh <> $rel_artifacts/bad_revs exit 1 else echo "\$rev" >> $rel_artifacts/good_revs exit 0 fi EOF chmod +x $artifacts/test.sh bad_rev="${bad_rev-$(git_rev_parse_long $current_project $bad_branch)}" cat < $artifacts/trigger-build-rebase < $artifacts/trigger-build-rebase <> $artifacts/trigger-build-rebase < $artifacts/trigger-build-retry < $artifacts/jenkins/mail-recipients.txt trap "" EXIT exit 0 fi ln -f -s "build-$bad_rev" "$artifacts/build-bad" ln -f -s "build-$bad_rev.sh" "$artifacts/manifests/build-bad.sh" git bisect start $bad_rev $baseline_rev 2>&1 | tee $artifacts/bisect.log # "git bisect run" can fail (exit with non-zero) in a number of cases: # - on trivial bisects (e.g., between HEAD^ and HEAD), # - when merge-base between baseline and bad is worse than baseline, # - something else? # In all these cases we want to reset baseline to HEAD, so that we catch # most of the commits that introduced change in the result metric. git bisect run $artifacts/test.sh 2>&1 | tee -a $artifacts/bisect.log & res=0 && wait $! || res=$? if [ x"$res" = x"0" ]; then first_bad=$(cat .git/BISECT_RUN | head -n 1 | grep "is the first bad commit" | cut -d" " -f 1) assert [ x"$first_bad" != x"" ] res=0 for last_good in $(git rev-parse $first_bad^@); do # It seems that git-bisect assumes parent commit as "good" on # the basis of one of its children being "good". Therefore we # can have a situation when we have parent P with children C1 and C2, # and child C1 has a child of its own CB. Git-bisect tests C2 as # "good", and CB as "bad". From C2 being good it assumes P as "good", # and it knows CB is "bad", so git-bisect returns C1 as the first bad # commit. # To simplify investigations we explicitly test parent of $first_bad. echo "Testing first_bad's parent $last_good (hoping for success)" git checkout --detach "$last_good" $artifacts/test.sh & res=0 && wait $! || res=$? if [ x"$res" = x"0" ]; then break fi done if [ x"$res" != x"0" ]; then # It seems $last_good was on a path that tested good, even though # it itself is bad. We re-trigger the bisection job with updated # parameters. # We need to be careful to avoid re-trigger loops, so verify that # last_good is an ancestor of bad_rev. assert git merge-base --is-ancestor $last_good $bad_rev cat > $artifacts/trigger-bisect < $artifacts/jenkins/mail-recipients.txt touch $artifacts/jenkins/build-name sed -i -e "s/\$/-last_good-bad/" $artifacts/jenkins/build-name trap "" EXIT exit 0 fi echo $first_bad > $artifacts/first-bad else first_bad=$(git rev-parse HEAD) if ! [ -f .git/BISECT_LOG ]; then # It seems this was a trivial bisect with $bad_rev^ == $baseline_rev. first_bad=$bad_rev last_good=$(git rev-parse $first_bad^) assert [ x"$last_good" = x"$baseline_rev" ] echo $first_bad > $artifacts/first-bad fi fi cd .. # Save BISECT_* logs mkdir $artifacts/git-logs find "$current_project" -path "$current_project/.git/BISECT_*" -print0 | xargs -0 -I@ mv @ $artifacts/git-logs/ if [ -f $artifacts/first-bad ]; then mkdir -p $artifacts/jenkins touch $artifacts/jenkins/build-name sed -i -e "s/\$/-$first_bad/" $artifacts/jenkins/build-name ln -f -s "build-$first_bad" "$artifacts/build-first_bad" ln -f -s "build-$first_bad.sh" "$artifacts/manifests/build-first_bad.sh" good_name="last_good" good_sha1="$last_good" bad_name="first_bad" bad_sha1="$first_bad" ln -f -s "build-$last_good" "$artifacts/build-last_good" ln -f -s "build-$last_good.sh" "$artifacts/manifests/build-last_good.sh" cat >> $artifacts/jenkins/mail-body.txt < $(git -C $current_project log -n 1 $first_bad) EOF else good_name="baseline_rev" good_sha1="$baseline_rev" bad_name="bad" bad_sha1="$bad_rev" cat >> $artifacts/jenkins/mail-body.txt <> $artifacts/jenkins/mail-body.txt < mkdir investigate-$current_project-$bad_sha1 cd investigate-$current_project-$bad_sha1 git clone https://git.linaro.org/toolchain/jenkins-scripts mkdir -p $rel_artifacts/manifests curl -o $rel_artifacts/manifests/build-baseline.sh ${BUILD_URL}artifact/$rel_artifacts/manifests/build-baseline.sh curl -o $rel_artifacts/manifests/build-parameters.sh ${BUILD_URL}artifact/$rel_artifacts/manifests/build-parameters.sh curl -o $rel_artifacts/test.sh ${BUILD_URL}artifact/$rel_artifacts/test.sh chmod +x $rel_artifacts/test.sh # Reproduce the baseline build (build all pre-requisites) $build_script @@ $rel_artifacts/manifests/build-baseline.sh cd $current_project # Reproduce $bad_name build git checkout --detach $bad_sha1 ../$rel_artifacts/test.sh # Reproduce $good_name build git checkout --detach $good_sha1 ../$rel_artifacts/test.sh cd .. History of pending regressions and results: https://git.linaro.org/toolchain/ci/base-artifacts.git/log/?h=linaro-local/ci/${rr[ci_project]}/${rr[ci_config]} Bisect log: ${BUILD_URL}artifact/$rel_artifacts/bisect.log/*view*/ Artifacts: ${BUILD_URL}artifact/$rel_artifacts/ Build URL: $BUILD_URL Build log: ${BUILD_URL}consoleText EOF if [ -f $artifacts/first-bad ]; then cat >> $artifacts/jenkins/mail-body.txt < $(git -C $current_project show --stat --patch $first_bad | head -n 1000) EOF fi # Set mail recipients last to preserve catch-error value from .yaml file. # Email developers. CI_MAIL_RECIPIENTS="tcwg-validation@linaro.org" case "${rr[ci_project]}/${rr[ci_config]}:$current_project" in tcwg_kernel/gnu-*:linux) ;; tcwg_kernel/gnu-*:*) CI_MAIL_RECIPIENTS="$CI_MAIL_RECIPIENTS, christophe.lyon@linaro.org, maxim.kuvyrkov@linaro.org" ;; tcwg_kernel/llvm-*:linux) CI_MAIL_RECIPIENTS="$CI_MAIL_RECIPIENTS, arnd@linaro.org, mark.brown@linaro.org, ndesaulniers@google.com, trong@google.com" ;; tcwg_kernel/llvm-*:llvm) CI_MAIL_RECIPIENTS="$CI_MAIL_RECIPIENTS, adhemerval.zanella@linaro.org, maxim.kuvyrkov@linaro.org, ndesaulniers@google.com, trong@google.com, yvan.roux@linaro.org" ;; esac cat > $artifacts/jenkins/mail-recipients.txt <> $artifacts/trigger-build-1-reset # Trigger master build now instead of waiting for next timed SCM trigger. cp $artifacts/build-$bad_rev/trigger-build-$current_project $artifacts/trigger-build-2-default trap "" EXIT