forked from Minki/linux
ktest: Rewrite the config-bisect to actually work
I never liked the way config-bisect worked. I would assume the bad config had some config that broke the system. But it would not work if the bad config just happened to be missing something that the good config had. I rewrote the config-bisect to do this properly. It does a diff of the two configs, and sets half of the configs that are in one and not the other. The way it works is that when it "sets", it really just makes one copy what the other has. That is, a "set" can be setting a: # CONFIG_FOO is not set Basically, it looks at the differences between the two files and makes them similar until it comes down to one config that makes it work or not work depending on if it is set or not. Note, if more than one config change makes the bad config not work, it will only find one of them. But this is true with all bisect logic. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
parent
5269faad27
commit
6071c22e17
@ -2784,12 +2784,17 @@ my %dependency;
|
||||
sub assign_configs {
|
||||
my ($hash, $config) = @_;
|
||||
|
||||
doprint "Reading configs from $config\n";
|
||||
|
||||
open (IN, $config)
|
||||
or dodie "Failed to read $config";
|
||||
|
||||
while (<IN>) {
|
||||
chomp;
|
||||
if (/^((CONFIG\S*)=.*)/) {
|
||||
${$hash}{$2} = $1;
|
||||
} elsif (/^(# (CONFIG\S*) is not set)/) {
|
||||
${$hash}{$2} = $1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2841,54 +2846,98 @@ sub get_dependencies {
|
||||
return @deps;
|
||||
}
|
||||
|
||||
sub create_config {
|
||||
my @configs = @_;
|
||||
sub save_config {
|
||||
my ($pc, $file) = @_;
|
||||
|
||||
open(OUT, ">$output_config") or dodie "Can not write to $output_config";
|
||||
my %configs = %{$pc};
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
doprint "Saving configs into $file\n";
|
||||
|
||||
# turn off configs to keep off
|
||||
foreach my $config (keys %config_off) {
|
||||
print OUT "# $config is not set\n";
|
||||
}
|
||||
open(OUT, ">$file") or dodie "Can not write to $file";
|
||||
|
||||
# turn off configs that should be off for now
|
||||
foreach my $config (@config_off_tmp) {
|
||||
print OUT "# $config is not set\n";
|
||||
}
|
||||
|
||||
foreach my $config (keys %config_ignore) {
|
||||
print OUT "$config_ignore{$config}\n";
|
||||
foreach my $config (keys %configs) {
|
||||
print OUT "$configs{$config}\n";
|
||||
}
|
||||
close(OUT);
|
||||
}
|
||||
|
||||
sub create_config {
|
||||
my ($name, $pc) = @_;
|
||||
|
||||
doprint "Creating old config from $name configs\n";
|
||||
|
||||
save_config $pc, $output_config;
|
||||
|
||||
make_oldconfig;
|
||||
}
|
||||
|
||||
# compare two config hashes, and return configs with different vals.
|
||||
# It returns B's config values, but you can use A to see what A was.
|
||||
sub diff_config_vals {
|
||||
my ($pa, $pb) = @_;
|
||||
|
||||
# crappy Perl way to pass in hashes.
|
||||
my %a = %{$pa};
|
||||
my %b = %{$pb};
|
||||
|
||||
my %ret;
|
||||
|
||||
foreach my $item (keys %a) {
|
||||
if (defined($b{$item}) && $b{$item} ne $a{$item}) {
|
||||
$ret{$item} = $b{$item};
|
||||
}
|
||||
}
|
||||
|
||||
return %ret;
|
||||
}
|
||||
|
||||
# compare two config hashes and return the configs in B but not A
|
||||
sub diff_configs {
|
||||
my ($pa, $pb) = @_;
|
||||
|
||||
my %ret;
|
||||
|
||||
# crappy Perl way to pass in hashes.
|
||||
my %a = %{$pa};
|
||||
my %b = %{$pb};
|
||||
|
||||
foreach my $item (keys %b) {
|
||||
if (!defined($a{$item})) {
|
||||
$ret{$item} = $b{$item};
|
||||
}
|
||||
}
|
||||
|
||||
return %ret;
|
||||
}
|
||||
|
||||
# return if two configs are equal or not
|
||||
# 0 is equal +1 b has something a does not
|
||||
# +1 if a and b have a different item.
|
||||
# -1 if a has something b does not
|
||||
sub compare_configs {
|
||||
my (%a, %b) = @_;
|
||||
my ($pa, $pb) = @_;
|
||||
|
||||
my %ret;
|
||||
|
||||
# crappy Perl way to pass in hashes.
|
||||
my %a = %{$pa};
|
||||
my %b = %{$pb};
|
||||
|
||||
foreach my $item (keys %b) {
|
||||
if (!defined($a{$item})) {
|
||||
return 1;
|
||||
}
|
||||
if ($a{$item} ne $b{$item}) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $item (keys %a) {
|
||||
if (!defined($b{$item})) {
|
||||
print "diff $item\n";
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
delete $b{$item};
|
||||
}
|
||||
|
||||
my @keys = keys %b;
|
||||
if ($#keys) {
|
||||
print "diff2 $keys[0]\n";
|
||||
}
|
||||
return -1 if ($#keys >= 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2923,253 +2972,204 @@ sub process_failed {
|
||||
doprint "***************************************\n\n";
|
||||
}
|
||||
|
||||
sub run_config_bisect {
|
||||
# used for config bisecting
|
||||
my $good_config;
|
||||
my $bad_config;
|
||||
|
||||
my @start_list = keys %config_list;
|
||||
sub process_new_config {
|
||||
my ($tc, $nc, $gc, $bc) = @_;
|
||||
|
||||
if ($#start_list < 0) {
|
||||
doprint "No more configs to test!!!\n";
|
||||
return -1;
|
||||
my %tmp_config = %{$tc};
|
||||
my %good_configs = %{$gc};
|
||||
my %bad_configs = %{$bc};
|
||||
|
||||
my %new_configs;
|
||||
|
||||
my $runtest = 1;
|
||||
my $ret;
|
||||
|
||||
create_config "tmp_configs", \%tmp_config;
|
||||
assign_configs \%new_configs, $output_config;
|
||||
|
||||
$ret = compare_configs \%new_configs, \%bad_configs;
|
||||
if (!$ret) {
|
||||
doprint "New config equals bad config, try next test\n";
|
||||
$runtest = 0;
|
||||
}
|
||||
|
||||
doprint "***** RUN TEST ***\n";
|
||||
if ($runtest) {
|
||||
$ret = compare_configs \%new_configs, \%good_configs;
|
||||
if (!$ret) {
|
||||
doprint "New config equals good config, try next test\n";
|
||||
$runtest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
%{$nc} = %new_configs;
|
||||
|
||||
return $runtest;
|
||||
}
|
||||
|
||||
sub run_config_bisect {
|
||||
my ($pgood, $pbad) = @_;
|
||||
|
||||
my $type = $config_bisect_type;
|
||||
|
||||
my %good_configs = %{$pgood};
|
||||
my %bad_configs = %{$pbad};
|
||||
|
||||
my %diff_configs = diff_config_vals \%good_configs, \%bad_configs;
|
||||
my %b_configs = diff_configs \%good_configs, \%bad_configs;
|
||||
my %g_configs = diff_configs \%bad_configs, \%good_configs;
|
||||
|
||||
my @diff_arr = keys %diff_configs;
|
||||
my $len_diff = $#diff_arr + 1;
|
||||
|
||||
my @b_arr = keys %b_configs;
|
||||
my $len_b = $#b_arr + 1;
|
||||
|
||||
my @g_arr = keys %g_configs;
|
||||
my $len_g = $#g_arr + 1;
|
||||
|
||||
my $runtest = 1;
|
||||
my %new_configs;
|
||||
my $ret;
|
||||
my %current_config;
|
||||
|
||||
my $count = $#start_list + 1;
|
||||
doprint " $count configs to test\n";
|
||||
# First, lets get it down to a single subset.
|
||||
# Is the problem with a difference in values?
|
||||
# Is the problem with a missing config?
|
||||
# Is the problem with a config that breaks things?
|
||||
|
||||
my $half = int($#start_list / 2);
|
||||
# Enable all of one set and see if we get a new bad
|
||||
# or good config.
|
||||
|
||||
do {
|
||||
my @tophalf = @start_list[0 .. $half];
|
||||
# first set the good config to the bad values.
|
||||
|
||||
# keep the bottom half off
|
||||
if ($half < $#start_list) {
|
||||
@config_off_tmp = @start_list[$half + 1 .. $#start_list];
|
||||
} else {
|
||||
@config_off_tmp = ();
|
||||
}
|
||||
doprint "d=$len_diff g=$len_g b=$len_b\n";
|
||||
|
||||
create_config @tophalf;
|
||||
read_current_config \%current_config;
|
||||
# first lets enable things in bad config that are enabled in good 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 ($len_diff > 0) {
|
||||
if ($len_b > 0 || $len_g > 0) {
|
||||
my %tmp_config = %bad_configs;
|
||||
|
||||
doprint "Set tmp config to be bad config with good config values\n";
|
||||
foreach my $item (@diff_arr) {
|
||||
$tmp_config{$item} = $good_configs{$item};
|
||||
}
|
||||
|
||||
$runtest = process_new_config \%tmp_config, \%new_configs,
|
||||
\%good_configs, \%bad_configs;
|
||||
}
|
||||
if (!$found) {
|
||||
# try the other half
|
||||
doprint "Top half produced no set configs, trying bottom half\n";
|
||||
}
|
||||
|
||||
# keep the top half off
|
||||
@config_off_tmp = @tophalf;
|
||||
@tophalf = @start_list[$half + 1 .. $#start_list];
|
||||
if (!$runtest && $len_diff > 0) {
|
||||
|
||||
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 ($bisect_manual) {
|
||||
$ret = answer_bisect;
|
||||
}
|
||||
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";
|
||||
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) {
|
||||
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];
|
||||
if ($len_diff == 1) {
|
||||
doprint "The bad config setting is: $diff_arr[0]\n";
|
||||
return 1;
|
||||
}
|
||||
my %tmp_config = %bad_configs;
|
||||
|
||||
# remove half the configs we are looking at and see if
|
||||
# they are good.
|
||||
$half = int($#start_list / 2);
|
||||
} while ($#start_list > 0);
|
||||
my $half = int($#diff_arr / 2);
|
||||
my @tophalf = @diff_arr[0 .. $half];
|
||||
|
||||
# we found a single config, try it again unless we are running manually
|
||||
doprint "Settings bisect with top half:\n";
|
||||
doprint "Set tmp config to be bad config with some good config values\n";
|
||||
foreach my $item (@tophalf) {
|
||||
$tmp_config{$item} = $good_configs{$item};
|
||||
}
|
||||
|
||||
if ($bisect_manual) {
|
||||
process_failed $start_list[0];
|
||||
return 1;
|
||||
$runtest = process_new_config \%tmp_config, \%new_configs,
|
||||
\%good_configs, \%bad_configs;
|
||||
|
||||
if (!$runtest) {
|
||||
my %tmp_config = %bad_configs;
|
||||
|
||||
doprint "Try bottom half\n";
|
||||
|
||||
my @bottomhalf = @diff_arr[$half+1 .. $#diff_arr];
|
||||
|
||||
foreach my $item (@bottomhalf) {
|
||||
$tmp_config{$item} = $good_configs{$item};
|
||||
}
|
||||
|
||||
$runtest = process_new_config \%tmp_config, \%new_configs,
|
||||
\%good_configs, \%bad_configs;
|
||||
}
|
||||
}
|
||||
|
||||
my @tophalf = @start_list[0 .. 0];
|
||||
|
||||
$ret = run_config_bisect_test $type;
|
||||
if ($ret) {
|
||||
process_passed %current_config;
|
||||
if ($runtest) {
|
||||
$ret = run_config_bisect_test $type;
|
||||
if ($ret) {
|
||||
doprint "NEW GOOD CONFIG\n";
|
||||
%good_configs = %new_configs;
|
||||
run_command "mv $good_config ${good_config}.last";
|
||||
save_config \%good_configs, $good_config;
|
||||
%{$pgood} = %good_configs;
|
||||
} else {
|
||||
doprint "NEW BAD CONFIG\n";
|
||||
%bad_configs = %new_configs;
|
||||
run_command "mv $bad_config ${bad_config}.last";
|
||||
save_config \%bad_configs, $bad_config;
|
||||
%{$pbad} = %bad_configs;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
process_failed $start_list[0];
|
||||
return 1;
|
||||
fail "Hmm, need to do a mix match?\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
sub config_bisect {
|
||||
my ($i) = @_;
|
||||
|
||||
my $start_config = $config_bisect;
|
||||
my $type = $config_bisect_type;
|
||||
|
||||
my $tmpconfig = "$tmpdir/use_config";
|
||||
$bad_config = $config_bisect;
|
||||
|
||||
if (defined($config_bisect_good)) {
|
||||
process_config_ignore $config_bisect_good;
|
||||
}
|
||||
|
||||
# Make the file with the bad config and the min config
|
||||
if (defined($minconfig)) {
|
||||
# read the min config for things to ignore
|
||||
run_command "cp $minconfig $tmpconfig" or
|
||||
dodie "failed to copy $minconfig to $tmpconfig";
|
||||
$good_config = $config_bisect_good;
|
||||
} elsif (defined($minconfig)) {
|
||||
$good_config = $minconfig;
|
||||
} else {
|
||||
unlink $tmpconfig;
|
||||
}
|
||||
|
||||
if (-f $tmpconfig) {
|
||||
load_force_config($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 "failed to open $output_config";
|
||||
|
||||
while (<IN>) {
|
||||
if (/^((CONFIG\S*)=.*)/) {
|
||||
$config_check{$2} = $1;
|
||||
}
|
||||
}
|
||||
close(IN);
|
||||
|
||||
# Now run oldconfig with the minconfig
|
||||
make_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;
|
||||
}
|
||||
} elsif (/^# ((CONFIG\S*).*)/) {
|
||||
# Keep these configs disabled
|
||||
$config_set{$2} = $1;
|
||||
$config_off{$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";
|
||||
doprint "No config specified, checking if defconfig works";
|
||||
my $ret = run_bisect_test $type, "defconfig";
|
||||
if (!$ret) {
|
||||
fail "Have no good config to compare with, please set CONFIG_BISECT_GOOD";
|
||||
return 1;
|
||||
}
|
||||
$good_config = $output_config;
|
||||
}
|
||||
|
||||
my %config_test;
|
||||
my $once = 0;
|
||||
# we don't want min configs to cause issues here.
|
||||
doprint "Disabling 'MIN_CONFIG' for this test\n";
|
||||
undef $minconfig;
|
||||
|
||||
@config_off_tmp = ();
|
||||
my %good_configs;
|
||||
my %bad_configs;
|
||||
my %tmp_configs;
|
||||
|
||||
doprint "Run good configs through make oldconfig\n";
|
||||
assign_configs \%tmp_configs, $good_config;
|
||||
create_config "$good_config", \%tmp_configs;
|
||||
assign_configs \%good_configs, $output_config;
|
||||
|
||||
doprint "Run bad configs through make oldconfig\n";
|
||||
assign_configs \%tmp_configs, $bad_config;
|
||||
create_config "$bad_config", \%tmp_configs;
|
||||
assign_configs \%bad_configs, $output_config;
|
||||
|
||||
$good_config = "$tmpdir/good_config";
|
||||
$bad_config = "$tmpdir/bad_config";
|
||||
|
||||
save_config \%good_configs, $good_config;
|
||||
save_config \%bad_configs, $bad_config;
|
||||
|
||||
# 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;
|
||||
|
||||
if (defined($config_bisect_check) && $config_bisect_check) {
|
||||
doprint " Checking to make sure bad config with min config fails\n";
|
||||
create_config keys %config_list;
|
||||
$ret = run_config_bisect_test $config_bisect_type;
|
||||
if ($ret) {
|
||||
doprint " FAILED! Bad config with min config boots fine\n";
|
||||
return -1;
|
||||
}
|
||||
doprint " Bad config with min config fails as expected\n";
|
||||
}
|
||||
|
||||
do {
|
||||
$ret = run_config_bisect;
|
||||
$ret = run_config_bisect \%good_configs, \%bad_configs;
|
||||
} while (!$ret);
|
||||
|
||||
return $ret if ($ret < 0);
|
||||
|
Loading…
Reference in New Issue
Block a user