ktest: Use Kconfig dependencies to shorten time to make min_config
[platform/adaptation/renesas_rcar/renesas_kernel.git] / tools / testing / ktest / ktest.pl
index d0e1de6..a9f2e10 100755 (executable)
@@ -27,7 +27,7 @@ $default{"TEST_TYPE"}         = "test";
 $default{"BUILD_TYPE"}         = "randconfig";
 $default{"MAKE_CMD"}           = "make";
 $default{"TIMEOUT"}            = 120;
-$default{"TMP_DIR"}            = "/tmp/ktest";
+$default{"TMP_DIR"}            = "/tmp/ktest/\${MACHINE}";
 $default{"SLEEP_TIME"}         = 60;   # sleep time between tests
 $default{"BUILD_NOCLEAN"}      = 0;
 $default{"REBOOT_ON_ERROR"}    = 0;
@@ -86,6 +86,9 @@ my $make;
 my $post_install;
 my $noclean;
 my $minconfig;
+my $start_minconfig;
+my $output_minconfig;
+my $ignore_config;
 my $addconfig;
 my $in_bisect = 0;
 my $bisect_bad = "";
@@ -104,6 +107,7 @@ my $monitor_cnt = 0;
 my $sleep_time;
 my $bisect_sleep_time;
 my $patchcheck_sleep_time;
+my $ignore_warnings;
 my $store_failures;
 my $test_name;
 my $timeout;
