1*4882a593Smuzhiyun#!/bin/bash 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# Build performance regression test script 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# Copyright 2011 Intel Corporation 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-or-later 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# DESCRIPTION 10*4882a593Smuzhiyun# This script is intended to be used in conjunction with "git bisect run" 11*4882a593Smuzhiyun# in order to find regressions in build time, however it can also be used 12*4882a593Smuzhiyun# independently. It cleans out the build output directories, runs a 13*4882a593Smuzhiyun# specified worker script (an example is test_build_time_worker.sh) under 14*4882a593Smuzhiyun# TIME(1), logs the results to TEST_LOGDIR (default /tmp) and returns a 15*4882a593Smuzhiyun# value telling "git bisect run" whether the build time is good (under 16*4882a593Smuzhiyun# the specified threshold) or bad (over it). There is also a tolerance 17*4882a593Smuzhiyun# option but it is not particularly useful as it only subtracts the 18*4882a593Smuzhiyun# tolerance from the given threshold and uses it as the actual threshold. 19*4882a593Smuzhiyun# 20*4882a593Smuzhiyun# It is also capable of taking a file listing git revision hashes to be 21*4882a593Smuzhiyun# test-applied to the repository in order to get past build failures that 22*4882a593Smuzhiyun# would otherwise cause certain revisions to have to be skipped; if a 23*4882a593Smuzhiyun# revision does not apply cleanly then the script assumes it does not 24*4882a593Smuzhiyun# need to be applied and ignores it. 25*4882a593Smuzhiyun# 26*4882a593Smuzhiyun# Please see the help output (syntax below) for some important setup 27*4882a593Smuzhiyun# instructions. 28*4882a593Smuzhiyun# 29*4882a593Smuzhiyun# AUTHORS 30*4882a593Smuzhiyun# Paul Eggleton <paul.eggleton@linux.intel.com> 31*4882a593Smuzhiyun 32*4882a593Smuzhiyun 33*4882a593Smuzhiyunsyntax() { 34*4882a593Smuzhiyun echo "syntax: $0 <script> <time> <tolerance> [patchrevlist]" 35*4882a593Smuzhiyun echo "" 36*4882a593Smuzhiyun echo " script - worker script file (if in current dir, prefix with ./)" 37*4882a593Smuzhiyun echo " time - time threshold (in seconds, suffix m for minutes)" 38*4882a593Smuzhiyun echo " tolerance - tolerance (in seconds, suffix m for minutes or % for" 39*4882a593Smuzhiyun echo " percentage, can be 0)" 40*4882a593Smuzhiyun echo " patchrevlist - optional file listing revisions to apply as patches on top" 41*4882a593Smuzhiyun echo "" 42*4882a593Smuzhiyun echo "You must set TEST_BUILDDIR to point to a previously created build directory," 43*4882a593Smuzhiyun echo "however please note that this script will wipe out the TMPDIR defined in" 44*4882a593Smuzhiyun echo "TEST_BUILDDIR/conf/local.conf as part of its initial setup (as well as your" 45*4882a593Smuzhiyun echo "~/.ccache)" 46*4882a593Smuzhiyun echo "" 47*4882a593Smuzhiyun echo "To get rid of the sudo prompt, please add the following line to /etc/sudoers" 48*4882a593Smuzhiyun echo "(use 'visudo' to edit this; also it is assumed that the user you are running" 49*4882a593Smuzhiyun echo "as is a member of the 'wheel' group):" 50*4882a593Smuzhiyun echo "" 51*4882a593Smuzhiyun echo "%wheel ALL=(ALL) NOPASSWD: /sbin/sysctl -w vm.drop_caches=[1-3]" 52*4882a593Smuzhiyun echo "" 53*4882a593Smuzhiyun echo "Note: it is recommended that you disable crond and any other process that" 54*4882a593Smuzhiyun echo "may cause significant CPU or I/O usage during build performance tests." 55*4882a593Smuzhiyun} 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun# Note - we exit with 250 here because that will tell git bisect run that 58*4882a593Smuzhiyun# something bad happened and stop 59*4882a593Smuzhiyunif [ "$1" = "" ] ; then 60*4882a593Smuzhiyun syntax 61*4882a593Smuzhiyun exit 250 62*4882a593Smuzhiyunfi 63*4882a593Smuzhiyun 64*4882a593Smuzhiyunif [ "$2" = "" ] ; then 65*4882a593Smuzhiyun syntax 66*4882a593Smuzhiyun exit 250 67*4882a593Smuzhiyunfi 68*4882a593Smuzhiyun 69*4882a593Smuzhiyunif [ "$3" = "" ] ; then 70*4882a593Smuzhiyun syntax 71*4882a593Smuzhiyun exit 250 72*4882a593Smuzhiyunfi 73*4882a593Smuzhiyun 74*4882a593Smuzhiyunif ! [[ "$2" =~ ^[0-9][0-9m.]*$ ]] ; then 75*4882a593Smuzhiyun echo "'$2' is not a valid number for threshold" 76*4882a593Smuzhiyun exit 250 77*4882a593Smuzhiyunfi 78*4882a593Smuzhiyun 79*4882a593Smuzhiyunif ! [[ "$3" =~ ^[0-9][0-9m.%]*$ ]] ; then 80*4882a593Smuzhiyun echo "'$3' is not a valid number for tolerance" 81*4882a593Smuzhiyun exit 250 82*4882a593Smuzhiyunfi 83*4882a593Smuzhiyun 84*4882a593Smuzhiyunif [ "$TEST_BUILDDIR" = "" ] ; then 85*4882a593Smuzhiyun echo "Please set TEST_BUILDDIR to a previously created build directory" 86*4882a593Smuzhiyun exit 250 87*4882a593Smuzhiyunfi 88*4882a593Smuzhiyun 89*4882a593Smuzhiyunif [ ! -d "$TEST_BUILDDIR" ] ; then 90*4882a593Smuzhiyun echo "TEST_BUILDDIR $TEST_BUILDDIR not found" 91*4882a593Smuzhiyun exit 250 92*4882a593Smuzhiyunfi 93*4882a593Smuzhiyun 94*4882a593Smuzhiyungit diff --quiet 95*4882a593Smuzhiyunif [ $? != 0 ] ; then 96*4882a593Smuzhiyun echo "Working tree is dirty, cannot proceed" 97*4882a593Smuzhiyun exit 251 98*4882a593Smuzhiyunfi 99*4882a593Smuzhiyun 100*4882a593Smuzhiyunif [ "BB_ENV_PASSTHROUGH_ADDITIONS" != "" ] ; then 101*4882a593Smuzhiyun echo "WARNING: you are running after sourcing the build environment script, this is not recommended" 102*4882a593Smuzhiyunfi 103*4882a593Smuzhiyun 104*4882a593Smuzhiyunrunscript=$1 105*4882a593Smuzhiyuntimethreshold=$2 106*4882a593Smuzhiyuntolerance=$3 107*4882a593Smuzhiyun 108*4882a593Smuzhiyunif [ "$4" != "" ] ; then 109*4882a593Smuzhiyun patchrevlist=`cat $4` 110*4882a593Smuzhiyunelse 111*4882a593Smuzhiyun patchrevlist="" 112*4882a593Smuzhiyunfi 113*4882a593Smuzhiyun 114*4882a593Smuzhiyunif [[ timethreshold == *m* ]] ; then 115*4882a593Smuzhiyun timethreshold=`echo $timethreshold | sed s/m/*60/ | bc` 116*4882a593Smuzhiyunfi 117*4882a593Smuzhiyun 118*4882a593Smuzhiyunif [[ $tolerance == *m* ]] ; then 119*4882a593Smuzhiyun tolerance=`echo $tolerance | sed s/m/*60/ | bc` 120*4882a593Smuzhiyunelif [[ $tolerance == *%* ]] ; then 121*4882a593Smuzhiyun tolerance=`echo $tolerance | sed s/%//` 122*4882a593Smuzhiyun tolerance=`echo "scale = 2; (($tolerance * $timethreshold) / 100)" | bc` 123*4882a593Smuzhiyunfi 124*4882a593Smuzhiyun 125*4882a593Smuzhiyuntmpdir=`grep "^TMPDIR" $TEST_BUILDDIR/conf/local.conf | sed -e 's/TMPDIR[ \t]*=[ \t\?]*"//' -e 's/"//'` 126*4882a593Smuzhiyunif [ "x$tmpdir" = "x" ]; then 127*4882a593Smuzhiyun echo "Unable to determine TMPDIR from $TEST_BUILDDIR/conf/local.conf, bailing out" 128*4882a593Smuzhiyun exit 250 129*4882a593Smuzhiyunfi 130*4882a593Smuzhiyunsstatedir=`grep "^SSTATE_DIR" $TEST_BUILDDIR/conf/local.conf | sed -e 's/SSTATE_DIR[ \t\?]*=[ \t]*"//' -e 's/"//'` 131*4882a593Smuzhiyunif [ "x$sstatedir" = "x" ]; then 132*4882a593Smuzhiyun echo "Unable to determine SSTATE_DIR from $TEST_BUILDDIR/conf/local.conf, bailing out" 133*4882a593Smuzhiyun exit 250 134*4882a593Smuzhiyunfi 135*4882a593Smuzhiyun 136*4882a593Smuzhiyunif [ `expr length $tmpdir` -lt 4 ] ; then 137*4882a593Smuzhiyun echo "TMPDIR $tmpdir is less than 4 characters, bailing out" 138*4882a593Smuzhiyun exit 250 139*4882a593Smuzhiyunfi 140*4882a593Smuzhiyun 141*4882a593Smuzhiyunif [ `expr length $sstatedir` -lt 4 ] ; then 142*4882a593Smuzhiyun echo "SSTATE_DIR $sstatedir is less than 4 characters, bailing out" 143*4882a593Smuzhiyun exit 250 144*4882a593Smuzhiyunfi 145*4882a593Smuzhiyun 146*4882a593Smuzhiyunecho -n "About to wipe out TMPDIR $tmpdir, press Ctrl+C to break out... " 147*4882a593Smuzhiyunfor i in 9 8 7 6 5 4 3 2 1 148*4882a593Smuzhiyundo 149*4882a593Smuzhiyun echo -ne "\x08$i" 150*4882a593Smuzhiyun sleep 1 151*4882a593Smuzhiyundone 152*4882a593Smuzhiyunecho 153*4882a593Smuzhiyun 154*4882a593Smuzhiyunpushd . > /dev/null 155*4882a593Smuzhiyun 156*4882a593Smuzhiyunrm -f pseudodone 157*4882a593Smuzhiyunecho "Removing TMPDIR $tmpdir..." 158*4882a593Smuzhiyunrm -rf $tmpdir 159*4882a593Smuzhiyunecho "Removing TMPDIR $tmpdir-*libc..." 160*4882a593Smuzhiyunrm -rf $tmpdir-*libc 161*4882a593Smuzhiyunecho "Removing SSTATE_DIR $sstatedir..." 162*4882a593Smuzhiyunrm -rf $sstatedir 163*4882a593Smuzhiyunecho "Removing ~/.ccache..." 164*4882a593Smuzhiyunrm -rf ~/.ccache 165*4882a593Smuzhiyun 166*4882a593Smuzhiyunecho "Syncing..." 167*4882a593Smuzhiyunsync 168*4882a593Smuzhiyunsync 169*4882a593Smuzhiyunecho "Dropping VM cache..." 170*4882a593Smuzhiyun#echo 3 > /proc/sys/vm/drop_caches 171*4882a593Smuzhiyunsudo /sbin/sysctl -w vm.drop_caches=3 > /dev/null 172*4882a593Smuzhiyun 173*4882a593Smuzhiyunif [ "$TEST_LOGDIR" = "" ] ; then 174*4882a593Smuzhiyun logdir="/tmp" 175*4882a593Smuzhiyunelse 176*4882a593Smuzhiyun logdir="$TEST_LOGDIR" 177*4882a593Smuzhiyunfi 178*4882a593Smuzhiyunrev=`git rev-parse HEAD` 179*4882a593Smuzhiyunlogfile="$logdir/timelog_$rev.log" 180*4882a593Smuzhiyunecho -n > $logfile 181*4882a593Smuzhiyun 182*4882a593Smuzhiyungitroot=`git rev-parse --show-toplevel` 183*4882a593Smuzhiyuncd $gitroot 184*4882a593Smuzhiyunfor patchrev in $patchrevlist ; do 185*4882a593Smuzhiyun echo "Applying $patchrev" 186*4882a593Smuzhiyun patchfile=`mktemp` 187*4882a593Smuzhiyun git show $patchrev > $patchfile 188*4882a593Smuzhiyun git apply --check $patchfile &> /dev/null 189*4882a593Smuzhiyun if [ $? != 0 ] ; then 190*4882a593Smuzhiyun echo " ... patch does not apply without errors, ignoring" 191*4882a593Smuzhiyun else 192*4882a593Smuzhiyun echo "Applied $patchrev" >> $logfile 193*4882a593Smuzhiyun git apply $patchfile &> /dev/null 194*4882a593Smuzhiyun fi 195*4882a593Smuzhiyun rm $patchfile 196*4882a593Smuzhiyundone 197*4882a593Smuzhiyun 198*4882a593Smuzhiyunsync 199*4882a593Smuzhiyunecho "Quiescing for 5s..." 200*4882a593Smuzhiyunsleep 5 201*4882a593Smuzhiyun 202*4882a593Smuzhiyunecho "Running $runscript at $rev..." 203*4882a593Smuzhiyuntimeoutfile=`mktemp` 204*4882a593Smuzhiyun/usr/bin/time -o $timeoutfile -f "%e\nreal\t%E\nuser\t%Us\nsys\t%Ss\nmaxm\t%Mk" $runscript 2>&1 | tee -a $logfile 205*4882a593Smuzhiyunexitstatus=$PIPESTATUS 206*4882a593Smuzhiyun 207*4882a593Smuzhiyungit reset --hard HEAD > /dev/null 208*4882a593Smuzhiyunpopd > /dev/null 209*4882a593Smuzhiyun 210*4882a593Smuzhiyuntimeresult=`head -n1 $timeoutfile` 211*4882a593Smuzhiyuncat $timeoutfile | tee -a $logfile 212*4882a593Smuzhiyunrm $timeoutfile 213*4882a593Smuzhiyun 214*4882a593Smuzhiyunif [ $exitstatus != 0 ] ; then 215*4882a593Smuzhiyun # Build failed, exit with 125 to tell git bisect run to skip this rev 216*4882a593Smuzhiyun echo "*** Build failed (exit code $exitstatus), skipping..." | tee -a $logfile 217*4882a593Smuzhiyun exit 125 218*4882a593Smuzhiyunfi 219*4882a593Smuzhiyun 220*4882a593Smuzhiyunret=`echo "scale = 2; $timeresult > $timethreshold - $tolerance" | bc` 221*4882a593Smuzhiyunecho "Returning $ret" | tee -a $logfile 222*4882a593Smuzhiyunexit $ret 223*4882a593Smuzhiyun 224