2010-11-02 15:01:32 -04:00
#!/usr/bin/perl -w
2010-11-02 14:58:05 -04:00
#
# Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
# Licensed under the terms of the GNU GPL License version 2
#
2010-11-02 15:01:32 -04:00
use strict ;
use IPC::Open2 ;
use Fcntl qw( F_GETFL F_SETFL O_NONBLOCK ) ;
2010-11-02 14:58:22 -04:00
use File::Path qw( mkpath ) ;
use File::Copy qw( cp ) ;
2010-11-02 15:01:32 -04:00
use FileHandle ;
2010-11-02 14:35:37 -04:00
my $ VERSION = "0.2" ;
$# ARGV >= 0 || die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n" ;
2010-11-02 15:01:32 -04:00
$| = 1 ;
my % opt ;
2010-11-02 15:13:54 -04:00
my % repeat_tests ;
my % repeats ;
2010-11-02 14:58:27 -04:00
my % default ;
2010-11-02 15:01:32 -04:00
#default opts
2010-11-02 15:13:54 -04:00
$ default { "NUM_TESTS" } = 1 ;
2010-11-02 14:58:27 -04:00
$ default { "REBOOT_TYPE" } = "grub" ;
$ default { "TEST_TYPE" } = "test" ;
$ default { "BUILD_TYPE" } = "randconfig" ;
$ default { "MAKE_CMD" } = "make" ;
$ default { "TIMEOUT" } = 120 ;
2010-11-02 15:13:54 -04:00
$ default { "TMP_DIR" } = "/tmp/ktest" ;
2010-11-02 14:58:27 -04:00
$ 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 ;
2010-11-02 14:35:37 -04:00
$ 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" ;
2010-11-02 15:01:32 -04:00
my $ version ;
2010-11-02 14:58:27 -04:00
my $ machine ;
2010-11-02 14:35:37 -04:00
my $ ssh_user ;
2010-11-02 14:58:27 -04:00
my $ tmpdir ;
my $ builddir ;
my $ outputdir ;
2010-11-08 16:43:21 -05:00
my $ output_config ;
2010-11-02 14:58:27 -04:00
my $ test_type ;
2010-11-02 14:58:22 -04:00
my $ build_type ;
2010-11-02 14:58:27 -04:00
my $ build_options ;
my $ reboot_type ;
my $ reboot_script ;
my $ power_cycle ;
2010-11-02 14:35:37 -04:00
my $ reboot ;
2010-11-02 14:58:27 -04:00
my $ reboot_on_error ;
my $ poweroff_on_error ;
my $ die_on_failure ;
2010-11-02 14:58:38 -04:00
my $ powercycle_after_reboot ;
my $ poweroff_after_halt ;
2010-11-02 14:35:37 -04:00
my $ ssh_exec ;
my $ scp_to_target ;
2010-11-02 14:58:27 -04:00
my $ power_off ;
my $ grub_menu ;
2010-11-02 15:01:32 -04:00
my $ grub_number ;
my $ target ;
my $ make ;
2010-11-02 14:58:33 -04:00
my $ post_install ;
2010-11-02 14:57:01 -04:00
my $ noclean ;
2010-11-02 14:57:33 -04:00
my $ minconfig ;
2010-11-02 14:58:15 -04:00
my $ addconfig ;
2010-11-02 14:57:33 -04:00
my $ in_bisect = 0 ;
my $ bisect_bad = "" ;
2010-11-02 14:58:05 -04:00
my $ reverse_bisect ;
2010-11-02 14:57:58 -04:00
my $ in_patchcheck = 0 ;
2010-11-02 14:57:43 -04:00
my $ run_test ;
2010-11-02 14:57:58 -04:00
my $ redirect ;
2010-11-02 14:58:22 -04:00
my $ buildlog ;
my $ dmesg ;
my $ monitor_fp ;
my $ monitor_pid ;
my $ monitor_cnt = 0 ;
2010-11-02 14:58:27 -04:00
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 ;
2010-11-02 14:58:38 -04:00
my $ iteration = 0 ;
2010-11-02 14:35:37 -04:00
my $ successes = 0 ;
2010-11-02 15:01:32 -04:00
2010-11-02 15:13:54 -04:00
sub set_value {
my ( $ lvalue , $ rvalue ) = @ _ ;
if ( defined ( $ opt { $ lvalue } ) ) {
die "Error: Option $lvalue defined more than once!\n" ;
}
2010-11-08 16:45:50 -05:00
if ( $ rvalue =~ /^\s*$/ ) {
delete $ opt { $ lvalue } ;
} else {
$ opt { $ lvalue } = $ rvalue ;
}
2010-11-02 15:13:54 -04:00
}
2010-11-02 15:01:32 -04:00
sub read_config {
my ( $ config ) = @ _ ;
open ( IN , $ config ) || die "can't read file $config" ;
2010-11-02 15:13:54 -04:00
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 ;
2010-11-02 15:01:32 -04:00
while ( <IN> ) {
# ignore blank lines and comments
next if ( /^\s*$/ || /\s*\#/ ) ;
2010-11-02 15:13:54 -04:00
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 ;
2010-11-02 14:35:37 -04:00
my $ old_repeat = $ repeat ;
2010-11-02 15:13:54 -04:00
$ 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 ;
2010-11-02 14:35:37 -04:00
$ repeat = $ old_repeat ;
2010-11-02 15:13:54 -04:00
}
} 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 ) ;
2010-11-02 15:01:32 -04:00
my $ lvalue = $ 1 ;
my $ rvalue = $ 2 ;
2010-11-02 15:13:54 -04:00
if ( ! $ default &&
( $ lvalue eq "NUM_TESTS" ||
$ lvalue eq "LOG_FILE" ||
$ lvalue eq "CLEAR_LOG" ) ) {
die "$name: $.: $lvalue must be set in DEFAULTS section\n" ;
}
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 ;
}
2010-11-02 14:58:27 -04:00
}
2010-11-02 15:13:54 -04:00
} else {
die "$name: $.: Garbage found in config\n$_" ;
2010-11-02 15:01:32 -04:00
}
}
close ( IN ) ;
2010-11-02 14:58:27 -04:00
2010-11-02 15:13:54 -04:00
if ( $ test_num ) {
$ test_num += $ repeat - 1 ;
$ opt { "NUM_TESTS" } = $ test_num ;
}
2010-11-02 14:58:27 -04:00
# set any defaults
foreach my $ default ( keys % default ) {
if ( ! defined ( $ opt { $ default } ) ) {
$ opt { $ default } = $ default { $ default } ;
}
}
2010-11-02 15:01:32 -04:00
}
2010-11-08 16:39:57 -05:00
sub _logit {
2010-11-02 15:01:32 -04:00
if ( defined ( $ opt { "LOG_FILE" } ) ) {
open ( OUT , ">> $opt{LOG_FILE}" ) or die "Can't write to $opt{LOG_FILE}" ;
print OUT @ _ ;
close ( OUT ) ;
}
}
2010-11-08 16:39:57 -05:00
sub logit {
if ( defined ( $ opt { "LOG_FILE" } ) ) {
_logit @ _ ;
} else {
print @ _ ;
}
}
2010-11-02 14:57:33 -04:00
sub doprint {
print @ _ ;
2010-11-08 16:39:57 -05:00
_logit @ _ ;
2010-11-02 14:57:33 -04:00
}
2010-11-02 14:58:22 -04:00
sub run_command ;
sub reboot {
# try to reboot normally
2010-11-02 14:35:37 -04:00
if ( run_command $ reboot ) {
2010-11-02 14:58:38 -04:00
if ( defined ( $ powercycle_after_reboot ) ) {
sleep $ powercycle_after_reboot ;
run_command "$power_cycle" ;
}
} else {
2010-11-02 14:58:22 -04:00
# nope? power cycle it.
2010-11-02 14:58:27 -04:00
run_command "$power_cycle" ;
2010-11-02 14:58:22 -04:00
}
}
2010-11-02 14:58:38 -04:00
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" ) ;
}
2010-11-02 14:57:01 -04:00
sub dodie {
2010-11-02 14:57:43 -04:00
doprint "CRITICAL FAILURE... " , @ _ , "\n" ;
2010-11-02 14:57:01 -04:00
2010-11-02 14:58:38 -04:00
my $ i = $ iteration ;
if ( $ reboot_on_error && ! do_not_reboot ) {
2010-11-02 14:57:21 -04:00
doprint "REBOOTING\n" ;
2010-11-02 14:58:22 -04:00
reboot ;
2010-11-02 14:57:21 -04:00
2010-11-02 14:58:27 -04:00
} elsif ( $ poweroff_on_error && defined ( $ power_off ) ) {
2010-11-02 14:57:01 -04:00
doprint "POWERING OFF\n" ;
2010-11-02 14:58:27 -04:00
`$power_off` ;
2010-11-02 14:57:01 -04:00
}
2010-11-02 14:57:21 -04:00
2010-11-02 14:58:38 -04:00
die @ _ , "\n" ;
2010-11-02 14:57:01 -04:00
}
2010-11-02 14:58:22 -04:00
sub open_console {
my ( $ fp ) = @ _ ;
my $ flags ;
2010-11-02 14:58:27 -04:00
my $ pid = open ( $ fp , "$console|" ) or
dodie "Can't open console $console" ;
2010-11-02 14:58:22 -04:00
$ flags = fcntl ( $ fp , F_GETFL , 0 ) or
2010-11-02 14:58:38 -04:00
dodie "Can't get flags for the socket: $!" ;
2010-11-02 14:58:22 -04:00
$ flags = fcntl ( $ fp , F_SETFL , $ flags | O_NONBLOCK ) or
2010-11-02 14:58:38 -04:00
dodie "Can't set flags for the socket: $!" ;
2010-11-02 14:58:22 -04:00
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 ;
2010-11-02 14:58:27 -04:00
return ;
open ( MONFD , "Stop perl from warning about single use of MONFD" ) ;
2010-11-02 14:58:22 -04:00
}
sub end_monitor {
if ( - - $ monitor_cnt ) {
return ;
}
close_console ( $ monitor_fp , $ monitor_pid ) ;
}
sub wait_for_monitor {
my ( $ time ) = @ _ ;
my $ line ;
2010-11-02 14:58:27 -04:00
doprint "** Wait for monitor to settle down **\n" ;
2010-11-02 14:58:22 -04:00
# read the monitor and wait for the system to calm down
do {
$ line = wait_for_input ( $ monitor_fp , $ time ) ;
2010-11-02 14:58:27 -04:00
print "$line" if ( defined ( $ line ) ) ;
2010-11-02 14:58:22 -04:00
} while ( defined ( $ line ) ) ;
2010-11-02 14:58:27 -04:00
print "** Monitor flushed **\n" ;
2010-11-02 14:58:22 -04:00
}
2010-11-02 14:58:15 -04:00
sub fail {
2010-11-02 14:58:27 -04:00
if ( $ die_on_failure ) {
2010-11-02 14:58:15 -04:00
dodie @ _ ;
}
2010-11-02 14:58:27 -04:00
doprint "FAILED\n" ;
2010-11-02 14:58:22 -04:00
2010-11-02 14:58:38 -04:00
my $ i = $ iteration ;
2010-11-02 14:58:27 -04:00
# no need to reboot for just building.
2010-11-02 14:58:38 -04:00
if ( ! do_not_reboot ) {
2010-11-02 14:58:27 -04:00
doprint "REBOOTING\n" ;
reboot ;
start_monitor ;
wait_for_monitor $ sleep_time ;
end_monitor ;
}
2010-11-02 14:58:22 -04:00
2010-11-02 14:58:38 -04:00
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ;
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ;
2010-11-08 16:49:25 -05:00
doprint "KTEST RESULT: TEST $i Failed: " , @ _ , "\n" ;
2010-11-02 14:58:38 -04:00
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ;
doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" ;
2010-11-02 14:58:27 -04:00
return 1 if ( ! defined ( $ store_failures ) ) ;
2010-11-02 14:58:22 -04:00
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 ] ;
2010-11-02 14:58:27 -04:00
my $ dir = "$machine-$test_type-$build_type-fail-$date" ;
my $ faildir = "$store_failures/$dir" ;
2010-11-02 14:58:22 -04:00
if ( ! - d $ faildir ) {
mkpath ( $ faildir ) or
2010-11-02 14:58:27 -04:00
die "can't create $faildir" ;
2010-11-02 14:58:22 -04:00
}
2010-11-08 16:43:21 -05:00
if ( - f "$output_config" ) {
cp "$output_config" , "$faildir/config" or
2010-11-02 14:58:22 -04:00
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" ;
2010-11-02 14:58:15 -04:00
return 1 ;
}
2010-11-02 15:01:32 -04:00
sub run_command {
my ( $ command ) = @ _ ;
2010-11-02 14:58:05 -04:00
my $ dolog = 0 ;
my $ dord = 0 ;
my $ pid ;
2010-11-02 14:35:37 -04:00
$ command =~ s/\$SSH_USER/$ssh_user/g ;
$ command =~ s/\$MACHINE/$machine/g ;
2010-11-02 14:58:05 -04:00
doprint ( "$command ... " ) ;
$ pid = open ( CMD , "$command 2>&1 |" ) or
2010-11-02 14:58:15 -04:00
( fail "unable to exec $command" and return 0 ) ;
2010-11-02 15:01:32 -04:00
if ( defined ( $ opt { "LOG_FILE" } ) ) {
2010-11-02 14:58:05 -04:00
open ( LOG , ">>$opt{LOG_FILE}" ) or
dodie "failed to write to log" ;
$ dolog = 1 ;
2010-11-02 14:57:58 -04:00
}
if ( defined ( $ redirect ) ) {
2010-11-02 14:58:05 -04:00
open ( RD , ">$redirect" ) or
dodie "failed to write to redirect $redirect" ;
$ dord = 1 ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:58:05 -04:00
while ( <CMD> ) {
print LOG if ( $ dolog ) ;
print RD if ( $ dord ) ;
}
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:05 -04:00
waitpid ( $ pid , 0 ) ;
2010-11-02 15:01:32 -04:00
my $ failed = $? ;
2010-11-02 14:58:05 -04:00
close ( CMD ) ;
close ( LOG ) if ( $ dolog ) ;
close ( RD ) if ( $ dord ) ;
2010-11-02 15:01:32 -04:00
if ( $ failed ) {
doprint "FAILED!\n" ;
} else {
doprint "SUCCESS\n" ;
}
2010-11-02 14:57:33 -04:00
return ! $ failed ;
}
2010-11-02 14:35:37 -04:00
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" ;
}
2010-11-02 14:57:33 -04:00
sub get_grub_index {
2010-11-02 14:58:27 -04:00
if ( $ reboot_type ne "grub" ) {
return ;
}
2010-11-02 14:57:43 -04:00
return if ( defined ( $ grub_number ) ) ;
2010-11-02 14:57:33 -04:00
doprint "Find grub menu ... " ;
$ grub_number = - 1 ;
2010-11-02 14:35:37 -04:00
my $ ssh_grub = $ ssh_exec ;
$ ssh_grub =~ s , \ $ SSH_COMMAND , cat /boot/g rub / menu . lst , g ;
open ( IN , "$ssh_grub |" )
2010-11-02 14:57:33 -04:00
or die "unable to get menu.lst" ;
2010-11-02 14:35:37 -04:00
2010-11-02 14:57:33 -04:00
while ( <IN> ) {
2010-11-02 14:58:27 -04:00
if ( /^\s*title\s+$grub_menu\s*$/ ) {
2010-11-02 14:57:33 -04:00
$ grub_number + + ;
last ;
} elsif ( /^\s*title\s/ ) {
$ grub_number + + ;
}
}
close ( IN ) ;
2010-11-02 14:58:27 -04:00
die "Could not find '$grub_menu' in /boot/grub/menu on $machine"
2010-11-02 14:57:33 -04:00
if ( $ grub_number < 0 ) ;
doprint "$grub_number\n" ;
2010-11-02 15:01:32 -04:00
}
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 ;
}
2010-11-02 14:57:21 -04:00
sub reboot_to {
2010-11-02 14:58:27 -04:00
if ( $ reboot_type eq "grub" ) {
2010-11-02 14:35:37 -04:00
run_command "$ssh_exec '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'" ;
2010-11-02 14:58:27 -04:00
return ;
}
run_command "$reboot_script" ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 15:13:54 -04:00
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 ;
}
2010-11-02 14:57:43 -04:00
sub monitor {
2010-11-02 15:01:32 -04:00
my $ booted = 0 ;
my $ bug = 0 ;
2010-11-02 14:57:01 -04:00
my $ skip_call_trace = 0 ;
2010-11-02 14:58:15 -04:00
my $ loops ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:22 -04:00
wait_for_monitor 5 ;
2010-11-02 15:01:32 -04:00
my $ line ;
my $ full_line = "" ;
2010-11-02 14:58:22 -04:00
open ( DMESG , "> $dmesg" ) or
die "unable to write to $dmesg" ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:57:21 -04:00
reboot_to ;
2010-11-02 15:01:32 -04:00
for ( ; ; ) {
2010-11-02 14:58:15 -04:00
if ( $ booted ) {
2010-11-02 14:58:27 -04:00
$ line = wait_for_input ( $ monitor_fp , $ booted_timeout ) ;
2010-11-02 14:58:15 -04:00
} else {
2010-11-02 14:58:22 -04:00
$ line = wait_for_input ( $ monitor_fp ) ;
2010-11-02 14:58:15 -04:00
}
2010-11-02 15:01:32 -04:00
last if ( ! defined ( $ line ) ) ;
doprint $ line ;
2010-11-02 14:58:22 -04:00
print DMESG $ line ;
2010-11-02 15:01:32 -04:00
# we are not guaranteed to get a full line
$ full_line . = $ line ;
2010-11-02 14:58:27 -04:00
if ( $ full_line =~ /$success_line/ ) {
2010-11-02 15:01:32 -04:00
$ booted = 1 ;
}
2010-11-02 14:57:01 -04:00
if ( $ full_line =~ /\[ backtrace testing \]/ ) {
$ skip_call_trace = 1 ;
}
2010-11-02 15:01:32 -04:00
if ( $ full_line =~ /call trace:/i ) {
2010-11-02 14:57:01 -04:00
$ bug = 1 if ( ! $ skip_call_trace ) ;
}
if ( $ full_line =~ /\[ end of backtrace testing \]/ ) {
$ skip_call_trace = 0 ;
}
if ( $ full_line =~ /Kernel panic -/ ) {
2010-11-02 15:01:32 -04:00
$ bug = 1 ;
}
if ( $ line =~ /\n/ ) {
$ full_line = "" ;
}
}
2010-11-02 14:58:22 -04:00
close ( DMESG ) ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:27 -04:00
if ( $ bug ) {
2010-11-02 14:58:15 -04:00
return 0 if ( $ in_bisect ) ;
2010-11-02 14:58:38 -04:00
fail "failed - got a bug report" and return 0 ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:58:27 -04:00
if ( ! $ booted ) {
2010-11-02 14:58:15 -04:00
return 0 if ( $ in_bisect ) ;
2010-11-02 14:58:38 -04:00
fail "failed - never got a boot prompt." and return 0 ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:57:33 -04:00
2010-11-02 14:58:15 -04:00
return 1 ;
2010-11-02 15:01:32 -04:00
}
sub install {
2010-11-02 14:35:37 -04:00
run_scp "$outputdir/$build_target" , "$target_image" or
2010-11-02 14:57:01 -04:00
dodie "failed to copy image" ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:57:33 -04:00
my $ install_mods = 0 ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:57:33 -04:00
# should we process modules?
$ install_mods = 0 ;
2010-11-08 16:43:21 -05:00
open ( IN , "$output_config" ) or dodie ( "Can't read config file" ) ;
2010-11-02 14:57:33 -04:00
while ( <IN> ) {
if ( /CONFIG_MODULES(=y)?/ ) {
$ install_mods = 1 if ( defined ( $ 1 ) ) ;
last ;
2010-11-02 14:57:01 -04:00
}
2010-11-02 14:57:33 -04:00
}
close ( IN ) ;
2010-11-02 14:57:01 -04:00
2010-11-02 14:57:33 -04:00
if ( ! $ install_mods ) {
doprint "No modules needed\n" ;
return ;
}
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:27 -04:00
run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or
2010-11-02 14:57:33 -04:00
dodie "Failed to install modules" ;
2010-11-02 14:57:01 -04:00
2010-11-02 14:57:33 -04:00
my $ modlib = "/lib/modules/$version" ;
2010-11-02 15:13:54 -04:00
my $ modtar = "ktest-mods.tar.bz2" ;
2010-11-02 14:57:01 -04:00
2010-11-02 14:35:37 -04:00
run_ssh "rm -rf $modlib" or
2010-11-02 14:57:33 -04:00
dodie "failed to remove old mods: $modlib" ;
2010-11-02 14:57:01 -04:00
2010-11-02 14:57:33 -04:00
# would be nice if scp -r did not follow symbolic links
2010-11-02 14:58:27 -04:00
run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or
2010-11-02 14:57:33 -04:00
dodie "making tarball" ;
2010-11-02 14:35:37 -04:00
run_scp "$tmpdir/$modtar" , "/tmp" or
2010-11-02 14:57:33 -04:00
dodie "failed to copy modules" ;
2010-11-02 14:58:27 -04:00
unlink "$tmpdir/$modtar" ;
2010-11-02 14:57:33 -04:00
2010-11-02 14:35:37 -04:00
run_ssh "'(cd / && tar xf /tmp/$modtar)'" or
2010-11-02 14:57:33 -04:00
dodie "failed to tar modules" ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:35:37 -04:00
run_ssh "rm -f /tmp/$modtar" ;
2010-11-02 14:58:33 -04:00
return if ( ! defined ( $ post_install ) ) ;
2010-11-02 14:35:37 -04:00
my $ cp_post_install = $ post_install ;
$ cp_post_install = s/\$KERNEL_VERSION/$version/g ;
run_command "$cp_post_install" or
2010-11-02 14:58:38 -04:00
dodie "Failed to run post install" ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:57:58 -04:00
sub check_buildlog {
my ( $ patch ) = @ _ ;
my @ files = `git show $patch | diffstat -l` ;
open ( IN , "git show $patch |" ) or
dodie "failed to show $patch" ;
while ( <IN> ) {
if ( m , ^ - - - a / ( . * ) , ) {
chomp $ 1 ;
$ files [ $# files ] = $ 1 ;
}
}
close ( IN ) ;
open ( IN , $ buildlog ) or dodie "Can't open $buildlog" ;
while ( <IN> ) {
if ( /^\s*(.*?):.*(warning|error)/ ) {
my $ err = $ 1 ;
foreach my $ file ( @ files ) {
2010-11-02 14:58:27 -04:00
my $ fullpath = "$builddir/$file" ;
2010-11-02 14:57:58 -04:00
if ( $ file eq $ err || $ fullpath eq $ err ) {
2010-11-02 14:58:15 -04:00
fail "$file built with warnings" and return 0 ;
2010-11-02 14:57:58 -04:00
}
}
}
}
close ( IN ) ;
2010-11-02 14:58:15 -04:00
return 1 ;
2010-11-02 14:57:58 -04:00
}
2010-11-02 15:01:32 -04:00
sub build {
my ( $ type ) = @ _ ;
2010-11-02 14:57:01 -04:00
my $ defconfig = "" ;
2010-11-02 14:58:22 -04:00
unlink $ buildlog ;
2010-11-02 14:57:21 -04:00
if ( $ type =~ /^useconfig:(.*)/ ) {
2010-11-08 16:43:21 -05:00
run_command "cp $1 $output_config" or
2010-11-02 14:57:21 -04:00
dodie "could not copy $1 to .config" ;
2010-11-02 14:57:33 -04:00
2010-11-02 14:57:21 -04:00
$ type = "oldconfig" ;
}
2010-11-02 14:57:01 -04:00
# old config can ask questions
if ( $ type eq "oldconfig" ) {
2010-11-08 16:35:48 -05:00
$ type = "oldnoconfig" ;
2010-11-02 14:57:21 -04:00
# allow for empty configs
2010-11-08 16:43:21 -05:00
run_command "touch $output_config" ;
2010-11-02 14:57:21 -04:00
2010-11-08 16:43:21 -05:00
run_command "mv $output_config $outputdir/config_temp" or
2010-11-02 14:57:01 -04:00
dodie "moving .config" ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:57:33 -04:00
if ( ! $ noclean && ! run_command "$make mrproper" ) {
2010-11-02 14:57:01 -04:00
dodie "make mrproper" ;
}
2010-11-02 15:01:32 -04:00
2010-11-08 16:43:21 -05:00
run_command "mv $outputdir/config_temp $output_config" or
2010-11-02 14:57:01 -04:00
dodie "moving config_temp" ;
} elsif ( ! $ noclean ) {
2010-11-08 16:43:21 -05:00
unlink "$output_config" ;
2010-11-02 14:57:33 -04:00
run_command "$make mrproper" or
2010-11-02 14:57:01 -04:00
dodie "make mrproper" ;
}
2010-11-02 15:01:32 -04:00
# add something to distinguish this build
2010-11-02 14:58:27 -04:00
open ( OUT , "> $outputdir/localversion" ) or dodie ( "Can't make localversion file" ) ;
print OUT "$localversion\n" ;
2010-11-02 15:01:32 -04:00
close ( OUT ) ;
2010-11-02 14:57:33 -04:00
if ( defined ( $ minconfig ) ) {
$ defconfig = "KCONFIG_ALLCONFIG=$minconfig" ;
2010-11-02 15:01:32 -04:00
}
2010-11-08 16:35:48 -05:00
run_command "$defconfig $make $type" or
2010-11-02 14:57:01 -04:00
dodie "failed make config" ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:27 -04:00
$ redirect = "$buildlog" ;
if ( ! run_command "$make $build_options" ) {
2010-11-02 14:57:58 -04:00
undef $ redirect ;
2010-11-02 14:57:33 -04:00
# bisect may need this to pass
2010-11-02 14:58:15 -04:00
return 0 if ( $ in_bisect ) ;
fail "failed build" and return 0 ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:57:58 -04:00
undef $ redirect ;
2010-11-02 14:57:33 -04:00
2010-11-02 14:58:15 -04:00
return 1 ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:57:21 -04:00
sub halt {
2010-11-02 14:35:37 -04:00
if ( ! run_ssh "halt" or defined ( $ power_off ) ) {
2010-11-02 14:58:38 -04:00
if ( defined ( $ poweroff_after_halt ) ) {
sleep $ poweroff_after_halt ;
run_command "$power_off" ;
}
} else {
2010-11-02 14:57:21 -04:00
# nope? the zap it!
2010-11-02 14:58:27 -04:00
run_command "$power_off" ;
2010-11-02 14:57:21 -04:00
}
}
2010-11-02 14:57:33 -04:00
sub success {
my ( $ i ) = @ _ ;
2010-11-02 14:35:37 -04:00
$ successes + + ;
2010-11-02 14:57:33 -04:00
doprint "\n\n*******************************************\n" ;
doprint "*******************************************\n" ;
2010-11-08 16:49:25 -05:00
doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n" ;
2010-11-02 14:57:33 -04:00
doprint "*******************************************\n" ;
doprint "*******************************************\n" ;
2010-11-02 14:58:38 -04:00
if ( $ i != $ opt { "NUM_TESTS" } && ! do_not_reboot ) {
2010-11-02 14:58:27 -04:00
doprint "Reboot and wait $sleep_time seconds\n" ;
2010-11-02 14:57:33 -04:00
reboot ;
2010-11-02 14:58:22 -04:00
start_monitor ;
2010-11-02 14:58:27 -04:00
wait_for_monitor $ sleep_time ;
2010-11-02 14:58:22 -04:00
end_monitor ;
2010-11-02 14:57:33 -04:00
}
}
sub get_version {
# get the release name
doprint "$make kernelrelease ... " ;
$ version = `$make kernelrelease | tail -1` ;
chomp ( $ version ) ;
doprint "$version\n" ;
}
2010-11-02 14:57:43 -04:00
sub child_run_test {
2010-11-02 14:58:22 -04:00
my $ failed = 0 ;
2010-11-02 14:57:43 -04:00
2010-11-02 14:58:22 -04:00
# child should have no power
2010-11-02 14:58:27 -04:00
$ reboot_on_error = 0 ;
$ poweroff_on_error = 0 ;
$ die_on_failure = 1 ;
2010-11-02 14:58:22 -04:00
run_command $ run_test or $ failed = 1 ;
2010-11-02 14:57:43 -04:00
exit $ failed ;
}
my $ child_done ;
sub child_finished {
$ child_done = 1 ;
}
sub do_run_test {
my $ child_pid ;
my $ child_exit ;
my $ line ;
my $ full_line ;
my $ bug = 0 ;
2010-11-02 14:58:22 -04:00
wait_for_monitor 1 ;
2010-11-02 14:57:43 -04:00
2010-11-02 14:58:22 -04:00
doprint "run test $run_test\n" ;
2010-11-02 14:57:43 -04:00
$ child_done = 0 ;
$ SIG { CHLD } = qw( child_finished ) ;
$ child_pid = fork ;
child_run_test if ( ! $ child_pid ) ;
$ full_line = "" ;
do {
2010-11-02 14:58:22 -04:00
$ line = wait_for_input ( $ monitor_fp , 1 ) ;
2010-11-02 14:57:43 -04:00
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 = $? ;
if ( $ bug || $ child_exit ) {
2010-11-02 14:58:15 -04:00
return 0 if $ in_bisect ;
fail "test failed" and return 0 ;
2010-11-02 14:57:43 -04:00
}
2010-11-02 14:58:15 -04:00
return 1 ;
2010-11-02 14:57:43 -04:00
}
2010-11-02 14:58:27 -04:00
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 ;
}
2010-11-08 11:14:10 -05:00
# returns 1 on success, 0 on failure
sub run_bisect_test {
my ( $ type , $ buildtype ) = @ _ ;
2010-11-02 14:57:33 -04:00
2010-11-02 14:58:15 -04:00
my $ failed = 0 ;
2010-11-02 14:57:33 -04:00
my $ result ;
my $ output ;
my $ ret ;
2010-11-08 11:14:10 -05:00
$ in_bisect = 1 ;
build $ buildtype or $ failed = 1 ;
2010-11-02 14:57:33 -04:00
if ( $ type ne "build" ) {
2010-11-02 14:58:22 -04:00
dodie "Failed on build" if $ failed ;
2010-11-02 14:57:33 -04:00
# Now boot the box
get_grub_index ;
get_version ;
install ;
2010-11-02 14:58:22 -04:00
start_monitor ;
2010-11-02 14:58:15 -04:00
monitor or $ failed = 1 ;
2010-11-02 14:57:33 -04:00
if ( $ type ne "boot" ) {
2010-11-02 14:58:22 -04:00
dodie "Failed on boot" if $ failed ;
2010-11-02 14:57:43 -04:00
2010-11-02 14:58:15 -04:00
do_run_test or $ failed = 1 ;
2010-11-02 14:57:33 -04:00
}
2010-11-02 14:58:22 -04:00
end_monitor ;
2010-11-02 14:57:33 -04:00
}
if ( $ failed ) {
2010-11-08 11:14:10 -05:00
$ result = 0 ;
2010-11-02 14:57:43 -04:00
# reboot the box to a good kernel
2010-11-02 14:58:27 -04:00
if ( $ type ne "build" ) {
doprint "Reboot and sleep $bisect_sleep_time seconds\n" ;
2010-11-02 14:57:43 -04:00
reboot ;
2010-11-02 14:58:22 -04:00
start_monitor ;
2010-11-02 14:58:27 -04:00
wait_for_monitor $ bisect_sleep_time ;
2010-11-02 14:58:22 -04:00
end_monitor ;
2010-11-02 14:57:43 -04:00
}
2010-11-02 14:57:33 -04:00
} else {
2010-11-08 11:14:10 -05:00
$ 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" ;
2010-11-02 14:57:33 -04:00
}
2010-11-08 11:14:10 -05:00
my $ ret = run_bisect_test $ type , $ buildtype ;
2010-11-02 14:58:05 -04:00
# Are we looking for where it worked, not failed?
if ( $ reverse_bisect ) {
2010-11-08 11:14:10 -05:00
$ ret = ! $ ret ;
2010-11-02 14:58:05 -04:00
}
2010-11-08 11:14:10 -05:00
if ( $ ret ) {
return "good" ;
} else {
return "bad" ;
}
2010-11-02 14:57:33 -04:00
}
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]" } ;
2010-11-02 14:58:27 -04:00
my $ start = $ opt { "BISECT_START[$i]" } ;
my $ replay = $ opt { "BISECT_REPLAY[$i]" } ;
2010-11-02 14:57:33 -04:00
2010-11-02 15:13:54 -04:00
# convert to true sha1's
$ good = get_sha1 ( $ good ) ;
$ bad = get_sha1 ( $ bad ) ;
2010-11-02 14:58:05 -04:00
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 ;
}
2010-11-02 14:58:27 -04:00
# 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
2010-11-02 15:13:54 -04:00
my $ head = get_sha1 ( "HEAD" ) ;
2010-11-02 14:58:27 -04:00
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" ;
}
2010-11-02 14:57:33 -04:00
run_command "git bisect start" or
2010-11-02 14:58:27 -04:00
dodie "could not start bisect" ;
2010-11-02 14:57:33 -04:00
run_command "git bisect good $good" or
2010-11-02 14:58:27 -04:00
dodie "could not set bisect good to $good" ;
2010-11-02 14:57:33 -04:00
2010-11-02 14:58:27 -04:00
run_git_bisect "git bisect bad $bad" or
dodie "could not set bisect bad to $bad" ;
2010-11-02 14:57:33 -04:00
2010-11-02 14:58:27 -04:00
if ( defined ( $ replay ) ) {
run_command "git bisect replay $replay" or
dodie "failed to run replay" ;
2010-11-02 14:57:43 -04:00
}
2010-11-02 14:58:27 -04:00
if ( defined ( $ start ) ) {
run_command "git checkout $start" or
dodie "failed to checkout $start" ;
}
my $ test ;
2010-11-02 14:57:33 -04:00
do {
$ result = run_bisect $ type ;
2010-11-02 14:58:27 -04:00
$ test = run_git_bisect "git bisect $result" ;
} while ( $ test ) ;
2010-11-02 14:57:33 -04:00
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" ;
2010-11-08 11:14:10 -05:00
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 ( <IN> ) {
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 ( <IN> ) {
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 ( <IN> ) {
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 ( <IN> ) {
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 ) ;
2010-11-02 14:57:33 -04:00
success $ i ;
}
2010-11-02 14:57:58 -04:00
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]" } ;
}
2010-11-02 15:13:54 -04:00
# Get the true sha1's since we can use things like HEAD~3
$ start = get_sha1 ( $ start ) ;
$ end = get_sha1 ( $ end ) ;
2010-11-02 14:57:58 -04:00
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 ( <IN> ) {
chomp ;
$ list [ $# list + 1 ] = $ _ ;
last if ( /^$start/ ) ;
}
close ( IN ) ;
if ( $ list [ $# list ] !~ /^$start/ ) {
2010-11-02 14:58:15 -04:00
fail "SHA1 $start not found" ;
2010-11-02 14:57:58 -04:00
}
# 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 ) ) {
2010-11-02 14:58:15 -04:00
build "useconfig:$minconfig" or return 0 ;
2010-11-02 14:57:58 -04:00
} else {
# ?? no config to use?
2010-11-02 14:58:15 -04:00
build "oldconfig" or return 0 ;
2010-11-02 14:57:58 -04:00
}
2010-11-02 14:58:15 -04:00
check_buildlog $ sha1 or return 0 ;
2010-11-02 14:57:58 -04:00
next if ( $ type eq "build" ) ;
get_grub_index ;
get_version ;
install ;
2010-11-02 14:58:22 -04:00
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 ) ;
2010-11-02 14:57:58 -04:00
}
$ in_patchcheck = 0 ;
success $ i ;
2010-11-02 14:58:15 -04:00
return 1 ;
2010-11-02 14:57:58 -04:00
}
2010-11-02 15:01:32 -04:00
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" } ) ) ;
2010-11-02 14:57:21 -04:00
die "TARGET_IMAGE not defined\n" if ( ! defined ( $ opt { "TARGET_IMAGE" } ) ) ;
2010-11-02 15:01:32 -04:00
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" } ) ) ;
2010-11-02 14:58:15 -04:00
if ( $ opt { "CLEAR_LOG" } && defined ( $ opt { "LOG_FILE" } ) ) {
unlink $ opt { "LOG_FILE" } ;
}
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:15 -04:00
doprint "\n\nSTARTING AUTOMATED TESTS\n\n" ;
2010-11-02 15:13:54 -04:00
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" ;
}
2010-11-02 14:58:15 -04:00
}
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:27 -04:00
sub set_test_option {
2010-11-02 14:57:43 -04:00
my ( $ name , $ i ) = @ _ ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:57:43 -04:00
my $ option = "$name\[$i\]" ;
2010-11-02 14:57:01 -04:00
2010-11-02 14:57:43 -04:00
if ( defined ( $ opt { $ option } ) ) {
return $ opt { $ option } ;
2010-11-02 14:57:33 -04:00
}
2010-11-02 15:13:54 -04:00
foreach my $ test ( keys % repeat_tests ) {
if ( $ i >= $ test &&
$ i < $ test + $ repeat_tests { $ test } ) {
$ option = "$name\[$test\]" ;
if ( defined ( $ opt { $ option } ) ) {
return $ opt { $ option } ;
}
}
}
2010-11-02 14:57:43 -04:00
if ( defined ( $ opt { $ name } ) ) {
return $ opt { $ name } ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:57:43 -04:00
return undef ;
}
# First we need to do is the builds
2010-11-02 14:58:27 -04:00
for ( my $ i = 1 ; $ i <= $ opt { "NUM_TESTS" } ; $ i + + ) {
2010-11-02 14:58:38 -04:00
$ iteration = $ i ;
2010-11-02 14:58:27 -04:00
my $ makecmd = set_test_option ( "MAKE_CMD" , $ i ) ;
$ machine = set_test_option ( "MACHINE" , $ i ) ;
2010-11-02 14:35:37 -04:00
$ ssh_user = set_test_option ( "SSH_USER" , $ i ) ;
2010-11-02 14:58:27 -04:00
$ 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 ) ;
2010-11-02 14:35:37 -04:00
$ reboot = set_test_option ( "REBOOT" , $ i ) ;
2010-11-02 14:58:27 -04:00
$ 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 ) ;
2010-11-02 14:58:33 -04:00
$ post_install = set_test_option ( "POST_INSTALL" , $ i ) ;
2010-11-02 14:58:27 -04:00
$ 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 ) ;
2010-11-02 14:58:38 -04:00
$ powercycle_after_reboot = set_test_option ( "POWERCYCLE_AFTER_REBOOT" , $ i ) ;
$ poweroff_after_halt = set_test_option ( "POWEROFF_AFTER_HALT" , $ i ) ;
2010-11-02 14:58:27 -04:00
$ 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 ) ;
2010-11-02 14:35:37 -04:00
$ ssh_exec = set_test_option ( "SSH_EXEC" , $ i ) ;
$ scp_to_target = set_test_option ( "SCP_TO_TARGET" , $ i ) ;
2010-11-02 14:58:27 -04:00
$ 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" ;
}
2010-11-02 14:57:51 -04:00
2010-11-02 14:35:37 -04:00
$ ENV { "SSH_USER" } = $ ssh_user ;
$ ENV { "MACHINE" } = $ machine ;
2010-11-02 14:58:27 -04:00
$ target = "$ssh_user\@$machine" ;
$ buildlog = "$tmpdir/buildlog-$machine" ;
$ dmesg = "$tmpdir/dmesg-$machine" ;
$ make = "$makecmd O=$outputdir" ;
2010-11-08 16:43:21 -05:00
$ output_config = "$outputdir/.config" ;
2010-11-02 14:58:27 -04:00
if ( $ reboot_type eq "grub" ) {
2010-11-02 14:58:38 -04:00
dodie "GRUB_MENU not defined" if ( ! defined ( $ grub_menu ) ) ;
2010-11-02 14:58:27 -04:00
} elsif ( ! defined ( $ reboot_script ) ) {
2010-11-02 14:58:38 -04:00
dodie "REBOOT_SCRIPT not defined"
2010-11-02 14:58:27 -04:00
}
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]" } ;
2010-11-08 11:14:10 -05:00
} elsif ( $ test_type eq "config_bisect" ) {
$ run_type = $ opt { "CONFIG_BISECT_TYPE[$i]" } ;
2010-11-02 14:58:27 -04:00
}
# mistake in config file?
if ( ! defined ( $ run_type ) ) {
$ run_type = "ERROR" ;
}
2010-11-02 14:57:43 -04:00
2010-11-02 15:01:32 -04:00
doprint "\n\n" ;
2010-11-02 14:58:27 -04:00
doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n" ;
2010-11-02 14:58:22 -04:00
unlink $ dmesg ;
unlink $ buildlog ;
2010-11-02 15:01:32 -04:00
2010-11-02 14:58:15 -04:00
if ( ! defined ( $ minconfig ) ) {
$ minconfig = $ addconfig ;
} elsif ( defined ( $ addconfig ) ) {
2010-11-09 12:20:21 -05:00
run_command "cat $addconfig $minconfig > $tmpdir/add_config" or
2010-11-02 14:58:15 -04:00
dodie "Failed to create temp config" ;
2010-11-09 12:20:21 -05:00
$ minconfig = "$tmpdir/add_config" ;
2010-11-02 14:58:15 -04:00
}
2010-11-02 14:57:58 -04:00
my $ checkout = $ opt { "CHECKOUT[$i]" } ;
if ( defined ( $ checkout ) ) {
run_command "git checkout $checkout" or
die "failed to checkout $checkout" ;
}
2010-11-02 14:58:27 -04:00
if ( $ test_type eq "bisect" ) {
2010-11-02 14:57:33 -04:00
bisect $ i ;
next ;
2010-11-08 11:14:10 -05:00
} elsif ( $ test_type eq "config_bisect" ) {
config_bisect $ i ;
next ;
2010-11-02 14:58:27 -04:00
} elsif ( $ test_type eq "patchcheck" ) {
2010-11-02 14:57:58 -04:00
patchcheck $ i ;
next ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:58:22 -04:00
if ( $ build_type ne "nobuild" ) {
build $ build_type or next ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:58:27 -04:00
if ( $ test_type ne "build" ) {
get_grub_index ;
get_version ;
install ;
2010-11-02 14:57:43 -04:00
2010-11-02 14:58:27 -04:00
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 ) ;
2010-11-02 14:57:43 -04:00
}
2010-11-02 14:57:33 -04:00
success $ i ;
2010-11-02 15:01:32 -04:00
}
2010-11-02 14:57:01 -04:00
if ( $ opt { "POWEROFF_ON_SUCCESS" } ) {
2010-11-02 14:57:21 -04:00
halt ;
2010-11-02 14:58:38 -04:00
} elsif ( $ opt { "REBOOT_ON_SUCCESS" } && ! do_not_reboot ) {
2010-11-02 14:57:21 -04:00
reboot ;
2010-11-02 14:57:01 -04:00
}
2010-11-02 14:57:21 -04:00
2010-11-02 14:35:37 -04:00
doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n" ;
2010-11-02 15:01:32 -04:00
exit 0 ;