xref: /OK3568_Linux_fs/yocto/poky/scripts/contrib/test_build_time.sh (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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