ktest: Use Kconfig dependencies to shorten time to make min_config
[platform/adaptation/renesas_rcar/renesas_kernel.git] / tools / testing / ktest / ktest.pl
index 6166f3a..a9f2e10 100755 (executable)
@@ -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 = "";
@@ -1189,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
@@ -1678,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) = @_;
 
@@ -2149,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) {
@@ -2294,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);
@@ -2329,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) {
@@ -2361,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";
@@ -2396,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") {