1*4882a593Smuzhiyun#!/usr/bin/perl -w 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# Copyright 2015 - Steven Rostedt, Red Hat Inc. 5*4882a593Smuzhiyun# Copyright 2017 - Steven Rostedt, VMware, Inc. 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun# usage: 9*4882a593Smuzhiyun# config-bisect.pl [options] good-config bad-config [good|bad] 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun 12*4882a593Smuzhiyun# Compares a good config to a bad config, then takes half of the diffs 13*4882a593Smuzhiyun# and produces a config that is somewhere between the good config and 14*4882a593Smuzhiyun# the bad config. That is, the resulting config will start with the 15*4882a593Smuzhiyun# good config and will try to make half of the differences of between 16*4882a593Smuzhiyun# the good and bad configs match the bad config. It tries because of 17*4882a593Smuzhiyun# dependencies between the two configs it may not be able to change 18*4882a593Smuzhiyun# exactly half of the configs that are different between the two config 19*4882a593Smuzhiyun# files. 20*4882a593Smuzhiyun 21*4882a593Smuzhiyun# Here's a normal way to use it: 22*4882a593Smuzhiyun# 23*4882a593Smuzhiyun# $ cd /path/to/linux/kernel 24*4882a593Smuzhiyun# $ config-bisect.pl /path/to/good/config /path/to/bad/config 25*4882a593Smuzhiyun 26*4882a593Smuzhiyun# This will now pull in good config (blowing away .config in that directory 27*4882a593Smuzhiyun# so do not make that be one of the good or bad configs), and then 28*4882a593Smuzhiyun# build the config with "make oldconfig" to make sure it matches the 29*4882a593Smuzhiyun# current kernel. It will then store the configs in that result for 30*4882a593Smuzhiyun# the good config. It does the same for the bad config as well. 31*4882a593Smuzhiyun# The algorithm will run, merging half of the differences between 32*4882a593Smuzhiyun# the two configs and building them with "make oldconfig" to make sure 33*4882a593Smuzhiyun# the result changes (dependencies may reset changes the tool had made). 34*4882a593Smuzhiyun# It then copies the result of its good config to /path/to/good/config.tmp 35*4882a593Smuzhiyun# and the bad config to /path/to/bad/config.tmp (just appends ".tmp" to the 36*4882a593Smuzhiyun# files passed in). And the ".config" that you should test will be in 37*4882a593Smuzhiyun# directory 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun# After the first run, determine if the result is good or bad then 40*4882a593Smuzhiyun# run the same command appending the result 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun# For good results: 43*4882a593Smuzhiyun# $ config-bisect.pl /path/to/good/config /path/to/bad/config good 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun# For bad results: 46*4882a593Smuzhiyun# $ config-bisect.pl /path/to/good/config /path/to/bad/config bad 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun# Do not change the good-config or bad-config, config-bisect.pl will 49*4882a593Smuzhiyun# copy the good-config to a temp file with the same name as good-config 50*4882a593Smuzhiyun# but with a ".tmp" after it. It will do the same with the bad-config. 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun# If "good" or "bad" is not stated at the end, it will copy the good and 53*4882a593Smuzhiyun# bad configs to the .tmp versions. If a .tmp version already exists, it will 54*4882a593Smuzhiyun# warn before writing over them (-r will not warn, and just write over them). 55*4882a593Smuzhiyun# If the last config is labeled "good", then it will copy it to the good .tmp 56*4882a593Smuzhiyun# version. If the last config is labeled "bad", it will copy it to the bad 57*4882a593Smuzhiyun# .tmp version. It will continue this until it can not merge the two any more 58*4882a593Smuzhiyun# without the result being equal to either the good or bad .tmp configs. 59*4882a593Smuzhiyun 60*4882a593Smuzhiyunmy $start = 0; 61*4882a593Smuzhiyunmy $val = ""; 62*4882a593Smuzhiyun 63*4882a593Smuzhiyunmy $pwd = `pwd`; 64*4882a593Smuzhiyunchomp $pwd; 65*4882a593Smuzhiyunmy $tree = $pwd; 66*4882a593Smuzhiyunmy $build; 67*4882a593Smuzhiyun 68*4882a593Smuzhiyunmy $output_config; 69*4882a593Smuzhiyunmy $reset_bisect; 70*4882a593Smuzhiyun 71*4882a593Smuzhiyunsub usage { 72*4882a593Smuzhiyun print << "EOF" 73*4882a593Smuzhiyun 74*4882a593Smuzhiyunusage: config-bisect.pl [-l linux-tree][-b build-dir] good-config bad-config [good|bad] 75*4882a593Smuzhiyun -l [optional] define location of linux-tree (default is current directory) 76*4882a593Smuzhiyun -b [optional] define location to build (O=build-dir) (default is linux-tree) 77*4882a593Smuzhiyun good-config the config that is considered good 78*4882a593Smuzhiyun bad-config the config that does not work 79*4882a593Smuzhiyun "good" add this if the last run produced a good config 80*4882a593Smuzhiyun "bad" add this if the last run produced a bad config 81*4882a593Smuzhiyun If "good" or "bad" is not specified, then it is the start of a new bisect 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun Note, each run will create copy of good and bad configs with ".tmp" appended. 84*4882a593Smuzhiyun 85*4882a593SmuzhiyunEOF 86*4882a593Smuzhiyun; 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun exit(-1); 89*4882a593Smuzhiyun} 90*4882a593Smuzhiyun 91*4882a593Smuzhiyunsub doprint { 92*4882a593Smuzhiyun print @_; 93*4882a593Smuzhiyun} 94*4882a593Smuzhiyun 95*4882a593Smuzhiyunsub dodie { 96*4882a593Smuzhiyun doprint "CRITICAL FAILURE... ", @_, "\n"; 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun die @_, "\n"; 99*4882a593Smuzhiyun} 100*4882a593Smuzhiyun 101*4882a593Smuzhiyunsub expand_path { 102*4882a593Smuzhiyun my ($file) = @_; 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun if ($file =~ m,^/,) { 105*4882a593Smuzhiyun return $file; 106*4882a593Smuzhiyun } 107*4882a593Smuzhiyun return "$pwd/$file"; 108*4882a593Smuzhiyun} 109*4882a593Smuzhiyun 110*4882a593Smuzhiyunsub read_prompt { 111*4882a593Smuzhiyun my ($cancel, $prompt) = @_; 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun my $ans; 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun for (;;) { 116*4882a593Smuzhiyun if ($cancel) { 117*4882a593Smuzhiyun print "$prompt [y/n/C] "; 118*4882a593Smuzhiyun } else { 119*4882a593Smuzhiyun print "$prompt [y/N] "; 120*4882a593Smuzhiyun } 121*4882a593Smuzhiyun $ans = <STDIN>; 122*4882a593Smuzhiyun chomp $ans; 123*4882a593Smuzhiyun if ($ans =~ /^\s*$/) { 124*4882a593Smuzhiyun if ($cancel) { 125*4882a593Smuzhiyun $ans = "c"; 126*4882a593Smuzhiyun } else { 127*4882a593Smuzhiyun $ans = "n"; 128*4882a593Smuzhiyun } 129*4882a593Smuzhiyun } 130*4882a593Smuzhiyun last if ($ans =~ /^y$/i || $ans =~ /^n$/i); 131*4882a593Smuzhiyun if ($cancel) { 132*4882a593Smuzhiyun last if ($ans =~ /^c$/i); 133*4882a593Smuzhiyun print "Please answer either 'y', 'n' or 'c'.\n"; 134*4882a593Smuzhiyun } else { 135*4882a593Smuzhiyun print "Please answer either 'y' or 'n'.\n"; 136*4882a593Smuzhiyun } 137*4882a593Smuzhiyun } 138*4882a593Smuzhiyun if ($ans =~ /^c/i) { 139*4882a593Smuzhiyun exit; 140*4882a593Smuzhiyun } 141*4882a593Smuzhiyun if ($ans !~ /^y$/i) { 142*4882a593Smuzhiyun return 0; 143*4882a593Smuzhiyun } 144*4882a593Smuzhiyun return 1; 145*4882a593Smuzhiyun} 146*4882a593Smuzhiyun 147*4882a593Smuzhiyunsub read_yn { 148*4882a593Smuzhiyun my ($prompt) = @_; 149*4882a593Smuzhiyun 150*4882a593Smuzhiyun return read_prompt 0, $prompt; 151*4882a593Smuzhiyun} 152*4882a593Smuzhiyun 153*4882a593Smuzhiyunsub read_ync { 154*4882a593Smuzhiyun my ($prompt) = @_; 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun return read_prompt 1, $prompt; 157*4882a593Smuzhiyun} 158*4882a593Smuzhiyun 159*4882a593Smuzhiyunsub run_command { 160*4882a593Smuzhiyun my ($command, $redirect) = @_; 161*4882a593Smuzhiyun my $start_time; 162*4882a593Smuzhiyun my $end_time; 163*4882a593Smuzhiyun my $dord = 0; 164*4882a593Smuzhiyun my $pid; 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun $start_time = time; 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun doprint("$command ... "); 169*4882a593Smuzhiyun 170*4882a593Smuzhiyun $pid = open(CMD, "$command 2>&1 |") or 171*4882a593Smuzhiyun dodie "unable to exec $command"; 172*4882a593Smuzhiyun 173*4882a593Smuzhiyun if (defined($redirect)) { 174*4882a593Smuzhiyun open (RD, ">$redirect") or 175*4882a593Smuzhiyun dodie "failed to write to redirect $redirect"; 176*4882a593Smuzhiyun $dord = 1; 177*4882a593Smuzhiyun } 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun while (<CMD>) { 180*4882a593Smuzhiyun print RD if ($dord); 181*4882a593Smuzhiyun } 182*4882a593Smuzhiyun 183*4882a593Smuzhiyun waitpid($pid, 0); 184*4882a593Smuzhiyun my $failed = $?; 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun close(CMD); 187*4882a593Smuzhiyun close(RD) if ($dord); 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun $end_time = time; 190*4882a593Smuzhiyun my $delta = $end_time - $start_time; 191*4882a593Smuzhiyun 192*4882a593Smuzhiyun if ($delta == 1) { 193*4882a593Smuzhiyun doprint "[1 second] "; 194*4882a593Smuzhiyun } else { 195*4882a593Smuzhiyun doprint "[$delta seconds] "; 196*4882a593Smuzhiyun } 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun if ($failed) { 199*4882a593Smuzhiyun doprint "FAILED!\n"; 200*4882a593Smuzhiyun } else { 201*4882a593Smuzhiyun doprint "SUCCESS\n"; 202*4882a593Smuzhiyun } 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun return !$failed; 205*4882a593Smuzhiyun} 206*4882a593Smuzhiyun 207*4882a593Smuzhiyun###### CONFIG BISECT ###### 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun# config_ignore holds the configs that were set (or unset) for 210*4882a593Smuzhiyun# a good config and we will ignore these configs for the rest 211*4882a593Smuzhiyun# of a config bisect. These configs stay as they were. 212*4882a593Smuzhiyunmy %config_ignore; 213*4882a593Smuzhiyun 214*4882a593Smuzhiyun# config_set holds what all configs were set as. 215*4882a593Smuzhiyunmy %config_set; 216*4882a593Smuzhiyun 217*4882a593Smuzhiyun# config_off holds the set of configs that the bad config had disabled. 218*4882a593Smuzhiyun# We need to record them and set them in the .config when running 219*4882a593Smuzhiyun# olddefconfig, because olddefconfig keeps the defaults. 220*4882a593Smuzhiyunmy %config_off; 221*4882a593Smuzhiyun 222*4882a593Smuzhiyun# config_off_tmp holds a set of configs to turn off for now 223*4882a593Smuzhiyunmy @config_off_tmp; 224*4882a593Smuzhiyun 225*4882a593Smuzhiyun# config_list is the set of configs that are being tested 226*4882a593Smuzhiyunmy %config_list; 227*4882a593Smuzhiyunmy %null_config; 228*4882a593Smuzhiyun 229*4882a593Smuzhiyunmy %dependency; 230*4882a593Smuzhiyun 231*4882a593Smuzhiyunmy $make; 232*4882a593Smuzhiyun 233*4882a593Smuzhiyunsub make_oldconfig { 234*4882a593Smuzhiyun 235*4882a593Smuzhiyun if (!run_command "$make olddefconfig") { 236*4882a593Smuzhiyun # Perhaps olddefconfig doesn't exist in this version of the kernel 237*4882a593Smuzhiyun # try oldnoconfig 238*4882a593Smuzhiyun doprint "olddefconfig failed, trying make oldnoconfig\n"; 239*4882a593Smuzhiyun if (!run_command "$make oldnoconfig") { 240*4882a593Smuzhiyun doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; 241*4882a593Smuzhiyun # try a yes '' | oldconfig 242*4882a593Smuzhiyun run_command "yes '' | $make oldconfig" or 243*4882a593Smuzhiyun dodie "failed make config oldconfig"; 244*4882a593Smuzhiyun } 245*4882a593Smuzhiyun } 246*4882a593Smuzhiyun} 247*4882a593Smuzhiyun 248*4882a593Smuzhiyunsub assign_configs { 249*4882a593Smuzhiyun my ($hash, $config) = @_; 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun doprint "Reading configs from $config\n"; 252*4882a593Smuzhiyun 253*4882a593Smuzhiyun open (IN, $config) 254*4882a593Smuzhiyun or dodie "Failed to read $config"; 255*4882a593Smuzhiyun 256*4882a593Smuzhiyun while (<IN>) { 257*4882a593Smuzhiyun chomp; 258*4882a593Smuzhiyun if (/^((CONFIG\S*)=.*)/) { 259*4882a593Smuzhiyun ${$hash}{$2} = $1; 260*4882a593Smuzhiyun } elsif (/^(# (CONFIG\S*) is not set)/) { 261*4882a593Smuzhiyun ${$hash}{$2} = $1; 262*4882a593Smuzhiyun } 263*4882a593Smuzhiyun } 264*4882a593Smuzhiyun 265*4882a593Smuzhiyun close(IN); 266*4882a593Smuzhiyun} 267*4882a593Smuzhiyun 268*4882a593Smuzhiyunsub process_config_ignore { 269*4882a593Smuzhiyun my ($config) = @_; 270*4882a593Smuzhiyun 271*4882a593Smuzhiyun assign_configs \%config_ignore, $config; 272*4882a593Smuzhiyun} 273*4882a593Smuzhiyun 274*4882a593Smuzhiyunsub get_dependencies { 275*4882a593Smuzhiyun my ($config) = @_; 276*4882a593Smuzhiyun 277*4882a593Smuzhiyun my $arr = $dependency{$config}; 278*4882a593Smuzhiyun if (!defined($arr)) { 279*4882a593Smuzhiyun return (); 280*4882a593Smuzhiyun } 281*4882a593Smuzhiyun 282*4882a593Smuzhiyun my @deps = @{$arr}; 283*4882a593Smuzhiyun 284*4882a593Smuzhiyun foreach my $dep (@{$arr}) { 285*4882a593Smuzhiyun print "ADD DEP $dep\n"; 286*4882a593Smuzhiyun @deps = (@deps, get_dependencies $dep); 287*4882a593Smuzhiyun } 288*4882a593Smuzhiyun 289*4882a593Smuzhiyun return @deps; 290*4882a593Smuzhiyun} 291*4882a593Smuzhiyun 292*4882a593Smuzhiyunsub save_config { 293*4882a593Smuzhiyun my ($pc, $file) = @_; 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun my %configs = %{$pc}; 296*4882a593Smuzhiyun 297*4882a593Smuzhiyun doprint "Saving configs into $file\n"; 298*4882a593Smuzhiyun 299*4882a593Smuzhiyun open(OUT, ">$file") or dodie "Can not write to $file"; 300*4882a593Smuzhiyun 301*4882a593Smuzhiyun foreach my $config (keys %configs) { 302*4882a593Smuzhiyun print OUT "$configs{$config}\n"; 303*4882a593Smuzhiyun } 304*4882a593Smuzhiyun close(OUT); 305*4882a593Smuzhiyun} 306*4882a593Smuzhiyun 307*4882a593Smuzhiyunsub create_config { 308*4882a593Smuzhiyun my ($name, $pc) = @_; 309*4882a593Smuzhiyun 310*4882a593Smuzhiyun doprint "Creating old config from $name configs\n"; 311*4882a593Smuzhiyun 312*4882a593Smuzhiyun save_config $pc, $output_config; 313*4882a593Smuzhiyun 314*4882a593Smuzhiyun make_oldconfig; 315*4882a593Smuzhiyun} 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun# compare two config hashes, and return configs with different vals. 318*4882a593Smuzhiyun# It returns B's config values, but you can use A to see what A was. 319*4882a593Smuzhiyunsub diff_config_vals { 320*4882a593Smuzhiyun my ($pa, $pb) = @_; 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun # crappy Perl way to pass in hashes. 323*4882a593Smuzhiyun my %a = %{$pa}; 324*4882a593Smuzhiyun my %b = %{$pb}; 325*4882a593Smuzhiyun 326*4882a593Smuzhiyun my %ret; 327*4882a593Smuzhiyun 328*4882a593Smuzhiyun foreach my $item (keys %a) { 329*4882a593Smuzhiyun if (defined($b{$item}) && $b{$item} ne $a{$item}) { 330*4882a593Smuzhiyun $ret{$item} = $b{$item}; 331*4882a593Smuzhiyun } 332*4882a593Smuzhiyun } 333*4882a593Smuzhiyun 334*4882a593Smuzhiyun return %ret; 335*4882a593Smuzhiyun} 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun# compare two config hashes and return the configs in B but not A 338*4882a593Smuzhiyunsub diff_configs { 339*4882a593Smuzhiyun my ($pa, $pb) = @_; 340*4882a593Smuzhiyun 341*4882a593Smuzhiyun my %ret; 342*4882a593Smuzhiyun 343*4882a593Smuzhiyun # crappy Perl way to pass in hashes. 344*4882a593Smuzhiyun my %a = %{$pa}; 345*4882a593Smuzhiyun my %b = %{$pb}; 346*4882a593Smuzhiyun 347*4882a593Smuzhiyun foreach my $item (keys %b) { 348*4882a593Smuzhiyun if (!defined($a{$item})) { 349*4882a593Smuzhiyun $ret{$item} = $b{$item}; 350*4882a593Smuzhiyun } 351*4882a593Smuzhiyun } 352*4882a593Smuzhiyun 353*4882a593Smuzhiyun return %ret; 354*4882a593Smuzhiyun} 355*4882a593Smuzhiyun 356*4882a593Smuzhiyun# return if two configs are equal or not 357*4882a593Smuzhiyun# 0 is equal +1 b has something a does not 358*4882a593Smuzhiyun# +1 if a and b have a different item. 359*4882a593Smuzhiyun# -1 if a has something b does not 360*4882a593Smuzhiyunsub compare_configs { 361*4882a593Smuzhiyun my ($pa, $pb) = @_; 362*4882a593Smuzhiyun 363*4882a593Smuzhiyun my %ret; 364*4882a593Smuzhiyun 365*4882a593Smuzhiyun # crappy Perl way to pass in hashes. 366*4882a593Smuzhiyun my %a = %{$pa}; 367*4882a593Smuzhiyun my %b = %{$pb}; 368*4882a593Smuzhiyun 369*4882a593Smuzhiyun foreach my $item (keys %b) { 370*4882a593Smuzhiyun if (!defined($a{$item})) { 371*4882a593Smuzhiyun return 1; 372*4882a593Smuzhiyun } 373*4882a593Smuzhiyun if ($a{$item} ne $b{$item}) { 374*4882a593Smuzhiyun return 1; 375*4882a593Smuzhiyun } 376*4882a593Smuzhiyun } 377*4882a593Smuzhiyun 378*4882a593Smuzhiyun foreach my $item (keys %a) { 379*4882a593Smuzhiyun if (!defined($b{$item})) { 380*4882a593Smuzhiyun return -1; 381*4882a593Smuzhiyun } 382*4882a593Smuzhiyun } 383*4882a593Smuzhiyun 384*4882a593Smuzhiyun return 0; 385*4882a593Smuzhiyun} 386*4882a593Smuzhiyun 387*4882a593Smuzhiyunsub process_failed { 388*4882a593Smuzhiyun my ($config) = @_; 389*4882a593Smuzhiyun 390*4882a593Smuzhiyun doprint "\n\n***************************************\n"; 391*4882a593Smuzhiyun doprint "Found bad config: $config\n"; 392*4882a593Smuzhiyun doprint "***************************************\n\n"; 393*4882a593Smuzhiyun} 394*4882a593Smuzhiyun 395*4882a593Smuzhiyunsub process_new_config { 396*4882a593Smuzhiyun my ($tc, $nc, $gc, $bc) = @_; 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun my %tmp_config = %{$tc}; 399*4882a593Smuzhiyun my %good_configs = %{$gc}; 400*4882a593Smuzhiyun my %bad_configs = %{$bc}; 401*4882a593Smuzhiyun 402*4882a593Smuzhiyun my %new_configs; 403*4882a593Smuzhiyun 404*4882a593Smuzhiyun my $runtest = 1; 405*4882a593Smuzhiyun my $ret; 406*4882a593Smuzhiyun 407*4882a593Smuzhiyun create_config "tmp_configs", \%tmp_config; 408*4882a593Smuzhiyun assign_configs \%new_configs, $output_config; 409*4882a593Smuzhiyun 410*4882a593Smuzhiyun $ret = compare_configs \%new_configs, \%bad_configs; 411*4882a593Smuzhiyun if (!$ret) { 412*4882a593Smuzhiyun doprint "New config equals bad config, try next test\n"; 413*4882a593Smuzhiyun $runtest = 0; 414*4882a593Smuzhiyun } 415*4882a593Smuzhiyun 416*4882a593Smuzhiyun if ($runtest) { 417*4882a593Smuzhiyun $ret = compare_configs \%new_configs, \%good_configs; 418*4882a593Smuzhiyun if (!$ret) { 419*4882a593Smuzhiyun doprint "New config equals good config, try next test\n"; 420*4882a593Smuzhiyun $runtest = 0; 421*4882a593Smuzhiyun } 422*4882a593Smuzhiyun } 423*4882a593Smuzhiyun 424*4882a593Smuzhiyun %{$nc} = %new_configs; 425*4882a593Smuzhiyun 426*4882a593Smuzhiyun return $runtest; 427*4882a593Smuzhiyun} 428*4882a593Smuzhiyun 429*4882a593Smuzhiyunsub convert_config { 430*4882a593Smuzhiyun my ($config) = @_; 431*4882a593Smuzhiyun 432*4882a593Smuzhiyun if ($config =~ /^# (.*) is not set/) { 433*4882a593Smuzhiyun $config = "$1=n"; 434*4882a593Smuzhiyun } 435*4882a593Smuzhiyun 436*4882a593Smuzhiyun $config =~ s/^CONFIG_//; 437*4882a593Smuzhiyun return $config; 438*4882a593Smuzhiyun} 439*4882a593Smuzhiyun 440*4882a593Smuzhiyunsub print_config { 441*4882a593Smuzhiyun my ($sym, $config) = @_; 442*4882a593Smuzhiyun 443*4882a593Smuzhiyun $config = convert_config $config; 444*4882a593Smuzhiyun doprint "$sym$config\n"; 445*4882a593Smuzhiyun} 446*4882a593Smuzhiyun 447*4882a593Smuzhiyunsub print_config_compare { 448*4882a593Smuzhiyun my ($good_config, $bad_config) = @_; 449*4882a593Smuzhiyun 450*4882a593Smuzhiyun $good_config = convert_config $good_config; 451*4882a593Smuzhiyun $bad_config = convert_config $bad_config; 452*4882a593Smuzhiyun 453*4882a593Smuzhiyun my $good_value = $good_config; 454*4882a593Smuzhiyun my $bad_value = $bad_config; 455*4882a593Smuzhiyun $good_value =~ s/(.*)=//; 456*4882a593Smuzhiyun my $config = $1; 457*4882a593Smuzhiyun 458*4882a593Smuzhiyun $bad_value =~ s/.*=//; 459*4882a593Smuzhiyun 460*4882a593Smuzhiyun doprint " $config $good_value -> $bad_value\n"; 461*4882a593Smuzhiyun} 462*4882a593Smuzhiyun 463*4882a593Smuzhiyun# Pass in: 464*4882a593Smuzhiyun# $phalf: half of the configs names you want to add 465*4882a593Smuzhiyun# $oconfigs: The orginial configs to start with 466*4882a593Smuzhiyun# $sconfigs: The source to update $oconfigs with (from $phalf) 467*4882a593Smuzhiyun# $which: The name of which half that is updating (top / bottom) 468*4882a593Smuzhiyun# $type: The name of the source type (good / bad) 469*4882a593Smuzhiyunsub make_half { 470*4882a593Smuzhiyun my ($phalf, $oconfigs, $sconfigs, $which, $type) = @_; 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun my @half = @{$phalf}; 473*4882a593Smuzhiyun my %orig_configs = %{$oconfigs}; 474*4882a593Smuzhiyun my %source_configs = %{$sconfigs}; 475*4882a593Smuzhiyun 476*4882a593Smuzhiyun my %tmp_config = %orig_configs; 477*4882a593Smuzhiyun 478*4882a593Smuzhiyun doprint "Settings bisect with $which half of $type configs:\n"; 479*4882a593Smuzhiyun foreach my $item (@half) { 480*4882a593Smuzhiyun doprint "Updating $item to $source_configs{$item}\n"; 481*4882a593Smuzhiyun $tmp_config{$item} = $source_configs{$item}; 482*4882a593Smuzhiyun } 483*4882a593Smuzhiyun 484*4882a593Smuzhiyun return %tmp_config; 485*4882a593Smuzhiyun} 486*4882a593Smuzhiyun 487*4882a593Smuzhiyunsub run_config_bisect { 488*4882a593Smuzhiyun my ($pgood, $pbad) = @_; 489*4882a593Smuzhiyun 490*4882a593Smuzhiyun my %good_configs = %{$pgood}; 491*4882a593Smuzhiyun my %bad_configs = %{$pbad}; 492*4882a593Smuzhiyun 493*4882a593Smuzhiyun my %diff_configs = diff_config_vals \%good_configs, \%bad_configs; 494*4882a593Smuzhiyun my %b_configs = diff_configs \%good_configs, \%bad_configs; 495*4882a593Smuzhiyun my %g_configs = diff_configs \%bad_configs, \%good_configs; 496*4882a593Smuzhiyun 497*4882a593Smuzhiyun # diff_arr is what is in both good and bad but are different (y->n) 498*4882a593Smuzhiyun my @diff_arr = keys %diff_configs; 499*4882a593Smuzhiyun my $len_diff = $#diff_arr + 1; 500*4882a593Smuzhiyun 501*4882a593Smuzhiyun # b_arr is what is in bad but not in good (has depends) 502*4882a593Smuzhiyun my @b_arr = keys %b_configs; 503*4882a593Smuzhiyun my $len_b = $#b_arr + 1; 504*4882a593Smuzhiyun 505*4882a593Smuzhiyun # g_arr is what is in good but not in bad 506*4882a593Smuzhiyun my @g_arr = keys %g_configs; 507*4882a593Smuzhiyun my $len_g = $#g_arr + 1; 508*4882a593Smuzhiyun 509*4882a593Smuzhiyun my $runtest = 0; 510*4882a593Smuzhiyun my %new_configs; 511*4882a593Smuzhiyun my $ret; 512*4882a593Smuzhiyun 513*4882a593Smuzhiyun # Look at the configs that are different between good and bad. 514*4882a593Smuzhiyun # This does not include those that depend on other configs 515*4882a593Smuzhiyun # (configs depending on other configs that are not set would 516*4882a593Smuzhiyun # not show up even as a "# CONFIG_FOO is not set" 517*4882a593Smuzhiyun 518*4882a593Smuzhiyun 519*4882a593Smuzhiyun doprint "# of configs to check: $len_diff\n"; 520*4882a593Smuzhiyun doprint "# of configs showing only in good: $len_g\n"; 521*4882a593Smuzhiyun doprint "# of configs showing only in bad: $len_b\n"; 522*4882a593Smuzhiyun 523*4882a593Smuzhiyun if ($len_diff > 0) { 524*4882a593Smuzhiyun # Now test for different values 525*4882a593Smuzhiyun 526*4882a593Smuzhiyun doprint "Configs left to check:\n"; 527*4882a593Smuzhiyun doprint " Good Config\t\t\tBad Config\n"; 528*4882a593Smuzhiyun doprint " -----------\t\t\t----------\n"; 529*4882a593Smuzhiyun foreach my $item (@diff_arr) { 530*4882a593Smuzhiyun doprint " $good_configs{$item}\t$bad_configs{$item}\n"; 531*4882a593Smuzhiyun } 532*4882a593Smuzhiyun 533*4882a593Smuzhiyun my $half = int($#diff_arr / 2); 534*4882a593Smuzhiyun my @tophalf = @diff_arr[0 .. $half]; 535*4882a593Smuzhiyun 536*4882a593Smuzhiyun doprint "Set tmp config to be good config with some bad config values\n"; 537*4882a593Smuzhiyun 538*4882a593Smuzhiyun my %tmp_config = make_half \@tophalf, \%good_configs, 539*4882a593Smuzhiyun \%bad_configs, "top", "bad"; 540*4882a593Smuzhiyun 541*4882a593Smuzhiyun $runtest = process_new_config \%tmp_config, \%new_configs, 542*4882a593Smuzhiyun \%good_configs, \%bad_configs; 543*4882a593Smuzhiyun 544*4882a593Smuzhiyun if (!$runtest) { 545*4882a593Smuzhiyun doprint "Set tmp config to be bad config with some good config values\n"; 546*4882a593Smuzhiyun 547*4882a593Smuzhiyun my %tmp_config = make_half \@tophalf, \%bad_configs, 548*4882a593Smuzhiyun \%good_configs, "top", "good"; 549*4882a593Smuzhiyun 550*4882a593Smuzhiyun $runtest = process_new_config \%tmp_config, \%new_configs, 551*4882a593Smuzhiyun \%good_configs, \%bad_configs; 552*4882a593Smuzhiyun } 553*4882a593Smuzhiyun } 554*4882a593Smuzhiyun 555*4882a593Smuzhiyun if (!$runtest && $len_diff > 0) { 556*4882a593Smuzhiyun # do the same thing, but this time with bottom half 557*4882a593Smuzhiyun 558*4882a593Smuzhiyun my $half = int($#diff_arr / 2); 559*4882a593Smuzhiyun my @bottomhalf = @diff_arr[$half+1 .. $#diff_arr]; 560*4882a593Smuzhiyun 561*4882a593Smuzhiyun doprint "Set tmp config to be good config with some bad config values\n"; 562*4882a593Smuzhiyun 563*4882a593Smuzhiyun my %tmp_config = make_half \@bottomhalf, \%good_configs, 564*4882a593Smuzhiyun \%bad_configs, "bottom", "bad"; 565*4882a593Smuzhiyun 566*4882a593Smuzhiyun $runtest = process_new_config \%tmp_config, \%new_configs, 567*4882a593Smuzhiyun \%good_configs, \%bad_configs; 568*4882a593Smuzhiyun 569*4882a593Smuzhiyun if (!$runtest) { 570*4882a593Smuzhiyun doprint "Set tmp config to be bad config with some good config values\n"; 571*4882a593Smuzhiyun 572*4882a593Smuzhiyun my %tmp_config = make_half \@bottomhalf, \%bad_configs, 573*4882a593Smuzhiyun \%good_configs, "bottom", "good"; 574*4882a593Smuzhiyun 575*4882a593Smuzhiyun $runtest = process_new_config \%tmp_config, \%new_configs, 576*4882a593Smuzhiyun \%good_configs, \%bad_configs; 577*4882a593Smuzhiyun } 578*4882a593Smuzhiyun } 579*4882a593Smuzhiyun 580*4882a593Smuzhiyun if ($runtest) { 581*4882a593Smuzhiyun make_oldconfig; 582*4882a593Smuzhiyun doprint "READY TO TEST .config IN $build\n"; 583*4882a593Smuzhiyun return 0; 584*4882a593Smuzhiyun } 585*4882a593Smuzhiyun 586*4882a593Smuzhiyun doprint "\n%%%%%%%% FAILED TO FIND SINGLE BAD CONFIG %%%%%%%%\n"; 587*4882a593Smuzhiyun doprint "Hmm, can't make any more changes without making good == bad?\n"; 588*4882a593Smuzhiyun doprint "Difference between good (+) and bad (-)\n"; 589*4882a593Smuzhiyun 590*4882a593Smuzhiyun foreach my $item (keys %bad_configs) { 591*4882a593Smuzhiyun if (!defined($good_configs{$item})) { 592*4882a593Smuzhiyun print_config "-", $bad_configs{$item}; 593*4882a593Smuzhiyun } 594*4882a593Smuzhiyun } 595*4882a593Smuzhiyun 596*4882a593Smuzhiyun foreach my $item (keys %good_configs) { 597*4882a593Smuzhiyun next if (!defined($bad_configs{$item})); 598*4882a593Smuzhiyun if ($good_configs{$item} ne $bad_configs{$item}) { 599*4882a593Smuzhiyun print_config_compare $good_configs{$item}, $bad_configs{$item}; 600*4882a593Smuzhiyun } 601*4882a593Smuzhiyun } 602*4882a593Smuzhiyun 603*4882a593Smuzhiyun foreach my $item (keys %good_configs) { 604*4882a593Smuzhiyun if (!defined($bad_configs{$item})) { 605*4882a593Smuzhiyun print_config "+", $good_configs{$item}; 606*4882a593Smuzhiyun } 607*4882a593Smuzhiyun } 608*4882a593Smuzhiyun return -1; 609*4882a593Smuzhiyun} 610*4882a593Smuzhiyun 611*4882a593Smuzhiyunsub config_bisect { 612*4882a593Smuzhiyun my ($good_config, $bad_config) = @_; 613*4882a593Smuzhiyun my $ret; 614*4882a593Smuzhiyun 615*4882a593Smuzhiyun my %good_configs; 616*4882a593Smuzhiyun my %bad_configs; 617*4882a593Smuzhiyun my %tmp_configs; 618*4882a593Smuzhiyun 619*4882a593Smuzhiyun doprint "Run good configs through make oldconfig\n"; 620*4882a593Smuzhiyun assign_configs \%tmp_configs, $good_config; 621*4882a593Smuzhiyun create_config "$good_config", \%tmp_configs; 622*4882a593Smuzhiyun assign_configs \%good_configs, $output_config; 623*4882a593Smuzhiyun 624*4882a593Smuzhiyun doprint "Run bad configs through make oldconfig\n"; 625*4882a593Smuzhiyun assign_configs \%tmp_configs, $bad_config; 626*4882a593Smuzhiyun create_config "$bad_config", \%tmp_configs; 627*4882a593Smuzhiyun assign_configs \%bad_configs, $output_config; 628*4882a593Smuzhiyun 629*4882a593Smuzhiyun save_config \%good_configs, $good_config; 630*4882a593Smuzhiyun save_config \%bad_configs, $bad_config; 631*4882a593Smuzhiyun 632*4882a593Smuzhiyun return run_config_bisect \%good_configs, \%bad_configs; 633*4882a593Smuzhiyun} 634*4882a593Smuzhiyun 635*4882a593Smuzhiyunwhile ($#ARGV >= 0) { 636*4882a593Smuzhiyun if ($ARGV[0] !~ m/^-/) { 637*4882a593Smuzhiyun last; 638*4882a593Smuzhiyun } 639*4882a593Smuzhiyun my $opt = shift @ARGV; 640*4882a593Smuzhiyun 641*4882a593Smuzhiyun if ($opt eq "-b") { 642*4882a593Smuzhiyun $val = shift @ARGV; 643*4882a593Smuzhiyun if (!defined($val)) { 644*4882a593Smuzhiyun die "-b requires value\n"; 645*4882a593Smuzhiyun } 646*4882a593Smuzhiyun $build = $val; 647*4882a593Smuzhiyun } 648*4882a593Smuzhiyun 649*4882a593Smuzhiyun elsif ($opt eq "-l") { 650*4882a593Smuzhiyun $val = shift @ARGV; 651*4882a593Smuzhiyun if (!defined($val)) { 652*4882a593Smuzhiyun die "-l requires value\n"; 653*4882a593Smuzhiyun } 654*4882a593Smuzhiyun $tree = $val; 655*4882a593Smuzhiyun } 656*4882a593Smuzhiyun 657*4882a593Smuzhiyun elsif ($opt eq "-r") { 658*4882a593Smuzhiyun $reset_bisect = 1; 659*4882a593Smuzhiyun } 660*4882a593Smuzhiyun 661*4882a593Smuzhiyun elsif ($opt eq "-h") { 662*4882a593Smuzhiyun usage; 663*4882a593Smuzhiyun } 664*4882a593Smuzhiyun 665*4882a593Smuzhiyun else { 666*4882a593Smuzhiyun die "Unknown option $opt\n"; 667*4882a593Smuzhiyun } 668*4882a593Smuzhiyun} 669*4882a593Smuzhiyun 670*4882a593Smuzhiyun$build = $tree if (!defined($build)); 671*4882a593Smuzhiyun 672*4882a593Smuzhiyun$tree = expand_path $tree; 673*4882a593Smuzhiyun$build = expand_path $build; 674*4882a593Smuzhiyun 675*4882a593Smuzhiyunif ( ! -d $tree ) { 676*4882a593Smuzhiyun die "$tree not a directory\n"; 677*4882a593Smuzhiyun} 678*4882a593Smuzhiyun 679*4882a593Smuzhiyunif ( ! -d $build ) { 680*4882a593Smuzhiyun die "$build not a directory\n"; 681*4882a593Smuzhiyun} 682*4882a593Smuzhiyun 683*4882a593Smuzhiyunusage if $#ARGV < 1; 684*4882a593Smuzhiyun 685*4882a593Smuzhiyunif ($#ARGV == 1) { 686*4882a593Smuzhiyun $start = 1; 687*4882a593Smuzhiyun} elsif ($#ARGV == 2) { 688*4882a593Smuzhiyun $val = $ARGV[2]; 689*4882a593Smuzhiyun if ($val ne "good" && $val ne "bad") { 690*4882a593Smuzhiyun die "Unknown command '$val', bust be either \"good\" or \"bad\"\n"; 691*4882a593Smuzhiyun } 692*4882a593Smuzhiyun} else { 693*4882a593Smuzhiyun usage; 694*4882a593Smuzhiyun} 695*4882a593Smuzhiyun 696*4882a593Smuzhiyunmy $good_start = expand_path $ARGV[0]; 697*4882a593Smuzhiyunmy $bad_start = expand_path $ARGV[1]; 698*4882a593Smuzhiyun 699*4882a593Smuzhiyunmy $good = "$good_start.tmp"; 700*4882a593Smuzhiyunmy $bad = "$bad_start.tmp"; 701*4882a593Smuzhiyun 702*4882a593Smuzhiyun$make = "make"; 703*4882a593Smuzhiyun 704*4882a593Smuzhiyunif ($build ne $tree) { 705*4882a593Smuzhiyun $make = "make O=$build" 706*4882a593Smuzhiyun} 707*4882a593Smuzhiyun 708*4882a593Smuzhiyun$output_config = "$build/.config"; 709*4882a593Smuzhiyun 710*4882a593Smuzhiyunif ($start) { 711*4882a593Smuzhiyun if ( ! -f $good_start ) { 712*4882a593Smuzhiyun die "$good_start not found\n"; 713*4882a593Smuzhiyun } 714*4882a593Smuzhiyun if ( ! -f $bad_start ) { 715*4882a593Smuzhiyun die "$bad_start not found\n"; 716*4882a593Smuzhiyun } 717*4882a593Smuzhiyun if ( -f $good || -f $bad ) { 718*4882a593Smuzhiyun my $p = ""; 719*4882a593Smuzhiyun 720*4882a593Smuzhiyun if ( -f $good ) { 721*4882a593Smuzhiyun $p = "$good exists\n"; 722*4882a593Smuzhiyun } 723*4882a593Smuzhiyun 724*4882a593Smuzhiyun if ( -f $bad ) { 725*4882a593Smuzhiyun $p = "$p$bad exists\n"; 726*4882a593Smuzhiyun } 727*4882a593Smuzhiyun 728*4882a593Smuzhiyun if (!defined($reset_bisect)) { 729*4882a593Smuzhiyun if (!read_yn "${p}Overwrite and start new bisect anyway?") { 730*4882a593Smuzhiyun exit (-1); 731*4882a593Smuzhiyun } 732*4882a593Smuzhiyun } 733*4882a593Smuzhiyun } 734*4882a593Smuzhiyun run_command "cp $good_start $good" or die "failed to copy to $good\n"; 735*4882a593Smuzhiyun run_command "cp $bad_start $bad" or die "failed to copy to $bad\n"; 736*4882a593Smuzhiyun} else { 737*4882a593Smuzhiyun if ( ! -f $good ) { 738*4882a593Smuzhiyun die "Can not find file $good\n"; 739*4882a593Smuzhiyun } 740*4882a593Smuzhiyun if ( ! -f $bad ) { 741*4882a593Smuzhiyun die "Can not find file $bad\n"; 742*4882a593Smuzhiyun } 743*4882a593Smuzhiyun if ($val eq "good") { 744*4882a593Smuzhiyun run_command "cp $output_config $good" or die "failed to copy $config to $good\n"; 745*4882a593Smuzhiyun } elsif ($val eq "bad") { 746*4882a593Smuzhiyun run_command "cp $output_config $bad" or die "failed to copy $config to $bad\n"; 747*4882a593Smuzhiyun } 748*4882a593Smuzhiyun} 749*4882a593Smuzhiyun 750*4882a593Smuzhiyunchdir $tree || die "can't change directory to $tree"; 751*4882a593Smuzhiyun 752*4882a593Smuzhiyunmy $ret = config_bisect $good, $bad; 753*4882a593Smuzhiyun 754*4882a593Smuzhiyunif (!$ret) { 755*4882a593Smuzhiyun exit(0); 756*4882a593Smuzhiyun} 757*4882a593Smuzhiyun 758*4882a593Smuzhiyunif ($ret > 0) { 759*4882a593Smuzhiyun doprint "Cleaning temp files\n"; 760*4882a593Smuzhiyun run_command "rm $good"; 761*4882a593Smuzhiyun run_command "rm $bad"; 762*4882a593Smuzhiyun exit(1); 763*4882a593Smuzhiyun} else { 764*4882a593Smuzhiyun doprint "See good and bad configs for details:\n"; 765*4882a593Smuzhiyun doprint "good: $good\n"; 766*4882a593Smuzhiyun doprint "bad: $bad\n"; 767*4882a593Smuzhiyun doprint "%%%%%%%% FAILED TO FIND SINGLE BAD CONFIG %%%%%%%%\n"; 768*4882a593Smuzhiyun} 769*4882a593Smuzhiyunexit(2); 770