Check for sparse before starting the regression tests.
[mirror/scst/.git] / scripts / run-regression-tests
1 #!/bin/bash
2
3 ############################################################################
4 #
5 # Script for running those SCST regression tests that can be run automatically.
6 #
7 # Copyright (C) 2008 Bart Van Assche <bart.vanassche@gmail.com>
8 #
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation, version 2
12 # of the License.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 ############################################################################
20
21 ############################################################################
22 # This script performs the following actions:
23 # - Creates a temporary directory for storing the output of the regression
24 #   tests. No existing files are modified by this script.
25 # - Verifies whether the top-level *.patch files apply cleanly to the SCST
26 #   tree.
27 # - Duplicates the entire source tree to the temporary directory and
28 #   compiles the SCST source code.
29 # - Duplicates the entire source tree to the temporary directory, applies
30 #   the full-perf patches and again compiles the SCST source code.
31 # - Checks whether the specified kernel version is present
32 #   in the directory specified through option -c.
33 # - If the source code of the specified kernel version is not present,
34 #   download it.
35 # - Convert the SCST source code into a kernel patch.
36 # - Extract the kernel sources.
37 # - Run checkpatch on the SCST kernel patch.
38 # - Apply the SCST kernel patch to the kernel tree.
39 # - Run 'make allmodconfig'.
40 # - Run the sparse source code checker on the SCST directory.
41 # - Run 'make headers_check'.
42 # - Compile the kernel tree.
43 # - Run 'make checkstack'.
44 # - Run 'make namespacecheck'.
45 # - Run 'make htmldocs'.
46 #
47 # Note: the results of the individual steps are not verified by this script
48 # -- the output generated by the individual steps has to be verified by
49 # reviewing the output files written into the temporary directory.
50 ############################################################################
51
52
53 ########################
54 # Function definitions #
55 ########################
56
57 function usage {
58   echo "Usage: $0 [-c] [-d] [-f] [-h] [-j] <kver1> <kver2> ..."
59   echo "        -c - cache directory for Linux kernel tarballs."
60   echo "        -d - directory for temporary regression test files."
61   echo "        -f - full check -- do not only compile SCST but the whole" \
62        "kernel tree."
63   echo "        -h - display this help information."
64   echo "        -j - number of jobs that 'make' should run simultaneously."
65   echo "        <kver1> <kver2> ... - kernel versions to test."
66 }
67
68 # First three components of the kernel version number.
69 function kernel_version {
70   echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/p'
71 }
72
73 # Last component of the kernel version, or the empty string if $1 has only
74 # three components.
75 function patchlevel {
76   echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\)[.-]\(.*\)$/\2/p'
77 }
78
79 # Create a linux-$1 tree in the current directory, where $1 is a kernel
80 # version number with either three or four components.
81 function extract_kernel_tree {
82   local kver="$(kernel_version $1)"
83   local plevel="$(patchlevel $1)"
84   local tmpdir=kernel-tree-tmp-$$
85
86   rm -rf "linux-$1" "${tmpdir}"
87   mkdir "${tmpdir}" || return $?
88   (
89     cd "${tmpdir}" || return $?
90     tar xjf "${kernel_sources}/linux-${kver}.tar.bz2" || return $?
91     cd "linux-${kver}" || return $?
92     if [ "${plevel}" != "" ]; then
93       bzip2 -cd "${kernel_sources}/patch-$1.bz2" \
94         | patch -p1 -f -s || return $?
95     fi
96     cd ..
97     mv "linux-${kver}" "../linux-$1" || return $?
98     if [ "${kver}" = "2.6.29" ]; then
99       cd "../linux-$1" || return $?
100       patch -f -s -p1 <<'EOF'
101 Make sure that branch profiling does not trigger sparse warnings.
102 See also http://bugzilla.kernel.org/show_bug.cgi?id=12925
103 --- orig/linux-2.6.29/include/linux/compiler.h  2009-03-23 19:12:14.000000000 -0400
104 +++ linux-2.6.29/include/linux/compiler.h       2009-03-24 08:46:46.000000000 -0400
105 @@ -75,7 +75,8 @@ struct ftrace_branch_data {
106   * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code
107   * to disable branch tracing on a per file basis.
108   */
109 -#if defined(CONFIG_TRACE_BRANCH_PROFILING) && !defined(DISABLE_BRANCH_PROFILING)
110 +#if defined(CONFIG_TRACE_BRANCH_PROFILING) \
111 +    && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__)
112  void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
113  
114  #define likely_notrace(x)      __builtin_expect(!!(x), 1)
115 EOF
116     fi
117   )
118   rmdir "${tmpdir}"
119 }
120
121 # Test whether the *.patch files in the SCST top-level directory apply cleanly
122 # to the SCST tree. Does not modify any files nor produce any output files.
123 function test_scst_tree_patches {
124   local rc=0
125   echo "Testing whether the SCST patches apply cleanly to the SCST tree ..."
126   for p in *.patch srpt/patches/scst_increase_max_tgt_cmds.patch
127   do
128     if ! patch -p0 -f --dry-run -s <$p &>/dev/null; then
129       echo "ERROR: patch $p does not apply cleanly."
130       rc=1
131     fi
132   done
133   if [ "${rc}" = 0 ]; then
134     echo "OK"
135   fi
136 }
137
138 # Copy the entire SCST source code tree from "$1" into the current directory.
139 # Only copy those files which are administered by Subversion.
140 function duplicate_scst_source_tree {
141   if [ -e "$1/AskingQuestions" ]; then
142     ( cd "$1" && svn status -v | cut -c41- \
143       | while read f; do [ ! -d "$f" ] && echo "$f"; done ) \
144     | tar -C "$1" --files-from=- -c -f - | tar -x -f -
145   else
146     return 1
147   fi
148 }
149
150 # Compile the unpatched SCST source code.
151 function compile_scst_unpatched {
152   local scst="$PWD"
153   local outputfile="${outputdir}/compilation-output-unpatched.txt"
154   local workingdirectory="${outputdir}/scst-unpatched"
155
156   echo "Testing whether the SCST tree compiles fine ..."
157   (
158     if mkdir -p "${workingdirectory}"  \
159        && cd "${workingdirectory}"     \
160        && duplicate_scst_source_tree "${scst}"  \
161        && (make -s clean \
162            && make -s scst iscsi-scst  \
163            && if "${scst_local}" = "true" ; then make -C scst_local clean; fi \
164            && if "${scst_local}" = "true" ; then make -C scst_local -s   ; fi \
165            && if "${mpt_scst}"   = "true" ; then make -C mpt clean; fi \
166            && if "${mpt_scst}"   = "true" ; then make -C mpt -s   ; fi \
167            && make -C srpt -s clean    \
168            && make -C srpt -s )    \
169            &> "${outputfile}"
170     then
171       echo "OK"
172     else
173       echo "FAILED"
174     fi
175   )
176 }
177
178 # Test out-of-tree compilation agains the kernel header files in
179 # /lib/modules/$(uname -r)/build.
180 function compile_scst_patched {
181   local scst="$PWD"
182   local outputfile="${outputdir}/compilation-output-patched.txt"
183   local workingdirectory="${outputdir}/scst-patched"
184
185   echo "Testing whether the full-perf SCST tree compiles fine ..."
186   (
187     if mkdir -p "${workingdirectory}"  \
188        && cd "${workingdirectory}"     \
189        && duplicate_scst_source_tree "${scst}"  \
190        && patch -p0 -f -s <"${scst}/iscsi-full_perf.patch"    \
191        && patch -p0 -f -s <"${scst}/qla2x00t-full_perf.patch" \
192        && patch -p0 -f -s <"${scst}/scst-full_perf.patch"     \
193        && (make -s clean               \
194            && make -s scst iscsi-scst  \
195            && if "${scst_local}" = "true" ; then make -C scst_local clean; fi \
196            && if "${scst_local}" = "true" ; then make -C scst_local -s   ; fi \
197            && if "${mpt_scst}"   = "true" ; then make -C mpt clean; fi \
198            && if "${mpt_scst}"   = "true" ; then make -C mpt -s   ; fi \
199            && make -C srpt -s clean    \
200            && make -C srpt -s )    \
201           &> "${outputfile}"
202     then
203       echo "OK"
204     else
205       echo "FAILED"
206     fi
207   )
208 }
209
210 # Download the file from URL $1 and save it in the current directory.
211 function download_file {
212   if [ ! -e "$(basename "$1")" ]; then
213     echo "Downloading $1 ..."
214     if ! wget -q -nc "$1"; then
215       echo "Downloading $1 failed."
216       return 1
217     fi
218   fi
219 }
220
221 # Make sure the kernel tarball and patch file are present in directory
222 # ${kernel_sources}. Download any missing files from ${kernel_mirror}.
223 function download_kernel {
224   local kver="$(kernel_version $1)"
225   local plevel="$(patchlevel $1)"
226
227   mkdir -p "${kernel_sources}" || return $?
228   test -w "${kernel_sources}" || return $?
229   (
230     cd "${kernel_sources}" || return $?
231     download_file "${kernel_mirror}/linux-$(kernel_version $1).tar.bz2" \
232       || return $?
233     if [ "${plevel}" != "" ]; then
234       download_file "${kernel_mirror}/patch-$1.bz2" || return $?
235     fi
236   )
237 }
238
239 # Generate a kernel patch from the SCST source tree for kernel version $1.
240 function generate_kernel_patch {
241   local scst_dir="${PWD}"
242   local kver="$(kernel_version $1)"
243   local patchfile="${outputdir}/scst-$1-kernel.patch"
244
245   SIGNED_OFF_BY="..." \
246   scripts/generate-kernel-patch \
247     $([ "${scst_local}" = "true" ] && echo -- "-l") \
248     $([ "${mpt_scst}"   = "true" ] && echo -- "-m") \
249     $([ "${qla2x00t}"   = "true" ] && echo -- "-q") \
250     ${kver} > "${patchfile}"
251 }
252
253 # Generate a kernel patch through scripts/generate-kernel-patch and test
254 # whether it applies cleanly to kernel version $1. Leaves a vanilla kernel
255 # in directory "${outputdir}/linux-$1" at exit.
256 function test_if_patch_applies_cleanly {
257   local kver="$(kernel_version $1)"
258   local plevel="$(patchlevel $1)"
259   local outputfile="${outputdir}/kernel-$1-patch-output.txt"
260   local patchfile="${outputdir}/scst-$1-kernel.patch"
261   local rc=0
262
263   echo "Testing whether the generated kernel patch applies cleanly ..."
264   ( cd "${outputdir}" && extract_kernel_tree "$1" )
265   ( cd "${outputdir}/linux-$1" \
266     && patch -p1 --dry-run -f < "${patchfile}" &> "${outputfile}" )
267   if [ $? != 0 ]; then
268     echo "FAILED"
269     rc=1
270   fi
271   return $rc
272 }
273
274 # Run checkpatch on the generated kernel patch. Assumes that there is a
275 # vanilla kernel tree present in directory "${outputdir}/linux-$1", and leaves
276 # this kernel tree clean.
277 function run_checkpatch  {
278   local kver="$(kernel_version $1)"
279   local plevel="$(patchlevel $1)"
280   local outputfile="${outputdir}/checkpatch-$1-output.txt"
281   local patchfile="${outputdir}/scst-$1-kernel.patch"
282
283   if [ -e "${outputdir}/linux-$1/scripts/checkpatch.pl" ]
284   then
285     echo "Running checkpatch on the SCST kernel patch ..."
286     ( cd "${outputdir}/linux-$1" \
287       && scripts/checkpatch.pl - < "${patchfile}" &> "${outputfile}")
288     local errors=$(grep -c '^ERROR' "${outputfile}")
289     local warnings=$(grep -c '^WARNING' "${outputfile}")
290     echo "${errors} errors / ${warnings} warnings."
291     grep -E '^WARNING|^ERROR' "${outputfile}" | sort | uniq -c
292   else
293     echo "Skipping checkpatch step for kernel $1."
294   fi
295   return 0
296 }
297
298 function patch_and_configure_kernel {
299   local patchfile="${outputdir}/scst-$1-kernel.patch"
300
301   echo "Patching and configuring kernel ..."
302   (
303     cd "${outputdir}/linux-$1"                                     \
304     && patch -p1 -f -s <"${patchfile}"                             \
305                        >"${outputdir}/patch-command-output-$1.txt" \
306     && make -s allmodconfig &>/dev/null
307   )
308 }
309
310 # Patches and compiles a kernel tree. Assumes that there is a vanilla kernel
311 # tree present in directory "${outputdir}/linux-$1".
312 function compile_patched_kernel {
313   local kver="$(kernel_version $1)"
314   local plevel="$(patchlevel $1)"
315   local outputfile="${outputdir}/kernel-$1-compilation-output.txt"
316
317   echo "Compiling kernel $1 ..."
318   (
319     (
320       cd "${outputdir}/linux-$1" \
321       && LC_ALL=C make -s -k bzImage modules
322     )
323   ) &> "${outputfile}"
324   echo "See also ${outputfile}."
325   return 0
326 }
327
328 # Run the source code verification tool 'sparse' on the SCST code. Assumes that
329 # there is a patched kernel tree present in directory "${outputdir}/linux-$1".
330 # For more information about endianness annotations, see also
331 # http://lwn.net/Articles/205624/.
332 function run_sparse {
333   local kver="$(kernel_version $1)"
334   local plevel="$(patchlevel $1)"
335   local outputfile="${outputdir}/sparse-$1-output.txt"
336
337   echo "Running sparse on the patched kernel ..."
338   (
339     cd "${outputdir}/linux-$1" \
340     && make -s prepare \
341     && make -s scripts \
342     && LC_ALL=C make -k C=2 M=drivers/scst # CF=-D__CHECK_ENDIAN__
343   ) &> "${outputfile}"
344   local errors=$(grep -c ' error:' "${outputfile}")
345   local warnings=$(grep -c ' warning:' "${outputfile}")
346   echo "${errors} errors / ${warnings} warnings."
347   cat "${outputfile}" \
348     | grep -E 'warning:|error:' \
349     | sed -e 's/^[^ ]*:[^ ]*:[^ ]*: //' \
350           -e "s/context imbalance in '[^']*':/context imbalance in <function>:/g" \
351           -e "s/context problem in '[^']*': '[^']*'/context problem in <function>: <function>/g" \
352           -e "s/function '[^']*'/function/g" \
353           -e "s/symbol '[^']*'/symbol/g" \
354     | sort \
355     | uniq -c
356   return 0
357 }
358
359 function run_checkstack {
360   local kver="$(kernel_version $1)"
361   local plevel="$(patchlevel $1)"
362   local outputfile="${outputdir}/checkstack-$1-output.txt"
363
364   echo "Running checkstack on the patched $1 kernel ..."
365   (
366     cd "${outputdir}/linux-$1" \
367     && make -s prepare \
368     && make -s scripts \
369     && LC_ALL=C make -k checkstack
370   ) &> "${outputfile}"
371   echo "See also ${outputfile}."
372   return 0
373 }
374
375 function run_namespacecheck {
376   local kver="$(kernel_version $1)"
377   local plevel="$(patchlevel $1)"
378   local outputfile="${outputdir}/namespacecheck-$1-output.txt"
379
380   echo "Running namespacecheck on the patched $1 kernel ..."
381   (
382     cd "${outputdir}/linux-$1" \
383     && make -s prepare \
384     && make -s scripts \
385     && LC_ALL=C make -k namespacecheck
386   ) &> "${outputfile}"
387   echo "See also ${outputfile}."
388   return 0
389 }
390
391 function run_headers_check {
392   local kver="$(kernel_version $1)"
393   local plevel="$(patchlevel $1)"
394   local outputfile="${outputdir}/headers_check-$1-output.txt"
395
396   echo "Running headers check on the patched $1 kernel ..."
397   (
398     cd "${outputdir}/linux-$1" \
399     && make -s prepare \
400     && make -s scripts \
401     && LC_ALL=C make -k headers_check
402   ) &> "${outputfile}"
403   local errors=$(grep -c '^[^ ]' "${outputfile}")
404   echo "${errors} errors."
405   grep '^[^ ]' "${outputfile}" | sed 's/.*: //' | sort | uniq -c
406   return 0
407 }
408
409 function run_make_htmldocs {
410   local kver="$(kernel_version $1)"
411   local plevel="$(patchlevel $1)"
412   local outputfile="${outputdir}/htmldocs-$1-output.txt"
413
414   echo "Generating HTML documentation for the patched $1 kernel ..."
415   (
416     cd "${outputdir}/linux-$1" \
417     && make -s prepare \
418     && make -s scripts \
419     && LC_ALL=C make -k htmldocs
420   ) &> "${outputfile}"
421   echo "See also ${outputfile}."
422   return 0
423 }
424
425
426 #########################
427 # Argument verification #
428 #########################
429
430 if [ ! -e scst -o ! -e iscsi-scst -o ! -e srpt ]; then
431   echo "Please run this script from inside the SCST subversion source tree."
432   exit 1
433 fi
434
435 full_check="false"
436 # Where to store persistenly downloaded kernel tarballs and kernel patches.
437 kernel_sources="$HOME/software/downloads"
438 # URL for downloading kernel tarballs and kernel patches.
439 kernel_mirror="ftp://ftp.eu.kernel.org/pub/linux/kernel/v2.6"
440 kernel_versions=""
441 # Directory in which the regression test output files will be stored. Must be
442 # an absolute path.
443 outputdir="${PWD}/regression-test-output-$(date +%Y-%m-%d_%Hh%Mm%Ss)"
444 # Driver configuration.
445 mpt_scst="false"
446 qla2x00t="false"
447 scst_local="true"
448
449 set -- $(/usr/bin/getopt "c:d:fj:h" "$@")
450 while [ "$1" != "${1#-}" ]
451 do
452   case "$1" in
453     '-c') kernel_sources="$2"; shift; shift;;
454     '-d') outputdir="$2"; shift; shift;;
455     '-f') full_check="true"; shift;;
456     '-h') usage; exit 1;;
457     '-j') export MAKEFLAGS="-j$2"; shift; shift;;
458     '--') shift;;
459     *)    usage; exit 1;;
460   esac
461 done
462
463 kernel_versions="$*"
464
465 # RHEL 4.x / CentOS 4.x has a kernel based on version 2.6.9.
466 # RHEL 5.x / CentOS 5.x has a kernel based on version 2.6.18.
467 # Ubuntu 8.04 (Hardy Heron) has a kernel based on version 2.6.24.
468 # Ubuntu 8.10 (Intrepid Ibex) has a kernel based on version 2.6.27.
469 # openSUSE 11.0 has a kernel based on version 2.6.25.
470 # openSUSE 11.1 has a kernel based on version 2.6.27.
471 #kernel_versions="2.6.23.17 2.6.24.7 2.6.25.20 2.6.26.8 2.6.27.21 2.6.28.9 2.6.29.1"
472
473 if [ "${kernel_versions}" = "" ]; then
474   usage
475   exit 1
476 fi
477
478
479 ####################
480 # Regression tests #
481 ####################
482
483 if [ "$(type -p sparse)" = "" ]; then
484   echo "Error: sparse has not yet been installed."
485 fi
486
487 if ! mkdir -p "${outputdir}"; then
488   if [ -e "${outputdir}" ]; then
489     echo "Error: directory ${outputdir} already exists."
490   else
491     echo "Error: could not create directory ${outputdir}."
492   fi
493 fi
494
495 test_scst_tree_patches || exit $?
496 compile_scst_unpatched || exit $?
497 compile_scst_patched || exit $?
498
499 for k in ${kernel_versions}
500 do
501   echo "===================="
502   printf "= kernel %-9s =\n" "${k}"
503   echo "===================="
504
505   download_kernel $k || continue
506   generate_kernel_patch $k || continue
507   test_if_patch_applies_cleanly $k || continue
508   run_checkpatch $k
509   patch_and_configure_kernel $k
510   run_sparse $k
511   run_headers_check $k
512   [ "${full_check}" = "true" ] || continue
513   compile_patched_kernel $k
514   run_checkstack $k
515   run_namespacecheck $k
516   run_make_htmldocs $k
517 done