diff options
Diffstat (limited to 'tools/testing/ktest')
| -rwxr-xr-x | tools/testing/ktest/compare-ktest-sample.pl | 4 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/README | 32 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/crosstests.conf | 254 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/include/bisect.conf | 90 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/include/defaults.conf | 157 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/include/min-config.conf | 60 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/include/patchcheck.conf | 111 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/include/tests.conf | 74 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/kvm.conf | 92 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/snowball.conf | 53 | ||||
| -rw-r--r-- | tools/testing/ktest/examples/test.conf | 62 | ||||
| -rwxr-xr-x | tools/testing/ktest/ktest.pl | 2836 | ||||
| -rw-r--r-- | tools/testing/ktest/sample.conf | 695 |
13 files changed, 4166 insertions, 354 deletions
diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl index 9a571e71683..a373a5bfff6 100755 --- a/tools/testing/ktest/compare-ktest-sample.pl +++ b/tools/testing/ktest/compare-ktest-sample.pl @@ -2,7 +2,9 @@ open (IN,"ktest.pl"); while (<IN>) { + # hashes are now used if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ || + /^\s*"?([A-Z].*?)"?\s*=>\s*/ || /set_test_option\("(.*?)"/) { $opt{$1} = 1; } @@ -11,7 +13,7 @@ close IN; open (IN, "sample.conf"); while (<IN>) { - if (/^\s*#?\s*(\S+)\s*=/) { + if (/^\s*#?\s*([A-Z]\S*)\s*=/) { $samp{$1} = 1; } } diff --git a/tools/testing/ktest/examples/README b/tools/testing/ktest/examples/README new file mode 100644 index 00000000000..a12d295a09d --- /dev/null +++ b/tools/testing/ktest/examples/README @@ -0,0 +1,32 @@ +This directory contains example configs to use ktest for various tasks. +The configs still need to be customized for your environment, but it +is broken up by task which makes it easier to understand how to set up +ktest. + +The configs are based off of real working configs but have been modified +and commented to show more generic use cases that are more helpful for +developers. + +crosstests.conf - this config shows an example of testing a git repo against + lots of different architectures. It only does build tests, but makes + it easy to compile test different archs. You can download the arch + cross compilers from: + http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ + +test.conf - A generic example of a config. This is based on an actual config + used to perform real testing. + +kvm.conf - A example of a config that is used to test a virtual guest running + on a host. + +snowball.conf - An example config that was used to demo ktest.pl against + a snowball ARM board. + +include/ - The include directory holds default configs that can be + included into other configs. This is a real use example that shows how + to reuse configs for various machines or set ups. The files here + are included by other config files, where the other config files define + options and variables that will make the included config work for the + given environment. + + diff --git a/tools/testing/ktest/examples/crosstests.conf b/tools/testing/ktest/examples/crosstests.conf new file mode 100644 index 00000000000..a1203148dfa --- /dev/null +++ b/tools/testing/ktest/examples/crosstests.conf @@ -0,0 +1,254 @@ +# +# Example config for cross compiling +# +# In this config, it is expected that the tool chains from: +# +# http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ +# +# running on a x86_64 system have been downloaded and installed into: +# +# /usr/local/ +# +# such that the compiler binaries are something like: +# +# /usr/local/gcc-4.5.2-nolibc/mips-linux/bin/mips-linux-gcc +# +# Some of the archs will use gcc-4.5.1 instead of gcc-4.5.2 +# this config uses variables to differentiate them. +# +# Comments describe some of the options, but full descriptions of +# options are described in the samples.conf file. + +# ${PWD} is defined by ktest.pl to be the directory that the user +# was in when they executed ktest.pl. It may be better to hardcode the +# path name here. THIS_DIR is the variable used through out the config file +# in case you want to change it. + +THIS_DIR := ${PWD} + +# Update the BUILD_DIR option to the location of your git repo you want to test. +BUILD_DIR = ${THIS_DIR}/linux.git + +# The build will go into this directory. It will be created when you run the test. +OUTPUT_DIR = ${THIS_DIR}/cross-compile + +# The build will be compiled with -j8 +BUILD_OPTIONS = -j8 + +# The test will not stop when it hits a failure. +DIE_ON_FAILURE = 0 + +# If you want to have ktest.pl store the failure somewhere, uncomment this option +# and change the directory where ktest should store the failures. +#STORE_FAILURES = ${THIS_DIR}/failures + +# The log file is stored in the OUTPUT_DIR called cross.log +# If you enable this, you need to create the OUTPUT_DIR. It wont be created for you. +LOG_FILE = ${OUTPUT_DIR}/cross.log + +# The log file will be cleared each time you run ktest. +CLEAR_LOG = 1 + +# As some archs do not build with the defconfig, they have been marked +# to be ignored. If you want to test them anyway, change DO_FAILED to 1. +# If a test that has been marked as DO_FAILED passes, then you should change +# that test to be DO_DEFAULT + +DO_FAILED := 0 +DO_DEFAULT := 1 + +# By setting both DO_FAILED and DO_DEFAULT to zero, you can pick a single +# arch that you want to test. (uncomment RUN and chose your arch) +#RUN := m32r + +# At the bottom of the config file exists a bisect test. You can update that +# test and set DO_FAILED and DO_DEFAULT to zero, and uncomment this variable +# to run the bisect on the arch. +#RUN := bisect + +# By default all tests will be running gcc 4.5.2. Some tests are using 4.5.1 +# and they select that in the test. +# Note: GCC_VER is declared as on option and not a variable ('=' instead of ':=') +# This is important. A variable is used only in the config file and if it is set +# it stays that way for the rest of the config file until it is change again. +# Here we want GCC_VER to remain persistent and change for each test, as it is used in +# the MAKE_CMD. By using '=' instead of ':=' we achieve our goal. + +GCC_VER = 4.5.2 +MAKE_CMD = PATH=/usr/local/gcc-${GCC_VER}-nolibc/${CROSS}/bin:$PATH CROSS_COMPILE=${CROSS}- make ARCH=${ARCH} + +# all tests are only doing builds. +TEST_TYPE = build + +# If you want to add configs on top of the defconfig, you can add those configs into +# the add-config file and uncomment this option. This is useful if you want to test +# all cross compiles with PREEMPT set, or TRACING on, etc. +#ADD_CONFIG = ${THIS_DIR}/add-config + +# All tests are using defconfig +BUILD_TYPE = defconfig + +# The test names will have the arch and cross compiler used. This will be shown in +# the results. +TEST_NAME = ${ARCH} ${CROSS} + +# alpha +TEST_START IF ${RUN} == alpha || ${DO_DEFAULT} +# Notice that CROSS and ARCH are also options and not variables (again '=' instead +# of ':='). This is because TEST_NAME and MAKE_CMD wil use them for each test. +# Only options are available during runs. Variables are only present in parsing the +# config file. +CROSS = alpha-linux +ARCH = alpha + +# arm +TEST_START IF ${RUN} == arm || ${DO_DEFAULT} +CROSS = arm-unknown-linux-gnueabi +ARCH = arm + +# black fin +TEST_START IF ${RUN} == bfin || ${DO_DEFAULT} +CROSS = bfin-uclinux +ARCH = blackfin +BUILD_OPTIONS = -j8 vmlinux + +# cris - FAILS? +TEST_START IF ${RUN} == cris || ${RUN} == cris64 || ${DO_FAILED} +CROSS = cris-linux +ARCH = cris + +# cris32 - not right arch? +TEST_START IF ${RUN} == cris || ${RUN} == cris32 || ${DO_FAILED} +CROSS = crisv32-linux +ARCH = cris + +# ia64 +TEST_START IF ${RUN} == ia64 || ${DO_DEFAULT} +CROSS = ia64-linux +ARCH = ia64 + +# frv +TEST_START IF ${RUN} == frv || ${DO_FAILED} +CROSS = frv-linux +ARCH = frv +GCC_VER = 4.5.1 + +# m68k fails with error? +TEST_START IF ${RUN} == m68k || ${DO_DEFAULT} +CROSS = m68k-linux +ARCH = m68k + +# mips64 +TEST_START IF ${RUN} == mips || ${RUN} == mips64 || ${DO_DEFAULT} +CROSS = mips64-linux +ARCH = mips + +# mips32 +TEST_START IF ${RUN} == mips || ${RUN} == mips32 || ${DO_DEFAULT} +CROSS = mips-linux +ARCH = mips + +# m32r +TEST_START IF ${RUN} == m32r || ${DO_FAILED} +CROSS = m32r-linux +ARCH = m32r +GCC_VER = 4.5.1 +BUILD_OPTIONS = -j8 vmlinux + +# parisc64 failed? +TEST_START IF ${RUN} == hppa || ${RUN} == hppa64 || ${DO_FAILED} +CROSS = hppa64-linux +ARCH = parisc + +# parisc +TEST_START IF ${RUN} == hppa || ${RUN} == hppa32 || ${DO_FAILED} +CROSS = hppa-linux +ARCH = parisc + +# ppc +TEST_START IF ${RUN} == ppc || ${RUN} == ppc32 || ${DO_DEFAULT} +CROSS = powerpc-linux +ARCH = powerpc + +# ppc64 +TEST_START IF ${RUN} == ppc || ${RUN} == ppc64 || ${DO_DEFAULT} +CROSS = powerpc64-linux +ARCH = powerpc + +# s390 +TEST_START IF ${RUN} == s390 || ${DO_DEFAULT} +CROSS = s390x-linux +ARCH = s390 + +# sh +TEST_START IF ${RUN} == sh || ${DO_DEFAULT} +CROSS = sh4-linux +ARCH = sh + +# sparc64 +TEST_START IF ${RUN} == sparc || ${RUN} == sparc64 || ${DO_DEFAULT} +CROSS = sparc64-linux +ARCH = sparc64 + +# sparc +TEST_START IF ${RUN} == sparc || ${RUN} == sparc32 || ${DO_DEFAULT} +CROSS = sparc-linux +ARCH = sparc + +# xtensa failed +TEST_START IF ${RUN} == xtensa || ${DO_FAILED} +CROSS = xtensa-linux +ARCH = xtensa + +# UML +TEST_START IF ${RUN} == uml || ${DO_DEFAULT} +MAKE_CMD = make ARCH=um SUBARCH=x86_64 +ARCH = uml +CROSS = + +TEST_START IF ${RUN} == x86 || ${RUN} == i386 || ${DO_DEFAULT} +MAKE_CMD = make ARCH=i386 +ARCH = i386 +CROSS = + +TEST_START IF ${RUN} == x86 || ${RUN} == x86_64 || ${DO_DEFAULT} +MAKE_CMD = make ARCH=x86_64 +ARCH = x86_64 +CROSS = + +################################# + +# This is a bisect if needed. You need to give it a MIN_CONFIG that +# will be the config file it uses. Basically, just copy the created defconfig +# for the arch someplace and point MIN_CONFIG to it. +TEST_START IF ${RUN} == bisect +MIN_CONFIG = ${THIS_DIR}/min-config +CROSS = s390x-linux +ARCH = s390 +TEST_TYPE = bisect +BISECT_TYPE = build +BISECT_GOOD = v3.1 +BISECT_BAD = v3.2 +CHECKOUT = v3.2 + +################################# + +# These defaults are needed to keep ktest.pl from complaining. They are +# ignored because the test does not go pass the build. No install or +# booting of the target images. + +DEFAULTS +MACHINE = crosstest +SSH_USER = root +BUILD_TARGET = cross +TARGET_IMAGE = image +POWER_CYCLE = cycle +CONSOLE = console +LOCALVERSION = version +GRUB_MENU = grub + +REBOOT_ON_ERROR = 0 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 +REBOOT_ON_SUCCESS = 0 + diff --git a/tools/testing/ktest/examples/include/bisect.conf b/tools/testing/ktest/examples/include/bisect.conf new file mode 100644 index 00000000000..009bea65bfb --- /dev/null +++ b/tools/testing/ktest/examples/include/bisect.conf @@ -0,0 +1,90 @@ +# +# This example shows the bisect tests (git bisect and config bisect) +# + + +# The config that includes this file may define a RUN_TEST +# variable that will tell this config what test to run. +# (what to set the TEST option to). +# +DEFAULTS IF NOT DEFINED RUN_TEST +# Requires that hackbench is in the PATH +RUN_TEST := ${SSH} hackbench 50 + + +# Set TEST to 'bisect' to do a normal git bisect. You need +# to modify the options below to make it bisect the exact +# commits you are interested in. +# +TEST_START IF ${TEST} == bisect +TEST_TYPE = bisect +# You must set the commit that was considered good (git bisect good) +BISECT_GOOD = v3.3 +# You must set the commit that was considered bad (git bisect bad) +BISECT_BAD = HEAD +# It's best to specify the branch to checkout before starting the bisect. +CHECKOUT = origin/master +# This can be build, boot, or test. Here we are doing a bisect +# that requires to run a test to know if the bisect was good or bad. +# The test should exit with 0 on good, non-zero for bad. But see +# the BISECT_RET_* options in samples.conf to override this. +BISECT_TYPE = test +TEST = ${RUN_TEST} +# It is usually a good idea to confirm that the GOOD and the BAD +# commits are truly good and bad respectively. Having BISECT_CHECK +# set to 1 will check both that the good commit works and the bad +# commit fails. If you only want to check one or the other, +# set BISECT_CHECK to 'good' or to 'bad'. +BISECT_CHECK = 1 +#BISECT_CHECK = good +#BISECT_CHECK = bad + +# Usually it's a good idea to specify the exact config you +# want to use throughout the entire bisect. Here we placed +# it in the directory we called ktest.pl from and named it +# 'config-bisect'. +MIN_CONFIG = ${THIS_DIR}/config-bisect +# By default, if we are doing a BISECT_TYPE = test run but the +# build or boot fails, ktest.pl will do a 'git bisect skip'. +# Uncomment the below option to make ktest stop testing on such +# an error. +#BISECT_SKIP = 0 +# Now if you had BISECT_SKIP = 0 and the test fails, you can +# examine what happened and then do 'git bisect log > /tmp/replay' +# Set BISECT_REPLAY to /tmp/replay and ktest.pl will run the +# 'git bisect replay /tmp/replay' before continuing the bisect test. +#BISECT_REPLAY = /tmp/replay +# If you used BISECT_REPLAY after the bisect test failed, you may +# not want to continue the bisect on that commit that failed. +# By setting BISECT_START to a new commit. ktest.pl will checkout +# that commit after it has performed the 'git bisect replay' but +# before it continues running the bisect test. +#BISECT_START = 2545eb6198e7e1ec50daa0cfc64a4cdfecf24ec9 + +# Now if you don't trust ktest.pl to make the decisions for you, then +# set BISECT_MANUAL to 1. This will cause ktest.pl not to decide +# if the commit was good or bad. Instead, it will ask you to tell +# it if the current commit was good. In the mean time, you could +# take the result, load it on any machine you want. Run several tests, +# or whatever you feel like. Then, when you are happy, you can tell +# ktest if you think it was good or not and ktest.pl will continue +# the git bisect. You can even change what commit it is currently at. +#BISECT_MANUAL = 1 + + +# One of the unique tests that ktest does is the config bisect. +# Currently (which hopefully will be fixed soon), the bad config +# must be a superset of the good config. This is because it only +# searches for a config that causes the target to fail. If the +# good config is not a subset of the bad config, or if the target +# fails because of a lack of a config, then it will not find +# the config for you. +TEST_START IF ${TEST} == config-bisect +TEST_TYPE = config_bisect +# set to build, boot, test +CONFIG_BISECT_TYPE = boot +# Set the config that is considered bad. +CONFIG_BISECT = ${THIS_DIR}/config-bad +# This config is optional. By default it uses the +# MIN_CONFIG as the good config. +CONFIG_BISECT_GOOD = ${THIS_DIR}/config-good diff --git a/tools/testing/ktest/examples/include/defaults.conf b/tools/testing/ktest/examples/include/defaults.conf new file mode 100644 index 00000000000..63a1a83f4f0 --- /dev/null +++ b/tools/testing/ktest/examples/include/defaults.conf @@ -0,0 +1,157 @@ +# This file holds defaults for most the tests. It defines the options that +# are most common to tests that are likely to be shared. +# +# Note, after including this file, a config file may override any option +# with a DEFAULTS OVERRIDE section. +# + +# For those cases that use the same machine to boot a 64 bit +# and a 32 bit version. The MACHINE is the DNS name to get to the +# box (usually different if it was 64 bit or 32 bit) but the +# BOX here is defined as a variable that will be the name of the box +# itself. It is useful for calling scripts that will power cycle +# the box, as only one script needs to be created to power cycle +# even though the box itself has multiple operating systems on it. +# By default, BOX and MACHINE are the same. + +DEFAULTS IF NOT DEFINED BOX +BOX := ${MACHINE} + + +# Consider each box as 64 bit box, unless the config including this file +# has defined BITS = 32 + +DEFAULTS IF NOT DEFINED BITS +BITS := 64 + + +DEFAULTS + +# THIS_DIR is used through out the configs and defaults to ${PWD} which +# is the directory that ktest.pl was called from. + +THIS_DIR := ${PWD} + + +# to organize your configs, having each machine save their configs +# into a separate directly is useful. +CONFIG_DIR := ${THIS_DIR}/configs/${MACHINE} + +# Reset the log before running each test. +CLEAR_LOG = 1 + +# As installing kernels usually requires root privilege, default the +# user on the target as root. It is also required that the target +# allows ssh to root from the host without asking for a password. + +SSH_USER = root + +# For accesing the machine, we will ssh to root@machine. +SSH := ssh ${SSH_USER}@${MACHINE} + +# Update this. The default here is ktest will ssh to the target box +# and run a script called 'run-test' located on that box. +TEST = ${SSH} run-test + +# Point build dir to the git repo you use +BUILD_DIR = ${THIS_DIR}/linux.git + +# Each machine will have its own output build directory. +OUTPUT_DIR = ${THIS_DIR}/build/${MACHINE} + +# Yes this config is focused on x86 (but ktest works for other archs too) +BUILD_TARGET = arch/x86/boot/bzImage +TARGET_IMAGE = /boot/vmlinuz-test + +# have directory for the scripts to reboot and power cycle the boxes +SCRIPTS_DIR := ${THIS_DIR}/scripts + +# You can have each box/machine have a script to power cycle it. +# Name your script <box>-cycle. +POWER_CYCLE = ${SCRIPTS_DIR}/${BOX}-cycle + +# This script is used to power off the box. +POWER_OFF = ${SCRIPTS_DIR}/${BOX}-poweroff + +# Keep your test kernels separate from your other kernels. +LOCALVERSION = -test + +# The /boot/grub/menu.lst is searched for the line: +# title Test Kernel +# and ktest will use that kernel to reboot into. +# For grub2 or other boot loaders, you need to set BOOT_TYPE +# to 'script' and define other ways to load the kernel. +# See snowball.conf example. +# +GRUB_MENU = Test Kernel + +# The kernel build will use this option. +BUILD_OPTIONS = -j8 + +# Keeping the log file with the output dir is convenient. +LOG_FILE = ${OUTPUT_DIR}/${MACHINE}.log + +# Each box should have their own minum configuration +# See min-config.conf +MIN_CONFIG = ${CONFIG_DIR}/config-min + +# For things like randconfigs, there may be configs you find that +# are already broken, or there may be some configs that you always +# want set. Uncomment ADD_CONFIG and point it to the make config files +# that set the configs you want to keep on (or off) in your build. +# ADD_CONFIG is usually something to add configs to all machines, +# where as, MIN_CONFIG is specific per machine. +#ADD_CONFIG = ${THIS_DIR}/config-broken ${THIS_DIR}/config-general + +# To speed up reboots for bisects and patchcheck, instead of +# waiting 60 seconds for the console to be idle, if this line is +# seen in the console output, ktest will know the good kernel has +# finished rebooting and it will be able to continue the tests. +REBOOT_SUCCESS_LINE = ${MACHINE} login: + +# The following is different ways to end the test. +# by setting the variable REBOOT to: none, error, fail or +# something else, ktest will power cycle or reboot the target box +# at the end of the tests. +# +# REBOOT := none +# Don't do anything at the end of the test. +# +# REBOOT := error +# Reboot the box if ktest detects an error +# +# REBOOT := fail +# Do not stop on failure, and after all tests are complete +# power off the box (for both success and error) +# This is good to run over a weekend and you don't want to waste +# electricity. +# + +DEFAULTS IF ${REBOOT} == none +REBOOT_ON_SUCCESS = 0 +REBOOT_ON_ERROR = 0 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 + +DEFAULTS ELSE IF ${REBOOT} == error +REBOOT_ON_SUCCESS = 0 +REBOOT_ON_ERROR = 1 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 + +DEFAULTS ELSE IF ${REBOOT} == fail +REBOOT_ON_SUCCESS = 0 +POWEROFF_ON_ERROR = 1 +POWEROFF_ON_SUCCESS = 1 +POWEROFF_AFTER_HALT = 120 +DIE_ON_FAILURE = 0 + +# Store the failure information into this directory +# such as the .config, dmesg, and build log. +STORE_FAILURES = ${THIS_DIR}/failures + +DEFAULTS ELSE +REBOOT_ON_SUCCESS = 1 +REBOOT_ON_ERROR = 1 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 diff --git a/tools/testing/ktest/examples/include/min-config.conf b/tools/testing/ktest/examples/include/min-config.conf new file mode 100644 index 00000000000..c703cc46d15 --- /dev/null +++ b/tools/testing/ktest/examples/include/min-config.conf @@ -0,0 +1,60 @@ +# +# This file has some examples for creating a MIN_CONFIG. +# (A .config file that is the minimum for a machine to boot, or +# to boot and make a network connection.) +# +# A MIN_CONFIG is very useful as it is the minimum configuration +# needed to boot a given machine. You can debug someone else's +# .config by only setting the configs in your MIN_CONFIG. The closer +# your MIN_CONFIG is to the true minimum set of configs needed to +# boot your machine, the closer the config you test with will be +# to the users config that had the failure. +# +# The make_min_config test allows you to create a MIN_CONFIG that +# is truly the minimum set of configs needed to boot a box. +# +# In this example, the final config will reside in +# ${CONFIG_DIR}/config-new-min and ${CONFIG_DIR}/config-new-min-net. +# Just move one to the location you have set for MIN_CONFIG. +# +# The first test creates a MIN_CONFIG that will be the minimum +# configuration to boot ${MACHINE} and be able to ssh to it. +# +# The second test creates a MIN_CONFIG that will only boot +# the target and most likely will not let you ssh to it. (Notice +# how the second test uses the first test's result to continue with. +# This is because the second test config is a subset of the first). +# +# The ${CONFIG_DIR}/config-skip (and -net) will hold the configs +# that ktest.pl found would not boot the target without them set. +# The config-new-min holds configs that ktest.pl could not test +# directly because another config that was needed to boot the box +# selected them. Sometimes it is possible that this file will hold +# the true minimum configuration. You can test to see if this is +# the case by running the boot test with BOOT_TYPE = allnoconfig and +# setting setting the MIN_CONFIG to ${CONFIG_DIR}/config-skip. If the +# machine still boots, then you can use the config-skip as your MIN_CONFIG. +# +# These tests can run for several hours (and perhaps days). +# It's OK to kill the test with a Ctrl^C. By restarting without +# modifying this config, ktest.pl will notice that the config-new-min(-net) +# exists, and will use that instead as the starting point. +# The USE_OUTPUT_MIN_CONFIG is set to 1 to keep ktest.pl from asking +# you if you want to use the OUTPUT_MIN_CONFIG as the starting point. +# By using the OUTPUT_MIN_CONFIG as the starting point will allow ktest.pl to +# start almost where it left off. +# +TEST_START IF ${TEST} == min-config +TEST_TYPE = make_min_config +OUTPUT_MIN_CONFIG = ${CONFIG_DIR}/config-new-min-net +IGNORE_CONFIG = ${CONFIG_DIR}/config-skip-net +MIN_CONFIG_TYPE = test +TEST = ${SSH} echo hi +USE_OUTPUT_MIN_CONFIG = 1 + +TEST_START IF ${TEST} == min-config && ${MULTI} +TEST_TYPE = make_min_config +OUTPUT_MIN_CONFIG = ${CONFIG_DIR}/config-new-min +IGNORE_CONFIG = ${CONFIG_DIR}/config-skip +MIN_CONFIG = ${CONFIG_DIR}/config-new-min-net +USE_OUTPUT_MIN_CONFIG = 1 diff --git a/tools/testing/ktest/examples/include/patchcheck.conf b/tools/testing/ktest/examples/include/patchcheck.conf new file mode 100644 index 00000000000..0eb0a5ac77d --- /dev/null +++ b/tools/testing/ktest/examples/include/patchcheck.conf @@ -0,0 +1,111 @@ +# patchcheck.conf +# +# This contains a test that takes two git commits and will test each +# commit between the two. The build test will look at what files the +# commit has touched, and if any of those files produce a warning, then +# the build will fail. + + +# PATCH_START is the commit to begin with and PATCH_END is the commit +# to end with (inclusive). This is similar to doing a git rebase -i PATCH_START~1 +# and then testing each commit and doing a git rebase --continue. +# You can use a SHA1, a git tag, or anything that git will accept for a checkout + +PATCH_START := HEAD~3 +PATCH_END := HEAD + +# Use the oldconfig if build_type wasn't defined +DEFAULTS IF NOT DEFINED BUILD_TYPE +DO_BUILD_TYPE := oldconfig + +DEFAULTS ELSE +DO_BUILD_TYPE := ${BUILD_TYPE} + +DEFAULTS + + +# Change PATCH_CHECKOUT to be the branch you want to test. The test will +# do a git checkout of this branch before starting. Obviously both +# PATCH_START and PATCH_END must be in this branch (and PATCH_START must +# be contained by PATCH_END). + +PATCH_CHECKOUT := test/branch + +# Usually it's a good idea to have a set config to use for testing individual +# patches. +PATCH_CONFIG := ${CONFIG_DIR}/config-patchcheck + +# Change PATCH_TEST to run some test for each patch. Each commit that is +# tested, after it is built and installed on the test machine, this command +# will be executed. Usually what is done is to ssh to the target box and +# run some test scripts. If you just want to boot test your patches +# comment PATCH_TEST out. +PATCH_TEST := ${SSH} "/usr/local/bin/ktest-test-script" + +DEFAULTS IF DEFINED PATCH_TEST +PATCH_TEST_TYPE := test + +DEFAULTS ELSE +PATCH_TEST_TYPE := boot + +# If for some reason a file has a warning that one of your patches touch +# but you do not care about it, set IGNORE_WARNINGS to that commit(s) +# (space delimited) +#IGNORE_WARNINGS = 39eaf7ef884dcc44f7ff1bac803ca2a1dcf43544 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce + +# Instead of just checking for warnings to files that are changed +# it can be advantageous to check for any new warnings. If a +# header file is changed, it could cause a warning in a file not +# touched by the commit. To detect these kinds of warnings, you +# can use the WARNINGS_FILE option. +# +# If the variable CREATE_WARNINGS_FILE is set, this config will +# enable the WARNINGS_FILE during the patchcheck test. Also, +# before running the patchcheck test, it will create the +# warnings file. +# +DEFAULTS IF DEFINED CREATE_WARNINGS_FILE +WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file + +TEST_START IF DEFINED CREATE_WARNINGS_FILE +# WARNINGS_FILE is already set by the DEFAULTS above +TEST_TYPE = make_warnings_file +# Checkout the commit before the patches to test, +# and record all the warnings that exist before the patches +# to test are added +CHECKOUT = ${PATCHCHECK_START}~1 +# Force a full build +BUILD_NOCLEAN = 0 +BUILD_TYPE = ${DO_BUILD_TYPE} + +# If you are running a multi test, and the test failed on the first +# test but on, say the 5th patch. If you want to restart on the +# fifth patch, set PATCH_START1. This will make the first test start +# from this commit instead of the PATCH_START commit. +# Note, do not change this option. Just define PATCH_START1 in the +# top config (the one you pass to ktest.pl), and this will use it, +# otherwise it will just use PATCH_START if PATCH_START1 is not defined. +DEFAULTS IF NOT DEFINED PATCH_START1 +PATCH_START1 := ${PATCH_START} + +TEST_START IF ${TEST} == patchcheck +TEST_TYPE = patchcheck +MIN_CONFIG = ${PATCH_CONFIG} +TEST = ${PATCH_TEST} +PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} +PATCHCHECK_START = ${PATCH_START1} +PATCHCHECK_END = ${PATCH_END} +CHECKOUT = ${PATCH_CHECKOUT} +BUILD_TYPE = ${DO_BUILD_TYPE} + +TEST_START IF ${TEST} == patchcheck && ${MULTI} +TEST_TYPE = patchcheck +MIN_CONFIG = ${PATCH_CONFIG} +TEST = ${PATCH_TEST} +PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} +PATCHCHECK_START = ${PATCH_START} +PATCHCHECK_END = ${PATCH_END} +CHECKOUT = ${PATCH_CHECKOUT} +# Use multi to test different compilers? +MAKE_CMD = CC=gcc-4.5.1 make +BUILD_TYPE = ${DO_BUILD_TYPE} diff --git a/tools/testing/ktest/examples/include/tests.conf b/tools/testing/ktest/examples/include/tests.conf new file mode 100644 index 00000000000..60cedb1a115 --- /dev/null +++ b/tools/testing/ktest/examples/include/tests.conf @@ -0,0 +1,74 @@ +# +# This is an example of various tests that you can run +# +# The variable TEST can be of boot, build, randconfig, or test. +# +# Note that TEST is a variable created with ':=' and only exists +# throughout the config processing (not during the tests itself). +# +# The TEST option (defined with '=') is used to tell ktest.pl +# what test to run after a successful boot. The TEST option is +# persistent into the test runs. +# + +# The config that includes this file may define a BOOT_TYPE +# variable that tells this config what type of boot test to run. +# If it's not defined, the below DEFAULTS will set the default +# to 'oldconfig'. +# +DEFAULTS IF NOT DEFINED BOOT_TYPE +BOOT_TYPE := oldconfig + +# The config that includes this file may define a RUN_TEST +# variable that will tell this config what test to run. +# (what to set the TEST option to). +# +DEFAULTS IF NOT DEFINED RUN_TEST +# Requires that hackbench is in the PATH +RUN_TEST := ${SSH} hackbench 50 + + +# If TEST is set to 'boot' then just build a kernel and boot +# the target. +TEST_START IF ${TEST} == boot +TEST_TYPE = boot +# Notice how we set the BUILD_TYPE option to the BOOT_TYPE variable. +BUILD_TYPE = ${BOOT_TYPE} +# Do not do a make mrproper. +BUILD_NOCLEAN = 1 + +# If you only want to build the kernel, and perhaps install +# and test it yourself, then just set TEST to build. +TEST_START IF ${TEST} == build +TEST_TYPE = build +BUILD_TYPE = ${BOOT_TYPE} +BUILD_NOCLEAN = 1 + +# Build, install, boot and test with a randconfg 10 times. +# It is important that you have set MIN_CONFIG in the config +# that includes this file otherwise it is likely that the +# randconfig will not have the necessary configs needed to +# boot your box. This version of the test requires a min +# config that has enough to make sure the target has network +# working. +TEST_START ITERATE 10 IF ${TEST} == randconfig +MIN_CONFIG = ${CONFIG_DIR}/config-min-net +TEST_TYPE = test +BUILD_TYPE = randconfig +TEST = ${RUN_TEST} + +# This is the same as above, but only tests to a boot prompt. +# The MIN_CONFIG used here does not need to have networking +# working. +TEST_START ITERATE 10 IF ${TEST} == randconfig && ${MULTI} +TEST_TYPE = boot +BUILD_TYPE = randconfig +MIN_CONFIG = ${CONFIG_DIR}/config-min +MAKE_CMD = make + +# This builds, installs, boots and tests the target. +TEST_START IF ${TEST} == test +TEST_TYPE = test +BUILD_TYPE = ${BOOT_TYPE} +TEST = ${RUN_TEST} +BUILD_NOCLEAN = 1 diff --git a/tools/testing/ktest/examples/kvm.conf b/tools/testing/ktest/examples/kvm.conf new file mode 100644 index 00000000000..fbc134f9ac6 --- /dev/null +++ b/tools/testing/ktest/examples/kvm.conf @@ -0,0 +1,92 @@ +# +# This config is an example usage of ktest.pl with a kvm guest +# +# The guest is called 'Guest' and this would be something that +# could be run on the host to test a virtual machine target. + +MACHINE = Guest + + +# Use virsh to read the serial console of the guest +CONSOLE = virsh console ${MACHINE} + +# Use SIGKILL to terminate virsh console. We can't kill virsh console +# by the default signal, SIGINT. +CLOSE_CONSOLE_SIGNAL = KILL + +#*************************************# +# This part is the same as test.conf # +#*************************************# + +# The include files will set up the type of test to run. Just set TEST to +# which test you want to run. +# +# TESTS = patchcheck, randconfig, boot, test, config-bisect, bisect, min-config +# +# See the include/*.conf files that define these tests +# +TEST := patchcheck + +# Some tests may have more than one test to run. Define MULTI := 1 to run +# the extra tests. +MULTI := 0 + +# In case you want to differentiate which type of system you are testing +BITS := 64 + +# REBOOT = none, error, fail, empty +# See include/defaults.conf +REBOOT := empty + + +# The defaults file will set up various settings that can be used by all +# machine configs. +INCLUDE include/defaults.conf + + +#*************************************# +# Now we are different from test.conf # +#*************************************# + + +# The example here assumes that Guest is running a Fedora release +# that uses dracut for its initfs. The POST_INSTALL will be executed +# after the install of the kernel and modules are complete. +# +POST_INSTALL = ${SSH} /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION + +# Guests sometimes get stuck on reboot. We wait 3 seconds after running +# the reboot command and then do a full power-cycle of the guest. +# This forces the guest to restart. +# +POWERCYCLE_AFTER_REBOOT = 3 + +# We do the same after the halt command, but this time we wait 20 seconds. +POWEROFF_AFTER_HALT = 20 + + +# As the defaults.conf file has a POWER_CYCLE option already defined, +# and options can not be defined in the same section more than once +# (all DEFAULTS sections are considered the same). We use the +# DEFAULTS OVERRIDE to tell ktest.pl to ignore the previous defined +# options, for the options set in the OVERRIDE section. +# +DEFAULTS OVERRIDE + +# Instead of using the default POWER_CYCLE option defined in +# defaults.conf, we use virsh to cycle it. To do so, we destroy +# the guest, wait 5 seconds, and then start it up again. +# Crude, but effective. +# +POWER_CYCLE = virsh destroy ${MACHINE}; sleep 5; virsh start ${MACHINE} + + +DEFAULTS + +# The following files each handle a different test case. +# Having them included allows you to set up more than one machine and share +# the same tests. +INCLUDE include/patchcheck.conf +INCLUDE include/tests.conf +INCLUDE include/bisect.conf +INCLUDE include/min-config.conf diff --git a/tools/testing/ktest/examples/snowball.conf b/tools/testing/ktest/examples/snowball.conf new file mode 100644 index 00000000000..a82a3c5bc2b --- /dev/null +++ b/tools/testing/ktest/examples/snowball.conf @@ -0,0 +1,53 @@ +# This example was used to boot the snowball ARM board. +# See http://people.redhat.com/srostedt/ktest-embedded-2012/ + +# PWD is a ktest.pl variable that will result in the process working +# directory that ktest.pl is executed in. + +# THIS_DIR is automatically assigned the PWD of the path that generated +# the config file. It is best to use this variable when assigning other +# directory paths within this directory. This allows you to easily +# move the test cases to other locations or to other machines. +# +THIS_DIR := /home/rostedt/work/demo/ktest-embed +LOG_FILE = ${OUTPUT_DIR}/snowball.log +CLEAR_LOG = 1 +MAKE_CMD = PATH=/usr/local/gcc-4.5.2-nolibc/arm-unknown-linux-gnueabi/bin:$PATH CROSS_COMPILE=arm-unknown-linux-gnueabi- make ARCH=arm +ADD_CONFIG = ${THIS_DIR}/addconfig + +SCP_TO_TARGET = echo "don't do scp" + +TFTPBOOT := /var/lib/tftpboot +TFTPDEF := ${TFTPBOOT}/snowball-default +TFTPTEST := ${OUTPUT_DIR}/${BUILD_TARGET} + +SWITCH_TO_GOOD = cp ${TFTPDEF} ${TARGET_IMAGE} +SWITCH_TO_TEST = cp ${TFTPTEST} ${TARGET_IMAGE} + +# Define each test with TEST_START +# The config options below it will override the defaults +TEST_START SKIP +TEST_TYPE = boot +BUILD_TYPE = u8500_defconfig +BUILD_NOCLEAN = 1 + +TEST_START +TEST_TYPE = make_min_config +OUTPUT_MIN_CONFIG = ${THIS_DIR}/config.newmin +START_MIN_CONFIG = ${THIS_DIR}/config.orig +IGNORE_CONFIG = ${THIS_DIR}/config.ignore +BUILD_NOCLEAN = 1 + + +DEFAULTS +LOCALVERSION = -test +POWER_CYCLE = echo use the thumb luke; read a +CONSOLE = cat ${THIS_DIR}/snowball-cat +REBOOT_TYPE = script +SSH_USER = root +BUILD_OPTIONS = -j8 uImage +BUILD_DIR = ${THIS_DIR}/linux.git +OUTPUT_DIR = ${THIS_DIR}/snowball-build +MACHINE = snowball +TARGET_IMAGE = /var/lib/tftpboot/snowball-image +BUILD_TARGET = arch/arm/boot/uImage diff --git a/tools/testing/ktest/examples/test.conf b/tools/testing/ktest/examples/test.conf new file mode 100644 index 00000000000..b725210efb7 --- /dev/null +++ b/tools/testing/ktest/examples/test.conf @@ -0,0 +1,62 @@ +# +# Generic config for a machine +# + +# Name your machine (the DNS name, what you ssh to) +MACHINE = foo + +# BOX can be different than foo, if the machine BOX has +# multiple partitions with different systems installed. For example, +# you may have a i386 and x86_64 installation on a test box. +# If this is the case, MACHINE defines the way to connect to the +# machine, which may be different between which system the machine +# is booting into. BOX is used for the scripts to reboot and power cycle +# the machine, where it does not matter which system the machine boots into. +# +#BOX := bar + +# Define a way to read the console +CONSOLE = stty -F /dev/ttyS0 115200 parodd; cat /dev/ttyS0 + +# The include files will set up the type of test to run. Just set TEST to +# which test you want to run. +# +# TESTS = patchcheck, randconfig, boot, test, config-bisect, bisect, min-config +# +# See the include/*.conf files that define these tests +# +TEST := patchcheck + +# Some tests may have more than one test to run. Define MULTI := 1 to run +# the extra tests. +MULTI := 0 + +# In case you want to differentiate which type of system you are testing +BITS := 64 + +# REBOOT = none, error, fail, empty +# See include/defaults.conf +REBOOT := empty + +# The defaults file will set up various settings that can be used by all +# machine configs. +INCLUDE include/defaults.conf + +# In case you need to add a patch for a bisect or something +#PRE_BUILD = patch -p1 < ${THIS_DIR}/fix.patch + +# Reset the repo after the build and remove all 'test' modules from the target +# Notice that DO_POST_BUILD is a variable (defined by ':=') and POST_BUILD +# is the option (defined by '=') + +DO_POST_BUILD := git reset --hard +POST_BUILD = ${SSH} 'rm -rf /lib/modules/*-test*'; ${DO_POST_BUILD} + +# The following files each handle a different test case. +# Having them included allows you to set up more than one machine and share +# the same tests. +INCLUDE include/patchcheck.conf +INCLUDE include/tests.conf +INCLUDE include/bisect.conf +INCLUDE include/min-config.conf + diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index e1c62eeb88f..40631569a0f 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. +# Copyright 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. # Licensed under the terms of the GNU GPL License version 2 # @@ -18,38 +18,65 @@ $| = 1; my %opt; my %repeat_tests; my %repeats; -my %default; +my %evals; #default opts -$default{"NUM_TESTS"} = 1; -$default{"REBOOT_TYPE"} = "grub"; -$default{"TEST_TYPE"} = "test"; -$default{"BUILD_TYPE"} = "randconfig"; -$default{"MAKE_CMD"} = "make"; -$default{"TIMEOUT"} = 120; -$default{"TMP_DIR"} = "/tmp/ktest"; -$default{"SLEEP_TIME"} = 60; # sleep time between tests -$default{"BUILD_NOCLEAN"} = 0; -$default{"REBOOT_ON_ERROR"} = 0; -$default{"POWEROFF_ON_ERROR"} = 0; -$default{"REBOOT_ON_SUCCESS"} = 1; -$default{"POWEROFF_ON_SUCCESS"} = 0; -$default{"BUILD_OPTIONS"} = ""; -$default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects -$default{"CLEAR_LOG"} = 0; -$default{"SUCCESS_LINE"} = "login:"; -$default{"BOOTED_TIMEOUT"} = 1; -$default{"DIE_ON_FAILURE"} = 1; -$default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; -$default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; -$default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; -$default{"STOP_AFTER_SUCCESS"} = 10; -$default{"STOP_AFTER_FAILURE"} = 60; -$default{"LOCALVERSION"} = "-test"; +my %default = ( + "NUM_TESTS" => 1, + "TEST_TYPE" => "build", + "BUILD_TYPE" => "randconfig", + "MAKE_CMD" => "make", + "CLOSE_CONSOLE_SIGNAL" => "INT", + "TIMEOUT" => 120, + "TMP_DIR" => "/tmp/ktest/\${MACHINE}", + "SLEEP_TIME" => 60, # sleep time between tests + "BUILD_NOCLEAN" => 0, + "REBOOT_ON_ERROR" => 0, + "POWEROFF_ON_ERROR" => 0, + "REBOOT_ON_SUCCESS" => 1, + "POWEROFF_ON_SUCCESS" => 0, + "BUILD_OPTIONS" => "", + "BISECT_SLEEP_TIME" => 60, # sleep time between bisects + "PATCHCHECK_SLEEP_TIME" => 60, # sleep time between patch checks + "CLEAR_LOG" => 0, + "BISECT_MANUAL" => 0, + "BISECT_SKIP" => 1, + "BISECT_TRIES" => 1, + "MIN_CONFIG_TYPE" => "boot", + "SUCCESS_LINE" => "login:", + "DETECT_TRIPLE_FAULT" => 1, + "NO_INSTALL" => 0, + "BOOTED_TIMEOUT" => 1, + "DIE_ON_FAILURE" => 1, + "SSH_EXEC" => "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND", + "SCP_TO_TARGET" => "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE", + "SCP_TO_TARGET_INSTALL" => "\${SCP_TO_TARGET}", + "REBOOT" => "ssh \$SSH_USER\@\$MACHINE reboot", + "STOP_AFTER_SUCCESS" => 10, + "STOP_AFTER_FAILURE" => 60, + "STOP_TEST_AFTER" => 600, + "MAX_MONITOR_WAIT" => 1800, + "GRUB_REBOOT" => "grub2-reboot", + "SYSLINUX" => "extlinux", + "SYSLINUX_PATH" => "/boot/extlinux", + +# required, and we will ask users if they don't have them but we keep the default +# value something that is common. + "REBOOT_TYPE" => "grub", + "LOCALVERSION" => "-test", + "SSH_USER" => "root", + "BUILD_TARGET" => "arch/x86/boot/bzImage", + "TARGET_IMAGE" => "/boot/vmlinuz-test", + + "LOG_FILE" => undef, + "IGNORE_UNUSED" => 0, +); my $ktest_config; my $version; +my $have_version = 0; my $machine; +my $last_machine; my $ssh_user; my $tmpdir; my $builddir; @@ -58,57 +85,254 @@ my $output_config; my $test_type; my $build_type; my $build_options; +my $final_post_ktest; +my $pre_ktest; +my $post_ktest; +my $pre_test; +my $post_test; +my $pre_build; +my $post_build; +my $pre_build_die; +my $post_build_die; my $reboot_type; my $reboot_script; my $power_cycle; my $reboot; my $reboot_on_error; +my $switch_to_good; +my $switch_to_test; my $poweroff_on_error; +my $reboot_on_success; my $die_on_failure; my $powercycle_after_reboot; my $poweroff_after_halt; +my $max_monitor_wait; my $ssh_exec; my $scp_to_target; +my $scp_to_target_install; my $power_off; my $grub_menu; +my $last_grub_menu; +my $grub_file; my $grub_number; +my $grub_reboot; +my $syslinux; +my $syslinux_path; +my $syslinux_label; my $target; my $make; +my $pre_install; my $post_install; +my $no_install; my $noclean; my $minconfig; +my $start_minconfig; +my $start_minconfig_defined; +my $output_minconfig; +my $minconfig_type; +my $use_output_minconfig; +my $warnings_file; +my $ignore_config; +my $ignore_errors; my $addconfig; my $in_bisect = 0; -my $bisect_bad = ""; +my $bisect_bad_commit = ""; my $reverse_bisect; +my $bisect_manual; +my $bisect_skip; +my $bisect_tries; +my $config_bisect_good; +my $bisect_ret_good; +my $bisect_ret_bad; +my $bisect_ret_skip; +my $bisect_ret_abort; +my $bisect_ret_default; my $in_patchcheck = 0; my $run_test; my $redirect; my $buildlog; +my $testlog; my $dmesg; my $monitor_fp; my $monitor_pid; my $monitor_cnt = 0; my $sleep_time; my $bisect_sleep_time; +my $patchcheck_sleep_time; +my $ignore_warnings; my $store_failures; +my $store_successes; +my $test_name; my $timeout; my $booted_timeout; +my $detect_triplefault; my $console; +my $close_console_signal; +my $reboot_success_line; my $success_line; my $stop_after_success; my $stop_after_failure; +my $stop_test_after; my $build_target; my $target_image; +my $checkout; my $localversion; my $iteration = 0; my $successes = 0; +my $bisect_good; +my $bisect_bad; +my $bisect_type; +my $bisect_start; +my $bisect_replay; +my $bisect_files; +my $bisect_reverse; +my $bisect_check; + +my $config_bisect; +my $config_bisect_type; +my $config_bisect_check; + +my $patchcheck_type; +my $patchcheck_start; +my $patchcheck_end; + +# set when a test is something other that just building or install +# which would require more options. +my $buildonly = 1; + +# tell build not to worry about warnings, even when WARNINGS_FILE is set +my $warnings_ok = 0; + +# set when creating a new config +my $newconfig = 0; + my %entered_configs; my %config_help; +my %variable; + +# force_config is the list of configs that we force enabled (or disabled) +# in a .config file. The MIN_CONFIG and ADD_CONFIG configs. +my %force_config; + +# do not force reboots on config problems +my $no_reboot = 1; + +# reboot on success +my $reboot_success = 0; + +my %option_map = ( + "MACHINE" => \$machine, + "SSH_USER" => \$ssh_user, + "TMP_DIR" => \$tmpdir, + "OUTPUT_DIR" => \$outputdir, + "BUILD_DIR" => \$builddir, + "TEST_TYPE" => \$test_type, + "PRE_KTEST" => \$pre_ktest, + "POST_KTEST" => \$post_ktest, + "PRE_TEST" => \$pre_test, + "POST_TEST" => \$post_test, + "BUILD_TYPE" => \$build_type, + "BUILD_OPTIONS" => \$build_options, + "PRE_BUILD" => \$pre_build, + "POST_BUILD" => \$post_build, + "PRE_BUILD_DIE" => \$pre_build_die, + "POST_BUILD_DIE" => \$post_build_die, + "POWER_CYCLE" => \$power_cycle, + "REBOOT" => \$reboot, + "BUILD_NOCLEAN" => \$noclean, + "MIN_CONFIG" => \$minconfig, + "OUTPUT_MIN_CONFIG" => \$output_minconfig, + "START_MIN_CONFIG" => \$start_minconfig, + "MIN_CONFIG_TYPE" => \$minconfig_type, + "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, + "WARNINGS_FILE" => \$warnings_file, + "IGNORE_CONFIG" => \$ignore_config, + "TEST" => \$run_test, + "ADD_CONFIG" => \$addconfig, + "REBOOT_TYPE" => \$reboot_type, + "GRUB_MENU" => \$grub_menu, + "GRUB_FILE" => \$grub_file, + "GRUB_REBOOT" => \$grub_reboot, + "SYSLINUX" => \$syslinux, + "SYSLINUX_PATH" => \$syslinux_path, + "SYSLINUX_LABEL" => \$syslinux_label, + "PRE_INSTALL" => \$pre_install, + "POST_INSTALL" => \$post_install, + "NO_INSTALL" => \$no_install, + "REBOOT_SCRIPT" => \$reboot_script, + "REBOOT_ON_ERROR" => \$reboot_on_error, + "SWITCH_TO_GOOD" => \$switch_to_good, + "SWITCH_TO_TEST" => \$switch_to_test, + "POWEROFF_ON_ERROR" => \$poweroff_on_error, + "REBOOT_ON_SUCCESS" => \$reboot_on_success, + "DIE_ON_FAILURE" => \$die_on_failure, + "POWER_OFF" => \$power_off, + "POWERCYCLE_AFTER_REBOOT" => \$powercycle_after_reboot, + "POWEROFF_AFTER_HALT" => \$poweroff_after_halt, + "MAX_MONITOR_WAIT" => \$max_monitor_wait, + "SLEEP_TIME" => \$sleep_time, + "BISECT_SLEEP_TIME" => \$bisect_sleep_time, + "PATCHCHECK_SLEEP_TIME" => \$patchcheck_sleep_time, + "IGNORE_WARNINGS" => \$ignore_warnings, + "IGNORE_ERRORS" => \$ignore_errors, + "BISECT_MANUAL" => \$bisect_manual, + "BISECT_SKIP" => \$bisect_skip, + "BISECT_TRIES" => \$bisect_tries, + "CONFIG_BISECT_GOOD" => \$config_bisect_good, + "BISECT_RET_GOOD" => \$bisect_ret_good, + "BISECT_RET_BAD" => \$bisect_ret_bad, + "BISECT_RET_SKIP" => \$bisect_ret_skip, + "BISECT_RET_ABORT" => \$bisect_ret_abort, + "BISECT_RET_DEFAULT" => \$bisect_ret_default, + "STORE_FAILURES" => \$store_failures, + "STORE_SUCCESSES" => \$store_successes, + "TEST_NAME" => \$test_name, + "TIMEOUT" => \$timeout, + "BOOTED_TIMEOUT" => \$booted_timeout, + "CONSOLE" => \$console, + "CLOSE_CONSOLE_SIGNAL" => \$close_console_signal, + "DETECT_TRIPLE_FAULT" => \$detect_triplefault, + "SUCCESS_LINE" => \$success_line, + "REBOOT_SUCCESS_LINE" => \$reboot_success_line, + "STOP_AFTER_SUCCESS" => \$stop_after_success, + "STOP_AFTER_FAILURE" => \$stop_after_failure, + "STOP_TEST_AFTER" => \$stop_test_after, + "BUILD_TARGET" => \$build_target, + "SSH_EXEC" => \$ssh_exec, + "SCP_TO_TARGET" => \$scp_to_target, + "SCP_TO_TARGET_INSTALL" => \$scp_to_target_install, + "CHECKOUT" => \$checkout, + "TARGET_IMAGE" => \$target_image, + "LOCALVERSION" => \$localversion, + + "BISECT_GOOD" => \$bisect_good, + "BISECT_BAD" => \$bisect_bad, + "BISECT_TYPE" => \$bisect_type, + "BISECT_START" => \$bisect_start, + "BISECT_REPLAY" => \$bisect_replay, + "BISECT_FILES" => \$bisect_files, + "BISECT_REVERSE" => \$bisect_reverse, + "BISECT_CHECK" => \$bisect_check, + + "CONFIG_BISECT" => \$config_bisect, + "CONFIG_BISECT_TYPE" => \$config_bisect_type, + "CONFIG_BISECT_CHECK" => \$config_bisect_check, + + "PATCHCHECK_TYPE" => \$patchcheck_type, + "PATCHCHECK_START" => \$patchcheck_start, + "PATCHCHECK_END" => \$patchcheck_end, +); + +# Options may be used by other options, record them. +my %used_options; + +# default variables that can be used +chomp ($variable{"PWD"} = `pwd`); $config_help{"MACHINE"} = << "EOF" The machine hostname that you will test. + For build only tests, it is still needed to differentiate log files. EOF ; $config_help{"SSH_USER"} = << "EOF" @@ -118,11 +342,15 @@ EOF ; $config_help{"BUILD_DIR"} = << "EOF" The directory that contains the Linux source code (full path). + You can use \${PWD} that will be the path where ktest.pl is run, or use + \${THIS_DIR} which is assigned \${PWD} but may be changed later. EOF ; $config_help{"OUTPUT_DIR"} = << "EOF" The directory that the objects will be built (full path). (can not be same as BUILD_DIR) + You can use \${PWD} that will be the path where ktest.pl is run, or use + \${THIS_DIR} which is assigned \${PWD} but may be changed later. EOF ; $config_help{"BUILD_TARGET"} = << "EOF" @@ -130,6 +358,11 @@ $config_help{"BUILD_TARGET"} = << "EOF" (relative to OUTPUT_DIR) EOF ; +$config_help{"BUILD_OPTIONS"} = << "EOF" + Options to add to \"make\" when building. + i.e. -j20 +EOF + ; $config_help{"TARGET_IMAGE"} = << "EOF" The place to put your image on the test machine. EOF @@ -162,7 +395,7 @@ EOF ; $config_help{"REBOOT_TYPE"} = << "EOF" Way to reboot the box to the test kernel. - Only valid options so far are "grub" and "script". + Only valid options so far are "grub", "grub2", "syslinux", and "script". If you specify grub, it will assume grub version 1 and will search in /boot/grub/menu.lst for the title \$GRUB_MENU @@ -172,11 +405,19 @@ $config_help{"REBOOT_TYPE"} = << "EOF" The entry in /boot/grub/menu.lst must be entered in manually. The test will not modify that file. + + If you specify grub2, then you also need to specify both \$GRUB_MENU + and \$GRUB_FILE. + + If you specify syslinux, then you may use SYSLINUX to define the syslinux + command (defaults to extlinux), and SYSLINUX_PATH to specify the path to + the syslinux install (defaults to /boot/extlinux). But you have to specify + SYSLINUX_LABEL to define the label to boot to for the test kernel. EOF ; $config_help{"GRUB_MENU"} = << "EOF" The grub title name for the test kernel to boot - (Only mandatory if REBOOT_TYPE = grub) + (Only mandatory if REBOOT_TYPE = grub or grub2) Note, ktest.pl will not update the grub menu.lst, you need to manually add an option for the test. ktest.pl will search @@ -187,6 +428,22 @@ $config_help{"GRUB_MENU"} = << "EOF" title Test Kernel kernel vmlinuz-test GRUB_MENU = Test Kernel + + For grub2, a search of \$GRUB_FILE is performed for the lines + that begin with "menuentry". It will not detect submenus. The + menu must be a non-nested menu. Add the quotes used in the menu + to guarantee your selection, as the first menuentry with the content + of \$GRUB_MENU that is found will be used. +EOF + ; +$config_help{"GRUB_FILE"} = << "EOF" + If grub2 is used, the full path for the grub.cfg file is placed + here. Use something like /boot/grub2/grub.cfg to search. +EOF + ; +$config_help{"SYSLINUX_LABEL"} = << "EOF" + If syslinux is used, the label that boots the target kernel must + be specified with SYSLINUX_LABEL. EOF ; $config_help{"REBOOT_SCRIPT"} = << "EOF" @@ -195,9 +452,79 @@ $config_help{"REBOOT_SCRIPT"} = << "EOF" EOF ; +sub _logit { + if (defined($opt{"LOG_FILE"})) { + open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; + print OUT @_; + close(OUT); + } +} + +sub logit { + if (defined($opt{"LOG_FILE"})) { + _logit @_; + } else { + print @_; + } +} + +sub doprint { + print @_; + _logit @_; +} + +sub read_prompt { + my ($cancel, $prompt) = @_; + + my $ans; + + for (;;) { + if ($cancel) { + print "$prompt [y/n/C] "; + } else { + print "$prompt [Y/n] "; + } + $ans = <STDIN>; + chomp $ans; + if ($ans =~ /^\s*$/) { + if ($cancel) { + $ans = "c"; + } else { + $ans = "y"; + } + } + last if ($ans =~ /^y$/i || $ans =~ /^n$/i); + if ($cancel) { + last if ($ans =~ /^c$/i); + print "Please answer either 'y', 'n' or 'c'.\n"; + } else { + print "Please answer either 'y' or 'n'.\n"; + } + } + if ($ans =~ /^c/i) { + exit; + } + if ($ans !~ /^y$/i) { + return 0; + } + return 1; +} + +sub read_yn { + my ($prompt) = @_; + + return read_prompt 0, $prompt; +} + +sub read_ync { + my ($prompt) = @_; + + return read_prompt 1, $prompt; +} sub get_ktest_config { my ($config) = @_; + my $ans; return if (defined($opt{$config})); @@ -208,34 +535,50 @@ sub get_ktest_config { for (;;) { print "$config = "; - if (defined($default{$config})) { + if (defined($default{$config}) && length($default{$config})) { print "\[$default{$config}\] "; } - $entered_configs{$config} = <STDIN>; - $entered_configs{$config} =~ s/^\s*(.*\S)\s*$/$1/; - if ($entered_configs{$config} =~ /^\s*$/) { + $ans = <STDIN>; + $ans =~ s/^\s*(.*\S)\s*$/$1/; + if ($ans =~ /^\s*$/) { if ($default{$config}) { - $entered_configs{$config} = $default{$config}; + $ans = $default{$config}; } else { print "Your answer can not be blank\n"; next; } } + $entered_configs{$config} = ${ans}; last; } } sub get_ktest_configs { get_ktest_config("MACHINE"); - get_ktest_config("SSH_USER"); get_ktest_config("BUILD_DIR"); get_ktest_config("OUTPUT_DIR"); - get_ktest_config("BUILD_TARGET"); - get_ktest_config("TARGET_IMAGE"); - get_ktest_config("POWER_CYCLE"); - get_ktest_config("CONSOLE"); + + if ($newconfig) { + get_ktest_config("BUILD_OPTIONS"); + } + + # options required for other than just building a kernel + if (!$buildonly) { + get_ktest_config("POWER_CYCLE"); + get_ktest_config("CONSOLE"); + } + + # options required for install and more + if ($buildonly != 1) { + get_ktest_config("SSH_USER"); + get_ktest_config("BUILD_TARGET"); + get_ktest_config("TARGET_IMAGE"); + } + get_ktest_config("LOCALVERSION"); + return if ($buildonly); + my $rtype = $opt{"REBOOT_TYPE"}; if (!defined($rtype)) { @@ -249,102 +592,419 @@ sub get_ktest_configs { if ($rtype eq "grub") { get_ktest_config("GRUB_MENU"); - } else { - get_ktest_config("REBOOT_SCRIPT"); + } + + if ($rtype eq "grub2") { + get_ktest_config("GRUB_MENU"); + get_ktest_config("GRUB_FILE"); + } + + if ($rtype eq "syslinux") { + get_ktest_config("SYSLINUX_LABEL"); } } +sub process_variables { + my ($value, $remove_undef) = @_; + my $retval = ""; + + # We want to check for '\', and it is just easier + # to check the previous characet of '$' and not need + # to worry if '$' is the first character. By adding + # a space to $value, we can just check [^\\]\$ and + # it will still work. + $value = " $value"; + + while ($value =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) { + my $begin = $1; + my $var = $2; + my $end = $3; + # append beginning of value to retval + $retval = "$retval$begin"; + if (defined($variable{$var})) { + $retval = "$retval$variable{$var}"; + } elsif (defined($remove_undef) && $remove_undef) { + # for if statements, any variable that is not defined, + # we simple convert to 0 + $retval = "${retval}0"; + } else { + # put back the origin piece. + $retval = "$retval\$\{$var\}"; + # This could be an option that is used later, save + # it so we don't warn if this option is not one of + # ktests options. + $used_options{$var} = 1; + } + $value = $end; + } + $retval = "$retval$value"; + + # remove the space added in the beginning + $retval =~ s/ //; + + return "$retval" +} + sub set_value { - my ($lvalue, $rvalue) = @_; + my ($lvalue, $rvalue, $override, $overrides, $name) = @_; + + my $prvalue = process_variables($rvalue); + + if ($buildonly && $lvalue =~ /^TEST_TYPE(\[.*\])?$/ && $prvalue ne "build") { + # Note if a test is something other than build, then we + # will need other manditory options. + if ($prvalue ne "install") { + # for bisect, we need to check BISECT_TYPE + if ($prvalue ne "bisect") { + $buildonly = 0; + } + } else { + # install still limits some manditory options. + $buildonly = 2; + } + } + + if ($buildonly && $lvalue =~ /^BISECT_TYPE(\[.*\])?$/ && $prvalue ne "build") { + if ($prvalue ne "install") { + $buildonly = 0; + } else { + # install still limits some manditory options. + $buildonly = 2; + } + } if (defined($opt{$lvalue})) { - die "Error: Option $lvalue defined more than once!\n"; + if (!$override || defined(${$overrides}{$lvalue})) { + my $extra = ""; + if ($override) { + $extra = "In the same override section!\n"; + } + die "$name: $.: Option $lvalue defined more than once!\n$extra"; + } + ${$overrides}{$lvalue} = $prvalue; } if ($rvalue =~ /^\s*$/) { delete $opt{$lvalue}; } else { - $opt{$lvalue} = $rvalue; + $opt{$lvalue} = $prvalue; } } -sub read_config { - my ($config) = @_; +sub set_eval { + my ($lvalue, $rvalue, $name) = @_; + + my $prvalue = process_variables($rvalue); + my $arr; + + if (defined($evals{$lvalue})) { + $arr = $evals{$lvalue}; + } else { + $arr = []; + $evals{$lvalue} = $arr; + } + + push @{$arr}, $rvalue; +} + +sub set_variable { + my ($lvalue, $rvalue) = @_; + + if ($rvalue =~ /^\s*$/) { + delete $variable{$lvalue}; + } else { + $rvalue = process_variables($rvalue); + $variable{$lvalue} = $rvalue; + } +} + +sub process_compare { + my ($lval, $cmp, $rval) = @_; + + # remove whitespace + + $lval =~ s/^\s*//; + $lval =~ s/\s*$//; + + $rval =~ s/^\s*//; + $rval =~ s/\s*$//; + + if ($cmp eq "==") { + return $lval eq $rval; + } elsif ($cmp eq "!=") { + return $lval ne $rval; + } elsif ($cmp eq "=~") { + return $lval =~ m/$rval/; + } elsif ($cmp eq "!~") { + return $lval !~ m/$rval/; + } + + my $statement = "$lval $cmp $rval"; + my $ret = eval $statement; + + # $@ stores error of eval + if ($@) { + return -1; + } + + return $ret; +} + +sub value_defined { + my ($val) = @_; + + return defined($variable{$2}) || + defined($opt{$2}); +} + +my $d = 0; +sub process_expression { + my ($name, $val) = @_; + + my $c = $d++; + + while ($val =~ s/\(([^\(]*?)\)/\&\&\&\&VAL\&\&\&\&/) { + my $express = $1; + + if (process_expression($name, $express)) { + $val =~ s/\&\&\&\&VAL\&\&\&\&/ 1 /; + } else { + $val =~ s/\&\&\&\&VAL\&\&\&\&/ 0 /; + } + } + + $d--; + my $OR = "\\|\\|"; + my $AND = "\\&\\&"; + + while ($val =~ s/^(.*?)($OR|$AND)//) { + my $express = $1; + my $op = $2; + + if (process_expression($name, $express)) { + if ($op eq "||") { + return 1; + } + } else { + if ($op eq "&&") { + return 0; + } + } + } + + if ($val =~ /(.*)(==|\!=|>=|<=|>|<|=~|\!~)(.*)/) { + my $ret = process_compare($1, $2, $3); + if ($ret < 0) { + die "$name: $.: Unable to process comparison\n"; + } + return $ret; + } + + if ($val =~ /^\s*(NOT\s*)?DEFINED\s+(\S+)\s*$/) { + if (defined $1) { + return !value_defined($2); + } else { + return value_defined($2); + } + } + + if ($val =~ /^\s*0\s*$/) { + return 0; + } elsif ($val =~ /^\s*\d+\s*$/) { + return 1; + } + + die ("$name: $.: Undefined content $val in if statement\n"); +} + +sub process_if { + my ($name, $value) = @_; + + # Convert variables and replace undefined ones with 0 + my $val = process_variables($value, 1); + my $ret = process_expression $name, $val; - open(IN, $config) || die "can't read file $config"; + return $ret; +} + +sub __read_config { + my ($config, $current_test_num) = @_; + + my $in; + open($in, $config) || die "can't read file $config"; my $name = $config; $name =~ s,.*/(.*),$1,; - my $test_num = 0; + my $test_num = $$current_test_num; my $default = 1; my $repeat = 1; my $num_tests_set = 0; my $skip = 0; my $rest; + my $line; + my $test_case = 0; + my $if = 0; + my $if_set = 0; + my $override = 0; - while (<IN>) { + my %overrides; + + while (<$in>) { # ignore blank lines and comments next if (/^\s*$/ || /\s*\#/); - if (/^\s*TEST_START(.*)/) { + if (/^\s*(TEST_START|DEFAULTS)\b(.*)/) { - $rest = $1; + my $type = $1; + $rest = $2; + $line = $2; - if ($num_tests_set) { - die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; - } + my $old_test_num; + my $old_repeat; + $override = 0; + + if ($type eq "TEST_START") { + + if ($num_tests_set) { + die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; + } - my $old_test_num = $test_num; - my $old_repeat = $repeat; + $old_test_num = $test_num; + $old_repeat = $repeat; - $test_num += $repeat; - $default = 0; - $repeat = 1; + $test_num += $repeat; + $default = 0; + $repeat = 1; + } else { + $default = 1; + } - if ($rest =~ /\s+SKIP(.*)/) { - $rest = $1; + # If SKIP is anywhere in the line, the command will be skipped + if ($rest =~ s/\s+SKIP\b//) { $skip = 1; } else { + $test_case = 1; $skip = 0; } - if ($rest =~ /\s+ITERATE\s+(\d+)(.*)$/) { - $repeat = $1; - $rest = $2; - $repeat_tests{"$test_num"} = $repeat; + if ($rest =~ s/\sELSE\b//) { + if (!$if) { + die "$name: $.: ELSE found with out matching IF section\n$_"; + } + $if = 0; + + if ($if_set) { + $skip = 1; + } else { + $skip = 0; + } } - if ($rest =~ /\s+SKIP(.*)/) { - $rest = $1; - $skip = 1; + if ($rest =~ s/\sIF\s+(.*)//) { + if (process_if($name, $1)) { + $if_set = 1; + } else { + $skip = 1; + } + $if = 1; + } else { + $if = 0; + $if_set = 0; } - if ($rest !~ /^\s*$/) { - die "$name: $.: Gargbage found after TEST_START\n$_"; + if (!$skip) { + if ($type eq "TEST_START") { + if ($rest =~ s/\s+ITERATE\s+(\d+)//) { + $repeat = $1; + $repeat_tests{"$test_num"} = $repeat; + } + } elsif ($rest =~ s/\sOVERRIDE\b//) { + # DEFAULT only + $override = 1; + # Clear previous overrides + %overrides = (); + } + } + + if (!$skip && $rest !~ /^\s*$/) { + die "$name: $.: Gargbage found after $type\n$_"; } - if ($skip) { + if ($skip && $type eq "TEST_START") { $test_num = $old_test_num; $repeat = $old_repeat; } - } elsif (/^\s*DEFAULTS(.*)$/) { - $default = 1; - + } elsif (/^\s*ELSE\b(.*)$/) { + if (!$if) { + die "$name: $.: ELSE found with out matching IF section\n$_"; + } $rest = $1; - - if ($rest =~ /\s+SKIP(.*)/) { - $rest = $1; + if ($if_set) { $skip = 1; + $rest = ""; } else { $skip = 0; + + if ($rest =~ /\sIF\s+(.*)/) { + # May be a ELSE IF section. + if (process_if($name, $1)) { + $if_set = 1; + } else { + $skip = 1; + } + $rest = ""; + } else { + $if = 0; + } } if ($rest !~ /^\s*$/) { die "$name: $.: Gargbage found after DEFAULTS\n$_"; } + } elsif (/^\s*INCLUDE\s+(\S+)/) { + + next if ($skip); + + if (!$default) { + die "$name: $.: INCLUDE can only be done in default sections\n$_"; + } + + my $file = process_variables($1); + + if ($file !~ m,^/,) { + # check the path of the config file first + if ($config =~ m,(.*)/,) { + if (-f "$1/$file") { + $file = "$1/$file"; + } + } + } + + if ( ! -r $file ) { + die "$name: $.: Can't read file $file\n$_"; + } + + if (__read_config($file, \$test_num)) { + $test_case = 1; + } + + } elsif (/^\s*([A-Z_\[\]\d]+)\s*=~\s*(.*?)\s*$/) { + + next if ($skip); + + my $lvalue = $1; + my $rvalue = $2; + + if ($default || $lvalue =~ /\[\d+\]$/) { + set_eval($lvalue, $rvalue, $name); + } else { + my $val = "$lvalue\[$test_num\]"; + set_eval($val, $rvalue, $name); + } + } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) { next if ($skip); @@ -370,30 +1030,74 @@ sub read_config { } if ($default || $lvalue =~ /\[\d+\]$/) { - set_value($lvalue, $rvalue); + set_value($lvalue, $rvalue, $override, \%overrides, $name); } else { my $val = "$lvalue\[$test_num\]"; - set_value($val, $rvalue); + set_value($val, $rvalue, $override, \%overrides, $name); if ($repeat > 1) { $repeats{$val} = $repeat; } } + } elsif (/^\s*([A-Z_\[\]\d]+)\s*:=\s*(.*?)\s*$/) { + next if ($skip); + + my $lvalue = $1; + my $rvalue = $2; + + # process config variables. + # Config variables are only active while reading the + # config and can be defined anywhere. They also ignore + # TEST_START and DEFAULTS, but are skipped if they are in + # on of these sections that have SKIP defined. + # The save variable can be + # defined multiple times and the new one simply overrides + # the prevous one. + set_variable($lvalue, $rvalue); + } else { die "$name: $.: Garbage found in config\n$_"; } } - close(IN); - if ($test_num) { $test_num += $repeat - 1; $opt{"NUM_TESTS"} = $test_num; } + close($in); + + $$current_test_num = $test_num; + + return $test_case; +} + +sub get_test_case { + print "What test case would you like to run?\n"; + print " (build, install or boot)\n"; + print " Other tests are available but require editing the config file\n"; + my $ans = <STDIN>; + chomp $ans; + $default{"TEST_TYPE"} = $ans; +} + +sub read_config { + my ($config) = @_; + + my $test_case; + my $test_num = 0; + + $test_case = __read_config $config, \$test_num; + # make sure we have all mandatory configs get_ktest_configs; + # was a test specified? + if (!$test_case) { + print "No test case specified.\n"; + get_test_case; + } + # set any defaults foreach my $default (keys %default) { @@ -401,32 +1105,173 @@ sub read_config { $opt{$default} = $default{$default}; } } + + if ($opt{"IGNORE_UNUSED"} == 1) { + return; + } + + my %not_used; + + # check if there are any stragglers (typos?) + foreach my $option (keys %opt) { + my $op = $option; + # remove per test labels. + $op =~ s/\[.*\]//; + if (!exists($option_map{$op}) && + !exists($default{$op}) && + !exists($used_options{$op})) { + $not_used{$op} = 1; + } + } + + if (%not_used) { + my $s = "s are"; + $s = " is" if (keys %not_used == 1); + print "The following option$s not used; could be a typo:\n"; + foreach my $option (keys %not_used) { + print "$option\n"; + } + print "Set IGRNORE_UNUSED = 1 to have ktest ignore unused variables\n"; + if (!read_yn "Do you want to continue?") { + exit -1; + } + } } -sub _logit { - if (defined($opt{"LOG_FILE"})) { - open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; - print OUT @_; - close(OUT); +sub __eval_option { + my ($name, $option, $i) = @_; + + # Add space to evaluate the character before $ + $option = " $option"; + my $retval = ""; + my $repeated = 0; + my $parent = 0; + + foreach my $test (keys %repeat_tests) { + if ($i >= $test && + $i < $test + $repeat_tests{$test}) { + + $repeated = 1; + $parent = $test; + last; + } } + + while ($option =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) { + my $start = $1; + my $var = $2; + my $end = $3; + + # Append beginning of line + $retval = "$retval$start"; + + # If the iteration option OPT[$i] exists, then use that. + # otherwise see if the default OPT (without [$i]) exists. + + my $o = "$var\[$i\]"; + my $parento = "$var\[$parent\]"; + + # If a variable contains itself, use the default var + if (($var eq $name) && defined($opt{$var})) { + $o = $opt{$var}; + $retval = "$retval$o"; + } elsif (defined($opt{$o})) { + $o = $opt{$o}; + $retval = "$retval$o"; + } elsif ($repeated && defined($opt{$parento})) { + $o = $opt{$parento}; + $retval = "$retval$o"; + } elsif (defined($opt{$var})) { + $o = $opt{$var}; + $retval = "$retval$o"; + } elsif ($var eq "KERNEL_VERSION" && defined($make)) { + # special option KERNEL_VERSION uses kernel version + get_version(); + $retval = "$retval$version"; + } else { + $retval = "$retval\$\{$var\}"; + } + + $option = $end; + } + + $retval = "$retval$option"; + + $retval =~ s/^ //; + + return $retval; } -sub logit { - if (defined($opt{"LOG_FILE"})) { - _logit @_; +sub process_evals { + my ($name, $option, $i) = @_; + + my $option_name = "$name\[$i\]"; + my $ev; + + my $old_option = $option; + + if (defined($evals{$option_name})) { + $ev = $evals{$option_name}; + } elsif (defined($evals{$name})) { + $ev = $evals{$name}; } else { - print @_; + return $option; } + + for my $e (@{$ev}) { + eval "\$option =~ $e"; + } + + if ($option ne $old_option) { + doprint("$name changed from '$old_option' to '$option'\n"); + } + + return $option; } -sub doprint { - print @_; - _logit @_; +sub eval_option { + my ($name, $option, $i) = @_; + + my $prev = ""; + + # Since an option can evaluate to another option, + # keep iterating until we do not evaluate any more + # options. + my $r = 0; + while ($prev ne $option) { + # Check for recursive evaluations. + # 100 deep should be more than enough. + if ($r++ > 100) { + die "Over 100 evaluations accurred with $option\n" . + "Check for recursive variables\n"; + } + $prev = $option; + $option = __eval_option($name, $option, $i); + } + + $option = process_evals($name, $option, $i); + + return $option; } sub run_command; +sub start_monitor; +sub end_monitor; +sub wait_for_monitor; sub reboot { + my ($time) = @_; + + # Make sure everything has been written to disk + run_ssh("sync"); + + if (defined($time)) { + start_monitor; + # flush out current monitor + # May contain the reboot success line + wait_for_monitor 1; + } + # try to reboot normally if (run_command $reboot) { if (defined($powercycle_after_reboot)) { @@ -437,12 +1282,44 @@ sub reboot { # nope? power cycle it. run_command "$power_cycle"; } + + if (defined($time)) { + + # We only want to get to the new kernel, don't fail + # if we stumble over a call trace. + my $save_ignore_errors = $ignore_errors; + $ignore_errors = 1; + + # Look for the good kernel to boot + if (wait_for_monitor($time, "Linux version")) { + # reboot got stuck? + doprint "Reboot did not finish. Forcing power cycle\n"; + run_command "$power_cycle"; + } + + $ignore_errors = $save_ignore_errors; + + # Still need to wait for the reboot to finish + wait_for_monitor($time, $reboot_success_line); + + end_monitor; + } +} + +sub reboot_to_good { + my ($time) = @_; + + if (defined($switch_to_good)) { + run_command $switch_to_good; + } + + reboot $time; } sub do_not_reboot { my $i = $iteration; - return $test_type eq "build" || + return $test_type eq "build" || $no_reboot || ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") || ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build"); } @@ -455,13 +1332,17 @@ sub dodie { if ($reboot_on_error && !do_not_reboot) { doprint "REBOOTING\n"; - reboot; + reboot_to_good; } elsif ($poweroff_on_error && defined($power_off)) { doprint "POWERING OFF\n"; `$power_off`; } + if (defined($opt{"LOG_FILE"})) { + print " See $opt{LOG_FILE} for more info.\n"; + } + die @_, "\n"; } @@ -485,7 +1366,7 @@ sub close_console { my ($fp, $pid) = @_; doprint "kill child process $pid\n"; - kill 2, $pid; + kill $close_console_signal, $pid; print "closing!\n"; close($fp); @@ -504,6 +1385,7 @@ sub start_monitor { } sub end_monitor { + return if (!defined $console); if (--$monitor_cnt) { return; } @@ -511,46 +1393,67 @@ sub end_monitor { } sub wait_for_monitor { - my ($time) = @_; + my ($time, $stop) = @_; + my $full_line = ""; my $line; + my $booted = 0; + my $start_time = time; + my $skip_call_trace = 0; + my $bug = 0; + my $bug_ignored = 0; + my $now; doprint "** Wait for monitor to settle down **\n"; # read the monitor and wait for the system to calm down - do { + while (!$booted) { $line = wait_for_input($monitor_fp, $time); - print "$line" if (defined($line)); - } while (defined($line)); - print "** Monitor flushed **\n"; -} - -sub fail { + last if (!defined($line)); + print "$line"; + $full_line .= $line; - if ($die_on_failure) { - dodie @_; + if (defined($stop) && $full_line =~ /$stop/) { + doprint "wait for monitor detected $stop\n"; + $booted = 1; } - doprint "FAILED\n"; + if ($full_line =~ /\[ backtrace testing \]/) { + $skip_call_trace = 1; + } - my $i = $iteration; + if ($full_line =~ /call trace:/i) { + if (!$bug && !$skip_call_trace) { + if ($ignore_errors) { + $bug_ignored = 1; + } else { + $bug = 1; + } + } + } - # no need to reboot for just building. - if (!do_not_reboot) { - doprint "REBOOTING\n"; - reboot; - start_monitor; - wait_for_monitor $sleep_time; - end_monitor; + if ($full_line =~ /\[ end of backtrace testing \]/) { + $skip_call_trace = 0; } - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n"; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + if ($full_line =~ /Kernel panic -/) { + $bug = 1; + } - return 1 if (!defined($store_failures)); + if ($line =~ /\n/) { + $full_line = ""; + } + $now = time; + if ($now - $start_time >= $max_monitor_wait) { + doprint "Exiting monitor flush due to hitting MAX_MONITOR_WAIT\n"; + return 1; + } + } + print "** Monitor flushed **\n"; + return $bug; +} +sub save_logs { + my ($result, $basedir) = @_; my @t = localtime; my $date = sprintf "%04d%02d%02d%02d%02d%02d", 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; @@ -560,27 +1463,67 @@ sub fail { $type = "useconfig"; } - my $dir = "$machine-$test_type-$type-fail-$date"; - my $faildir = "$store_failures/$dir"; + my $dir = "$machine-$test_type-$type-$result-$date"; - if (!-d $faildir) { - mkpath($faildir) or - die "can't create $faildir"; + $dir = "$basedir/$dir"; + + if (!-d $dir) { + mkpath($dir) or + die "can't create $dir"; } - if (-f "$output_config") { - cp "$output_config", "$faildir/config" or - die "failed to copy .config"; + + my %files = ( + "config" => $output_config, + "buildlog" => $buildlog, + "dmesg" => $dmesg, + "testlog" => $testlog, + ); + + while (my ($name, $source) = each(%files)) { + if (-f "$source") { + cp "$source", "$dir/$name" or + die "failed to copy $source"; + } } - if (-f $buildlog) { - cp $buildlog, "$faildir/buildlog" or - die "failed to move $buildlog"; + + doprint "*** Saved info to $dir ***\n"; +} + +sub fail { + + if (defined($post_test)) { + run_command $post_test; + } + + if ($die_on_failure) { + dodie @_; + } + + doprint "FAILED\n"; + + my $i = $iteration; + + # no need to reboot for just building. + if (!do_not_reboot) { + doprint "REBOOTING\n"; + reboot_to_good $sleep_time; } - if (-f $dmesg) { - cp $dmesg, "$faildir/dmesg" or - die "failed to move $dmesg"; + + my $name = ""; + + if (defined($test_name)) { + $name = " ($test_name)"; } - doprint "*** Saved info to $faildir ***\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + + if (defined($store_failures)) { + save_logs "fail", $store_failures; + } return 1; } @@ -641,8 +1584,7 @@ sub run_ssh { } sub run_scp { - my ($src, $dst) = @_; - my $cp_scp = $scp_to_target; + my ($src, $dst, $cp_scp) = @_; $cp_scp =~ s/\$SRC_FILE/$src/g; $cp_scp =~ s/\$DST_FILE/$dst/g; @@ -650,12 +1592,70 @@ sub run_scp { return run_command "$cp_scp"; } +sub run_scp_install { + my ($src, $dst) = @_; + + my $cp_scp = $scp_to_target_install; + + return run_scp($src, $dst, $cp_scp); +} + +sub run_scp_mod { + my ($src, $dst) = @_; + + my $cp_scp = $scp_to_target; + + return run_scp($src, $dst, $cp_scp); +} + +sub get_grub2_index { + + return if (defined($grub_number) && defined($last_grub_menu) && + $last_grub_menu eq $grub_menu && defined($last_machine) && + $last_machine eq $machine); + + doprint "Find grub2 menu ... "; + $grub_number = -1; + + my $ssh_grub = $ssh_exec; + $ssh_grub =~ s,\$SSH_COMMAND,cat $grub_file,g; + + open(IN, "$ssh_grub |") + or die "unable to get $grub_file"; + + my $found = 0; + + while (<IN>) { + if (/^menuentry.*$grub_menu/) { + $grub_number++; + $found = 1; + last; + } elsif (/^menuentry\s/) { + $grub_number++; + } + } + close(IN); + + die "Could not find '$grub_menu' in $grub_file on $machine" + if (!$found); + doprint "$grub_number\n"; + $last_grub_menu = $grub_menu; + $last_machine = $machine; +} + sub get_grub_index { + if ($reboot_type eq "grub2") { + get_grub2_index; + return; + } + if ($reboot_type ne "grub") { return; } - return if (defined($grub_number)); + return if (defined($grub_number) && defined($last_grub_menu) && + $last_grub_menu eq $grub_menu && defined($last_machine) && + $last_machine eq $machine); doprint "Find grub menu ... "; $grub_number = -1; @@ -666,9 +1666,12 @@ sub get_grub_index { open(IN, "$ssh_grub |") or die "unable to get menu.lst"; + my $found = 0; + while (<IN>) { if (/^\s*title\s+$grub_menu\s*$/) { $grub_number++; + $found = 1; last; } elsif (/^\s*title\s/) { $grub_number++; @@ -677,8 +1680,10 @@ sub get_grub_index { close(IN); die "Could not find '$grub_menu' in /boot/grub/menu on $machine" - if ($grub_number < 0); + if (!$found); doprint "$grub_number\n"; + $last_grub_menu = $grub_menu; + $last_machine = $machine; } sub wait_for_input @@ -695,7 +1700,7 @@ sub wait_for_input $rin = ''; vec($rin, fileno($fp), 1) = 1; - $ready = select($rin, undef, undef, $time); + ($ready, $time) = select($rin, undef, undef, $time); $line = ""; @@ -713,12 +1718,20 @@ sub wait_for_input } sub reboot_to { - if ($reboot_type eq "grub") { - run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; - return; + if (defined($switch_to_test)) { + run_command $switch_to_test; } - run_command "$reboot_script"; + if ($reboot_type eq "grub") { + run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; + } elsif ($reboot_type eq "grub2") { + run_ssh "$grub_reboot $grub_number"; + } elsif ($reboot_type eq "syslinux") { + run_ssh "$syslinux --once \\\"$syslinux_label\\\" $syslinux_path"; + } elsif (defined $reboot_script) { + run_command "$reboot_script"; + } + reboot; } sub get_sha1 { @@ -745,6 +1758,7 @@ sub get_sha1 { sub monitor { my $booted = 0; my $bug = 0; + my $bug_ignored = 0; my $skip_call_trace = 0; my $loops; @@ -760,17 +1774,37 @@ sub monitor { my $success_start; my $failure_start; + my $monitor_start = time; + my $done = 0; + my $version_found = 0; - for (;;) { + while (!$done) { - if ($booted) { + if ($bug && defined($stop_after_failure) && + $stop_after_failure >= 0) { + my $time = $stop_after_failure - (time - $failure_start); + $line = wait_for_input($monitor_fp, $time); + if (!defined($line)) { + doprint "bug timed out after $booted_timeout seconds\n"; + doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; + last; + } + } elsif ($booted) { $line = wait_for_input($monitor_fp, $booted_timeout); + if (!defined($line)) { + my $s = $booted_timeout == 1 ? "" : "s"; + doprint "Successful boot found: break after $booted_timeout second$s\n"; + last; + } } else { $line = wait_for_input($monitor_fp); + if (!defined($line)) { + my $s = $timeout == 1 ? "" : "s"; + doprint "Timed out after $timeout second$s\n"; + last; + } } - last if (!defined($line)); - doprint $line; print DMESG $line; @@ -796,9 +1830,13 @@ sub monitor { } if ($full_line =~ /call trace:/i) { - if (!$skip_call_trace) { - $bug = 1; - $failure_start = time; + if (!$bug && !$skip_call_trace) { + if ($ignore_errors) { + $bug_ignored = 1; + } else { + $bug = 1; + $failure_start = time; + } } } @@ -816,12 +1854,36 @@ sub monitor { } if ($full_line =~ /Kernel panic -/) { + $failure_start = time; $bug = 1; } + # Detect triple faults by testing the banner + if ($full_line =~ /\bLinux version (\S+).*\n/) { + if ($1 eq $version) { + $version_found = 1; + } elsif ($version_found && $detect_triplefault) { + # We already booted into the kernel we are testing, + # but now we booted into another kernel? + # Consider this a triple fault. + doprint "Already booted in Linux kernel $version, but now\n"; + doprint "we booted into Linux kernel $1.\n"; + doprint "Assuming that this is a triple fault.\n"; + doprint "To disable this: set DETECT_TRIPLE_FAULT to 0\n"; + last; + } + } + if ($line =~ /\n/) { $full_line = ""; } + + if ($stop_test_after > 0 && !$booted && !$bug) { + if (time - $monitor_start > $stop_test_after) { + doprint "STOP_TEST_AFTER ($stop_test_after seconds) timed out\n"; + $done = 1; + } + } } close(DMESG); @@ -836,12 +1898,53 @@ sub monitor { fail "failed - never got a boot prompt." and return 0; } + if ($bug_ignored) { + doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n"; + } + return 1; } +sub eval_kernel_version { + my ($option) = @_; + + $option =~ s/\$KERNEL_VERSION/$version/g; + + return $option; +} + +sub do_post_install { + + return if (!defined($post_install)); + + my $cp_post_install = eval_kernel_version $post_install; + run_command "$cp_post_install" or + dodie "Failed to run post install"; +} + +# Sometimes the reboot fails, and will hang. We try to ssh to the box +# and if we fail, we force another reboot, that should powercycle it. +sub test_booted { + if (!run_ssh "echo testing connection") { + reboot $sleep_time; + } +} + sub install { - run_scp "$outputdir/$build_target", "$target_image" or + return if ($no_install); + + if (defined($pre_install)) { + my $cp_pre_install = eval_kernel_version $pre_install; + run_command "$cp_pre_install" or + dodie "Failed to run pre install"; + } + + my $cp_target = eval_kernel_version $target_image; + + test_booted; + + run_scp_install "$outputdir/$build_target", "$cp_target" or dodie "failed to copy image"; my $install_mods = 0; @@ -851,18 +1954,21 @@ sub install { open(IN, "$output_config") or dodie("Can't read config file"); while (<IN>) { if (/CONFIG_MODULES(=y)?/) { - $install_mods = 1 if (defined($1)); - last; + if (defined($1)) { + $install_mods = 1; + last; + } } } close(IN); if (!$install_mods) { + do_post_install; doprint "No modules needed\n"; return; } - run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or + run_command "$make INSTALL_MOD_STRIP=1 INSTALL_MOD_PATH=$tmpdir modules_install" or dodie "Failed to install modules"; my $modlib = "/lib/modules/$version"; @@ -875,29 +1981,127 @@ sub install { run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or dodie "making tarball"; - run_scp "$tmpdir/$modtar", "/tmp" or + run_scp_mod "$tmpdir/$modtar", "/tmp" or dodie "failed to copy modules"; unlink "$tmpdir/$modtar"; - run_ssh "'(cd / && tar xf /tmp/$modtar)'" or + run_ssh "'(cd / && tar xjf /tmp/$modtar)'" or dodie "failed to tar modules"; run_ssh "rm -f /tmp/$modtar"; - return if (!defined($post_install)); + do_post_install; +} - my $cp_post_install = $post_install; - $cp_post_install = s/\$KERNEL_VERSION/$version/g; - run_command "$cp_post_install" or - dodie "Failed to run post install"; +sub get_version { + # get the release name + return if ($have_version); + doprint "$make kernelrelease ... "; + $version = `$make kernelrelease | tail -1`; + chomp($version); + doprint "$version\n"; + $have_version = 1; +} + +sub start_monitor_and_boot { + # Make sure the stable kernel has finished booting + + # Install bisects, don't need console + if (defined $console) { + start_monitor; + wait_for_monitor 5; + end_monitor; + } + + get_grub_index; + get_version; + install; + + start_monitor if (defined $console); + return monitor; +} + +my $check_build_re = ".*:.*(warning|error|Error):.*"; +my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; + +sub process_warning_line { + my ($line) = @_; + + chomp $line; + + # for distcc heterogeneous systems, some compilers + # do things differently causing warning lines + # to be slightly different. This makes an attempt + # to fixe those issues. + + # chop off the index into the line + # using distcc, some compilers give different indexes + # depending on white space + $line =~ s/^(\s*\S+:\d+:)\d+/$1/; + + # Some compilers use UTF-8 extended for quotes and some don't. + $line =~ s/$utf8_quote/'/g; + + return $line; } +# Read buildlog and check against warnings file for any +# new warnings. +# +# Returns 1 if OK +# 0 otherwise sub check_buildlog { + return 1 if (!defined $warnings_file); + + my %warnings_list; + + # Failed builds should not reboot the target + my $save_no_reboot = $no_reboot; + $no_reboot = 1; + + if (-f $warnings_file) { + open(IN, $warnings_file) or + dodie "Error opening $warnings_file"; + + while (<IN>) { + if (/$check_build_re/) { + my $warning = process_warning_line $_; + + $warnings_list{$warning} = 1; + } + } + close(IN); + } + + # If warnings file didn't exist, and WARNINGS_FILE exist, + # then we fail on any warning! + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while (<IN>) { + if (/$check_build_re/) { + my $warning = process_warning_line $_; + + if (!defined $warnings_list{$warning}) { + fail "New warning found (not in $warnings_file)\n$_\n"; + $no_reboot = $save_no_reboot; + return 0; + } + } + } + $no_reboot = $save_no_reboot; + close(IN); +} + +sub check_patch_buildlog { my ($patch) = @_; my @files = `git show $patch | diffstat -l`; + foreach my $file (@files) { + chomp $file; + } + open(IN, "git show $patch |") or dodie "failed to show $patch"; while (<IN>) { @@ -925,12 +2129,96 @@ sub check_buildlog { return 1; } +sub apply_min_config { + my $outconfig = "$output_config.new"; + + # Read the config file and remove anything that + # is in the force_config hash (from minconfig and others) + # then add the force config back. + + doprint "Applying minimum configurations into $output_config.new\n"; + + open (OUT, ">$outconfig") or + dodie "Can't create $outconfig"; + + if (-f $output_config) { + open (IN, $output_config) or + dodie "Failed to open $output_config"; + while (<IN>) { + if (/^(# )?(CONFIG_[^\s=]*)/) { + next if (defined($force_config{$2})); + } + print OUT; + } + close IN; + } + foreach my $config (keys %force_config) { + print OUT "$force_config{$config}\n"; + } + close OUT; + + run_command "mv $outconfig $output_config"; +} + +sub make_oldconfig { + + my @force_list = keys %force_config; + + if ($#force_list >= 0) { + apply_min_config; + } + + if (!run_command "$make olddefconfig") { + # Perhaps olddefconfig doesn't exist in this version of the kernel + # try oldnoconfig + doprint "olddefconfig failed, trying make oldnoconfig\n"; + if (!run_command "$make oldnoconfig") { + doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; + # try a yes '' | oldconfig + run_command "yes '' | $make oldconfig" or + dodie "failed make config oldconfig"; + } + } +} + +# read a config file and use this to force new configs. +sub load_force_config { + my ($config) = @_; + + doprint "Loading force configs from $config\n"; + open(IN, $config) or + dodie "failed to read $config"; + while (<IN>) { + chomp; + if (/^(CONFIG[^\s=]*)(\s*=.*)/) { + $force_config{$1} = $_; + } elsif (/^# (CONFIG_\S*) is not set/) { + $force_config{$1} = $_; + } + } + close IN; +} + sub build { my ($type) = @_; - my $defconfig = ""; unlink $buildlog; + # Failed builds should not reboot the target + my $save_no_reboot = $no_reboot; + $no_reboot = 1; + + # Calculate a new version from here. + $have_version = 0; + + if (defined($pre_build)) { + my $ret = run_command $pre_build; + if (!$ret && defined($pre_build_die) && + $pre_build_die) { + dodie "failed to pre_build\n"; + } + } + if ($type =~ /^useconfig:(.*)/) { run_command "cp $1 $output_config" or dodie "could not copy $1 to .config"; @@ -940,20 +2228,20 @@ sub build { # old config can ask questions if ($type eq "oldconfig") { - $type = "oldnoconfig"; + $type = "olddefconfig"; # allow for empty configs run_command "touch $output_config"; - run_command "mv $output_config $outputdir/config_temp" or - dodie "moving .config"; + if (!$noclean) { + run_command "mv $output_config $outputdir/config_temp" or + dodie "moving .config"; - if (!$noclean && !run_command "$make mrproper") { - dodie "make mrproper"; - } + run_command "$make mrproper" or dodie "make mrproper"; - run_command "mv $outputdir/config_temp $output_config" or - dodie "moving config_temp"; + run_command "mv $outputdir/config_temp $output_config" or + dodie "moving config_temp"; + } } elsif (!$noclean) { unlink "$output_config"; @@ -967,20 +2255,41 @@ sub build { close(OUT); if (defined($minconfig)) { - $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; + load_force_config($minconfig); } - run_command "$defconfig $make $type" or - dodie "failed make config"; + if ($type ne "olddefconfig") { + run_command "$make $type" or + dodie "failed make config"; + } + # Run old config regardless, to enforce min configurations + make_oldconfig; $redirect = "$buildlog"; - if (!run_command "$make $build_options") { - undef $redirect; + my $build_ret = run_command "$make $build_options"; + undef $redirect; + + if (defined($post_build)) { + # Because a post build may change the kernel version + # do it now. + get_version; + my $ret = run_command $post_build; + if (!$ret && defined($post_build_die) && + $post_build_die) { + dodie "failed to post_build\n"; + } + } + + if (!$build_ret) { # bisect may need this to pass - return 0 if ($in_bisect); + if ($in_bisect) { + $no_reboot = $save_no_reboot; + return 0; + } fail "failed build" and return 0; } - undef $redirect; + + $no_reboot = $save_no_reboot; return 1; } @@ -1000,29 +2309,47 @@ sub halt { sub success { my ($i) = @_; + if (defined($post_test)) { + run_command $post_test; + } + $successes++; + my $name = ""; + + if (defined($test_name)) { + $name = " ($test_name)"; + } + doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; - doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; + doprint "KTEST RESULT: TEST $i$name SUCCESS!!!! **\n"; doprint "*******************************************\n"; doprint "*******************************************\n"; + if (defined($store_successes)) { + save_logs "success", $store_successes; + } + if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { doprint "Reboot and wait $sleep_time seconds\n"; - reboot; - start_monitor; - wait_for_monitor $sleep_time; - end_monitor; + reboot_to_good $sleep_time; } } -sub get_version { - # get the release name - doprint "$make kernelrelease ... "; - $version = `$make kernelrelease | tail -1`; - chomp($version); - doprint "$version\n"; +sub answer_bisect { + for (;;) { + doprint "Pass or fail? [p/f]"; + my $ans = <STDIN>; + chomp $ans; + if ($ans eq "p" || $ans eq "P") { + return 1; + } elsif ($ans eq "f" || $ans eq "F") { + return 0; + } else { + print "Please answer 'P' or 'F'\n"; + } + } } sub child_run_test { @@ -1033,7 +2360,10 @@ sub child_run_test { $poweroff_on_error = 0; $die_on_failure = 1; + $redirect = "$testlog"; run_command $run_test or $failed = 1; + undef $redirect; + exit $failed; } @@ -1049,6 +2379,7 @@ sub do_run_test { my $line; my $full_line; my $bug = 0; + my $bug_ignored = 0; wait_for_monitor 1; @@ -1070,9 +2401,14 @@ sub do_run_test { # we are not guaranteed to get a full line $full_line .= $line; + doprint $line; if ($full_line =~ /call trace:/i) { - $bug = 1; + if ($ignore_errors) { + $bug_ignored = 1; + } else { + $bug = 1; + } } if ($full_line =~ /Kernel panic -/) { @@ -1085,7 +2421,24 @@ sub do_run_test { } } while (!$child_done && !$bug); + if (!$bug && $bug_ignored) { + doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n"; + } + if ($bug) { + my $failure_start = time; + my $now; + do { + $line = wait_for_input($monitor_fp, 1); + if (defined($line)) { + doprint $line; + } + $now = time; + if ($now - $failure_start >= $stop_after_failure) { + last; + } + } while (defined($line)); + doprint "Detected kernel crash!\n"; # kill the child with extreme prejudice kill 9, $child_pid; @@ -1094,6 +2447,43 @@ sub do_run_test { waitpid $child_pid, 0; $child_exit = $?; + if (!$bug && $in_bisect) { + if (defined($bisect_ret_good)) { + if ($child_exit == $bisect_ret_good) { + return 1; + } + } + if (defined($bisect_ret_skip)) { + if ($child_exit == $bisect_ret_skip) { + return -1; + } + } + if (defined($bisect_ret_abort)) { + if ($child_exit == $bisect_ret_abort) { + fail "test abort" and return -2; + } + } + if (defined($bisect_ret_bad)) { + if ($child_exit == $bisect_ret_skip) { + return 0; + } + } + if (defined($bisect_ret_default)) { + if ($bisect_ret_default eq "good") { + return 1; + } elsif ($bisect_ret_default eq "bad") { + return 0; + } elsif ($bisect_ret_default eq "skip") { + return -1; + } elsif ($bisect_ret_default eq "abort") { + return -2; + } else { + fail "unknown default action: $bisect_ret_default" + and return -2; + } + } + } + if ($bug || $child_exit) { return 0 if $in_bisect; fail "test failed" and return 0; @@ -1120,7 +2510,7 @@ sub run_git_bisect { if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { doprint "$1 [$2]\n"; } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { - $bisect_bad = $1; + $bisect_bad_commit = $1; doprint "Found bad commit... $1\n"; return 0; } else { @@ -1131,7 +2521,12 @@ sub run_git_bisect { return 1; } -# returns 1 on success, 0 on failure +sub bisect_reboot { + doprint "Reboot and sleep $bisect_sleep_time seconds\n"; + reboot_to_good $bisect_sleep_time; +} + +# returns 1 on success, 0 on failure, -1 on skip sub run_bisect_test { my ($type, $buildtype) = @_; @@ -1145,17 +2540,22 @@ sub run_bisect_test { build $buildtype or $failed = 1; if ($type ne "build") { + if ($failed && $bisect_skip) { + $in_bisect = 0; + return -1; + } dodie "Failed on build" if $failed; # Now boot the box - get_grub_index; - get_version; - install; - - start_monitor; - monitor or $failed = 1; + start_monitor_and_boot or $failed = 1; if ($type ne "boot") { + if ($failed && $bisect_skip) { + end_monitor; + bisect_reboot; + $in_bisect = 0; + return -1; + } dodie "Failed on boot" if $failed; do_run_test or $failed = 1; @@ -1165,18 +2565,14 @@ sub run_bisect_test { if ($failed) { $result = 0; - - # reboot the box to a good kernel - if ($type ne "build") { - doprint "Reboot and sleep $bisect_sleep_time seconds\n"; - reboot; - start_monitor; - wait_for_monitor $bisect_sleep_time; - end_monitor; - } } else { $result = 1; } + + # reboot the box to a kernel we can ssh to + if ($type ne "build") { + bisect_reboot; + } $in_bisect = 0; return $result; @@ -1191,42 +2587,78 @@ sub run_bisect { $buildtype = "useconfig:$minconfig"; } - my $ret = run_bisect_test $type, $buildtype; + # If the user sets bisect_tries to less than 1, then no tries + # is a success. + my $ret = 1; + + # Still let the user manually decide that though. + if ($bisect_tries < 1 && $bisect_manual) { + $ret = answer_bisect; + } + + for (my $i = 0; $i < $bisect_tries; $i++) { + if ($bisect_tries > 1) { + my $t = $i + 1; + doprint("Running bisect trial $t of $bisect_tries:\n"); + } + $ret = run_bisect_test $type, $buildtype; + + if ($bisect_manual) { + $ret = answer_bisect; + } + last if (!$ret); + } # Are we looking for where it worked, not failed? - if ($reverse_bisect) { + if ($reverse_bisect && $ret >= 0) { $ret = !$ret; } - if ($ret) { + if ($ret > 0) { return "good"; - } else { + } elsif ($ret == 0) { return "bad"; + } elsif ($bisect_skip) { + doprint "HIT A BAD COMMIT ... SKIPPING\n"; + return "skip"; } } +sub update_bisect_replay { + my $tmp_log = "$tmpdir/ktest_bisect_log"; + run_command "git bisect log > $tmp_log" or + die "can't create bisect log"; + return $tmp_log; +} + sub bisect { my ($i) = @_; my $result; - die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"})); - die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"})); - die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"})); + die "BISECT_GOOD[$i] not defined\n" if (!defined($bisect_good)); + die "BISECT_BAD[$i] not defined\n" if (!defined($bisect_bad)); + die "BISECT_TYPE[$i] not defined\n" if (!defined($bisect_type)); + + my $good = $bisect_good; + my $bad = $bisect_bad; + my $type = $bisect_type; + my $start = $bisect_start; + my $replay = $bisect_replay; + my $start_files = $bisect_files; - my $good = $opt{"BISECT_GOOD[$i]"}; - my $bad = $opt{"BISECT_BAD[$i]"}; - my $type = $opt{"BISECT_TYPE[$i]"}; - my $start = $opt{"BISECT_START[$i]"}; - my $replay = $opt{"BISECT_REPLAY[$i]"}; + if (defined($start_files)) { + $start_files = " -- " . $start_files; + } else { + $start_files = ""; + } # convert to true sha1's $good = get_sha1($good); $bad = get_sha1($bad); - if (defined($opt{"BISECT_REVERSE[$i]"}) && - $opt{"BISECT_REVERSE[$i]"} == 1) { + if (defined($bisect_reverse) && $bisect_reverse == 1) { doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; $reverse_bisect = 1; } else { @@ -1238,8 +2670,31 @@ sub bisect { $type = "boot"; } - my $check = $opt{"BISECT_CHECK[$i]"}; - if (defined($check) && $check ne "0") { + # Check if a bisect was running + my $bisect_start_file = "$builddir/.git/BISECT_START"; + + my $check = $bisect_check; + my $do_check = defined($check) && $check ne "0"; + + if ( -f $bisect_start_file ) { + print "Bisect in progress found\n"; + if ($do_check) { + print " If you say yes, then no checks of good or bad will be done\n"; + } + if (defined($replay)) { + print "** BISECT_REPLAY is defined in config file **"; + print " Ignore config option and perform new git bisect log?\n"; + if (read_ync " (yes, no, or cancel) ") { + $replay = update_bisect_replay; + $do_check = 0; + } + } elsif (read_yn "read git log and continue?") { + $replay = update_bisect_replay; + $do_check = 0; + } + } + + if ($do_check) { # get current HEAD my $head = get_sha1("HEAD"); @@ -1273,7 +2728,7 @@ sub bisect { die "Failed to checkout $head"; } - run_command "git bisect start" or + run_command "git bisect start$start_files" or dodie "could not start bisect"; run_command "git bisect good $good" or @@ -1304,34 +2759,54 @@ sub bisect { run_command "git bisect reset" or dodie "could not reset git bisect"; - doprint "Bad commit was [$bisect_bad]\n"; + doprint "Bad commit was [$bisect_bad_commit]\n"; success $i; } +# config_ignore holds the configs that were set (or unset) for +# a good config and we will ignore these configs for the rest +# of a config bisect. These configs stay as they were. my %config_ignore; + +# config_set holds what all configs were set as. my %config_set; +# config_off holds the set of configs that the bad config had disabled. +# We need to record them and set them in the .config when running +# olddefconfig, because olddefconfig keeps the defaults. +my %config_off; + +# config_off_tmp holds a set of configs to turn off for now +my @config_off_tmp; + +# config_list is the set of configs that are being tested my %config_list; my %null_config; my %dependency; -sub process_config_ignore { - my ($config) = @_; +sub assign_configs { + my ($hash, $config) = @_; open (IN, $config) or dodie "Failed to read $config"; while (<IN>) { - if (/^(.*?(CONFIG\S*)(=.*| is not set))/) { - $config_ignore{$2} = $1; + if (/^((CONFIG\S*)=.*)/) { + ${$hash}{$2} = $1; } } close(IN); } +sub process_config_ignore { + my ($config) = @_; + + assign_configs \%config_ignore, $config; +} + sub read_current_config { my ($config_ref) = @_; @@ -1384,15 +2859,22 @@ sub create_config { } } + # turn off configs to keep off + foreach my $config (keys %config_off) { + print OUT "# $config is not set\n"; + } + + # turn off configs that should be off for now + foreach my $config (@config_off_tmp) { + print OUT "# $config is not set\n"; + } + foreach my $config (keys %config_ignore) { print OUT "$config_ignore{$config}\n"; } close(OUT); -# exit; - run_command "$make oldnoconfig" or - dodie "failed make config oldconfig"; - + make_oldconfig; } sub compare_configs { @@ -1456,7 +2938,7 @@ sub run_config_bisect { } doprint "***** RUN TEST ***\n"; - my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"}; + my $type = $config_bisect_type; my $ret; my %current_config; @@ -1468,6 +2950,13 @@ sub run_config_bisect { do { my @tophalf = @start_list[0 .. $half]; + # keep the bottom half off + if ($half < $#start_list) { + @config_off_tmp = @start_list[$half + 1 .. $#start_list]; + } else { + @config_off_tmp = (); + } + create_config @tophalf; read_current_config \%current_config; @@ -1484,7 +2973,11 @@ sub run_config_bisect { if (!$found) { # try the other half doprint "Top half produced no set configs, trying bottom half\n"; - @tophalf = @start_list[$half .. $#start_list]; + + # keep the top half off + @config_off_tmp = @tophalf; + @tophalf = @start_list[$half + 1 .. $#start_list]; + create_config @tophalf; read_current_config \%current_config; foreach my $config (@tophalf) { @@ -1505,7 +2998,9 @@ sub run_config_bisect { } $ret = run_config_bisect_test $type; - + if ($bisect_manual) { + $ret = answer_bisect; + } if ($ret) { process_passed %current_config; return 0; @@ -1534,9 +3029,15 @@ sub run_config_bisect { # remove half the configs we are looking at and see if # they are good. $half = int($#start_list / 2); - } while ($half > 0); + } while ($#start_list > 0); + + # we found a single config, try it again unless we are running manually + + if ($bisect_manual) { + process_failed $start_list[0]; + return 1; + } - # we found a single config, try it again my @tophalf = @start_list[0 .. 0]; $ret = run_config_bisect_test $type; @@ -1552,10 +3053,14 @@ sub run_config_bisect { sub config_bisect { my ($i) = @_; - my $start_config = $opt{"CONFIG_BISECT[$i]"}; + my $start_config = $config_bisect; my $tmpconfig = "$tmpdir/use_config"; + if (defined($config_bisect_good)) { + process_config_ignore $config_bisect_good; + } + # Make the file with the bad config and the min config if (defined($minconfig)) { # read the min config for things to ignore @@ -1565,15 +3070,8 @@ sub config_bisect { unlink $tmpconfig; } - # Add other configs - if (defined($addconfig)) { - run_command "cat $addconfig >> $tmpconfig" or - dodie "failed to append $addconfig"; - } - - my $defconfig = ""; if (-f $tmpconfig) { - $defconfig = "KCONFIG_ALLCONFIG=$tmpconfig"; + load_force_config($tmpconfig); process_config_ignore $tmpconfig; } @@ -1584,7 +3082,7 @@ sub config_bisect { # read directly what we want to check my %config_check; open (IN, $output_config) - or dodie "faied to open $output_config"; + or dodie "failed to open $output_config"; while (<IN>) { if (/^((CONFIG\S*)=.*)/) { @@ -1593,9 +3091,8 @@ sub config_bisect { } close(IN); - # Now run oldconfig with the minconfig (and addconfigs) - run_command "$defconfig $make oldnoconfig" or - dodie "failed make config oldconfig"; + # Now run oldconfig with the minconfig + make_oldconfig; # check to see what we lost (or gained) open (IN, $output_config) @@ -1618,6 +3115,10 @@ sub config_bisect { $added_configs{$2} = $1; $config_list{$2} = $1; } + } elsif (/^# ((CONFIG\S*).*)/) { + # Keep these configs disabled + $config_set{$2} = $1; + $config_off{$2} = $1; } } close(IN); @@ -1640,6 +3141,8 @@ sub config_bisect { my %config_test; my $once = 0; + @config_off_tmp = (); + # Sometimes kconfig does weird things. We must make sure # that the config we autocreate has everything we need # to test, otherwise we may miss testing configs, or @@ -1658,6 +3161,18 @@ sub config_bisect { } } my $ret; + + if (defined($config_bisect_check) && $config_bisect_check) { + doprint " Checking to make sure bad config with min config fails\n"; + create_config keys %config_list; + $ret = run_config_bisect_test $config_bisect_type; + if ($ret) { + doprint " FAILED! Bad config with min config boots fine\n"; + return -1; + } + doprint " Bad config with min config fails as expected\n"; + } + do { $ret = run_config_bisect; } while (!$ret); @@ -1667,26 +3182,31 @@ sub config_bisect { success $i; } +sub patchcheck_reboot { + doprint "Reboot and sleep $patchcheck_sleep_time seconds\n"; + reboot_to_good $patchcheck_sleep_time; +} + sub patchcheck { my ($i) = @_; die "PATCHCHECK_START[$i] not defined\n" - if (!defined($opt{"PATCHCHECK_START[$i]"})); + if (!defined($patchcheck_start)); die "PATCHCHECK_TYPE[$i] not defined\n" - if (!defined($opt{"PATCHCHECK_TYPE[$i]"})); + if (!defined($patchcheck_type)); - my $start = $opt{"PATCHCHECK_START[$i]"}; + my $start = $patchcheck_start; my $end = "HEAD"; - if (defined($opt{"PATCHCHECK_END[$i]"})) { - $end = $opt{"PATCHCHECK_END[$i]"}; + if (defined($patchcheck_end)) { + $end = $patchcheck_end; } # Get the true sha1's since we can use things like HEAD~3 $start = get_sha1($start); $end = get_sha1($end); - my $type = $opt{"PATCHCHECK_TYPE[$i]"}; + my $type = $patchcheck_type; # Can't have a test without having a test to run if ($type eq "test" && !defined($run_test)) { @@ -1713,6 +3233,13 @@ sub patchcheck { @list = reverse @list; my $save_clean = $noclean; + my %ignored_warnings; + + if (defined($ignore_warnings)) { + foreach my $sha1 (split /\s+/, $ignore_warnings) { + $ignored_warnings{$sha1} = 1; + } + } $in_patchcheck = 1; foreach my $item (@list) { @@ -1739,18 +3266,18 @@ sub patchcheck { build "oldconfig" or return 0; } - check_buildlog $sha1 or return 0; + # No need to do per patch checking if warnings file exists + if (!defined($warnings_file) && !defined($ignored_warnings{$sha1})) { + check_patch_buildlog $sha1 or return 0; + } - next if ($type eq "build"); + check_buildlog or return 0; - get_grub_index; - get_version; - install; + next if ($type eq "build"); my $failed = 0; - start_monitor; - monitor or $failed = 1; + start_monitor_and_boot or $failed = 1; if (!$failed && $type ne "boot"){ do_run_test or $failed = 1; @@ -1758,6 +3285,8 @@ sub patchcheck { end_monitor; return 0 if ($failed); + patchcheck_reboot; + } $in_patchcheck = 0; success $i; @@ -1765,24 +3294,582 @@ sub patchcheck { return 1; } +my %depends; +my %depcount; +my $iflevel = 0; +my @ifdeps; + +# prevent recursion +my %read_kconfigs; + +sub add_dep { + # $config depends on $dep + my ($config, $dep) = @_; + + if (defined($depends{$config})) { + $depends{$config} .= " " . $dep; + } else { + $depends{$config} = $dep; + } + + # record the number of configs depending on $dep + if (defined $depcount{$dep}) { + $depcount{$dep}++; + } else { + $depcount{$dep} = 1; + } +} + +# taken from streamline_config.pl +sub read_kconfig { + my ($kconfig) = @_; + + my $state = "NONE"; + my $config; + my @kconfigs; + + my $cont = 0; + my $line; + + + if (! -f $kconfig) { + doprint "file $kconfig does not exist, skipping\n"; + return; + } + + open(KIN, "$kconfig") + or die "Can't open $kconfig"; + while (<KIN>) { + chomp; + + # Make sure that lines ending with \ continue + if ($cont) { + $_ = $line . " " . $_; + } + + if (s/\\$//) { + $cont = 1; + $line = $_; + next; + } + + $cont = 0; + + # collect any Kconfig sources + if (/^source\s*"(.*)"/) { + $kconfigs[$#kconfigs+1] = $1; + } + + # configs found + if (/^\s*(menu)?config\s+(\S+)\s*$/) { + $state = "NEW"; + $config = $2; + + for (my $i = 0; $i < $iflevel; $i++) { + add_dep $config, $ifdeps[$i]; + } + + # collect the depends for the config + } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) { + + add_dep $config, $1; + + # Get the configs that select this config + } elsif ($state eq "NEW" && /^\s*select\s+(\S+)/) { + + # selected by depends on config + add_dep $1, $config; + + # Check for if statements + } elsif (/^if\s+(.*\S)\s*$/) { + my $deps = $1; + # remove beginning and ending non text + $deps =~ s/^[^a-zA-Z0-9_]*//; + $deps =~ s/[^a-zA-Z0-9_]*$//; + + my @deps = split /[^a-zA-Z0-9_]+/, $deps; + + $ifdeps[$iflevel++] = join ':', @deps; + + } elsif (/^endif/) { + + $iflevel-- if ($iflevel); + + # stop on "help" + } elsif (/^\s*help\s*$/) { + $state = "NONE"; + } + } + close(KIN); + + # read in any configs that were found. + foreach $kconfig (@kconfigs) { + if (!defined($read_kconfigs{$kconfig})) { + $read_kconfigs{$kconfig} = 1; + read_kconfig("$builddir/$kconfig"); + } + } +} + +sub read_depends { + # find out which arch this is by the kconfig file + open (IN, $output_config) + or dodie "Failed to read $output_config"; + my $arch; + while (<IN>) { + if (m,Linux/(\S+)\s+\S+\s+Kernel Configuration,) { + $arch = $1; + last; + } + } + close IN; + + if (!defined($arch)) { + doprint "Could not find arch from config file\n"; + doprint "no dependencies used\n"; + return; + } + + # arch is really the subarch, we need to know + # what directory to look at. + if ($arch eq "i386" || $arch eq "x86_64") { + $arch = "x86"; + } elsif ($arch =~ /^tile/) { + $arch = "tile"; + } + + my $kconfig = "$builddir/arch/$arch/Kconfig"; + + if (! -f $kconfig && $arch =~ /\d$/) { + my $orig = $arch; + # some subarchs have numbers, truncate them + $arch =~ s/\d*$//; + $kconfig = "$builddir/arch/$arch/Kconfig"; + if (! -f $kconfig) { + doprint "No idea what arch dir $orig is for\n"; + doprint "no dependencies used\n"; + return; + } + } + + read_kconfig($kconfig); +} + +sub read_config_list { + my ($config) = @_; + + open (IN, $config) + or dodie "Failed to read $config"; + + while (<IN>) { + if (/^((CONFIG\S*)=.*)/) { + if (!defined($config_ignore{$2})) { + $config_list{$2} = $1; + } + } + } + + close(IN); +} + +sub read_output_config { + my ($config) = @_; + + assign_configs \%config_ignore, $config; +} + +sub make_new_config { + my @configs = @_; + + open (OUT, ">$output_config") + or dodie "Failed to write $output_config"; + + foreach my $config (@configs) { + print OUT "$config\n"; + } + close OUT; +} + +sub chomp_config { + my ($config) = @_; + + $config =~ s/CONFIG_//; + + return $config; +} + +sub get_depends { + my ($dep) = @_; + + my $kconfig = chomp_config $dep; + + $dep = $depends{"$kconfig"}; + + # the dep string we have saves the dependencies as they + # were found, including expressions like ! && ||. We + # want to split this out into just an array of configs. + + my $valid = "A-Za-z_0-9"; + + my @configs; + + while ($dep =~ /[$valid]/) { + + if ($dep =~ /^[^$valid]*([$valid]+)/) { + my $conf = "CONFIG_" . $1; + + $configs[$#configs + 1] = $conf; + + $dep =~ s/^[^$valid]*[$valid]+//; + } else { + die "this should never happen"; + } + } + + return @configs; +} + +my %min_configs; +my %keep_configs; +my %save_configs; +my %processed_configs; +my %nochange_config; + +sub test_this_config { + my ($config) = @_; + + my $found; + + # if we already processed this config, skip it + if (defined($processed_configs{$config})) { + return undef; + } + $processed_configs{$config} = 1; + + # if this config failed during this round, skip it + if (defined($nochange_config{$config})) { + return undef; + } + + my $kconfig = chomp_config $config; + + # Test dependencies first + if (defined($depends{"$kconfig"})) { + my @parents = get_depends $config; + foreach my $parent (@parents) { + # if the parent is in the min config, check it first + next if (!defined($min_configs{$parent})); + $found = test_this_config($parent); + if (defined($found)) { + return $found; + } + } + } + + # Remove this config from the list of configs + # do a make olddefconfig and then read the resulting + # .config to make sure it is missing the config that + # we had before + my %configs = %min_configs; + delete $configs{$config}; + make_new_config ((values %configs), (values %keep_configs)); + make_oldconfig; + undef %configs; + assign_configs \%configs, $output_config; + + return $config if (!defined($configs{$config})); + + doprint "disabling config $config did not change .config\n"; + + $nochange_config{$config} = 1; + + return undef; +} + +sub make_min_config { + my ($i) = @_; + + my $type = $minconfig_type; + if ($type ne "boot" && $type ne "test") { + fail "Invalid MIN_CONFIG_TYPE '$minconfig_type'\n" . + " make_min_config works only with 'boot' and 'test'\n" and return; + } + + if (!defined($output_minconfig)) { + fail "OUTPUT_MIN_CONFIG not defined" and return; + } + + # If output_minconfig exists, and the start_minconfig + # came from min_config, than ask if we should use + # that instead. + if (-f $output_minconfig && !$start_minconfig_defined) { + print "$output_minconfig exists\n"; + if (!defined($use_output_minconfig)) { + if (read_yn " Use it as minconfig?") { + $start_minconfig = $output_minconfig; + } + } elsif ($use_output_minconfig > 0) { + doprint "Using $output_minconfig as MIN_CONFIG\n"; + $start_minconfig = $output_minconfig; + } else { + doprint "Set to still use MIN_CONFIG as starting point\n"; + } + } + + if (!defined($start_minconfig)) { + fail "START_MIN_CONFIG or MIN_CONFIG not defined" and return; + } + + my $temp_config = "$tmpdir/temp_config"; + + # First things first. We build an allnoconfig to find + # out what the defaults are that we can't touch. + # Some are selections, but we really can't handle selections. + + my $save_minconfig = $minconfig; + undef $minconfig; + + run_command "$make allnoconfig" or return 0; + + read_depends; + + process_config_ignore $output_config; + + undef %save_configs; + undef %min_configs; + + if (defined($ignore_config)) { + # make sure the file exists + `touch $ignore_config`; + assign_configs \%save_configs, $ignore_config; + } + + %keep_configs = %save_configs; + + doprint "Load initial configs from $start_minconfig\n"; + + # Look at the current min configs, and save off all the + # ones that were set via the allnoconfig + assign_configs \%min_configs, $start_minconfig; + + my @config_keys = keys %min_configs; + + # All configs need a depcount + foreach my $config (@config_keys) { + my $kconfig = chomp_config $config; + if (!defined $depcount{$kconfig}) { + $depcount{$kconfig} = 0; + } + } + + # Remove anything that was set by the make allnoconfig + # we shouldn't need them as they get set for us anyway. + foreach my $config (@config_keys) { + # Remove anything in the ignore_config + if (defined($keep_configs{$config})) { + my $file = $ignore_config; + $file =~ s,.*/(.*?)$,$1,; + doprint "$config set by $file ... ignored\n"; + delete $min_configs{$config}; + next; + } + # But make sure the settings are the same. If a min config + # sets a selection, we do not want to get rid of it if + # it is not the same as what we have. Just move it into + # the keep configs. + if (defined($config_ignore{$config})) { + if ($config_ignore{$config} ne $min_configs{$config}) { + doprint "$config is in allnoconfig as '$config_ignore{$config}'"; + doprint " but it is '$min_configs{$config}' in minconfig .. keeping\n"; + $keep_configs{$config} = $min_configs{$config}; + } else { + doprint "$config set by allnoconfig ... ignored\n"; + } + delete $min_configs{$config}; + } + } + + my $done = 0; + my $take_two = 0; + + while (!$done) { + + my $config; + my $found; + + # Now disable each config one by one and do a make oldconfig + # till we find a config that changes our list. + + my @test_configs = keys %min_configs; + + # Sort keys by who is most dependent on + @test_configs = sort { $depcount{chomp_config($b)} <=> $depcount{chomp_config($a)} } + @test_configs ; + + # Put configs that did not modify the config at the end. + my $reset = 1; + for (my $i = 0; $i < $#test_configs; $i++) { + if (!defined($nochange_config{$test_configs[0]})) { + $reset = 0; + last; + } + # This config didn't change the .config last time. + # Place it at the end + my $config = shift @test_configs; + push @test_configs, $config; + } + + # if every test config has failed to modify the .config file + # in the past, then reset and start over. + if ($reset) { + undef %nochange_config; + } + + undef %processed_configs; + + foreach my $config (@test_configs) { + + $found = test_this_config $config; + + last if (defined($found)); + + # oh well, try another config + } + + if (!defined($found)) { + # we could have failed due to the nochange_config hash + # reset and try again + if (!$take_two) { + undef %nochange_config; + $take_two = 1; + next; + } + doprint "No more configs found that we can disable\n"; + $done = 1; + last; + } + $take_two = 0; + + $config = $found; + + doprint "Test with $config disabled\n"; + + # set in_bisect to keep build and monitor from dieing + $in_bisect = 1; + + my $failed = 0; + build "oldconfig" or $failed = 1; + if (!$failed) { + start_monitor_and_boot or $failed = 1; + + if ($type eq "test" && !$failed) { + do_run_test or $failed = 1; + } + + end_monitor; + } + + $in_bisect = 0; + + if ($failed) { + doprint "$min_configs{$config} is needed to boot the box... keeping\n"; + # this config is needed, add it to the ignore list. + $keep_configs{$config} = $min_configs{$config}; + $save_configs{$config} = $min_configs{$config}; + delete $min_configs{$config}; + + # update new ignore configs + if (defined($ignore_config)) { + open (OUT, ">$temp_config") + or die "Can't write to $temp_config"; + foreach my $config (keys %save_configs) { + print OUT "$save_configs{$config}\n"; + } + close OUT; + run_command "mv $temp_config $ignore_config" or + dodie "failed to copy update to $ignore_config"; + } + + } else { + # We booted without this config, remove it from the minconfigs. + doprint "$config is not needed, disabling\n"; + + delete $min_configs{$config}; + + # Also disable anything that is not enabled in this config + my %configs; + assign_configs \%configs, $output_config; + my @config_keys = keys %min_configs; + foreach my $config (@config_keys) { + if (!defined($configs{$config})) { + doprint "$config is not set, disabling\n"; + delete $min_configs{$config}; + } + } + + # Save off all the current mandidory configs + open (OUT, ">$temp_config") + or die "Can't write to $temp_config"; + foreach my $config (keys %keep_configs) { + print OUT "$keep_configs{$config}\n"; + } + foreach my $config (keys %min_configs) { + print OUT "$min_configs{$config}\n"; + } + close OUT; + + run_command "mv $temp_config $output_minconfig" or + dodie "failed to copy update to $output_minconfig"; + } + + doprint "Reboot and wait $sleep_time seconds\n"; + reboot_to_good $sleep_time; + } + + success $i; + return 1; +} + +sub make_warnings_file { + my ($i) = @_; + + if (!defined($warnings_file)) { + dodie "Must define WARNINGS_FILE for make_warnings_file test"; + } + + if ($build_type eq "nobuild") { + dodie "BUILD_TYPE can not be 'nobuild' for make_warnings_file test"; + } + + build $build_type or dodie "Failed to build"; + + open(OUT, ">$warnings_file") or dodie "Can't create $warnings_file"; + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while (<IN>) { + + # Some compilers use UTF-8 extended for quotes + # for distcc heterogeneous systems, this causes issues + s/$utf8_quote/'/g; + + if (/$check_build_re/) { + print OUT; + } + } + close(IN); + + close(OUT); + + success $i; +} + $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; if ($#ARGV == 0) { $ktest_config = $ARGV[0]; if (! -f $ktest_config) { print "$ktest_config does not exist.\n"; - my $ans; - for (;;) { - print "Create it? [Y/n] "; - $ans = <STDIN>; - chomp $ans; - if ($ans =~ /^\s*$/) { - $ans = "y"; - } - last if ($ans =~ /^y$/i || $ans =~ /^n$/i); - print "Please answer either 'y' or 'n'.\n"; - } - if ($ans !~ /^y$/i) { + if (!read_yn "Create it?") { exit 0; } } @@ -1791,13 +3878,27 @@ if ($#ARGV == 0) { } if (! -f $ktest_config) { + $newconfig = 1; + get_test_case; open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; print OUT << "EOF" # Generated by ktest.pl # + +# PWD is a ktest.pl variable that will result in the process working +# directory that ktest.pl is executed in. + +# THIS_DIR is automatically assigned the PWD of the path that generated +# the config file. It is best to use this variable when assigning other +# directory paths within this directory. This allows you to easily +# move the test cases to other locations or to other machines. +# +THIS_DIR := $variable{"PWD"} + # Define each test with TEST_START # The config options below it will override the defaults TEST_START +TEST_TYPE = $default{"TEST_TYPE"} DEFAULTS EOF @@ -1806,6 +3907,10 @@ EOF } read_config $ktest_config; +if (defined($opt{"LOG_FILE"})) { + $opt{"LOG_FILE"} = eval_option("LOG_FILE", $opt{"LOG_FILE"}, -1); +} + # Append any configs entered in manually to the config file. my @new_configs = keys %entered_configs; if ($#new_configs >= 0) { @@ -1813,7 +3918,7 @@ if ($#new_configs >= 0) { open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config"; foreach my $config (@new_configs) { print OUT "$config = $entered_configs{$config}\n"; - $opt{$config} = $entered_configs{$config}; + $opt{$config} = process_variables($entered_configs{$config}); } } @@ -1848,7 +3953,7 @@ for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) { } } -sub set_test_option { +sub __set_test_option { my ($name, $i) = @_; my $option = "$name\[$i\]"; @@ -1874,54 +3979,66 @@ sub set_test_option { return undef; } +sub set_test_option { + my ($name, $i) = @_; + + my $option = __set_test_option($name, $i); + return $option if (!defined($option)); + + return eval_option($name, $option, $i); +} + # First we need to do is the builds for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { + # Do not reboot on failing test options + $no_reboot = 1; + $reboot_success = 0; + + $have_version = 0; + $iteration = $i; + undef %force_config; + my $makecmd = set_test_option("MAKE_CMD", $i); - $machine = set_test_option("MACHINE", $i); - $ssh_user = set_test_option("SSH_USER", $i); - $tmpdir = set_test_option("TMP_DIR", $i); $outputdir = set_test_option("OUTPUT_DIR", $i); $builddir = set_test_option("BUILD_DIR", $i); - $test_type = set_test_option("TEST_TYPE", $i); - $build_type = set_test_option("BUILD_TYPE", $i); - $build_options = set_test_option("BUILD_OPTIONS", $i); - $power_cycle = set_test_option("POWER_CYCLE", $i); - $reboot = set_test_option("REBOOT", $i); - $noclean = set_test_option("BUILD_NOCLEAN", $i); - $minconfig = set_test_option("MIN_CONFIG", $i); - $run_test = set_test_option("TEST", $i); - $addconfig = set_test_option("ADD_CONFIG", $i); - $reboot_type = set_test_option("REBOOT_TYPE", $i); - $grub_menu = set_test_option("GRUB_MENU", $i); - $post_install = set_test_option("POST_INSTALL", $i); - $reboot_script = set_test_option("REBOOT_SCRIPT", $i); - $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i); - $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i); - $die_on_failure = set_test_option("DIE_ON_FAILURE", $i); - $power_off = set_test_option("POWER_OFF", $i); - $powercycle_after_reboot = set_test_option("POWERCYCLE_AFTER_REBOOT", $i); - $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i); - $sleep_time = set_test_option("SLEEP_TIME", $i); - $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); - $store_failures = set_test_option("STORE_FAILURES", $i); - $timeout = set_test_option("TIMEOUT", $i); - $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); - $console = set_test_option("CONSOLE", $i); - $success_line = set_test_option("SUCCESS_LINE", $i); - $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); - $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); - $build_target = set_test_option("BUILD_TARGET", $i); - $ssh_exec = set_test_option("SSH_EXEC", $i); - $scp_to_target = set_test_option("SCP_TO_TARGET", $i); - $target_image = set_test_option("TARGET_IMAGE", $i); - $localversion = set_test_option("LOCALVERSION", $i); chdir $builddir || die "can't change directory to $builddir"; + if (!-d $outputdir) { + mkpath($outputdir) or + die "can't create $outputdir"; + } + + $make = "$makecmd O=$outputdir"; + + # Load all the options into their mapped variable names + foreach my $opt (keys %option_map) { + ${$option_map{$opt}} = set_test_option($opt, $i); + } + + $start_minconfig_defined = 1; + + # The first test may override the PRE_KTEST option + if (defined($pre_ktest) && $i == 1) { + doprint "\n"; + run_command $pre_ktest; + } + + # Any test can override the POST_KTEST option + # The last test takes precedence. + if (defined($post_ktest)) { + $final_post_ktest = $post_ktest; + } + + if (!defined($start_minconfig)) { + $start_minconfig_defined = 0; + $start_minconfig = $minconfig; + } + if (!-d $tmpdir) { mkpath($tmpdir) or die "can't create $tmpdir"; @@ -1930,26 +4047,34 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $ENV{"SSH_USER"} = $ssh_user; $ENV{"MACHINE"} = $machine; - $target = "$ssh_user\@$machine"; - $buildlog = "$tmpdir/buildlog-$machine"; + $testlog = "$tmpdir/testlog-$machine"; $dmesg = "$tmpdir/dmesg-$machine"; - $make = "$makecmd O=$outputdir"; $output_config = "$outputdir/.config"; - if ($reboot_type eq "grub") { - dodie "GRUB_MENU not defined" if (!defined($grub_menu)); - } elsif (!defined($reboot_script)) { - dodie "REBOOT_SCRIPT not defined" + if (!$buildonly) { + $target = "$ssh_user\@$machine"; + if ($reboot_type eq "grub") { + dodie "GRUB_MENU not defined" if (!defined($grub_menu)); + } elsif ($reboot_type eq "grub2") { + dodie "GRUB_MENU not defined" if (!defined($grub_menu)); + dodie "GRUB_FILE not defined" if (!defined($grub_file)); + } elsif ($reboot_type eq "syslinux") { + dodie "SYSLINUX_LABEL not defined" if (!defined($syslinux_label)); + } } my $run_type = $build_type; if ($test_type eq "patchcheck") { - $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; + $run_type = $patchcheck_type; } elsif ($test_type eq "bisect") { - $run_type = $opt{"BISECT_TYPE[$i]"}; + $run_type = $bisect_type; } elsif ($test_type eq "config_bisect") { - $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; + $run_type = $config_bisect_type; + } elsif ($test_type eq "make_min_config") { + $run_type = ""; + } elsif ($test_type eq "make_warnings_file") { + $run_type = ""; } # mistake in config file? @@ -1957,27 +4082,42 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $run_type = "ERROR"; } + my $installme = ""; + $installme = " no_install" if ($no_install); + doprint "\n\n"; - doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type$installme\n\n"; + + if (defined($pre_test)) { + run_command $pre_test; + } unlink $dmesg; unlink $buildlog; + unlink $testlog; - if (!defined($minconfig)) { - $minconfig = $addconfig; - - } elsif (defined($addconfig)) { - run_command "cat $addconfig $minconfig > $tmpdir/add_config" or + if (defined($addconfig)) { + my $min = $minconfig; + if (!defined($minconfig)) { + $min = ""; + } + run_command "cat $addconfig $min > $tmpdir/add_config" or dodie "Failed to create temp config"; $minconfig = "$tmpdir/add_config"; } - my $checkout = $opt{"CHECKOUT[$i]"}; if (defined($checkout)) { run_command "git checkout $checkout" or die "failed to checkout $checkout"; } + $no_reboot = 0; + + # A test may opt to not reboot the box + if ($reboot_on_success) { + $reboot_success = 1; + } + if ($test_type eq "bisect") { bisect $i; next; @@ -1987,20 +4127,30 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { } elsif ($test_type eq "patchcheck") { patchcheck $i; next; + } elsif ($test_type eq "make_min_config") { + make_min_config $i; + next; + } elsif ($test_type eq "make_warnings_file") { + $no_reboot = 1; + make_warnings_file $i; + next; } if ($build_type ne "nobuild") { build $build_type or next; + check_buildlog or next; } - if ($test_type ne "build") { - get_grub_index; + if ($test_type eq "install") { get_version; install; + success $i; + next; + } + if ($test_type ne "build") { my $failed = 0; - start_monitor; - monitor or $failed = 1;; + start_monitor_and_boot or $failed = 1; if (!$failed && $test_type ne "boot" && defined($run_test)) { do_run_test or $failed = 1; @@ -2012,12 +4162,20 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { success $i; } +if (defined($final_post_ktest)) { + run_command $final_post_ktest; +} + if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { - reboot; +} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot && $reboot_success) { + reboot_to_good; +} elsif (defined($switch_to_good)) { + # still need to get to the good kernel + run_command $switch_to_good; } + doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; exit 0; diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 3408c594b2d..172eec4517f 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -72,7 +72,218 @@ # the same option name under the same test or as default # ktest will fail to execute, and no tests will run. # +# DEFAULTS OVERRIDE +# +# Options defined in the DEFAULTS section can not be duplicated +# even if they are defined in two different DEFAULT sections. +# This is done to catch mistakes where an option is added but +# the previous option was forgotten about and not commented. +# +# The OVERRIDE keyword can be added to a section to allow this +# section to override other DEFAULT sections values that have +# been defined previously. It will only override options that +# have been defined before its use. Options defined later +# in a non override section will still error. The same option +# can not be defined in the same section even if that section +# is marked OVERRIDE. +# +# +# +# Both TEST_START and DEFAULTS sections can also have the IF keyword +# The value after the IF must evaluate into a 0 or non 0 positive +# integer, and can use the config variables (explained below). +# +# DEFAULTS IF ${IS_X86_32} +# +# The above will process the DEFAULTS section if the config +# variable IS_X86_32 evaluates to a non zero positive integer +# otherwise if it evaluates to zero, it will act the same +# as if the SKIP keyword was used. +# +# The ELSE keyword can be used directly after a section with +# a IF statement. +# +# TEST_START IF ${RUN_NET_TESTS} +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-network +# +# ELSE +# +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-normal +# +# +# The ELSE keyword can also contain an IF statement to allow multiple +# if then else sections. But all the sections must be either +# DEFAULT or TEST_START, they can not be a mixture. +# +# TEST_START IF ${RUN_NET_TESTS} +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-network +# +# ELSE IF ${RUN_DISK_TESTS} +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-tests +# +# ELSE IF ${RUN_CPU_TESTS} +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-cpu +# +# ELSE +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-network +# +# The if statement may also have comparisons that will and for +# == and !=, strings may be used for both sides. +# +# BOX_TYPE := x86_32 +# +# DEFAULTS IF ${BOX_TYPE} == x86_32 +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-32 +# ELSE +# BUILD_TYPE = useconfig:${CONFIG_DIR}/config-64 +# +# The DEFINED keyword can be used by the IF statements too. +# It returns true if the given config variable or option has been defined +# or false otherwise. +# +# +# DEFAULTS IF DEFINED USE_CC +# CC := ${USE_CC} +# ELSE +# CC := gcc +# +# +# As well as NOT DEFINED. +# +# DEFAULTS IF NOT DEFINED MAKE_CMD +# MAKE_CMD := make ARCH=x86 +# +# +# And/or ops (&&,||) may also be used to make complex conditionals. +# +# TEST_START IF (DEFINED ALL_TESTS || ${MYTEST} == boottest) && ${MACHINE} == gandalf +# +# Notice the use of parentheses. Without any parentheses the above would be +# processed the same as: +# +# TEST_START IF DEFINED ALL_TESTS || (${MYTEST} == boottest && ${MACHINE} == gandalf) +# +# +# +# INCLUDE file +# +# The INCLUDE keyword may be used in DEFAULT sections. This will +# read another config file and process that file as well. The included +# file can include other files, add new test cases or default +# statements. Config variables will be passed to these files and changes +# to config variables will be seen by top level config files. Including +# a file is processed just like the contents of the file was cut and pasted +# into the top level file, except, that include files that end with +# TEST_START sections will have that section ended at the end of +# the include file. That is, an included file is included followed +# by another DEFAULT keyword. +# +# Unlike other files referenced in this config, the file path does not need +# to be absolute. If the file does not start with '/', then the directory +# that the current config file was located in is used. If no config by the +# given name is found there, then the current directory is searched. +# +# INCLUDE myfile +# DEFAULT +# +# is the same as: +# +# INCLUDE myfile +# +# Note, if the include file does not contain a full path, the file is +# searched first by the location of the original include file, and then +# by the location that ktest.pl was executed in. +# + +#### Config variables #### +# +# This config file can also contain "config variables". +# These are assigned with ":=" instead of the ktest option +# assigment "=". +# +# The difference between ktest options and config variables +# is that config variables can be used multiple times, +# where each instance will override the previous instance. +# And that they only live at time of processing this config. +# +# The advantage to config variables are that they can be used +# by any option or any other config variables to define thing +# that you may use over and over again in the options. +# +# For example: +# +# USER := root +# TARGET := mybox +# TEST_CASE := ssh ${USER}@${TARGET} /path/to/my/test +# +# TEST_START +# MIN_CONFIG = config1 +# TEST = ${TEST_CASE} +# +# TEST_START +# MIN_CONFIG = config2 +# TEST = ${TEST_CASE} +# +# TEST_CASE := ssh ${USER}@${TARGET} /path/to/my/test2 +# +# TEST_START +# MIN_CONFIG = config1 +# TEST = ${TEST_CASE} +# +# TEST_START +# MIN_CONFIG = config2 +# TEST = ${TEST_CASE} +# +# TEST_DIR := /home/me/test +# +# BUILD_DIR = ${TEST_DIR}/linux.git +# OUTPUT_DIR = ${TEST_DIR}/test +# +# Note, the config variables are evaluated immediately, thus +# updating TARGET after TEST_CASE has been assigned does nothing +# to TEST_CASE. +# +# As shown in the example, to evaluate a config variable, you +# use the ${X} convention. Simple $X will not work. +# +# If the config variable does not exist, the ${X} will not +# be evaluated. Thus: +# +# MAKE_CMD = PATH=/mypath:${PATH} make +# +# If PATH is not a config variable, then the ${PATH} in +# the MAKE_CMD option will be evaluated by the shell when +# the MAKE_CMD option is passed into shell processing. +#### Using options in other options #### +# +# Options that are defined in the config file may also be used +# by other options. All options are evaulated at time of +# use (except that config variables are evaluated at config +# processing time). +# +# If an ktest option is used within another option, instead of +# typing it again in that option you can simply use the option +# just like you can config variables. +# +# MACHINE = mybox +# +# TEST = ssh root@${MACHINE} /path/to/test +# +# The option will be used per test case. Thus: +# +# TEST_TYPE = test +# TEST = ssh root@{MACHINE} +# +# TEST_START +# MACHINE = box1 +# +# TEST_START +# MACHINE = box2 +# +# For both test cases, MACHINE will be evaluated at the time +# of the test case. The first test will run ssh root@box1 +# and the second will run ssh root@box2. #### Mandatory Default Options #### @@ -117,12 +328,29 @@ # For a virtual machine with guest name "Guest". #CONSOLE = virsh console Guest +# Signal to send to kill console. +# ktest.pl will create a child process to monitor the console. +# When the console is finished, ktest will kill the child process +# with this signal. +# (default INT) +#CLOSE_CONSOLE_SIGNAL = HUP + # Required version ending to differentiate the test # from other linux builds on the system. #LOCALVERSION = -test +# For REBOOT_TYPE = grub2, you must specify where the grub.cfg +# file is. This is the file that is searched to find the menu +# option to boot to with GRUB_REBOOT +#GRUB_FILE = /boot/grub2/grub.cfg + +# The tool for REBOOT_TYPE = grub2 to set the next reboot kernel +# to boot into (one shot mode). +# (default grub2_reboot) +#GRUB_REBOOT = grub2_reboot + # The grub title name for the test kernel to boot -# (Only mandatory if REBOOT_TYPE = grub) +# (Only mandatory if REBOOT_TYPE = grub or grub2) # # Note, ktest.pl will not update the grub menu.lst, you need to # manually add an option for the test. ktest.pl will search @@ -132,10 +360,38 @@ # For example, if in the /boot/grub/menu.lst the test kernel title has: # title Test Kernel # kernel vmlinuz-test +# +# For grub2, a search of top level "menuentry"s are done. No +# submenu is searched. The menu is found by searching for the +# contents of GRUB_MENU in the line that starts with "menuentry". +# You may want to include the quotes around the option. For example: +# for: menuentry 'Test Kernel' +# do a: GRUB_MENU = 'Test Kernel' +# For customizing, add your entry in /etc/grub.d/40_custom. +# #GRUB_MENU = Test Kernel +# For REBOOT_TYPE = syslinux, the name of the syslinux executable +# (on the target) to use to set up the next reboot to boot the +# test kernel. +# (default extlinux) +#SYSLINUX = syslinux + +# For REBOOT_TYPE = syslinux, the path that is passed to to the +# syslinux command where syslinux is installed. +# (default /boot/extlinux) +#SYSLINUX_PATH = /boot/syslinux + +# For REBOOT_TYPE = syslinux, the syslinux label that references the +# test kernel in the syslinux config file. +# (default undefined) +#SYSLINUX_LABEL = "test-kernel" + # A script to reboot the target into the test kernel -# (Only mandatory if REBOOT_TYPE = script) +# This and SWITCH_TO_TEST are about the same, except +# SWITCH_TO_TEST is run even for REBOOT_TYPE = grub. +# This may be left undefined. +# (default undefined) #REBOOT_SCRIPT = #### Optional Config Options (all have defaults) #### @@ -162,11 +418,30 @@ # DEFAULTS # DEFAULTS SKIP +# If you want to execute some command before the first test runs +# you can set this option. Note, it can be set as a default option +# or an option in the first test case. All other test cases will +# ignore it. If both the default and first test have this option +# set, then the first test will take precedence. +# +# default (undefined) +#PRE_KTEST = ${SSH} ~/set_up_test + +# If you want to execute some command after all the tests have +# completed, you can set this option. Note, it can be set as a +# default or any test case can override it. If multiple test cases +# set this option, then the last test case that set it will take +# precedence +# +# default (undefined) +#POST_KTEST = ${SSH} ~/dismantle_test + # The default test type (default test) # The test types may be: -# build - only build the kernel, do nothing else -# boot - build and boot the kernel -# test - build, boot and if TEST is set, run the test script +# build - only build the kernel, do nothing else +# install - build and install, but do nothing else (does not reboot) +# boot - build, install, and boot the kernel +# test - build, boot and if TEST is set, run the test script # (If TEST is not set, it defaults back to boot) # bisect - Perform a bisect on the kernel (see BISECT_TYPE below) # patchcheck - Do a test on a series of commits in git (see PATCHCHECK below) @@ -193,6 +468,14 @@ # (default "") #BUILD_OPTIONS = -j20 +# If you need to do some special handling before installing +# you can add a script with this option. +# The environment variable KERNEL_VERSION will be set to the +# kernel version that is used. +# +# default (undefined) +#PRE_INSTALL = ssh user@target rm -rf '/lib/modules/*-test*' + # If you need an initrd, you can add a script or code here to install # it. The environment variable KERNEL_VERSION will be set to the # kernel version that is used. Remember to add the initrd line @@ -204,8 +487,59 @@ # or on some systems: #POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION +# If for some reason you just want to boot the kernel and you do not +# want the test to install anything new. For example, you may just want +# to boot test the same kernel over and over and do not want to go through +# the hassle of installing anything, you can set this option to 1 +# (default 0) +#NO_INSTALL = 1 + +# If there is a command that you want to run before the individual test +# case executes, then you can set this option +# +# default (undefined) +#PRE_TEST = ${SSH} reboot_to_special_kernel + +# If there is a command you want to run after the individual test case +# completes, then you can set this option. +# +# default (undefined) +#POST_TEST = cd ${BUILD_DIR}; git reset --hard + +# If there is a script that you require to run before the build is done +# you can specify it with PRE_BUILD. +# +# One example may be if you must add a temporary patch to the build to +# fix a unrelated bug to perform a patchcheck test. This will apply the +# patch before each build that is made. Use the POST_BUILD to do a git reset --hard +# to remove the patch. +# +# (default undef) +#PRE_BUILD = cd ${BUILD_DIR} && patch -p1 < /tmp/temp.patch + +# To specify if the test should fail if the PRE_BUILD fails, +# PRE_BUILD_DIE needs to be set to 1. Otherwise the PRE_BUILD +# result is ignored. +# (default 0) +# PRE_BUILD_DIE = 1 + +# If there is a script that should run after the build is done +# you can specify it with POST_BUILD. +# +# As the example in PRE_BUILD, POST_BUILD can be used to reset modifications +# made by the PRE_BUILD. +# +# (default undef) +#POST_BUILD = cd ${BUILD_DIR} && git reset --hard + +# To specify if the test should fail if the POST_BUILD fails, +# POST_BUILD_DIE needs to be set to 1. Otherwise the POST_BUILD +# result is ignored. +# (default 0) +#POST_BUILD_DIE = 1 + # Way to reboot the box to the test kernel. -# Only valid options so far are "grub" and "script" +# Only valid options so far are "grub", "grub2", "syslinux" and "script" # (default grub) # If you specify grub, it will assume grub version 1 # and will search in /boot/grub/menu.lst for the title $GRUB_MENU @@ -213,10 +547,38 @@ # your setup, then specify "script" and have a command or script # specified in REBOOT_SCRIPT to boot to the target. # +# For REBOOT_TYPE = grub2, you must define both GRUB_MENU and +# GRUB_FILE. +# +# For REBOOT_TYPE = syslinux, you must define SYSLINUX_LABEL, and +# perhaps modify SYSLINUX (default extlinux) and SYSLINUX_PATH +# (default /boot/extlinux) +# # The entry in /boot/grub/menu.lst must be entered in manually. # The test will not modify that file. #REBOOT_TYPE = grub +# If you are using a machine that doesn't boot with grub, and +# perhaps gets its kernel from a remote server (tftp), then +# you can use this option to update the target image with the +# test image. +# +# You could also do the same with POST_INSTALL, but the difference +# between that option and this option is that POST_INSTALL runs +# after the install, where this one runs just before a reboot. +# (default undefined) +#SWITCH_TO_TEST = cp ${OUTPUT_DIR}/${BUILD_TARGET} ${TARGET_IMAGE} + +# If you are using a machine that doesn't boot with grub, and +# perhaps gets its kernel from a remote server (tftp), then +# you can use this option to update the target image with the +# the known good image to reboot safely back into. +# +# This option holds a command that will execute before needing +# to reboot to a good known image. +# (default undefined) +#SWITCH_TO_GOOD = ssh ${SSH_USER}/${MACHINE} cp good_image ${TARGET_IMAGE} + # The min config that is needed to build for the machine # A nice way to create this is with the following: # @@ -271,8 +633,8 @@ #ADD_CONFIG = /home/test/config-broken # The location on the host where to write temp files -# (default /tmp/ktest) -#TMP_DIR = /tmp/ktest +# (default /tmp/ktest/${MACHINE}) +#TMP_DIR = /tmp/ktest/${MACHINE} # Optional log file to write the status (recommended) # Note, this is a DEFAULT section only option. @@ -294,6 +656,14 @@ # (default "login:") #SUCCESS_LINE = login: +# To speed up between reboots, defining a line that the +# default kernel produces that represents that the default +# kernel has successfully booted and can be used to pass +# a new test kernel to it. Otherwise ktest.pl will wait till +# SLEEP_TIME to continue. +# (default undefined) +#REBOOT_SUCCESS_LINE = login: + # In case the console constantly fills the screen, having # a specified time to stop the test after success is recommended. # (in seconds) @@ -306,6 +676,14 @@ # (default 60) #STOP_AFTER_FAILURE = 60 +# In case the console constantly fills the screen, having +# a specified time to stop the test if it never succeeds nor fails +# is recommended. +# Note: this is ignored if a success or failure is detected. +# (in seconds) +# (default 600, -1 is to never stop) +#STOP_TEST_AFTER = 600 + # Stop testing if a build fails. If set, the script will end if # a failure is detected, otherwise it will save off the .config, # dmesg and bootlog in a directory called @@ -322,6 +700,12 @@ # (default undefined) #STORE_FAILURES = /home/test/failures +# Directory to store success directories on success. If this is not +# set, the .config, dmesg and bootlog will not be saved if a +# test succeeds. +# (default undefined) +#STORE_SUCCESSES = /home/test/successes + # Build without doing a make mrproper, or removing .config # (default 0) #BUILD_NOCLEAN = 0 @@ -351,6 +735,8 @@ # another test. If a reboot to the reliable kernel happens, # we wait SLEEP_TIME for the console to stop producing output # before starting the next test. +# +# You can speed up reboot times even more by setting REBOOT_SUCCESS_LINE. # (default 60) #SLEEP_TIME = 60 @@ -358,6 +744,18 @@ # (default 60) #BISECT_SLEEP_TIME = 60 +# The max wait time (in seconds) for waiting for the console to finish. +# If for some reason, the console is outputting content without +# ever finishing, this will cause ktest to get stuck. This +# option is the max time ktest will wait for the monitor (console) +# to settle down before continuing. +# (default 1800) +#MAX_MONITOR_WAIT + +# The time in between patch checks to sleep (in seconds) +# (default 60) +#PATCHCHECK_SLEEP_TIME = 60 + # Reboot the target box on error (default 0) #REBOOT_ON_ERROR = 0 @@ -402,21 +800,72 @@ # Example for a virtual guest call "Guest". #POWER_OFF = virsh destroy Guest +# To have the build fail on "new" warnings, create a file that +# contains a list of all known warnings (they must match exactly +# to the line with 'warning:', 'error:' or 'Error:'. If the option +# WARNINGS_FILE is set, then that file will be read, and if the +# build detects a warning, it will examine this file and if the +# warning does not exist in it, it will fail the build. +# +# Note, if this option is defined to a file that does not exist +# then any warning will fail the build. +# (see make_warnings_file below) +# +# (optional, default undefined) +#WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file + # The way to execute a command on the target # (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) # The variables SSH_USER, MACHINE and SSH_COMMAND are defined #SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND"; -# The way to copy a file to the target +# The way to copy a file to the target (install and modules) # (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE) -# The variables SSH_USER, MACHINE, SRC_FILE and DST_FILE are defined. -#SCP_TO_TARGET = scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE +# The variables SSH_USER, MACHINE are defined by the config +# SRC_FILE and DST_FILE are ktest internal variables and +# should only have '$' and not the '${}' notation. +# (default scp $SRC_FILE ${SSH_USER}@${MACHINE}:$DST_FILE) +#SCP_TO_TARGET = echo skip scp for $SRC_FILE $DST_FILE + +# If install needs to be different than modules, then this +# option will override the SCP_TO_TARGET for installation. +# (default ${SCP_TO_TARGET} ) +#SCP_TO_TARGET_INSTALL = scp $SRC_FILE tftp@tftpserver:$DST_FILE # The nice way to reboot the target # (default ssh $SSH_USER@$MACHINE reboot) # The variables SSH_USER and MACHINE are defined. #REBOOT = ssh $SSH_USER@$MACHINE reboot +# The way triple faults are detected is by testing the kernel +# banner. If the kernel banner for the kernel we are testing is +# found, and then later a kernel banner for another kernel version +# is found, it is considered that we encountered a triple fault, +# and there is no panic or callback, but simply a reboot. +# To disable this (because it did a false positive) set the following +# to 0. +# (default 1) +#DETECT_TRIPLE_FAULT = 0 + +# All options in the config file should be either used by ktest +# or could be used within a value of another option. If an option +# in the config file is not used, ktest will warn about it and ask +# if you want to continue. +# +# If you don't care if there are non-used options, enable this +# option. Be careful though, a non-used option is usually a sign +# of an option name being typed incorrectly. +# (default 0) +#IGNORE_UNUSED = 1 + +# When testing a kernel that happens to have WARNINGs, and call +# traces, ktest.pl will detect these and fail a boot or test run +# due to warnings. By setting this option, ktest will ignore +# call traces, and will not fail a test if the kernel produces +# an oops. Use this option with care. +# (default 0) +#IGNORE_ERRORS = 1 + #### Per test run options #### # The following options are only allowed in TEST_START sections. # They are ignored in the DEFAULTS sections. @@ -434,6 +883,12 @@ # all preceding tests until a new CHECKOUT is set. # # +# TEST_NAME = name +# +# If you want the test to have a name that is displayed in +# the test result banner at the end of the test, then use this +# option. This is useful to search for the RESULT keyword and +# not have to translate a test number to a test in the config. # # For TEST_TYPE = patchcheck # @@ -455,7 +910,12 @@ # build, boot, test. # # Note, the build test will look for warnings, if a warning occurred -# in a file that a commit touches, the build will fail. +# in a file that a commit touches, the build will fail, unless +# IGNORE_WARNINGS is set for the given commit's sha1 +# +# IGNORE_WARNINGS can be used to disable the failure of patchcheck +# on a particuler commit (SHA1). You can add more than one commit +# by adding a list of SHA1s that are space delimited. # # If BUILD_NOCLEAN is set, then make mrproper will not be run on # any of the builds, just like all other TEST_TYPE tests. But @@ -470,6 +930,7 @@ # PATCHCHECK_TYPE = boot # PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 # PATCHCHECK_END = HEAD~2 +# IGNORE_WARNINGS = 42f9c6b69b54946ffc0515f57d01dc7f5c0e4712 0c17ca2c7187f431d8ffc79e81addc730f33d128 # # # @@ -519,6 +980,24 @@ # git bisect good, git bisect bad, and running the git bisect replay # if the BISECT_REPLAY is set. # +# BISECT_SKIP = 1 (optional, default 0) +# +# If BISECT_TYPE is set to test but the build fails, ktest will +# simply fail the test and end their. You could use BISECT_REPLAY +# and BISECT_START to resume after you found a new starting point, +# or you could set BISECT_SKIP to 1. If BISECT_SKIP is set to 1, +# when something other than the BISECT_TYPE fails, ktest.pl will +# run "git bisect skip" and try again. +# +# BISECT_FILES = <path> (optional, default undefined) +# +# To just run the git bisect on a specific path, set BISECT_FILES. +# For example: +# +# BISECT_FILES = arch/x86 kernel/time +# +# Will run the bisect with "git bisect start -- arch/x86 kernel/time" +# # BISECT_REVERSE = 1 (optional, default 0) # # In those strange instances where it was broken forever @@ -528,6 +1007,15 @@ # With BISECT_REVERSE = 1, The test will consider failures as # good, and success as bad. # +# BISECT_MANUAL = 1 (optional, default 0) +# +# In case there's a problem with automating the bisect for +# whatever reason. (Can't reboot, want to inspect each iteration) +# Doing a BISECT_MANUAL will have the test wait for you to +# tell it if the test passed or failed after each iteration. +# This is basicall the same as running git bisect yourself +# but ktest will rebuild and install the kernel for you. +# # BISECT_CHECK = 1 (optional, default 0) # # Just to be sure the good is good and bad is bad, setting @@ -540,6 +1028,56 @@ # BISECT_BAD with BISECT_CHECK = good or # BISECT_CHECK = bad, respectively. # +# BISECT_TRIES = 5 (optional, default 1) +# +# For those cases that it takes several tries to hit a bug, +# the BISECT_TRIES is useful. It is the number of times the +# test is ran before it says the kernel is good. The first failure +# will stop trying and mark the current SHA1 as bad. +# +# Note, as with all race bugs, there's no guarantee that if +# it succeeds, it is really a good bisect. But it helps in case +# the bug is some what reliable. +# +# You can set BISECT_TRIES to zero, and all tests will be considered +# good, unless you also set BISECT_MANUAL. +# +# BISECT_RET_GOOD = 0 (optional, default undefined) +# +# In case the specificed test returns something other than just +# 0 for good, and non-zero for bad, you can override 0 being +# good by defining BISECT_RET_GOOD. +# +# BISECT_RET_BAD = 1 (optional, default undefined) +# +# In case the specificed test returns something other than just +# 0 for good, and non-zero for bad, you can override non-zero being +# bad by defining BISECT_RET_BAD. +# +# BISECT_RET_ABORT = 255 (optional, default undefined) +# +# If you need to abort the bisect if the test discovers something +# that was wrong, you can define BISECT_RET_ABORT to be the error +# code returned by the test in order to abort the bisect. +# +# BISECT_RET_SKIP = 2 (optional, default undefined) +# +# If the test detects that the current commit is neither good +# nor bad, but something else happened (another bug detected) +# you can specify BISECT_RET_SKIP to an error code that the +# test returns when it should skip the current commit. +# +# BISECT_RET_DEFAULT = good (optional, default undefined) +# +# You can override the default of what to do when the above +# options are not hit. This may be one of, "good", "bad", +# "abort" or "skip" (without the quotes). +# +# Note, if you do not define any of the previous BISECT_RET_* +# and define BISECT_RET_DEFAULT, all bisects results will do +# what the BISECT_RET_DEFAULT has. +# +# # Example: # TEST_START # TEST_TYPE = bisect @@ -611,12 +1149,141 @@ # boot - bad builds but fails to boot # test - bad boots but fails a test # -# CONFIG_BISECT is the config that failed to boot +# CONFIG_BISECT is the config that failed to boot +# +# If BISECT_MANUAL is set, it will pause between iterations. +# This is useful to use just ktest.pl just for the config bisect. +# If you set it to build, it will run the bisect and you can +# control what happens in between iterations. It will ask you if +# the test succeeded or not and continue the config bisect. +# +# CONFIG_BISECT_GOOD (optional) +# If you have a good config to start with, then you +# can specify it with CONFIG_BISECT_GOOD. Otherwise +# the MIN_CONFIG is the base. +# +# CONFIG_BISECT_CHECK (optional) +# Set this to 1 if you want to confirm that the config ktest +# generates (the bad config with the min config) is still bad. +# It may be that the min config fixes what broke the bad config +# and the test will not return a result. # # Example: # TEST_START # TEST_TYPE = config_bisect # CONFIG_BISECT_TYPE = build -# CONFIG_BISECT = /home/test/˘onfig-bad +# CONFIG_BISECT = /home/test/config-bad # MIN_CONFIG = /home/test/config-min +# BISECT_MANUAL = 1 +# +# +# +# For TEST_TYPE = make_min_config +# +# After doing a make localyesconfig, your kernel configuration may +# not be the most useful minimum configuration. Having a true minimum +# config that you can use against other configs is very useful if +# someone else has a config that breaks on your code. By only forcing +# those configurations that are truly required to boot your machine +# will give you less of a chance that one of your set configurations +# will make the bug go away. This will give you a better chance to +# be able to reproduce the reported bug matching the broken config. +# +# Note, this does take some time, and may require you to run the +# test over night, or perhaps over the weekend. But it also allows +# you to interrupt it, and gives you the current minimum config +# that was found till that time. +# +# Note, this test automatically assumes a BUILD_TYPE of oldconfig +# and its test type acts like boot. +# TODO: add a test version that makes the config do more than just +# boot, like having network access. +# +# To save time, the test does not just grab any option and test +# it. The Kconfig files are examined to determine the dependencies +# of the configs. If a config is chosen that depends on another +# config, that config will be checked first. By checking the +# parents first, we can eliminate whole groups of configs that +# may have been enabled. +# +# For example, if a USB device config is chosen and depends on CONFIG_USB, +# the CONFIG_USB will be tested before the device. If CONFIG_USB is +# found not to be needed, it, as well as all configs that depend on +# it, will be disabled and removed from the current min_config. +# +# OUTPUT_MIN_CONFIG is the path and filename of the file that will +# be created from the MIN_CONFIG. If you interrupt the test, set +# this file as your new min config, and use it to continue the test. +# This file does not need to exist on start of test. +# This file is not created until a config is found that can be removed. +# If this file exists, you will be prompted if you want to use it +# as the min_config (overriding MIN_CONFIG) if START_MIN_CONFIG +# is not defined. +# (required field) +# +# START_MIN_CONFIG is the config to use to start the test with. +# you can set this as the same OUTPUT_MIN_CONFIG, but if you do +# the OUTPUT_MIN_CONFIG file must exist. +# (default MIN_CONFIG) +# +# IGNORE_CONFIG is used to specify a config file that has configs that +# you already know must be set. Configs are written here that have +# been tested and proved to be required. It is best to define this +# file if you intend on interrupting the test and running it where +# it left off. New configs that it finds will be written to this file +# and will not be tested again in later runs. +# (optional) +# +# MIN_CONFIG_TYPE can be either 'boot' or 'test'. With 'boot' it will +# test if the created config can just boot the machine. If this is +# set to 'test', then the TEST option must be defined and the created +# config will not only boot the target, but also make sure that the +# config lets the test succeed. This is useful to make sure the final +# config that is generated allows network activity (ssh). +# (optional) +# +# USE_OUTPUT_MIN_CONFIG set this to 1 if you do not want to be prompted +# about using the OUTPUT_MIN_CONFIG as the MIN_CONFIG as the starting +# point. Set it to 0 if you want to always just use the given MIN_CONFIG. +# If it is not defined, it will prompt you to pick which config +# to start with (MIN_CONFIG or OUTPUT_MIN_CONFIG). +# +# Example: +# +# TEST_TYPE = make_min_config +# OUTPUT_MIN_CONFIG = /path/to/config-new-min +# START_MIN_CONFIG = /path/to/config-min +# IGNORE_CONFIG = /path/to/config-tested +# MIN_CONFIG_TYPE = test +# TEST = ssh ${USER}@${MACHINE} echo hi +# +# +# +# +# For TEST_TYPE = make_warnings_file +# +# If you want the build to fail when a new warning is discovered +# you set the WARNINGS_FILE to point to a file of known warnings. +# +# The test "make_warnings_file" will let you create a new warnings +# file before you run other tests, like patchcheck. +# +# What this test does is to run just a build, you still need to +# specify BUILD_TYPE to tell the test what type of config to use. +# A BUILD_TYPE of nobuild will fail this test. +# +# The test will do the build and scan for all warnings. Any warning +# it discovers will be saved in the WARNINGS_FILE (required) option. +# +# It is recommended (but not necessary) to make sure BUILD_NOCLEAN is +# off, so that a full build is done (make mrproper is performed). +# That way, all warnings will be captured. +# +# Example: +# +# TEST_TYPE = make_warnings_file +# WARNINGS_FILE = ${OUTPUT_DIR} +# BUILD_TYPE = useconfig:oldconfig +# CHECKOUT = v3.8 +# BUILD_NOCLEAN = 0 # |