@@ -344,6 +348,7 @@ sub read_config {
     my $num_tests_set = 0;
     my $skip = 0;
     my $rest;
+    my $test_case = 0;
 
     while (<IN>) {
 
@@ -369,6 +374,7 @@ sub read_config {
                $rest = $1;
                $skip = 1;
            } else {
+               $test_case = 1;
                $skip = 0;
            }
 
@@ -473,6 +479,15 @@ sub read_config {
     # make sure we have all mandatory configs
     get_ktest_configs;
 
+    # was a test specified?
+    if (!$test_case) {
+       print "No test case specified.\n";
+       print "What test case would you like to run?\n";
+       my $ans = <STDIN>;
+       chomp $ans;
+       $default{"TEST_TYPE"} = $ans;
+    }
+
     # set any defaults
 
     foreach my $default (keys %default) {
@@ -1087,7 +1102,7 @@ sub install {
 
     unlink "$tmpdir/$modtar";
 
-    run_ssh "'(cd / && tar xf /tmp/$modtar)'" or
+    run_ssh "'(cd / && tar xjf /tmp/$modtar)'" or
        dodie "failed to tar modules";
 
     run_ssh "rm -f /tmp/$modtar";
@@ -1095,6 +1110,23 @@ sub install {
     do_post_install;
 }
 
+sub get_version {
+    # get the release name
+    doprint "$make kernelrelease ... ";
+    $version = `$make kernelrelease | tail -1`;
+    chomp($version);
+    doprint "$version\n";
+}
+
+sub start_monitor_and_boot {
+    get_grub_index;
+    get_version;
+    install;
+
+    start_monitor;
+    return monitor;
+}
+
 sub check_buildlog {
     my ($patch) = @_;
 
@@ -1160,7 +1192,11 @@ sub apply_min_config {
 
 sub make_oldconfig {
 
-    apply_min_config;
+    my @force_list = keys %force_config;
+
+    if ($#force_list >= 0) {
+       apply_min_config;
+    }
 
     if (!run_command "$make oldnoconfig") {
        # Perhaps oldnoconfig doesn't exist in this version of the kernel
@@ -1306,14 +1342,6 @@ sub success {
     }
 }
 
-sub get_version {
-    # get the release name
-    doprint "$make kernelrelease ... ";
-    $version = `$make kernelrelease | tail -1`;
-    chomp($version);
-    doprint "$version\n";
-}
-
 sub answer_bisect {
     for (;;) {
        doprint "Pass or fail? [p/f]";
@@ -1478,12 +1506,7 @@ sub run_bisect_test {
        dodie "Failed on build" if $failed;
 
        # Now boot the box
-       get_grub_index;
-       get_version;
-       install;
-
-       start_monitor;
-       monitor or $failed = 1;
+       start_monitor_and_boot or $failed = 1;
 
        if ($type ne "boot") {
            if ($failed && $bisect_skip) {
@@ -1662,21 +1685,27 @@ my %null_config;
 
 my %dependency;
 
-sub process_config_ignore {
-    my ($config) = @_;
+sub assign_configs {
+    my ($hash, $config) = @_;
 
     open (IN, $config)
        or dodie "Failed to read $config";
 
     while (<IN>) {
        if (/^((CONFIG\S*)=.*)/) {
-           $config_ignore{$2} = $1;
+           ${$hash}{$2} = $1;
        }
     }
 
     close(IN);
 }
 
+sub process_config_ignore {
+    my ($config) = @_;
+
+    assign_configs \%config_ignore, $config;
+}
+
 sub read_current_config {
     my ($config_ref) = @_;
 
@@ -2074,6 +2103,13 @@ sub patchcheck {
     @list = reverse @list;
 
     my $save_clean = $noclean;
+    my %ignored_warnings;
+
+    if (defined($ignore_warnings)) {
+       foreach my $sha1 (split /\s+/, $ignore_warnings) {
+           $ignored_warnings{$sha1} = 1;
+       }
+    }
 
     $in_patchcheck = 1;
     foreach my $item (@list) {
@@ -2100,18 +2136,16 @@ sub patchcheck {
            build "oldconfig" or return 0;
        }
 
-       check_buildlog $sha1 or return 0;
 
-       next if ($type eq "build");
+       if (!defined($ignored_warnings{$sha1})) {
+           check_buildlog $sha1 or return 0;
+       }
 
-       get_grub_index;
-       get_version;
-       install;
+       next if ($type eq "build");
 
        my $failed = 0;
 
-       start_monitor;
-       monitor or $failed = 1;
+       start_monitor_and_boot or $failed = 1;
 
        if (!$failed && $type ne "boot"){
            do_run_test or $failed = 1;
@@ -2128,6 +2162,465 @@ sub patchcheck {
     return 1;
 }
 
+my %depends;
+my $iflevel = 0;
+my @ifdeps;
+
+# prevent recursion
+my %read_kconfigs;
+
+# taken from streamline_config.pl
+sub read_kconfig {
+    my ($kconfig) = @_;
+
+    my $state = "NONE";
+    my $config;
+    my @kconfigs;
+
+    my $cont = 0;
+    my $line;
+
+
+    if (! -f $kconfig) {
+       doprint "file $kconfig does not exist, skipping\n";
+       return;
+    }
+
+    open(KIN, "$kconfig")
+       or die "Can't open $kconfig";
+    while (<KIN>) {
+       chomp;
+
+       # Make sure that lines ending with \ continue
+       if ($cont) {
+           $_ = $line . " " . $_;
+       }
+
+       if (s/\\$//) {
+           $cont = 1;
+           $line = $_;
+           next;
+       }
+
+       $cont = 0;
+
+       # collect any Kconfig sources
+       if (/^source\s*"(.*)"/) {
+           $kconfigs[$#kconfigs+1] = $1;
+       }
+
+       # configs found
+       if (/^\s*(menu)?config\s+(\S+)\s*$/) {
+           $state = "NEW";
+           $config = $2;
+
+           for (my $i = 0; $i < $iflevel; $i++) {
+               if ($i) {
+                   $depends{$config} .= " " . $ifdeps[$i];
+               } else {
+                   $depends{$config} = $ifdeps[$i];
+               }
+               $state = "DEP";
+           }
+
+       # collect the depends for the config
+       } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) {
+
+           if (defined($depends{$1})) {
+               $depends{$config} .= " " . $1;
+           } else {
+               $depends{$config} = $1;
+           }
+
+       # Get the configs that select this config
+       } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) {
+           if (defined($depends{$1})) {
+               $depends{$1} .= " " . $config;
+           } else {
+               $depends{$1} = $config;
+           }
+
+       # Check for if statements
+       } elsif (/^if\s+(.*\S)\s*$/) {
+           my $deps = $1;
+           # remove beginning and ending non text
+           $deps =~ s/^[^a-zA-Z0-9_]*//;
+           $deps =~ s/[^a-zA-Z0-9_]*$//;
+
+           my @deps = split /[^a-zA-Z0-9_]+/, $deps;
+
+           $ifdeps[$iflevel++] = join ':', @deps;
+
+       } elsif (/^endif/) {
+
+           $iflevel-- if ($iflevel);
+
+       # stop on "help"
+       } elsif (/^\s*help\s*$/) {
+           $state = "NONE";
+       }
+    }
+    close(KIN);
+
+    # read in any configs that were found.
+    foreach $kconfig (@kconfigs) {
+       if (!defined($read_kconfigs{$kconfig})) {
+           $read_kconfigs{$kconfig} = 1;
+           read_kconfig("$builddir/$kconfig");
+       }
+    }
+}
+
+sub read_depends {
+    # find out which arch this is by the kconfig file
+    open (IN, $output_config)
+       or dodie "Failed to read $output_config";
+    my $arch;
+    while (<IN>) {
+       if (m,Linux/(\S+)\s+\S+\s+Kernel Configuration,) {
+           $arch = $1;
+           last;
+       }
+    }
+    close IN;
+
+    if (!defined($arch)) {
+       doprint "Could not find arch from config file\n";
+       doprint "no dependencies used\n";
+       return;
+    }
+
+    # arch is really the subarch, we need to know
+    # what directory to look at.
+    if ($arch eq "i386" || $arch eq "x86_64") {
+       $arch = "x86";
+    } elsif ($arch =~ /^tile/) {
+       $arch = "tile";
+    }
+
+    my $kconfig = "$builddir/arch/$arch/Kconfig";
+
+    if (! -f $kconfig && $arch =~ /\d$/) {
+       my $orig = $arch;
+       # some subarchs have numbers, truncate them
+       $arch =~ s/\d*$//;
+       $kconfig = "$builddir/arch/$arch/Kconfig";
+       if (! -f $kconfig) {
+           doprint "No idea what arch dir $orig is for\n";
+           doprint "no dependencies used\n";
+           return;
+       }
+    }
+
+    read_kconfig($kconfig);
+}
+
+sub read_config_list {
+    my ($config) = @_;
+
+    open (IN, $config)
+       or dodie "Failed to read $config";
+
+    while (<IN>) {
+       if (/^((CONFIG\S*)=.*)/) {
+           if (!defined($config_ignore{$2})) {
+               $config_list{$2} = $1;
+           }
+       }
+    }
+
+    close(IN);
+}
+
+sub read_output_config {
+    my ($config) = @_;
+
+    assign_configs \%config_ignore, $config;
+}
+
+sub make_new_config {
+    my @configs = @_;
+
+    open (OUT, ">$output_config")
+       or dodie "Failed to write $output_config";
+
+    foreach my $config (@configs) {
+       print OUT "$config\n";
+    }
+    close OUT;
+}
+
+sub get_depends {
+    my ($dep) = @_;
+
+    my $kconfig = $dep;
+    $kconfig =~ s/CONFIG_//;
+
+    $dep = $depends{"$kconfig"};
+
+    # the dep string we have saves the dependencies as they
+    # were found, including expressions like ! && ||. We
+    # want to split this out into just an array of configs.
+
+    my $valid = "A-Za-z_0-9";
+
+    my @configs;
+
+    while ($dep =~ /[$valid]/) {
+
+       if ($dep =~ /^[^$valid]*([$valid]+)/) {
+           my $conf = "CONFIG_" . $1;
+
+           $configs[$#configs + 1] = $conf;
+
+           $dep =~ s/^[^$valid]*[$valid]+//;
+       } else {
+           die "this should never happen";
+       }
+    }
+
+    return @configs;
+}
+
+my %min_configs;
+my %keep_configs;
+my %processed_configs;
+my %nochange_config;
+
+sub test_this_config {
+    my ($config) = @_;
+
+    my $found;
+
+    # if we already processed this config, skip it
+    if (defined($processed_configs{$config})) {
+       return undef;
+    }
+    $processed_configs{$config} = 1;
+
+    # if this config failed during this round, skip it
+    if (defined($nochange_config{$config})) {
+       return undef;
+    }
+
+    my $kconfig = $config;
+    $kconfig =~ s/CONFIG_//;
+
+    # Test dependencies first
+    if (defined($depends{"$kconfig"})) {
+       my @parents = get_depends $config;
+       foreach my $parent (@parents) {
+           # if the parent is in the min config, check it first
+           next if (!defined($min_configs{$parent}));
+           $found = test_this_config($parent);
+           if (defined($found)) {
+               return $found;
+           }
+       }
+    }
+
+    # Remove this config from the list of configs
+    # do a make oldnoconfig and then read the resulting
+    # .config to make sure it is missing the config that
+    # we had before
+    my %configs = %min_configs;
+    delete $configs{$config};
+    make_new_config ((values %configs), (values %keep_configs));
+    make_oldconfig;
+    undef %configs;
+    assign_configs \%configs, $output_config;
+
+    return $config if (!defined($configs{$config}));
+
+    doprint "disabling config $config did not change .config\n";
+
+    $nochange_config{$config} = 1;
+
+    return undef;
+}
+
+sub make_min_config {
+    my ($i) = @_;
+
+    if (!defined($output_minconfig)) {
+       fail "OUTPUT_MIN_CONFIG not defined" and return;
+    }
+    if (!defined($start_minconfig)) {
+       fail "START_MIN_CONFIG or MIN_CONFIG not defined" and return;
+    }
+
+    # First things first. We build an allnoconfig to find
+    # out what the defaults are that we can't touch.
+    # Some are selections, but we really can't handle selections.
+
+    my $save_minconfig = $minconfig;
+    undef $minconfig;
+
+    run_command "$make allnoconfig" or return 0;
+
+    read_depends;
+
+    process_config_ignore $output_config;
+
+    undef %keep_configs;
+    undef %min_configs;
+
+    if (defined($ignore_config)) {
+       # make sure the file exists
+       `touch $ignore_config`;
+       assign_configs \%keep_configs, $ignore_config;
+    }
+
+    doprint "Load initial configs from $start_minconfig\n";
+
+    # Look at the current min configs, and save off all the
+    # ones that were set via the allnoconfig
+    assign_configs \%min_configs, $start_minconfig;
+
+    my @config_keys = keys %min_configs;
+
+    # Remove anything that was set by the make allnoconfig
+    # we shouldn't need them as they get set for us anyway.
+    foreach my $config (@config_keys) {
+       # Remove anything in the ignore_config
+       if (defined($keep_configs{$config})) {
+           my $file = $ignore_config;
+           $file =~ s,.*/(.*?)$,$1,;
+           doprint "$config set by $file ... ignored\n";
+           delete $min_configs{$config};
+           next;
+       }
+       # But make sure the settings are the same. If a min config
+       # sets a selection, we do not want to get rid of it if
+       # it is not the same as what we have. Just move it into
+       # the keep configs.
+       if (defined($config_ignore{$config})) {
+           if ($config_ignore{$config} ne $min_configs{$config}) {
+               doprint "$config is in allnoconfig as '$config_ignore{$config}'";
+               doprint " but it is '$min_configs{$config}' in minconfig .. keeping\n";
+               $keep_configs{$config} = $min_configs{$config};
+           } else {
+               doprint "$config set by allnoconfig ... ignored\n";
+           }
+           delete $min_configs{$config};
+       }
+    }
+
+    my $done = 0;
+    my $take_two = 0;
+
+    while (!$done) {
+
+       my $config;
+       my $found;
+
+       # Now disable each config one by one and do a make oldconfig
+       # till we find a config that changes our list.
+
+       # Put configs that did not modify the config at the end.
+       my @test_configs = keys %min_configs;
+       my $reset = 1;
+       for (my $i = 0; $i < $#test_configs; $i++) {
+           if (!defined($nochange_config{$test_configs[0]})) {
+               $reset = 0;
+               last;
+           }
+           # This config didn't change the .config last time.
+           # Place it at the end
+           my $config = shift @test_configs;
+           push @test_configs, $config;
+       }
+
+       # if every test config has failed to modify the .config file
+       # in the past, then reset and start over.
+       if ($reset) {
+           undef %nochange_config;
+       }
+
+       undef %processed_configs;
+
+       foreach my $config (@test_configs) {
+
+           $found = test_this_config $config;
+
+           last if (defined($found));
+
+           # oh well, try another config
+       }
+
+       if (!defined($found)) {
+           # we could have failed due to the nochange_config hash
+           # reset and try again
+           if (!$take_two) {
+               undef %nochange_config;
+               $take_two = 1;
+               next;
+           }
+           doprint "No more configs found that we can disable\n";
+           $done = 1;
+           last;
+       }
+       $take_two = 0;
+
+       $config = $found;
+
+       doprint "Test with $config disabled\n";
+
+       # set in_bisect to keep build and monitor from dieing
+       $in_bisect = 1;
+
+       my $failed = 0;
+       build "oldconfig";
+       start_monitor_and_boot or $failed = 1;
+       end_monitor;
+
+       $in_bisect = 0;
+
+       if ($failed) {
+           doprint "$min_configs{$config} is needed to boot the box... keeping\n";
+           # this config is needed, add it to the ignore list.
+           $keep_configs{$config} = $min_configs{$config};
+           delete $min_configs{$config};
+       } else {
+           # We booted without this config, remove it from the minconfigs.
+           doprint "$config is not needed, disabling\n";
+
+           delete $min_configs{$config};
+
+           # Also disable anything that is not enabled in this config
+           my %configs;
+           assign_configs \%configs, $output_config;
+           my @config_keys = keys %min_configs;
+           foreach my $config (@config_keys) {
+               if (!defined($configs{$config})) {
+                   doprint "$config is not set, disabling\n";
+                   delete $min_configs{$config};
+               }
+           }
+
+           # Save off all the current mandidory configs
+           open (OUT, ">$output_minconfig")
+               or die "Can't write to $output_minconfig";
+           foreach my $config (keys %keep_configs) {
+               print OUT "$keep_configs{$config}\n";
+           }
+           foreach my $config (keys %min_configs) {
+               print OUT "$min_configs{$config}\n";
+           }
+           close OUT;
+       }
+
+       doprint "Reboot and wait $sleep_time seconds\n";
+       reboot;
+       start_monitor;
+       wait_for_monitor $sleep_time;
+       end_monitor;
+    }
+
+    success $i;
+    return 1;
+}
+
 $#ARGV < 1 or die "ktest.pl version: $VERSION\n   usage: ktest.pl config-file\n";
 
 if ($#ARGV == 0) {
@@ -2273,6 +2766,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     $reboot = set_test_option("REBOOT", $i);
     $noclean = set_test_option("BUILD_NOCLEAN", $i);
     $minconfig = set_test_option("MIN_CONFIG", $i);
+    $output_minconfig = set_test_option("OUTPUT_MIN_CONFIG", $i);
+    $start_minconfig = set_test_option("START_MIN_CONFIG", $i);
+    $ignore_config = set_test_option("IGNORE_CONFIG", $i);
     $run_test = set_test_option("TEST", $i);
     $addconfig = set_test_option("ADD_CONFIG", $i);
     $reboot_type = set_test_option("REBOOT_TYPE", $i);
@@ -2288,6 +2784,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     $sleep_time = set_test_option("SLEEP_TIME", $i);
     $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i);
     $patchcheck_sleep_time = set_test_option("PATCHCHECK_SLEEP_TIME", $i);
+    $ignore_warnings = set_test_option("IGNORE_WARNINGS", $i);
     $bisect_manual = set_test_option("BISECT_MANUAL", $i);
     $bisect_skip = set_test_option("BISECT_SKIP", $i);
     $config_bisect_good = set_test_option("CONFIG_BISECT_GOOD", $i);
@@ -2307,6 +2804,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     $target_image = set_test_option("TARGET_IMAGE", $i);
     $localversion = set_test_option("LOCALVERSION", $i);
 
+    if (!defined($start_minconfig)) {
+       $start_minconfig = $minconfig;
+    }
+
     chdir $builddir || die "can't change directory to $builddir";
 
     if (!-d $tmpdir) {
@@ -2339,6 +2840,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
        $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"};
     }
 
+    if ($test_type eq "make_min_config") {
+       $run_type = "";
+    }
+
     # mistake in config file?
     if (!defined($run_type)) {
        $run_type = "ERROR";
@@ -2374,6 +2879,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     } elsif ($test_type eq "patchcheck") {
        patchcheck $i;
        next;
+    } elsif ($test_type eq "make_min_config") {
+       make_min_config $i;
+       next;
     }
 
     if ($build_type ne "nobuild") {
@@ -2381,13 +2889,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
     }
 
     if ($test_type ne "build") {
-       get_grub_index;
-       get_version;
-       install;
-
        my $failed = 0;
-       start_monitor;
-       monitor or $failed = 1;;
+       start_monitor_and_boot or $failed = 1;
 
        if (!$failed && $test_type ne "boot" && defined($run_test)) {
            do_run_test or $failed = 1;