From 2545eb6198e7e1ec50daa0cfc64a4cdfecf24ec9 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 15:01:32 -0400 Subject: [PATCH 01/30] Initial start of ktest.pl Originally named autotest.pl, but renamed to ktest.pl now because the autotest name is used by other projects. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 334 +++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 tools/testing/ktest/ktest.pl diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl new file mode 100644 index 000000000000..81fb2e431058 --- /dev/null +++ b/tools/testing/ktest/ktest.pl @@ -0,0 +1,334 @@ +#!/usr/bin/perl -w + +use strict; +use IPC::Open2; +use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); +use FileHandle; + +$#ARGV >= 0 || die "usage: autotest.pl config-file\n"; + +$| = 1; + +my %opt; + +#default opts +$opt{"NUM_BUILDS"} = 5; +$opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; +$opt{"MAKE_CMD"} = "make"; +$opt{"TIMEOUT"} = 50; +$opt{"TMP_DIR"} = "/tmp/autotest"; +$opt{"SLEEP_TIME"} = 60; # sleep time between tests + +my $version; +my $install_mods; +my $grub_number; +my $target; +my $make; + +sub read_config { + my ($config) = @_; + + open(IN, $config) || die "can't read file $config"; + + while () { + + # ignore blank lines and comments + next if (/^\s*$/ || /\s*\#/); + + if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) { + my $lvalue = $1; + my $rvalue = $2; + + $opt{$lvalue} = $rvalue; + } + } + + close(IN); +} + +sub doprint { + print @_; + + if (defined($opt{"LOG_FILE"})) { + open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; + print OUT @_; + close(OUT); + } +} + +sub run_command { + my ($command) = @_; + my $redirect = ""; + + if (defined($opt{"LOG_FILE"})) { + $redirect = " >> $opt{LOG_FILE} 2>&1"; + } + + doprint "$command ... "; + `$command $redirect`; + + my $failed = $?; + + if ($failed) { + doprint "FAILED!\n"; + } else { + doprint "SUCCESS\n"; + } + + return $failed; +} + +my $timeout = $opt{"TIMEOUT"}; + +sub wait_for_input +{ + my ($fp, $time) = @_; + my $rin; + my $ready; + my $line; + my $ch; + + if (!defined($time)) { + $time = $timeout; + } + + $rin = ''; + vec($rin, fileno($fp), 1) = 1; + $ready = select($rin, undef, undef, $time); + + $line = ""; + + # try to read one char at a time + while (sysread $fp, $ch, 1) { + $line .= $ch; + last if ($ch eq "\n"); + } + + if (!length($line)) { + return undef; + } + + return $line; +} + +sub reboot { + run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; +} + +sub monitor { + my $flags; + my $booted = 0; + my $bug = 0; + my $pid; + my $doopen2 = 0; + + if ($doopen2) { + $pid = open2(\*IN, \*OUT, $opt{CONSOLE}); + if ($pid < 0) { + die "Failed to connect to the console"; + } + } else { + $pid = open(IN, "$opt{CONSOLE} |"); + } + + $flags = fcntl(IN, F_GETFL, 0) or + die "Can't get flags for the socket: $!\n"; + + $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or + die "Can't set flags for the socket: $!\n"; + + my $line; + my $full_line = ""; + + doprint "Wait for monitor to settle down.\n"; + # read the monitor and wait for the system to calm down + do { + $line = wait_for_input(\*IN, 5); + } while (defined($line)); + + reboot; + + for (;;) { + + $line = wait_for_input(\*IN); + + last if (!defined($line)); + + doprint $line; + + # we are not guaranteed to get a full line + $full_line .= $line; + + if ($full_line =~ /login:/) { + $booted = 1; + } + + if ($full_line =~ /call trace:/i) { + $bug = 1; + } + + if ($line =~ /\n/) { + $full_line = ""; + } + } + + doprint "kill child process $pid\n"; + kill 2, $pid; + + print "closing!\n"; + close(IN); + + if (!$booted) { + die "failed - never got a boot prompt.\n"; + } + + if ($bug) { + die "failed - got a bug report\n"; + } +} + +sub install { + + if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") { + die "failed to copy image"; + } + + if ($install_mods) { + my $modlib = "/lib/modules/$version"; + + if (run_command "ssh $target rm -rf $modlib") { + die "failed to remove old mods: $modlib"; + } + + if (run_command "scp -r $opt{TMP_DIR}/lib $target:/lib/modules/$version") { + die "failed to copy modules"; + } + } + +} + +sub build { + my ($type) = @_; + + unlink "$opt{OUTPUT_DIR}/.config"; + + run_command "$make mrproper"; + + # add something to distinguish this build + open(OUT, "> $opt{OUTPUT_DIR}/localversion") or die("Can't make localversion file"); + print OUT "$opt{LOCALVERSION}\n"; + close(OUT); + + if (run_command "$make $opt{$type}") { + die "failed make config"; + } + + if (defined($opt{"MIN_CONFIG"})) { + run_command "cat $opt{MIN_CONFIG} >> $opt{OUTPUT_DIR}/.config"; + run_command "yes '' | $make oldconfig"; + } + + if (run_command "$make $opt{BUILD_OPTIONS}") { + die "failed build"; + } +} + +read_config $ARGV[0]; + +# mandatory configs +die "MACHINE not defined\n" if (!defined($opt{"MACHINE"})); +die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); +die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); +die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); +die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); +die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); +die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); +die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); +die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"})); + +chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; + +$target = "$opt{SSH_USER}\@$opt{MACHINE}"; + +doprint "\n\nSTARTING AUTOMATED TESTS\n"; + +doprint "Find grub menu ... "; +$grub_number = -1; +open(IN, "ssh $target cat /boot/grub/menu.lst |") + or die "unable to get menu.lst"; +while () { + if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { + $grub_number++; + last; + } elsif (/^\s*title\s/) { + $grub_number++; + } +} +close(IN); +die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" + if ($grub_number < 0); +doprint "$grub_number\n"; + +$make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; + +# First we need to do is the builds +for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { + my $type = "BUILD_TYPE[$i]"; + + if (!defined($opt{$type})) { + $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; + } + + doprint "\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + + if ($opt{$type} ne "nobuild") { + build $type; + } + + # get the release name + doprint "$make kernelrelease ... "; + $version = `$make kernelrelease | tail -1`; + chomp($version); + doprint "$version\n"; + + # should we process modules? + $install_mods = 0; + open(IN, "$opt{OUTPUT_DIR}/.config") or die("Can't read config file"); + while () { + if (/CONFIG_MODULES(=y)?/) { + $install_mods = 1 if (defined($1)); + last; + } + } + close(IN); + + if ($install_mods) { + if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") { + die "Failed to install modules"; + } + } else { + doprint "No modules needed\n"; + } + + install; + + monitor; + + doprint "\n\n*******************************************\n"; + doprint "*******************************************\n"; + doprint "** SUCCESS!!!! **\n"; + doprint "*******************************************\n"; + doprint "*******************************************\n"; + + # try to reboot normally + + if (run_command "ssh $target reboot") { + # nope? power cycle it. + run_command "$opt{POWER_CYCLE}"; + } + + sleep "$opt{SLEEP_TIME}"; +} + +exit 0; From 5c42fc5b975869e73bb8b6c279dd2da81eab5607 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:01 -0400 Subject: [PATCH 02/30] ktest: New features: noclean, dodie, poweroff on error and success Added dodie function to have a bit more control over die calls. BUILD_NOCLEAN to not run make mrproper or remove .config. POWEROFF_ON_{SUCCESS,ERROR} to turn off the power after tests. Skip backtrace calls that were done by the backtrace tests. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 115 ++++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 21 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 81fb2e431058..1acb0e1ab0f4 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -18,12 +18,16 @@ $opt{"MAKE_CMD"} = "make"; $opt{"TIMEOUT"} = 50; $opt{"TMP_DIR"} = "/tmp/autotest"; $opt{"SLEEP_TIME"} = 60; # sleep time between tests +$opt{"BUILD_NOCLEAN"} = 0; +$opt{"POWEROFF_ON_ERROR"} = 0; +$opt{"POWEROFF_ON_SUCCESS"} = 0; my $version; my $install_mods; my $grub_number; my $target; my $make; +my $noclean; sub read_config { my ($config) = @_; @@ -56,6 +60,16 @@ sub doprint { } } +sub dodie { + doprint "CRITICAL FAILURE... ", @_; + + if ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { + doprint "POWERING OFF\n"; + `$opt{"POWER_OFF"}`; + } + die @_; +} + sub run_command { my ($command) = @_; my $redirect = ""; @@ -121,21 +135,22 @@ sub monitor { my $bug = 0; my $pid; my $doopen2 = 0; + my $skip_call_trace = 0; if ($doopen2) { $pid = open2(\*IN, \*OUT, $opt{CONSOLE}); if ($pid < 0) { - die "Failed to connect to the console"; + dodie "Failed to connect to the console"; } } else { $pid = open(IN, "$opt{CONSOLE} |"); } $flags = fcntl(IN, F_GETFL, 0) or - die "Can't get flags for the socket: $!\n"; + dodie "Can't get flags for the socket: $!\n"; $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or - die "Can't set flags for the socket: $!\n"; + dodie "Can't set flags for the socket: $!\n"; my $line; my $full_line = ""; @@ -163,7 +178,19 @@ sub monitor { $booted = 1; } + if ($full_line =~ /\[ backtrace testing \]/) { + $skip_call_trace = 1; + } + if ($full_line =~ /call trace:/i) { + $bug = 1 if (!$skip_call_trace); + } + + if ($full_line =~ /\[ end of backtrace testing \]/) { + $skip_call_trace = 0; + } + + if ($full_line =~ /Kernel panic -/) { $bug = 1; } @@ -179,57 +206,90 @@ sub monitor { close(IN); if (!$booted) { - die "failed - never got a boot prompt.\n"; + dodie "failed - never got a boot prompt.\n"; } if ($bug) { - die "failed - got a bug report\n"; + dodie "failed - got a bug report\n"; } } sub install { if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") { - die "failed to copy image"; + dodie "failed to copy image"; } if ($install_mods) { my $modlib = "/lib/modules/$version"; + my $modtar = "autotest-mods.tar.bz2"; if (run_command "ssh $target rm -rf $modlib") { - die "failed to remove old mods: $modlib"; + dodie "failed to remove old mods: $modlib"; } - if (run_command "scp -r $opt{TMP_DIR}/lib $target:/lib/modules/$version") { - die "failed to copy modules"; + # would be nice if scp -r did not follow symbolic links + if (run_command "cd $opt{TMP_DIR}; tar -cjf $modtar lib/modules/$version") { + dodie "making tarball"; } + + if (run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp") { + dodie "failed to copy modules"; + } + + unlink "$opt{TMP_DIR}/$modtar"; + + if (run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'") { + dodie "failed to tar modules"; + } + + run_command "ssh $target rm -f /tmp/$modtar"; } } sub build { my ($type) = @_; + my $defconfig = ""; + my $append = ""; - unlink "$opt{OUTPUT_DIR}/.config"; + # old config can ask questions + if ($type eq "oldconfig") { + $append = "yes ''|"; + if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") { + dodie "moving .config"; + } - run_command "$make mrproper"; + if (!$noclean && run_command "$make mrproper") { + dodie "make mrproper"; + } + + if (run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config") { + dodie "moving config_temp"; + } + + } elsif (!$noclean) { + unlink "$opt{OUTPUT_DIR}/.config"; + if (run_command "$make mrproper") { + dodie "make mrproper"; + } + } # add something to distinguish this build - open(OUT, "> $opt{OUTPUT_DIR}/localversion") or die("Can't make localversion file"); + open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file"); print OUT "$opt{LOCALVERSION}\n"; close(OUT); - if (run_command "$make $opt{$type}") { - die "failed make config"; + if (defined($opt{"MIN_CONFIG"})) { + $defconfig = "KCONFIG_ALLCONFIG=$opt{MIN_CONFIG}"; } - if (defined($opt{"MIN_CONFIG"})) { - run_command "cat $opt{MIN_CONFIG} >> $opt{OUTPUT_DIR}/.config"; - run_command "yes '' | $make oldconfig"; + if (run_command "$defconfig $append $make $type") { + dodie "failed make config"; } if (run_command "$make $opt{BUILD_OPTIONS}") { - die "failed build"; + dodie "failed build"; } } @@ -275,6 +335,13 @@ $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { my $type = "BUILD_TYPE[$i]"; + if (defined($opt{"BUILD_NOCLEAN[$i]"}) && + $opt{"BUILD_NOCLEAN[$i]"} != 0) { + $noclean = 1; + } else { + $noclean = $opt{"BUILD_NOCLEAN"}; + } + if (!defined($opt{$type})) { $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; } @@ -283,7 +350,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; if ($opt{$type} ne "nobuild") { - build $type; + build $opt{$type}; } # get the release name @@ -294,7 +361,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { # should we process modules? $install_mods = 0; - open(IN, "$opt{OUTPUT_DIR}/.config") or die("Can't read config file"); + open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); while () { if (/CONFIG_MODULES(=y)?/) { $install_mods = 1 if (defined($1)); @@ -305,7 +372,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { if ($install_mods) { if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") { - die "Failed to install modules"; + dodie "Failed to install modules"; } } else { doprint "No modules needed\n"; @@ -331,4 +398,10 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { sleep "$opt{SLEEP_TIME}"; } +if ($opt{"POWEROFF_ON_SUCCESS"}) { + if (run_command "ssh $target halt" && defined($opt{"POWER_OFF"})) { + # nope? the zap it! + run_command "$opt{POWER_OFF}"; + } +} exit 0; From 75c3fda79e97b1e2c390e3623f19476e1e78ca0c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:21 -0400 Subject: [PATCH 03/30] ktest: New features reboot on error, make options REBOOT_ON_ERROR to reboot the box on error BUILD_OPTIONS to add options to the make build (like -j40) Added "useconfig:". Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 62 ++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 1acb0e1ab0f4..79da57f3023b 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -19,8 +19,10 @@ $opt{"TIMEOUT"} = 50; $opt{"TMP_DIR"} = "/tmp/autotest"; $opt{"SLEEP_TIME"} = 60; # sleep time between tests $opt{"BUILD_NOCLEAN"} = 0; +$opt{"REBOOT_ON_ERROR"} = 0; $opt{"POWEROFF_ON_ERROR"} = 0; $opt{"POWEROFF_ON_SUCCESS"} = 0; +$opt{"BUILD_OPTIONS"} = ""; my $version; my $install_mods; @@ -63,10 +65,15 @@ sub doprint { sub dodie { doprint "CRITICAL FAILURE... ", @_; - if ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { + if ($opt{"REBOOT_ON_ERROR"}) { + doprint "REBOOTING\n"; + `$opt{"POWER_CYCLE"}`; + + } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { doprint "POWERING OFF\n"; `$opt{"POWER_OFF"}`; } + die @_; } @@ -125,7 +132,7 @@ sub wait_for_input return $line; } -sub reboot { +sub reboot_to { run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; } @@ -138,7 +145,7 @@ sub monitor { my $skip_call_trace = 0; if ($doopen2) { - $pid = open2(\*IN, \*OUT, $opt{CONSOLE}); + $pid = open2(\*IN, \*OUT, $opt{"CONSOLE"}); if ($pid < 0) { dodie "Failed to connect to the console"; } @@ -161,7 +168,7 @@ sub monitor { $line = wait_for_input(\*IN, 5); } while (defined($line)); - reboot; + reboot_to; for (;;) { @@ -229,7 +236,7 @@ sub install { } # would be nice if scp -r did not follow symbolic links - if (run_command "cd $opt{TMP_DIR}; tar -cjf $modtar lib/modules/$version") { + if (run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version") { dodie "making tarball"; } @@ -253,9 +260,20 @@ sub build { my $defconfig = ""; my $append = ""; + if ($type =~ /^useconfig:(.*)/) { + if (run_command "cp $1 $opt{OUTPUT_DIR}/.config") { + dodie "could not copy $1 to .config"; + } + $type = "oldconfig"; + } + # old config can ask questions if ($type eq "oldconfig") { $append = "yes ''|"; + + # allow for empty configs + run_command "touch $opt{OUTPUT_DIR}/.config"; + if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") { dodie "moving .config"; } @@ -293,6 +311,21 @@ sub build { } } +sub reboot { + # try to reboot normally + if (run_command "ssh $target reboot") { + # nope? power cycle it. + run_command "$opt{POWER_CYCLE}"; + } +} + +sub halt { + if ((run_command "ssh $target halt") or defined($opt{"POWER_OFF"})) { + # nope? the zap it! + run_command "$opt{POWER_OFF}"; + } +} + read_config $ARGV[0]; # mandatory configs @@ -301,6 +334,7 @@ die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); +die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); @@ -388,20 +422,16 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "*******************************************\n"; doprint "*******************************************\n"; - # try to reboot normally - - if (run_command "ssh $target reboot") { - # nope? power cycle it. - run_command "$opt{POWER_CYCLE}"; + if ($i != $opt{"NUM_BUILDS"}) { + reboot; + sleep "$opt{SLEEP_TIME}"; } - - sleep "$opt{SLEEP_TIME}"; } if ($opt{"POWEROFF_ON_SUCCESS"}) { - if (run_command "ssh $target halt" && defined($opt{"POWER_OFF"})) { - # nope? the zap it! - run_command "$opt{POWER_OFF}"; - } + halt; +} else { + reboot; } + exit 0; From 5f9b6ced04a4e9c3ee6b4d4042ac5935ef5a8dbd Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:33 -0400 Subject: [PATCH 04/30] ktest: Bisecting, install modules, add logging Added bisecting, modules, logging of the output. Banners that show success. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 321 +++++++++++++++++++++++++---------- 1 file changed, 227 insertions(+), 94 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 79da57f3023b..a34f6f4193df 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -25,11 +25,13 @@ $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; my $version; -my $install_mods; my $grub_number; my $target; my $make; my $noclean; +my $minconfig; +my $in_bisect = 0; +my $bisect_bad = ""; sub read_config { my ($config) = @_; @@ -52,9 +54,7 @@ sub read_config { close(IN); } -sub doprint { - print @_; - +sub logit { if (defined($opt{"LOG_FILE"})) { open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; print OUT @_; @@ -62,6 +62,11 @@ sub doprint { } } +sub doprint { + print @_; + logit @_; +} + sub dodie { doprint "CRITICAL FAILURE... ", @_; @@ -96,7 +101,30 @@ sub run_command { doprint "SUCCESS\n"; } - return $failed; + return !$failed; +} + +sub get_grub_index { + + return if ($grub_number >= 0); + + doprint "Find grub menu ... "; + $grub_number = -1; + open(IN, "ssh $target cat /boot/grub/menu.lst |") + or die "unable to get menu.lst"; + while () { + if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { + $grub_number++; + last; + } elsif (/^\s*title\s/) { + $grub_number++; + } + } + close(IN); + + die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" + if ($grub_number < 0); + doprint "$grub_number\n"; } my $timeout = $opt{"TIMEOUT"}; @@ -213,46 +241,63 @@ sub monitor { close(IN); if (!$booted) { + return 1 if (!$in_bisect); dodie "failed - never got a boot prompt.\n"; } if ($bug) { + return 1 if (!$in_bisect); dodie "failed - got a bug report\n"; } + + return 0; } sub install { - if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") { + run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}" or dodie "failed to copy image"; + + my $install_mods = 0; + + # should we process modules? + $install_mods = 0; + open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); + while () { + if (/CONFIG_MODULES(=y)?/) { + $install_mods = 1 if (defined($1)); + last; + } + } + close(IN); + + if (!$install_mods) { + doprint "No modules needed\n"; + return; } - if ($install_mods) { - my $modlib = "/lib/modules/$version"; - my $modtar = "autotest-mods.tar.bz2"; + run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install" or + dodie "Failed to install modules"; - if (run_command "ssh $target rm -rf $modlib") { - dodie "failed to remove old mods: $modlib"; - } + my $modlib = "/lib/modules/$version"; + my $modtar = "autotest-mods.tar.bz2"; - # would be nice if scp -r did not follow symbolic links - if (run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version") { - dodie "making tarball"; - } + run_command "ssh $target rm -rf $modlib" or + dodie "failed to remove old mods: $modlib"; - if (run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp") { - dodie "failed to copy modules"; - } + # would be nice if scp -r did not follow symbolic links + run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version" or + dodie "making tarball"; - unlink "$opt{TMP_DIR}/$modtar"; + run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp" or + dodie "failed to copy modules"; - if (run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'") { - dodie "failed to tar modules"; - } + unlink "$opt{TMP_DIR}/$modtar"; - run_command "ssh $target rm -f /tmp/$modtar"; - } + run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or + dodie "failed to tar modules"; + run_command "ssh $target rm -f /tmp/$modtar"; } sub build { @@ -261,9 +306,9 @@ sub build { my $append = ""; if ($type =~ /^useconfig:(.*)/) { - if (run_command "cp $1 $opt{OUTPUT_DIR}/.config") { + run_command "cp $1 $opt{OUTPUT_DIR}/.config" or dodie "could not copy $1 to .config"; - } + $type = "oldconfig"; } @@ -274,23 +319,20 @@ sub build { # allow for empty configs run_command "touch $opt{OUTPUT_DIR}/.config"; - if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") { + run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp" or dodie "moving .config"; - } - if (!$noclean && run_command "$make mrproper") { + if (!$noclean && !run_command "$make mrproper") { dodie "make mrproper"; } - if (run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config") { + run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config" or dodie "moving config_temp"; - } } elsif (!$noclean) { unlink "$opt{OUTPUT_DIR}/.config"; - if (run_command "$make mrproper") { + run_command "$make mrproper" or dodie "make mrproper"; - } } # add something to distinguish this build @@ -298,34 +340,162 @@ sub build { print OUT "$opt{LOCALVERSION}\n"; close(OUT); - if (defined($opt{"MIN_CONFIG"})) { - $defconfig = "KCONFIG_ALLCONFIG=$opt{MIN_CONFIG}"; + if (defined($minconfig)) { + $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - if (run_command "$defconfig $append $make $type") { + run_command "$defconfig $append $make $type" or dodie "failed make config"; - } - if (run_command "$make $opt{BUILD_OPTIONS}") { + if (!run_command "$make $opt{BUILD_OPTIONS}") { + # bisect may need this to pass + return 1 if ($in_bisect); dodie "failed build"; } + + return 0; } sub reboot { # try to reboot normally - if (run_command "ssh $target reboot") { + if (!run_command "ssh $target reboot") { # nope? power cycle it. run_command "$opt{POWER_CYCLE}"; } } sub halt { - if ((run_command "ssh $target halt") or defined($opt{"POWER_OFF"})) { + if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { # nope? the zap it! run_command "$opt{POWER_OFF}"; } } +sub success { + my ($i) = @_; + + doprint "\n\n*******************************************\n"; + doprint "*******************************************\n"; + doprint "** SUCCESS!!!! **\n"; + doprint "*******************************************\n"; + doprint "*******************************************\n"; + + if ($i != $opt{"NUM_BUILDS"}) { + reboot; + doprint "Sleeping $opt{SLEEP_TIME} seconds\n"; + sleep "$opt{SLEEP_TIME}"; + } +} + +sub get_version { + # get the release name + doprint "$make kernelrelease ... "; + $version = `$make kernelrelease | tail -1`; + chomp($version); + doprint "$version\n"; +} + +sub run_bisect { + my ($type) = @_; + + my $failed; + my $result; + my $output; + my $ret; + + + if (defined($minconfig)) { + $failed = build "useconfig:$minconfig"; + } else { + # ?? no config to use? + $failed = build "oldconfig"; + } + + if ($type ne "build") { + dodie "Failed on build" if $failed; + + # Now boot the box + get_grub_index; + get_version; + install; + $failed = monitor; + + if ($type ne "boot") { + dodie "Failed on boot" if $failed; + } + } + + if ($failed) { + $result = "bad"; + } else { + $result = "good"; + } + + doprint "git bisect $result ... "; + $output = `git bisect $result 2>&1`; + $ret = $?; + + logit $output; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to git bisect"; + } + + doprint "SUCCESS\n"; + if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\)) \[([[:xdigit:]]+)\]/) { + doprint "$1 [$2]\n"; + } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { + $bisect_bad = $1; + doprint "Found bad commit... $1\n"; + return 0; + } + + + return 1; +} + +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]"})); + + my $good = $opt{"BISECT_GOOD[$i]"}; + my $bad = $opt{"BISECT_BAD[$i]"}; + my $type = $opt{"BISECT_TYPE[$i]"}; + + $in_bisect = 1; + + run_command "git bisect start" or + dodie "could not start bisect"; + + run_command "git bisect good $good" or + dodie "could not set bisect good to $good"; + + run_command "git bisect bad $bad" or + dodie "could not set bisect good to $bad"; + + do { + $result = run_bisect $type; + } while ($result); + + run_command "git bisect log" or + dodie "could not capture git bisect log"; + + run_command "git bisect reset" or + dodie "could not reset git bisect"; + + doprint "Bad commit was [$bisect_bad]\n"; + + $in_bisect = 0; + + success $i; +} + read_config $ARGV[0]; # mandatory configs @@ -346,22 +516,6 @@ $target = "$opt{SSH_USER}\@$opt{MACHINE}"; doprint "\n\nSTARTING AUTOMATED TESTS\n"; -doprint "Find grub menu ... "; -$grub_number = -1; -open(IN, "ssh $target cat /boot/grub/menu.lst |") - or die "unable to get menu.lst"; -while () { - if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { - $grub_number++; - last; - } elsif (/^\s*title\s/) { - $grub_number++; - } -} -close(IN); -die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" - if ($grub_number < 0); -doprint "$grub_number\n"; $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; @@ -376,6 +530,14 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { $noclean = $opt{"BUILD_NOCLEAN"}; } + if (defined($opt{"MIN_CONFIG[$i]"})) { + $minconfig = $opt{"MIN_CONFIG[$i]"}; + } elsif (defined($opt{"MIN_CONFIG"})) { + $minconfig = $opt{"MIN_CONFIG"}; + } else { + undef $minconfig; + } + if (!defined($opt{$type})) { $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; } @@ -383,49 +545,20 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + if ($opt{$type} eq "bisect") { + bisect $i; + next; + } + if ($opt{$type} ne "nobuild") { build $opt{$type}; } - # get the release name - doprint "$make kernelrelease ... "; - $version = `$make kernelrelease | tail -1`; - chomp($version); - doprint "$version\n"; - - # should we process modules? - $install_mods = 0; - open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); - while () { - if (/CONFIG_MODULES(=y)?/) { - $install_mods = 1 if (defined($1)); - last; - } - } - close(IN); - - if ($install_mods) { - if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") { - dodie "Failed to install modules"; - } - } else { - doprint "No modules needed\n"; - } - + get_grub_index; + get_version; install; - monitor; - - doprint "\n\n*******************************************\n"; - doprint "*******************************************\n"; - doprint "** SUCCESS!!!! **\n"; - doprint "*******************************************\n"; - doprint "*******************************************\n"; - - if ($i != $opt{"NUM_BUILDS"}) { - reboot; - sleep "$opt{SLEEP_TIME}"; - } + success $i; } if ($opt{"POWEROFF_ON_SUCCESS"}) { From 5a391fbff8755592eb080784ef32ff818d2daa44 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:43 -0400 Subject: [PATCH 05/30] ktest: Added better console, add test build Better reading of the console. Added running a script to do testing after build succeeds. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 209 +++++++++++++++++++++++++++-------- 1 file changed, 160 insertions(+), 49 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a34f6f4193df..9eaf8d05c749 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -23,6 +23,7 @@ $opt{"REBOOT_ON_ERROR"} = 0; $opt{"POWEROFF_ON_ERROR"} = 0; $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; +$opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects my $version; my $grub_number; @@ -32,6 +33,7 @@ my $noclean; my $minconfig; my $in_bisect = 0; my $bisect_bad = ""; +my $run_test; sub read_config { my ($config) = @_; @@ -68,7 +70,7 @@ sub doprint { } sub dodie { - doprint "CRITICAL FAILURE... ", @_; + doprint "CRITICAL FAILURE... ", @_, "\n"; if ($opt{"REBOOT_ON_ERROR"}) { doprint "REBOOTING\n"; @@ -84,14 +86,14 @@ sub dodie { sub run_command { my ($command) = @_; - my $redirect = ""; + my $redirect_log = ""; if (defined($opt{"LOG_FILE"})) { - $redirect = " >> $opt{LOG_FILE} 2>&1"; + $redirect_log = " >> $opt{LOG_FILE} 2>&1"; } doprint "$command ... "; - `$command $redirect`; + `$command $redirect_log`; my $failed = $?; @@ -106,7 +108,7 @@ sub run_command { sub get_grub_index { - return if ($grub_number >= 0); + return if (defined($grub_number)); doprint "Find grub menu ... "; $grub_number = -1; @@ -164,28 +166,40 @@ sub reboot_to { run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; } -sub monitor { +sub open_console { + my ($fp) = @_; + my $flags; + + my $pid = open($fp, "$opt{CONSOLE}|") or + dodie "Can't open console $opt{CONSOLE}"; + + $flags = fcntl($fp, F_GETFL, 0) or + dodie "Can't get flags for the socket: $!\n"; + $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or + dodie "Can't set flags for the socket: $!\n"; + + return $pid; +} + +sub close_console { + my ($fp, $pid) = @_; + + doprint "kill child process $pid\n"; + kill 2, $pid; + + print "closing!\n"; + close($fp); +} + +sub monitor { my $booted = 0; my $bug = 0; my $pid; - my $doopen2 = 0; my $skip_call_trace = 0; + my $fp = \*IN; - if ($doopen2) { - $pid = open2(\*IN, \*OUT, $opt{"CONSOLE"}); - if ($pid < 0) { - dodie "Failed to connect to the console"; - } - } else { - $pid = open(IN, "$opt{CONSOLE} |"); - } - - $flags = fcntl(IN, F_GETFL, 0) or - dodie "Can't get flags for the socket: $!\n"; - - $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or - dodie "Can't set flags for the socket: $!\n"; + $pid = open_console($fp); my $line; my $full_line = ""; @@ -193,14 +207,14 @@ sub monitor { doprint "Wait for monitor to settle down.\n"; # read the monitor and wait for the system to calm down do { - $line = wait_for_input(\*IN, 5); + $line = wait_for_input($fp, 5); } while (defined($line)); reboot_to; for (;;) { - $line = wait_for_input(\*IN); + $line = wait_for_input($fp); last if (!defined($line)); @@ -234,19 +248,15 @@ sub monitor { } } - doprint "kill child process $pid\n"; - kill 2, $pid; - - print "closing!\n"; - close(IN); + close_console($fp, $pid); if (!$booted) { - return 1 if (!$in_bisect); + return 1 if ($in_bisect); dodie "failed - never got a boot prompt.\n"; } if ($bug) { - return 1 if (!$in_bisect); + return 1 if ($in_bisect); dodie "failed - got a bug report\n"; } @@ -395,6 +405,84 @@ sub get_version { doprint "$version\n"; } +sub child_run_test { + my $failed; + + $failed = !run_command $run_test; + exit $failed; +} + +my $child_done; + +sub child_finished { + $child_done = 1; +} + +sub do_run_test { + my $child_pid; + my $child_exit; + my $pid; + my $line; + my $full_line; + my $bug = 0; + my $fp = \*IN; + + $pid = open_console($fp); + + # read the monitor and wait for the system to calm down + do { + $line = wait_for_input($fp, 1); + } while (defined($line)); + + $child_done = 0; + + $SIG{CHLD} = qw(child_finished); + + $child_pid = fork; + + child_run_test if (!$child_pid); + + $full_line = ""; + + do { + $line = wait_for_input($fp, 1); + if (defined($line)) { + + # we are not guaranteed to get a full line + $full_line .= $line; + + if ($full_line =~ /call trace:/i) { + $bug = 1; + } + + if ($full_line =~ /Kernel panic -/) { + $bug = 1; + } + + if ($line =~ /\n/) { + $full_line = ""; + } + } + } while (!$child_done && !$bug); + + if ($bug) { + doprint "Detected kernel crash!\n"; + # kill the child with extreme prejudice + kill 9, $child_pid; + } + + waitpid $child_pid, 0; + $child_exit = $?; + + close_console($fp, $pid); + + if ($bug || $child_exit) { + return 1 if $in_bisect; + dodie "test failed"; + } + return 0; +} + sub run_bisect { my ($type) = @_; @@ -422,11 +510,20 @@ sub run_bisect { if ($type ne "boot") { dodie "Failed on boot" if $failed; + + $failed = do_run_test; } } if ($failed) { $result = "bad"; + + # reboot the box to a good kernel + if ($type eq "boot") { + reboot; + doprint "sleep a little for reboot\n"; + sleep $opt{"BISECT_SLEEP_TIME"}; + } } else { $result = "good"; } @@ -443,12 +540,15 @@ sub run_bisect { } doprint "SUCCESS\n"; - if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\)) \[([[:xdigit:]]+)\]/) { + 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; doprint "Found bad commit... $1\n"; return 0; + } else { + # we already logged it, just print it now. + print $output; } @@ -479,6 +579,11 @@ sub bisect { run_command "git bisect bad $bad" or dodie "could not set bisect good to $bad"; + # Can't have a test without having a test to run + if ($type eq "test" && !defined($run_test)) { + $type = "boot"; + } + do { $result = run_bisect $type; } while ($result); @@ -519,28 +624,29 @@ doprint "\n\nSTARTING AUTOMATED TESTS\n"; $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; +sub set_build_option { + my ($name, $i) = @_; + + my $option = "$name\[$i\]"; + + if (defined($opt{$option})) { + return $opt{$option}; + } + + if (defined($opt{$name})) { + return $opt{$name}; + } + + return undef; +} + # First we need to do is the builds for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { my $type = "BUILD_TYPE[$i]"; - if (defined($opt{"BUILD_NOCLEAN[$i]"}) && - $opt{"BUILD_NOCLEAN[$i]"} != 0) { - $noclean = 1; - } else { - $noclean = $opt{"BUILD_NOCLEAN"}; - } - - if (defined($opt{"MIN_CONFIG[$i]"})) { - $minconfig = $opt{"MIN_CONFIG[$i]"}; - } elsif (defined($opt{"MIN_CONFIG"})) { - $minconfig = $opt{"MIN_CONFIG"}; - } else { - undef $minconfig; - } - - if (!defined($opt{$type})) { - $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; - } + $noclean = set_build_option("BUILD_NOCLEAN", $i); + $minconfig = set_build_option("MIN_CONFIG", $i); + $run_test = set_build_option("TEST", $i); doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; @@ -558,6 +664,11 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { get_version; install; monitor; + + if (defined($run_test)) { + do_run_test; + } + success $i; } From 1a5cfce344711a541aa63bdff81a58db35e20564 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:51 -0400 Subject: [PATCH 06/30] ktest: Added reboot on success Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 9eaf8d05c749..f344fd0d4f28 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -21,6 +21,7 @@ $opt{"SLEEP_TIME"} = 60; # sleep time between tests $opt{"BUILD_NOCLEAN"} = 0; $opt{"REBOOT_ON_ERROR"} = 0; $opt{"POWEROFF_ON_ERROR"} = 0; +$opt{"REBOOT_ON_SUCCESS"} = 1; $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; $opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects @@ -644,6 +645,10 @@ sub set_build_option { for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { my $type = "BUILD_TYPE[$i]"; + if (!defined($opt{$type})) { + $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; + } + $noclean = set_build_option("BUILD_NOCLEAN", $i); $minconfig = set_build_option("MIN_CONFIG", $i); $run_test = set_build_option("TEST", $i); @@ -674,7 +679,7 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} else { +} elsif ($opt{"REBOOT_ON_SUCCESS"}) { reboot; } From 6c5ee0be02f73ebd70eb50b84013e8830f08a6da Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:57:58 -0400 Subject: [PATCH 07/30] ktest: Added patchcheck Added patchcheck functionality. It will checkout a given SHA1 and test that commit and all commits to another given SHA1. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 142 ++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 2 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index f344fd0d4f28..e0ded14b5477 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -34,7 +34,9 @@ my $noclean; my $minconfig; my $in_bisect = 0; my $bisect_bad = ""; +my $in_patchcheck = 0; my $run_test; +my $redirect; sub read_config { my ($config) = @_; @@ -88,13 +90,18 @@ sub dodie { sub run_command { my ($command) = @_; my $redirect_log = ""; + my $redirect_tee = ""; if (defined($opt{"LOG_FILE"})) { - $redirect_log = " >> $opt{LOG_FILE} 2>&1"; + $redirect_log = "| tee -a $opt{LOG_FILE}"; + } + + if (defined($redirect)) { + $redirect_tee = "| tee $redirect" } doprint "$command ... "; - `$command $redirect_log`; + `$command 2>&1 $redirect_tee $redirect_log > /dev/null`; my $failed = $?; @@ -311,6 +318,37 @@ sub install { run_command "ssh $target rm -f /tmp/$modtar"; } +sub check_buildlog { + my ($patch) = @_; + + my $buildlog = "$opt{TMP_DIR}/buildlog"; + my @files = `git show $patch | diffstat -l`; + + open(IN, "git show $patch |") or + dodie "failed to show $patch"; + while () { + if (m,^--- a/(.*),) { + chomp $1; + $files[$#files] = $1; + } + } + close(IN); + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while () { + if (/^\s*(.*?):.*(warning|error)/) { + my $err = $1; + foreach my $file (@files) { + my $fullpath = "$opt{BUILD_DIR}/$file"; + if ($file eq $err || $fullpath eq $err) { + dodie "$file built with warnings"; + } + } + } + } + close(IN); +} + sub build { my ($type) = @_; my $defconfig = ""; @@ -358,11 +396,18 @@ sub build { run_command "$defconfig $append $make $type" or dodie "failed make config"; + # patch check will examine the log + if ($in_patchcheck) { + $redirect = "$opt{TMP_DIR}/buildlog"; + } + if (!run_command "$make $opt{BUILD_OPTIONS}") { + undef $redirect; # bisect may need this to pass return 1 if ($in_bisect); dodie "failed build"; } + undef $redirect; return 0; } @@ -602,6 +647,90 @@ sub bisect { success $i; } +sub patchcheck { + my ($i) = @_; + + die "PATCHCHECK_START[$i] not defined\n" + if (!defined($opt{"PATCHCHECK_START[$i]"})); + die "PATCHCHECK_TYPE[$i] not defined\n" + if (!defined($opt{"PATCHCHECK_TYPE[$i]"})); + + my $start = $opt{"PATCHCHECK_START[$i]"}; + + my $end = "HEAD"; + if (defined($opt{"PATCHCHECK_END[$i]"})) { + $end = $opt{"PATCHCHECK_END[$i]"}; + } + + my $type = $opt{"PATCHCHECK_TYPE[$i]"}; + + # Can't have a test without having a test to run + if ($type eq "test" && !defined($run_test)) { + $type = "boot"; + } + + open (IN, "git log --pretty=oneline $end|") or + dodie "could not get git list"; + + my @list; + + while () { + chomp; + $list[$#list+1] = $_; + last if (/^$start/); + } + close(IN); + + if ($list[$#list] !~ /^$start/) { + dodie "SHA1 $start not found"; + } + + # go backwards in the list + @list = reverse @list; + + my $save_clean = $noclean; + + $in_patchcheck = 1; + foreach my $item (@list) { + my $sha1 = $item; + $sha1 =~ s/^([[:xdigit:]]+).*/$1/; + + doprint "\nProcessing commit $item\n\n"; + + run_command "git checkout $sha1" or + die "Failed to checkout $sha1"; + + # only clean on the first and last patch + if ($item eq $list[0] || + $item eq $list[$#list]) { + $noclean = $save_clean; + } else { + $noclean = 1; + } + + if (defined($minconfig)) { + build "useconfig:$minconfig"; + } else { + # ?? no config to use? + build "oldconfig"; + } + + check_buildlog $sha1; + + next if ($type eq "build"); + + get_grub_index; + get_version; + install; + monitor; + + next if ($type eq "boot"); + do_run_test; + } + $in_patchcheck = 0; + success $i; +} + read_config $ARGV[0]; # mandatory configs @@ -656,9 +785,18 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + my $checkout = $opt{"CHECKOUT[$i]"}; + if (defined($checkout)) { + run_command "git checkout $checkout" or + die "failed to checkout $checkout"; + } + if ($opt{$type} eq "bisect") { bisect $i; next; + } elsif ($opt{$type} eq "patchcheck") { + patchcheck $i; + next; } if ($opt{$type} ne "nobuild") { From d6ce2a0b33eb71f6862dfb6cbddd0e842f8132de Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:05 -0400 Subject: [PATCH 08/30] ktest: Add reverse bisect, better logging, copyright Added the ability to do a reverse bisect. Better logging of running commands. Added the copyright statement. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 51 +++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index e0ded14b5477..0dc403e7170c 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1,4 +1,8 @@ #!/usr/bin/perl -w +# +# Copywrite 2010 - Steven Rostedt , Red Hat Inc. +# Licensed under the terms of the GNU GPL License version 2 +# use strict; use IPC::Open2; @@ -34,6 +38,7 @@ my $noclean; my $minconfig; my $in_bisect = 0; my $bisect_bad = ""; +my $reverse_bisect; my $in_patchcheck = 0; my $run_test; my $redirect; @@ -89,22 +94,39 @@ sub dodie { sub run_command { my ($command) = @_; - my $redirect_log = ""; - my $redirect_tee = ""; + my $dolog = 0; + my $dord = 0; + my $pid; + + doprint("$command ... "); + + $pid = open(CMD, "$command 2>&1 |") or + dodie "unable to exec $command"; if (defined($opt{"LOG_FILE"})) { - $redirect_log = "| tee -a $opt{LOG_FILE}"; + open(LOG, ">>$opt{LOG_FILE}") or + dodie "failed to write to log"; + $dolog = 1; } if (defined($redirect)) { - $redirect_tee = "| tee $redirect" + open (RD, ">$redirect") or + dodie "failed to write to redirect $redirect"; + $dord = 1; } - doprint "$command ... "; - `$command 2>&1 $redirect_tee $redirect_log > /dev/null`; + while () { + print LOG if ($dolog); + print RD if ($dord); + } + waitpid($pid, 0); my $failed = $?; + close(CMD); + close(LOG) if ($dolog); + close(RD) if ($dord); + if ($failed) { doprint "FAILED!\n"; } else { @@ -574,6 +596,15 @@ sub run_bisect { $result = "good"; } + # Are we looking for where it worked, not failed? + if ($reverse_bisect) { + if ($failed) { + $result = "good"; + } else { + $result = "bad"; + } + } + doprint "git bisect $result ... "; $output = `git bisect $result 2>&1`; $ret = $?; @@ -614,6 +645,14 @@ sub bisect { my $bad = $opt{"BISECT_BAD[$i]"}; my $type = $opt{"BISECT_TYPE[$i]"}; + if (defined($opt{"BISECT_REVERSE[$i]"}) && + $opt{"BISECT_REVERSE[$i]"} == 1) { + doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; + $reverse_bisect = 1; + } else { + $reverse_bisect = 0; + } + $in_bisect = 1; run_command "git bisect start" or From 2b7d9b21426f10448cb047d1d7c3be05da848fd2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:15 -0400 Subject: [PATCH 09/30] ktest: Added continuing on success, clear log and timeout Add option to continue after a test fails. Add option to reset the log at start of running ktest. Update default timeout to 2 minutes. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 116 +++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 38 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0dc403e7170c..0a0b1b16fccd 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -19,7 +19,7 @@ my %opt; $opt{"NUM_BUILDS"} = 5; $opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; $opt{"MAKE_CMD"} = "make"; -$opt{"TIMEOUT"} = 50; +$opt{"TIMEOUT"} = 120; $opt{"TMP_DIR"} = "/tmp/autotest"; $opt{"SLEEP_TIME"} = 60; # sleep time between tests $opt{"BUILD_NOCLEAN"} = 0; @@ -29,6 +29,10 @@ $opt{"REBOOT_ON_SUCCESS"} = 1; $opt{"POWEROFF_ON_SUCCESS"} = 0; $opt{"BUILD_OPTIONS"} = ""; $opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects +$opt{"CLEAR_LOG"} = 0; +$opt{"SUCCESS_LINE"} = "login:"; +$opt{"BOOTED_TIMEOUT"} = 1; +$opt{"DIE_ON_FAILURE"} = 1; my $version; my $grub_number; @@ -36,6 +40,7 @@ my $target; my $make; my $noclean; my $minconfig; +my $addconfig; my $in_bisect = 0; my $bisect_bad = ""; my $reverse_bisect; @@ -92,6 +97,16 @@ sub dodie { die @_; } +sub fail { + + if ($opt{"DIE_ON_FAILURE"}) { + dodie @_; + } + + doprint "Failed: ", @_, "\n"; + return 1; +} + sub run_command { my ($command) = @_; my $dolog = 0; @@ -101,7 +116,7 @@ sub run_command { doprint("$command ... "); $pid = open(CMD, "$command 2>&1 |") or - dodie "unable to exec $command"; + (fail "unable to exec $command" and return 0); if (defined($opt{"LOG_FILE"})) { open(LOG, ">>$opt{LOG_FILE}") or @@ -228,6 +243,7 @@ sub monitor { my $pid; my $skip_call_trace = 0; my $fp = \*IN; + my $loops; $pid = open_console($fp); @@ -244,7 +260,11 @@ sub monitor { for (;;) { - $line = wait_for_input($fp); + if ($booted) { + $line = wait_for_input($fp, $opt{"BOOTED_TIMEOUT"}); + } else { + $line = wait_for_input($fp); + } last if (!defined($line)); @@ -253,7 +273,7 @@ sub monitor { # we are not guaranteed to get a full line $full_line .= $line; - if ($full_line =~ /login:/) { + if ($full_line =~ /$opt{"SUCCESS_LINE"}/) { $booted = 1; } @@ -281,16 +301,16 @@ sub monitor { close_console($fp, $pid); if (!$booted) { - return 1 if ($in_bisect); - dodie "failed - never got a boot prompt.\n"; + return 0 if ($in_bisect); + fail "failed - never got a boot prompt.\n" and return 0; } if ($bug) { - return 1 if ($in_bisect); - dodie "failed - got a bug report\n"; + return 0 if ($in_bisect); + fail "failed - got a bug report\n" and return 0; } - return 0; + return 1; } sub install { @@ -363,12 +383,14 @@ sub check_buildlog { foreach my $file (@files) { my $fullpath = "$opt{BUILD_DIR}/$file"; if ($file eq $err || $fullpath eq $err) { - dodie "$file built with warnings"; + fail "$file built with warnings" and return 0; } } } } close(IN); + + return 1; } sub build { @@ -426,12 +448,12 @@ sub build { if (!run_command "$make $opt{BUILD_OPTIONS}") { undef $redirect; # bisect may need this to pass - return 1 if ($in_bisect); - dodie "failed build"; + return 0 if ($in_bisect); + fail "failed build" and return 0; } undef $redirect; - return 0; + return 1; } sub reboot { @@ -545,41 +567,40 @@ sub do_run_test { close_console($fp, $pid); if ($bug || $child_exit) { - return 1 if $in_bisect; - dodie "test failed"; + return 0 if $in_bisect; + fail "test failed" and return 0; } - return 0; + return 1; } sub run_bisect { my ($type) = @_; - my $failed; + my $failed = 0; my $result; my $output; my $ret; - if (defined($minconfig)) { - $failed = build "useconfig:$minconfig"; + build "useconfig:$minconfig" or $failed = 1; } else { # ?? no config to use? - $failed = build "oldconfig"; + build "oldconfig" or $failed = 1; } if ($type ne "build") { - dodie "Failed on build" if $failed; + fail "Failed on build" if $failed; # Now boot the box get_grub_index; get_version; install; - $failed = monitor; + monitor or $failed = 1; if ($type ne "boot") { - dodie "Failed on boot" if $failed; + fail "Failed on boot" if $failed; - $failed = do_run_test; + do_run_test or $failed = 1; } } @@ -613,7 +634,7 @@ sub run_bisect { if ($ret) { doprint "FAILED\n"; - dodie "Failed to git bisect"; + fail "Failed to git bisect"; } doprint "SUCCESS\n"; @@ -656,13 +677,13 @@ sub bisect { $in_bisect = 1; run_command "git bisect start" or - dodie "could not start bisect"; + fail "could not start bisect"; run_command "git bisect good $good" or - dodie "could not set bisect good to $good"; + fail "could not set bisect good to $good"; run_command "git bisect bad $bad" or - dodie "could not set bisect good to $bad"; + fail "could not set bisect good to $bad"; # Can't have a test without having a test to run if ($type eq "test" && !defined($run_test)) { @@ -721,7 +742,7 @@ sub patchcheck { close(IN); if ($list[$#list] !~ /^$start/) { - dodie "SHA1 $start not found"; + fail "SHA1 $start not found"; } # go backwards in the list @@ -748,26 +769,28 @@ sub patchcheck { } if (defined($minconfig)) { - build "useconfig:$minconfig"; + build "useconfig:$minconfig" or return 0; } else { # ?? no config to use? - build "oldconfig"; + build "oldconfig" or return 0; } - check_buildlog $sha1; + check_buildlog $sha1 or return 0; next if ($type eq "build"); get_grub_index; get_version; install; - monitor; + monitor or return 0; next if ($type eq "boot"); - do_run_test; + do_run_test or next; } $in_patchcheck = 0; success $i; + + return 1; } read_config $ARGV[0]; @@ -788,8 +811,15 @@ chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; $target = "$opt{SSH_USER}\@$opt{MACHINE}"; -doprint "\n\nSTARTING AUTOMATED TESTS\n"; +if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { + unlink $opt{"LOG_FILE"}; +} +doprint "\n\nSTARTING AUTOMATED TESTS\n\n"; + +foreach my $option (sort keys %opt) { + doprint "$option = $opt{$option}\n"; +} $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; @@ -820,10 +850,20 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { $noclean = set_build_option("BUILD_NOCLEAN", $i); $minconfig = set_build_option("MIN_CONFIG", $i); $run_test = set_build_option("TEST", $i); + $addconfig = set_build_option("ADD_CONFIG", $i); doprint "\n\n"; doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + if (!defined($minconfig)) { + $minconfig = $addconfig; + + } elsif (defined($addconfig)) { + run_command "cat $addconfig $minconfig > $opt{TMP_DIR}/use_config" or + dodie "Failed to create temp config"; + $minconfig = "$opt{TMP_DIR}/use_config"; + } + my $checkout = $opt{"CHECKOUT[$i]"}; if (defined($checkout)) { run_command "git checkout $checkout" or @@ -839,16 +879,16 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { } if ($opt{$type} ne "nobuild") { - build $opt{$type}; + build $opt{$type} or next; } get_grub_index; get_version; install; - monitor; + monitor or next; if (defined($run_test)) { - do_run_test; + do_run_test or next; } success $i; From 7faafbd69639b53b6cc2d450c283d9cc12e62c70 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:22 -0400 Subject: [PATCH 10/30] ktest: Add open and close console and start stop monitor It is much better to keep the monitor running throughout a test than to constantly start and stop it. Some console readers will show everything that has happened before when opening the console, and by opening it several times, causes the old content to be read multiple times in a single test. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 257 +++++++++++++++++++++++------------ 1 file changed, 170 insertions(+), 87 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0a0b1b16fccd..ff6283a44847 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -7,6 +7,8 @@ use strict; use IPC::Open2; use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); +use File::Path qw(mkpath); +use File::Copy qw(cp); use FileHandle; $#ARGV >= 0 || die "usage: autotest.pl config-file\n"; @@ -17,7 +19,7 @@ my %opt; #default opts $opt{"NUM_BUILDS"} = 5; -$opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; +$opt{"BUILD_TYPE"} = "randconfig"; $opt{"MAKE_CMD"} = "make"; $opt{"TIMEOUT"} = 120; $opt{"TMP_DIR"} = "/tmp/autotest"; @@ -35,6 +37,7 @@ $opt{"BOOTED_TIMEOUT"} = 1; $opt{"DIE_ON_FAILURE"} = 1; my $version; +my $build_type; my $grub_number; my $target; my $make; @@ -47,6 +50,11 @@ my $reverse_bisect; my $in_patchcheck = 0; my $run_test; my $redirect; +my $buildlog; +my $dmesg; +my $monitor_fp; +my $monitor_pid; +my $monitor_cnt = 0; sub read_config { my ($config) = @_; @@ -82,12 +90,22 @@ sub doprint { logit @_; } +sub run_command; + +sub reboot { + # try to reboot normally + if (!run_command "ssh $target reboot") { + # nope? power cycle it. + run_command "$opt{POWER_CYCLE}"; + } +} + sub dodie { doprint "CRITICAL FAILURE... ", @_, "\n"; if ($opt{"REBOOT_ON_ERROR"}) { doprint "REBOOTING\n"; - `$opt{"POWER_CYCLE"}`; + reboot; } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { doprint "POWERING OFF\n"; @@ -97,6 +115,59 @@ sub dodie { die @_; } +sub open_console { + my ($fp) = @_; + + my $flags; + + my $pid = open($fp, "$opt{CONSOLE}|") or + dodie "Can't open console $opt{CONSOLE}"; + + $flags = fcntl($fp, F_GETFL, 0) or + dodie "Can't get flags for the socket: $!\n"; + $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or + dodie "Can't set flags for the socket: $!\n"; + + return $pid; +} + +sub close_console { + my ($fp, $pid) = @_; + + doprint "kill child process $pid\n"; + kill 2, $pid; + + print "closing!\n"; + close($fp); +} + +sub start_monitor { + if ($monitor_cnt++) { + return; + } + $monitor_fp = \*MONFD; + $monitor_pid = open_console $monitor_fp; +} + +sub end_monitor { + if (--$monitor_cnt) { + return; + } + close_console($monitor_fp, $monitor_pid); +} + +sub wait_for_monitor { + my ($time) = @_; + my $line; + + doprint "Wait for monitor to settle down.\n"; + + # read the monitor and wait for the system to calm down + do { + $line = wait_for_input($monitor_fp, $time); + } while (defined($line)); +} + sub fail { if ($opt{"DIE_ON_FAILURE"}) { @@ -104,6 +175,41 @@ sub fail { } doprint "Failed: ", @_, "\n"; + + doprint "REBOOTING\n"; + reboot; + start_monitor; + wait_for_monitor $opt{"SLEEP_TIME"}; + end_monitor; + + return 1 if (!defined($opt{"STORE_FAILURES"})); + + 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]; + + my $dir = "$opt{MACHINE}-$build_type-fail-$date"; + my $faildir = "$opt{STORE_FAILURES}/$dir"; + + if (!-d $faildir) { + mkpath($faildir) or + die "can't create $opt{STORE_FAILURES}"; + } + if (-f "$opt{OUTPUT_DIR}/.config") { + cp "$opt{OUTPUT_DIR}/.config", "$faildir/config" or + die "failed to copy .config"; + } + if (-f $buildlog) { + cp $buildlog, "$faildir/buildlog" or + die "failed to move $buildlog"; + } + if (-f $dmesg) { + cp $dmesg, "$faildir/dmesg" or + die "failed to move $dmesg"; + } + + doprint "*** Saved info to $faildir ***\n"; + return 1; } @@ -211,64 +317,34 @@ sub reboot_to { run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; } -sub open_console { - my ($fp) = @_; - - my $flags; - - my $pid = open($fp, "$opt{CONSOLE}|") or - dodie "Can't open console $opt{CONSOLE}"; - - $flags = fcntl($fp, F_GETFL, 0) or - dodie "Can't get flags for the socket: $!\n"; - $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or - dodie "Can't set flags for the socket: $!\n"; - - return $pid; -} - -sub close_console { - my ($fp, $pid) = @_; - - doprint "kill child process $pid\n"; - kill 2, $pid; - - print "closing!\n"; - close($fp); -} - sub monitor { my $booted = 0; my $bug = 0; - my $pid; my $skip_call_trace = 0; - my $fp = \*IN; my $loops; - $pid = open_console($fp); + wait_for_monitor 5; my $line; my $full_line = ""; - doprint "Wait for monitor to settle down.\n"; - # read the monitor and wait for the system to calm down - do { - $line = wait_for_input($fp, 5); - } while (defined($line)); + open(DMESG, "> $dmesg") or + die "unable to write to $dmesg"; reboot_to; for (;;) { if ($booted) { - $line = wait_for_input($fp, $opt{"BOOTED_TIMEOUT"}); + $line = wait_for_input($monitor_fp, $opt{"BOOTED_TIMEOUT"}); } else { - $line = wait_for_input($fp); + $line = wait_for_input($monitor_fp); } last if (!defined($line)); doprint $line; + print DMESG $line; # we are not guaranteed to get a full line $full_line .= $line; @@ -298,7 +374,7 @@ sub monitor { } } - close_console($fp, $pid); + close(DMESG); if (!$booted) { return 0 if ($in_bisect); @@ -363,7 +439,6 @@ sub install { sub check_buildlog { my ($patch) = @_; - my $buildlog = "$opt{TMP_DIR}/buildlog"; my @files = `git show $patch | diffstat -l`; open(IN, "git show $patch |") or @@ -398,6 +473,8 @@ sub build { my $defconfig = ""; my $append = ""; + unlink $buildlog; + if ($type =~ /^useconfig:(.*)/) { run_command "cp $1 $opt{OUTPUT_DIR}/.config" or dodie "could not copy $1 to .config"; @@ -440,11 +517,7 @@ sub build { run_command "$defconfig $append $make $type" or dodie "failed make config"; - # patch check will examine the log - if ($in_patchcheck) { - $redirect = "$opt{TMP_DIR}/buildlog"; - } - + $redirect = "$opt{TMP_DIR}/buildlog"; if (!run_command "$make $opt{BUILD_OPTIONS}") { undef $redirect; # bisect may need this to pass @@ -456,14 +529,6 @@ sub build { return 1; } -sub reboot { - # try to reboot normally - if (!run_command "ssh $target reboot") { - # nope? power cycle it. - run_command "$opt{POWER_CYCLE}"; - } -} - sub halt { if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { # nope? the zap it! @@ -481,9 +546,11 @@ sub success { doprint "*******************************************\n"; if ($i != $opt{"NUM_BUILDS"}) { + doprint "Reboot and wait $opt{SLEEP_TIME} seconds\n"; reboot; - doprint "Sleeping $opt{SLEEP_TIME} seconds\n"; - sleep "$opt{SLEEP_TIME}"; + start_monitor; + wait_for_monitor $opt{"SLEEP_TIME"}; + end_monitor; } } @@ -496,9 +563,14 @@ sub get_version { } sub child_run_test { - my $failed; + my $failed = 0; - $failed = !run_command $run_test; + # child should have no power + $opt{"REBOOT_ON_ERROR"} = 0; + $opt{"POWEROFF_ON_ERROR"} = 0; + $opt{"DIE_ON_FAILURE"} = 1; + + run_command $run_test or $failed = 1; exit $failed; } @@ -511,18 +583,13 @@ sub child_finished { sub do_run_test { my $child_pid; my $child_exit; - my $pid; my $line; my $full_line; my $bug = 0; - my $fp = \*IN; - $pid = open_console($fp); + wait_for_monitor 1; - # read the monitor and wait for the system to calm down - do { - $line = wait_for_input($fp, 1); - } while (defined($line)); + doprint "run test $run_test\n"; $child_done = 0; @@ -535,7 +602,7 @@ sub do_run_test { $full_line = ""; do { - $line = wait_for_input($fp, 1); + $line = wait_for_input($monitor_fp, 1); if (defined($line)) { # we are not guaranteed to get a full line @@ -564,8 +631,6 @@ sub do_run_test { waitpid $child_pid, 0; $child_exit = $?; - close_console($fp, $pid); - if ($bug || $child_exit) { return 0 if $in_bisect; fail "test failed" and return 0; @@ -589,19 +654,22 @@ sub run_bisect { } if ($type ne "build") { - fail "Failed on build" if $failed; + dodie "Failed on build" if $failed; # Now boot the box get_grub_index; get_version; install; + + start_monitor; monitor or $failed = 1; if ($type ne "boot") { - fail "Failed on boot" if $failed; + dodie "Failed on boot" if $failed; do_run_test or $failed = 1; } + end_monitor; } if ($failed) { @@ -609,9 +677,11 @@ sub run_bisect { # reboot the box to a good kernel if ($type eq "boot") { + doprint "Reboot and sleep $opt{BISECT_SLEEP_TIME} seconds\n"; reboot; - doprint "sleep a little for reboot\n"; - sleep $opt{"BISECT_SLEEP_TIME"}; + start_monitor; + wait_for_monitor $opt{"BISECT_SLEEP_TIME"}; + end_monitor; } } else { $result = "good"; @@ -782,10 +852,18 @@ sub patchcheck { get_grub_index; get_version; install; - monitor or return 0; - next if ($type eq "boot"); - do_run_test or next; + my $failed = 0; + + start_monitor; + monitor or $failed = 1; + + if (!$failed && $type ne "boot"){ + do_run_test or $failed = 1; + } + end_monitor; + return 0 if ($failed); + } $in_patchcheck = 0; success $i; @@ -821,6 +899,8 @@ foreach my $option (sort keys %opt) { doprint "$option = $opt{$option}\n"; } +$buildlog = "$opt{TMP_DIR}/buildlog"; +$dmesg = "$opt{TMP_DIR}/dmesg"; $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; sub set_build_option { @@ -841,19 +921,18 @@ sub set_build_option { # First we need to do is the builds for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { - my $type = "BUILD_TYPE[$i]"; - - if (!defined($opt{$type})) { - $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; - } + $build_type = set_build_option("BUILD_TYPE", $i); $noclean = set_build_option("BUILD_NOCLEAN", $i); $minconfig = set_build_option("MIN_CONFIG", $i); $run_test = set_build_option("TEST", $i); $addconfig = set_build_option("ADD_CONFIG", $i); doprint "\n\n"; - doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $build_type\n\n"; + + unlink $dmesg; + unlink $buildlog; if (!defined($minconfig)) { $minconfig = $addconfig; @@ -870,26 +949,30 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { die "failed to checkout $checkout"; } - if ($opt{$type} eq "bisect") { + if ($build_type eq "bisect") { bisect $i; next; - } elsif ($opt{$type} eq "patchcheck") { + } elsif ($build_type eq "patchcheck") { patchcheck $i; next; } - if ($opt{$type} ne "nobuild") { - build $opt{$type} or next; + if ($build_type ne "nobuild") { + build $build_type or next; } get_grub_index; get_version; install; - monitor or next; - if (defined($run_test)) { - do_run_test or next; + my $failed = 0; + start_monitor; + monitor or $failed = 1;; + if (!$failed && defined($run_test)) { + do_run_test or $failed = 1; } + end_monitor; + next if ($failed); success $i; } From a75fececff3cc1e86d74e98de634ea2ed1b47697 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:27 -0400 Subject: [PATCH 11/30] ktest: Added sample.conf, new %default option format Added sample.conf as a nice document to show new users. Use a %default hash to separate out the options that are default and allow us to complain about options being set twice. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 467 ++++++++++++++++++++++---------- tools/testing/ktest/sample.conf | 330 ++++++++++++++++++++++ 2 files changed, 652 insertions(+), 145 deletions(-) create mode 100644 tools/testing/ktest/sample.conf diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index ff6283a44847..1f28c45f15b8 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -16,28 +16,45 @@ $#ARGV >= 0 || die "usage: autotest.pl config-file\n"; $| = 1; my %opt; +my %default; #default opts -$opt{"NUM_BUILDS"} = 5; -$opt{"BUILD_TYPE"} = "randconfig"; -$opt{"MAKE_CMD"} = "make"; -$opt{"TIMEOUT"} = 120; -$opt{"TMP_DIR"} = "/tmp/autotest"; -$opt{"SLEEP_TIME"} = 60; # sleep time between tests -$opt{"BUILD_NOCLEAN"} = 0; -$opt{"REBOOT_ON_ERROR"} = 0; -$opt{"POWEROFF_ON_ERROR"} = 0; -$opt{"REBOOT_ON_SUCCESS"} = 1; -$opt{"POWEROFF_ON_SUCCESS"} = 0; -$opt{"BUILD_OPTIONS"} = ""; -$opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects -$opt{"CLEAR_LOG"} = 0; -$opt{"SUCCESS_LINE"} = "login:"; -$opt{"BOOTED_TIMEOUT"} = 1; -$opt{"DIE_ON_FAILURE"} = 1; +$default{"NUM_TESTS"} = 5; +$default{"REBOOT_TYPE"} = "grub"; +$default{"TEST_TYPE"} = "test"; +$default{"BUILD_TYPE"} = "randconfig"; +$default{"MAKE_CMD"} = "make"; +$default{"TIMEOUT"} = 120; +$default{"TMP_DIR"} = "/tmp/autotest"; +$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; my $version; +my $machine; +my $tmpdir; +my $builddir; +my $outputdir; +my $test_type; my $build_type; +my $build_options; +my $reboot_type; +my $reboot_script; +my $power_cycle; +my $reboot_on_error; +my $poweroff_on_error; +my $die_on_failure; +my $power_off; +my $grub_menu; my $grub_number; my $target; my $make; @@ -55,6 +72,16 @@ my $dmesg; my $monitor_fp; my $monitor_pid; my $monitor_cnt = 0; +my $sleep_time; +my $bisect_sleep_time; +my $store_failures; +my $timeout; +my $booted_timeout; +my $console; +my $success_line; +my $build_target; +my $target_image; +my $localversion; sub read_config { my ($config) = @_; @@ -70,11 +97,22 @@ sub read_config { my $lvalue = $1; my $rvalue = $2; + if (defined($opt{$lvalue})) { + die "Error: Option $lvalue defined more than once!\n"; + } $opt{$lvalue} = $rvalue; } } close(IN); + + # set any defaults + + foreach my $default (keys %default) { + if (!defined($opt{$default})) { + $opt{$default} = $default{$default}; + } + } } sub logit { @@ -96,20 +134,20 @@ sub reboot { # try to reboot normally if (!run_command "ssh $target reboot") { # nope? power cycle it. - run_command "$opt{POWER_CYCLE}"; + run_command "$power_cycle"; } } sub dodie { doprint "CRITICAL FAILURE... ", @_, "\n"; - if ($opt{"REBOOT_ON_ERROR"}) { + if ($reboot_on_error && $test_type ne "build") { doprint "REBOOTING\n"; reboot; - } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { + } elsif ($poweroff_on_error && defined($power_off)) { doprint "POWERING OFF\n"; - `$opt{"POWER_OFF"}`; + `$power_off`; } die @_; @@ -120,8 +158,8 @@ sub open_console { my $flags; - my $pid = open($fp, "$opt{CONSOLE}|") or - dodie "Can't open console $opt{CONSOLE}"; + my $pid = open($fp, "$console|") or + dodie "Can't open console $console"; $flags = fcntl($fp, F_GETFL, 0) or dodie "Can't get flags for the socket: $!\n"; @@ -147,6 +185,10 @@ sub start_monitor { } $monitor_fp = \*MONFD; $monitor_pid = open_console $monitor_fp; + + return; + + open(MONFD, "Stop perl from warning about single use of MONFD"); } sub end_monitor { @@ -160,43 +202,50 @@ sub wait_for_monitor { my ($time) = @_; my $line; - doprint "Wait for monitor to settle down.\n"; + doprint "** Wait for monitor to settle down **\n"; # read the monitor and wait for the system to calm down do { $line = wait_for_input($monitor_fp, $time); + print "$line" if (defined($line)); } while (defined($line)); + print "** Monitor flushed **\n"; } sub fail { - if ($opt{"DIE_ON_FAILURE"}) { + if ($die_on_failure) { dodie @_; } - doprint "Failed: ", @_, "\n"; + doprint "FAILED\n"; - doprint "REBOOTING\n"; - reboot; - start_monitor; - wait_for_monitor $opt{"SLEEP_TIME"}; - end_monitor; + # no need to reboot for just building. + if ($test_type ne "build") { + doprint "REBOOTING\n"; + reboot; + start_monitor; + wait_for_monitor $sleep_time; + end_monitor; + } - return 1 if (!defined($opt{"STORE_FAILURES"})); + doprint "**** Failed: ", @_, " ****\n"; + + return 1 if (!defined($store_failures)); 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]; - my $dir = "$opt{MACHINE}-$build_type-fail-$date"; - my $faildir = "$opt{STORE_FAILURES}/$dir"; + my $dir = "$machine-$test_type-$build_type-fail-$date"; + my $faildir = "$store_failures/$dir"; if (!-d $faildir) { mkpath($faildir) or - die "can't create $opt{STORE_FAILURES}"; + die "can't create $faildir"; } - if (-f "$opt{OUTPUT_DIR}/.config") { - cp "$opt{OUTPUT_DIR}/.config", "$faildir/config" or + if (-f "$outputdir/.config") { + cp "$outputdir/.config", "$faildir/config" or die "failed to copy .config"; } if (-f $buildlog) { @@ -259,6 +308,9 @@ sub run_command { sub get_grub_index { + if ($reboot_type ne "grub") { + return; + } return if (defined($grub_number)); doprint "Find grub menu ... "; @@ -266,7 +318,7 @@ sub get_grub_index { open(IN, "ssh $target cat /boot/grub/menu.lst |") or die "unable to get menu.lst"; while () { - if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { + if (/^\s*title\s+$grub_menu\s*$/) { $grub_number++; last; } elsif (/^\s*title\s/) { @@ -275,13 +327,11 @@ sub get_grub_index { } close(IN); - die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" + die "Could not find '$grub_menu' in /boot/grub/menu on $machine" if ($grub_number < 0); doprint "$grub_number\n"; } -my $timeout = $opt{"TIMEOUT"}; - sub wait_for_input { my ($fp, $time) = @_; @@ -314,7 +364,12 @@ sub wait_for_input } sub reboot_to { - run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + if ($reboot_type eq "grub") { + run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + return; + } + + run_command "$reboot_script"; } sub monitor { @@ -336,7 +391,7 @@ sub monitor { for (;;) { if ($booted) { - $line = wait_for_input($monitor_fp, $opt{"BOOTED_TIMEOUT"}); + $line = wait_for_input($monitor_fp, $booted_timeout); } else { $line = wait_for_input($monitor_fp); } @@ -349,7 +404,7 @@ sub monitor { # we are not guaranteed to get a full line $full_line .= $line; - if ($full_line =~ /$opt{"SUCCESS_LINE"}/) { + if ($full_line =~ /$success_line/) { $booted = 1; } @@ -376,29 +431,29 @@ sub monitor { close(DMESG); - if (!$booted) { - return 0 if ($in_bisect); - fail "failed - never got a boot prompt.\n" and return 0; - } - if ($bug) { return 0 if ($in_bisect); fail "failed - got a bug report\n" and return 0; } + if (!$booted) { + return 0 if ($in_bisect); + fail "failed - never got a boot prompt.\n" and return 0; + } + return 1; } sub install { - run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}" or + run_command "scp $outputdir/$build_target $target:$target_image" or dodie "failed to copy image"; my $install_mods = 0; # should we process modules? $install_mods = 0; - open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); + open(IN, "$outputdir/.config") or dodie("Can't read config file"); while () { if (/CONFIG_MODULES(=y)?/) { $install_mods = 1 if (defined($1)); @@ -412,7 +467,7 @@ sub install { return; } - run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install" or + run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or dodie "Failed to install modules"; my $modlib = "/lib/modules/$version"; @@ -422,13 +477,13 @@ sub install { dodie "failed to remove old mods: $modlib"; # would be nice if scp -r did not follow symbolic links - run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version" or + run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or dodie "making tarball"; - run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp" or + run_command "scp $tmpdir/$modtar $target:/tmp" or dodie "failed to copy modules"; - unlink "$opt{TMP_DIR}/$modtar"; + unlink "$tmpdir/$modtar"; run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or dodie "failed to tar modules"; @@ -456,7 +511,7 @@ sub check_buildlog { if (/^\s*(.*?):.*(warning|error)/) { my $err = $1; foreach my $file (@files) { - my $fullpath = "$opt{BUILD_DIR}/$file"; + my $fullpath = "$builddir/$file"; if ($file eq $err || $fullpath eq $err) { fail "$file built with warnings" and return 0; } @@ -476,7 +531,7 @@ sub build { unlink $buildlog; if ($type =~ /^useconfig:(.*)/) { - run_command "cp $1 $opt{OUTPUT_DIR}/.config" or + run_command "cp $1 $outputdir/.config" or dodie "could not copy $1 to .config"; $type = "oldconfig"; @@ -487,38 +542,38 @@ sub build { $append = "yes ''|"; # allow for empty configs - run_command "touch $opt{OUTPUT_DIR}/.config"; + run_command "touch $outputdir/.config"; - run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp" or + run_command "mv $outputdir/.config $outputdir/config_temp" or dodie "moving .config"; if (!$noclean && !run_command "$make mrproper") { dodie "make mrproper"; } - run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config" or + run_command "mv $outputdir/config_temp $outputdir/.config" or dodie "moving config_temp"; } elsif (!$noclean) { - unlink "$opt{OUTPUT_DIR}/.config"; + unlink "$outputdir/.config"; run_command "$make mrproper" or dodie "make mrproper"; } # add something to distinguish this build - open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file"); - print OUT "$opt{LOCALVERSION}\n"; + open(OUT, "> $outputdir/localversion") or dodie("Can't make localversion file"); + print OUT "$localversion\n"; close(OUT); if (defined($minconfig)) { $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - run_command "$defconfig $append $make $type" or + run_command "$append $defconfig $make $type" or dodie "failed make config"; - $redirect = "$opt{TMP_DIR}/buildlog"; - if (!run_command "$make $opt{BUILD_OPTIONS}") { + $redirect = "$buildlog"; + if (!run_command "$make $build_options") { undef $redirect; # bisect may need this to pass return 0 if ($in_bisect); @@ -530,9 +585,9 @@ sub build { } sub halt { - if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { + if (!run_command "ssh $target halt" or defined($power_off)) { # nope? the zap it! - run_command "$opt{POWER_OFF}"; + run_command "$power_off"; } } @@ -541,15 +596,17 @@ sub success { doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; - doprint "** SUCCESS!!!! **\n"; + doprint "** TEST $i SUCCESS!!!! **\n"; doprint "*******************************************\n"; doprint "*******************************************\n"; - if ($i != $opt{"NUM_BUILDS"}) { - doprint "Reboot and wait $opt{SLEEP_TIME} seconds\n"; + if ($i != $opt{"NUM_TESTS"} && $test_type ne "build" && + !($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") && + !($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build")) { + doprint "Reboot and wait $sleep_time seconds\n"; reboot; start_monitor; - wait_for_monitor $opt{"SLEEP_TIME"}; + wait_for_monitor $sleep_time; end_monitor; } } @@ -566,9 +623,9 @@ sub child_run_test { my $failed = 0; # child should have no power - $opt{"REBOOT_ON_ERROR"} = 0; - $opt{"POWEROFF_ON_ERROR"} = 0; - $opt{"DIE_ON_FAILURE"} = 1; + $reboot_on_error = 0; + $poweroff_on_error = 0; + $die_on_failure = 1; run_command $run_test or $failed = 1; exit $failed; @@ -638,6 +695,36 @@ sub do_run_test { return 1; } +sub run_git_bisect { + my ($command) = @_; + + doprint "$command ... "; + + my $output = `$command 2>&1`; + my $ret = $?; + + logit $output; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to git bisect"; + } + + doprint "SUCCESS\n"; + 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; + doprint "Found bad commit... $1\n"; + return 0; + } else { + # we already logged it, just print it now. + print $output; + } + + return 1; +} + sub run_bisect { my ($type) = @_; @@ -676,11 +763,11 @@ sub run_bisect { $result = "bad"; # reboot the box to a good kernel - if ($type eq "boot") { - doprint "Reboot and sleep $opt{BISECT_SLEEP_TIME} seconds\n"; + if ($type ne "build") { + doprint "Reboot and sleep $bisect_sleep_time seconds\n"; reboot; start_monitor; - wait_for_monitor $opt{"BISECT_SLEEP_TIME"}; + wait_for_monitor $bisect_sleep_time; end_monitor; } } else { @@ -696,31 +783,7 @@ sub run_bisect { } } - doprint "git bisect $result ... "; - $output = `git bisect $result 2>&1`; - $ret = $?; - - logit $output; - - if ($ret) { - doprint "FAILED\n"; - fail "Failed to git bisect"; - } - - doprint "SUCCESS\n"; - 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; - doprint "Found bad commit... $1\n"; - return 0; - } else { - # we already logged it, just print it now. - print $output; - } - - - return 1; + return $result; } sub bisect { @@ -735,6 +798,8 @@ sub bisect { 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($opt{"BISECT_REVERSE[$i]"}) && $opt{"BISECT_REVERSE[$i]"} == 1) { @@ -746,23 +811,83 @@ sub bisect { $in_bisect = 1; - run_command "git bisect start" or - fail "could not start bisect"; - - run_command "git bisect good $good" or - fail "could not set bisect good to $good"; - - run_command "git bisect bad $bad" or - fail "could not set bisect good to $bad"; - # Can't have a test without having a test to run if ($type eq "test" && !defined($run_test)) { $type = "boot"; } + my $check = $opt{"BISECT_CHECK[$i]"}; + if (defined($check) && $check ne "0") { + + # get current HEAD + doprint "git rev-list HEAD --max-count=1 ... "; + my $head = `git rev-list HEAD --max-count=1`; + my $ret = $?; + + logit $head; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to get git HEAD"; + } + + print "SUCCESS\n"; + + chomp $head; + + if ($check ne "good") { + doprint "TESTING BISECT BAD [$bad]\n"; + run_command "git checkout $bad" or + die "Failed to checkout $bad"; + + $result = run_bisect $type; + + if ($result ne "bad") { + fail "Tested BISECT_BAD [$bad] and it succeeded" and return 0; + } + } + + if ($check ne "bad") { + doprint "TESTING BISECT GOOD [$good]\n"; + run_command "git checkout $good" or + die "Failed to checkout $good"; + + $result = run_bisect $type; + + if ($result ne "good") { + fail "Tested BISECT_GOOD [$good] and it failed" and return 0; + } + } + + # checkout where we started + run_command "git checkout $head" or + die "Failed to checkout $head"; + } + + run_command "git bisect start" or + dodie "could not start bisect"; + + run_command "git bisect good $good" or + dodie "could not set bisect good to $good"; + + run_git_bisect "git bisect bad $bad" or + dodie "could not set bisect bad to $bad"; + + if (defined($replay)) { + run_command "git bisect replay $replay" or + dodie "failed to run replay"; + } + + if (defined($start)) { + run_command "git checkout $start" or + dodie "failed to checkout $start"; + } + + my $test; do { $result = run_bisect $type; - } while ($result); + $test = run_git_bisect "git bisect $result"; + } while ($test); run_command "git bisect log" or dodie "could not capture git bisect log"; @@ -883,11 +1008,6 @@ die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); -die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"})); - -chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; - -$target = "$opt{SSH_USER}\@$opt{MACHINE}"; if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { unlink $opt{"LOG_FILE"}; @@ -899,11 +1019,7 @@ foreach my $option (sort keys %opt) { doprint "$option = $opt{$option}\n"; } -$buildlog = "$opt{TMP_DIR}/buildlog"; -$dmesg = "$opt{TMP_DIR}/dmesg"; -$make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; - -sub set_build_option { +sub set_test_option { my ($name, $i) = @_; my $option = "$name\[$i\]"; @@ -920,16 +1036,74 @@ sub set_build_option { } # First we need to do is the builds -for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { +for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { - $build_type = set_build_option("BUILD_TYPE", $i); - $noclean = set_build_option("BUILD_NOCLEAN", $i); - $minconfig = set_build_option("MIN_CONFIG", $i); - $run_test = set_build_option("TEST", $i); - $addconfig = set_build_option("ADD_CONFIG", $i); + my $ssh_user = set_test_option("SSH_USER", $i); + my $makecmd = set_test_option("MAKE_CMD", $i); + + $machine = set_test_option("MACHINE", $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); + $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); + $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); + $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); + $build_target = set_test_option("BUILD_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 $tmpdir) { + mkpath($tmpdir) or + die "can't create $tmpdir"; + } + + $target = "$ssh_user\@$machine"; + + $buildlog = "$tmpdir/buildlog-$machine"; + $dmesg = "$tmpdir/dmesg-$machine"; + $make = "$makecmd O=$outputdir"; + + if ($reboot_type eq "grub") { + dodie "GRUB_MENU not defined\n" if (!defined($grub_menu)); + } elsif (!defined($reboot_script)) { + dodie "REBOOT_SCRIPT not defined\n" + } + + my $run_type = $build_type; + if ($test_type eq "patchcheck") { + $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; + } elsif ($test_type eq "bisect") { + $run_type = $opt{"BISECT_TYPE[$i]"}; + } + + # mistake in config file? + if (!defined($run_type)) { + $run_type = "ERROR"; + } doprint "\n\n"; - doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $build_type\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n"; unlink $dmesg; unlink $buildlog; @@ -938,9 +1112,9 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { $minconfig = $addconfig; } elsif (defined($addconfig)) { - run_command "cat $addconfig $minconfig > $opt{TMP_DIR}/use_config" or + run_command "cat $addconfig $minconfig > $tmpdir/use_config" or dodie "Failed to create temp config"; - $minconfig = "$opt{TMP_DIR}/use_config"; + $minconfig = "$tmpdir/use_config"; } my $checkout = $opt{"CHECKOUT[$i]"}; @@ -949,10 +1123,10 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { die "failed to checkout $checkout"; } - if ($build_type eq "bisect") { + if ($test_type eq "bisect") { bisect $i; next; - } elsif ($build_type eq "patchcheck") { + } elsif ($test_type eq "patchcheck") { patchcheck $i; next; } @@ -961,25 +1135,28 @@ for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { build $build_type or next; } - get_grub_index; - get_version; - install; + if ($test_type ne "build") { + get_grub_index; + get_version; + install; - my $failed = 0; - start_monitor; - monitor or $failed = 1;; - if (!$failed && defined($run_test)) { - do_run_test or $failed = 1; + my $failed = 0; + start_monitor; + monitor or $failed = 1;; + + if (!$failed && $test_type ne "boot" && defined($run_test)) { + do_run_test or $failed = 1; + } + end_monitor; + next if ($failed); } - end_monitor; - next if ($failed); success $i; } if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} elsif ($opt{"REBOOT_ON_SUCCESS"}) { +} elsif ($opt{"REBOOT_ON_SUCCESS"} && $test_type ne "build") { reboot; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf new file mode 100644 index 000000000000..42f803fe4a8d --- /dev/null +++ b/tools/testing/ktest/sample.conf @@ -0,0 +1,330 @@ +# +# Config file for autotest.pl +# +# Note, all paths must be absolute +# + +# Almost all options may be overwritten per test run, by appending +# a [x] to the config. For example, to change the test type for +# the third iteration of tests, you can specify: +# (1 is for the first test, 2 for the second, and so on) +# +# TEST_TYPE[3] = build +# +# The options that can not be changed like this are: +# NUM_TESTS +# LOG_FILE +# CLEAR_LOG +# POWEROFF_ON_SUCCESS +# REBOOT_ON_SUCCESS +# + +#### Mandatory Config Options #### + +# The machine hostname that you will test +#MACHINE = target + +# The box is expected to have ssh on normal bootup, provide the user +# (most likely root, since you need privileged operations) +#SSH_USER = root + +# The directory that contains the Linux source code +#BUILD_DIR = /home/test/linux.git + +# The directory that the objects will be built +# (can not be same as BUILD_DIR) +#OUTPUT_DIR = /home/test/build/target + +# The location of the compiled file to copy to the target +# (relative to OUTPUT_DIR) +#BUILD_TARGET = arch/x86/boot/bzImage + +# The place to put your image on the test machine +#TARGET_IMAGE = /boot/vmlinuz-test + +# A script or command to reboot the box +# Here is a digital loggers power switch example +#POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=CCL' +# Here is an example to reboot a virtual box on the current host +# with the name "Guest". +#POWER_CYCLE = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy; sleep 5; virsh start Guest + +# The script or command that reads the console +# If you use ttywatch server, something like the following would work. +#CONSOLE = nc -d localhost 3001 +# For a virtual machine with guest name "Guest". +#CONSOLE = virsh console `virsh list | grep '\' | awk '{printf ("%d", $1)}'` + +# Required version ending to differentiate the test +# from other linux builds on the system. +#LOCALVERSION = -test + +# The grub title name for the test kernel to boot +# (Only mandatory if REBOOT_TYPE = grub) +# +# For example, if in the /boot/grub/menu.lst the test kernel title has: +# title Test Kernel +#GRUB_MENU = Test Kernel + +# A script to reboot the target into the test kernel +# (Only mandatory if REBOOT_TYPE = script) +#REBOOT_SCRIPT = + +#### Optional Config Options (all have defaults) #### + +# The number of tests to run (default 5) +#NUM_TESTS = 5 + +# 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 +# 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) +#TEST_TYPE = test + +# The build type is any make config type or a command. +# (default randconfig) +# nobuild - skip the clean and build step +#BUILD_TYPE = randconfig + +# The make command (default make) +# If you are building a 32bit x86 on a 64 bit host +#MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 + +# Way to reboot the box to the test kernel. +# Only valid options so far are "grub" 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 +# and select that target to reboot to the kernel. If this is not +# your setup, then specify "script" and have a command or script +# specified in REBOOT_SCRIPT to boot to the target. +#REBOOT_TYPE = grub + +# Line to define success in output. (default "login:") +# This is what the line contains, not the entire line. If you need +# the entire line to match, then use regural expression syntax like +# ^MyBox Login:$ +#SUCCESS_LINE = login: + +# As the test reads the console, after it hits the SUCCESS_LINE +# the time it waits for the monitor to settle down between reads +# can usually be lowered. +# (in seconds) (default 1) +#BOOTED_TIMEOUT = 1 + +# The timeout in seconds when we consider the box hung after +# the console stop producing output. +# (default 120) +#TIMEOUT = 120 + +# The location on the host where to write temp files +# (default /tmp/autotest) +#TMP_DIR = /tmp/autotest + +# In between tests, a reboot of the box may occur, and this +# is the time to wait for the console after it stops producing +# output. Some machines may not produce a large lag on reboot +# so this should accommodate it. +# (default 60) +#SLEEP_TIME = 60 + +# The time in between bisects to sleep (in seconds) +# Can be less than SLEEP_TIME since bisects do more work +# in between boots. (default 60) +#BISECT_SLEEP_TIME = 60 + +# Build without doing a make mrproper, or removing .config +# (default 0) +#BUILD_NOCLEAN = 0 + +# Reboot the target box on error (default 0) +#REBOOT_ON_ERROR = 0 + +# Power off the target on error (ignored if REBOOT_ON_ERROR is set) +# (default 0) +#POWEROFF_ON_ERROR = 0 + +# Power off the target after all tests have completed successfully +# (default 0) +#POWEROFF_ON_SUCCESS = 0 + +# Reboot the target after all test completed successfully (default 1) +# (ignored if POWEROFF_ON_SUCCESS is set) +#REBOOT_ON_SUCCESS = 1 + +# 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 +# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss +# if the STORE_FAILURES directory is set. +# (default 1) +# Note, even if this is set to zero, there are some errors that still +# stop the tests. +#DIE_ON_FAILURE = 1 + +# Directory to store failure directories on failure. If this is not +# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and +# bootlog. +#STORE_FAILURES = /home/test/failures + +# A script or command to power off the box (default undef) +# Needed for POWEROFF_ON_ERROR and SUCCESS +# Example for digital loggers power switch: +#POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' +# Example for a virtual guest call "Guest". +#POWER_OFF = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy + +# Any build options for the make (default "") +#BUILD_OPTIONS = -j20 + +# Optional log file to write the status (recommended) +# (default undef) +#LOG_FILE = /home/test/logfiles/target.log + +# Remove old logfile if it exists before starting all tests. +# (default 0) +#CLEAR_LOG = 0 + +# Test to run if there is a successful boot and TEST_TYPE is test. +# Must exit with 0 on success and non zero on error +# default (undef) +#TEST = ssh user@machine /root/run_test +#TEST[1] = ssh root@mxtest /root/run_test + +# The min config that is needed to build for the machine +# A nice way to get this to work, is to do a "lsmod > mymods" on the target +# copy it to the build server, and then run "make LSMOD=mymods localyesconfig". +# Then copy all the options that are set: "grep '^CONFIG' > /home/test/config-min" +# +# You might want to set: +# CONFIG_CMDLINE="" +# randconfig may set the above and override your real command +# line options. +# (default undef) +#MIN_CONFIG = /home/test/config-min + +# Sometimes there's options that just break the boot and +# you do not care about. Here are a few: +# # CONFIG_STAGING is not set +# Staging drivers are horrible, and can break the build. +# # CONFIG_SCSI_DEBUG is not set +# SCSI_DEBUG may change your root partition +# # CONFIG_KGDB_SERIAL_CONSOLE is not set +# KGDB may cause oops waiting for a connection that's not there. +# This option points to the file containing config options that will be prepended +# to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) +# before running it through randconfig +# (default undef) +#ADD_CONFIG = /home/test/config-broken + +#### Per test run options #### +# These are options are per build only. The only exist with the [x] +# syntax, and there is no general option. +# +# All are optional and undef by default +# +# CHECKOUT[x] = branch +# +# If the BUILD_DIR is a git repository, then you can set this option +# to checkout the given branch before running the TEST. If you +# specify this for the first run, that branch will be used for +# all preceding tests until a new CHECKOUT[x] is set. +# +# For TEST_TYPE[x] = patchcheck +# +# This expects the BUILD_DIR to be a git repository, and +# will checkout the PATCHCHECK_START[x]. +# +# PATCHCHECK_START[x] is required and is the first patch to +# test (the SHA1 of the commit). +# +# PATCHCHECK_END[x] is the last patch to check (default HEAD) +# +# PATCHCHECK_TYPE[x] is required and is the type of test to run: +# 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. +# +# 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 +# what makes patchcheck different from the other tests, is if +# BUILD_NOCLEAN is not set, only the first and last patch run +# make mrproper. This helps speed up the test. +# +# Example: +# TEST_TYPE[1] = patchcheck +# CHECKOUT[1] = mybranch +# PATCHCHECK_TYPE[1] = boot +# PATCHCHECK_START[1] = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 +# PATCHCHEKC_END[1] = b8b2663bd7c9da04ac804659b9f617c199d0252c +# +# +# For TEST_TYPE[x] = bisect +# +# You can specify a git bisect if the BUILD_DIR is a git repository. +# The MIN_CONFIG will be used for all builds of the bisect. The build type +# used for bisecting is oldconfig. +# +# BISECT_TYPE[x] is the type of test to perform: +# build - bad fails to build +# boot - bad builds but fails to boot +# test - bad boots but fails a test +# +# BISECT_GOOD[x] is the commit (SHA1) to label as good +# BISECT_BAD[x] is the commit to label as bad +# +# The above three options are required for a bisect operation. +# +# BISECT_REPLAY[x] = /path/to/replay/file (optional, default undefined) +# +# If an operation failed in the bisect that was not expected to +# fail. Then the test ends. The state of the BUILD_DIR will be +# left off at where the failur occurred. You can examine the +# reason for the failure, and perhaps even find a git commit +# that would work to continue with. You can run: +# +# git bisect log > /path/to/replay/file +# +# and if BISECT_REPLAY[x] is set, the test will run git bisect replay +# before continuing with the bisect. +# +# BISECT_START[x] = commit (optional, default undefined) +# +# As with BISECT_REPLAY[x], if the test failed on a commit that +# just happen to have a bad commit in the middle of the bisect, +# and you need to skip it. If BISECT_START[x] is defined, it +# will checkout that commit before continuing with the bisect. +# +# Note, BISECT_REPLAY[x] is executed before BISECT_START[x]. +# +# BISECT_REVERSE[x] = 1 (optional, default 0) +# +# In those strange instances where it was broken forever +# and you are trying to find where it started to work! +# Set BISECT_GOOD[x] to the commit that was last known to fail +# Set BISECT_BAD[x] to the commit that is known where it started +# to work. With BISECT_REVERSE[x] = 1, The test will consider +# failures as good, and success as bad. +# +# BISECT_CHECK[x] = 1 (optional, default 0) +# +# Just to be sure the good is good and bad is bad, setting +# BISECT_CHECK[x] to 1 will start the bisect by first checking +# out BISECT_BAD[x] and makes sure it fails, then it will check +# out BISECT_GOOD[x] and makes sure it succeeds before starting +# the bisect (it works for BISECT_REVERSE[x] too). +# +# You can limit the test to just check BISECT_GOOD[x] or +# BISECT_BAD[x] with BISECT_CHECK[x] = good or +# BISECT_CHECK[x] = bad, respectively. +# +# Example: +# TEST_TYPE[1] = bisect +# BISECT_GOOD[1] = v2.6.36 +# BISECT_BAD[1] = b5153163ed580e00c67bdfecb02b2e3843817b3e +# BISECT_TYPE[1] = build +# MIN_CONFIG[1] = /home/test/config-bisect From 8b37ca8cac46b2c108386e3901cc8611dc81c7aa Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:33 -0400 Subject: [PATCH 12/30] ktest: Add POST_INSTALL to allow initrds to be created Add a POST_INSTALL option that runs after the build and install but before rebooting to the test kernel. This alls the user to run a script that will install an initrd (or anything else that may be special) before booting. An environment variable KERNEL_VERSION is set. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 11 +++++++++++ tools/testing/ktest/sample.conf | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 1f28c45f15b8..687a85475af5 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -58,6 +58,7 @@ my $grub_menu; my $grub_number; my $target; my $make; +my $post_install; my $noclean; my $minconfig; my $addconfig; @@ -489,6 +490,15 @@ sub install { dodie "failed to tar modules"; run_command "ssh $target rm -f /tmp/$modtar"; + + return if (!defined($post_install)); + + my $save_env = $ENV{KERNEL_VERSION}; + + $ENV{KERNEL_VERSION} = $version; + run_command "$post_install"; + + $ENV{KERNEL_VERSION} = $save_env; } sub check_buildlog { @@ -1055,6 +1065,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $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); diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 42f803fe4a8d..0115a6762a6d 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -93,6 +93,11 @@ # If you are building a 32bit x86 on a 64 bit host #MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 +# 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. +#POST_INSTALL = ssh user@target /sbin/mkinitrd --allow-missing -f /boot/initramfs-test.img $KERNEL_VERSION + # Way to reboot the box to the test kernel. # Only valid options so far are "grub" and "script" # (default grub) From 576f627c817dff0b7081374287a77247325b9cc0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:58:38 -0400 Subject: [PATCH 13/30] ktest: Add poweroff after halt and powercycle after reboot Added the options POWEROFF_AFTER_HALT to handle boxes that do not really shut off after a halt is called. Added POWERCYCLE_AFTER_REBOOT to force a power cycle for boxes that don't reboot but get stuck during the reboot. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 63 +++++++++++++++++++++++++-------- tools/testing/ktest/sample.conf | 18 ++++++++++ 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 687a85475af5..ef978171ecb7 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -53,6 +53,8 @@ my $power_cycle; my $reboot_on_error; my $poweroff_on_error; my $die_on_failure; +my $powercycle_after_reboot; +my $poweroff_after_halt; my $power_off; my $grub_menu; my $grub_number; @@ -83,6 +85,7 @@ my $success_line; my $build_target; my $target_image; my $localversion; +my $iteration = 0; sub read_config { my ($config) = @_; @@ -133,16 +136,32 @@ sub run_command; sub reboot { # try to reboot normally - if (!run_command "ssh $target reboot") { + if (run_command "ssh $target reboot") { + if (defined($powercycle_after_reboot)) { + sleep $powercycle_after_reboot; + run_command "$power_cycle"; + } + } else { # nope? power cycle it. run_command "$power_cycle"; } } +sub do_not_reboot { + my $i = $iteration; + + return $test_type eq "build" || + ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") || + ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build"); +} + sub dodie { doprint "CRITICAL FAILURE... ", @_, "\n"; - if ($reboot_on_error && $test_type ne "build") { + my $i = $iteration; + + if ($reboot_on_error && !do_not_reboot) { + doprint "REBOOTING\n"; reboot; @@ -151,7 +170,7 @@ sub dodie { `$power_off`; } - die @_; + die @_, "\n"; } sub open_console { @@ -163,9 +182,9 @@ sub open_console { dodie "Can't open console $console"; $flags = fcntl($fp, F_GETFL, 0) or - dodie "Can't get flags for the socket: $!\n"; + dodie "Can't get flags for the socket: $!"; $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or - dodie "Can't set flags for the socket: $!\n"; + dodie "Can't set flags for the socket: $!"; return $pid; } @@ -221,8 +240,10 @@ sub fail { doprint "FAILED\n"; + my $i = $iteration; + # no need to reboot for just building. - if ($test_type ne "build") { + if (!do_not_reboot) { doprint "REBOOTING\n"; reboot; start_monitor; @@ -230,7 +251,11 @@ sub fail { end_monitor; } + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doprint "**** Failed: ", @_, " ****\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; return 1 if (!defined($store_failures)); @@ -434,12 +459,12 @@ sub monitor { if ($bug) { return 0 if ($in_bisect); - fail "failed - got a bug report\n" and return 0; + fail "failed - got a bug report" and return 0; } if (!$booted) { return 0 if ($in_bisect); - fail "failed - never got a boot prompt.\n" and return 0; + fail "failed - never got a boot prompt." and return 0; } return 1; @@ -496,7 +521,8 @@ sub install { my $save_env = $ENV{KERNEL_VERSION}; $ENV{KERNEL_VERSION} = $version; - run_command "$post_install"; + run_command "$post_install" or + dodie "Failed to run post install"; $ENV{KERNEL_VERSION} = $save_env; } @@ -596,6 +622,11 @@ sub build { sub halt { if (!run_command "ssh $target halt" or defined($power_off)) { + if (defined($poweroff_after_halt)) { + sleep $poweroff_after_halt; + run_command "$power_off"; + } + } else { # nope? the zap it! run_command "$power_off"; } @@ -610,9 +641,7 @@ sub success { doprint "*******************************************\n"; doprint "*******************************************\n"; - if ($i != $opt{"NUM_TESTS"} && $test_type ne "build" && - !($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") && - !($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build")) { + if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { doprint "Reboot and wait $sleep_time seconds\n"; reboot; start_monitor; @@ -1048,6 +1077,8 @@ sub set_test_option { # First we need to do is the builds for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { + $iteration = $i; + my $ssh_user = set_test_option("SSH_USER", $i); my $makecmd = set_test_option("MAKE_CMD", $i); @@ -1071,6 +1102,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $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); @@ -1096,9 +1129,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $make = "$makecmd O=$outputdir"; if ($reboot_type eq "grub") { - dodie "GRUB_MENU not defined\n" if (!defined($grub_menu)); + dodie "GRUB_MENU not defined" if (!defined($grub_menu)); } elsif (!defined($reboot_script)) { - dodie "REBOOT_SCRIPT not defined\n" + dodie "REBOOT_SCRIPT not defined" } my $run_type = $build_type; @@ -1167,7 +1200,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { if ($opt{"POWEROFF_ON_SUCCESS"}) { halt; -} elsif ($opt{"REBOOT_ON_SUCCESS"} && $test_type ne "build") { +} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { reboot; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 0115a6762a6d..546014a6bb03 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -160,6 +160,24 @@ # (ignored if POWEROFF_ON_SUCCESS is set) #REBOOT_ON_SUCCESS = 1 +# In case there's isses with rebooting, you can specify this +# to always powercycle after this amount of time after calling +# reboot. +# Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just +# makes it powercycle immediately after rebooting. Do not define +# it if you do not want it. +# (default undefined) +#POWERCYCLE_AFTER_REBOOT = 5 + +# In case there's isses with halting, you can specify this +# to always poweroff after this amount of time after calling +# halt. +# Note, POWEROFF_AFTER_HALT = 0 does NOT disable it. It just +# makes it poweroff immediately after halting. Do not define +# it if you do not want it. +# (default undefined) +#POWEROFF_AFTER_HALT = 20 + # 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 From a57419b366a20009c9363c3cdad2449dc2fa8b9c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 15:13:54 -0400 Subject: [PATCH 14/30] ktest: New TEST_START instead of using [], and use real SHA1s Change the config to use TEST_START where the options after a TEST_START automatically get the [] as it is read and they do not need to exist in the config file; TEST_START MIN_CONFIG = myconfig is the same as MIN_CONFIG[1] = myconfig The benefit is that you no longer need to keep track of test numbers with tests. Also process the commit ids that are passed to the options to get the actually SHA1 so it is no longer relative to the branch. Ie, saying HEAD will get the current SHA1 and then that will be used, and will work even if another branch is checked out. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 206 ++++++++++++++++--- tools/testing/ktest/sample.conf | 345 +++++++++++++++++++++++--------- 2 files changed, 433 insertions(+), 118 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index ef978171ecb7..a7e86e391172 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -11,21 +11,23 @@ use File::Path qw(mkpath); use File::Copy qw(cp); use FileHandle; -$#ARGV >= 0 || die "usage: autotest.pl config-file\n"; +$#ARGV >= 0 || die "usage: ktest.pl config-file\n"; $| = 1; my %opt; +my %repeat_tests; +my %repeats; my %default; #default opts -$default{"NUM_TESTS"} = 5; +$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/autotest"; +$default{"TMP_DIR"} = "/tmp/ktest"; $default{"SLEEP_TIME"} = 60; # sleep time between tests $default{"BUILD_NOCLEAN"} = 0; $default{"REBOOT_ON_ERROR"} = 0; @@ -87,29 +89,138 @@ my $target_image; my $localversion; my $iteration = 0; +sub set_value { + my ($lvalue, $rvalue) = @_; + + if (defined($opt{$lvalue})) { + die "Error: Option $lvalue defined more than once!\n"; + } + $opt{$lvalue} = $rvalue; +} + sub read_config { my ($config) = @_; open(IN, $config) || die "can't read file $config"; + my $name = $config; + $name =~ s,.*/(.*),$1,; + + my $test_num = 0; + my $default = 1; + my $repeat = 1; + my $num_tests_set = 0; + my $skip = 0; + my $rest; + while () { # ignore blank lines and comments next if (/^\s*$/ || /\s*\#/); - if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) { + if (/^\s*TEST_START(.*)/) { + + $rest = $1; + + if ($num_tests_set) { + die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; + } + + my $old_test_num = $test_num; + + $test_num += $repeat; + $default = 0; + $repeat = 1; + + if ($rest =~ /\s+SKIP(.*)/) { + $rest = $1; + $skip = 1; + } else { + $skip = 0; + } + + if ($rest =~ /\s+ITERATE\s+(\d+)(.*)$/) { + $repeat = $1; + $rest = $2; + $repeat_tests{"$test_num"} = $repeat; + } + + if ($rest =~ /\s+SKIP(.*)/) { + $rest = $1; + $skip = 1; + } + + if ($rest !~ /^\s*$/) { + die "$name: $.: Gargbage found after TEST_START\n$_"; + } + + if ($skip) { + $test_num = $old_test_num; + $repeat = 1; + } + + } elsif (/^\s*DEFAULTS(.*)$/) { + $default = 1; + + $rest = $1; + + if ($rest =~ /\s+SKIP(.*)/) { + $rest = $1; + $skip = 1; + } else { + $skip = 0; + } + + if ($rest !~ /^\s*$/) { + die "$name: $.: Gargbage found after DEFAULTS\n$_"; + } + + } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) { + + next if ($skip); + my $lvalue = $1; my $rvalue = $2; - if (defined($opt{$lvalue})) { - die "Error: Option $lvalue defined more than once!\n"; + if (!$default && + ($lvalue eq "NUM_TESTS" || + $lvalue eq "LOG_FILE" || + $lvalue eq "CLEAR_LOG")) { + die "$name: $.: $lvalue must be set in DEFAULTS section\n"; } - $opt{$lvalue} = $rvalue; + + if ($lvalue eq "NUM_TESTS") { + if ($test_num) { + die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; + } + if (!$default) { + die "$name: $.: NUM_TESTS must be set in default section\n"; + } + $num_tests_set = 1; + } + + if ($default || $lvalue =~ /\[\d+\]$/) { + set_value($lvalue, $rvalue); + } else { + my $val = "$lvalue\[$test_num\]"; + set_value($val, $rvalue); + + if ($repeat > 1) { + $repeats{$val} = $repeat; + } + } + } else { + die "$name: $.: Garbage found in config\n$_"; } } close(IN); + if ($test_num) { + $test_num += $repeat - 1; + $opt{"NUM_TESTS"} = $test_num; + } + # set any defaults foreach my $default (keys %default) { @@ -398,6 +509,27 @@ sub reboot_to { run_command "$reboot_script"; } +sub get_sha1 { + my ($commit) = @_; + + doprint "git rev-list --max-count=1 $commit ... "; + my $sha1 = `git rev-list --max-count=1 $commit`; + my $ret = $?; + + logit $sha1; + + if ($ret) { + doprint "FAILED\n"; + dodie "Failed to get git $commit"; + } + + print "SUCCESS\n"; + + chomp $sha1; + + return $sha1; +} + sub monitor { my $booted = 0; my $bug = 0; @@ -497,7 +629,7 @@ sub install { dodie "Failed to install modules"; my $modlib = "/lib/modules/$version"; - my $modtar = "autotest-mods.tar.bz2"; + my $modtar = "ktest-mods.tar.bz2"; run_command "ssh $target rm -rf $modlib" or dodie "failed to remove old mods: $modlib"; @@ -840,6 +972,10 @@ sub bisect { my $start = $opt{"BISECT_START[$i]"}; my $replay = $opt{"BISECT_REPLAY[$i]"}; + # convert to true sha1's + $good = get_sha1($good); + $bad = get_sha1($bad); + if (defined($opt{"BISECT_REVERSE[$i]"}) && $opt{"BISECT_REVERSE[$i]"} == 1) { doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; @@ -859,20 +995,7 @@ sub bisect { if (defined($check) && $check ne "0") { # get current HEAD - doprint "git rev-list HEAD --max-count=1 ... "; - my $head = `git rev-list HEAD --max-count=1`; - my $ret = $?; - - logit $head; - - if ($ret) { - doprint "FAILED\n"; - dodie "Failed to get git HEAD"; - } - - print "SUCCESS\n"; - - chomp $head; + my $head = get_sha1("HEAD"); if ($check ne "good") { doprint "TESTING BISECT BAD [$bad]\n"; @@ -956,6 +1079,10 @@ sub patchcheck { $end = $opt{"PATCHCHECK_END[$i]"}; } + # 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]"}; # Can't have a test without having a test to run @@ -1054,8 +1181,29 @@ if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { doprint "\n\nSTARTING AUTOMATED TESTS\n\n"; -foreach my $option (sort keys %opt) { - doprint "$option = $opt{$option}\n"; +for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) { + + if (!$i) { + doprint "DEFAULT OPTIONS:\n"; + } else { + doprint "\nTEST $i OPTIONS"; + if (defined($repeat_tests{$i})) { + $repeat = $repeat_tests{$i}; + doprint " ITERATE $repeat"; + } + doprint "\n"; + } + + foreach my $option (sort keys %opt) { + + if ($option =~ /\[(\d+)\]$/) { + next if ($i != $1); + } else { + next if ($i); + } + + doprint "$option = $opt{$option}\n"; + } } sub set_test_option { @@ -1067,6 +1215,16 @@ sub set_test_option { return $opt{$option}; } + foreach my $test (keys %repeat_tests) { + if ($i >= $test && + $i < $test + $repeat_tests{$test}) { + $option = "$name\[$test\]"; + if (defined($opt{$option})) { + return $opt{$option}; + } + } + } + if (defined($opt{$name})) { return $opt{$name}; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 546014a6bb03..9236fe977fa2 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -1,25 +1,83 @@ # -# Config file for autotest.pl +# Config file for ktest.pl # # Note, all paths must be absolute # -# Almost all options may be overwritten per test run, by appending -# a [x] to the config. For example, to change the test type for -# the third iteration of tests, you can specify: -# (1 is for the first test, 2 for the second, and so on) +# Options set in the beginning of the file are considered to be +# default options. These options can be overriden by test specific +# options, with the following exceptions: # -# TEST_TYPE[3] = build -# -# The options that can not be changed like this are: -# NUM_TESTS # LOG_FILE # CLEAR_LOG # POWEROFF_ON_SUCCESS # REBOOT_ON_SUCCESS # +# Test specific options are set after the label: +# +# TEST_START +# +# The options after a TEST_START label are specific to that test. +# Each TEST_START label will set up a new test. If you want to +# perform a test more than once, you can add the ITERATE label +# to it followed by the number of times you want that test +# to iterate. If the ITERATE is left off, the test will only +# be performed once. +# +# TEST_START ITERATE 10 +# +# You can skip a test by adding SKIP (before or after the ITERATE +# and number) +# +# TEST_START SKIP +# +# TEST_START SKIP ITERATE 10 +# +# TEST_START ITERATE 10 SKIP +# +# The SKIP label causes the options and the test itself to be ignored. +# This is useful to set up several different tests in one config file, and +# only enabling the ones you want to use for a current test run. +# +# You can add default options anywhere in the file as well +# with the DEFAULTS tag. This allows you to have default options +# after the test options to keep the test options at the top +# of the file. You can even place the DEFAULTS tag between +# test cases (but not in the middle of a single test case) +# +# TEST_START +# MIN_CONFIG = /home/test/config-test1 +# +# DEFAULTS +# MIN_CONFIG = /home/test/config-default +# +# TEST_START ITERATE 10 +# +# The above will run the first test with MIN_CONFIG set to +# /home/test/config-test-1. Then 10 tests will be executed +# with MIN_CONFIG with /home/test/config-default. +# +# You can also disable defaults with the SKIP option +# +# DEFAULTS SKIP +# MIN_CONFIG = /home/test/config-use-sometimes +# +# DEFAULTS +# MIN_CONFIG = /home/test/config-most-times +# +# The above will ignore the first MIN_CONFIG. If you want to +# use the first MIN_CONFIG, remove the SKIP from the first +# DEFAULTS tag and add it to the second. Be careful, options +# may only be declared once per test or default. If you have +# the same option name under the same test or as default +# ktest will fail to execute, and no tests will run. +# -#### Mandatory Config Options #### + +#### Mandatory Default Options #### + +# These options must be in the default section, although most +# may be overridden by test options. # The machine hostname that you will test #MACHINE = target @@ -43,17 +101,21 @@ #TARGET_IMAGE = /boot/vmlinuz-test # A script or command to reboot the box +# # Here is a digital loggers power switch example #POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=CCL' +# # Here is an example to reboot a virtual box on the current host # with the name "Guest". -#POWER_CYCLE = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy; sleep 5; virsh start Guest +#POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest # The script or command that reads the console +# # If you use ttywatch server, something like the following would work. #CONSOLE = nc -d localhost 3001 +# # For a virtual machine with guest name "Guest". -#CONSOLE = virsh console `virsh list | grep '\' | awk '{printf ("%d", $1)}'` +#CONSOLE = virsh console Guest # Required version ending to differentiate the test # from other linux builds on the system. @@ -62,8 +124,14 @@ # The grub title name for the test kernel to boot # (Only mandatory if REBOOT_TYPE = grub) # +# Note, ktest.pl will not update the grub menu.lst, you need to +# manually add an option for the test. ktest.pl will search +# the grub menu.lst for this option to find what kernel to +# reboot into. +# # For example, if in the /boot/grub/menu.lst the test kernel title has: # title Test Kernel +# kernel vmlinuz-test #GRUB_MENU = Test Kernel # A script to reboot the target into the test kernel @@ -72,21 +140,37 @@ #### Optional Config Options (all have defaults) #### -# The number of tests to run (default 5) -#NUM_TESTS = 5 +# Start a test setup. If you leave this off, all options +# will be default and the test will run once. +# This is a label and not really an option (it takes no value). +# You can append ITERATE and a number after it to iterate the +# test a number of times, or SKIP to ignore this test. +# +#TEST_START +#TEST_START ITERATE 5 +#TEST_START SKIP # 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 +# (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) #TEST_TYPE = test -# The build type is any make config type or a command. +# Test to run if there is a successful boot and TEST_TYPE is test. +# Must exit with 0 on success and non zero on error +# default (undefined) +#TEST = ssh user@machine /root/run_test + +# The build type is any make config type or special command # (default randconfig) # nobuild - skip the clean and build step +# useconfig:/path/to/config - use the given config and run +# oldconfig on it. +# This option is ignored if TEST_TYPE is patchcheck or bisect #BUILD_TYPE = randconfig # The make command (default make) @@ -95,8 +179,14 @@ # 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. +# kernel version that is used. Remember to add the initrd line +# to your grub menu.lst file. +# +# Here's a couple of examples to use: #POST_INSTALL = ssh user@target /sbin/mkinitrd --allow-missing -f /boot/initramfs-test.img $KERNEL_VERSION +# +# or on some systems: +#POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION # Way to reboot the box to the test kernel. # Only valid options so far are "grub" and "script" @@ -106,12 +196,19 @@ # and select that target to reboot to the kernel. If this is not # your setup, then specify "script" and have a command or script # specified in REBOOT_SCRIPT to boot to the target. +# +# The entry in /boot/grub/menu.lst must be entered in manually. +# The test will not modify that file. #REBOOT_TYPE = grub -# Line to define success in output. (default "login:") +# Line to define a successful boot up in console output. # This is what the line contains, not the entire line. If you need -# the entire line to match, then use regural expression syntax like -# ^MyBox Login:$ +# the entire line to match, then use regural expression syntax like: +# (do not add any quotes around it) +# +# SUCCESS_LINE = ^MyBox Login:$ +# +# (default "login:") #SUCCESS_LINE = login: # As the test reads the console, after it hits the SUCCESS_LINE @@ -121,24 +218,33 @@ #BOOTED_TIMEOUT = 1 # The timeout in seconds when we consider the box hung after -# the console stop producing output. +# the console stop producing output. Be sure to leave enough +# time here to get pass a reboot. Some machines may not produce +# any console output for a long time during a reboot. You do +# not want the test to fail just because the system was in +# the process of rebooting to the test kernel. # (default 120) #TIMEOUT = 120 # The location on the host where to write temp files -# (default /tmp/autotest) -#TMP_DIR = /tmp/autotest +# (default /tmp/ktest) +#TMP_DIR = /tmp/ktest # In between tests, a reboot of the box may occur, and this # is the time to wait for the console after it stops producing # output. Some machines may not produce a large lag on reboot # so this should accommodate it. +# The difference between this and TIMEOUT, is that TIMEOUT happens +# when rebooting to the test kernel. This sleep time happens +# after a test has completed and we are about to start running +# 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. # (default 60) #SLEEP_TIME = 60 # The time in between bisects to sleep (in seconds) -# Can be less than SLEEP_TIME since bisects do more work -# in between boots. (default 60) +# (default 60) #BISECT_SLEEP_TIME = 60 # Build without doing a make mrproper, or removing .config @@ -149,10 +255,12 @@ #REBOOT_ON_ERROR = 0 # Power off the target on error (ignored if REBOOT_ON_ERROR is set) +# Note, this is a DEFAULT section only option. # (default 0) #POWEROFF_ON_ERROR = 0 # Power off the target after all tests have completed successfully +# Note, this is a DEFAULT section only option. # (default 0) #POWEROFF_ON_SUCCESS = 0 @@ -160,7 +268,7 @@ # (ignored if POWEROFF_ON_SUCCESS is set) #REBOOT_ON_SUCCESS = 1 -# In case there's isses with rebooting, you can specify this +# In case there are isses with rebooting, you can specify this # to always powercycle after this amount of time after calling # reboot. # Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just @@ -190,43 +298,68 @@ # Directory to store failure directories on failure. If this is not # set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and -# bootlog. +# bootlog. This option is ignored if DIE_ON_FAILURE is not set. +# (default undefined) #STORE_FAILURES = /home/test/failures -# A script or command to power off the box (default undef) +# A script or command to power off the box (default undefined) # Needed for POWEROFF_ON_ERROR and SUCCESS +# # Example for digital loggers power switch: #POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' +# # Example for a virtual guest call "Guest". -#POWER_OFF = virsh list | grep '\' | awk '{printf ("%d", $1)}' | xargs virsh destroy +#POWER_OFF = virsh destroy Guest -# Any build options for the make (default "") +# Any build options for the make of the kernel (not for other makes, like configs) +# (default "") #BUILD_OPTIONS = -j20 # Optional log file to write the status (recommended) -# (default undef) +# Note, this is a DEFAULT section only option. +# (default undefined) #LOG_FILE = /home/test/logfiles/target.log # Remove old logfile if it exists before starting all tests. +# Note, this is a DEFAULT section only option. # (default 0) #CLEAR_LOG = 0 -# Test to run if there is a successful boot and TEST_TYPE is test. -# Must exit with 0 on success and non zero on error -# default (undef) -#TEST = ssh user@machine /root/run_test -#TEST[1] = ssh root@mxtest /root/run_test - # The min config that is needed to build for the machine -# A nice way to get this to work, is to do a "lsmod > mymods" on the target -# copy it to the build server, and then run "make LSMOD=mymods localyesconfig". -# Then copy all the options that are set: "grep '^CONFIG' > /home/test/config-min" +# A nice way to create this is with the following: # -# You might want to set: +# $ ssh target +# $ lsmod > mymods +# $ scp mymods host:/tmp +# $ exit +# $ cd linux.git +# $ rm .config +# $ make LSMOD=mymods localyesconfig +# $ grep '^CONFIG' .config > /home/test/config-min +# +# If you want even less configs: +# +# log in directly to target (do not ssh) +# +# $ su +# # lsmod | cut -d' ' -f1 | xargs rmmod +# +# repeat the above several times +# +# # lsmod > mymods +# # reboot +# +# May need to reboot to get your network back to copy the mymods +# to the host, and then remove the previous .config and run the +# localyesconfig again. The CONFIG_MIN generated like this will +# not guarantee network activity to the box so the TEST_TYPE of +# test may fail. +# +# You might also want to set: # CONFIG_CMDLINE="" # randconfig may set the above and override your real command # line options. -# (default undef) +# (default undefined) #MIN_CONFIG = /home/test/config-min # Sometimes there's options that just break the boot and @@ -239,34 +372,47 @@ # KGDB may cause oops waiting for a connection that's not there. # This option points to the file containing config options that will be prepended # to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) -# before running it through randconfig -# (default undef) +# +# Note, config options in MIN_CONFIG will override these options. +# +# (default undefined) #ADD_CONFIG = /home/test/config-broken #### Per test run options #### -# These are options are per build only. The only exist with the [x] -# syntax, and there is no general option. +# The following options are only allowed in TEST_START sections. +# They are ignored in the DEFAULTS sections. # -# All are optional and undef by default +# All of these are optional and undefined by default, although +# some of these options are required for TEST_TYPE of patchcheck +# and bisect. # -# CHECKOUT[x] = branch +# +# CHECKOUT = branch # # If the BUILD_DIR is a git repository, then you can set this option # to checkout the given branch before running the TEST. If you # specify this for the first run, that branch will be used for -# all preceding tests until a new CHECKOUT[x] is set. +# all preceding tests until a new CHECKOUT is set. # -# For TEST_TYPE[x] = patchcheck +# +# +# For TEST_TYPE = patchcheck # # This expects the BUILD_DIR to be a git repository, and -# will checkout the PATCHCHECK_START[x]. +# will checkout the PATCHCHECK_START commit. # -# PATCHCHECK_START[x] is required and is the first patch to -# test (the SHA1 of the commit). +# The option BUILD_TYPE will be ignored. # -# PATCHCHECK_END[x] is the last patch to check (default HEAD) +# The MIN_CONFIG will be used for all builds of the patchcheck. The build type +# used for patchcheck is oldconfig. # -# PATCHCHECK_TYPE[x] is required and is the type of test to run: +# PATCHCHECK_START is required and is the first patch to +# test (the SHA1 of the commit). You may also specify anything +# that git checkout allows (branch name, tage, HEAD~3). +# +# PATCHCHECK_END is the last patch to check (default HEAD) +# +# PATCHCHECK_TYPE is required and is the type of test to run: # build, boot, test. # # Note, the build test will look for warnings, if a warning occurred @@ -279,75 +425,86 @@ # make mrproper. This helps speed up the test. # # Example: -# TEST_TYPE[1] = patchcheck -# CHECKOUT[1] = mybranch -# PATCHCHECK_TYPE[1] = boot -# PATCHCHECK_START[1] = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 -# PATCHCHEKC_END[1] = b8b2663bd7c9da04ac804659b9f617c199d0252c +# TEST_START +# TEST_TYPE = patchcheck +# CHECKOUT = mybranch +# PATCHCHECK_TYPE = boot +# PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 +# PATCHCHEKC_END = HEAD~2 # # -# For TEST_TYPE[x] = bisect # -# You can specify a git bisect if the BUILD_DIR is a git repository. -# The MIN_CONFIG will be used for all builds of the bisect. The build type -# used for bisecting is oldconfig. +# For TEST_TYPE = bisect # -# BISECT_TYPE[x] is the type of test to perform: +# You can specify a git bisect if the BUILD_DIR is a git repository. +# The MIN_CONFIG will be used for all builds of the bisect. The build type +# used for bisecting is oldconfig. +# +# The option BUILD_TYPE will be ignored. +# +# BISECT_TYPE is the type of test to perform: # build - bad fails to build # boot - bad builds but fails to boot # test - bad boots but fails a test # -# BISECT_GOOD[x] is the commit (SHA1) to label as good -# BISECT_BAD[x] is the commit to label as bad +# BISECT_GOOD is the commit (SHA1) to label as good (accepts all git good commit types) +# BISECT_BAD is the commit to label as bad (accepts all git bad commit types) # # The above three options are required for a bisect operation. # -# BISECT_REPLAY[x] = /path/to/replay/file (optional, default undefined) +# BISECT_REPLAY = /path/to/replay/file (optional, default undefined) # # If an operation failed in the bisect that was not expected to # fail. Then the test ends. The state of the BUILD_DIR will be -# left off at where the failur occurred. You can examine the +# left off at where the failure occurred. You can examine the # reason for the failure, and perhaps even find a git commit # that would work to continue with. You can run: # # git bisect log > /path/to/replay/file # -# and if BISECT_REPLAY[x] is set, the test will run git bisect replay -# before continuing with the bisect. +# The adding: # -# BISECT_START[x] = commit (optional, default undefined) +# BISECT_REPLAY= /path/to/replay/file # -# As with BISECT_REPLAY[x], if the test failed on a commit that +# And running the test again. The test will perform the initial +# git bisect start, git bisect good, and git bisect bad, and +# then it will run git bisect replay on this file, before +# continuing with the bisect. +# +# BISECT_START = commit (optional, default undefined) +# +# As with BISECT_REPLAY, if the test failed on a commit that # just happen to have a bad commit in the middle of the bisect, -# and you need to skip it. If BISECT_START[x] is defined, it -# will checkout that commit before continuing with the bisect. +# and you need to skip it. If BISECT_START is defined, it +# will checkout that commit after doing the initial git bisect start, +# git bisect good, git bisect bad, and running the git bisect replay +# if the BISECT_REPLAY is set. # -# Note, BISECT_REPLAY[x] is executed before BISECT_START[x]. -# -# BISECT_REVERSE[x] = 1 (optional, default 0) +# BISECT_REVERSE = 1 (optional, default 0) # # In those strange instances where it was broken forever # and you are trying to find where it started to work! -# Set BISECT_GOOD[x] to the commit that was last known to fail -# Set BISECT_BAD[x] to the commit that is known where it started -# to work. With BISECT_REVERSE[x] = 1, The test will consider -# failures as good, and success as bad. +# Set BISECT_GOOD to the commit that was last known to fail +# Set BISECT_BAD to the commit that is known to start working. +# With BISECT_REVERSE = 1, The test will consider failures as +# good, and success as bad. # -# BISECT_CHECK[x] = 1 (optional, default 0) +# BISECT_CHECK = 1 (optional, default 0) # # Just to be sure the good is good and bad is bad, setting -# BISECT_CHECK[x] to 1 will start the bisect by first checking -# out BISECT_BAD[x] and makes sure it fails, then it will check -# out BISECT_GOOD[x] and makes sure it succeeds before starting -# the bisect (it works for BISECT_REVERSE[x] too). +# BISECT_CHECK to 1 will start the bisect by first checking +# out BISECT_BAD and makes sure it fails, then it will check +# out BISECT_GOOD and makes sure it succeeds before starting +# the bisect (it works for BISECT_REVERSE too). # -# You can limit the test to just check BISECT_GOOD[x] or -# BISECT_BAD[x] with BISECT_CHECK[x] = good or -# BISECT_CHECK[x] = bad, respectively. +# You can limit the test to just check BISECT_GOOD or +# BISECT_BAD with BISECT_CHECK = good or +# BISECT_CHECK = bad, respectively. # # Example: -# TEST_TYPE[1] = bisect -# BISECT_GOOD[1] = v2.6.36 -# BISECT_BAD[1] = b5153163ed580e00c67bdfecb02b2e3843817b3e -# BISECT_TYPE[1] = build -# MIN_CONFIG[1] = /home/test/config-bisect +# TEST_START +# TEST_TYPE = bisect +# BISECT_GOOD = v2.6.36 +# BISECT_BAD = b5153163ed580e00c67bdfecb02b2e3843817b3e +# BISECT_TYPE = build +# MIN_CONFIG = /home/test/config-bisect From dc89568884ae1b8b96ca6fffe83b404ae472750e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 15:22:53 -0400 Subject: [PATCH 15/30] ktest: Update the sample config file with more documentation Signed-off-by: Steven Rostedt --- tools/testing/ktest/sample.conf | 260 +++++++++++++++++--------------- 1 file changed, 136 insertions(+), 124 deletions(-) diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 9236fe977fa2..03d6e91245fe 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -150,6 +150,18 @@ #TEST_START ITERATE 5 #TEST_START SKIP +# Have the following options as default again. Used after tests +# have already been defined by TEST_START. Optionally, you can +# just define all default options before the first TEST_START +# and you do not need this option. +# +# This is a label and not really an option (it takes no value). +# You can append SKIP to this label and the options within this +# section will be ignored. +# +# DEFAULTS +# DEFAULTS SKIP + # The default test type (default test) # The test types may be: # build - only build the kernel, do nothing else @@ -177,6 +189,10 @@ # If you are building a 32bit x86 on a 64 bit host #MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 +# Any build options for the make of the kernel (not for other makes, like configs) +# (default "") +#BUILD_OPTIONS = -j20 + # 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 @@ -201,130 +217,6 @@ # The test will not modify that file. #REBOOT_TYPE = grub -# Line to define a successful boot up in console output. -# This is what the line contains, not the entire line. If you need -# the entire line to match, then use regural expression syntax like: -# (do not add any quotes around it) -# -# SUCCESS_LINE = ^MyBox Login:$ -# -# (default "login:") -#SUCCESS_LINE = login: - -# As the test reads the console, after it hits the SUCCESS_LINE -# the time it waits for the monitor to settle down between reads -# can usually be lowered. -# (in seconds) (default 1) -#BOOTED_TIMEOUT = 1 - -# The timeout in seconds when we consider the box hung after -# the console stop producing output. Be sure to leave enough -# time here to get pass a reboot. Some machines may not produce -# any console output for a long time during a reboot. You do -# not want the test to fail just because the system was in -# the process of rebooting to the test kernel. -# (default 120) -#TIMEOUT = 120 - -# The location on the host where to write temp files -# (default /tmp/ktest) -#TMP_DIR = /tmp/ktest - -# In between tests, a reboot of the box may occur, and this -# is the time to wait for the console after it stops producing -# output. Some machines may not produce a large lag on reboot -# so this should accommodate it. -# The difference between this and TIMEOUT, is that TIMEOUT happens -# when rebooting to the test kernel. This sleep time happens -# after a test has completed and we are about to start running -# 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. -# (default 60) -#SLEEP_TIME = 60 - -# The time in between bisects to sleep (in seconds) -# (default 60) -#BISECT_SLEEP_TIME = 60 - -# Build without doing a make mrproper, or removing .config -# (default 0) -#BUILD_NOCLEAN = 0 - -# Reboot the target box on error (default 0) -#REBOOT_ON_ERROR = 0 - -# Power off the target on error (ignored if REBOOT_ON_ERROR is set) -# Note, this is a DEFAULT section only option. -# (default 0) -#POWEROFF_ON_ERROR = 0 - -# Power off the target after all tests have completed successfully -# Note, this is a DEFAULT section only option. -# (default 0) -#POWEROFF_ON_SUCCESS = 0 - -# Reboot the target after all test completed successfully (default 1) -# (ignored if POWEROFF_ON_SUCCESS is set) -#REBOOT_ON_SUCCESS = 1 - -# In case there are isses with rebooting, you can specify this -# to always powercycle after this amount of time after calling -# reboot. -# Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just -# makes it powercycle immediately after rebooting. Do not define -# it if you do not want it. -# (default undefined) -#POWERCYCLE_AFTER_REBOOT = 5 - -# In case there's isses with halting, you can specify this -# to always poweroff after this amount of time after calling -# halt. -# Note, POWEROFF_AFTER_HALT = 0 does NOT disable it. It just -# makes it poweroff immediately after halting. Do not define -# it if you do not want it. -# (default undefined) -#POWEROFF_AFTER_HALT = 20 - -# 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 -# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss -# if the STORE_FAILURES directory is set. -# (default 1) -# Note, even if this is set to zero, there are some errors that still -# stop the tests. -#DIE_ON_FAILURE = 1 - -# Directory to store failure directories on failure. If this is not -# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and -# bootlog. This option is ignored if DIE_ON_FAILURE is not set. -# (default undefined) -#STORE_FAILURES = /home/test/failures - -# A script or command to power off the box (default undefined) -# Needed for POWEROFF_ON_ERROR and SUCCESS -# -# Example for digital loggers power switch: -#POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' -# -# Example for a virtual guest call "Guest". -#POWER_OFF = virsh destroy Guest - -# Any build options for the make of the kernel (not for other makes, like configs) -# (default "") -#BUILD_OPTIONS = -j20 - -# Optional log file to write the status (recommended) -# Note, this is a DEFAULT section only option. -# (default undefined) -#LOG_FILE = /home/test/logfiles/target.log - -# Remove old logfile if it exists before starting all tests. -# Note, this is a DEFAULT section only option. -# (default 0) -#CLEAR_LOG = 0 - # The min config that is needed to build for the machine # A nice way to create this is with the following: # @@ -378,6 +270,126 @@ # (default undefined) #ADD_CONFIG = /home/test/config-broken +# The location on the host where to write temp files +# (default /tmp/ktest) +#TMP_DIR = /tmp/ktest + +# Optional log file to write the status (recommended) +# Note, this is a DEFAULT section only option. +# (default undefined) +#LOG_FILE = /home/test/logfiles/target.log + +# Remove old logfile if it exists before starting all tests. +# Note, this is a DEFAULT section only option. +# (default 0) +#CLEAR_LOG = 0 + +# Line to define a successful boot up in console output. +# This is what the line contains, not the entire line. If you need +# the entire line to match, then use regural expression syntax like: +# (do not add any quotes around it) +# +# SUCCESS_LINE = ^MyBox Login:$ +# +# (default "login:") +#SUCCESS_LINE = login: + +# 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 +# MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss +# if the STORE_FAILURES directory is set. +# (default 1) +# Note, even if this is set to zero, there are some errors that still +# stop the tests. +#DIE_ON_FAILURE = 1 + +# Directory to store failure directories on failure. If this is not +# set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and +# bootlog. This option is ignored if DIE_ON_FAILURE is not set. +# (default undefined) +#STORE_FAILURES = /home/test/failures + +# Build without doing a make mrproper, or removing .config +# (default 0) +#BUILD_NOCLEAN = 0 + +# As the test reads the console, after it hits the SUCCESS_LINE +# the time it waits for the monitor to settle down between reads +# can usually be lowered. +# (in seconds) (default 1) +#BOOTED_TIMEOUT = 1 + +# The timeout in seconds when we consider the box hung after +# the console stop producing output. Be sure to leave enough +# time here to get pass a reboot. Some machines may not produce +# any console output for a long time during a reboot. You do +# not want the test to fail just because the system was in +# the process of rebooting to the test kernel. +# (default 120) +#TIMEOUT = 120 + +# In between tests, a reboot of the box may occur, and this +# is the time to wait for the console after it stops producing +# output. Some machines may not produce a large lag on reboot +# so this should accommodate it. +# The difference between this and TIMEOUT, is that TIMEOUT happens +# when rebooting to the test kernel. This sleep time happens +# after a test has completed and we are about to start running +# 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. +# (default 60) +#SLEEP_TIME = 60 + +# The time in between bisects to sleep (in seconds) +# (default 60) +#BISECT_SLEEP_TIME = 60 + +# Reboot the target box on error (default 0) +#REBOOT_ON_ERROR = 0 + +# Power off the target on error (ignored if REBOOT_ON_ERROR is set) +# Note, this is a DEFAULT section only option. +# (default 0) +#POWEROFF_ON_ERROR = 0 + +# Power off the target after all tests have completed successfully +# Note, this is a DEFAULT section only option. +# (default 0) +#POWEROFF_ON_SUCCESS = 0 + +# Reboot the target after all test completed successfully (default 1) +# (ignored if POWEROFF_ON_SUCCESS is set) +#REBOOT_ON_SUCCESS = 1 + +# In case there are isses with rebooting, you can specify this +# to always powercycle after this amount of time after calling +# reboot. +# Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just +# makes it powercycle immediately after rebooting. Do not define +# it if you do not want it. +# (default undefined) +#POWERCYCLE_AFTER_REBOOT = 5 + +# In case there's isses with halting, you can specify this +# to always poweroff after this amount of time after calling +# halt. +# Note, POWEROFF_AFTER_HALT = 0 does NOT disable it. It just +# makes it poweroff immediately after halting. Do not define +# it if you do not want it. +# (default undefined) +#POWEROFF_AFTER_HALT = 20 + +# A script or command to power off the box (default undefined) +# Needed for POWEROFF_ON_ERROR and SUCCESS +# +# Example for digital loggers power switch: +#POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' +# +# Example for a virtual guest call "Guest". +#POWER_OFF = virsh destroy Guest + #### Per test run options #### # The following options are only allowed in TEST_START sections. # They are ignored in the DEFAULTS sections. From 9386c6ab7a33044d9907b00fc80976292bb02c2d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:35:48 -0500 Subject: [PATCH 16/30] ktest: Use oldnoconfig instead of yes command Running the command "yes ''" through the make oldconfig may enable things we do not want enabled. If something is default enabled, the yes command with '' as an argument will enable it. Use oldnoconfig, which runs everything as if 'no' was used. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a7e86e391172..5bd00755727c 100644 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -694,7 +694,6 @@ sub check_buildlog { sub build { my ($type) = @_; my $defconfig = ""; - my $append = ""; unlink $buildlog; @@ -707,7 +706,7 @@ sub build { # old config can ask questions if ($type eq "oldconfig") { - $append = "yes ''|"; + $type = "oldnoconfig"; # allow for empty configs run_command "touch $outputdir/.config"; @@ -737,7 +736,7 @@ sub build { $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - run_command "$append $defconfig $make $type" or + run_command "$defconfig $make $type" or dodie "failed make config"; $redirect = "$buildlog"; From d1e2f22ad718c83ab19297e7717679c5ed17c020 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:39:57 -0500 Subject: [PATCH 17/30] ktest: Write to stdout if no log file is given If no LOG_FILE option is set, then write what would be logged to that file to standard output. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) mode change 100644 => 100755 tools/testing/ktest/ktest.pl diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl old mode 100644 new mode 100755 index 5bd00755727c..0a5ed0db4ff3 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -230,7 +230,7 @@ sub read_config { } } -sub logit { +sub _logit { if (defined($opt{"LOG_FILE"})) { open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; print OUT @_; @@ -238,9 +238,17 @@ sub logit { } } +sub logit { + if (defined($opt{"LOG_FILE"})) { + _logit @_; + } else { + print @_; + } +} + sub doprint { print @_; - logit @_; + _logit @_; } sub run_command; From 51ad1dd1034684e9c490eb41c17cde8ffb682ab1 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:43:21 -0500 Subject: [PATCH 18/30] ktest: Use $output_config instead of typing $outputdir/.config To help prevent typos, use $output_config as the reference to "$outputdir/.config". Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0a5ed0db4ff3..3d88be784df0 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -46,6 +46,7 @@ my $machine; my $tmpdir; my $builddir; my $outputdir; +my $output_config; my $test_type; my $build_type; my $build_options; @@ -389,8 +390,8 @@ sub fail { mkpath($faildir) or die "can't create $faildir"; } - if (-f "$outputdir/.config") { - cp "$outputdir/.config", "$faildir/config" or + if (-f "$output_config") { + cp "$output_config", "$faildir/config" or die "failed to copy .config"; } if (-f $buildlog) { @@ -619,7 +620,7 @@ sub install { # should we process modules? $install_mods = 0; - open(IN, "$outputdir/.config") or dodie("Can't read config file"); + open(IN, "$output_config") or dodie("Can't read config file"); while () { if (/CONFIG_MODULES(=y)?/) { $install_mods = 1 if (defined($1)); @@ -706,7 +707,7 @@ sub build { unlink $buildlog; if ($type =~ /^useconfig:(.*)/) { - run_command "cp $1 $outputdir/.config" or + run_command "cp $1 $output_config" or dodie "could not copy $1 to .config"; $type = "oldconfig"; @@ -717,20 +718,20 @@ sub build { $type = "oldnoconfig"; # allow for empty configs - run_command "touch $outputdir/.config"; + run_command "touch $output_config"; - run_command "mv $outputdir/.config $outputdir/config_temp" or + run_command "mv $output_config $outputdir/config_temp" or dodie "moving .config"; if (!$noclean && !run_command "$make mrproper") { dodie "make mrproper"; } - run_command "mv $outputdir/config_temp $outputdir/.config" or + run_command "mv $outputdir/config_temp $output_config" or dodie "moving config_temp"; } elsif (!$noclean) { - unlink "$outputdir/.config"; + unlink "$output_config"; run_command "$make mrproper" or dodie "make mrproper"; } @@ -1292,6 +1293,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $buildlog = "$tmpdir/buildlog-$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)); From 21a9679feadf6b215c4f932b0df5d252b4822c45 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:45:50 -0500 Subject: [PATCH 19/30] ktest: Allow a test case to undefine a default value Allow a test case in the config file to undefine a default value by specifying the option and equal sign but not assigning it a value: OPTION = Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 3d88be784df0..962c0f773e20 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -97,6 +97,11 @@ sub set_value { die "Error: Option $lvalue defined more than once!\n"; } $opt{$lvalue} = $rvalue; + if ($rvalue =~ /^\s*$/) { + delete $opt{$lvalue}; + } else { + $opt{$lvalue} = $rvalue; + } } sub read_config { From 7a849cd93ad2cf7d32427f3dbf5f524d5f588d20 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 16:49:25 -0500 Subject: [PATCH 20/30] ktest: Output something easy to parse for failure or success Have a easy way to parse the log file for success or failure. KTEST RESULT: ... Suggested-by: Tim Bird Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 962c0f773e20..e0e5935e94c8 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -378,7 +378,7 @@ sub fail { doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "**** Failed: ", @_, " ****\n"; + doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n"; doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; @@ -782,7 +782,7 @@ sub success { doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; - doprint "** TEST $i SUCCESS!!!! **\n"; + doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; doprint "*******************************************\n"; doprint "*******************************************\n"; From e48c5293bde398e253f83fdd0247fb2bc71cc92f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 2 Nov 2010 14:35:37 -0400 Subject: [PATCH 21/30] ktest/cleanups: Added version 0.2, ssh as options Updated to version 0.2. Now have SSH_EXEC options. Also added some cleanups for keeping track of success and reading the config file. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 81 ++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index e0e5935e94c8..7b6f8e1a82a4 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -11,7 +11,9 @@ use File::Path qw(mkpath); use File::Copy qw(cp); use FileHandle; -$#ARGV >= 0 || die "usage: ktest.pl config-file\n"; +my $VERSION = "0.2"; + +$#ARGV >= 0 || die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; $| = 1; @@ -40,9 +42,13 @@ $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"; my $version; my $machine; +my $ssh_user; my $tmpdir; my $builddir; my $outputdir; @@ -53,11 +59,14 @@ my $build_options; my $reboot_type; my $reboot_script; my $power_cycle; +my $reboot; my $reboot_on_error; my $poweroff_on_error; my $die_on_failure; my $powercycle_after_reboot; my $poweroff_after_halt; +my $ssh_exec; +my $scp_to_target; my $power_off; my $grub_menu; my $grub_number; @@ -89,6 +98,7 @@ my $build_target; my $target_image; my $localversion; my $iteration = 0; +my $successes = 0; sub set_value { my ($lvalue, $rvalue) = @_; @@ -133,6 +143,7 @@ sub read_config { } my $old_test_num = $test_num; + my $old_repeat = $repeat; $test_num += $repeat; $default = 0; @@ -162,7 +173,7 @@ sub read_config { if ($skip) { $test_num = $old_test_num; - $repeat = 1; + $repeat = $old_repeat; } } elsif (/^\s*DEFAULTS(.*)$/) { @@ -261,7 +272,7 @@ sub run_command; sub reboot { # try to reboot normally - if (run_command "ssh $target reboot") { + if (run_command $reboot) { if (defined($powercycle_after_reboot)) { sleep $powercycle_after_reboot; run_command "$power_cycle"; @@ -419,6 +430,9 @@ sub run_command { my $dord = 0; my $pid; + $command =~ s/\$SSH_USER/$ssh_user/g; + $command =~ s/\$MACHINE/$machine/g; + doprint("$command ... "); $pid = open(CMD, "$command 2>&1 |") or @@ -457,6 +471,24 @@ sub run_command { return !$failed; } +sub run_ssh { + my ($cmd) = @_; + my $cp_exec = $ssh_exec; + + $cp_exec =~ s/\$SSH_COMMAND/$cmd/g; + return run_command "$cp_exec"; +} + +sub run_scp { + my ($src, $dst) = @_; + my $cp_scp = $scp_to_target; + + $cp_scp =~ s/\$SRC_FILE/$src/g; + $cp_scp =~ s/\$DST_FILE/$dst/g; + + return run_command "$cp_scp"; +} + sub get_grub_index { if ($reboot_type ne "grub") { @@ -466,8 +498,13 @@ sub get_grub_index { doprint "Find grub menu ... "; $grub_number = -1; - open(IN, "ssh $target cat /boot/grub/menu.lst |") + + my $ssh_grub = $ssh_exec; + $ssh_grub =~ s,\$SSH_COMMAND,cat /boot/grub/menu.lst,g; + + open(IN, "$ssh_grub |") or die "unable to get menu.lst"; + while () { if (/^\s*title\s+$grub_menu\s*$/) { $grub_number++; @@ -516,7 +553,7 @@ sub wait_for_input sub reboot_to { if ($reboot_type eq "grub") { - run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + run_command "$ssh_exec '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; return; } @@ -618,7 +655,7 @@ sub monitor { sub install { - run_command "scp $outputdir/$build_target $target:$target_image" or + run_scp "$outputdir/$build_target", "$target_image" or dodie "failed to copy image"; my $install_mods = 0; @@ -645,32 +682,29 @@ sub install { my $modlib = "/lib/modules/$version"; my $modtar = "ktest-mods.tar.bz2"; - run_command "ssh $target rm -rf $modlib" or + run_ssh "rm -rf $modlib" or dodie "failed to remove old mods: $modlib"; # would be nice if scp -r did not follow symbolic links run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or dodie "making tarball"; - run_command "scp $tmpdir/$modtar $target:/tmp" or + run_scp "$tmpdir/$modtar", "/tmp" or dodie "failed to copy modules"; unlink "$tmpdir/$modtar"; - run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or + run_ssh "'(cd / && tar xf /tmp/$modtar)'" or dodie "failed to tar modules"; - run_command "ssh $target rm -f /tmp/$modtar"; + run_ssh "rm -f /tmp/$modtar"; return if (!defined($post_install)); - my $save_env = $ENV{KERNEL_VERSION}; - - $ENV{KERNEL_VERSION} = $version; - run_command "$post_install" or + 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"; - - $ENV{KERNEL_VERSION} = $save_env; } sub check_buildlog { @@ -766,7 +800,7 @@ sub build { } sub halt { - if (!run_command "ssh $target halt" or defined($power_off)) { + if (!run_ssh "halt" or defined($power_off)) { if (defined($poweroff_after_halt)) { sleep $poweroff_after_halt; run_command "$power_off"; @@ -780,6 +814,8 @@ sub halt { sub success { my ($i) = @_; + $successes++; + doprint "\n\n*******************************************\n"; doprint "*******************************************\n"; doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; @@ -1250,10 +1286,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $iteration = $i; - my $ssh_user = set_test_option("SSH_USER", $i); 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); @@ -1261,6 +1297,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $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); @@ -1283,6 +1320,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $console = set_test_option("CONSOLE", $i); $success_line = set_test_option("SUCCESS_LINE", $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); @@ -1293,12 +1332,16 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { die "can't create $tmpdir"; } + $ENV{"SSH_USER"} = $ssh_user; + $ENV{"MACHINE"} = $machine; + $target = "$ssh_user\@$machine"; $buildlog = "$tmpdir/buildlog-$machine"; $dmesg = "$tmpdir/dmesg-$machine"; $make = "$makecmd O=$outputdir"; $output_config = "$outputdir/.config"; + $output_config = "$outputdir/.config"; if ($reboot_type eq "grub") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); @@ -1376,4 +1419,6 @@ if ($opt{"POWEROFF_ON_SUCCESS"}) { reboot; } +doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; + exit 0; From 0a05c769a9de554aefa773061c592c7054d5e7a0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 11:14:10 -0500 Subject: [PATCH 22/30] ktest: Added config_bisect test type Added the ability to do a config_bisect. It starts with a bad config and does the following loop. Enable half the configs. if none of the configs to check are not enabled (caused by missing dependencies) enable the other half. Run the test if the test passes, remove the configs from the check but enabled them for further tests (to satisfy dependencies). else Remove any config that was not enabled, as we have found a new config that can cause a failure. loop till we have only one config left. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 409 +++++++++++++++++++++++++++++++++-- 1 file changed, 388 insertions(+), 21 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 7b6f8e1a82a4..0d1080548ea6 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -106,7 +106,6 @@ sub set_value { if (defined($opt{$lvalue})) { die "Error: Option $lvalue defined more than once!\n"; } - $opt{$lvalue} = $rvalue; if ($rvalue =~ /^\s*$/) { delete $opt{$lvalue}; } else { @@ -945,20 +944,18 @@ sub run_git_bisect { return 1; } -sub run_bisect { - my ($type) = @_; +# returns 1 on success, 0 on failure +sub run_bisect_test { + my ($type, $buildtype) = @_; my $failed = 0; my $result; my $output; my $ret; - if (defined($minconfig)) { - build "useconfig:$minconfig" or $failed = 1; - } else { - # ?? no config to use? - build "oldconfig" or $failed = 1; - } + $in_bisect = 1; + + build $buildtype or $failed = 1; if ($type ne "build") { dodie "Failed on build" if $failed; @@ -980,7 +977,7 @@ sub run_bisect { } if ($failed) { - $result = "bad"; + $result = 0; # reboot the box to a good kernel if ($type ne "build") { @@ -991,19 +988,35 @@ sub run_bisect { end_monitor; } } else { - $result = "good"; + $result = 1; } + $in_bisect = 0; + + return $result; +} + +sub run_bisect { + my ($type) = @_; + my $buildtype = "oldconfig"; + + # We should have a minconfig to use? + if (defined($minconfig)) { + $buildtype = "useconfig:$minconfig"; + } + + my $ret = run_bisect_test $type, $buildtype; + # Are we looking for where it worked, not failed? if ($reverse_bisect) { - if ($failed) { - $result = "good"; - } else { - $result = "bad"; - } + $ret = !$ret; } - return $result; + if ($ret) { + return "good"; + } else { + return "bad"; + } } sub bisect { @@ -1033,8 +1046,6 @@ sub bisect { $reverse_bisect = 0; } - $in_bisect = 1; - # Can't have a test without having a test to run if ($type eq "test" && !defined($run_test)) { $type = "boot"; @@ -1108,7 +1119,359 @@ sub bisect { doprint "Bad commit was [$bisect_bad]\n"; - $in_bisect = 0; + success $i; +} + +my %config_ignore; +my %config_set; + +my %config_list; +my %null_config; + +my %dependency; + +sub process_config_ignore { + my ($config) = @_; + + open (IN, $config) + or dodie "Failed to read $config"; + + while () { + if (/^(.*?(CONFIG\S*)(=.*| is not set))/) { + $config_ignore{$2} = $1; + } + } + + close(IN); +} + +sub read_current_config { + my ($config_ref) = @_; + + %{$config_ref} = (); + undef %{$config_ref}; + + my @key = keys %{$config_ref}; + if ($#key >= 0) { + print "did not delete!\n"; + exit; + } + open (IN, "$output_config"); + + while () { + if (/^(CONFIG\S+)=(.*)/) { + ${$config_ref}{$1} = $2; + } + } + close(IN); +} + +sub get_dependencies { + my ($config) = @_; + + my $arr = $dependency{$config}; + if (!defined($arr)) { + return (); + } + + my @deps = @{$arr}; + + foreach my $dep (@{$arr}) { + print "ADD DEP $dep\n"; + @deps = (@deps, get_dependencies $dep); + } + + return @deps; +} + +sub create_config { + my @configs = @_; + + open(OUT, ">$output_config") or dodie "Can not write to $output_config"; + + foreach my $config (@configs) { + print OUT "$config_set{$config}\n"; + my @deps = get_dependencies $config; + foreach my $dep (@deps) { + print OUT "$config_set{$dep}\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"; + +} + +sub compare_configs { + my (%a, %b) = @_; + + foreach my $item (keys %a) { + if (!defined($b{$item})) { + print "diff $item\n"; + return 1; + } + delete $b{$item}; + } + + my @keys = keys %b; + if ($#keys) { + print "diff2 $keys[0]\n"; + } + return -1 if ($#keys >= 0); + + return 0; +} + +sub run_config_bisect_test { + my ($type) = @_; + + return run_bisect_test $type, "oldconfig"; +} + +sub process_passed { + my (%configs) = @_; + + doprint "These configs had no failure: (Enabling them for further compiles)\n"; + # Passed! All these configs are part of a good compile. + # Add them to the min options. + foreach my $config (keys %configs) { + if (defined($config_list{$config})) { + doprint " removing $config\n"; + $config_ignore{$config} = $config_list{$config}; + delete $config_list{$config}; + } + } +} + +sub process_failed { + my ($config) = @_; + + doprint "\n\n***************************************\n"; + doprint "Found bad config: $config\n"; + doprint "***************************************\n\n"; +} + +sub run_config_bisect { + + my @start_list = keys %config_list; + + if ($#start_list < 0) { + doprint "No more configs to test!!!\n"; + return -1; + } + + doprint "***** RUN TEST ***\n"; + my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"}; + my $ret; + my %current_config; + + my $count = $#start_list + 1; + doprint " $count configs to test\n"; + + my $half = int($#start_list / 2); + + do { + my @tophalf = @start_list[0 .. $half]; + + create_config @tophalf; + read_current_config \%current_config; + + $count = $#tophalf + 1; + doprint "Testing $count configs\n"; + my $found = 0; + # make sure we test something + foreach my $config (@tophalf) { + if (defined($current_config{$config})) { + logit " $config\n"; + $found = 1; + } + } + if (!$found) { + # try the other half + doprint "Top half produced no set configs, trying bottom half\n"; + @tophalf = @start_list[$half .. $#start_list]; + create_config @tophalf; + read_current_config \%current_config; + foreach my $config (@tophalf) { + if (defined($current_config{$config})) { + logit " $config\n"; + $found = 1; + } + } + if (!$found) { + doprint "Failed: Can't make new config with current configs\n"; + foreach my $config (@start_list) { + doprint " CONFIG: $config\n"; + } + return -1; + } + $count = $#tophalf + 1; + doprint "Testing $count configs\n"; + } + + $ret = run_config_bisect_test $type; + + if ($ret) { + process_passed %current_config; + return 0; + } + + doprint "This config had a failure.\n"; + doprint "Removing these configs that were not set in this config:\n"; + + # A config exists in this group that was bad. + foreach my $config (keys %config_list) { + if (!defined($current_config{$config})) { + doprint " removing $config\n"; + delete $config_list{$config}; + } + } + + @start_list = @tophalf; + + if ($#start_list == 0) { + process_failed $start_list[0]; + return 1; + } + + # remove half the configs we are looking at and see if + # they are good. + $half = int($#start_list / 2); + } while ($half > 0); + + # we found a single config, try it again + my @tophalf = @start_list[0 .. 0]; + + $ret = run_config_bisect_test $type; + if ($ret) { + process_passed %current_config; + return 0; + } + + process_failed $start_list[0]; + return 1; +} + +sub config_bisect { + my ($i) = @_; + + my $start_config = $opt{"CONFIG_BISECT[$i]"}; + + my $tmpconfig = "$tmpdir/use_config"; + + # Make the file with the bad config and the min config + if (defined($minconfig)) { + # read the min config for things to ignore + run_command "cp $minconfig $tmpconfig" or + dodie "failed to copy $minconfig to $tmpconfig"; + } else { + 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"; + process_config_ignore $tmpconfig; + } + + # now process the start config + run_command "cp $start_config $output_config" or + dodie "failed to copy $start_config to $output_config"; + + # read directly what we want to check + my %config_check; + open (IN, $output_config) + or dodie "faied to open $output_config"; + + while () { + if (/^((CONFIG\S*)=.*)/) { + $config_check{$2} = $1; + } + } + close(IN); + + # Now run oldconfig with the minconfig (and addconfigs) + run_command "$defconfig $make oldnoconfig" or + dodie "failed make config oldconfig"; + + # check to see what we lost (or gained) + open (IN, $output_config) + or dodie "Failed to read $start_config"; + + my %removed_configs; + my %added_configs; + + while () { + if (/^((CONFIG\S*)=.*)/) { + # save off all options + $config_set{$2} = $1; + if (defined($config_check{$2})) { + if (defined($config_ignore{$2})) { + $removed_configs{$2} = $1; + } else { + $config_list{$2} = $1; + } + } elsif (!defined($config_ignore{$2})) { + $added_configs{$2} = $1; + $config_list{$2} = $1; + } + } + } + close(IN); + + my @confs = keys %removed_configs; + if ($#confs >= 0) { + doprint "Configs overridden by default configs and removed from check:\n"; + foreach my $config (@confs) { + doprint " $config\n"; + } + } + @confs = keys %added_configs; + if ($#confs >= 0) { + doprint "Configs appearing in make oldconfig and added:\n"; + foreach my $config (@confs) { + doprint " $config\n"; + } + } + + my %config_test; + my $once = 0; + + # 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 + # may not be able to create a new config. + # Here we create a config with everything set. + create_config (keys %config_list); + read_current_config \%config_test; + foreach my $config (keys %config_list) { + if (!defined($config_test{$config})) { + if (!$once) { + $once = 1; + doprint "Configs not produced by kconfig (will not be checked):\n"; + } + doprint " $config\n"; + delete $config_list{$config}; + } + } + my $ret; + do { + $ret = run_config_bisect; + } while (!$ret); + + return $ret if ($ret < 0); success $i; } @@ -1341,7 +1704,6 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $dmesg = "$tmpdir/dmesg-$machine"; $make = "$makecmd O=$outputdir"; $output_config = "$outputdir/.config"; - $output_config = "$outputdir/.config"; if ($reboot_type eq "grub") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); @@ -1354,6 +1716,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; } elsif ($test_type eq "bisect") { $run_type = $opt{"BISECT_TYPE[$i]"}; + } elsif ($test_type eq "config_bisect") { + $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; } # mistake in config file? @@ -1385,6 +1749,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { if ($test_type eq "bisect") { bisect $i; next; + } elsif ($test_type eq "config_bisect") { + config_bisect $i; + next; } elsif ($test_type eq "patchcheck") { patchcheck $i; next; From dbc6d0aa8ae38cf9cfe39c6076cc5378ef4ca4a2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 17:18:37 -0500 Subject: [PATCH 23/30] ktest: Added compare script to test ktest.pl to sample.conf Add a compare script that makes sure that all the options in sample.conf are used in ktest.pl, and all the options in ktest.pl are described in sample.conf. Signed-off-by: Steven Rostedt --- tools/testing/ktest/compare-ktest-sample.pl | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100755 tools/testing/ktest/compare-ktest-sample.pl diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl new file mode 100755 index 000000000000..9a571e71683c --- /dev/null +++ b/tools/testing/ktest/compare-ktest-sample.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl + +open (IN,"ktest.pl"); +while () { + if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ || + /set_test_option\("(.*?)"/) { + $opt{$1} = 1; + } +} +close IN; + +open (IN, "sample.conf"); +while () { + if (/^\s*#?\s*(\S+)\s*=/) { + $samp{$1} = 1; + } +} +close IN; + +foreach $opt (keys %opt) { + if (!defined($samp{$opt})) { + print "opt = $opt\n"; + } +} + +foreach $samp (keys %samp) { + if (!defined($opt{$samp})) { + print "samp = $samp\n"; + } +} From d1fbd7e6a6a4c67ab895a0219a2bbcf09d039c3a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 8 Nov 2010 17:41:37 -0500 Subject: [PATCH 24/30] ktest: Updated the sample.conf for the latest options Added documentation for SSH_EXEC, SCP_TO_TARGET, REBOOT, and CONFIG_BISECT and friends. Signed-off-by: Steven Rostedt --- tools/testing/ktest/sample.conf | 90 ++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 03d6e91245fe..e1272746ce8c 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -390,6 +390,21 @@ # Example for a virtual guest call "Guest". #POWER_OFF = virsh destroy Guest +# 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 +# (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 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 + #### Per test run options #### # The following options are only allowed in TEST_START sections. # They are ignored in the DEFAULTS sections. @@ -442,7 +457,7 @@ # CHECKOUT = mybranch # PATCHCHECK_TYPE = boot # PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 -# PATCHCHEKC_END = HEAD~2 +# PATCHCHECK_END = HEAD~2 # # # @@ -520,3 +535,76 @@ # BISECT_BAD = b5153163ed580e00c67bdfecb02b2e3843817b3e # BISECT_TYPE = build # MIN_CONFIG = /home/test/config-bisect +# +# +# +# For TEST_TYPE = config_bisect +# +# In those cases that you have two different configs. One of them +# work, the other does not, and you do not know what config causes +# the problem. +# The TEST_TYPE config_bisect will bisect the bad config looking for +# what config causes the failure. +# +# The way it works is this: +# +# First it finds a config to work with. Since a different version, or +# MIN_CONFIG may cause different dependecies, it must run through this +# preparation. +# +# Overwrites any config set in the bad config with a config set in +# either the MIN_CONFIG or ADD_CONFIG. Thus, make sure these configs +# are minimal and do not disable configs you want to test: +# (ie. # CONFIG_FOO is not set). +# +# An oldconfig is run on the bad config and any new config that +# appears will be added to the configs to test. +# +# Finally, it generates a config with the above result and runs it +# again through make oldconfig to produce a config that should be +# satisfied by kconfig. +# +# Then it starts the bisect. +# +# The configs to test are cut in half. If all the configs in this +# half depend on a config in the other half, then the other half +# is tested instead. If no configs are enabled by either half, then +# this means a circular dependency exists and the test fails. +# +# A config is created with the test half, and the bisect test is run. +# +# If the bisect succeeds, then all configs in the generated config +# are removed from the configs to test and added to the configs that +# will be enabled for all builds (they will be enabled, but not be part +# of the configs to examine). +# +# If the bisect fails, then all test configs that were not enabled by +# the config file are removed from the test. These configs will not +# be enabled in future tests. Since current config failed, we consider +# this to be a subset of the config that we started with. +# +# When we are down to one config, it is considered the bad config. +# +# Note, the config chosen may not be the true bad config. Due to +# dependencies and selections of the kbuild system, mulitple +# configs may be needed to cause a failure. If you disable the +# config that was found and restart the test, if the test fails +# again, it is recommended to rerun the config_bisect with a new +# bad config without the found config enabled. +# +# The option BUILD_TYPE will be ignored. +# +# CONFIG_BISECT_TYPE is the type of test to perform: +# build - bad fails to build +# boot - bad builds but fails to boot +# test - bad boots but fails a test +# +# CONFIG_BISECT is the config that failed to boot +# +# Example: +# TEST_START +# TEST_TYPE = config_bisect +# CONFIG_BISECT_TYPE = build +# CONFIG_BISECT = /home/test/˘onfig-bad +# MIN_CONFIG = /home/test/config-min +# From 9be2e6b590b5d9cb6d6e38b6362a552bbcb118e0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 9 Nov 2010 12:20:21 -0500 Subject: [PATCH 25/30] ktest: Use different temp config name for minconfig By using the "use_config" for minconfig and addconfig we risk trying to copy itself to itself, which will cause an unexpected failure. Use a different name instead. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0d1080548ea6..a7a74f015ef3 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1735,9 +1735,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $minconfig = $addconfig; } elsif (defined($addconfig)) { - run_command "cat $addconfig $minconfig > $tmpdir/use_config" or + run_command "cat $addconfig $minconfig > $tmpdir/add_config" or dodie "Failed to create temp config"; - $minconfig = "$tmpdir/use_config"; + $minconfig = "$tmpdir/add_config"; } my $checkout = $opt{"CHECKOUT[$i]"}; From cccae1a62a81dc8e32bf787024fdcf7ef71f1285 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 9 Nov 2010 12:21:32 -0500 Subject: [PATCH 26/30] ktest: Parse off the directory name in useconfig for failures When we store failures, we create a directory that has the build_type in it. For useconfig, it also contains the name path of the config file it uses. This unfortunately gets its own directory on failure. Parse off the directory name when creating the directory to store the failures. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a7a74f015ef3..52e45b8551e8 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -398,7 +398,12 @@ sub fail { my $date = sprintf "%04d%02d%02d%02d%02d%02d", 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; - my $dir = "$machine-$test_type-$build_type-fail-$date"; + my $type = $build_type; + if ($type =~ /useconfig/) { + $type = "useconfig"; + } + + my $dir = "$machine-$test_type-$type-fail-$date"; my $faildir = "$store_failures/$dir"; if (!-d $faildir) { From 1c8a617a274c4065681d964cd5a5afb921de4a87 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 9 Nov 2010 12:55:40 -0500 Subject: [PATCH 27/30] ktest: Added force stop after success and failure Added the options STOP_AFTER_SUCCESS and STOP_AFTER_FAILURE to allow the user to give a time (in seconds) to stop the monitor after a stack trace or login has been detected. Sometimes the kernel constantly prints out to the console and this may cause the test to run indefinitely. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 33 ++++++++++++++++++++++++++++++++- tools/testing/ktest/sample.conf | 12 ++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 52e45b8551e8..6e8597398468 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -45,6 +45,8 @@ $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; my $version; my $machine; @@ -94,6 +96,8 @@ my $timeout; my $booted_timeout; my $console; my $success_line; +my $stop_after_success; +my $stop_after_failure; my $build_target; my $target_image; my $localversion; @@ -601,6 +605,9 @@ sub monitor { reboot_to; + my $success_start; + my $failure_start; + for (;;) { if ($booted) { @@ -619,6 +626,16 @@ sub monitor { if ($full_line =~ /$success_line/) { $booted = 1; + $success_start = time; + } + + if ($booted && defined($stop_after_success) && + $stop_after_success >= 0) { + my $now = time; + if ($now - $success_start >= $stop_after_success) { + doprint "Test forced to stop after $stop_after_success seconds after success\n"; + last; + } } if ($full_line =~ /\[ backtrace testing \]/) { @@ -626,7 +643,19 @@ sub monitor { } if ($full_line =~ /call trace:/i) { - $bug = 1 if (!$skip_call_trace); + if (!$skip_call_trace) { + $bug = 1; + $failure_start = time; + } + } + + if ($bug && defined($stop_after_failure) && + $stop_after_failure >= 0) { + my $now = time; + if ($now - $failure_start >= $stop_after_failure) { + doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; + last; + } } if ($full_line =~ /\[ end of backtrace testing \]/) { @@ -1687,6 +1716,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $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); diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index e1272746ce8c..3408c594b2de 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -294,6 +294,18 @@ # (default "login:") #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) +# (default 10) +#STOP_AFTER_SUCCESS = 10 + +# In case the console constantly fills the screen, having +# a specified time to stop the test after failure is recommended. +# (in seconds) +# (default 60) +#STOP_AFTER_FAILURE = 60 + # 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 From eec5646031a54858362f7192a928511a23612f6b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 10 Nov 2010 09:08:20 -0500 Subject: [PATCH 28/30] ktest: For grub reboot, use run_ssh instead of run_command The run_ssh handles the ssh variable $SSH_COMMAND, which was not being used by the run_command in reboot_to function. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 6e8597398468..08a875fa4251 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -561,7 +561,7 @@ sub wait_for_input sub reboot_to { if ($reboot_type eq "grub") { - run_command "$ssh_exec '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; + run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; return; } From f1a27850095ebc66c138c940c1efedb8a95f92c4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 11 Nov 2010 11:34:38 -0500 Subject: [PATCH 29/30] ktest: Copy the last good and bad configs in config_bisect During the config_bisect, in case of failure, it is nice to have the last good and bad .configs that were used. This would let us restart the config_bisect from those configs. Copy the last good config into the output dir as config_good, and the last bad config as config_bad. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 08a875fa4251..04a3227ef5e0 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1281,6 +1281,8 @@ sub process_passed { delete $config_list{$config}; } } + doprint "config copied to $outputdir/config_good\n"; + run_command "cp -f $output_config $outputdir/config_good"; } sub process_failed { @@ -1358,6 +1360,8 @@ sub run_config_bisect { doprint "This config had a failure.\n"; doprint "Removing these configs that were not set in this config:\n"; + doprint "config copied to $outputdir/config_bad\n"; + run_command "cp -f $output_config $outputdir/config_bad"; # A config exists in this group that was bad. foreach my $config (keys %config_list) { From 8d1491bae72e3500b74e1855afa10f0544068cea Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 18 Nov 2010 15:39:48 -0500 Subject: [PATCH 30/30] ktest: Ask for the manditory config options instead of just failing In keeping with the notion that all tools should be simple for all to use. I've changed ktest.pl to ask for mandatory options instead of just failing. It will append (or create) the options the user types in, onto the config file. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 218 ++++++++++++++++++++++++++++++++--- 1 file changed, 205 insertions(+), 13 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 04a3227ef5e0..e1c62eeb88f5 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -13,8 +13,6 @@ use FileHandle; my $VERSION = "0.2"; -$#ARGV >= 0 || die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; - $| = 1; my %opt; @@ -47,7 +45,9 @@ $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 $ktest_config; my $version; my $machine; my $ssh_user; @@ -104,6 +104,156 @@ my $localversion; my $iteration = 0; my $successes = 0; +my %entered_configs; +my %config_help; + +$config_help{"MACHINE"} = << "EOF" + The machine hostname that you will test. +EOF + ; +$config_help{"SSH_USER"} = << "EOF" + The box is expected to have ssh on normal bootup, provide the user + (most likely root, since you need privileged operations) +EOF + ; +$config_help{"BUILD_DIR"} = << "EOF" + The directory that contains the Linux source code (full path). +EOF + ; +$config_help{"OUTPUT_DIR"} = << "EOF" + The directory that the objects will be built (full path). + (can not be same as BUILD_DIR) +EOF + ; +$config_help{"BUILD_TARGET"} = << "EOF" + The location of the compiled file to copy to the target. + (relative to OUTPUT_DIR) +EOF + ; +$config_help{"TARGET_IMAGE"} = << "EOF" + The place to put your image on the test machine. +EOF + ; +$config_help{"POWER_CYCLE"} = << "EOF" + A script or command to reboot the box. + + Here is a digital loggers power switch example + POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin\@power/outlet?5=CCL' + + Here is an example to reboot a virtual box on the current host + with the name "Guest". + POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest +EOF + ; +$config_help{"CONSOLE"} = << "EOF" + The script or command that reads the console + + If you use ttywatch server, something like the following would work. +CONSOLE = nc -d localhost 3001 + + For a virtual machine with guest name "Guest". +CONSOLE = virsh console Guest +EOF + ; +$config_help{"LOCALVERSION"} = << "EOF" + Required version ending to differentiate the test + from other linux builds on the system. +EOF + ; +$config_help{"REBOOT_TYPE"} = << "EOF" + Way to reboot the box to the test kernel. + Only valid options so far are "grub" 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 + and select that target to reboot to the kernel. If this is not + your setup, then specify "script" and have a command or script + specified in REBOOT_SCRIPT to boot to the target. + + The entry in /boot/grub/menu.lst must be entered in manually. + The test will not modify that file. +EOF + ; +$config_help{"GRUB_MENU"} = << "EOF" + The grub title name for the test kernel to boot + (Only mandatory if REBOOT_TYPE = grub) + + Note, ktest.pl will not update the grub menu.lst, you need to + manually add an option for the test. ktest.pl will search + the grub menu.lst for this option to find what kernel to + reboot into. + + For example, if in the /boot/grub/menu.lst the test kernel title has: + title Test Kernel + kernel vmlinuz-test + GRUB_MENU = Test Kernel +EOF + ; +$config_help{"REBOOT_SCRIPT"} = << "EOF" + A script to reboot the target into the test kernel + (Only mandatory if REBOOT_TYPE = script) +EOF + ; + + +sub get_ktest_config { + my ($config) = @_; + + return if (defined($opt{$config})); + + if (defined($config_help{$config})) { + print "\n"; + print $config_help{$config}; + } + + for (;;) { + print "$config = "; + if (defined($default{$config})) { + print "\[$default{$config}\] "; + } + $entered_configs{$config} = ; + $entered_configs{$config} =~ s/^\s*(.*\S)\s*$/$1/; + if ($entered_configs{$config} =~ /^\s*$/) { + if ($default{$config}) { + $entered_configs{$config} = $default{$config}; + } else { + print "Your answer can not be blank\n"; + next; + } + } + 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"); + get_ktest_config("LOCALVERSION"); + + my $rtype = $opt{"REBOOT_TYPE"}; + + if (!defined($rtype)) { + if (!defined($opt{"GRUB_MENU"})) { + get_ktest_config("REBOOT_TYPE"); + $rtype = $entered_configs{"REBOOT_TYPE"}; + } else { + $rtype = "grub"; + } + } + + if ($rtype eq "grub") { + get_ktest_config("GRUB_MENU"); + } else { + get_ktest_config("REBOOT_SCRIPT"); + } +} + sub set_value { my ($lvalue, $rvalue) = @_; @@ -241,6 +391,9 @@ sub read_config { $opt{"NUM_TESTS"} = $test_num; } + # make sure we have all mandatory configs + get_ktest_configs; + # set any defaults foreach my $default (keys %default) { @@ -1612,18 +1765,57 @@ sub patchcheck { return 1; } -read_config $ARGV[0]; +$#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; -# mandatory configs -die "MACHINE not defined\n" if (!defined($opt{"MACHINE"})); -die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); -die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); -die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); -die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); -die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); -die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); -die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); -die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); +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 = ; + 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) { + exit 0; + } + } +} else { + $ktest_config = "ktest.conf"; +} + +if (! -f $ktest_config) { + open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; + print OUT << "EOF" +# Generated by ktest.pl +# +# Define each test with TEST_START +# The config options below it will override the defaults +TEST_START + +DEFAULTS +EOF +; + close(OUT); +} +read_config $ktest_config; + +# Append any configs entered in manually to the config file. +my @new_configs = keys %entered_configs; +if ($#new_configs >= 0) { + print "\nAppending entered in configs to $ktest_config\n"; + 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}; + } +} if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { unlink $opt{"LOG_FILE"};