aboutsummaryrefslogtreecommitdiff
path: root/bisect
diff options
context:
space:
mode:
authorRenato Golin <rengolin@gmail.com>2016-04-26 11:02:23 +0100
committerRenato Golin <rengolin@gmail.com>2016-04-26 11:02:23 +0100
commit94cc104f044261f74fbff3ff587855df1a05f64d (patch)
tree7c6664fdf7e7fab54fea926b1fc6196a09b6540f /bisect
downloadlinaro-scripts-94cc104f044261f74fbff3ff587855df1a05f64d.tar.gz
Initial Commit, moving from dev-private and removing private stuff
Diffstat (limited to 'bisect')
-rw-r--r--bisect/README.txt41
-rwxr-xr-xbisect/bisect.pl145
-rwxr-xr-xbisect/checkout.sh61
-rwxr-xr-xbisect/common.sh11
-rwxr-xr-xbisect/run.sh114
-rwxr-xr-xbisect/sequential.sh45
-rwxr-xr-xbisect/test.sh39
-rwxr-xr-xbisect/unit_test.sh56
8 files changed, 512 insertions, 0 deletions
diff --git a/bisect/README.txt b/bisect/README.txt
new file mode 100644
index 0000000..83abfb1
--- /dev/null
+++ b/bisect/README.txt
@@ -0,0 +1,41 @@
+LLVM Bisect Scripts
+===================
+
+These scripts help you bisect LLVM/Clang for problems on build, check
+and test-suite (on a specific test, not all of it). The only script
+you should use is 'bisect.pl', as that will call all others in the
+correct way for a given command line options.
+
+You can rely on the power of run.sh to build and test (either check-all
+or the test-suite), but you can also write your own test.sh script and
+pass it to bisect.pl.
+
+Usage:
+ $ ./bisect.sh [options] good_rev bad_rev
+
+Options:
+ -c checkout_script (default: ./checkout.sh)
+ -b build_script (default: ./run.sh)
+ -t test_script (default: empty)
+
+Notes:
+
+* If the test script is empty, the success status will come from the build
+ script. So, if you're bisecting a build issue, or your build script is
+ also testing (ex: ./run.sh -c), leaving the test script empty will work
+ as expected.
+
+* There is no support for Libc++, LLd etc. But it shouldn't be
+ too hard to add it to the list of checkout paths and symlinks on run.sh.
+
+* It assumes your test-suite checkout is correct and on the same directory,
+ please see the test-suite scripts to help you set it up. If you have your
+ own setup, please write an according test script.
+
+* The script creates a log for each checkout ($rev.log), so that you can tail
+ it while building. It will also move the log to .good and .bad once it
+ finishes, given run.sh or your test script's return status.
+
+* I *strongly* advise you to use ccache, since we *must* clean up the build
+ directory before *every* build to avoid carrying on problems (one of the
+ reasons I moved back from a proper bisect).
diff --git a/bisect/bisect.pl b/bisect/bisect.pl
new file mode 100755
index 0000000..acf6c21
--- /dev/null
+++ b/bisect/bisect.pl
@@ -0,0 +1,145 @@
+#!/usr/bin/env perl
+
+# This script drives the bisection of LLVM/Clang/RT regressions
+# by receiving checkout, build and test commands.
+#
+# By default, the lines are:
+# checkout: './checkout.sh'
+# build: './run.sh'
+# test: ''
+#
+# Which means: success == builds.
+#
+# If you use a different checkout script, make sure it accepts one
+# parameter, the SVN revision.
+#
+# Since testing is the hard part, you should write your own
+# custom test script for each job, but you can reuse the checkout
+# and run scripts if you like.
+#
+# The test script should return 0 when the commit is GOOD and non-zero
+# when it is bad.
+#
+# The build script has a way to quickly test (check-all, test-suite)
+# and you can use that as well, as a shortcut for the testing.
+
+use strict;
+use warnings;
+use Getopt::Std;
+use Scalar::Util qw(looks_like_number);
+
+################################## Command line parsing and validation
+my ($prog) = ($0 =~ /\/([^\/]+)$/);
+my $syntax = "Syntax: $prog [-c checkout_script] [-b build_script] [-t test_script] good_rev bad_rev\n";
+die $syntax unless &getopts('c:b:t:');
+our($opt_c, $opt_b, $opt_t);
+# Job Statuses
+my $Good = 0;
+my $CompilerError = 1;
+my $TestError = 2;
+# Defaults
+my ($checkout, $build, $test) = ("./checkout.sh", "./run.sh", "");
+$checkout = $opt_c if defined $opt_c;
+$build = $opt_b if defined $opt_b;
+$test = $opt_t if defined $opt_t;
+# Validate
+die "Invalid checkout script '$checkout'\n" unless &validate_script(\$checkout);
+die "Invalid run script '$build'\n" unless &validate_script(\$build);
+die "Invalid test script '$test'\n" unless not $test or &validate_script(\$test);
+# Revs
+my $last = scalar @ARGV-1;
+die $syntax unless $last == 1;
+my $good_rev = $ARGV[$last-1];
+die $syntax unless looks_like_number $good_rev;
+my $bad_rev = $ARGV[$last];
+die $syntax unless looks_like_number $bad_rev;
+die "Bad rev '$bad_rev' is less than or equal good rev '$good_rev'\n"
+ unless $bad_rev > $good_rev;
+&main();
+exit;
+
+################################## Main bisect logic
+
+sub main () {
+ my ($good, $bad) = ($good_rev, $bad_rev);
+
+ # Header
+ print "\n==============================================\n".
+ " Checkout script: $checkout\n".
+ " Build script: $build\n".
+ " Test script: $test\n".
+ " Range: [ $good, $bad ]\n".
+ "\n==============================================\n";
+
+ # Iterations
+ my $step = 1;
+ my $range = $bad-$good;
+ my $forced_rev = 0;
+ while ($bad > $good and $bad != $good+1) {
+ my $rev = int($good+($bad-$good)/2);
+ # Use forced rev, from compilation error
+ if ($forced_rev) {
+ $rev = $forced_rev;
+ $forced_rev = 0;
+ }
+ print "\n---------------------------\n";
+ print "Step $step: $good -> $rev -> $bad\n";
+ my $result = &check($rev);
+ # All good
+ if ($result == $Good) {
+ print "Revision '$rev' is good\n";
+ rename "$rev.log", "$rev.good";
+ $good = $rev;
+ # Compiler error when test should run
+ } elsif ($result == $CompilerError and $test) {
+ print "Revision '$rev' has a compiler error\n".
+ "Since you're looking for a test error,\n".
+ "we'll skip a rev\n";
+ rename "$rev.log", "$rev.compile";
+ $forced_rev = $rev+1;
+ # Proper error
+ } else {
+ print "Revision '$rev' is bad\n";
+ rename "$rev.log", "$rev.bad";
+ $bad = $rev;
+ }
+ $step++;
+ }
+
+ # Final status
+ print "\n==============================================\n".
+ " First bad revision is '$bad'\n".
+ " Found in $step steps in a range of $range commits".
+ "\n==============================================\n";
+}
+
+################################## Checkout, run, test
+
+sub check ($) {
+ my ($rev) = @_;
+ unlink "$rev.log";
+ my $log = "2>&1 >> $rev.log";
+ print "Checking out rev '$rev'\n";
+ die "Error while checking out '$rev'\n" if system("$checkout $rev $log");
+ print "Building rev '$rev'\n";
+ return $CompilerError if system("$build $log");
+ if ($test) {
+ print "Testing rev '$rev'\n";
+ return $TestError if system("$test $log");
+ }
+ return $Good;
+}
+
+################################## Helpers
+
+sub validate_script($) {
+ my ($command) = @_;
+ my ($script) = split /\s+/, $$command;
+ return 0 if (! -x $script);
+ if ($script !~ /^[\/\.]/) {
+ $script = "./$script";
+ return 0 if (! -x $script);
+ $$command = "./$$command";
+ }
+ return 1;
+}
diff --git a/bisect/checkout.sh b/bisect/checkout.sh
new file mode 100755
index 0000000..16fe25c
--- /dev/null
+++ b/bisect/checkout.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+
+# This script checks out both LLVM and Clang on the same revision number.
+# It can also revert a commit in the past to check for hidden failures.
+
+validate () {
+ echo $1 | grep -q "^[0-9]\+$"
+}
+
+checkout () {
+ name=$1
+ dir=$2
+ to=$3
+ echo $name
+ if [ -d $dir ]; then
+ cd $dir
+ svn revert .
+ svn up -r $to
+ cd ..
+ else
+ svn co -r $to http://llvm.org/svn/llvm-project/$dir/trunk $dir
+ fi
+}
+
+revert () {
+ dir=$1
+ minus=$2
+ cd $dir
+ svn merge -c -$minus .
+ cd ..
+}
+
+if [ "$1" = "" ]; then
+ echo "Syntax: $0 <revision-to> [<revision-minus>]"
+ exit 1
+fi
+if ! validate $1; then
+ echo "Invalid SVN revision to $1"
+ exit 1
+fi
+if [ "$2" != "" ] && ! validate $2; then
+ echo "Invalid SVN revision minus $2"
+ exit 1
+fi
+
+checkout "LLVM" llvm $1
+if [ "$2" != "" ]; then
+ revert llvm $2
+fi
+
+checkout "Clang" cfe $1
+ln -sf `pwd`/cfe `pwd`/llvm/tools/clang
+if [ "$2" != "" ]; then
+ revert cfe $2
+fi
+
+checkout "Compiler-RT" compiler-rt $1
+ln -sf `pwd`/compiler-rt `pwd`/llvm/projects/compiler-rt
+if [ "$2" != "" ]; then
+ revert compiler-rt $2
+fi
diff --git a/bisect/common.sh b/bisect/common.sh
new file mode 100755
index 0000000..82ff7e4
--- /dev/null
+++ b/bisect/common.sh
@@ -0,0 +1,11 @@
+# Common functions for the bisect execution
+# Do not call directly. Feel free to use.
+
+safe_run() {
+ CMD="$*"
+ $CMD
+ if [[ $? != 0 ]]; then
+ echo "'$CMD' failed, bailing out"
+ exit 1
+ fi
+}
diff --git a/bisect/run.sh b/bisect/run.sh
new file mode 100755
index 0000000..6219248
--- /dev/null
+++ b/bisect/run.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+
+# This script run one cycle of the bisect, by building (or self-hosting)
+# LLVM+Clang, running the compiler tests and the test-suite, if required.
+# Please, use the sequential.sh script to drive this one.
+
+. common.sh
+
+CORE=`grep "CPU part" /proc/cpuinfo | awk '{print $4}'`
+if [[ $CORE = '0xc08' ]]; then
+ CORE="-mcpu=cortex-a8"
+else if [[ $CORE = '0xc09' ]]; then
+ CORE="-mcpu=cortex-a9"
+else if [[ $CORE = '0xc0f' ]]; then
+ CORE="-mcpu=cortex-a15"
+else if [[ $CORE = '0xd03' ]]; then
+ CORE="-mcpu=cortex-a53"
+else if [[ $CORE = '0xd07' ]]; then
+ CORE="-mcpu=cortex-a57"
+else
+ CORE=''
+fi fi fi fi fi
+
+selfhost=false
+check=false
+cflags=$CORE
+full=false
+keepbuild=false
+while getopts "sct:hfvk" opt; do
+ case $opt in
+ s)
+ selfhost=true
+ ;;
+ c)
+ check=true
+ ;;
+ t)
+ test=$OPTARG
+ ;;
+ h)
+ cflags="$cflags -mthumb"
+ ;;
+ v)
+ cflags="$cflags -mfpu=vfpv3"
+ ;;
+ f)
+ full=true
+ ;;
+ k)
+ keepbuild=true
+ ;;
+ *)
+ echo "Syntax: $0 [-(s)elfhost | -(c)heck-all | -(t)est <test-suite-name>] | -T(h)umb | -(v)fp3 | -(f)ull-with-RT | -(k)eep build dir"
+ exit 1
+ ;;
+ esac
+done
+
+flush() {
+ if ! $keepbuild; then
+ if [[ -d $1 && $1 != '/' ]]; then
+ rm -rf $1
+ fi
+ fi
+ mkdir -p $1
+ cd $1
+}
+
+build() {
+ CC=gcc
+ CXX=g++
+ if [[ $1 != '' ]]; then
+ CC=$1/bin/clang
+ CXX=$1/bin/clang++
+ fi
+ CC=$CC CXX=$CXX \
+ cmake -G Ninja ../llvm -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_C_FLAGS="$cflags" \
+ -DCMAKE_CXX_FLAGS="$cflags" \
+ -DLLVM_TARGETS_TO_BUILD="ARM;AArch64" \
+ -DLLVM_BUILD_TESTS=True \
+ -DLLVM_ENABLE_ASSERTIONS=True
+ safe_run ninja
+ if $check; then
+ safe_run ninja check-all
+ fi
+}
+
+ROOT=`pwd`
+
+if $full; then
+ ln -sf `pwd`/compiler-rt `pwd`/llvm/projects/compiler-rt
+else
+ rm -f `pwd`/llvm/projects/compiler-rt
+fi
+
+# Native build
+flush $ROOT/build
+build
+
+if $selfhost; then
+ # Self-hosting build
+ flush $ROOT/build2
+ build $ROOT/build
+fi
+
+if [[ $test != '' ]]; then
+ cd $ROOT
+ safe_run ./test.sh $test
+fi
+
+# SVN info
+cd $ROOT
+svn info llvm
diff --git a/bisect/sequential.sh b/bisect/sequential.sh
new file mode 100755
index 0000000..bd1d639
--- /dev/null
+++ b/bisect/sequential.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+# This script is the main driver of the bisection. Please read the
+# README.txt in this directory for more info.
+
+syntax="Syntax: $0 [-(s)tep N | -(o)pts '-hvsc -t TEST'] <first commit> <last commit>"
+
+step=1
+while getopts "s:o:" opt; do
+ case $opt in
+ s)
+ step=$OPTARG
+ ;;
+ o)
+ run_opts="$run_opts $OPTARG"
+ ;;
+ *)
+ echo -e $syntax
+ exit 1
+ ;;
+ esac
+done
+shift $(($OPTIND-1))
+
+if [[ $1 = '' || $2 = '' ]]; then
+ echo -e $syntax
+ exit 1
+fi
+
+from=$1
+to=$(($2+1))
+cur=$from
+
+while [[ $cur < $to ]]; do
+ echo "Testing $cur..."
+ ./checkout.sh $cur 2>&1 > $cur.log
+ ./run.sh $run_opts 2>&1 >> $cur.log
+ if [[ $? != 0 ]]; then
+ echo "BAD"
+ exit 1
+ else
+ echo "GOOD"
+ fi
+ cur=$(($cur+$step))
+done
diff --git a/bisect/test.sh b/bisect/test.sh
new file mode 100755
index 0000000..5d7eb13
--- /dev/null
+++ b/bisect/test.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+# This script calls the run.sh in the test-suite (see the test-suite dir)
+# and bails if the tests fail, giving enough to run.sh to mark that commit
+# as BAD and bail.
+
+if [[ $1 = '' ]]; then
+ echo "Syntax: $0 <full-test-name>"
+ exit 1
+fi
+testname=$1
+if [ ! -d test ]; then
+ echo "Missing test directory"
+ exit 1
+fi
+if [ ! -f test/run.sh ]; then
+ echo "Missing test script"
+ exit 1
+fi
+
+# Check which build was built last
+build_dir=build
+if [ build2/bin/clang -nt build/bin/clang ]; then
+ build_dir=build2
+fi
+
+# Update install dir
+rm -f install
+ln -s $build_dir install
+
+# Run the test
+cd test
+./run.sh -t $testname
+result=$?
+cd ..
+
+if [[ $result != 0 ]]; then
+ exit 1
+fi
diff --git a/bisect/unit_test.sh b/bisect/unit_test.sh
new file mode 100755
index 0000000..f842e7e
--- /dev/null
+++ b/bisect/unit_test.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+
+rev_file=.unit.rev
+if [ "$1" = "" ]; then
+ echo "For bisect.pl testing."
+ echo
+ echo "Usage:"
+ echo "./bisect.pl -c "unit_test -c" \\"
+ echo " -b "unit_test -b BAD_COMPILATION_REV" \\"
+ echo " -t "unit_test -t BAD_TEST_REV" \\"
+ echo " start_rev stop_rev"
+ echo
+ echo "So that you can make sure that BAD_COMPILATION_REV"
+ echo "does't break the bisect looking for BAD_TEST_REV."
+ rm -f $rev_file
+ exit 0
+fi
+
+# Replaces checkout.sh REV
+# Dummy print revision to temp file
+if [ "$1" = "-c" ]; then
+ echo $2 > $rev_file
+ exit 0
+fi
+
+# From here on, we assume the temp file exists
+if [ ! -f $rev_file ]; then
+ echo "Use unit_checkout.sh REV first"
+ exit 1
+fi
+REV=`cat $rev_file`
+echo "Testing revision $REV..."
+
+# Replaces run.sh
+# Dummy check for temp rev, compares to fake broken arg
+if [ "$1" = "-b" ]; then
+ if [ "$2" = "$REV" ]; then
+ echo "Bad compilation"
+ exit 1
+ else
+ echo "Good compilation"
+ exit 0
+ fi
+fi
+
+# Replaces test.sh
+# Dummy check for temp rev, compares to fake broken arg
+if [ "$1" = "-t" ]; then
+ if [ $REV -ge $2 ]; then
+ echo "Bad test"
+ exit 1
+ else
+ echo "Good test"
+ exit 0
+ fi
+fi