Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-ktest
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-ktest:
ktest: Fix bug when ADD_CONFIG is set but MIN_CONFIG is not
ktest: Keep fonud configs separate from default configs
ktest: Add prompt to use OUTPUT_MIN_CONFIG
ktest: Use Kconfig dependencies to shorten time to make min_config
ktest: Add test type make_min_config
ktest: Require one TEST_START in config file
ktest: Add helper function to avoid duplicate code
ktest: Add IGNORE_WARNINGS to ignore warnings in some patches
ktest: Fix tar extracting of modules to target
ktest: Have the testing tmp dir include machine name
ktest: Add POST/PRE_BUILD options
ktest: Allow initrd processing without modules defined
ktest: Have LOG_FILE evaluate options as well
ktest: Have wait on stdio honor bug timeout
ktest: Implement our own force min config
ktest: Add TEST_NAME option
ktest: Add CONFIG_BISECT_GOOD option
ktest: Add detection of triple faults
ktest: Notify reason to break out of monitoring boot
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index cef28e6..8d02ccb 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -27,7 +27,7 @@
$default{"BUILD_TYPE"} = "randconfig";
$default{"MAKE_CMD"} = "make";
$default{"TIMEOUT"} = 120;
-$default{"TMP_DIR"} = "/tmp/ktest";
+$default{"TMP_DIR"} = "/tmp/ktest/\${MACHINE}";
$default{"SLEEP_TIME"} = 60; # sleep time between tests
$default{"BUILD_NOCLEAN"} = 0;
$default{"REBOOT_ON_ERROR"} = 0;
@@ -41,6 +41,7 @@
$default{"BISECT_MANUAL"} = 0;
$default{"BISECT_SKIP"} = 1;
$default{"SUCCESS_LINE"} = "login:";
+$default{"DETECT_TRIPLE_FAULT"} = 1;
$default{"BOOTED_TIMEOUT"} = 1;
$default{"DIE_ON_FAILURE"} = 1;
$default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND";
@@ -62,6 +63,10 @@
my $test_type;
my $build_type;
my $build_options;
+my $pre_build;
+my $post_build;
+my $pre_build_die;
+my $post_build_die;
my $reboot_type;
my $reboot_script;
my $power_cycle;
@@ -81,12 +86,17 @@
my $post_install;
my $noclean;
my $minconfig;
+my $start_minconfig;
+my $start_minconfig_defined;
+my $output_minconfig;
+my $ignore_config;
my $addconfig;
my $in_bisect = 0;
my $bisect_bad = "";
my $reverse_bisect;
my $bisect_manual;
my $bisect_skip;
+my $config_bisect_good;
my $in_patchcheck = 0;
my $run_test;
my $redirect;
@@ -98,9 +108,12 @@
my $sleep_time;
my $bisect_sleep_time;
my $patchcheck_sleep_time;
+my $ignore_warnings;
my $store_failures;
+my $test_name;
my $timeout;
my $booted_timeout;
+my $detect_triplefault;
my $console;
my $success_line;
my $stop_after_success;
@@ -115,6 +128,7 @@
my %entered_configs;
my %config_help;
my %variable;
+my %force_config;
$config_help{"MACHINE"} = << "EOF"
The machine hostname that you will test.
@@ -204,6 +218,26 @@
EOF
;
+sub read_yn {
+ my ($prompt) = @_;
+
+ my $ans;
+
+ for (;;) {
+ print "$prompt [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) {
+ return 0;
+ }
+ return 1;
+}
sub get_ktest_config {
my ($config) = @_;
@@ -335,6 +369,7 @@
my $num_tests_set = 0;
my $skip = 0;
my $rest;
+ my $test_case = 0;
while (<IN>) {
@@ -360,6 +395,7 @@
$rest = $1;
$skip = 1;
} else {
+ $test_case = 1;
$skip = 0;
}
@@ -464,6 +500,15 @@
# make sure we have all mandatory configs
get_ktest_configs;
+ # was a test specified?
+ if (!$test_case) {
+ print "No test case specified.\n";
+ print "What test case would you like to run?\n";
+ my $ans = <STDIN>;
+ chomp $ans;
+ $default{"TEST_TYPE"} = $ans;
+ }
+
# set any defaults
foreach my $default (keys %default) {
@@ -473,6 +518,69 @@
}
}
+sub __eval_option {
+ my ($option, $i) = @_;
+
+ # Add space to evaluate the character before $
+ $option = " $option";
+ my $retval = "";
+
+ 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\]";
+
+ if (defined($opt{$o})) {
+ $o = $opt{$o};
+ $retval = "$retval$o";
+ } elsif (defined($opt{$var})) {
+ $o = $opt{$var};
+ $retval = "$retval$o";
+ } else {
+ $retval = "$retval\$\{$var\}";
+ }
+
+ $option = $end;
+ }
+
+ $retval = "$retval$option";
+
+ $retval =~ s/^ //;
+
+ return $retval;
+}
+
+sub eval_option {
+ my ($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($option, $i);
+ }
+
+ return $option;
+}
+
sub _logit {
if (defined($opt{"LOG_FILE"})) {
open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
@@ -617,9 +725,15 @@
end_monitor;
}
+ my $name = "";
+
+ if (defined($test_name)) {
+ $name = " ($test_name)";
+ }
+
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
- doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n";
+ doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n";
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
@@ -836,17 +950,35 @@
my $failure_start;
my $monitor_start = time;
my $done = 0;
+ my $version_found = 0;
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;
@@ -896,6 +1028,22 @@
$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 "Aleady 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 = "";
}
@@ -923,6 +1071,16 @@
return 1;
}
+sub do_post_install {
+
+ return if (!defined($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 install {
run_scp "$outputdir/$build_target", "$target_image" or
@@ -942,6 +1100,7 @@
close(IN);
if (!$install_mods) {
+ do_post_install;
doprint "No modules needed\n";
return;
}
@@ -964,17 +1123,29 @@
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
+ doprint "$make kernelrelease ... ";
+ $version = `$make kernelrelease | tail -1`;
+ chomp($version);
+ doprint "$version\n";
+}
+
+sub start_monitor_and_boot {
+ get_grub_index;
+ get_version;
+ install;
+
+ start_monitor;
+ return monitor;
}
sub check_buildlog {
@@ -1009,24 +1180,84 @@
return 1;
}
-sub make_oldconfig {
- my ($defconfig) = @_;
+sub apply_min_config {
+ my $outconfig = "$output_config.new";
- if (!run_command "$defconfig $make oldnoconfig") {
+ # 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 oldnoconfig") {
# Perhaps oldnoconfig doesn't exist in this version of the kernel
# try a yes '' | oldconfig
doprint "oldnoconfig failed, trying yes '' | make oldconfig\n";
- run_command "yes '' | $defconfig $make oldconfig" or
+ 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) = @_;
+
+ 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;
+ 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";
@@ -1063,24 +1294,33 @@
close(OUT);
if (defined($minconfig)) {
- $defconfig = "KCONFIG_ALLCONFIG=$minconfig";
+ load_force_config($minconfig);
}
- if ($type eq "oldnoconfig") {
- make_oldconfig $defconfig;
- } else {
- run_command "$defconfig $make $type" or
+ if ($type ne "oldnoconfig") {
+ 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)) {
+ 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);
fail "failed build" and return 0;
}
- undef $redirect;
return 1;
}
@@ -1102,9 +1342,15 @@
$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";
@@ -1117,14 +1363,6 @@
}
}
-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]";
@@ -1289,12 +1527,7 @@
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) {
@@ -1473,21 +1706,27 @@
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*)=.*)/) {
- $config_ignore{$2} = $1;
+ ${$hash}{$2} = $1;
}
}
close(IN);
}
+sub process_config_ignore {
+ my ($config) = @_;
+
+ assign_configs \%config_ignore, $config;
+}
+
sub read_current_config {
my ($config_ref) = @_;
@@ -1546,7 +1785,7 @@
close(OUT);
# exit;
- make_oldconfig "";
+ make_oldconfig;
}
sub compare_configs {
@@ -1718,6 +1957,10 @@
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
@@ -1727,15 +1970,8 @@
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;
}
@@ -1755,8 +1991,8 @@
}
close(IN);
- # Now run oldconfig with the minconfig (and addconfigs)
- make_oldconfig $defconfig;
+ # Now run oldconfig with the minconfig
+ make_oldconfig;
# check to see what we lost (or gained)
open (IN, $output_config)
@@ -1882,6 +2118,13 @@
@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) {
@@ -1908,18 +2151,16 @@
build "oldconfig" or return 0;
}
- check_buildlog $sha1 or return 0;
+
+ if (!defined($ignored_warnings{$sha1})) {
+ check_buildlog $sha1 or return 0;
+ }
next if ($type eq "build");
- get_grub_index;
- get_version;
- install;
-
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;
@@ -1936,24 +2177,505 @@
return 1;
}
+my %depends;
+my $iflevel = 0;
+my @ifdeps;
+
+# prevent recursion
+my %read_kconfigs;
+
+# 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++) {
+ if ($i) {
+ $depends{$config} .= " " . $ifdeps[$i];
+ } else {
+ $depends{$config} = $ifdeps[$i];
+ }
+ $state = "DEP";
+ }
+
+ # collect the depends for the config
+ } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) {
+
+ if (defined($depends{$1})) {
+ $depends{$config} .= " " . $1;
+ } else {
+ $depends{$config} = $1;
+ }
+
+ # Get the configs that select this config
+ } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) {
+ if (defined($depends{$1})) {
+ $depends{$1} .= " " . $config;
+ } else {
+ $depends{$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 get_depends {
+ my ($dep) = @_;
+
+ my $kconfig = $dep;
+ $kconfig =~ s/CONFIG_//;
+
+ $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 = $config;
+ $kconfig =~ s/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 oldnoconfig 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) = @_;
+
+ 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 (read_yn " Use it as minconfig?") {
+ $start_minconfig = $output_minconfig;
+ }
+ }
+
+ 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;
+
+ # 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.
+
+ # Put configs that did not modify the config at the end.
+ my @test_configs = keys %min_configs;
+ 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";
+ start_monitor_and_boot 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;
+ start_monitor;
+ wait_for_monitor $sleep_time;
+ end_monitor;
+ }
+
+ success $i;
+ return 1;
+}
+
$#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;
}
}
@@ -1977,6 +2699,10 @@
}
read_config $ktest_config;
+if (defined($opt{"LOG_FILE"})) {
+ $opt{"LOG_FILE"} = eval_option($opt{"LOG_FILE"}, -1);
+}
+
# Append any configs entered in manually to the config file.
my @new_configs = keys %entered_configs;
if ($#new_configs >= 0) {
@@ -2045,70 +2771,13 @@
return undef;
}
-sub eval_option {
- my ($option, $i) = @_;
-
- # Add space to evaluate the character before $
- $option = " $option";
- my $retval = "";
-
- 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\]";
-
- if (defined($opt{$o})) {
- $o = $opt{$o};
- $retval = "$retval$o";
- } elsif (defined($opt{$var})) {
- $o = $opt{$var};
- $retval = "$retval$o";
- } else {
- $retval = "$retval\$\{$var\}";
- }
-
- $option = $end;
- }
-
- $retval = "$retval$option";
-
- $retval =~ s/^ //;
-
- return $retval;
-}
-
sub set_test_option {
my ($name, $i) = @_;
my $option = __set_test_option($name, $i);
return $option if (!defined($option));
- 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 $name\n" .
- "Check for recursive variables\n";
- }
- $prev = $option;
- $option = eval_option($option, $i);
- }
-
- return $option;
+ return eval_option($option, $i);
}
# First we need to do is the builds
@@ -2126,10 +2795,17 @@
$test_type = set_test_option("TEST_TYPE", $i);
$build_type = set_test_option("BUILD_TYPE", $i);
$build_options = set_test_option("BUILD_OPTIONS", $i);
+ $pre_build = set_test_option("PRE_BUILD", $i);
+ $post_build = set_test_option("POST_BUILD", $i);
+ $pre_build_die = set_test_option("PRE_BUILD_DIE", $i);
+ $post_build_die = set_test_option("POST_BUILD_DIE", $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);
+ $output_minconfig = set_test_option("OUTPUT_MIN_CONFIG", $i);
+ $start_minconfig = set_test_option("START_MIN_CONFIG", $i);
+ $ignore_config = set_test_option("IGNORE_CONFIG", $i);
$run_test = set_test_option("TEST", $i);
$addconfig = set_test_option("ADD_CONFIG", $i);
$reboot_type = set_test_option("REBOOT_TYPE", $i);
@@ -2145,12 +2821,16 @@
$sleep_time = set_test_option("SLEEP_TIME", $i);
$bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i);
$patchcheck_sleep_time = set_test_option("PATCHCHECK_SLEEP_TIME", $i);
+ $ignore_warnings = set_test_option("IGNORE_WARNINGS", $i);
$bisect_manual = set_test_option("BISECT_MANUAL", $i);
$bisect_skip = set_test_option("BISECT_SKIP", $i);
+ $config_bisect_good = set_test_option("CONFIG_BISECT_GOOD", $i);
$store_failures = set_test_option("STORE_FAILURES", $i);
+ $test_name = set_test_option("TEST_NAME", $i);
$timeout = set_test_option("TIMEOUT", $i);
$booted_timeout = set_test_option("BOOTED_TIMEOUT", $i);
$console = set_test_option("CONSOLE", $i);
+ $detect_triplefault = set_test_option("DETECT_TRIPLE_FAULT", $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);
@@ -2161,6 +2841,13 @@
$target_image = set_test_option("TARGET_IMAGE", $i);
$localversion = set_test_option("LOCALVERSION", $i);
+ $start_minconfig_defined = 1;
+
+ if (!defined($start_minconfig)) {
+ $start_minconfig_defined = 0;
+ $start_minconfig = $minconfig;
+ }
+
chdir $builddir || die "can't change directory to $builddir";
if (!-d $tmpdir) {
@@ -2193,6 +2880,10 @@
$run_type = $opt{"CONFIG_BISECT_TYPE[$i]"};
}
+ if ($test_type eq "make_min_config") {
+ $run_type = "";
+ }
+
# mistake in config file?
if (!defined($run_type)) {
$run_type = "ERROR";
@@ -2204,11 +2895,12 @@
unlink $dmesg;
unlink $buildlog;
- 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";
}
@@ -2228,6 +2920,9 @@
} elsif ($test_type eq "patchcheck") {
patchcheck $i;
next;
+ } elsif ($test_type eq "make_min_config") {
+ make_min_config $i;
+ next;
}
if ($build_type ne "nobuild") {
@@ -2235,13 +2930,8 @@
}
if ($test_type ne "build") {
- get_grub_index;
- get_version;
- install;
-
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;
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
index 48cbcc80..b8bcd14 100644
--- a/tools/testing/ktest/sample.conf
+++ b/tools/testing/ktest/sample.conf
@@ -293,6 +293,38 @@
# or on some systems:
#POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION
+# 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"
# (default grub)
@@ -360,8 +392,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.
@@ -518,6 +550,16 @@
# 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
+
#### Per test run options ####
# The following options are only allowed in TEST_START sections.
# They are ignored in the DEFAULTS sections.
@@ -535,6 +577,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
#
@@ -556,7 +604,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
@@ -571,6 +624,7 @@
# PATCHCHECK_TYPE = boot
# PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7
# PATCHCHECK_END = HEAD~2
+# IGNORE_WARNINGS = 42f9c6b69b54946ffc0515f57d01dc7f5c0e4712 0c17ca2c7187f431d8ffc79e81addc730f33d128
#
#
#
@@ -739,13 +793,18 @@
# 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.
+# 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.
#
# Example:
# TEST_START
@@ -755,3 +814,68 @@
# 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)
+#
+# 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
+#