162306a36Sopenharmony_ci#!/usr/bin/perl -w 262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only 362306a36Sopenharmony_ci# 462306a36Sopenharmony_ci# Copyright 2015 - Steven Rostedt, Red Hat Inc. 562306a36Sopenharmony_ci# Copyright 2017 - Steven Rostedt, VMware, Inc. 662306a36Sopenharmony_ci# 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci# usage: 962306a36Sopenharmony_ci# config-bisect.pl [options] good-config bad-config [good|bad] 1062306a36Sopenharmony_ci# 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci# Compares a good config to a bad config, then takes half of the diffs 1362306a36Sopenharmony_ci# and produces a config that is somewhere between the good config and 1462306a36Sopenharmony_ci# the bad config. That is, the resulting config will start with the 1562306a36Sopenharmony_ci# good config and will try to make half of the differences of between 1662306a36Sopenharmony_ci# the good and bad configs match the bad config. It tries because of 1762306a36Sopenharmony_ci# dependencies between the two configs it may not be able to change 1862306a36Sopenharmony_ci# exactly half of the configs that are different between the two config 1962306a36Sopenharmony_ci# files. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci# Here's a normal way to use it: 2262306a36Sopenharmony_ci# 2362306a36Sopenharmony_ci# $ cd /path/to/linux/kernel 2462306a36Sopenharmony_ci# $ config-bisect.pl /path/to/good/config /path/to/bad/config 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci# This will now pull in good config (blowing away .config in that directory 2762306a36Sopenharmony_ci# so do not make that be one of the good or bad configs), and then 2862306a36Sopenharmony_ci# build the config with "make oldconfig" to make sure it matches the 2962306a36Sopenharmony_ci# current kernel. It will then store the configs in that result for 3062306a36Sopenharmony_ci# the good config. It does the same for the bad config as well. 3162306a36Sopenharmony_ci# The algorithm will run, merging half of the differences between 3262306a36Sopenharmony_ci# the two configs and building them with "make oldconfig" to make sure 3362306a36Sopenharmony_ci# the result changes (dependencies may reset changes the tool had made). 3462306a36Sopenharmony_ci# It then copies the result of its good config to /path/to/good/config.tmp 3562306a36Sopenharmony_ci# and the bad config to /path/to/bad/config.tmp (just appends ".tmp" to the 3662306a36Sopenharmony_ci# files passed in). And the ".config" that you should test will be in 3762306a36Sopenharmony_ci# directory 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci# After the first run, determine if the result is good or bad then 4062306a36Sopenharmony_ci# run the same command appending the result 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci# For good results: 4362306a36Sopenharmony_ci# $ config-bisect.pl /path/to/good/config /path/to/bad/config good 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci# For bad results: 4662306a36Sopenharmony_ci# $ config-bisect.pl /path/to/good/config /path/to/bad/config bad 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci# Do not change the good-config or bad-config, config-bisect.pl will 4962306a36Sopenharmony_ci# copy the good-config to a temp file with the same name as good-config 5062306a36Sopenharmony_ci# but with a ".tmp" after it. It will do the same with the bad-config. 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci# If "good" or "bad" is not stated at the end, it will copy the good and 5362306a36Sopenharmony_ci# bad configs to the .tmp versions. If a .tmp version already exists, it will 5462306a36Sopenharmony_ci# warn before writing over them (-r will not warn, and just write over them). 5562306a36Sopenharmony_ci# If the last config is labeled "good", then it will copy it to the good .tmp 5662306a36Sopenharmony_ci# version. If the last config is labeled "bad", it will copy it to the bad 5762306a36Sopenharmony_ci# .tmp version. It will continue this until it can not merge the two any more 5862306a36Sopenharmony_ci# without the result being equal to either the good or bad .tmp configs. 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cimy $start = 0; 6162306a36Sopenharmony_cimy $val = ""; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cimy $pwd = `pwd`; 6462306a36Sopenharmony_cichomp $pwd; 6562306a36Sopenharmony_cimy $tree = $pwd; 6662306a36Sopenharmony_cimy $build; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cimy $output_config; 6962306a36Sopenharmony_cimy $reset_bisect; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cisub usage { 7262306a36Sopenharmony_ci print << "EOF" 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciusage: config-bisect.pl [-l linux-tree][-b build-dir] good-config bad-config [good|bad] 7562306a36Sopenharmony_ci -l [optional] define location of linux-tree (default is current directory) 7662306a36Sopenharmony_ci -b [optional] define location to build (O=build-dir) (default is linux-tree) 7762306a36Sopenharmony_ci good-config the config that is considered good 7862306a36Sopenharmony_ci bad-config the config that does not work 7962306a36Sopenharmony_ci "good" add this if the last run produced a good config 8062306a36Sopenharmony_ci "bad" add this if the last run produced a bad config 8162306a36Sopenharmony_ci If "good" or "bad" is not specified, then it is the start of a new bisect 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci Note, each run will create copy of good and bad configs with ".tmp" appended. 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciEOF 8662306a36Sopenharmony_ci; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci exit(-1); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cisub doprint { 9262306a36Sopenharmony_ci print @_; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cisub dodie { 9662306a36Sopenharmony_ci doprint "CRITICAL FAILURE... ", @_, "\n"; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci die @_, "\n"; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cisub expand_path { 10262306a36Sopenharmony_ci my ($file) = @_; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if ($file =~ m,^/,) { 10562306a36Sopenharmony_ci return $file; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci return "$pwd/$file"; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cisub read_prompt { 11162306a36Sopenharmony_ci my ($cancel, $prompt) = @_; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci my $ans; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for (;;) { 11662306a36Sopenharmony_ci if ($cancel) { 11762306a36Sopenharmony_ci print "$prompt [y/n/C] "; 11862306a36Sopenharmony_ci } else { 11962306a36Sopenharmony_ci print "$prompt [y/N] "; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci $ans = <STDIN>; 12262306a36Sopenharmony_ci chomp $ans; 12362306a36Sopenharmony_ci if ($ans =~ /^\s*$/) { 12462306a36Sopenharmony_ci if ($cancel) { 12562306a36Sopenharmony_ci $ans = "c"; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci $ans = "n"; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci last if ($ans =~ /^y$/i || $ans =~ /^n$/i); 13162306a36Sopenharmony_ci if ($cancel) { 13262306a36Sopenharmony_ci last if ($ans =~ /^c$/i); 13362306a36Sopenharmony_ci print "Please answer either 'y', 'n' or 'c'.\n"; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci print "Please answer either 'y' or 'n'.\n"; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci if ($ans =~ /^c/i) { 13962306a36Sopenharmony_ci exit; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci if ($ans !~ /^y$/i) { 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci return 1; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cisub read_yn { 14862306a36Sopenharmony_ci my ($prompt) = @_; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return read_prompt 0, $prompt; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cisub read_ync { 15462306a36Sopenharmony_ci my ($prompt) = @_; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return read_prompt 1, $prompt; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cisub run_command { 16062306a36Sopenharmony_ci my ($command, $redirect) = @_; 16162306a36Sopenharmony_ci my $start_time; 16262306a36Sopenharmony_ci my $end_time; 16362306a36Sopenharmony_ci my $dord = 0; 16462306a36Sopenharmony_ci my $pid; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci $start_time = time; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci doprint("$command ... "); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci $pid = open(CMD, "$command 2>&1 |") or 17162306a36Sopenharmony_ci dodie "unable to exec $command"; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (defined($redirect)) { 17462306a36Sopenharmony_ci open (RD, ">$redirect") or 17562306a36Sopenharmony_ci dodie "failed to write to redirect $redirect"; 17662306a36Sopenharmony_ci $dord = 1; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci while (<CMD>) { 18062306a36Sopenharmony_ci print RD if ($dord); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci waitpid($pid, 0); 18462306a36Sopenharmony_ci my $failed = $?; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci close(CMD); 18762306a36Sopenharmony_ci close(RD) if ($dord); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci $end_time = time; 19062306a36Sopenharmony_ci my $delta = $end_time - $start_time; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if ($delta == 1) { 19362306a36Sopenharmony_ci doprint "[1 second] "; 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci doprint "[$delta seconds] "; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if ($failed) { 19962306a36Sopenharmony_ci doprint "FAILED!\n"; 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci doprint "SUCCESS\n"; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return !$failed; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci###### CONFIG BISECT ###### 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci# config_ignore holds the configs that were set (or unset) for 21062306a36Sopenharmony_ci# a good config and we will ignore these configs for the rest 21162306a36Sopenharmony_ci# of a config bisect. These configs stay as they were. 21262306a36Sopenharmony_cimy %config_ignore; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci# config_set holds what all configs were set as. 21562306a36Sopenharmony_cimy %config_set; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci# config_off holds the set of configs that the bad config had disabled. 21862306a36Sopenharmony_ci# We need to record them and set them in the .config when running 21962306a36Sopenharmony_ci# olddefconfig, because olddefconfig keeps the defaults. 22062306a36Sopenharmony_cimy %config_off; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci# config_off_tmp holds a set of configs to turn off for now 22362306a36Sopenharmony_cimy @config_off_tmp; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci# config_list is the set of configs that are being tested 22662306a36Sopenharmony_cimy %config_list; 22762306a36Sopenharmony_cimy %null_config; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cimy %dependency; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cimy $make; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cisub make_oldconfig { 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!run_command "$make olddefconfig") { 23662306a36Sopenharmony_ci # Perhaps olddefconfig doesn't exist in this version of the kernel 23762306a36Sopenharmony_ci # try oldnoconfig 23862306a36Sopenharmony_ci doprint "olddefconfig failed, trying make oldnoconfig\n"; 23962306a36Sopenharmony_ci if (!run_command "$make oldnoconfig") { 24062306a36Sopenharmony_ci doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; 24162306a36Sopenharmony_ci # try a yes '' | oldconfig 24262306a36Sopenharmony_ci run_command "yes '' | $make oldconfig" or 24362306a36Sopenharmony_ci dodie "failed make config oldconfig"; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cisub assign_configs { 24962306a36Sopenharmony_ci my ($hash, $config) = @_; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci doprint "Reading configs from $config\n"; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci open (IN, $config) 25462306a36Sopenharmony_ci or dodie "Failed to read $config"; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci while (<IN>) { 25762306a36Sopenharmony_ci chomp; 25862306a36Sopenharmony_ci if (/^((CONFIG\S*)=.*)/) { 25962306a36Sopenharmony_ci ${$hash}{$2} = $1; 26062306a36Sopenharmony_ci } elsif (/^(# (CONFIG\S*) is not set)/) { 26162306a36Sopenharmony_ci ${$hash}{$2} = $1; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci close(IN); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cisub process_config_ignore { 26962306a36Sopenharmony_ci my ($config) = @_; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci assign_configs \%config_ignore, $config; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cisub get_dependencies { 27562306a36Sopenharmony_ci my ($config) = @_; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci my $arr = $dependency{$config}; 27862306a36Sopenharmony_ci if (!defined($arr)) { 27962306a36Sopenharmony_ci return (); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci my @deps = @{$arr}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci foreach my $dep (@{$arr}) { 28562306a36Sopenharmony_ci print "ADD DEP $dep\n"; 28662306a36Sopenharmony_ci @deps = (@deps, get_dependencies $dep); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return @deps; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cisub save_config { 29362306a36Sopenharmony_ci my ($pc, $file) = @_; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci my %configs = %{$pc}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci doprint "Saving configs into $file\n"; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci open(OUT, ">$file") or dodie "Can not write to $file"; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci foreach my $config (keys %configs) { 30262306a36Sopenharmony_ci print OUT "$configs{$config}\n"; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci close(OUT); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cisub create_config { 30862306a36Sopenharmony_ci my ($name, $pc) = @_; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci doprint "Creating old config from $name configs\n"; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci save_config $pc, $output_config; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci make_oldconfig; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci# compare two config hashes, and return configs with different vals. 31862306a36Sopenharmony_ci# It returns B's config values, but you can use A to see what A was. 31962306a36Sopenharmony_cisub diff_config_vals { 32062306a36Sopenharmony_ci my ($pa, $pb) = @_; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci # crappy Perl way to pass in hashes. 32362306a36Sopenharmony_ci my %a = %{$pa}; 32462306a36Sopenharmony_ci my %b = %{$pb}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci my %ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci foreach my $item (keys %a) { 32962306a36Sopenharmony_ci if (defined($b{$item}) && $b{$item} ne $a{$item}) { 33062306a36Sopenharmony_ci $ret{$item} = $b{$item}; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return %ret; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci# compare two config hashes and return the configs in B but not A 33862306a36Sopenharmony_cisub diff_configs { 33962306a36Sopenharmony_ci my ($pa, $pb) = @_; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci my %ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci # crappy Perl way to pass in hashes. 34462306a36Sopenharmony_ci my %a = %{$pa}; 34562306a36Sopenharmony_ci my %b = %{$pb}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci foreach my $item (keys %b) { 34862306a36Sopenharmony_ci if (!defined($a{$item})) { 34962306a36Sopenharmony_ci $ret{$item} = $b{$item}; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return %ret; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci# return if two configs are equal or not 35762306a36Sopenharmony_ci# 0 is equal +1 b has something a does not 35862306a36Sopenharmony_ci# +1 if a and b have a different item. 35962306a36Sopenharmony_ci# -1 if a has something b does not 36062306a36Sopenharmony_cisub compare_configs { 36162306a36Sopenharmony_ci my ($pa, $pb) = @_; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci my %ret; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci # crappy Perl way to pass in hashes. 36662306a36Sopenharmony_ci my %a = %{$pa}; 36762306a36Sopenharmony_ci my %b = %{$pb}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci foreach my $item (keys %b) { 37062306a36Sopenharmony_ci if (!defined($a{$item})) { 37162306a36Sopenharmony_ci return 1; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci if ($a{$item} ne $b{$item}) { 37462306a36Sopenharmony_ci return 1; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci foreach my $item (keys %a) { 37962306a36Sopenharmony_ci if (!defined($b{$item})) { 38062306a36Sopenharmony_ci return -1; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cisub process_failed { 38862306a36Sopenharmony_ci my ($config) = @_; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci doprint "\n\n***************************************\n"; 39162306a36Sopenharmony_ci doprint "Found bad config: $config\n"; 39262306a36Sopenharmony_ci doprint "***************************************\n\n"; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cisub process_new_config { 39662306a36Sopenharmony_ci my ($tc, $nc, $gc, $bc) = @_; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci my %tmp_config = %{$tc}; 39962306a36Sopenharmony_ci my %good_configs = %{$gc}; 40062306a36Sopenharmony_ci my %bad_configs = %{$bc}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci my %new_configs; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci my $runtest = 1; 40562306a36Sopenharmony_ci my $ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci create_config "tmp_configs", \%tmp_config; 40862306a36Sopenharmony_ci assign_configs \%new_configs, $output_config; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci $ret = compare_configs \%new_configs, \%bad_configs; 41162306a36Sopenharmony_ci if (!$ret) { 41262306a36Sopenharmony_ci doprint "New config equals bad config, try next test\n"; 41362306a36Sopenharmony_ci $runtest = 0; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if ($runtest) { 41762306a36Sopenharmony_ci $ret = compare_configs \%new_configs, \%good_configs; 41862306a36Sopenharmony_ci if (!$ret) { 41962306a36Sopenharmony_ci doprint "New config equals good config, try next test\n"; 42062306a36Sopenharmony_ci $runtest = 0; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci %{$nc} = %new_configs; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return $runtest; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cisub convert_config { 43062306a36Sopenharmony_ci my ($config) = @_; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if ($config =~ /^# (.*) is not set/) { 43362306a36Sopenharmony_ci $config = "$1=n"; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci $config =~ s/^CONFIG_//; 43762306a36Sopenharmony_ci return $config; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cisub print_config { 44162306a36Sopenharmony_ci my ($sym, $config) = @_; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci $config = convert_config $config; 44462306a36Sopenharmony_ci doprint "$sym$config\n"; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cisub print_config_compare { 44862306a36Sopenharmony_ci my ($good_config, $bad_config) = @_; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci $good_config = convert_config $good_config; 45162306a36Sopenharmony_ci $bad_config = convert_config $bad_config; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci my $good_value = $good_config; 45462306a36Sopenharmony_ci my $bad_value = $bad_config; 45562306a36Sopenharmony_ci $good_value =~ s/(.*)=//; 45662306a36Sopenharmony_ci my $config = $1; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci $bad_value =~ s/.*=//; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci doprint " $config $good_value -> $bad_value\n"; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci# Pass in: 46462306a36Sopenharmony_ci# $phalf: half of the configs names you want to add 46562306a36Sopenharmony_ci# $oconfigs: The orginial configs to start with 46662306a36Sopenharmony_ci# $sconfigs: The source to update $oconfigs with (from $phalf) 46762306a36Sopenharmony_ci# $which: The name of which half that is updating (top / bottom) 46862306a36Sopenharmony_ci# $type: The name of the source type (good / bad) 46962306a36Sopenharmony_cisub make_half { 47062306a36Sopenharmony_ci my ($phalf, $oconfigs, $sconfigs, $which, $type) = @_; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci my @half = @{$phalf}; 47362306a36Sopenharmony_ci my %orig_configs = %{$oconfigs}; 47462306a36Sopenharmony_ci my %source_configs = %{$sconfigs}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci my %tmp_config = %orig_configs; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci doprint "Settings bisect with $which half of $type configs:\n"; 47962306a36Sopenharmony_ci foreach my $item (@half) { 48062306a36Sopenharmony_ci doprint "Updating $item to $source_configs{$item}\n"; 48162306a36Sopenharmony_ci $tmp_config{$item} = $source_configs{$item}; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return %tmp_config; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cisub run_config_bisect { 48862306a36Sopenharmony_ci my ($pgood, $pbad) = @_; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci my %good_configs = %{$pgood}; 49162306a36Sopenharmony_ci my %bad_configs = %{$pbad}; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci my %diff_configs = diff_config_vals \%good_configs, \%bad_configs; 49462306a36Sopenharmony_ci my %b_configs = diff_configs \%good_configs, \%bad_configs; 49562306a36Sopenharmony_ci my %g_configs = diff_configs \%bad_configs, \%good_configs; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci # diff_arr is what is in both good and bad but are different (y->n) 49862306a36Sopenharmony_ci my @diff_arr = keys %diff_configs; 49962306a36Sopenharmony_ci my $len_diff = $#diff_arr + 1; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci # b_arr is what is in bad but not in good (has depends) 50262306a36Sopenharmony_ci my @b_arr = keys %b_configs; 50362306a36Sopenharmony_ci my $len_b = $#b_arr + 1; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci # g_arr is what is in good but not in bad 50662306a36Sopenharmony_ci my @g_arr = keys %g_configs; 50762306a36Sopenharmony_ci my $len_g = $#g_arr + 1; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci my $runtest = 0; 51062306a36Sopenharmony_ci my %new_configs; 51162306a36Sopenharmony_ci my $ret; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci # Look at the configs that are different between good and bad. 51462306a36Sopenharmony_ci # This does not include those that depend on other configs 51562306a36Sopenharmony_ci # (configs depending on other configs that are not set would 51662306a36Sopenharmony_ci # not show up even as a "# CONFIG_FOO is not set" 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci doprint "# of configs to check: $len_diff\n"; 52062306a36Sopenharmony_ci doprint "# of configs showing only in good: $len_g\n"; 52162306a36Sopenharmony_ci doprint "# of configs showing only in bad: $len_b\n"; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if ($len_diff > 0) { 52462306a36Sopenharmony_ci # Now test for different values 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci doprint "Configs left to check:\n"; 52762306a36Sopenharmony_ci doprint " Good Config\t\t\tBad Config\n"; 52862306a36Sopenharmony_ci doprint " -----------\t\t\t----------\n"; 52962306a36Sopenharmony_ci foreach my $item (@diff_arr) { 53062306a36Sopenharmony_ci doprint " $good_configs{$item}\t$bad_configs{$item}\n"; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci my $half = int($#diff_arr / 2); 53462306a36Sopenharmony_ci my @tophalf = @diff_arr[0 .. $half]; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci doprint "Set tmp config to be good config with some bad config values\n"; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci my %tmp_config = make_half \@tophalf, \%good_configs, 53962306a36Sopenharmony_ci \%bad_configs, "top", "bad"; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci $runtest = process_new_config \%tmp_config, \%new_configs, 54262306a36Sopenharmony_ci \%good_configs, \%bad_configs; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!$runtest) { 54562306a36Sopenharmony_ci doprint "Set tmp config to be bad config with some good config values\n"; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci my %tmp_config = make_half \@tophalf, \%bad_configs, 54862306a36Sopenharmony_ci \%good_configs, "top", "good"; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci $runtest = process_new_config \%tmp_config, \%new_configs, 55162306a36Sopenharmony_ci \%good_configs, \%bad_configs; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!$runtest && $len_diff > 0) { 55662306a36Sopenharmony_ci # do the same thing, but this time with bottom half 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci my $half = int($#diff_arr / 2); 55962306a36Sopenharmony_ci my @bottomhalf = @diff_arr[$half+1 .. $#diff_arr]; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci doprint "Set tmp config to be good config with some bad config values\n"; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci my %tmp_config = make_half \@bottomhalf, \%good_configs, 56462306a36Sopenharmony_ci \%bad_configs, "bottom", "bad"; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci $runtest = process_new_config \%tmp_config, \%new_configs, 56762306a36Sopenharmony_ci \%good_configs, \%bad_configs; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (!$runtest) { 57062306a36Sopenharmony_ci doprint "Set tmp config to be bad config with some good config values\n"; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci my %tmp_config = make_half \@bottomhalf, \%bad_configs, 57362306a36Sopenharmony_ci \%good_configs, "bottom", "good"; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci $runtest = process_new_config \%tmp_config, \%new_configs, 57662306a36Sopenharmony_ci \%good_configs, \%bad_configs; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if ($runtest) { 58162306a36Sopenharmony_ci make_oldconfig; 58262306a36Sopenharmony_ci doprint "READY TO TEST .config IN $build\n"; 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci doprint "\n%%%%%%%% FAILED TO FIND SINGLE BAD CONFIG %%%%%%%%\n"; 58762306a36Sopenharmony_ci doprint "Hmm, can't make any more changes without making good == bad?\n"; 58862306a36Sopenharmony_ci doprint "Difference between good (+) and bad (-)\n"; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci foreach my $item (keys %bad_configs) { 59162306a36Sopenharmony_ci if (!defined($good_configs{$item})) { 59262306a36Sopenharmony_ci print_config "-", $bad_configs{$item}; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci foreach my $item (keys %good_configs) { 59762306a36Sopenharmony_ci next if (!defined($bad_configs{$item})); 59862306a36Sopenharmony_ci if ($good_configs{$item} ne $bad_configs{$item}) { 59962306a36Sopenharmony_ci print_config_compare $good_configs{$item}, $bad_configs{$item}; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci foreach my $item (keys %good_configs) { 60462306a36Sopenharmony_ci if (!defined($bad_configs{$item})) { 60562306a36Sopenharmony_ci print_config "+", $good_configs{$item}; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci return -1; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cisub config_bisect { 61262306a36Sopenharmony_ci my ($good_config, $bad_config) = @_; 61362306a36Sopenharmony_ci my $ret; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci my %good_configs; 61662306a36Sopenharmony_ci my %bad_configs; 61762306a36Sopenharmony_ci my %tmp_configs; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci doprint "Run good configs through make oldconfig\n"; 62062306a36Sopenharmony_ci assign_configs \%tmp_configs, $good_config; 62162306a36Sopenharmony_ci create_config "$good_config", \%tmp_configs; 62262306a36Sopenharmony_ci assign_configs \%good_configs, $output_config; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci doprint "Run bad configs through make oldconfig\n"; 62562306a36Sopenharmony_ci assign_configs \%tmp_configs, $bad_config; 62662306a36Sopenharmony_ci create_config "$bad_config", \%tmp_configs; 62762306a36Sopenharmony_ci assign_configs \%bad_configs, $output_config; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci save_config \%good_configs, $good_config; 63062306a36Sopenharmony_ci save_config \%bad_configs, $bad_config; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return run_config_bisect \%good_configs, \%bad_configs; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ciwhile ($#ARGV >= 0) { 63662306a36Sopenharmony_ci if ($ARGV[0] !~ m/^-/) { 63762306a36Sopenharmony_ci last; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci my $opt = shift @ARGV; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if ($opt eq "-b") { 64262306a36Sopenharmony_ci $val = shift @ARGV; 64362306a36Sopenharmony_ci if (!defined($val)) { 64462306a36Sopenharmony_ci die "-b requires value\n"; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci $build = $val; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci elsif ($opt eq "-l") { 65062306a36Sopenharmony_ci $val = shift @ARGV; 65162306a36Sopenharmony_ci if (!defined($val)) { 65262306a36Sopenharmony_ci die "-l requires value\n"; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci $tree = $val; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci elsif ($opt eq "-r") { 65862306a36Sopenharmony_ci $reset_bisect = 1; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci elsif ($opt eq "-h") { 66262306a36Sopenharmony_ci usage; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci else { 66662306a36Sopenharmony_ci die "Unknown option $opt\n"; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci$build = $tree if (!defined($build)); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci$tree = expand_path $tree; 67362306a36Sopenharmony_ci$build = expand_path $build; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ciif ( ! -d $tree ) { 67662306a36Sopenharmony_ci die "$tree not a directory\n"; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ciif ( ! -d $build ) { 68062306a36Sopenharmony_ci die "$build not a directory\n"; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ciusage if $#ARGV < 1; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ciif ($#ARGV == 1) { 68662306a36Sopenharmony_ci $start = 1; 68762306a36Sopenharmony_ci} elsif ($#ARGV == 2) { 68862306a36Sopenharmony_ci $val = $ARGV[2]; 68962306a36Sopenharmony_ci if ($val ne "good" && $val ne "bad") { 69062306a36Sopenharmony_ci die "Unknown command '$val', bust be either \"good\" or \"bad\"\n"; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci} else { 69362306a36Sopenharmony_ci usage; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cimy $good_start = expand_path $ARGV[0]; 69762306a36Sopenharmony_cimy $bad_start = expand_path $ARGV[1]; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cimy $good = "$good_start.tmp"; 70062306a36Sopenharmony_cimy $bad = "$bad_start.tmp"; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci$make = "make"; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciif ($build ne $tree) { 70562306a36Sopenharmony_ci $make = "make O=$build" 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci$output_config = "$build/.config"; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciif ($start) { 71162306a36Sopenharmony_ci if ( ! -f $good_start ) { 71262306a36Sopenharmony_ci die "$good_start not found\n"; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci if ( ! -f $bad_start ) { 71562306a36Sopenharmony_ci die "$bad_start not found\n"; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci if ( -f $good || -f $bad ) { 71862306a36Sopenharmony_ci my $p = ""; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if ( -f $good ) { 72162306a36Sopenharmony_ci $p = "$good exists\n"; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if ( -f $bad ) { 72562306a36Sopenharmony_ci $p = "$p$bad exists\n"; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (!defined($reset_bisect)) { 72962306a36Sopenharmony_ci if (!read_yn "${p}Overwrite and start new bisect anyway?") { 73062306a36Sopenharmony_ci exit (-1); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci run_command "cp $good_start $good" or die "failed to copy to $good\n"; 73562306a36Sopenharmony_ci run_command "cp $bad_start $bad" or die "failed to copy to $bad\n"; 73662306a36Sopenharmony_ci} else { 73762306a36Sopenharmony_ci if ( ! -f $good ) { 73862306a36Sopenharmony_ci die "Can not find file $good\n"; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci if ( ! -f $bad ) { 74162306a36Sopenharmony_ci die "Can not find file $bad\n"; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci if ($val eq "good") { 74462306a36Sopenharmony_ci run_command "cp $output_config $good" or die "failed to copy $config to $good\n"; 74562306a36Sopenharmony_ci } elsif ($val eq "bad") { 74662306a36Sopenharmony_ci run_command "cp $output_config $bad" or die "failed to copy $config to $bad\n"; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cichdir $tree || die "can't change directory to $tree"; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cimy $ret = config_bisect $good, $bad; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ciif (!$ret) { 75562306a36Sopenharmony_ci exit(0); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ciif ($ret > 0) { 75962306a36Sopenharmony_ci doprint "Cleaning temp files\n"; 76062306a36Sopenharmony_ci run_command "rm $good"; 76162306a36Sopenharmony_ci run_command "rm $bad"; 76262306a36Sopenharmony_ci exit(1); 76362306a36Sopenharmony_ci} else { 76462306a36Sopenharmony_ci doprint "See good and bad configs for details:\n"; 76562306a36Sopenharmony_ci doprint "good: $good\n"; 76662306a36Sopenharmony_ci doprint "bad: $bad\n"; 76762306a36Sopenharmony_ci doprint "%%%%%%%% FAILED TO FIND SINGLE BAD CONFIG %%%%%%%%\n"; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ciexit(2); 770